false, 'error' => $msg)); exit; } function rand_hex($len = 12) { // 12 bytes -> 24 hex chars if (function_exists('openssl_random_pseudo_bytes')) { $bytes = openssl_random_pseudo_bytes($len, $strong); if ($bytes !== false && $strong === true) { return bin2hex($bytes); } } // Fallback (mt_rand) $out = ''; for ($i = 0; $i < $len; $i++) { $out .= chr(mt_rand(0, 255)); } return bin2hex($out); } if (!isset($_SERVER['REQUEST_METHOD']) || $_SERVER['REQUEST_METHOD'] !== 'POST') { fail(405, 'Use POST with multipart/form-data'); } // Validate JSON field if (!isset($_POST['data'])) { fail(400, 'Missing field: data'); } $rawJson = (string)$_POST['data']; $meta = json_decode($rawJson, true); if (json_last_error() !== JSON_ERROR_NONE) { $msg = function_exists('json_last_error_msg') ? json_last_error_msg() : 'JSON decode error'; fail(400, 'Invalid JSON in data: ' . $msg); } // Validate file field if (!isset($_FILES['file'])) { fail(400, 'Missing file field: file'); } $uf = $_FILES['file']; if (!is_array($uf) || !isset($uf['error']) || $uf['error'] !== UPLOAD_ERR_OK) { $errMap = array( UPLOAD_ERR_INI_SIZE => 'UPLOAD_ERR_INI_SIZE', UPLOAD_ERR_FORM_SIZE => 'UPLOAD_ERR_FORM_SIZE', UPLOAD_ERR_PARTIAL => 'UPLOAD_ERR_PARTIAL', UPLOAD_ERR_NO_FILE => 'UPLOAD_ERR_NO_FILE', UPLOAD_ERR_NO_TMP_DIR => 'UPLOAD_ERR_NO_TMP_DIR', UPLOAD_ERR_CANT_WRITE => 'UPLOAD_ERR_CANT_WRITE', UPLOAD_ERR_EXTENSION => 'UPLOAD_ERR_EXTENSION', ); $code = isset($uf['error']) ? $uf['error'] : 0; fail(400, 'File upload error: ' . (isset($errMap[$code]) ? $errMap[$code] : ('code ' . $code))); } // Prepare target directory $targetDir = __DIR__ . '/uploads'; if (!is_dir($targetDir) && !mkdir($targetDir, 0755, true)) { fail(500, 'Server cannot create upload directory'); } // Build a safe filename $origName = isset($uf['name']) ? $uf['name'] : 'file.bin'; $base = pathinfo($origName, PATHINFO_FILENAME); $ext = pathinfo($origName, PATHINFO_EXTENSION); $safeBase = preg_replace('/[^A-Za-z0-9._-]/', '_', $base); $safeExt = preg_replace('/[^A-Za-z0-9]/', '', $ext); $finalName = ($safeBase !== '' ? $safeBase : 'file') . '_' . rand_hex(6) . ($safeExt !== '' ? '.' . $safeExt : ''); $destPath = $targetDir . DIRECTORY_SEPARATOR . $finalName; // Store uploaded file if (!isset($uf['tmp_name']) || !is_uploaded_file($uf['tmp_name'])) { fail(400, 'Invalid upload (not an uploaded file)'); } if (!move_uploaded_file($uf['tmp_name'], $destPath)) { fail(500, 'Server failed to store uploaded file'); } // Compute hash & size for logging (optional) $size = @filesize($destPath); $hash = @hash_file('sha256', $destPath); // Log to Apache error_log $clientIp = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : 'unknown'; $logPayload = array( 'client_ip' => $clientIp, 'stored_as' => $finalName, 'stored_path' => $destPath, 'mime' => isset($uf['type']) ? $uf['type'] : null, 'size_bytes' => $size, 'sha256' => $hash, 'metadata' => $meta, ); error_log('[upload.php] ' . json_encode($logPayload)); // Respond echo json_encode(array( 'ok' => true, 'file' => array('name' => $finalName, 'size' => $size, 'sha256' => $hash), 'metadata' => $meta, ));