forked from Kieran/void.cat
Enabled legacy uploads
This commit is contained in:
parent
991d8a841d
commit
3a9f0c4890
@ -184,7 +184,7 @@ const App = {
|
||||
CheckBrowserSupport: function () {
|
||||
if (!App.IsFirefox) {
|
||||
if (App.IsChrome) {
|
||||
App.AddNoticeItem("Uploads bigger then 100MiB usually crash Chrome when uploading. Please upload with Firefox. Or check <a target=\"_blank\" href=\"https://github.com/v0l/void.cat/tree/v3-b2b/tools\">GitHub</a> for tools.");
|
||||
App.AddNoticeItem("Uploads bigger then 100MiB usually crash Chrome when uploading. Please upload with Firefox. Or check <a target=\"_blank\" href=\"https://github.com/v0l/void.cat/releases\">GitHub</a> for tools.");
|
||||
}
|
||||
if (App.IsEdge) {
|
||||
let edge_version = /Edge\/([0-9]{1,3}\.[0-9]{1,5})/.exec(navigator.userAgent)[1];
|
||||
|
@ -62,29 +62,38 @@ const FileDownloader = function (fileinfo, key, iv) {
|
||||
* @returns {Promise<File>} The loaded and decripted file
|
||||
*/
|
||||
this.DownloadFile = async function () {
|
||||
let link = (this.fileinfo.DownloadHost !== null ? `${window.location.protocol}//${this.fileinfo.DownloadHost}` : '') + `/${this.fileinfo.FileId}`
|
||||
let link = (this.fileinfo.DownloadHost !== null ? `${window.location.protocol}//${this.fileinfo.DownloadHost}` : '') + `/${this.fileinfo.FileId}`;
|
||||
Log.I(`Starting download from: ${link}`);
|
||||
let rsp = await XHR('GET', link, undefined, undefined, undefined, function (ev) {
|
||||
let now = new Date().getTime();
|
||||
let dxLoaded = ev.loaded - this.downloadStats.lastLoaded;
|
||||
let dxTime = now - this.downloadStats.lastProgress;
|
||||
if(this.fileinfo.IsLegacyUpload) {
|
||||
return {
|
||||
isLegacy: true,
|
||||
name: this.fileinfo.LegacyFilename,
|
||||
mime: this.fileinfo.LegacyMime,
|
||||
url: link
|
||||
};
|
||||
} else {
|
||||
let rsp = await XHR('GET', link, undefined, undefined, undefined, function (ev) {
|
||||
let now = new Date().getTime();
|
||||
let dxLoaded = ev.loaded - this.downloadStats.lastLoaded;
|
||||
let dxTime = now - this.downloadStats.lastProgress;
|
||||
|
||||
this.downloadStats.lastLoaded = ev.loaded;
|
||||
this.downloadStats.lastProgress = now;
|
||||
this.downloadStats.lastLoaded = ev.loaded;
|
||||
this.downloadStats.lastProgress = now;
|
||||
|
||||
this.HandleProgress('progress-speed', `${Utils.FormatBytes(dxLoaded / (dxTime / 1000.0), 2)}/s`);
|
||||
this.HandleProgress('progress-download', ev.loaded / (ev.lengthComputable ? parseFloat(ev.total) : this.fileinfo.Size));
|
||||
}.bind(this), function (req) {
|
||||
req.responseType = "arraybuffer";
|
||||
});
|
||||
this.HandleProgress('progress-speed', `${Utils.FormatBytes(dxLoaded / (dxTime / 1000.0), 2)}/s`);
|
||||
this.HandleProgress('progress-download', ev.loaded / (ev.lengthComputable ? parseFloat(ev.total) : this.fileinfo.Size));
|
||||
}.bind(this), function (req) {
|
||||
req.responseType = "arraybuffer";
|
||||
});
|
||||
|
||||
if (rsp.status === 200) {
|
||||
this.HandleProgress('decrypt-start');
|
||||
let fd_decrypted = await this.DecryptFile(rsp.response);
|
||||
this.HandleProgress('download-complete');
|
||||
return fd_decrypted;
|
||||
} else if (rsp.status === 429) {
|
||||
this.HandleProgress('rate-limited');
|
||||
if (rsp.status === 200) {
|
||||
this.HandleProgress('decrypt-start');
|
||||
let fd_decrypted = await this.DecryptFile(rsp.response);
|
||||
this.HandleProgress('download-complete');
|
||||
return fd_decrypted;
|
||||
} else if (rsp.status === 429) {
|
||||
this.HandleProgress('rate-limited');
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -33,8 +33,16 @@ const ViewManager = function () {
|
||||
|
||||
let nelm = document.importNode($("template[id='tmpl-view-default']").content, true);
|
||||
nelm.querySelector('.view-file-id').textContent = fileinfo.FileId;
|
||||
nelm.querySelector('.view-key').textContent = this.key;
|
||||
nelm.querySelector('.view-iv').textContent = this.iv;
|
||||
if (fileinfo.IsLegacyUpload) {
|
||||
let keyrow = nelm.querySelector('.view-key');
|
||||
keyrow.textContent = fileinfo.LegacyFilename;
|
||||
keyrow.previousElementSibling.textContent = "Filename:";
|
||||
nelm.querySelector('.view-iv').parentNode.style.display = "none";
|
||||
nelm.querySelector('.view-transfer-stats').style.display = "none";
|
||||
} else {
|
||||
nelm.querySelector('.view-key').textContent = this.key;
|
||||
nelm.querySelector('.view-iv').textContent = this.iv;
|
||||
}
|
||||
nelm.querySelector('.btn-download').addEventListener('click', function () {
|
||||
let fd = new FileDownloader(this.fileinfo, this.self.key, this.self.iv);
|
||||
fd.onprogress = function (x) {
|
||||
@ -59,7 +67,7 @@ const ViewManager = function () {
|
||||
alert('Captcha check failed, are you a robot?');
|
||||
}
|
||||
}.bind({ id: this.id }));
|
||||
}else {
|
||||
} else {
|
||||
Log.E('No recaptcha config set');
|
||||
}
|
||||
}.bind({
|
||||
@ -68,7 +76,7 @@ const ViewManager = function () {
|
||||
});
|
||||
fd.DownloadFile().then(function (file) {
|
||||
if (file !== null) {
|
||||
var objurl = URL.createObjectURL(file.blob);
|
||||
var objurl = file.isLegacy !== undefined ? file.url : URL.createObjectURL(file.blob);
|
||||
var dl_link = document.createElement('a');
|
||||
dl_link.href = objurl;
|
||||
dl_link.download = file.name;
|
||||
|
@ -37,7 +37,7 @@
|
||||
case "file_info": {
|
||||
$rsp->ok = true;
|
||||
$rsp->data = $fs->GetFileInfo($cmd->id);
|
||||
$rsp->data->DownloadHost = Upload::GetUploadHost(); //bypass CF proxy for downloads (slow..)
|
||||
//$rsp->data->DownloadHost = Upload::GetUploadHost(); //bypass CF proxy for downloads (slow..)
|
||||
break;
|
||||
}
|
||||
case 'captcha_info': {
|
||||
|
38
src/php/auth.php
Normal file
38
src/php/auth.php
Normal file
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
class Auth {
|
||||
|
||||
//https://stackoverflow.com/questions/40582161/how-to-properly-use-bearer-tokens
|
||||
public function GetAuthorizationHeader() : ?string {
|
||||
$headers = null;
|
||||
if (isset($_SERVER['Authorization'])) {
|
||||
$headers = trim($_SERVER["Authorization"]);
|
||||
}
|
||||
else if (isset($_SERVER['HTTP_AUTHORIZATION'])) { //Nginx or fast CGI
|
||||
$headers = trim($_SERVER["HTTP_AUTHORIZATION"]);
|
||||
} elseif (function_exists('apache_request_headers')) {
|
||||
$requestHeaders = apache_request_headers();
|
||||
// Server-side fix for bug in old Android versions (a nice side-effect of this fix means we don't care about capitalization for Authorization)
|
||||
$requestHeaders = array_combine(array_map('ucwords', array_keys($requestHeaders)), array_values($requestHeaders));
|
||||
if (isset($requestHeaders['Authorization'])) {
|
||||
$headers = trim($requestHeaders['Authorization']);
|
||||
}
|
||||
}
|
||||
return $headers;
|
||||
}
|
||||
|
||||
function GetBearerToken() : ?string {
|
||||
$headers = $this->GetAuthorizationHeader();
|
||||
if (!empty($headers)) {
|
||||
if (preg_match('/Bearer\s(\S+)/', $headers, $matches)) {
|
||||
return $matches[1];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function CheckApiToken($token) {
|
||||
return StaticRedis::ReadOp()->sIsMember(REDIS_PREFIX . "api-keys", $token);
|
||||
}
|
||||
}
|
||||
?>
|
@ -8,42 +8,49 @@
|
||||
}
|
||||
|
||||
public function HandleRequest() : void {
|
||||
$this->Fs = new FileStore(Config::$Instance->upload_folder);
|
||||
if(isset($_REQUEST["id"])){
|
||||
$id = $_REQUEST["id"];
|
||||
$ref = isset($_SERVER["HTTP_REFERER"]) ? $_SERVER["HTTP_REFERER"] : null;
|
||||
$id = isset($_REQUEST["id"]) ? $_REQUEST["id"] : null;
|
||||
|
||||
if($this->Fs->FileExists($id)){
|
||||
$this->StartDownload($id);
|
||||
if($ref === null && $id !== null) {
|
||||
header("location: /#$id");
|
||||
} else if($id !== null) {
|
||||
$this->Fs = new FileStore(Config::$Instance->upload_folder);
|
||||
if($this->Fs->FileExists($id)) {
|
||||
$this->StartDownload($id, $this->Fs->GetFileInfo($id));
|
||||
} else {
|
||||
http_response_code(404);
|
||||
exit();
|
||||
}
|
||||
} else {
|
||||
http_response_code(404);
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
function StartDownload($id){
|
||||
function StartDownload($id, $info) : void {
|
||||
$abuse = new Abuse();
|
||||
$tracking = new Tracking();
|
||||
|
||||
header("Cache-Control: private");
|
||||
header("Access-Control-Allow-Origin: " . $_SERVER["HTTP_ORIGIN"]);
|
||||
header("Access-Control-Allow-Method: GET");
|
||||
if(isset($_SERVER["HTTP_ORIGIN"])) {
|
||||
header("Access-Control-Allow-Origin: " . $_SERVER["HTTP_ORIGIN"]);
|
||||
header("Access-Control-Allow-Method: GET");
|
||||
}
|
||||
|
||||
$abuse->CheckDownload($id);
|
||||
$tracking->TrackDownload($this->Fs, $id);
|
||||
|
||||
//allow embeded header from preflight check
|
||||
if($_SERVER["REQUEST_METHOD"] === "GET") {
|
||||
$this->InternalNginxRedirect($this->Fs->GetRelativeFilePath($id), 604800);
|
||||
$this->InternalNginxRedirect($this->Fs->GetRelativeFilePath($id), 604800, $info);
|
||||
}
|
||||
}
|
||||
|
||||
function InternalNginxRedirect($location, $expire){
|
||||
header("Content-Type: application/octet-stream");
|
||||
function InternalNginxRedirect($location, $expire, $info) : void {
|
||||
header("X-Accel-Redirect: /" . $location);
|
||||
if($info->IsLegacyUpload) {
|
||||
header("Content-Type: $info->LegacyMime");
|
||||
header("Content-Disposition: inline; filename=\"$info->LegacyFilename\"");
|
||||
} else {
|
||||
header("Content-Type: application/octet-stream");
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
@ -7,5 +7,9 @@
|
||||
public $Uploaded;
|
||||
public $Size;
|
||||
public $DownloadHost;
|
||||
|
||||
public $IsLegacyUpload;
|
||||
public $LegacyFilename;
|
||||
public $LegacyMime;
|
||||
}
|
||||
?>
|
@ -22,13 +22,26 @@
|
||||
$redis = StaticRedis::ReadOp();
|
||||
$file_key = REDIS_PREFIX . $id;
|
||||
|
||||
$public_file_info = $redis->hMGet($file_key, array('views', 'lastview'));
|
||||
$public_file_info = $redis->hMGet($file_key, array('views', 'lastview', 'islegacy', 'filename', 'mime'));
|
||||
return (object)array(
|
||||
"views" => ($public_file_info["views"] !== False ? $public_file_info["views"] : 0),
|
||||
"lastview" => ($public_file_info["lastview"] !== False ? $public_file_info["lastview"] : 0)
|
||||
"lastview" => ($public_file_info["lastview"] !== False ? $public_file_info["lastview"] : 0),
|
||||
"islegacy" => ($public_file_info["islegacy"] !== False ? $public_file_info["islegacy"] === "1" : false),
|
||||
"filename" => ($public_file_info["filename"] !== False ? $public_file_info["filename"] : ""),
|
||||
"mime" => ($public_file_info["mime"] !== False ? $public_file_info["mime"] : ""),
|
||||
);
|
||||
}
|
||||
|
||||
public function SetAsLegacyFile($info) : void {
|
||||
$redis = StaticRedis::WriteOp();
|
||||
$file_key = REDIS_PREFIX . $info->FileId;
|
||||
$redis->hMSet($file_key, array(
|
||||
'islegacy' => true,
|
||||
'filename' => $info->LegacyFilename,
|
||||
'mime' => $info->LegacyMime
|
||||
));
|
||||
}
|
||||
|
||||
public function GetUploadDirAbsolute() : string {
|
||||
return "$this->DocumentRoot/$this->UploadFolder";
|
||||
}
|
||||
@ -53,6 +66,10 @@
|
||||
$file->LastView = intval($stats->lastview);
|
||||
$file->Size = $file_stat["size"];
|
||||
$file->Uploaded = $file_stat["ctime"];
|
||||
|
||||
$file->IsLegacyUpload = $stats->islegacy;
|
||||
$file->LegacyFilename = $stats->filename;
|
||||
$file->LegacyMime = $stats->mime;
|
||||
|
||||
return $file;
|
||||
}
|
||||
|
@ -7,7 +7,7 @@
|
||||
}
|
||||
|
||||
class Upload implements RequestHandler {
|
||||
private $isMultipart = False;
|
||||
private $IsMultipart = False;
|
||||
|
||||
public function __construct() {
|
||||
Config::LoadConfig(array('max_upload_size', 'upload_folder', 'public_hash_algo'));
|
||||
@ -26,8 +26,10 @@
|
||||
}
|
||||
|
||||
public function HandleRequest() : void {
|
||||
header("Access-Control-Allow-Origin: " . $_SERVER["HTTP_ORIGIN"]);
|
||||
header("Access-Control-Allow-Method: POST");
|
||||
if(isset($_SERVER["HTTP_ORIGIN"])) {
|
||||
header("Access-Control-Allow-Origin: " . $_SERVER["HTTP_ORIGIN"]);
|
||||
header("Access-Control-Allow-Method: POST");
|
||||
}
|
||||
|
||||
$rsp = new UploadResponse();
|
||||
$file_size = $_SERVER["CONTENT_LENGTH"];
|
||||
@ -36,19 +38,41 @@
|
||||
$rsp->status = 1;
|
||||
$rsp->msg = "File is too large";
|
||||
} else {
|
||||
$bf = BlobFile::LoadHeader("php://input");
|
||||
$auth = new Auth();
|
||||
$token = $auth->GetBearerToken();
|
||||
|
||||
if($bf != null){
|
||||
//save upload
|
||||
$id = $this->SaveUpload($bf);
|
||||
if($token !== null) {
|
||||
if($auth->CheckApiToken($token)) {
|
||||
$id = $this->SaveLegacyUpload();
|
||||
|
||||
//sync to other servers
|
||||
$rsp->sync = $this->SyncFileUpload($id);
|
||||
$rsp->status = 200;
|
||||
$rsp->id = $id;
|
||||
//sync to other servers
|
||||
if($id !== null) {
|
||||
$rsp->sync = $this->SyncFileUpload($id);
|
||||
$rsp->status = 200;
|
||||
$rsp->id = $id;
|
||||
} else {
|
||||
$rsp->status = 3;
|
||||
$rsp->msg = "Legacy upload error";
|
||||
}
|
||||
} else {
|
||||
http_response_code(403);
|
||||
exit();
|
||||
}
|
||||
} else {
|
||||
$rsp->status = 2;
|
||||
$rsp->msg = "Invalid file header";
|
||||
$bf = BlobFile::LoadHeader("php://input");
|
||||
|
||||
if($bf != null){
|
||||
//save upload
|
||||
$id = $this->SaveUpload($bf);
|
||||
|
||||
//sync to other servers
|
||||
$rsp->sync = $this->SyncFileUpload($id);
|
||||
$rsp->status = 200;
|
||||
$rsp->id = $id;
|
||||
} else {
|
||||
$rsp->status = 2;
|
||||
$rsp->msg = "Invalid file header";
|
||||
}
|
||||
}
|
||||
}
|
||||
header('Content-Type: application/json');
|
||||
@ -81,6 +105,25 @@
|
||||
return $id;
|
||||
}
|
||||
|
||||
function SaveLegacyUpload() : ?string {
|
||||
if(isset($_SERVER["HTTP_X_LEGACY_FILENAME"])){
|
||||
$hash = hash_file("sha256", "php://input");
|
||||
$id = gmp_strval(gmp_init("0x" . hash(Config::$Instance->public_hash_algo, $hash)), 62);
|
||||
|
||||
$fs = new FileStore(Config::$Instance->upload_folder);
|
||||
$fs->StoreFile("php://input", $id);
|
||||
|
||||
$info = new FileInfo();
|
||||
$info->FileId = $id;
|
||||
$info->LegacyFilename = $_SERVER["HTTP_X_LEGACY_FILENAME"];
|
||||
$info->LegacyMime = isset($_SERVER["CONTENT_TYPE"]) ? $_SERVER["CONTENT_TYPE"] : "application/octet-stream";
|
||||
|
||||
$fs->SetAsLegacyFile($info);
|
||||
return $id;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function GetUploadHost() : string {
|
||||
$cont = geoip_continent_code_by_name(USER_IP);
|
||||
if($cont === False){
|
||||
|
@ -9,7 +9,7 @@ namespace void_util
|
||||
class Program
|
||||
{
|
||||
public static string BaseHostname => "v3.void.cat";
|
||||
public static string UserAgent => "VoidUtil/1.0";
|
||||
public static string UserAgent => "VoidUtil/1.1";
|
||||
|
||||
static void PrintHelp()
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user