Enabled legacy uploads

This commit is contained in:
Kieran 2018-12-18 17:16:05 +08:00
parent 991d8a841d
commit 3a9f0c4890
10 changed files with 181 additions and 55 deletions

View File

@ -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];

View File

@ -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;

View File

@ -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;

View File

@ -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
View 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);
}
}
?>

View File

@ -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");
}
}
}
?>

View File

@ -7,5 +7,9 @@
public $Uploaded;
public $Size;
public $DownloadHost;
public $IsLegacyUpload;
public $LegacyFilename;
public $LegacyMime;
}
?>

View File

@ -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;
}

View 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){

View File

@ -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()
{