download.php 1.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445
  1. <?php
  2. /**
  3. * Gallery archive download: /gallery/download.php?g=<slug>
  4. *
  5. * Hands the visitor a short-lived presigned URL for the gallery's prebuilt ZIP
  6. * and gets out of the way. The transfer runs browser ↔ S3, so a 3 GB archive is
  7. * unaffected by the host's 60 s execution limit and stays resumable — this
  8. * script only ever sends a redirect.
  9. *
  10. * An out-of-date archive is refused rather than served, mirroring the disabled
  11. * button on the gallery page: a client must not be handed a ZIP that silently
  12. * omits the photos uploaded since it was built.
  13. *
  14. * Every failure looks the same as an unknown gallery, so the endpoint reveals
  15. * nothing the visitor does not already know from the link.
  16. */
  17. define('SITE_BASE', '../');
  18. require dirname(__DIR__) . '/app/bootstrap.php';
  19. session_boot();
  20. $slug = (string)($_GET['g'] ?? '');
  21. $gallery = $slug !== '' ? gallery_load($slug) : null;
  22. $allowed = $gallery !== null
  23. && !gallery_is_expired($gallery)
  24. && !empty($gallery['downloads_enabled'])
  25. && !empty($gallery['archive']['key'])
  26. && !archive_is_stale($gallery)
  27. // The same per-gallery session unlock the viewer sets.
  28. && (empty($gallery['password_hash']) || !empty($_SESSION['gallery_unlocked'][$slug]));
  29. if (!$allowed) {
  30. http_response_code(404);
  31. public_header('Download not available');
  32. echo '<div class="gate"><div class="gate-card"><h1>Download not available</h1>'
  33. . '<p class="page-sub">This download does not exist or is not ready yet.</p></div></div>';
  34. public_footer();
  35. exit;
  36. }
  37. // Short TTL on purpose: the browser only needs the URL long enough to start the
  38. // transfer. A GET already in flight is not cut off when the signature expires.
  39. redirect(s3_presign_get((string)$gallery['archive']['key'], 900));