Last active
February 21, 2023 08:56
-
-
Save toriato/95edf3c364b2b8b2d6b8be63a482703f to your computer and use it in GitHub Desktop.
simple file uploader and url shortener for sharex
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
define('MYSQL_DSN', 'mysql:host=mariadb;dbname=aeon'); | |
define('MYSQL_USER', 'MYUSER'); | |
define('MYSQL_PASS', 'MYP4SS'); | |
define('MYSQL_OPTIONS', [ | |
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, | |
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC | |
]); | |
define('URLS_MAX_ID_DUPLICATED_RETRIES', 10); | |
define('FILES_BASEDIR', 'files'); | |
// https://gist.github.com/henriquemoody/6580488 | |
define('HTTP_STATUS_MESSAGES', [ | |
100 => 'Continue', | |
101 => 'Switching Protocols', | |
102 => 'Processing', // WebDAV; RFC 2518 | |
103 => 'Early Hints', // RFC 8297 | |
200 => 'OK', | |
201 => 'Created', | |
202 => 'Accepted', | |
203 => 'Non-Authoritative Information', // since HTTP/1.1 | |
204 => 'No Content', | |
205 => 'Reset Content', | |
206 => 'Partial Content', // RFC 7233 | |
207 => 'Multi-Status', // WebDAV; RFC 4918 | |
208 => 'Already Reported', // WebDAV; RFC 5842 | |
226 => 'IM Used', // RFC 3229 | |
300 => 'Multiple Choices', | |
301 => 'Moved Permanently', | |
302 => 'Found', // Previously "Moved temporarily" | |
303 => 'See Other', // since HTTP/1.1 | |
304 => 'Not Modified', // RFC 7232 | |
305 => 'Use Proxy', // since HTTP/1.1 | |
306 => 'Switch Proxy', | |
307 => 'Temporary Redirect', // since HTTP/1.1 | |
308 => 'Permanent Redirect', // RFC 7538 | |
400 => 'Bad Request', | |
401 => 'Unauthorized', // RFC 7235 | |
402 => 'Payment Required', | |
403 => 'Forbidden', | |
404 => 'Not Found', | |
405 => 'Method Not Allowed', | |
406 => 'Not Acceptable', | |
407 => 'Proxy Authentication Required', // RFC 7235 | |
408 => 'Request Timeout', | |
409 => 'Conflict', | |
410 => 'Gone', | |
411 => 'Length Required', | |
412 => 'Precondition Failed', // RFC 7232 | |
413 => 'Payload Too Large', // RFC 7231 | |
414 => 'URI Too Long', // RFC 7231 | |
415 => 'Unsupported Media Type', // RFC 7231 | |
416 => 'Range Not Satisfiable', // RFC 7233 | |
417 => 'Expectation Failed', | |
418 => 'I\'m a teapot', // RFC 2324, RFC 7168 | |
421 => 'Misdirected Request', // RFC 7540 | |
422 => 'Unprocessable Entity', // WebDAV; RFC 4918 | |
423 => 'Locked', // WebDAV; RFC 4918 | |
424 => 'Failed Dependency', // WebDAV; RFC 4918 | |
425 => 'Too Early', // RFC 8470 | |
426 => 'Upgrade Required', | |
428 => 'Precondition Required', // RFC 6585 | |
429 => 'Too Many Requests', // RFC 6585 | |
431 => 'Request Header Fields Too Large', // RFC 6585 | |
451 => 'Unavailable For Legal Reasons', // RFC 7725 | |
500 => 'Internal Server Error', | |
501 => 'Not Implemented', | |
502 => 'Bad Gateway', | |
503 => 'Service Unavailable', | |
504 => 'Gateway Timeout', | |
505 => 'HTTP Version Not Supported', | |
506 => 'Variant Also Negotiates', // RFC 2295 | |
507 => 'Insufficient Storage', // WebDAV; RFC 4918 | |
508 => 'Loop Detected', // WebDAV; RFC 5842 | |
510 => 'Not Extended', // RFC 2774 | |
511 => 'Network Authentication Required', // RFC 6585 | |
]); | |
function exitWithStatusCode(int $statusCode, string $message = null): never | |
{ | |
// use default status message if exists | |
if (is_null($message) && in_array($statusCode, HTTP_STATUS_MESSAGES)) { | |
$message = HTTP_STATUS_MESSAGES[$statusCode]; | |
} | |
http_response_code($statusCode); | |
die($message); | |
} | |
$db = new PDO(MYSQL_DSN, MYSQL_USER, MYSQL_PASS, MYSQL_OPTIONS); | |
switch ($_SERVER['REQUEST_METHOD']) { | |
case 'GET': | |
$id = base64_decode(str_replace(['-', '_'], ['+', '/'], trim(@$_GET['id']))); | |
if (empty($id)) { | |
header('Location: https://gall.dcinside.com/mini/owo'); | |
die; | |
} | |
if ($id === false) { | |
exitWithStatusCode(404, 'Malformed ID'); | |
} | |
$sth = $db->prepare('SELECT url FROM urls WHERE id = :id'); | |
$sth->bindValue(':id', $id, PDO::PARAM_INT); | |
$sth->execute(); | |
if ($sth->rowCount() < 1) { | |
exitWithStatusCode(404); | |
} | |
$record = $sth->fetch(); | |
header('Location: ' . $record['url']); | |
break; | |
case 'POST': | |
$token = @$_POST['token']; | |
$sth = $db->prepare(<<<SQL | |
SELECT node | |
FROM token_permission | |
WHERE token_id = ( | |
SELECT id | |
FROM token | |
WHERE token = :token | |
) | |
SQL); | |
$sth->bindValue(':token', $token, PDO::PARAM_STR); | |
$sth->execute(); | |
$nodes = array_merge(...array_map(fn ($n) => array_values($n), $sth->fetchAll())); | |
if (!empty(@$_POST['url'])) { | |
if (!in_array('urls.create', $nodes)) { | |
exitWithStatusCode(403); | |
} | |
$url = $_POST['url']; | |
if (!filter_var($url, FILTER_VALIDATE_URL)) { | |
exitWithStatusCode(400, 'Malformed URL'); | |
} | |
// while until id is not duplicated | |
$retries = 0; | |
while (++$retries < URLS_MAX_ID_DUPLICATED_RETRIES) { | |
try { | |
$sth = $db->prepare(<<<SQL | |
INSERT INTO urls (token_id, url) VALUES ((SELECT id FROM token WHERE token = :token), :url) | |
SQL); | |
$sth->bindValue(':token', $token); | |
$sth->bindValue(':url', $url); | |
$sth->execute(); | |
$id = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($db->lastInsertId())); | |
exitWithStatusCode(200, 'https://vmm.pw/' . $id); | |
} catch (PDOException $e) { | |
switch ($e->errorInfo[1]) { | |
// duplicated entry | |
case 1062: | |
continue 2; | |
default: | |
exitWithStatusCode(500, 'Database Error'); | |
} | |
} | |
} | |
} else if (isset($_FILES['file'])) { | |
if (!in_array('files.create', $nodes)) { | |
exitWithStatusCode(403); | |
} | |
$file = $_FILES['file']; | |
if ($file['error'] !== UPLOAD_ERR_OK) { | |
exitWithStatusCode(500, 'Upload Error'); | |
} | |
// check mime types | |
$mime = mime_content_type($file['tmp_name']); | |
switch (true) { | |
case in_array('files.allowed_types.*', $nodes): // power of asterisk! | |
case in_array("files.allowed_types.$mime", $nodes): // specific mime type | |
break; | |
default: | |
exitWithStatusCode(415); | |
} | |
$path = join(DIRECTORY_SEPARATOR, [FILES_BASEDIR, floor(microtime(true) * 1000), $file['name']]); | |
mkdir(dirname($path), 0777, true); | |
move_uploaded_file($file['tmp_name'], $path); | |
exitWithStatusCode(200, 'https://vmm.pw/' . $path); | |
} | |
exitWithStatusCode(500); | |
break; | |
default: | |
exitWithStatusCode(405); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*!40101 SET NAMES utf8 */; | |
/*!50503 SET NAMES utf8mb4 */; | |
CREATE TABLE IF NOT EXISTS `token` ( | |
`id` int(10) unsigned NOT NULL AUTO_INCREMENT, | |
`token` char(128) NOT NULL, | |
PRIMARY KEY (`id`), | |
UNIQUE KEY `token` (`token`) | |
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4; | |
CREATE TABLE IF NOT EXISTS `token_permission` ( | |
`id` int(10) unsigned NOT NULL AUTO_INCREMENT, | |
`token_id` int(10) unsigned NOT NULL, | |
`node` varchar(64) NOT NULL, | |
PRIMARY KEY (`id`), | |
UNIQUE KEY `token_node` (`token_id`,`node`) USING BTREE, | |
CONSTRAINT `FK_token_permission_token` FOREIGN KEY (`token_id`) REFERENCES `token` (`id`) ON DELETE CASCADE | |
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4; | |
CREATE TABLE IF NOT EXISTS `urls` ( | |
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, | |
`token_id` int(10) unsigned NOT NULL, | |
`url` varchar(2083) NOT NULL, | |
`created_at` int(11) DEFAULT NULL, | |
PRIMARY KEY (`id`), | |
UNIQUE KEY `id` (`id`), | |
KEY `FK_urls_token` (`token_id`) USING BTREE, | |
CONSTRAINT `FK_urls_token` FOREIGN KEY (`token_id`) REFERENCES `token` (`id`) ON DELETE CASCADE | |
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment