diff --git a/viewerfeed.php b/viewerfeed.php new file mode 100644 index 0000000..59edb6a --- /dev/null +++ b/viewerfeed.php @@ -0,0 +1,373 @@ +connect_errno) { + http_response_code(500); + echo '

Database connection failed.

'; + exit; + } + + $db->set_charset('utf8mb4'); + return $db; +} + +function is_uuid(string $value): bool +{ + return preg_match('/^[0-9a-fA-F-]{36}$/', $value) === 1; +} + +function normalize_lookup_input(string $value): string +{ + $v = trim(urldecode($value)); + + // Some viewers/grids may pass values wrapped in brackets, e.g. [uuid]. + if (strlen($v) >= 2 && $v[0] === '[' && $v[strlen($v) - 1] === ']') { + $v = substr($v, 1, -1); + $v = trim($v); + } + + return $v; +} + +function resolve_user_uuid(mysqli $db, string $nameOrUuid): ?array +{ + $input = normalize_lookup_input($nameOrUuid); + if ($input === '') { + return null; + } + + if (is_uuid($input)) { + $stmt = $db->prepare('SELECT PrincipalID, FirstName, LastName FROM UserAccounts WHERE PrincipalID = ? LIMIT 1'); + if ($stmt !== false) { + $stmt->bind_param('s', $input); + $stmt->execute(); + $stmt->bind_result($uuid, $first, $last); + $found = $stmt->fetch(); + $stmt->close(); + if ($found) { + return [ + 'uuid' => (string)$uuid, + 'name' => trim((string)$first . ' ' . (string)$last), + ]; + } + } + + // UUID was passed by viewer but account row could not be resolved. + // Continue with UUID so profile/picks queries can still work. + return [ + 'uuid' => $input, + 'name' => $input, + ]; + } + + $normalized = strtolower(str_replace(' ', '.', $input)); + + $sql = 'SELECT PrincipalID, FirstName, LastName + FROM UserAccounts + WHERE LOWER(CONCAT(FirstName, ".", LastName)) = ? + OR LOWER(CONCAT(FirstName, " ", LastName)) = ? + LIMIT 1'; + + $stmt = $db->prepare($sql); + if ($stmt === false) { + return null; + } + $stmt->bind_param('ss', $normalized, $normalized); + $stmt->execute(); + $stmt->bind_result($uuid, $first, $last); + $found = $stmt->fetch(); + $stmt->close(); + + if (!$found) { + return null; + } + + return [ + 'uuid' => (string)$uuid, + 'name' => trim((string)$first . ' ' . (string)$last), + ]; +} + +function pick_lookup_value(): string +{ + // Prefer explicit UUID-style parameters when available. + $uuidParam = normalize_lookup_input((string)($_GET['uuid'] ?? '')); + $avatarIdParam = normalize_lookup_input((string)($_GET['avatar_id'] ?? '')); + $idParam = normalize_lookup_input((string)($_GET['id'] ?? '')); + $nameParam = normalize_lookup_input((string)($_GET['name'] ?? '')); + $userParam = normalize_lookup_input((string)($_GET['user'] ?? '')); + + foreach ([$uuidParam, $avatarIdParam, $idParam] as $candidate) { + if ($candidate !== '' && is_uuid($candidate)) { + return $candidate; + } + } + + if ($nameParam !== '') { + return $nameParam; + } + + if ($userParam !== '') { + return $userParam; + } + + foreach ([$uuidParam, $avatarIdParam, $idParam] as $candidate) { + if ($candidate !== '') { + return $candidate; + } + } + + return ''; +} + +function load_user_profile(mysqli $db, string $uuid): ?array +{ + $stmt = $db->prepare('SELECT * FROM userprofile WHERE useruuid = ? LIMIT 1'); + if ($stmt === false) { + return null; + } + + $stmt->bind_param('s', $uuid); + $stmt->execute(); + $result = $stmt->get_result(); + $profile = $result ? $result->fetch_assoc() : null; + $stmt->close(); + + return $profile ?: null; +} + +function load_user_picks(mysqli $db, string $uuid): array +{ + $sql = 'SELECT pickuuid, name, description, simname, posglobal, snapshotuuid, gatekeeper, enabled, sortorder + FROM userpicks + WHERE creatoruuid = ? + ORDER BY sortorder ASC, name ASC'; + + $stmt = $db->prepare($sql); + if ($stmt === false) { + return []; + } + + $stmt->bind_param('s', $uuid); + $stmt->execute(); + $result = $stmt->get_result(); + + $rows = []; + if ($result) { + while ($row = $result->fetch_assoc()) { + $rows[] = $row; + } + } + + $stmt->close(); + return $rows; +} + +$feedOnly = parse_bool((string)($_GET['feed_only'] ?? '0')); +$diag = parse_bool((string)($_GET['diag'] ?? '0')); +$lookup = pick_lookup_value(); + +$db = connect_main_db(); +$user = $lookup !== '' ? resolve_user_uuid($db, $lookup) : null; +$profile = null; +$picks = []; + +if ($user !== null) { + $profile = load_user_profile($db, $user['uuid']); + $picks = load_user_picks($db, $user['uuid']); +} + +$db->close(); + +$pageTitle = $user !== null ? ($user['name'] . ' - Viewer Feed') : 'Viewer Feed Bridge'; + +?> + + + + + + <?php echo h($pageTitle); ?> + + + +
+
+

OpenSim Viewer Feed Bridge

+

Use with Firestorm Web Profile URL, for example:
/oswebinterface/viewerfeed.php?name=[AGENT_NAME]

+ + +

No avatar provided. Add ?name=first.last or ?uuid=....

+ +

Avatar not found for:

+ +

+

UUID:

+ + + +
+

Diagnostics

+

raw query:

+

remote addr:

+

user-agent:

+ +
+ + + +
+

Profile

+

About:

+

First Life:

+

Profile URL:

+
+ +
+

No userprofile row found for this user.

+
+ + +
+

Picks ()

+ + +

No picks available.

+ + +
+

+
+ sim: + enabled: + sort: +
+

+

pickuuid:

+
+ + +
+ +
+ +