mirror of
https://git.v0l.io/Kieran/void.cat.git
synced 2025-03-27 00:21:42 +01:00
set lastview on upload
This commit is contained in:
parent
6a5395f244
commit
196bec4412
@ -1,46 +1,46 @@
|
||||
<?php
|
||||
class Abuse {
|
||||
public function CheckDownload($id) {
|
||||
$redis = StaticRedis::WriteOp();
|
||||
$key = REDIS_PREFIX . "uvc:" . USER_IP;
|
||||
|
||||
$views = $redis->hGet($key, $id);
|
||||
if($views !== False) {
|
||||
if($views >= Config::$Instance->download_captcha_check * 2) {
|
||||
|
||||
} else if($views >= Config::$Instance->download_captcha_check) {
|
||||
http_response_code(429); // Too many requests, tell the client to do captcha check
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
$redis->hIncrBy($key, $id, 1);
|
||||
}
|
||||
|
||||
public function VerifyCaptcha($token) : ?object {
|
||||
if(isset(Config::$Instance->recaptcha_secret)) {
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, 'https://www.google.com/recaptcha/api/siteverify');
|
||||
curl_setopt($ch, CURLOPT_POST, 1);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, array(
|
||||
"secret" => Config::$Instance->recaptcha_secret,
|
||||
"response" => $token,
|
||||
"remoteip" => USER_IP
|
||||
));
|
||||
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
$crsp = json_decode(curl_exec($ch));
|
||||
curl_close ($ch);
|
||||
|
||||
return $crsp;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function ResetRateLimits($id) {
|
||||
$redis = StaticRedis::WriteOp();
|
||||
$key = REDIS_PREFIX . "uvc:" . USER_IP;
|
||||
$redis->hSet($key, $id, 0);
|
||||
}
|
||||
}
|
||||
<?php
|
||||
class Abuse {
|
||||
public function CheckDownload($id) {
|
||||
$redis = StaticRedis::WriteOp();
|
||||
$key = REDIS_PREFIX . "uvc:" . USER_IP;
|
||||
|
||||
$views = $redis->hGet($key, $id);
|
||||
if($views !== False) {
|
||||
if($views >= Config::$Instance->download_captcha_check * 2) {
|
||||
|
||||
} else if($views >= Config::$Instance->download_captcha_check) {
|
||||
http_response_code(429); // Too many requests, tell the client to do captcha check
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
$redis->hIncrBy($key, $id, 1);
|
||||
}
|
||||
|
||||
public function VerifyCaptcha($token) : ?object {
|
||||
if(isset(Config::$Instance->recaptcha_secret)) {
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, 'https://www.google.com/recaptcha/api/siteverify');
|
||||
curl_setopt($ch, CURLOPT_POST, 1);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, array(
|
||||
"secret" => Config::$Instance->recaptcha_secret,
|
||||
"response" => $token,
|
||||
"remoteip" => USER_IP
|
||||
));
|
||||
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
$crsp = json_decode(curl_exec($ch));
|
||||
curl_close ($ch);
|
||||
|
||||
return $crsp;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function ResetRateLimits($id) {
|
||||
$redis = StaticRedis::WriteOp();
|
||||
$key = REDIS_PREFIX . "uvc:" . USER_IP;
|
||||
$redis->hSet($key, $id, 0);
|
||||
}
|
||||
}
|
||||
?>
|
@ -1,9 +1,9 @@
|
||||
<?php
|
||||
|
||||
class Admin implements RequestHandler {
|
||||
public function HandleRequest() : void {
|
||||
include(dirname(__FILE__) . "/../admin/index.html");
|
||||
}
|
||||
}
|
||||
|
||||
<?php
|
||||
|
||||
class Admin implements RequestHandler {
|
||||
public function HandleRequest() : void {
|
||||
include(dirname(__FILE__) . "/../admin/index.html");
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
176
src/php/api.php
176
src/php/api.php
@ -1,88 +1,88 @@
|
||||
<?php
|
||||
class ApiResponse {
|
||||
public $ok = false;
|
||||
public $msg;
|
||||
public $data;
|
||||
public $cmd;
|
||||
}
|
||||
|
||||
class Api implements RequestHandler {
|
||||
|
||||
public function __construct(){
|
||||
Config::LoadConfig(array('max_upload_size', 'upload_folder', 'recaptcha_site_key', 'recaptcha_secret'));
|
||||
|
||||
ini_set('enable_post_data_reading', 0);
|
||||
}
|
||||
|
||||
public function HandleRequest() : void {
|
||||
$cmd = json_decode(file_get_contents("php://input"));
|
||||
|
||||
$rsp = new ApiResponse();
|
||||
$rsp->cmd = $cmd;
|
||||
|
||||
$fs = new FileStore(Config::$Instance->upload_folder);
|
||||
|
||||
switch($cmd->cmd){
|
||||
case "site_info": {
|
||||
$rsp->ok = true;
|
||||
$rsp->data = array(
|
||||
"max_upload_size" => Config::$Instance->max_upload_size,
|
||||
"basic_stats" => Stats::Get(),
|
||||
"upload_host" => Upload::GetUploadHost(),
|
||||
"geoip_info" => geoip_database_info(),
|
||||
"host" => gethostname()
|
||||
);
|
||||
break;
|
||||
}
|
||||
case "file_info": {
|
||||
$rsp->ok = true;
|
||||
$rsp->data = $fs->GetFileInfo($cmd->id);
|
||||
//$rsp->data->DownloadHost = Upload::GetUploadHost(); //bypass CF proxy for downloads (slow..)
|
||||
break;
|
||||
}
|
||||
case 'captcha_info': {
|
||||
if(isset(Config::$Instance->recaptcha_site_key) && Config::$Instance->recaptcha_site_key !== False && isset(Config::$Instance->recaptcha_secret) && Config::$Instance->recaptcha_secret !== False) {
|
||||
$rsp->ok = true;
|
||||
$rsp->data = array(
|
||||
"site_key" => Config::$Instance->recaptcha_site_key
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "verify_captcha_rate_limit": {
|
||||
$abuse = new Abuse();
|
||||
$rsp->data = $abuse->VerifyCaptcha($cmd->token);
|
||||
if($rsp->data !== null && $rsp->data->success) {
|
||||
$abuse->ResetRateLimits($cmd->id);
|
||||
$rsp->ok = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "7_day_tx_graph": {
|
||||
$stats = Stats::GetTxStats(24 * 7);
|
||||
$data = array();
|
||||
foreach($stats as $time => $bytes){
|
||||
$data[] = array(
|
||||
"t" => date_timestamp_get(date_create_from_format("YmdH", $time)) * 1000,
|
||||
"y" => $bytes
|
||||
);
|
||||
}
|
||||
|
||||
$rsp->data = array(
|
||||
"datasets" => array(
|
||||
array(
|
||||
"label" => "bytes",
|
||||
"data" => $data
|
||||
)
|
||||
)
|
||||
);
|
||||
$rsp->ok = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode($rsp);
|
||||
}
|
||||
}
|
||||
?>
|
||||
<?php
|
||||
class ApiResponse {
|
||||
public $ok = false;
|
||||
public $msg;
|
||||
public $data;
|
||||
public $cmd;
|
||||
}
|
||||
|
||||
class Api implements RequestHandler {
|
||||
|
||||
public function __construct(){
|
||||
Config::LoadConfig(array('max_upload_size', 'upload_folder', 'recaptcha_site_key', 'recaptcha_secret'));
|
||||
|
||||
ini_set('enable_post_data_reading', 0);
|
||||
}
|
||||
|
||||
public function HandleRequest() : void {
|
||||
$cmd = json_decode(file_get_contents("php://input"));
|
||||
|
||||
$rsp = new ApiResponse();
|
||||
$rsp->cmd = $cmd;
|
||||
|
||||
$fs = new FileStore(Config::$Instance->upload_folder);
|
||||
|
||||
switch($cmd->cmd){
|
||||
case "site_info": {
|
||||
$rsp->ok = true;
|
||||
$rsp->data = array(
|
||||
"max_upload_size" => Config::$Instance->max_upload_size,
|
||||
"basic_stats" => Stats::Get(),
|
||||
"upload_host" => Upload::GetUploadHost(),
|
||||
"geoip_info" => geoip_database_info(),
|
||||
"host" => gethostname()
|
||||
);
|
||||
break;
|
||||
}
|
||||
case "file_info": {
|
||||
$rsp->ok = true;
|
||||
$rsp->data = $fs->GetFileInfo($cmd->id);
|
||||
//$rsp->data->DownloadHost = Upload::GetUploadHost(); //bypass CF proxy for downloads (slow..)
|
||||
break;
|
||||
}
|
||||
case 'captcha_info': {
|
||||
if(isset(Config::$Instance->recaptcha_site_key) && Config::$Instance->recaptcha_site_key !== False && isset(Config::$Instance->recaptcha_secret) && Config::$Instance->recaptcha_secret !== False) {
|
||||
$rsp->ok = true;
|
||||
$rsp->data = array(
|
||||
"site_key" => Config::$Instance->recaptcha_site_key
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "verify_captcha_rate_limit": {
|
||||
$abuse = new Abuse();
|
||||
$rsp->data = $abuse->VerifyCaptcha($cmd->token);
|
||||
if($rsp->data !== null && $rsp->data->success) {
|
||||
$abuse->ResetRateLimits($cmd->id);
|
||||
$rsp->ok = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "7_day_tx_graph": {
|
||||
$stats = Stats::GetTxStats(24 * 7);
|
||||
$data = array();
|
||||
foreach($stats as $time => $bytes){
|
||||
$data[] = array(
|
||||
"t" => date_timestamp_get(date_create_from_format("YmdH", $time)) * 1000,
|
||||
"y" => $bytes
|
||||
);
|
||||
}
|
||||
|
||||
$rsp->data = array(
|
||||
"datasets" => array(
|
||||
array(
|
||||
"label" => "bytes",
|
||||
"data" => $data
|
||||
)
|
||||
)
|
||||
);
|
||||
$rsp->ok = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode($rsp);
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
@ -1,38 +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);
|
||||
}
|
||||
}
|
||||
<?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);
|
||||
}
|
||||
}
|
||||
?>
|
@ -1,40 +1,41 @@
|
||||
<?php
|
||||
class BlobFile {
|
||||
public $Version;
|
||||
public $Hash;
|
||||
public $Uploaded;
|
||||
|
||||
public static function LoadHeader($path) : ?BlobFile {
|
||||
$input = fopen($path, "rb");
|
||||
$version = ord(fread($input, 1));
|
||||
//error_log($version);
|
||||
|
||||
$bf = new BlobFile();
|
||||
if($version == 1) {
|
||||
$header = fread($input, 36); //+32 byte hash (64 hex digits) + 4 byte timestamp
|
||||
fclose($input);
|
||||
$header_data = unpack("H64hash256/Vuploaded", $header);
|
||||
|
||||
$bf->Version = 1;
|
||||
$bf->Hash = $header_data["hash256"];
|
||||
$bf->Uploaded = $header_data["uploaded"];
|
||||
return $bf;
|
||||
} elseif($version == 2) {
|
||||
$header = fread($input, 11); //+7 magic bytes + 4 byte timestamp
|
||||
$header_data = unpack("H14magic/Vuploaded", $header);
|
||||
fclose($input);
|
||||
|
||||
//error_log("Magic is: " . $header_data["magic"]);
|
||||
if($header_data["magic"] == "4f4944f09f90b1") { //OID🐱 as hex (UTF-8)
|
||||
$bf->Version = 2;
|
||||
$bf->Uploaded = $header_data["uploaded"];
|
||||
return $bf;
|
||||
}
|
||||
} else {
|
||||
fclose($input);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
<?php
|
||||
class BlobFile {
|
||||
public $Version;
|
||||
public $Hash;
|
||||
public $Uploaded;
|
||||
|
||||
public static function LoadHeader($path) : ?BlobFile {
|
||||
$input = fopen($path, "rb");
|
||||
if ($input !== false) {
|
||||
$version = ord(fread($input, 1));
|
||||
//error_log($version);
|
||||
|
||||
$bf = new BlobFile();
|
||||
if($version == 1) {
|
||||
$header = fread($input, 36); //+32 byte hash (64 hex digits) + 4 byte timestamp
|
||||
fclose($input);
|
||||
$header_data = unpack("H64hash256/Vuploaded", $header);
|
||||
|
||||
$bf->Version = 1;
|
||||
$bf->Hash = $header_data["hash256"];
|
||||
$bf->Uploaded = $header_data["uploaded"];
|
||||
return $bf;
|
||||
} elseif($version == 2) {
|
||||
$header = fread($input, 11); //+7 magic bytes + 4 byte timestamp
|
||||
$header_data = unpack("H14magic/Vuploaded", $header);
|
||||
fclose($input);
|
||||
|
||||
//error_log("Magic is: " . $header_data["magic"]);
|
||||
if($header_data["magic"] == "4f4944f09f90b1") { //OID🐱 as hex (UTF-8)
|
||||
$bf->Version = 2;
|
||||
$bf->Uploaded = $header_data["uploaded"];
|
||||
return $bf;
|
||||
}
|
||||
} else {
|
||||
fclose($input);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
?>
|
@ -1,33 +1,33 @@
|
||||
<?php
|
||||
class Config {
|
||||
public static $Instance;
|
||||
|
||||
public static function GetConfig($config_name) {
|
||||
$redis = StaticRedis::ReadOp();
|
||||
return $redis->hGet(REDIS_PREFIX . 'config', $config_name);
|
||||
}
|
||||
|
||||
public static function MGetConfig($config_name) {
|
||||
$redis = StaticRedis::ReadOp();
|
||||
return (object)$redis->hMGet(REDIS_PREFIX . 'config', $config_name);
|
||||
}
|
||||
|
||||
public static function LoadConfig($config_name){
|
||||
self::$Instance = self::MGetConfig($config_name);
|
||||
|
||||
//set defaults
|
||||
if(!isset(self::$Instance->upload_folder) || self::$Instance->upload_folder == False) {
|
||||
self::$Instance->upload_folder = "out";
|
||||
}
|
||||
if(!isset(self::$Instance->public_hash_algo) || self::$Instance->public_hash_algo == False) {
|
||||
self::$Instance->public_hash_algo = "ripemd160";
|
||||
}
|
||||
if(!isset(self::$Instance->max_upload_size) || self::$Instance->max_upload_size == False) {
|
||||
self::$Instance->max_upload_size = 104857600; //100MiB is the default upload size
|
||||
}
|
||||
if(!isset(self::$Instance->download_captcha_check) || self::$Instance->download_captcha_check == False) {
|
||||
self::$Instance->download_captcha_check = 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
<?php
|
||||
class Config {
|
||||
public static $Instance;
|
||||
|
||||
public static function GetConfig($config_name) {
|
||||
$redis = StaticRedis::ReadOp();
|
||||
return $redis->hGet(REDIS_PREFIX . 'config', $config_name);
|
||||
}
|
||||
|
||||
public static function MGetConfig($config_name) {
|
||||
$redis = StaticRedis::ReadOp();
|
||||
return (object)$redis->hMGet(REDIS_PREFIX . 'config', $config_name);
|
||||
}
|
||||
|
||||
public static function LoadConfig($config_name){
|
||||
self::$Instance = self::MGetConfig($config_name);
|
||||
|
||||
//set defaults
|
||||
if(!isset(self::$Instance->upload_folder) || self::$Instance->upload_folder == False) {
|
||||
self::$Instance->upload_folder = "out";
|
||||
}
|
||||
if(!isset(self::$Instance->public_hash_algo) || self::$Instance->public_hash_algo == False) {
|
||||
self::$Instance->public_hash_algo = "ripemd160";
|
||||
}
|
||||
if(!isset(self::$Instance->max_upload_size) || self::$Instance->max_upload_size == False) {
|
||||
self::$Instance->max_upload_size = 104857600; //100MiB is the default upload size
|
||||
}
|
||||
if(!isset(self::$Instance->download_captcha_check) || self::$Instance->download_captcha_check == False) {
|
||||
self::$Instance->download_captcha_check = 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
122
src/php/cron.php
122
src/php/cron.php
@ -1,62 +1,62 @@
|
||||
<?php
|
||||
include_once("init.php");
|
||||
|
||||
if(StaticRedis::Connect()) {
|
||||
echo "Connected to redis..\n";
|
||||
|
||||
Config::LoadConfig(array("upload_folder", "discord_webhook_pub"));
|
||||
$fs = new FileStore(Config::$Instance->upload_folder, $_SERVER["cron_root"]);
|
||||
|
||||
//delete expired files
|
||||
$pmsg = "`" . gethostname() . ":\n";
|
||||
$redis = StaticRedis::ReadOp();
|
||||
$deleted = false;
|
||||
foreach($fs->ListFiles() as $file) {
|
||||
$id = basename($file);
|
||||
$file_key = REDIS_PREFIX . $id;
|
||||
$lv = $redis->hGet($file_key, "lastview");
|
||||
$expire = time() - (30 * 24 * 60 * 60);
|
||||
|
||||
//use the file upload timestamp if there is no view data recorded
|
||||
//if the file upload time is greater than the current timestamp, mark as old (!!abuse!!)
|
||||
//this will also force legacy file uploads with no views to be deleted (header will always fail to load)
|
||||
if($lv === false) {
|
||||
$file_header = BlobFile::LoadHeader($file);
|
||||
if($file_header !== null && $file_header->Uploaded <= time()){
|
||||
$lv = $file_header->Uploaded;
|
||||
} else {
|
||||
//cant read file header or upload timestamp is invalid, mark as old
|
||||
$lv = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if($lv !== false && intval($lv) < $expire) {
|
||||
$nmsg = "Deleting expired file: " . $id . " (lastview=" . date("Y-m-d h:i:s", intval($lv)) . ")\n";
|
||||
if(strlen($pmsg) + strlen($nmsg) >= 2000){
|
||||
//send to discord public hook
|
||||
$pmsg = $pmsg . "`";
|
||||
Discord::SendPublic(array(
|
||||
"content" => $pmsg
|
||||
));
|
||||
$pmsg = "`";
|
||||
}
|
||||
$pmsg = $pmsg . $nmsg;
|
||||
unlink($file);
|
||||
$deleted = true;
|
||||
}
|
||||
}
|
||||
|
||||
//send last message if any
|
||||
if(strlen($pmsg) > 0 && $deleted) {
|
||||
$pmsg = $pmsg . "`";
|
||||
Discord::SendPublic(array(
|
||||
"content" => $pmsg
|
||||
));
|
||||
}
|
||||
|
||||
if(StaticRedis::$IsConnectedToSlave == False) {
|
||||
echo "Runing master node tasks..\n";
|
||||
Stats::Collect($fs);
|
||||
}
|
||||
}
|
||||
<?php
|
||||
include_once("init.php");
|
||||
|
||||
if(StaticRedis::Connect()) {
|
||||
echo "Connected to redis..\n";
|
||||
|
||||
Config::LoadConfig(array("upload_folder", "discord_webhook_pub"));
|
||||
$fs = new FileStore(Config::$Instance->upload_folder, $_SERVER["cron_root"]);
|
||||
|
||||
//delete expired files
|
||||
$pmsg = "`" . gethostname() . ":\n";
|
||||
$redis = StaticRedis::ReadOp();
|
||||
$deleted = false;
|
||||
foreach($fs->ListFiles() as $file) {
|
||||
$id = basename($file);
|
||||
$file_key = REDIS_PREFIX . $id;
|
||||
$lv = $redis->hGet($file_key, "lastview");
|
||||
$expire = time() - (30 * 24 * 60 * 60);
|
||||
|
||||
//use the file upload timestamp if there is no view data recorded
|
||||
//if the file upload time is greater than the current timestamp, mark as old (!!abuse!!)
|
||||
//this will also force legacy file uploads with no views to be deleted (header will always fail to load)
|
||||
if($lv === false) {
|
||||
$file_header = BlobFile::LoadHeader($file);
|
||||
if($file_header !== null && $file_header->Uploaded <= time()){
|
||||
$lv = $file_header->Uploaded;
|
||||
} else {
|
||||
//cant read file header or upload timestamp is invalid, mark as old
|
||||
$lv = $file_header !== null ? $file_header->Uploaded : 0;
|
||||
}
|
||||
}
|
||||
|
||||
if($lv !== false && (intval($lv) < $expire) || intval($lv) > time()) {
|
||||
$nmsg = "Deleting expired file: " . $id . " (lastview=" . date("Y-m-d h:i:s", intval($lv)) . ")\n";
|
||||
if(strlen($pmsg) + strlen($nmsg) >= 2000){
|
||||
//send to discord public hook
|
||||
$pmsg = $pmsg . "`";
|
||||
Discord::SendPublic(array(
|
||||
"content" => $pmsg
|
||||
));
|
||||
$pmsg = "`";
|
||||
}
|
||||
$pmsg = $pmsg . $nmsg;
|
||||
unlink($file);
|
||||
$deleted = true;
|
||||
}
|
||||
}
|
||||
|
||||
//send last message if any
|
||||
if(strlen($pmsg) > 0 && $deleted) {
|
||||
$pmsg = $pmsg . "`";
|
||||
Discord::SendPublic(array(
|
||||
"content" => $pmsg
|
||||
));
|
||||
}
|
||||
|
||||
if(StaticRedis::$IsConnectedToSlave == False) {
|
||||
echo "Runing master node tasks..\n";
|
||||
Stats::Collect($fs);
|
||||
}
|
||||
}
|
||||
?>
|
@ -1,26 +1,26 @@
|
||||
<?php
|
||||
|
||||
class Discord {
|
||||
public static function SendPublic($msg) : void {
|
||||
self::CallWebhook(Config::$Instance->discord_webhook_pub, $msg);
|
||||
}
|
||||
|
||||
private static function CallWebhook($url, $data) : void {
|
||||
self::CurlPost($url, json_encode($data));
|
||||
}
|
||||
|
||||
private static function CurlPost($url, $data) : ?string {
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
|
||||
|
||||
$result = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
<?php
|
||||
|
||||
class Discord {
|
||||
public static function SendPublic($msg) : void {
|
||||
self::CallWebhook(Config::$Instance->discord_webhook_pub, $msg);
|
||||
}
|
||||
|
||||
private static function CallWebhook($url, $data) : void {
|
||||
self::CurlPost($url, json_encode($data));
|
||||
}
|
||||
|
||||
private static function CurlPost($url, $data) : ?string {
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
|
||||
|
||||
$result = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
@ -1,56 +1,56 @@
|
||||
<?php
|
||||
class Download implements RequestHandler {
|
||||
private $Config;
|
||||
private $Fs;
|
||||
|
||||
public function __construct() {
|
||||
Config::LoadConfig(array("upload_folder", "download_captcha_check"));
|
||||
}
|
||||
|
||||
public function HandleRequest() : void {
|
||||
$ref = isset($_SERVER["HTTP_REFERER"]) ? $_SERVER["HTTP_REFERER"] : null;
|
||||
$id = isset($_REQUEST["id"]) ? $_REQUEST["id"] : null;
|
||||
|
||||
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);
|
||||
}
|
||||
} else {
|
||||
http_response_code(404);
|
||||
}
|
||||
}
|
||||
|
||||
function StartDownload($id, $info) : void {
|
||||
$abuse = new Abuse();
|
||||
$tracking = new Tracking();
|
||||
|
||||
header("Cache-Control: private");
|
||||
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);
|
||||
|
||||
if($_SERVER["REQUEST_METHOD"] === "GET") {
|
||||
$this->InternalNginxRedirect($this->Fs->GetRelativeFilePath($id), 604800, $info);
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
<?php
|
||||
class Download implements RequestHandler {
|
||||
private $Config;
|
||||
private $Fs;
|
||||
|
||||
public function __construct() {
|
||||
Config::LoadConfig(array("upload_folder", "download_captcha_check"));
|
||||
}
|
||||
|
||||
public function HandleRequest() : void {
|
||||
$ref = isset($_SERVER["HTTP_REFERER"]) ? $_SERVER["HTTP_REFERER"] : null;
|
||||
$id = isset($_REQUEST["id"]) ? $_REQUEST["id"] : null;
|
||||
|
||||
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);
|
||||
}
|
||||
} else {
|
||||
http_response_code(404);
|
||||
}
|
||||
}
|
||||
|
||||
function StartDownload($id, $info) : void {
|
||||
$abuse = new Abuse();
|
||||
$tracking = new Tracking();
|
||||
|
||||
header("Cache-Control: private");
|
||||
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);
|
||||
|
||||
if($_SERVER["REQUEST_METHOD"] === "GET") {
|
||||
$this->InternalNginxRedirect($this->Fs->GetRelativeFilePath($id), 604800, $info);
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
@ -1,15 +1,15 @@
|
||||
<?php
|
||||
class FileInfo {
|
||||
public $FileId;
|
||||
public $Views;
|
||||
public $Hash;
|
||||
public $LastView;
|
||||
public $Uploaded;
|
||||
public $Size;
|
||||
public $DownloadHost;
|
||||
|
||||
public $IsLegacyUpload;
|
||||
public $LegacyFilename;
|
||||
public $LegacyMime;
|
||||
}
|
||||
<?php
|
||||
class FileInfo {
|
||||
public $FileId;
|
||||
public $Views;
|
||||
public $Hash;
|
||||
public $LastView;
|
||||
public $Uploaded;
|
||||
public $Size;
|
||||
public $DownloadHost;
|
||||
|
||||
public $IsLegacyUpload;
|
||||
public $LegacyFilename;
|
||||
public $LegacyMime;
|
||||
}
|
||||
?>
|
@ -1,135 +1,135 @@
|
||||
<?php
|
||||
class FileStore {
|
||||
private $UploadFolder;
|
||||
private $DocumentRoot;
|
||||
|
||||
public function __construct($dir, $root = null) {
|
||||
$this->UploadFolder = $dir;
|
||||
$this->DocumentRoot = $root === null ? $_SERVER["DOCUMENT_ROOT"] : $root;
|
||||
}
|
||||
|
||||
public function SetFileStats($info) : void {
|
||||
$redis = StaticRedis::WriteOp();
|
||||
$file_key = REDIS_PREFIX . $info->FileId;
|
||||
|
||||
$redis->hMSet($file_key, array(
|
||||
'views' => $info->Views,
|
||||
'lastview' => $info->LastView
|
||||
));
|
||||
}
|
||||
|
||||
public function GetFileStats($id) : object {
|
||||
$redis = StaticRedis::ReadOp();
|
||||
$file_key = REDIS_PREFIX . $id;
|
||||
|
||||
$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),
|
||||
"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";
|
||||
}
|
||||
|
||||
public function GetRelativeFilePath($id) : string {
|
||||
return "$this->UploadFolder/$id";
|
||||
}
|
||||
|
||||
public function GetAbsoluteFilePath($id) : string {
|
||||
return $this->GetUploadDirAbsolute() . "/" . $id;
|
||||
}
|
||||
|
||||
public function GetFileInfo($id) : ?FileInfo {
|
||||
$file_path = $this->GetAbsoluteFilePath($id);
|
||||
if($this->FileExists($id)) {
|
||||
$stats = $this->GetFileStats($id);
|
||||
$file_stat = stat($file_path);
|
||||
|
||||
$file = new FileInfo();
|
||||
$file->FileId = $id;
|
||||
$file->Views = intval($stats->views);
|
||||
$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;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
public function FileExists($id) : bool {
|
||||
$file_path = $this->GetAbsoluteFilePath($id);
|
||||
return file_exists($file_path);
|
||||
}
|
||||
|
||||
public function StoreFile($file, $id) : bool {
|
||||
$file_path = $this->GetAbsoluteFilePath($id);
|
||||
|
||||
if(!file_exists($file_path)) {
|
||||
$fout = fopen($file_path, 'wb+');
|
||||
stream_copy_to_stream($file, $fout);
|
||||
fclose($fout);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function StoreV1File($bf, $file) : ?string {
|
||||
$id = gmp_strval(gmp_init("0x" . hash(Config::$Instance->public_hash_algo, $bf->Hash)), 62);
|
||||
|
||||
$input = fopen($file, "rb");
|
||||
$res = $this->StoreFile($input, $id);
|
||||
fclose($input);
|
||||
|
||||
return $res ? $id : null;
|
||||
}
|
||||
|
||||
public function StoreV2File($bf, $file) : ?string {
|
||||
//we need to seek to the end before finding the id, do that first
|
||||
$input = fopen($file, "rb");
|
||||
$temp_name = tempnam($this->GetUploadDirAbsolute(), "VTMP_");
|
||||
$input_temp = fopen($temp_name, "wb+");
|
||||
stream_copy_to_stream($input, $input_temp);
|
||||
fclose($input);
|
||||
|
||||
fseek($input_temp, -32, SEEK_END);
|
||||
$hash = unpack("H64hash256", fread($input_temp, 32));
|
||||
fclose($input_temp);
|
||||
|
||||
$id = gmp_strval(gmp_init("0x" . hash(Config::$Instance->public_hash_algo, $hash["hash256"])), 62);
|
||||
$file_path = $this->GetAbsoluteFilePath($id);
|
||||
if(!file_exists($file_path)){
|
||||
rename($temp_name, $file_path);
|
||||
return $id;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function GetFileSize($id) : int {
|
||||
return filesize($this->GetAbsoluteFilePath($id));
|
||||
}
|
||||
|
||||
public function ListFiles() : array {
|
||||
return glob($this->GetUploadDirAbsolute() . "/*");
|
||||
}
|
||||
}
|
||||
<?php
|
||||
class FileStore {
|
||||
private $UploadFolder;
|
||||
private $DocumentRoot;
|
||||
|
||||
public function __construct($dir, $root = null) {
|
||||
$this->UploadFolder = $dir;
|
||||
$this->DocumentRoot = $root === null ? $_SERVER["DOCUMENT_ROOT"] : $root;
|
||||
}
|
||||
|
||||
public function SetFileStats($info) : void {
|
||||
$redis = StaticRedis::WriteOp();
|
||||
$file_key = REDIS_PREFIX . $info->FileId;
|
||||
|
||||
$redis->hMSet($file_key, array(
|
||||
'views' => $info->Views,
|
||||
'lastview' => $info->LastView
|
||||
));
|
||||
}
|
||||
|
||||
public function GetFileStats($id) : object {
|
||||
$redis = StaticRedis::ReadOp();
|
||||
$file_key = REDIS_PREFIX . $id;
|
||||
|
||||
$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),
|
||||
"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";
|
||||
}
|
||||
|
||||
public function GetRelativeFilePath($id) : string {
|
||||
return "$this->UploadFolder/$id";
|
||||
}
|
||||
|
||||
public function GetAbsoluteFilePath($id) : string {
|
||||
return $this->GetUploadDirAbsolute() . "/" . $id;
|
||||
}
|
||||
|
||||
public function GetFileInfo($id) : ?FileInfo {
|
||||
$file_path = $this->GetAbsoluteFilePath($id);
|
||||
if($this->FileExists($id)) {
|
||||
$stats = $this->GetFileStats($id);
|
||||
$file_stat = stat($file_path);
|
||||
|
||||
$file = new FileInfo();
|
||||
$file->FileId = $id;
|
||||
$file->Views = intval($stats->views);
|
||||
$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;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
public function FileExists($id) : bool {
|
||||
$file_path = $this->GetAbsoluteFilePath($id);
|
||||
return file_exists($file_path);
|
||||
}
|
||||
|
||||
public function StoreFile($file, $id) : bool {
|
||||
$file_path = $this->GetAbsoluteFilePath($id);
|
||||
|
||||
if(!file_exists($file_path)) {
|
||||
$fout = fopen($file_path, 'wb+');
|
||||
stream_copy_to_stream($file, $fout);
|
||||
fclose($fout);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function StoreV1File($bf, $file) : ?string {
|
||||
$id = gmp_strval(gmp_init("0x" . hash(Config::$Instance->public_hash_algo, $bf->Hash)), 62);
|
||||
|
||||
$input = fopen($file, "rb");
|
||||
$res = $this->StoreFile($input, $id);
|
||||
fclose($input);
|
||||
|
||||
return $res ? $id : null;
|
||||
}
|
||||
|
||||
public function StoreV2File($bf, $file) : ?string {
|
||||
//we need to seek to the end before finding the id, do that first
|
||||
$input = fopen($file, "rb");
|
||||
$temp_name = tempnam($this->GetUploadDirAbsolute(), "VTMP_");
|
||||
$input_temp = fopen($temp_name, "wb+");
|
||||
stream_copy_to_stream($input, $input_temp);
|
||||
fclose($input);
|
||||
|
||||
fseek($input_temp, -32, SEEK_END);
|
||||
$hash = unpack("H64hash256", fread($input_temp, 32));
|
||||
fclose($input_temp);
|
||||
|
||||
$id = gmp_strval(gmp_init("0x" . hash(Config::$Instance->public_hash_algo, $hash["hash256"])), 62);
|
||||
$file_path = $this->GetAbsoluteFilePath($id);
|
||||
if(!file_exists($file_path)){
|
||||
rename($temp_name, $file_path);
|
||||
return $id;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function GetFileSize($id) : int {
|
||||
return filesize($this->GetAbsoluteFilePath($id));
|
||||
}
|
||||
|
||||
public function ListFiles() : array {
|
||||
return glob($this->GetUploadDirAbsolute() . "/*");
|
||||
}
|
||||
}
|
||||
?>
|
@ -1,25 +1,25 @@
|
||||
<?php
|
||||
include_once("init.php");
|
||||
|
||||
//Startup
|
||||
if(StaticRedis::Connect() == True) {
|
||||
Tracking::SendMatomoEvent();
|
||||
|
||||
if(isset($_REQUEST["h"])) {
|
||||
$handler_name = $_REQUEST["h"];
|
||||
if(file_exists($handler_name . '.php')){
|
||||
$handler = new $handler_name();
|
||||
if($handler instanceof RequestHandler){
|
||||
$handler->HandleRequest();
|
||||
exit();
|
||||
}
|
||||
}
|
||||
}
|
||||
//var_dump($_REQUEST);
|
||||
http_response_code(400);
|
||||
exit();
|
||||
} else {
|
||||
http_response_code(500);
|
||||
exit();
|
||||
}
|
||||
<?php
|
||||
include_once("init.php");
|
||||
|
||||
//Startup
|
||||
if(StaticRedis::Connect() == True) {
|
||||
Tracking::SendMatomoEvent();
|
||||
|
||||
if(isset($_REQUEST["h"])) {
|
||||
$handler_name = $_REQUEST["h"];
|
||||
if(file_exists($handler_name . '.php')){
|
||||
$handler = new $handler_name();
|
||||
if($handler instanceof RequestHandler){
|
||||
$handler->HandleRequest();
|
||||
exit();
|
||||
}
|
||||
}
|
||||
}
|
||||
//var_dump($_REQUEST);
|
||||
http_response_code(400);
|
||||
exit();
|
||||
} else {
|
||||
http_response_code(500);
|
||||
exit();
|
||||
}
|
||||
?>
|
@ -1,7 +1,7 @@
|
||||
<?php
|
||||
class Info implements RequestHandler {
|
||||
public function HandleRequest() : void {
|
||||
phpinfo();
|
||||
}
|
||||
}
|
||||
<?php
|
||||
class Info implements RequestHandler {
|
||||
public function HandleRequest() : void {
|
||||
phpinfo();
|
||||
}
|
||||
}
|
||||
?>
|
@ -1,13 +1,13 @@
|
||||
<?php
|
||||
define('REDIS_CONFIG', 'redis-host');
|
||||
define('REDIS_PREFIX', 'vc:');
|
||||
define('USER_IP', isset($_SERVER['HTTP_CF_CONNECTING_IP']) ? $_SERVER['HTTP_CF_CONNECTING_IP'] : (isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : "127.0.0.1"));
|
||||
|
||||
if(!isset($_COOKIE["VC:UID"])) {
|
||||
setcookie("VC:UID", uniqid());
|
||||
}
|
||||
|
||||
spl_autoload_register(function ($class_name) {
|
||||
include dirname(__FILE__) . '/' . strtolower($class_name) . '.php';
|
||||
});
|
||||
<?php
|
||||
define('REDIS_CONFIG', 'redis-host');
|
||||
define('REDIS_PREFIX', 'vc:');
|
||||
define('USER_IP', isset($_SERVER['HTTP_CF_CONNECTING_IP']) ? $_SERVER['HTTP_CF_CONNECTING_IP'] : (isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : "127.0.0.1"));
|
||||
|
||||
if(!isset($_COOKIE["VC:UID"])) {
|
||||
setcookie("VC:UID", uniqid());
|
||||
}
|
||||
|
||||
spl_autoload_register(function ($class_name) {
|
||||
include dirname(__FILE__) . '/' . strtolower($class_name) . '.php';
|
||||
});
|
||||
?>
|
@ -1,5 +1,5 @@
|
||||
<?php
|
||||
interface RequestHandler {
|
||||
public function HandleRequest() : void;
|
||||
}
|
||||
<?php
|
||||
interface RequestHandler {
|
||||
public function HandleRequest() : void;
|
||||
}
|
||||
?>
|
@ -1,35 +1,35 @@
|
||||
<?php
|
||||
|
||||
class StaticRedis {
|
||||
public static $Instance = null;
|
||||
public static $MasterInstance = null;
|
||||
public static $IsConnectedToSlave = false;
|
||||
|
||||
public static function ReadOp() : object {
|
||||
return self::$Instance;
|
||||
}
|
||||
|
||||
public static function WriteOp() : object {
|
||||
if(self::$MasterInstance != null){
|
||||
return self::$MasterInstance;
|
||||
} else {
|
||||
return self::$Instance;
|
||||
}
|
||||
}
|
||||
|
||||
public static function Connect() : bool {
|
||||
self::$Instance = new Redis();
|
||||
$con = self::$Instance->pconnect(REDIS_CONFIG);
|
||||
if($con){
|
||||
$rep = self::$Instance->info();
|
||||
if($rep["role"] == "slave"){
|
||||
self::$IsConnectedToSlave = true;
|
||||
self::$MasterInstance = new Redis();
|
||||
$mcon = self::$MasterInstance->pconnect($rep["master_host"], $rep["master_port"]);
|
||||
return $con && $mcon;
|
||||
}
|
||||
}
|
||||
return $con;
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
class StaticRedis {
|
||||
public static $Instance = null;
|
||||
public static $MasterInstance = null;
|
||||
public static $IsConnectedToSlave = false;
|
||||
|
||||
public static function ReadOp() : object {
|
||||
return self::$Instance;
|
||||
}
|
||||
|
||||
public static function WriteOp() : object {
|
||||
if(self::$MasterInstance != null){
|
||||
return self::$MasterInstance;
|
||||
} else {
|
||||
return self::$Instance;
|
||||
}
|
||||
}
|
||||
|
||||
public static function Connect() : bool {
|
||||
self::$Instance = new Redis();
|
||||
$con = self::$Instance->pconnect(REDIS_CONFIG);
|
||||
if($con){
|
||||
$rep = self::$Instance->info();
|
||||
if($rep["role"] == "slave"){
|
||||
self::$IsConnectedToSlave = true;
|
||||
self::$MasterInstance = new Redis();
|
||||
$mcon = self::$MasterInstance->pconnect($rep["master_host"], $rep["master_port"]);
|
||||
return $con && $mcon;
|
||||
}
|
||||
}
|
||||
return $con;
|
||||
}
|
||||
}
|
||||
?>
|
@ -1,79 +1,79 @@
|
||||
<?php
|
||||
class Stats {
|
||||
public $Files;
|
||||
public $Size;
|
||||
public $Transfer_24h;
|
||||
|
||||
private static $AllTransferStatsKey = REDIS_PREFIX . "stats-transfer-all:";
|
||||
private static $GeneralStatsKey = REDIS_PREFIX . "stats-general";
|
||||
|
||||
public static function GetTxStats($nHours) : array {
|
||||
$redis = StaticRedis::ReadOp();
|
||||
$ret = array();
|
||||
|
||||
$now = time();
|
||||
for($x = 0; $x < $nHours; $x += 1) {
|
||||
$stat_key = date("YmdH", $now - (60 * 60 * $x));
|
||||
$val = $redis->get(self::$AllTransferStatsKey . $stat_key);
|
||||
if($val != False){
|
||||
$ret[$stat_key] = intval($val);
|
||||
} else {
|
||||
$ret[$stat_key] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public static function Get() : Stats {
|
||||
$redis = StaticRedis::ReadOp();
|
||||
|
||||
//calculate 24hr transfer stats
|
||||
$tx_24h = 0;
|
||||
foreach(self::GetTxStats(24) as $time => $bytes) {
|
||||
$tx_24h += $bytes;
|
||||
}
|
||||
|
||||
//get general stats
|
||||
$general = (object)$redis->hMGet(self::$GeneralStatsKey, array("files", "size"));
|
||||
|
||||
$ret = new Stats();
|
||||
$ret->Transfer_24h = $tx_24h;
|
||||
$ret->Files = intval($general->files !== False ? $general->files : 0);
|
||||
$ret->Size = intval($general->size !== False ? $general->size : 0);
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public static function TrackTransfer($id, $size) : void {
|
||||
self::AddAllTransfer($size);
|
||||
}
|
||||
|
||||
public static function AddAllTransfer($size) : void {
|
||||
$redis = StaticRedis::WriteOp();
|
||||
$stat_member = date("YmdH");
|
||||
$redis->incrBy(self::$AllTransferStatsKey . $stat_member, $size);
|
||||
$redis->setTimeout(self::$AllTransferStatsKey . $stat_member, 2592000); //store 30 days only
|
||||
}
|
||||
|
||||
public static function Collect($fs) : void {
|
||||
$redis = StaticRedis::WriteOp();
|
||||
|
||||
$files = $fs->ListFiles();
|
||||
$total_size = 0;
|
||||
foreach($files as $file) {
|
||||
$total_size += filesize($file);
|
||||
}
|
||||
|
||||
$redis->hMSet(self::$GeneralStatsKey, array(
|
||||
"files" => count($files),
|
||||
"size" => $total_size
|
||||
));
|
||||
|
||||
//tick from cron job to create keys for every hour
|
||||
//if no downloads are happening we will be missing keys
|
||||
//this will prevent inaccurate reporting
|
||||
self::AddAllTransfer(0);
|
||||
}
|
||||
}
|
||||
<?php
|
||||
class Stats {
|
||||
public $Files;
|
||||
public $Size;
|
||||
public $Transfer_24h;
|
||||
|
||||
private static $AllTransferStatsKey = REDIS_PREFIX . "stats-transfer-all:";
|
||||
private static $GeneralStatsKey = REDIS_PREFIX . "stats-general";
|
||||
|
||||
public static function GetTxStats($nHours) : array {
|
||||
$redis = StaticRedis::ReadOp();
|
||||
$ret = array();
|
||||
|
||||
$now = time();
|
||||
for($x = 0; $x < $nHours; $x += 1) {
|
||||
$stat_key = date("YmdH", $now - (60 * 60 * $x));
|
||||
$val = $redis->get(self::$AllTransferStatsKey . $stat_key);
|
||||
if($val != False){
|
||||
$ret[$stat_key] = intval($val);
|
||||
} else {
|
||||
$ret[$stat_key] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public static function Get() : Stats {
|
||||
$redis = StaticRedis::ReadOp();
|
||||
|
||||
//calculate 24hr transfer stats
|
||||
$tx_24h = 0;
|
||||
foreach(self::GetTxStats(24) as $time => $bytes) {
|
||||
$tx_24h += $bytes;
|
||||
}
|
||||
|
||||
//get general stats
|
||||
$general = (object)$redis->hMGet(self::$GeneralStatsKey, array("files", "size"));
|
||||
|
||||
$ret = new Stats();
|
||||
$ret->Transfer_24h = $tx_24h;
|
||||
$ret->Files = intval($general->files !== False ? $general->files : 0);
|
||||
$ret->Size = intval($general->size !== False ? $general->size : 0);
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public static function TrackTransfer($id, $size) : void {
|
||||
self::AddAllTransfer($size);
|
||||
}
|
||||
|
||||
public static function AddAllTransfer($size) : void {
|
||||
$redis = StaticRedis::WriteOp();
|
||||
$stat_member = date("YmdH");
|
||||
$redis->incrBy(self::$AllTransferStatsKey . $stat_member, $size);
|
||||
$redis->setTimeout(self::$AllTransferStatsKey . $stat_member, 2592000); //store 30 days only
|
||||
}
|
||||
|
||||
public static function Collect($fs) : void {
|
||||
$redis = StaticRedis::WriteOp();
|
||||
|
||||
$files = $fs->ListFiles();
|
||||
$total_size = 0;
|
||||
foreach($files as $file) {
|
||||
$total_size += filesize($file);
|
||||
}
|
||||
|
||||
$redis->hMSet(self::$GeneralStatsKey, array(
|
||||
"files" => count($files),
|
||||
"size" => $total_size
|
||||
));
|
||||
|
||||
//tick from cron job to create keys for every hour
|
||||
//if no downloads are happening we will be missing keys
|
||||
//this will prevent inaccurate reporting
|
||||
self::AddAllTransfer(0);
|
||||
}
|
||||
}
|
||||
?>
|
118
src/php/sync.php
118
src/php/sync.php
@ -1,59 +1,59 @@
|
||||
<?php
|
||||
class Sync implements RequestHandler {
|
||||
public function __construct(){
|
||||
Config::LoadConfig(array('max_upload_size', 'upload_folder'));
|
||||
|
||||
set_time_limit(1200);
|
||||
ini_set('post_max_size', Config::$Instance->max_upload_size);
|
||||
ini_set('upload_max_filesize', Config::$Instance->max_upload_size);
|
||||
ini_set('memory_limit', Config::$Instance->max_upload_size);
|
||||
ini_set('enable_post_data_reading', 0);
|
||||
}
|
||||
|
||||
public function HandleRequest() : void {
|
||||
if(isset($_SERVER["HTTP_X_FILE_ID"])) {
|
||||
$id = $_SERVER["HTTP_X_FILE_ID"];
|
||||
$fs = new FileStore(Config::$Instance->upload_folder);
|
||||
if(!$fs->FileExists($id)) {
|
||||
//resolve the hostnames to ips
|
||||
$redis = StaticRedis::ReadOp();
|
||||
$sync_hosts = $redis->sMembers(REDIS_PREFIX . 'sync-hosts');
|
||||
|
||||
$sync_hosts_ips = array();
|
||||
foreach($sync_hosts as $host) {
|
||||
$sync_hosts_ips[] = gethostbyname($host);
|
||||
}
|
||||
|
||||
//check the ip of the host submitting the file for sync
|
||||
if(in_array(USER_IP, $sync_hosts_ips)) {
|
||||
$fs->StoreFile("php://input", $id);
|
||||
http_response_code(201);
|
||||
} else {
|
||||
http_response_code(401);
|
||||
}
|
||||
} else {
|
||||
http_response_code(200);
|
||||
}
|
||||
} else {
|
||||
http_response_code(400);
|
||||
}
|
||||
}
|
||||
|
||||
public static function SyncFile($id, $filename, $host) : int {
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, "https://$host/sync");
|
||||
curl_setopt($ch, CURLOPT_POST, 1);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, file_get_contents($filename));
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
|
||||
"Content-Type: application/octet-stream",
|
||||
"X-File-Id: " . $id
|
||||
));
|
||||
curl_exec($ch);
|
||||
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
|
||||
curl_close ($ch);
|
||||
|
||||
return intval($status);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
<?php
|
||||
class Sync implements RequestHandler {
|
||||
public function __construct(){
|
||||
Config::LoadConfig(array('max_upload_size', 'upload_folder'));
|
||||
|
||||
set_time_limit(1200);
|
||||
ini_set('post_max_size', Config::$Instance->max_upload_size);
|
||||
ini_set('upload_max_filesize', Config::$Instance->max_upload_size);
|
||||
ini_set('memory_limit', Config::$Instance->max_upload_size);
|
||||
ini_set('enable_post_data_reading', 0);
|
||||
}
|
||||
|
||||
public function HandleRequest() : void {
|
||||
if(isset($_SERVER["HTTP_X_FILE_ID"])) {
|
||||
$id = $_SERVER["HTTP_X_FILE_ID"];
|
||||
$fs = new FileStore(Config::$Instance->upload_folder);
|
||||
if(!$fs->FileExists($id)) {
|
||||
//resolve the hostnames to ips
|
||||
$redis = StaticRedis::ReadOp();
|
||||
$sync_hosts = $redis->sMembers(REDIS_PREFIX . 'sync-hosts');
|
||||
|
||||
$sync_hosts_ips = array();
|
||||
foreach($sync_hosts as $host) {
|
||||
$sync_hosts_ips[] = gethostbyname($host);
|
||||
}
|
||||
|
||||
//check the ip of the host submitting the file for sync
|
||||
if(in_array(USER_IP, $sync_hosts_ips)) {
|
||||
$fs->StoreFile(fopen("php://input", "rb"), $id);
|
||||
http_response_code(201);
|
||||
} else {
|
||||
http_response_code(401);
|
||||
}
|
||||
} else {
|
||||
http_response_code(200);
|
||||
}
|
||||
} else {
|
||||
http_response_code(400);
|
||||
}
|
||||
}
|
||||
|
||||
public static function SyncFile($id, $filename, $host) : int {
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, "https://$host/sync");
|
||||
curl_setopt($ch, CURLOPT_POST, 1);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, file_get_contents($filename));
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
|
||||
"Content-Type: application/octet-stream",
|
||||
"X-File-Id: " . $id
|
||||
));
|
||||
curl_exec($ch);
|
||||
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
|
||||
curl_close ($ch);
|
||||
|
||||
return intval($status);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
|
@ -1,18 +1,18 @@
|
||||
<?php
|
||||
class SyncThread extends Thread {
|
||||
private $Destination;
|
||||
private $FilePath;
|
||||
private $Id;
|
||||
|
||||
public function __constrct($id, $filepath, $host){
|
||||
$this->Id = $id;
|
||||
$this->FilePath = $filepath;
|
||||
$this->Destination = $host;
|
||||
}
|
||||
|
||||
public function run() {
|
||||
Sync::SyncFile($this->Id, $this->FilePath, $this->Destination);
|
||||
}
|
||||
}
|
||||
|
||||
<?php
|
||||
class SyncThread extends Thread {
|
||||
private $Destination;
|
||||
private $FilePath;
|
||||
private $Id;
|
||||
|
||||
public function __constrct($id, $filepath, $host){
|
||||
$this->Id = $id;
|
||||
$this->FilePath = $filepath;
|
||||
$this->Destination = $host;
|
||||
}
|
||||
|
||||
public function run() {
|
||||
Sync::SyncFile($this->Id, $this->FilePath, $this->Destination);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
@ -1,62 +1,64 @@
|
||||
<?php
|
||||
class Tracking {
|
||||
public function TrackDownload($fs, $id) : void {
|
||||
$redis = StaticRedis::WriteOp();
|
||||
$file_key = REDIS_PREFIX . $id;
|
||||
|
||||
$file_size = $fs->GetFileSize($id);
|
||||
|
||||
if(!$this->IsRangeRequest()) {
|
||||
$redis->hIncrBy($file_key, 'views', 1);
|
||||
$redis->hSet($file_key, 'lastview', time());
|
||||
|
||||
Stats::TrackTransfer($id, $file_size);
|
||||
} else {
|
||||
$range = $this->GetRequestRange($file_size);
|
||||
Stats::TrackTransfer($id, $range->end - $range->start);
|
||||
}
|
||||
}
|
||||
|
||||
function GetRequestRange($len) : ?object {
|
||||
if(isset($_SERVER['HTTP_RANGE'])) {
|
||||
$rby = explode('=', $_SERVER['HTTP_RANGE']);
|
||||
$rbv = explode('-', $rby[1]);
|
||||
return (object)array(
|
||||
"start" => intval($rbv[0]),
|
||||
"end" => intval($rbv[1] == "" ? $len : $rbv[1])
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function IsRangeRequest() : bool {
|
||||
$range = $this->GetRequestRange(0);
|
||||
if($range !== null){
|
||||
if($range->start != 0){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function SendMatomoEvent() : void {
|
||||
$msg = "?" . http_build_query(array(
|
||||
"idsite" => 3,
|
||||
"rec" => 1,
|
||||
"apiv" => 1,
|
||||
"_id" => isset($_COOKIE["VC:UID"]) ? $_COOKIE["VC:UID"] : uniqid(),
|
||||
"url" => (isset($_SERVER['HTTPS']) ? "https" : "http") . "://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]",
|
||||
"cip" => USER_IP,
|
||||
"ua" => isset($_SERVER["HTTP_USER_AGENT"]) ? $_SERVER["HTTP_USER_AGENT"] : "",
|
||||
"urlref" => isset($_SERVER["HTTP_REFERER"]) ? $_SERVER["HTTP_REFERER"] : "",
|
||||
"h" => date("H"),
|
||||
"m" => date("i"),
|
||||
"s" => date("s")
|
||||
));
|
||||
|
||||
//this should be sent to the slave node if we are connected on a slave
|
||||
StaticRedis::ReadOp()->publish(StaticRedis::$IsConnectedToSlave ? 'v3-matomo' : 'v3-matomo-master', $msg);
|
||||
}
|
||||
}
|
||||
<?php
|
||||
class Tracking {
|
||||
public function TrackDownload($fs, $id) : void {
|
||||
$file_size = $fs->GetFileSize($id);
|
||||
|
||||
if(!$this->IsRangeRequest()) {
|
||||
$this->TrackView($id);
|
||||
Stats::TrackTransfer($id, $file_size);
|
||||
} else {
|
||||
$range = $this->GetRequestRange($file_size);
|
||||
Stats::TrackTransfer($id, $range->end - $range->start);
|
||||
}
|
||||
}
|
||||
|
||||
public function TrackView($id) : void {
|
||||
$redis = StaticRedis::WriteOp();
|
||||
$file_key = REDIS_PREFIX . $id;
|
||||
$redis->hIncrBy($file_key, 'views', 1);
|
||||
$redis->hSet($file_key, 'lastview', time());
|
||||
}
|
||||
|
||||
function GetRequestRange($len) : ?object {
|
||||
if(isset($_SERVER['HTTP_RANGE'])) {
|
||||
$rby = explode('=', $_SERVER['HTTP_RANGE']);
|
||||
$rbv = explode('-', $rby[1]);
|
||||
return (object)array(
|
||||
"start" => intval($rbv[0]),
|
||||
"end" => intval($rbv[1] == "" ? $len : $rbv[1])
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function IsRangeRequest() : bool {
|
||||
$range = $this->GetRequestRange(0);
|
||||
if($range !== null){
|
||||
if($range->start != 0){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function SendMatomoEvent() : void {
|
||||
$msg = "?" . http_build_query(array(
|
||||
"idsite" => 3,
|
||||
"rec" => 1,
|
||||
"apiv" => 1,
|
||||
"_id" => isset($_COOKIE["VC:UID"]) ? $_COOKIE["VC:UID"] : uniqid(),
|
||||
"url" => (isset($_SERVER['HTTPS']) ? "https" : "http") . "://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]",
|
||||
"cip" => USER_IP,
|
||||
"ua" => isset($_SERVER["HTTP_USER_AGENT"]) ? $_SERVER["HTTP_USER_AGENT"] : "",
|
||||
"urlref" => isset($_SERVER["HTTP_REFERER"]) ? $_SERVER["HTTP_REFERER"] : "",
|
||||
"h" => date("H"),
|
||||
"m" => date("i"),
|
||||
"s" => date("s")
|
||||
));
|
||||
|
||||
//this should be sent to the slave node if we are connected on a slave
|
||||
StaticRedis::ReadOp()->publish(StaticRedis::$IsConnectedToSlave ? 'v3-matomo' : 'v3-matomo-master', $msg);
|
||||
}
|
||||
}
|
||||
?>
|
@ -1,151 +1,158 @@
|
||||
<?php
|
||||
class UploadResponse {
|
||||
public $status = 0;
|
||||
public $msg;
|
||||
public $id;
|
||||
public $sync;
|
||||
}
|
||||
|
||||
class Upload implements RequestHandler {
|
||||
private $IsMultipart = False;
|
||||
|
||||
public function __construct() {
|
||||
Config::LoadConfig(array('max_upload_size', 'upload_folder', 'public_hash_algo'));
|
||||
|
||||
//set php params
|
||||
set_time_limit(1200);
|
||||
ini_set('post_max_size', Config::$Instance->max_upload_size);
|
||||
ini_set('upload_max_filesize', Config::$Instance->max_upload_size);
|
||||
ini_set('memory_limit', Config::$Instance->max_upload_size);
|
||||
ini_set('enable_post_data_reading', 0);
|
||||
|
||||
//check upload dir exists
|
||||
if(!file_exists("$_SERVER[DOCUMENT_ROOT]/" . Config::$Instance->upload_folder)){
|
||||
mkdir("$_SERVER[DOCUMENT_ROOT]/" . Config::$Instance->upload_folder);
|
||||
}
|
||||
}
|
||||
|
||||
public function HandleRequest() : void {
|
||||
if(isset($_SERVER["HTTP_ORIGIN"])) {
|
||||
header("Access-Control-Allow-Origin: " . $_SERVER["HTTP_ORIGIN"]);
|
||||
header("Access-Control-Allow-Method: POST,OPTIONS");
|
||||
header("Access-Control-Allow-Headers: Content-Type");
|
||||
}
|
||||
|
||||
$rsp = new UploadResponse();
|
||||
$file_size = $_SERVER["CONTENT_LENGTH"];
|
||||
|
||||
if($file_size > Config::$Instance->max_upload_size){
|
||||
$rsp->status = 1;
|
||||
$rsp->msg = "File is too large";
|
||||
} else {
|
||||
$auth = new Auth();
|
||||
$token = $auth->GetBearerToken();
|
||||
|
||||
if($token !== null) {
|
||||
if($auth->CheckApiToken($token)) {
|
||||
$id = $this->SaveLegacyUpload();
|
||||
|
||||
//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 {
|
||||
$read_from = "php://input";
|
||||
$bf = BlobFile::LoadHeader($read_from);
|
||||
|
||||
if($bf != null){
|
||||
//save upload
|
||||
$id = $this->SaveUpload($bf, $read_from);
|
||||
|
||||
//sync to other servers
|
||||
if($id == null) {
|
||||
$rsp->status = 4;
|
||||
$rsp->msg = "Invalid VBF or file already exists";
|
||||
} else {
|
||||
$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');
|
||||
echo json_encode($rsp);
|
||||
}
|
||||
|
||||
function SyncFileUpload($id) : array {
|
||||
$redis = StaticRedis::ReadOp();
|
||||
$sync_hosts = $redis->sMembers(REDIS_PREFIX . 'sync-hosts');
|
||||
if($sync_hosts !== False) {
|
||||
$fs = new FileStore(Config::$Instance->upload_folder);
|
||||
|
||||
$status_codes = [];
|
||||
foreach($sync_hosts as $host) {
|
||||
$status_codes[] = Sync::SyncFile($id, $fs->GetAbsoluteFilePath($id), $host);
|
||||
}
|
||||
|
||||
return $status_codes;
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
function SaveUpload($bf, $rf) : ?string {
|
||||
$fs = new FileStore(Config::$Instance->upload_folder);
|
||||
switch($bf->Version) {
|
||||
case 1:
|
||||
return $fs->StoreV1File($bf, $rf);
|
||||
case 2:
|
||||
return $fs->StoreV2File($bf, $rf);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
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(fopen("php://input", "rb"), $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){
|
||||
$cont = "EU";
|
||||
}
|
||||
|
||||
$redis = StaticRedis::ReadOp();
|
||||
$map = $redis->hGetAll(REDIS_PREFIX . "upload-region-mapping");
|
||||
if($map !== False && isset($map[$cont])) {
|
||||
return $map[$cont];
|
||||
} else {
|
||||
return $_SERVER["HTTP_HOST"];
|
||||
}
|
||||
}
|
||||
}
|
||||
<?php
|
||||
class UploadResponse {
|
||||
public $status = 0;
|
||||
public $msg;
|
||||
public $id;
|
||||
public $sync;
|
||||
}
|
||||
|
||||
class Upload implements RequestHandler {
|
||||
private $IsMultipart = False;
|
||||
|
||||
public function __construct() {
|
||||
Config::LoadConfig(array('max_upload_size', 'upload_folder', 'public_hash_algo'));
|
||||
|
||||
//set php params
|
||||
set_time_limit(1200);
|
||||
ini_set('post_max_size', Config::$Instance->max_upload_size);
|
||||
ini_set('upload_max_filesize', Config::$Instance->max_upload_size);
|
||||
ini_set('memory_limit', Config::$Instance->max_upload_size);
|
||||
ini_set('enable_post_data_reading', 0);
|
||||
|
||||
//check upload dir exists
|
||||
if(!file_exists("$_SERVER[DOCUMENT_ROOT]/" . Config::$Instance->upload_folder)){
|
||||
mkdir("$_SERVER[DOCUMENT_ROOT]/" . Config::$Instance->upload_folder);
|
||||
}
|
||||
}
|
||||
|
||||
public function HandleRequest() : void {
|
||||
if(isset($_SERVER["HTTP_ORIGIN"])) {
|
||||
header("Access-Control-Allow-Origin: " . $_SERVER["HTTP_ORIGIN"]);
|
||||
header("Access-Control-Allow-Method: POST,OPTIONS");
|
||||
header("Access-Control-Allow-Headers: Content-Type");
|
||||
}
|
||||
|
||||
$rsp = new UploadResponse();
|
||||
$file_size = $_SERVER["CONTENT_LENGTH"];
|
||||
|
||||
if($file_size > Config::$Instance->max_upload_size){
|
||||
$rsp->status = 1;
|
||||
$rsp->msg = "File is too large";
|
||||
} else {
|
||||
$auth = new Auth();
|
||||
$tracking = new Tracking();
|
||||
$token = $auth->GetBearerToken();
|
||||
|
||||
if($token !== null) {
|
||||
if($auth->CheckApiToken($token)) {
|
||||
$id = $this->SaveLegacyUpload();
|
||||
|
||||
//sync to other servers
|
||||
if($id !== null) {
|
||||
$rsp->sync = $this->SyncFileUpload($id);
|
||||
$rsp->status = 200;
|
||||
$rsp->id = $id;
|
||||
|
||||
//finally set the last view to now
|
||||
$tracking->TrackView($id);
|
||||
} else {
|
||||
$rsp->status = 3;
|
||||
$rsp->msg = "Legacy upload error";
|
||||
}
|
||||
} else {
|
||||
http_response_code(403);
|
||||
exit();
|
||||
}
|
||||
} else {
|
||||
$read_from = "php://input";
|
||||
$bf = BlobFile::LoadHeader($read_from);
|
||||
|
||||
if($bf != null){
|
||||
//save upload
|
||||
$id = $this->SaveUpload($bf, $read_from);
|
||||
|
||||
//sync to other servers
|
||||
if($id == null) {
|
||||
$rsp->status = 4;
|
||||
$rsp->msg = "Invalid VBF or file already exists";
|
||||
} else {
|
||||
$rsp->sync = $this->SyncFileUpload($id);
|
||||
$rsp->status = 200;
|
||||
$rsp->id = $id;
|
||||
|
||||
//finally set the last view to now
|
||||
$tracking->TrackView($id);
|
||||
}
|
||||
} else {
|
||||
$rsp->status = 2;
|
||||
$rsp->msg = "Invalid file header";
|
||||
}
|
||||
}
|
||||
}
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode($rsp);
|
||||
}
|
||||
|
||||
function SyncFileUpload($id) : array {
|
||||
$redis = StaticRedis::ReadOp();
|
||||
$sync_hosts = $redis->sMembers(REDIS_PREFIX . 'sync-hosts');
|
||||
if($sync_hosts !== False) {
|
||||
$fs = new FileStore(Config::$Instance->upload_folder);
|
||||
|
||||
$status_codes = [];
|
||||
foreach($sync_hosts as $host) {
|
||||
$status_codes[] = Sync::SyncFile($id, $fs->GetAbsoluteFilePath($id), $host);
|
||||
}
|
||||
|
||||
return $status_codes;
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
function SaveUpload($bf, $rf) : ?string {
|
||||
$fs = new FileStore(Config::$Instance->upload_folder);
|
||||
switch($bf->Version) {
|
||||
case 1:
|
||||
return $fs->StoreV1File($bf, $rf);
|
||||
case 2:
|
||||
return $fs->StoreV2File($bf, $rf);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
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(fopen("php://input", "rb"), $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){
|
||||
$cont = "EU";
|
||||
}
|
||||
|
||||
$redis = StaticRedis::ReadOp();
|
||||
$map = $redis->hGetAll(REDIS_PREFIX . "upload-region-mapping");
|
||||
if($map !== False && isset($map[$cont])) {
|
||||
return $map[$cont];
|
||||
} else {
|
||||
return $_SERVER["HTTP_HOST"];
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
Loading…
x
Reference in New Issue
Block a user