276 lines
10 KiB
PHP
276 lines
10 KiB
PHP
<?php
|
|
|
|
/*
|
|
* This file is part of Chevereto.
|
|
*
|
|
* (c) Rodolfo Berrios <rodolfo@chevereto.com>
|
|
*
|
|
* For the full copyright and license information, please view the LICENSE
|
|
* file that was distributed with this source code.
|
|
*/
|
|
|
|
/*
|
|
* This API V1 was introduced in Chevereto V2 and it was carried over to V3.
|
|
*
|
|
* From Chevereto V4 onwards the API versioning follow the major version:
|
|
*
|
|
* - V2 -> API V1
|
|
* - V3 -> API V1
|
|
* - V4 -> API V4 + API V1.1
|
|
*/
|
|
|
|
use Chevereto\Legacy\Classes\Akismet;
|
|
use Chevereto\Legacy\Classes\ApiKey;
|
|
use Chevereto\Legacy\Classes\Image;
|
|
use Chevereto\Legacy\Classes\Settings;
|
|
use Chevereto\Legacy\Classes\Upload;
|
|
use Chevereto\Legacy\Classes\User;
|
|
use Chevereto\Legacy\G\Handler;
|
|
use function Chevereto\Legacy\decodeID;
|
|
use function Chevereto\Legacy\encodeID;
|
|
use function Chevereto\Legacy\G\get_mimetype;
|
|
use function Chevereto\Legacy\G\getQsParams;
|
|
use function Chevereto\Legacy\G\is_image_url;
|
|
use function Chevereto\Legacy\G\is_url;
|
|
use function Chevereto\Legacy\G\json_document_output;
|
|
use function Chevereto\Legacy\G\json_error;
|
|
use function Chevereto\Legacy\G\mime_to_extension;
|
|
use function Chevereto\Legacy\G\random_string;
|
|
use function Chevereto\Legacy\getSetting;
|
|
use function Chevereto\Legacy\getVariable;
|
|
use function Chevereto\Vars\env;
|
|
use function Chevereto\Vars\files;
|
|
use function Chevereto\Vars\request;
|
|
use function Chevereto\Vars\server;
|
|
|
|
return function (Handler $handler) {
|
|
try {
|
|
$user = [];
|
|
$REQUEST = request();
|
|
$FILES = files();
|
|
$SERVER = server();
|
|
$format = $REQUEST['format'] ?? 'json';
|
|
$version = strval($handler->request()[0] ?? '');
|
|
$action = strval($handler->request()[1] ?? '');
|
|
$key = strval($SERVER['HTTP_X_API_KEY'] ?? $REQUEST['key'] ?? '');
|
|
foreach (['version', 'action', 'key'] as $var) {
|
|
if (${$var} === '') {
|
|
throw new Exception("No {$var} provided", 100);
|
|
}
|
|
}
|
|
if (! in_array($version, ['1'], true)) {
|
|
throw new Exception('Invalid API version.', 110);
|
|
}
|
|
$verify = ApiKey::verify($key);
|
|
if ($verify === []) {
|
|
if (! (bool) env()['CHEVERETO_ENABLE_API_GUEST']) {
|
|
throw new Exception('Invalid API key.', 100);
|
|
}
|
|
$apiV1Key = (string) (getSetting('api_v1_key') ?? '');
|
|
if ($apiV1Key === '') {
|
|
throw new Exception("API V1 public key can't be null. Go to /dashboard and set the Guest API key.", 0);
|
|
}
|
|
if (! hash_equals($apiV1Key, $key)) {
|
|
throw new Exception('Invalid API key.', 100);
|
|
}
|
|
} else {
|
|
$user = User::getSingle($verify['user_id']);
|
|
}
|
|
$isAdmin = boolval(($user['is_admin'] ?? false));
|
|
$upload_enabled = $isAdmin ?: getSetting('enable_uploads');
|
|
$upload_allowed = $upload_enabled;
|
|
if ($user === []) {
|
|
if (! getSetting('guest_uploads')
|
|
|| getSetting('website_privacy_mode') === 'private'
|
|
|| $handler::cond('maintenance')
|
|
) {
|
|
$upload_allowed = false;
|
|
}
|
|
} elseif (! $isAdmin
|
|
&& getSetting('website_mode') === 'personal'
|
|
&& getSetting('website_mode_personal_uid') !== ($user['id'] ?? 0)
|
|
) {
|
|
$upload_allowed = false;
|
|
}
|
|
if ((! (bool) env()['CHEVERETO_ENABLE_LOCAL_STORAGE'])
|
|
&& getVariable('storages_active')->nullInt() === 0
|
|
) {
|
|
$upload_enabled = false;
|
|
$upload_allowed = false;
|
|
}
|
|
if ($user === []
|
|
&& $upload_allowed
|
|
&& getSetting('upload_max_filesize_mb_guest')
|
|
) {
|
|
Settings::setValue('upload_max_filesize_mb_bak', getSetting('upload_max_filesize_mb'));
|
|
Settings::setValue('upload_max_filesize_mb', getSetting('upload_max_filesize_mb_guest'));
|
|
}
|
|
$handler::setCond('upload_enabled', $upload_enabled);
|
|
$handler::setCond('upload_allowed', $upload_allowed);
|
|
if (Settings::get('enable_uploads_url') && ! $isAdmin) {
|
|
Settings::setValue('enable_uploads_url', 0);
|
|
}
|
|
if (! $handler::cond('upload_allowed')) {
|
|
throw new Exception(_s('Request denied'), 401);
|
|
}
|
|
$version_to_actions = [
|
|
'1' => ['upload'],
|
|
];
|
|
if (! in_array($action, $version_to_actions[$version], true)) {
|
|
throw new Exception('Invalid API action', 120);
|
|
}
|
|
$source = $FILES['source']
|
|
?? $REQUEST['source']
|
|
?? $REQUEST['image']
|
|
?? null;
|
|
if ($source === null) {
|
|
throw new Exception('Empty upload source', 130);
|
|
}
|
|
switch (true) {
|
|
case isset($FILES['source'], $FILES['source']['tmp_name']):
|
|
$source = $FILES['source'];
|
|
|
|
break;
|
|
case is_image_url($source) || is_url($source):
|
|
if (($SERVER['REQUEST_METHOD'] ?? '') === 'GET') {
|
|
$sourceQs = urldecode(getQsParams()['source']);
|
|
}
|
|
$source = $sourceQs ?? $source;
|
|
|
|
break;
|
|
default:
|
|
if (($SERVER['REQUEST_METHOD'] ?? '') !== 'POST') {
|
|
throw new Exception('Upload using base64 source must be done using POST method.', 130);
|
|
}
|
|
$source = trim(preg_replace('/\s+/', '', $source));
|
|
$base64source = base64_encode(base64_decode($source, true));
|
|
if (! hash_equals($base64source, $source)) {
|
|
throw new Exception('Invalid base64 string', 120);
|
|
}
|
|
if ($source === '') {
|
|
throw new Exception('Empty source', 130);
|
|
}
|
|
|
|
try {
|
|
$api_temp_file = Upload::getTempNam();
|
|
} catch (Exception $e) {
|
|
throw new Exception("Can't get a tempnam", 200);
|
|
}
|
|
$fh = fopen($api_temp_file, 'w');
|
|
stream_filter_append($fh, 'convert.base64-decode', STREAM_FILTER_WRITE);
|
|
fwrite($fh, $source);
|
|
fclose($fh);
|
|
$mimetype = get_mimetype($api_temp_file);
|
|
$source = [
|
|
'name' => random_string(12) . '.' . mime_to_extension($mimetype),
|
|
'type' => $mimetype,
|
|
'tmp_name' => $api_temp_file,
|
|
'error' => 'UPLOAD_ERR_OK',
|
|
'size' => filesize($api_temp_file),
|
|
];
|
|
|
|
break;
|
|
}
|
|
$isImgBBSpec = array_key_exists('image', $REQUEST);
|
|
$albumId = $REQUEST['album_id'] ?? null;
|
|
if ($albumId !== null) {
|
|
$albumId = decodeID($albumId);
|
|
}
|
|
$expiration = $REQUEST['expiration'] ?? null;
|
|
if ($expiration !== null && ctype_digit($expiration)) {
|
|
$expiration = (int) $expiration;
|
|
}
|
|
$params = [
|
|
'album_id' => $albumId,
|
|
'category_id' => $REQUEST['category_id'] ?? null,
|
|
'description' => $REQUEST['description'] ?? null,
|
|
'nsfw' => $REQUEST['nsfw'] ?? null,
|
|
'title' => $REQUEST['title'] ?? $REQUEST['name'] ?? null,
|
|
'tags' => $REQUEST['tags'] ?? null,
|
|
'width' => $REQUEST['width'] ?? null,
|
|
'expiration' => $expiration,
|
|
'use_file_date' => $isAdmin
|
|
? ($REQUEST['use_file_date'] ?? false)
|
|
: false,
|
|
];
|
|
|
|
$params = array_filter($params);
|
|
if (! $handler::cond('content_manager') && getSetting('akismet')) {
|
|
$user_source_db = [
|
|
'user_name' => $user['name'] ?? null,
|
|
'user_username' => $user['username'] ?? null,
|
|
'user_email' => $user['email'] ?? null,
|
|
];
|
|
Akismet::checkImage(
|
|
title: $params['title'] ?? null,
|
|
description: $params['description'] ?? null,
|
|
tags: $params['tags'] ?? null,
|
|
source_db: $user_source_db
|
|
);
|
|
}
|
|
$uploadToWebsite = Image::uploadToWebsite($source, $user, $params);
|
|
$uploaded_id = intval($uploadToWebsite[0]);
|
|
$image = Image::formatArray(Image::getSingle($uploaded_id), true);
|
|
$image['delete_url'] = Image::getDeleteUrl(
|
|
type: $image['type'],
|
|
idEncoded: encodeID($uploaded_id),
|
|
password: $uploadToWebsite[1]
|
|
);
|
|
unset($image['user'], $image['album']);
|
|
if (! $image['is_approved']) {
|
|
unset($image['image']['url'], $image['thumb']['url'], $image['medium']['url'], $image['url'], $image['display_url']);
|
|
}
|
|
$json_array = [];
|
|
$json_array['status_code'] = 200;
|
|
if ($isImgBBSpec) {
|
|
$json_array['status'] = $json_array['status_code'];
|
|
$image['id'] = $image['id_encoded'];
|
|
}
|
|
$json_array['success'] = [
|
|
'message' => 'file uploaded',
|
|
'code' => 200,
|
|
];
|
|
$json_array[$isImgBBSpec ? 'data' : 'image'] = $image;
|
|
|
|
if ($version === '1') {
|
|
switch ($format) {
|
|
default:
|
|
case 'json':
|
|
json_document_output($json_array);
|
|
|
|
break;
|
|
case 'txt':
|
|
echo $image['url'];
|
|
|
|
break;
|
|
case 'redirect':
|
|
if ($json_array['status_code'] === 200) {
|
|
$redirect_url = $image['path_viewer'];
|
|
header("Location: {$redirect_url}");
|
|
} else {
|
|
exit($json_array['status_code']);
|
|
}
|
|
|
|
break;
|
|
}
|
|
exit();
|
|
}
|
|
json_document_output($json_array);
|
|
} catch (Exception $e) {
|
|
$json_array = json_error($e);
|
|
if ($version === '1') {
|
|
switch ($format) {
|
|
default:
|
|
case 'json':
|
|
json_document_output($json_array);
|
|
|
|
break;
|
|
case 'txt':
|
|
case 'redirect':
|
|
exit($json_array['error']['message']);
|
|
}
|
|
} else {
|
|
json_document_output($json_array);
|
|
}
|
|
}
|
|
};
|