'gone', 'reason' => $message] : ['error' => $message])); } if ($_SERVER['REQUEST_METHOD'] !== 'POST') { http_response_code(405); shareDownloadStop('Open the link you were sent, and download from there.'); } $input = json_decode((string) file_get_contents('php://input'), true); if (!is_array($input)) { $input = $_POST; } shareSweep(); $secret = (string) ($input['key'] ?? ''); $row = shareFind(intval($input['id'] ?? 0), $secret); if (!$row || !in_array($row['item_type'], ['File', 'Export'], true)) { shareDownloadStop('The link is not right - check you have all of it.', true); } $state = shareState($row); $again = shareOpenedHere($row); if (!($state === 'active' || ($state === 'used' && $again))) { shareDownloadStop($state === 'expired' ? 'It has expired.' : ($state === 'used' ? 'It has been downloaded as many times as it allowed.' : 'It was turned off.'), true); } if (!shareVerified($row)) { shareDownloadStop('Enter the passphrase or code first.'); } if ($row['item_type'] === 'Export') { // The PDF made with the link - opened with its secret, read before a view is used $pdf = shareExportRead($row['item_id'], shareContentKey($secret)); if ($pdf === null) { shareDownloadStop('The documentation it shared is no longer there - ask for a new link.', true); } $file = ['file_name' => shareName($row)]; $what = 'IT documentation ' . $file['file_name']; } else { $file = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT file_name, file_reference_name, file_client_id FROM files WHERE file_id = " . intval($row['item_related_id']) . " AND file_client_id = " . intval($row['item_client_id']) . " LIMIT 1")); $client_dir = realpath(dirname(__DIR__) . '/uploads/clients/' . intval($row['item_client_id'])); $path = $file ? realpath(dirname(__DIR__) . '/uploads/clients/' . intval($row['item_client_id']) . '/' . basename($file['file_reference_name'])) : false; // Don't use a view on a file that is missing from disk if (!$file || !$client_dir || !$path || strpos($path, $client_dir . DIRECTORY_SEPARATOR) !== 0 || !is_readable($path)) { shareDownloadStop('The file it shared has been removed.', true); } $what = 'File ' . $file['file_name']; } if (!$again) { // The claim, before a byte is sent - racing requests cannot both win the last view if (!shareClaim($row['item_id'])) { shareDownloadStop('It has been downloaded as many times as it allowed.', true); } shareSessionMark($row['item_id'], 'opened'); $who = shareProtection($row) === 'code' ? ' by ' . $row['item_recipient'] . ' (emailed code)' : (shareProtection($row) === 'passphrase' ? ' with the passphrase' : ''); logAudit('Share', 'Download', escapeSql("Downloaded$who"), intval($row['item_client_id']), intval($row['item_id'])); shareNotifyCreator($row, 'Shared link opened', "$what was downloaded$who - " . $row['client_name']); } // What the page says after - whether it downloads again $after_row = shareRow($row['item_id']); $left = intval($after_row['item_view_limit']) - intval($after_row['item_views']); $after = intval($after_row['item_view_limit']) && $left <= 0 ? 'That was the last download this link allows.' : (intval($after_row['item_view_limit']) ? "It can be downloaded $left more " . ($left === 1 ? 'time' : 'times') . '.' : 'The link keeps working until ' . date('M j \a\t g:i A', strtotime($after_row['item_expire_at'])) . '.'); $name = str_replace(["\r", "\n", '"'], ['', '', "'"], (string) $file['file_name']); $ascii = preg_replace('/[^\x20-\x7E]/', '_', $name); header('Content-Type: ' . ($row['item_type'] === 'Export' ? 'application/pdf' : 'application/octet-stream')); header('Content-Disposition: attachment; filename="' . $ascii . '"; filename*=UTF-8\'\'' . rawurlencode($name)); header('Content-Length: ' . ($row['item_type'] === 'Export' ? strlen($pdf) : filesize($path))); header('X-Content-Type-Options: nosniff'); header('X-Share-After: ' . rawurlencode($after)); while (ob_get_level()) { ob_end_clean(); } if ($row['item_type'] === 'Export') { echo $pdf; } else { readfile($path); } exit;