WebOrbiton
v3.0.0.2

FlatlyPage

157 lines · 5.6 KB
  1. <?php
  2. require_once __DIR__ . '/../config.php';
  3. ​
  4. require_login();
  5. ​
  6. header('Content-Type: application/json; charset=utf-8');
  7. ​
  8. const MEDIA_MAX_BYTES = 536870912;
  9. const MEDIA_LIST_LIMIT = 300;
  10. ​
  11. function media_respond(array $payload, int $status = 200): void
  12. {
  13. http_response_code($status);
  14. echo json_encode($payload);
  15. exit;
  16. }
  17. ​
  18. function media_ini_bytes(string $value): int
  19. {
  20. $value = trim($value);
  21. $unit = strtolower(substr($value, -1));
  22. $number = (float) $value;
  23. switch ($unit) {
  24. case 'g':
  25. $number *= 1024;
  26. case 'm':
  27. $number *= 1024;
  28. case 'k':
  29. $number *= 1024;
  30. }
  31. return (int) $number;
  32. }
  33. ​
  34. function media_format_bytes(int $bytes): string
  35. {
  36. if ($bytes >= 1048576) {
  37. return round($bytes / 1048576, 1) . ' MB';
  38. }
  39. return max(1, (int) round($bytes / 1024)) . ' KB';
  40. }
  41. ​
  42. $mediaDir = BASE_DIR . '/media';
  43. $types = media_types();
  44. ​
  45. if ($_SERVER['REQUEST_METHOD'] === 'GET') {
  46. $kind = $_GET['list'] ?? 'image';
  47. if (!isset($types[$kind])) {
  48. media_respond(['success' => false, 'error' => "This file type isn't supported here."], 400);
  49. }
  50. ​
  51. $items = [];
  52. foreach (glob($mediaDir . '/*') ?: [] as $file) {
  53. if (!is_file($file)) {
  54. continue;
  55. }
  56. $ext = strtolower(pathinfo($file, PATHINFO_EXTENSION));
  57. if (!isset($types[$kind][$ext])) {
  58. continue;
  59. }
  60. $items[] = [
  61. 'url' => '/media/' . rawurlencode(basename($file)),
  62. 'name' => basename($file),
  63. 'size' => media_format_bytes((int) filesize($file)),
  64. 'mtime' => (int) filemtime($file),
  65. ];
  66. }
  67. usort($items, fn($a, $b) => $b['mtime'] <=> $a['mtime']);
  68. media_respond(['success' => true, 'items' => array_slice($items, 0, MEDIA_LIST_LIMIT)]);
  69. }
  70. ​
  71. if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
  72. media_respond(['success' => false, 'error' => 'Method not allowed.'], 405);
  73. }
  74. ​
  75. $postMax = media_ini_bytes((string) ini_get('post_max_size'));
  76. if (empty($_FILES) && empty($_POST) && (int) ($_SERVER['CONTENT_LENGTH'] ?? 0) > 0 && $postMax > 0) {
  77. media_respond(['success' => false, 'error' => 'This file is too big for your server. The limit is ' . ini_get('post_max_size') . '.'], 413);
  78. }
  79. ​
  80. if (!verify_csrf_token((string) ($_POST['csrf_token'] ?? ''))) {
  81. media_respond(['success' => false, 'error' => 'Your session timed out. Refresh the page and try again.'], 403);
  82. }
  83. ​
  84. $kind = (string) ($_POST['kind'] ?? '');
  85. if (!isset($types[$kind])) {
  86. media_respond(['success' => false, 'error' => "This file type isn't supported here."], 400);
  87. }
  88. ​
  89. $file = $_FILES['file'] ?? null;
  90. if (!$file || !is_array($file) || is_array($file['name'] ?? null)) {
  91. media_respond(['success' => false, 'error' => "The file didn't arrive. Please try again."], 400);
  92. }
  93. ​
  94. if ($file['error'] !== UPLOAD_ERR_OK) {
  95. $messages = [
  96. UPLOAD_ERR_INI_SIZE => 'This file is too big for your server. The limit is ' . ini_get('upload_max_filesize') . '.',
  97. UPLOAD_ERR_FORM_SIZE => 'This file is too big.',
  98. UPLOAD_ERR_PARTIAL => "The upload was cut off. Please try again.",
  99. UPLOAD_ERR_NO_FILE => "The file didn't arrive. Please try again.",
  100. UPLOAD_ERR_NO_TMP_DIR => "Your server is missing its temporary folder. Ask your hosting provider to check it.",
  101. UPLOAD_ERR_CANT_WRITE => "Your server couldn't save the file. Check the folder permissions.",
  102. UPLOAD_ERR_EXTENSION => "Your server blocked this upload.",
  103. ];
  104. media_respond(['success' => false, 'error' => $messages[$file['error']] ?? 'Upload failed.'], 400);
  105. }
  106. ​
  107. if (!is_uploaded_file($file['tmp_name'])) {
  108. media_respond(['success' => false, 'error' => "The upload didn't work. Please try again."], 400);
  109. }
  110. ​
  111. if ($file['size'] > MEDIA_MAX_BYTES) {
  112. media_respond(['success' => false, 'error' => 'This file is bigger than ' . media_format_bytes(MEDIA_MAX_BYTES) . '.'], 413);
  113. }
  114. ​
  115. $original = (string) $file['name'];
  116. $ext = strtolower(pathinfo($original, PATHINFO_EXTENSION));
  117. if (!isset($types[$kind][$ext])) {
  118. media_respond(['success' => false, 'error' => '.' . $ext . ' files can\'t be used here. You can upload: ' . implode(', ', array_keys($types[$kind])) . '.'], 415);
  119. }
  120. ​
  121. if (function_exists('finfo_open')) {
  122. $finfo = finfo_open(FILEINFO_MIME_TYPE);
  123. $mime = $finfo ? (string) finfo_file($finfo, $file['tmp_name']) : '';
  124. unset($finfo);
  125. if ($mime !== '' && !in_array($mime, $types[$kind][$ext], true) && $mime !== 'application/octet-stream') {
  126. media_respond(['success' => false, 'error' => "This file's content doesn't match its extension."], 415);
  127. }
  128. }
  129. ​
  130. if ($kind === 'image' && $ext !== 'ico' && $ext !== 'avif' && @getimagesize($file['tmp_name']) === false) {
  131. media_respond(['success' => false, 'error' => "This file isn't a valid image."], 415);
  132. }
  133. ​
  134. if (!is_dir($mediaDir) && !mkdir($mediaDir, 0755, true) && !is_dir($mediaDir)) {
  135. media_respond(['success' => false, 'error' => "Couldn't create the media folder. Check the folder permissions on your server."], 500);
  136. }
  137. ​
  138. $base = slugify(pathinfo($original, PATHINFO_FILENAME));
  139. $base = substr($base === '' ? 'file' : $base, 0, 60);
  140. do {
  141. $name = $base . '-' . bin2hex(random_bytes(4)) . '.' . $ext;
  142. $target = $mediaDir . '/' . $name;
  143. } while (file_exists($target));
  144. ​
  145. if (!move_uploaded_file($file['tmp_name'], $target)) {
  146. media_respond(['success' => false, 'error' => "Couldn't save the file. Please try again."], 500);
  147. }
  148. @chmod($target, 0644);
  149. ​
  150. media_respond([
  151. 'success' => true,
  152. 'url' => '/media/' . rawurlencode($name),
  153. 'name' => $name,
  154. 'kind' => $kind,
  155. 'size' => media_format_bytes((int) $file['size']),
  156. ]);
  157. ​