From 58694d6f31dad913b83784ad19e0810810f7737f Mon Sep 17 00:00:00 2001 From: matjaeck <59416324+matjaeck@users.noreply.github.com> Date: Sun, 10 Jan 2021 22:15:44 +0100 Subject: [PATCH 1/5] Refactor avatar handling. PhpBB's driver->get_data() method does not expose absolute URLs and we need special handling for every avatar type due to each using a different URL pattern. We therefore have to hardcode the avatar type name and path fragments and depend on internals with which we will need to maintain compatibility in case of future upstream changes. Using our own implementation with less overhead does not introduce further costs in this case. --- controller/main.php | 14 +++-- core/core.php | 128 +++++++++++++------------------------------- 2 files changed, 48 insertions(+), 94 deletions(-) diff --git a/controller/main.php b/controller/main.php index 17546a7..fc9feba 100644 --- a/controller/main.php +++ b/controller/main.php @@ -63,6 +63,12 @@ public function fetchinfo($type, $fingerprint) return $this->get_response("Error: No profile data"); } + $avatar = [ + 'src' => $this->core->get_avatar_url($data['user_avatar'], $data['user_avatar_type'], $data['user_avatar_width'], $data['user_avatar_height']), + 'width' => $data['user_avatar_width'], + 'height' => $data['user_avatar_height'] + ]; + // Retrieve badge data $sql = $this->core->get_ubadge_sql_by_key($fingerprint); if (!($result = $this->db->sql_query_limit($sql, $this->config['max_profile_badges']))) @@ -92,11 +98,11 @@ public function fetchinfo($type, $fingerprint) $yaml .= "\tProfileName: " . $data['username'] . "\n"; $yaml .= "\tProfileRank: Registered User\n"; $yaml .= "\tAvatar:\n"; - if ($avatar_data = $this->core->get_avatar_data($data)) + if ($avatar['src']) { - $yaml .= "\t\tSrc: " . $avatar_data['src'] . "\n"; - $yaml .= "\t\tWidth: " . $avatar_data['width'] . "\n"; - $yaml .= "\t\tHeight:" . $avatar_data['height'] . "\n"; + $yaml .= "\t\tSrc: " . $avatar['src'] . "\n"; + $yaml .= "\t\tWidth: " . $avatar['width'] . "\n"; + $yaml .= "\t\tHeight:" . $avatar['height'] . "\n"; } $yaml .= "\tBadges:\n"; diff --git a/core/core.php b/core/core.php index 663f173..0e1857e 100644 --- a/core/core.php +++ b/core/core.php @@ -187,102 +187,50 @@ public function validate_badge_order($user_id) } /** - * Gets the url to the user avatar. - * Returns false on failure or if there is no avatar. + * Returns the absolute url to profile images (avatars) or an empty string if not found. * - * @param array $user_data Avatar data from the user table - * @return array|boolean + * @param string $user_avatar Filename or e-mail address + * @param string|int $avatar_type Internal "driver" name or legacy constants with integer values. + * @param int $width + * @param int $height + * @return string */ - public function get_avatar_data($user_data) + public function get_avatar_url($user_avatar, $avatar_type, $width, $height) { - if (!$this->config['allow_avatar']) - { - return false; - } - - $row = [ - 'avatar' => $user_data['user_avatar'], - 'avatar_width' => $user_data['user_avatar_width'], - 'avatar_height' => $user_data['user_avatar_height'], - ]; + // Avatar not allowed or not set by user + if (!$this->config['allow_avatar'] || !$user_avatar) + return ""; - $driver = $this->avatar_manager->get_driver($user_data['user_avatar_type']); - - if (!$driver) + switch ($avatar_type) { - return false; - } - - $avatar_data = $driver->get_data($row); - - if ($user_data['user_avatar_type'] === 'avatar.driver.gravatar') - { - $avatar_data['src'] = $this->get_gravatar_url($row); - } - else if ($user_data['user_avatar_type'] === 'avatar.driver.upload') - { - $avatar_data['src'] = $this->get_upload_avatar_url($user_data['user_avatar']); - } - else if ($user_data['user_avatar_type'] === 'avatar.driver.local') - { - $avatar_data['src'] = $this->get_local_avatar_url($user_data['user_avatar']); - } - - return $avatar_data; - } - - /** - * Gets the full URL for a user uploaded avatar. - * - * @param $user_avatar User avatar data - * @return string Avatar URL - */ - protected function get_upload_avatar_url($user_avatar) - { - return generate_board_url() . '/download/file.php' . '?avatar=' . $user_avatar; - } - - /** - * Gets the full URL for a gallery avatar. - * - * @param $user_avatar User avatar data - * @return string Avatar URL - */ - protected function get_local_avatar_url($user_avatar) - { - return generate_board_url() . '/' . $this->config['avatar_gallery_path'] . '/' . $user_avatar; - } - - /** - * Gets the URL for a gravatar. - * Essentially a copy of the protected method form avatar.driver.gravatar - * - * @param $row User data - * @return string Gravatar URL - */ - protected function get_gravatar_url($row) - { - global $phpbb_dispatcher; - - $url = 'https://secure.gravatar.com/avatar/'; - $url .= md5(strtolower(trim($row['avatar']))); + // 1 + case AVATAR_UPLOAD: + case 'avatar.driver.upload': + return generate_board_url() . '/download/file.php' . '?avatar=' . $user_avatar; + + // 2 + case AVATAR_REMOTE: + case 'avatar.driver.remote': + return $user_avatar; + + // 3 + case AVATAR_GALLERY: + case 'avatar.driver.local': + return generate_board_url() . '/' . $this->config['avatar_gallery_path'] . '/' . $user_avatar; + + // No legacy value + case 'avatar.driver.gravatar': + { + $url = 'https://secure.gravatar.com/avatar/' . md5(strtolower(trim($user_avatar))); + if ($width || $height) + $url .= '?s=' . max($width, $height); + + return $url; + } - if ($row['avatar_width'] || $row['avatar_height']) - { - $url .= '?s=' . max($row['avatar_width'], $row['avatar_height']); + // Invalid data + default: + return ""; } - - /** - * Modify gravatar url - * - * @event core.get_gravatar_url_after - * @var string row User data or group data - * @var string url Gravatar URL - * @since 3.1.7-RC1 - */ - $vars = array('row', 'url'); - extract($phpbb_dispatcher->trigger_event('core.get_gravatar_url_after', compact($vars))); - - return $url; } } From 519a2116e91e46f42834e72253184a37f641f358 Mon Sep 17 00:00:00 2001 From: matjaeck <59416324+matjaeck@users.noreply.github.com> Date: Sun, 10 Jan 2021 23:33:04 +0100 Subject: [PATCH 2/5] Move data access out of switch block. --- controller/main.php | 80 ++++++++++++++++++++++----------------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/controller/main.php b/controller/main.php index fc9feba..3320e0c 100644 --- a/controller/main.php +++ b/controller/main.php @@ -46,50 +46,50 @@ public function __construct(\openra\openrauseraccounts\core\core $core, \phpbb\d */ public function fetchinfo($type, $fingerprint) { - switch ($type) + // Retrieve profile data + $sql = $this->core->get_info_sql($fingerprint); + if (!($result = $this->db->sql_query($sql))) { - case 'info': - { - // Retrieve profile data - $sql = $this->core->get_info_sql($fingerprint); - if (!($result = $this->db->sql_query($sql))) - { - return $this->get_response("Error: Failed to query profile data"); - } - $data = $this->db->sql_fetchrow($result); - $this->db->sql_freeresult($result); - if (!$data) - { - return $this->get_response("Error: No profile data"); - } + return $this->get_response("Error: Failed to query profile data"); + } + $data = $this->db->sql_fetchrow($result); + $this->db->sql_freeresult($result); + if (!$data) + { + return $this->get_response("Error: No profile data"); + } - $avatar = [ - 'src' => $this->core->get_avatar_url($data['user_avatar'], $data['user_avatar_type'], $data['user_avatar_width'], $data['user_avatar_height']), - 'width' => $data['user_avatar_width'], - 'height' => $data['user_avatar_height'] - ]; + $avatar = [ + 'src' => $this->core->get_avatar_url($data['user_avatar'], $data['user_avatar_type'], $data['user_avatar_width'], $data['user_avatar_height']), + 'width' => $data['user_avatar_width'], + 'height' => $data['user_avatar_height'] + ]; - // Retrieve badge data - $sql = $this->core->get_ubadge_sql_by_key($fingerprint); - if (!($result = $this->db->sql_query_limit($sql, $this->config['max_profile_badges']))) - { - return $this->get_response("Error: Failed to query badge data"); - } - // Store all the badge data in an array to loop over it later - $badges = array(); - while ($row = $this->db->sql_fetchrow($result)) - { - $badges[] = $row; - } - $this->db->sql_freeresult($result); - - // Update last accessed time - $sql = $this->core->get_update_sql($fingerprint); - if (!($result = $this->db->sql_query($sql))) - { - return $this->get_response("Error: Failed to update last accessed time"); - } + // Retrieve badge data + $sql = $this->core->get_ubadge_sql_by_key($fingerprint); + if (!($result = $this->db->sql_query_limit($sql, $this->config['max_profile_badges']))) + { + return $this->get_response("Error: Failed to query badge data"); + } + // Store all the badge data in an array to loop over it later + $badges = array(); + while ($row = $this->db->sql_fetchrow($result)) + { + $badges[] = $row; + } + $this->db->sql_freeresult($result); + // Update last accessed time + $sql = $this->core->get_update_sql($fingerprint); + if (!($result = $this->db->sql_query($sql))) + { + return $this->get_response("Error: Failed to update last accessed time"); + } + + switch ($type) + { + case 'info': + { $yaml = "Player:\n"; $yaml .= "\tFingerprint: " . $data['fingerprint'] . "\n"; $yaml .= "\tPublicKey: " . base64_encode($data['public_key']) . "\n"; From b3d966e31e1ba4daf39a3493c391df7712099d56 Mon Sep 17 00:00:00 2001 From: matjaeck <59416324+matjaeck@users.noreply.github.com> Date: Sun, 10 Jan 2021 23:40:44 +0100 Subject: [PATCH 3/5] Simplify data access. --- controller/main.php | 26 +++++++------------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/controller/main.php b/controller/main.php index 3320e0c..840e357 100644 --- a/controller/main.php +++ b/controller/main.php @@ -46,18 +46,13 @@ public function __construct(\openra\openrauseraccounts\core\core $core, \phpbb\d */ public function fetchinfo($type, $fingerprint) { - // Retrieve profile data + // Profile data $sql = $this->core->get_info_sql($fingerprint); - if (!($result = $this->db->sql_query($sql))) - { - return $this->get_response("Error: Failed to query profile data"); - } + $result = $this->db->sql_query($sql); $data = $this->db->sql_fetchrow($result); $this->db->sql_freeresult($result); if (!$data) - { return $this->get_response("Error: No profile data"); - } $avatar = [ 'src' => $this->core->get_avatar_url($data['user_avatar'], $data['user_avatar_type'], $data['user_avatar_width'], $data['user_avatar_height']), @@ -65,14 +60,10 @@ public function fetchinfo($type, $fingerprint) 'height' => $data['user_avatar_height'] ]; - // Retrieve badge data + // Badge data $sql = $this->core->get_ubadge_sql_by_key($fingerprint); - if (!($result = $this->db->sql_query_limit($sql, $this->config['max_profile_badges']))) - { - return $this->get_response("Error: Failed to query badge data"); - } - // Store all the badge data in an array to loop over it later - $badges = array(); + $result = $this->db->sql_query_limit($sql, $this->config['max_profile_badges']); + $badges = []; while ($row = $this->db->sql_fetchrow($result)) { $badges[] = $row; @@ -81,11 +72,8 @@ public function fetchinfo($type, $fingerprint) // Update last accessed time $sql = $this->core->get_update_sql($fingerprint); - if (!($result = $this->db->sql_query($sql))) - { - return $this->get_response("Error: Failed to update last accessed time"); - } - + $result = $this->db->sql_query($sql); + switch ($type) { case 'info': From aa58193049286fcd2e996f377e67169fd729139f Mon Sep 17 00:00:00 2001 From: matjaeck <59416324+matjaeck@users.noreply.github.com> Date: Mon, 11 Jan 2021 00:50:44 +0100 Subject: [PATCH 4/5] Add new openra/{type}/{fp}/{format} response format parameter. We are not using the special built-in '_format' parameter because we need to set the response headers anyway manually for the default 'MiniYAML' format. See https://symfony.com/doc/2.8/routing.html#routing-format-param for Symfony's special '_format' parameter. --- config/routing.yml | 4 ++-- controller/main.php | 16 +++++++++------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/config/routing.yml b/config/routing.yml index 52cc978..95e26b8 100644 --- a/config/routing.yml +++ b/config/routing.yml @@ -1,3 +1,3 @@ openra_openrauseraccounts_controller: - path: /openra/{type}/{fingerprint} - defaults: { _controller: openra.openrauseraccounts.controller:fetchinfo } + path: /openra/{type}/{fingerprint}/{format} + defaults: { _controller: openra.openrauseraccounts.controller:fetchinfo, format: MiniYAML } diff --git a/controller/main.php b/controller/main.php index 840e357..f081a0d 100644 --- a/controller/main.php +++ b/controller/main.php @@ -38,13 +38,15 @@ public function __construct(\openra\openrauseraccounts\core\core $core, \phpbb\d } /** - * Controller for route /openra/{$type}/{$fingerprint} + * Controller for route /openra/{$type}/{$fingerprint}/{format} * - * @param string $type, $fingerprint + * @param string $type + * @param string $fingerprint + * @param string $format Response format, default value is 'MiniYAML' * * @return \Symfony\Component\HttpFoundation\Response A Symfony Response object */ - public function fetchinfo($type, $fingerprint) + public function fetchinfo($type, $fingerprint, $format) { // Profile data $sql = $this->core->get_info_sql($fingerprint); @@ -52,7 +54,7 @@ public function fetchinfo($type, $fingerprint) $data = $this->db->sql_fetchrow($result); $this->db->sql_freeresult($result); if (!$data) - return $this->get_response("Error: No profile data"); + return $this->get_response("Error: No profile data", $format); $avatar = [ 'src' => $this->core->get_avatar_url($data['user_avatar'], $data['user_avatar_type'], $data['user_avatar_width'], $data['user_avatar_height']), @@ -106,19 +108,19 @@ public function fetchinfo($type, $fingerprint) } } - return $this->get_response($yaml); + return $this->get_response($yaml, $format); break; } default: { - return $this->get_response("Error: Unknown route"); + return $this->get_response("Error: Unknown route", $format); } } } - public function get_response($content) + public function get_response($content, $format) { $response = new Response($content); $response->headers->set('Content-Type', 'Content-type: text/plain; charset=utf-8'); From 110c93e51b1fb9b430dce98e7ef4cb93b0a43cc6 Mon Sep 17 00:00:00 2001 From: matjaeck <59416324+matjaeck@users.noreply.github.com> Date: Mon, 11 Jan 2021 01:04:16 +0100 Subject: [PATCH 5/5] Add support for JSON response format. --- controller/main.php | 75 +++++++++++++++++++++++++++++---------------- 1 file changed, 49 insertions(+), 26 deletions(-) diff --git a/controller/main.php b/controller/main.php index f081a0d..ca01adf 100644 --- a/controller/main.php +++ b/controller/main.php @@ -80,35 +80,51 @@ public function fetchinfo($type, $fingerprint, $format) { case 'info': { - $yaml = "Player:\n"; - $yaml .= "\tFingerprint: " . $data['fingerprint'] . "\n"; - $yaml .= "\tPublicKey: " . base64_encode($data['public_key']) . "\n"; - $yaml .= "\tKeyRevoked: " . ($data['revoked'] ? 'true' : 'false') . "\n"; - $yaml .= "\tProfileID: " . $data['user_id'] . "\n"; - $yaml .= "\tProfileName: " . $data['username'] . "\n"; - $yaml .= "\tProfileRank: Registered User\n"; - $yaml .= "\tAvatar:\n"; - if ($avatar['src']) + if (strtolower($format) !== "json") { - $yaml .= "\t\tSrc: " . $avatar['src'] . "\n"; - $yaml .= "\t\tWidth: " . $avatar['width'] . "\n"; - $yaml .= "\t\tHeight:" . $avatar['height'] . "\n"; - } - - $yaml .= "\tBadges:\n"; - if ($badges) - { - $i = 0; - foreach ($badges as $badge) + $content = "Player:\n"; + $content .= "\tFingerprint: " . $data['fingerprint'] . "\n"; + $content .= "\tPublicKey: " . base64_encode($data['public_key']) . "\n"; + $content .= "\tKeyRevoked: " . ($data['revoked'] ? 'true' : 'false') . "\n"; + $content .= "\tProfileID: " . $data['user_id'] . "\n"; + $content .= "\tProfileName: " . $data['username'] . "\n"; + $content .= "\tProfileRank: Registered User\n"; + $content .= "\tAvatar:\n"; + if ($avatar['src']) + { + $content .= "\t\tSrc: " . $avatar['src'] . "\n"; + $content .= "\t\tWidth: " . $avatar['width'] . "\n"; + $content .= "\t\tHeight:" . $avatar['height'] . "\n"; + } + + $content .= "\tBadges:\n"; + if ($badges) { - $yaml .= "\t\tBadge@$i:\n"; - $yaml .= "\t\t\tLabel: " . $badge['badge_label'] . "\n"; - $yaml .= "\t\t\tIcon24: " . $badge['badge_icon_24'] . "\n"; - $i++; + $i = 0; + foreach ($badges as $badge) + { + $content .= "\t\tBadge@$i:\n"; + $content .= "\t\t\tLabel: " . $badge['badge_label'] . "\n"; + $content .= "\t\t\tIcon24: " . $badge['badge_icon_24'] . "\n"; + $i++; + } } + } else { + $content = [ + 'Player' => [ + 'Fingerprint' => $data['fingerprint'], + 'PublicKey' => base64_encode($data['public_key']), + 'KeyRevoked' => ($data['revoked'] ? 'true' : 'false'), + 'ProfileID' => $data['user_id'], + 'ProfileName' => $data['username'], + 'ProfileRank' => 'Registered User', + 'Avatar' => $avatar, + 'Badges' => $badges, + ] + ]; } - return $this->get_response($yaml, $format); + return $this->get_response($content, $format); break; } @@ -122,8 +138,15 @@ public function fetchinfo($type, $fingerprint, $format) public function get_response($content, $format) { - $response = new Response($content); - $response->headers->set('Content-Type', 'Content-type: text/plain; charset=utf-8'); + if (strtolower($format) !== "json") + { + $response = new Response($content); + $response->headers->set('Content-Type', 'Content-type: text/plain; charset=utf-8'); + } else { + $response = new Response(); + $response->setContent(json_encode($content, JSON_UNESCAPED_SLASHES)); + $response->headers->set('Content-Type', 'application/json'); + } return $response; } }