commit d35e0bbf742047880e04b5b39f6a1944a9ce1dbe Author: hyzen Date: Mon Feb 16 22:33:08 2026 +0530 Init diff --git a/index.php b/index.php new file mode 100644 index 0000000..3451955 --- /dev/null +++ b/index.php @@ -0,0 +1,294 @@ + $RATE_LIMIT) { + return false; + } + + $usage_data[] = [ + 'timestamp' => $current_time, + 'size' => $file_size, + ]; + + file_put_contents($rate_file, json_encode($usage_data, JSON_PRETTY_PRINT)); + + return true; +} + +function get_remaining_quota($ip) { + global $RATE_LIMIT_DIR, $RATE_LIMIT, $RATE_LIMIT_PERIOD; + + $safe_ip = preg_replace('/[^a-zA-Z0-9\-_\.]/', '_', $ip); + $rate_file = $RATE_LIMIT_DIR . $safe_ip . '.json'; + + if (!file_exists($rate_file)) { + return $RATE_LIMIT; + } + + $content = file_get_contents($rate_file); + $usage_data = json_decode($content, true); + if (!is_array($usage_data)) { + return $RATE_LIMIT; + } + + $current_time = time(); + $usage_data = array_filter($usage_data, function($entry) use ($current_time, $RATE_LIMIT_PERIOD) { + return ($current_time - $entry['timestamp']) < $RATE_LIMIT_PERIOD; + }); + + $total_usage = array_sum(array_column($usage_data, 'size')); + return max(0, $RATE_LIMIT - $total_usage); +} + +// ------------------------- +// CORS HEADERS +// ------------------------- +header('Access-Control-Allow-Methods: GET, POST, OPTIONS'); +header('Access-Control-Allow-Headers: Content-Type, Authorization'); +header('Access-Control-Max-Age: 7200'); +header('Access-Control-Allow-Origin: *'); + +// ------------------------- +// DETECT REQUEST TYPE +// ------------------------- +$request_method = $_SERVER['REQUEST_METHOD']; + +// ------------------------- +// QUOTA ENDPOINT +// ------------------------- +if ($request_method === 'GET' && isset($_GET['quota'])) { + $client_ip = get_client_ip(); + $remaining = get_remaining_quota($client_ip); + $total = $RATE_LIMIT; + header('Content-Type: application/json'); + echo json_encode([ + 'remaining' => $remaining, + 'total' => $total, + 'used' => $total - $remaining, + ]); + exit; +} + +// ------------------------- +// FILE DOWNLOAD (GET/HEAD) +// ------------------------- +else if ($request_method === 'GET' || $request_method === 'HEAD') { + $upload_file_name = substr($_SERVER['PHP_SELF'], strlen($_SERVER['SCRIPT_NAME']) + 1); + $sanitized_name = basename($upload_file_name); + $store_file_name = $UPLOAD_DIR . $sanitized_name; + + if (file_exists($store_file_name)) { + $mime_type = @file_get_contents($store_file_name . '-type'); + if ($mime_type === FALSE) { + $mime_type = 'application/octet-stream'; + header('Content-Disposition: attachment'); + } + + header('Content-Type: ' . $mime_type); + header('Content-Length: ' . filesize($store_file_name)); + header("Content-Security-Policy: default-src 'none'"); + header("X-Content-Security-Policy: default-src 'none'"); + header("X-WebKit-CSP: default-src 'none'"); + + if ($request_method !== 'HEAD') { + readfile($store_file_name); + } + } else { + header('HTTP/1.0 404 Not Found'); + } + exit; +} + +// ------------------------- +// OPTIONS (CORS preflight) +// ------------------------- +else if ($request_method === 'OPTIONS') { + exit; +} + +// ------------------------- +// BASIC HTTP AUTH (for POST uploads/deletes) +// ------------------------- +else if ($request_method === 'POST') { + if (!isset($_SERVER['PHP_AUTH_USER']) || + $_SERVER['PHP_AUTH_USER'] !== $AUTH_USER || + $_SERVER['PHP_AUTH_PW'] !== $AUTH_PASS) { + header('WWW-Authenticate: Basic realm="Uploader"'); + header('HTTP/1.0 401 Unauthorized'); + echo 'Authentication required.'; + exit; + } + + // ------------------------- + // FILE DELETE HANDLING + // ------------------------- + if (isset($_POST['delete'])) { + $fileToDelete = basename($_POST['delete']); + $target = $UPLOAD_DIR . $fileToDelete; + + if (file_exists($target)) { + if (unlink($target)) { + @unlink($target . '-type'); + echo 'Deleted successfully'; + exit; + } else { + http_response_code(500); + echo 'Failed to delete file.'; + exit; + } + } else { + http_response_code(404); + echo 'File not found.'; + exit; + } + } + + // ------------------------- + // FILE UPLOAD HANDLING + // ------------------------- + if (isset($_FILES['file'])) { + $file = $_FILES['file']; + + // Check for errors + if ($file['error'] !== UPLOAD_ERR_OK) { + http_response_code(400); + echo 'Upload error.'; + exit; + } + + // Optional MIME type check + if (!empty($ALLOWED_TYPES) && !in_array($file['type'], $ALLOWED_TYPES)) { + http_response_code(400); + echo 'File type not allowed.'; + exit; + } + + // Rate limiting check + $client_ip = get_client_ip(); + if (!check_rate_limit($client_ip, $file['size'])) { + $remaining = get_remaining_quota($client_ip); + http_response_code(429); + echo 'Rate limit exceeded. You have ' . round($remaining / 1024 / 1024, 2) . ' MB remaining in your 24-hour quota.'; + exit; + } + + // Check if upload directory exists + if (!is_dir($UPLOAD_DIR)) { + http_response_code(500); + echo 'Upload directory not available.'; + exit; + } + + // ------------------------- + // Generate short random string and append to original filename + // ------------------------- + function random_string($length = 5) { + $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; + $str = ''; + for ($i = 0; $i < $length; $i++) { + $str .= $chars[random_int(0, strlen($chars) - 1)]; + } + return $str; + } + + $originalName = basename($file['name']); + $ext = pathinfo($originalName, PATHINFO_EXTENSION); + $nameWithoutExt = pathinfo($originalName, PATHINFO_FILENAME); + $nameWithoutExt = preg_replace('/[^A-Za-z0-9_\-]/', '_', $nameWithoutExt); + + // Generate unique filename + do { + $random = random_string(); + $filename = $nameWithoutExt . '_' . $random . ($ext ? '.' . $ext : ''); + $target = $UPLOAD_DIR . $filename; + } while (file_exists($target)); + + // Move uploaded file + if (!move_uploaded_file($file['tmp_name'], $target)) { + http_response_code(500); + echo 'Failed to save file.'; + exit; + } + + // Store MIME type + file_put_contents($target . '-type', $file['type']); + + // Return public URL + $url = 'https://' . $_SERVER['HTTP_HOST'] . '/files/' . basename($target); + echo $url; + exit; + } + + // ------------------------- + // DEFAULT RESPONSE + // ------------------------- + http_response_code(405); + echo 'Only POST with file upload is supported.'; +} + +// ------------------------- +// INVALID REQUEST METHOD +// ------------------------- +else { + header('HTTP/1.0 400 Bad Request'); +} +?> diff --git a/nginx_conf b/nginx_conf new file mode 100644 index 0000000..cfb4add --- /dev/null +++ b/nginx_conf @@ -0,0 +1,37 @@ +# Nginx configuration for share.freedoms4.org +server { + listen 80; + server_name share.freedoms4.org; + + root /var/www/share; + index index.html index.php; + + # Default upload limit (adjust to match your $RATE_LIMIT in index.php) + client_max_body_size 1024m; + client_body_timeout 600s; + + # PHP handler + location ~ \.php$ { + include snippets/fastcgi-php.conf; + fastcgi_pass unix:/var/run/php/php8.2-fpm.sock; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + include fastcgi_params; + fastcgi_request_buffering off; + fastcgi_buffering off; + fastcgi_read_timeout 3600s; + fastcgi_send_timeout 3600s; + } + + # Serve uploaded files directly (no auth required for downloads) + location ~ ^/files/ { + try_files $uri =404; + } + + # Default location + location / { + if ($request_method = POST) { + rewrite ^ /index.php last; + } + try_files $uri $uri/ /index.php$is_args$args; + } +}