prepare("SELECT * FROM s3_mounts WHERE dav_user = ?");
$stmt->execute([$dav_user]);
$mount = $stmt->fetch();
if (!$mount || !password_verify($dav_pass, $mount['dav_pass'])) {
sleep(2);
header('WWW-Authenticate: Basic realm="WebDAV S3 Gateway"');
header('HTTP/1.0 401 Unauthorized');
exit;
}
// --- 2. Инициализация S3 ---
$s3 = new S3Client([
'version' => 'latest',
'region' => $mount['s3_region'],
'endpoint' => $mount['s3_endpoint'],
'credentials' => [
'key' => $mount['s3_key'],
'secret' => $mount['s3_secret'],
],
'use_path_style_endpoint' => true
]);
$bucket = $mount['s3_bucket'];
// --- 3. Парсинг путей ---
$baseUri = '/wd';
$requestUri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
// Вырезаем /wd из начала пути
$path = urldecode(substr($requestUri, strlen($baseUri)));
$path = ltrim($path, '/');
$method = $_SERVER['REQUEST_METHOD'];
// Форматы дат для Windows WebDAV
function getDavDates($timestamp = null) {
if (!$timestamp) $timestamp = time();
return [
'lastmod' => gmdate('D, d M Y H:i:s \G\M\T', $timestamp), // RFC 1123
'created' => gmdate('Y-m-d\TH:i:s\Z', $timestamp) // ISO 8601
];
}
// Генератор XML-узла для файла/папки (для идеальной совместимости с Windows)
function buildPropResponse($href, $isFolder, $size = 0, $lastModTime = null) {
$dates = getDavDates($lastModTime);
$href = implode('/', array_map('rawurlencode', explode('/', $href))); // Кодируем пути корректно
$xml = "
Для просмотра файлов через браузер используйте веб-интерфейс:
"; echo "Перейти в UI"; echo ""; } exit; } try { $params = ['Bucket' => $bucket, 'Key' => $path]; if (isset($_SERVER['HTTP_RANGE'])) { $params['Range'] = $_SERVER['HTTP_RANGE']; } $object = $s3->getObject($params); header('Content-Type: ' . $object['ContentType']); header('Content-Length: ' . $object['ContentLength']); header('Accept-Ranges: bytes'); // Заголовки кеширования, которые любит Windows header('Cache-Control: no-cache'); header('Pragma: no-cache'); if (isset($_SERVER['HTTP_RANGE']) && isset($object['ContentRange'])) { http_response_code(206); header('Content-Range: ' . $object['ContentRange']); } else { http_response_code(200); } if ($method === 'GET') { echo $object['Body']; } } catch (AwsException $e) { if ($e->getAwsErrorCode() === 'NoSuchKey') { http_response_code(404); } else { http_response_code(500); } } break; case 'PUT': // Снимаем лимит времени выполнения скрипта для загрузки больших файлов set_time_limit(0); ignore_user_abort(true); $tempFile = tempnam(sys_get_temp_dir(), 'dav_put_'); try { // 1. Сохраняем поток WebDAV во временный локальный файл $input = fopen('php://input', 'r'); $output = fopen($tempFile, 'w'); // stream_copy_to_stream не забивает оперативную память, а перекачивает данные напрямую stream_copy_to_stream($input, $output); fclose($input); fclose($output); // 2. Отправляем физический файл в S3 // Используем 'SourceFile' вместо 'Body', это включает Multipart-загрузку S3 под капотом $s3->putObject([ 'Bucket' => $bucket, 'Key' => $path, 'SourceFile' => $tempFile ]); // 3. Удаляем временный файл if (file_exists($tempFile)) { unlink($tempFile); } http_response_code(201); // Created } catch (Exception $e) { // Обязательно удаляем мусор, если произошла ошибка if (file_exists($tempFile)) { unlink($tempFile); } http_response_code(500); } break; case 'DELETE': try { // Для папок в S3 нужно удалять все вложенные объекты if (substr($path, -1) === '/') { $objects = $s3->listObjectsV2(['Bucket' => $bucket, 'Prefix' => $path]); if (isset($objects['Contents'])) { $deleteList = []; foreach ($objects['Contents'] as $obj) { $deleteList[] = ['Key' => $obj['Key']]; } $s3->deleteObjects([ 'Bucket' => $bucket, 'Delete' => ['Objects' => $deleteList] ]); } } else { $s3->deleteObject(['Bucket' => $bucket, 'Key' => $path]); } http_response_code(204); } catch (AwsException $e) { http_response_code(500); } break; case 'MKCOL': if (substr($path, -1) !== '/') $path .= '/'; try { $s3->putObject(['Bucket' => $bucket, 'Key' => $path, 'Body' => '']); http_response_code(201); } catch (AwsException $e) { http_response_code(500); } break; case 'LOCK': $token = 'urn:uuid:' . sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x', mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0x0fff) | 0x4000, mt_rand(0, 0x3fff) | 0x8000, mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)); header('Lock-Token: <' . $token . '>'); header('Content-Type: application/xml; charset="utf-8"'); echo "\n