| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253 |
- <?php
- /**
- * Public guest upload endpoint used by the browser-side uploader (assets/admin.js)
- * on upload.php. Same one-multipart-POST-per-image contract as admin/api.php, but
- * authenticated by the per-gallery upload key instead of an admin session.
- *
- * Fields: slug, key, original (required), thumb (optional). Access requires the
- * gallery to have guest uploads enabled, the key to match, the gallery to be
- * unexpired, and — if the gallery has a password — the visitor to have unlocked
- * it in this session (via upload.php). Any failure returns a uniform 403.
- *
- * Uploads are image-only here, so a public link cannot store arbitrary files.
- */
- require __DIR__ . '/app/bootstrap.php';
- session_boot();
- if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
- json_response(['error' => 'POST only'], 405);
- }
- csrf_verify();
- // One image per request; a single file may still be large, so lift the time cap.
- @set_time_limit(0);
- // When a request body exceeds post_max_size, PHP discards $_POST and $_FILES
- // entirely — surface that as a clear 413 instead of a misleading "no file".
- if ((int)($_SERVER['CONTENT_LENGTH'] ?? 0) > 0 && !$_POST && !$_FILES) {
- json_response(['error' => 'Upload exceeds the server post_max_size limit'], 413);
- }
- $gallery = gallery_load((string)($_POST['slug'] ?? ''));
- // Uniform 403 for every access failure (no key, wrong key, expired, locked) so
- // the endpoint reveals nothing a guest shouldn't already know from the link.
- $authorized = $gallery !== null
- && !empty($gallery['upload_key'])
- && hash_equals((string)$gallery['upload_key'], (string)($_POST['key'] ?? ''))
- && !gallery_is_expired($gallery)
- && (empty($gallery['password_hash']) || !empty($_SESSION['gallery_unlocked'][$gallery['slug']]));
- if (!$authorized) {
- json_response(['error' => 'Not authorized'], 403);
- }
- // Image-only: a public link must not be usable to store arbitrary file types.
- [$status, $payload] = gallery_store_s3_upload(
- $gallery,
- $_FILES['original'] ?? null,
- $_FILES['thumb'] ?? null,
- true
- );
- json_response($payload, $status);
|