false, 'ERROR' => 'method_not_allowed'], 405); } $body = readJsonBody(); $inviteToken = trim($body['InviteToken'] ?? ''); if (empty($inviteToken)) { jsonResponse(['OK' => false, 'ERROR' => 'invite_token_required'], 400); } // Look up the invite link $link = queryOne( "SELECT * FROM Hub_InviteLinks WHERE Token = ? LIMIT 1", [$inviteToken] ); if (!$link) { jsonResponse(['OK' => false, 'ERROR' => 'invalid_invite_token'], 404); } // Check if revoked if ($link['IsRevoked']) { jsonResponse(['OK' => false, 'ERROR' => 'invite_revoked'], 403); } // Check expiration if ($link['ExpiresAt'] && strtotime($link['ExpiresAt']) < time()) { jsonResponse(['OK' => false, 'ERROR' => 'invite_expired'], 403); } // Check max uses if ($link['MaxUses'] > 0 && $link['UseCount'] >= $link['MaxUses']) { jsonResponse(['OK' => false, 'ERROR' => 'invite_exhausted'], 403); } // Sanitize display name $displayName = trim($body['DisplayName'] ?? ''); if (empty($displayName)) { $displayName = 'Visitor'; } $displayName = substr($displayName, 0, 100); // Strip any HTML/script $displayName = htmlspecialchars($displayName, ENT_QUOTES, 'UTF-8'); // Generate visitor token $visitorToken = bin2hex(random_bytes(24)); // Create the visitor session queryTimed( "INSERT INTO Hub_Visitors (InviteLinkID, VisitorToken, DisplayName) VALUES (?, ?, ?)", [(int)$link['ID'], $visitorToken, $displayName] ); $visitorId = (int)lastInsertId(); // Increment use count on the invite link queryTimed( "UPDATE Hub_InviteLinks SET UseCount = UseCount + 1 WHERE ID = ?", [(int)$link['ID']] ); // Return the session info $allowedChannels = json_decode($link['AllowedChannels'], true); // Fetch channel details for the allowed channels $channelDetails = []; if (!empty($allowedChannels)) { $placeholders = implode(',', array_fill(0, count($allowedChannels), '?')); $channels = queryTimed( "SELECT ID, Name, DisplayName, Purpose, ChannelType FROM Hub_Channels WHERE ID IN ($placeholders) AND IsArchived = 0", array_map('intval', $allowedChannels) ); foreach ($channels as $ch) { $channelDetails[] = [ 'ID' => (int)$ch['ID'], 'Name' => $ch['Name'], 'DisplayName' => $ch['DisplayName'], 'Purpose' => $ch['Purpose'], 'ChannelType' => $ch['ChannelType'], ]; } } jsonResponse([ 'OK' => true, 'VisitorToken' => $visitorToken, 'VisitorID' => $visitorId, 'DisplayName' => $displayName, 'AllowedChannels' => $channelDetails, 'HostAddress' => $link['HostAddress'], ]);