From 0b7031959b8b9b614085eb23f3a544435db0a6e6 Mon Sep 17 00:00:00 2001
From: flashwave <me@flash.moe>
Date: Tue, 25 Mar 2025 14:27:50 +0000
Subject: [PATCH] Removed RPCii dependency.

---
 VERSION                            |   2 +-
 composer.json                      |   1 -
 composer.lock                      |  43 +------
 docs/source.md                     |   2 -
 src/Auth/AuthRpcHandler.php        |  64 ----------
 src/Emoticons/EmotesRpcHandler.php |  41 ------
 src/Info/InfoRoutes.php            |   3 -
 src/MisuzuContext.php              |  15 ---
 src/OAuth2/OAuth2RpcHandler.php    | 196 -----------------------------
 src/Users/UsersRpcHandler.php      | 120 ------------------
 10 files changed, 2 insertions(+), 485 deletions(-)
 delete mode 100644 src/Auth/AuthRpcHandler.php
 delete mode 100644 src/Emoticons/EmotesRpcHandler.php
 delete mode 100644 src/OAuth2/OAuth2RpcHandler.php
 delete mode 100644 src/Users/UsersRpcHandler.php

diff --git a/VERSION b/VERSION
index e0354d6b..7c3db70d 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-20250325.2
+20250325.3
diff --git a/composer.json b/composer.json
index b8128202..5f575a6a 100644
--- a/composer.json
+++ b/composer.json
@@ -3,7 +3,6 @@
         "php": ">=8.4",
         "ext-mbstring": "*",
         "flashwave/index": "^0.2503",
-        "flashii/rpcii": "~5.0",
         "erusev/parsedown": "~1.7",
         "chillerlan/php-qrcode": "~5.0",
         "symfony/mailer": "~7.2",
diff --git a/composer.lock b/composer.lock
index c805baa5..1c46a961 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "baa15cf491eb4500360fa9c20fd42a4d",
+    "content-hash": "cd96df7b2981a653c33b100a8b3837cd",
     "packages": [
         {
             "name": "carbonphp/carbon-doctrine-types",
@@ -499,47 +499,6 @@
             ],
             "time": "2025-03-15T12:00:00+00:00"
         },
-        {
-            "name": "flashii/rpcii",
-            "version": "v5.0.1",
-            "source": {
-                "type": "git",
-                "url": "https://patchii.net/flashii/rpcii-php.git",
-                "reference": "28c25e0a342173524f8894d03158663842e03252"
-            },
-            "require": {
-                "ext-msgpack": ">=2.2",
-                "flashwave/index": "^0.2503",
-                "guzzlehttp/guzzle": "~7.0",
-                "php": ">=8.4",
-                "psr/http-client": "^1.0"
-            },
-            "require-dev": {
-                "phpstan/phpstan": "^2.1",
-                "phpunit/phpunit": "^12.0"
-            },
-            "type": "library",
-            "autoload": {
-                "psr-4": {
-                    "RPCii\\": "src"
-                }
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "bsd-3-clause-clear"
-            ],
-            "authors": [
-                {
-                    "name": "flashwave",
-                    "email": "packagist@flash.moe",
-                    "homepage": "https://flash.moe",
-                    "role": "mom"
-                }
-            ],
-            "description": "HTTP RPC client/server library.",
-            "homepage": "https://railgun.sh/rpcii",
-            "time": "2025-03-21T19:38:29+00:00"
-        },
         {
             "name": "flashwave/index",
             "version": "v0.2503.251417",
diff --git a/docs/source.md b/docs/source.md
index 34fdd477..953f690c 100644
--- a/docs/source.md
+++ b/docs/source.md
@@ -12,7 +12,6 @@ Below are a number of links to source code repositories related to Flashii.net a
   - [Uiharu](https://patchii.net/flashii/uiharu): Service for looking up URL metadata.
   - [Seria](https://patchii.net/flashii/seria): Software used by the downloads tracker.
   - [Mince](https://patchii.net/flashii/mince): Source code for the Minecraft servers subwebsite.
-  - [Aleister](https://patchii.net/flashii/aleister): Public API gateway.
 
 ## Tools & Software
   - [SoFii](https://patchii.net/flashii/sofii): Launcher for Soldier of Fortune 2
@@ -21,7 +20,6 @@ Below are a number of links to source code repositories related to Flashii.net a
 
 ## First-Party Libraries
   - [Index](https://patchii.net/flash/index): Base library used in almost any component of the website that uses PHP.
-  - [RPCii](https://patchii.net/flashii/rpcii): Internal RPC extension, mainly used to supply data to the API gateway.
 
 ## Historical
   - [AJAX Chat (fork)](https://patchii.net/flashii/ajax-chat): Old chat software (2013-2015). Still kept on life support for the nostalgia.
diff --git a/src/Auth/AuthRpcHandler.php b/src/Auth/AuthRpcHandler.php
deleted file mode 100644
index 5500c803..00000000
--- a/src/Auth/AuthRpcHandler.php
+++ /dev/null
@@ -1,64 +0,0 @@
-<?php
-namespace Misuzu\Auth;
-
-use RuntimeException;
-use Misuzu\Users\{UsersContext,UserInfo};
-use RPCii\Server\{RpcHandler,RpcHandlerCommon,RpcAction};
-use Index\Config\Config;
-
-final class AuthRpcHandler implements RpcHandler {
-    use RpcHandlerCommon;
-
-    public function __construct(
-        private Config $impersonateConfig,
-        private UsersContext $usersCtx,
-        private AuthContext $authCtx
-    ) {}
-
-    private function canImpersonateUserId(UserInfo $impersonator, string $targetId): bool {
-        if($impersonator->super)
-            return true;
-
-        $whitelist = $this->impersonateConfig->getArray(sprintf('allow.u%s', $impersonator->id));
-        return in_array($targetId, $whitelist, true);
-    }
-
-    /** @return array{method: string, error: string}|array{method: string, type: string, user: string, expires: int} */
-    #[RpcAction('misuzu:auth:attemptMisuzuAuth')]
-    public function procAttemptMisuzuAuth(string $remoteAddr, string $token): array {
-        $tokenInfo = $this->authCtx->createAuthTokenPacker()->unpack($token);
-        if(!$tokenInfo->isEmpty)
-            $token = $tokenInfo->sessionToken;
-
-        try {
-            $sessionInfo = $this->authCtx->sessions->getSession(sessionToken: $token);
-        } catch(RuntimeException $ex) {
-            return ['method' => 'misuzu', 'error' => 'token'];
-        }
-
-        if($sessionInfo->expired) {
-            $this->authCtx->sessions->deleteSessions(sessionInfos: $sessionInfo);
-            return ['method' => 'misuzu', 'error' => 'expired'];
-        }
-
-        $this->authCtx->sessions->recordSessionActivity(sessionInfo: $sessionInfo, remoteAddr: $remoteAddr);
-
-        $userInfo = $this->usersCtx->users->getUser($sessionInfo->userId, 'id');
-        if($tokenInfo->hasImpersonatedUserId && $this->canImpersonateUserId($userInfo, $tokenInfo->impersonatedUserId)) {
-            $userInfoReal = $userInfo;
-
-            try {
-                $userInfo = $this->usersCtx->users->getUser($tokenInfo->impersonatedUserId, 'id');
-            } catch(RuntimeException $ex) {
-                $userInfo = $userInfoReal;
-            }
-        }
-
-        return [
-            'method' => 'misuzu',
-            'type' => 'user',
-            'user' => $userInfo->id,
-            'expires' => $sessionInfo->expiresTime,
-        ];
-    }
-}
diff --git a/src/Emoticons/EmotesRpcHandler.php b/src/Emoticons/EmotesRpcHandler.php
deleted file mode 100644
index be304e17..00000000
--- a/src/Emoticons/EmotesRpcHandler.php
+++ /dev/null
@@ -1,41 +0,0 @@
-<?php
-namespace Misuzu\Emoticons;
-
-use Index\XArray;
-use RPCii\Server\{RpcHandler,RpcHandlerCommon,RpcQuery};
-
-final class EmotesRpcHandler implements RpcHandler {
-    use RpcHandlerCommon;
-
-    public function __construct(
-        private EmotesData $emotes
-    ) {}
-
-    /** @return array<array{url: string, strings: string[], id?: string, order?: int, min_rank?: int}> */
-    #[RpcQuery('misuzu:emotes:all')]
-    public function queryAll(bool $includeId = false, bool $includeOrder = false): array {
-        return XArray::select(
-            $this->emotes->getEmotes(orderBy: 'order'),
-            function($emote) use ($includeId, $includeOrder) {
-                $info = [
-                    'url' => $emote->url,
-                    'strings' => XArray::select(
-                        $this->emotes->getEmoteStrings($emote),
-                        fn($string) => $string->string
-                    ),
-                ];
-
-                if($includeId)
-                    $info['id'] = $emote->id;
-                if($includeOrder)
-                    $info['order'] = $emote->order;
-
-                $rank = $emote->minRank;
-                if($rank !== 0)
-                    $info['min_rank'] = $rank;
-
-                return $info;
-            }
-        );
-    }
-}
diff --git a/src/Info/InfoRoutes.php b/src/Info/InfoRoutes.php
index 002f68fe..2493d726 100644
--- a/src/Info/InfoRoutes.php
+++ b/src/Info/InfoRoutes.php
@@ -8,7 +8,6 @@ use Index\Http\Routing\Routes\{ExactRoute,PatternRoute};
 use Index\Urls\{UrlFormat,UrlSource,UrlSourceCommon};
 use Misuzu\{Misuzu,Template};
 use Misuzu\Parsers\{Parsers,TextFormat};
-use RPCii\RPCii;
 
 class InfoRoutes implements RouteHandler, UrlSource {
     use RouteHandlerCommon, UrlSourceCommon;
@@ -16,12 +15,10 @@ class InfoRoutes implements RouteHandler, UrlSource {
     private const PROJECT_PATHS = [
         'misuzu' => Misuzu::PATH_ROOT,
         'index' => Index::PATH_ROOT,
-        'rpcii' => RPCii::PATH_ROOT,
     ];
     private const PROJECT_SUFFIXES = [
         'misuzu' => 'Misuzu » %s',
         'index' => 'Index » %s',
-        'rpcii' => 'RPCii » %s',
     ];
 
     #[ExactRoute('GET', '/info')]
diff --git a/src/MisuzuContext.php b/src/MisuzuContext.php
index ed374e37..d47672a4 100644
--- a/src/MisuzuContext.php
+++ b/src/MisuzuContext.php
@@ -11,8 +11,6 @@ use Index\Templating\TplEnvironment;
 use Index\Urls\UrlRegistry;
 use Misuzu\Routing\RoutingContext;
 use Misuzu\Users\UserInfo;
-use RPCii\HmacVerificationProvider;
-use RPCii\Server\{HttpRpcServer,RpcServer};
 
 class MisuzuContext {
     public private(set) Dependencies $deps;
@@ -158,11 +156,6 @@ class MisuzuContext {
     public function registerMainRoutes(
         RoutingContext $routingCtx
     ): void {
-        $rpcServer = new HttpRpcServer;
-        $routingCtx->register($rpcServer->createRouteHandler(
-            new HmacVerificationProvider(fn() => $this->config->getString('aleister.secret'))
-        ));
-
         $this->deps->register($wf = new WebFinger\WebFingerRegistry);
         $wf->register($this->deps->constructLazy(
             Users\UsersWebFingerResolver::class,
@@ -190,7 +183,6 @@ class MisuzuContext {
 
         $routingCtx->register($this->deps->constructLazy(OAuth2\OAuth2ApiRoutes::class));
         $routingCtx->register($this->deps->constructLazy(OAuth2\OAuth2WebRoutes::class));
-        $rpcServer->register($this->deps->constructLazy(OAuth2\OAuth2RpcHandler::class));
         $routingCtx->register($this->deps->constructLazy(WebFinger\WebFingerRoutes::class));
 
         $routingCtx->register($this->deps->constructLazy(Forum\ForumCategoriesRoutes::class));
@@ -210,13 +202,6 @@ class MisuzuContext {
         ));
 
         $routingCtx->register($this->deps->constructLazy(LegacyRoutes::class));
-
-        $rpcServer->register($this->deps->constructLazy(
-            Auth\AuthRpcHandler::class,
-            impersonateConfig: $this->config->scopeTo('impersonate')
-        ));
-        $rpcServer->register($this->deps->constructLazy(Emoticons\EmotesRpcHandler::class));
-        $rpcServer->register($this->deps->constructLazy(Users\UsersRpcHandler::class));
     }
 
     public function registerRedirectorRoutes(RoutingContext $routingCtx): void {
diff --git a/src/OAuth2/OAuth2RpcHandler.php b/src/OAuth2/OAuth2RpcHandler.php
deleted file mode 100644
index de32466c..00000000
--- a/src/OAuth2/OAuth2RpcHandler.php
+++ /dev/null
@@ -1,196 +0,0 @@
-<?php
-namespace Misuzu\OAuth2;
-
-use RuntimeException;
-use RPCii\Server\{RpcHandler,RpcHandlerCommon,RpcAction};
-
-final class OAuth2RpcHandler implements RpcHandler {
-    use RpcHandlerCommon;
-
-    public function __construct(
-        private OAuth2Context $oauth2Ctx
-    ) {}
-
-    /**
-     * @return array{
-     *  method: 'basic',
-     *  error?: 'app'|'secret',
-     *  type?: 'confapp'|'pubapp',
-     *  app?: string,
-     *  scope?: string[]
-     * }
-     */
-    #[RpcAction('hanyuu:oauth2:attemptAppAuth')]
-    public function procAttemptAppAuth(string $remoteAddr, string $clientId, string $clientSecret = ''): array {
-        try {
-            $appInfo = $this->oauth2Ctx->appsCtx->apps->getAppInfo(clientId: $clientId, deleted: false);
-        } catch(RuntimeException $ex) {
-            return ['method' => 'basic', 'error' => 'app'];
-        }
-
-        $authed = false;
-        if($clientSecret !== '') {
-            // TODO: rate limiting
-
-            if(!$appInfo->verifyClientSecret($clientSecret))
-                return ['method' => 'basic', 'error' => 'secret'];
-
-            $authed = true;
-        }
-
-        return [
-            'method' => 'basic',
-            'type' => $authed ? 'confapp' : 'pubapp',
-            'app' => $appInfo->id,
-            'scope' => ['oauth2'],
-        ];
-    }
-
-    /**
-     * @return array{
-     *  method: 'bearer',
-     *  error?: 'token'|'expired',
-     *  type?: 'app'|'user',
-     *  app?: string,
-     *  user?: string,
-     *  scope?: string[],
-     *  expires?: int,
-     * }
-     */
-    #[RpcAction('hanyuu:oauth2:attemptBearerAuth')]
-    public function procAttemptBearerAuth(string $remoteAddr, string $token): array {
-        try {
-            $tokenInfo = $this->oauth2Ctx->tokens->getAccessInfo($token, OAuth2AccessInfoGetField::Token);
-        } catch(RuntimeException $ex) {
-            return ['method' => 'bearer', 'error' => 'token'];
-        }
-
-        if($tokenInfo->expired)
-            return ['method' => 'bearer', 'error' => 'expired'];
-
-        return [
-            'method' => 'bearer',
-            'type' => empty($tokenInfo->userId) ? 'app' : 'user',
-            'app' => $tokenInfo->appId,
-            'user' => $tokenInfo->userId ?? '0',
-            'scope' => $tokenInfo->scopes,
-            'expires' => $tokenInfo->expiresTime,
-        ];
-    }
-
-    /**
-     * @return array{
-     *  device_code: string,
-     *  user_code: string,
-     *  verification_uri: string,
-     *  verification_uri_complete: string,
-     *  expires_in?: int,
-     *  interval?: int
-     * }|array{ error: string, error_description: string }
-     */
-    #[RpcAction('hanyuu:oauth2:createAuthoriseRequest')]
-    public function procCreateAuthoriseRequest(string $appId, ?string $scope = null): array {
-        try {
-            $appInfo = $this->oauth2Ctx->appsCtx->apps->getAppInfo(appId: $appId, deleted: false);
-        } catch(RuntimeException $ex) {
-            return [
-                'error' => 'invalid_client',
-                'error_description' => 'No application has been registered with this client ID.',
-            ];
-        }
-
-        return $this->oauth2Ctx->createDeviceAuthorisationRequest($appInfo, $scope);
-    }
-
-    /**
-     * @return array{
-     *  access_token: string,
-     *  token_type: 'Bearer',
-     *  expires_in?: int,
-     *  scope?: string,
-     *  refresh_token?: string,
-     * }|array{ error: string, error_description: string }
-     */
-    #[RpcAction('hanyuu:oauth2:createBearerToken:authorisationCode')]
-    public function procCreateBearerTokenAuthzCode(string $appId, bool $isAuthed, string $code, string $codeVerifier): array {
-        try {
-            $appInfo = $this->oauth2Ctx->appsCtx->apps->getAppInfo(appId: $appId, deleted: false);
-        } catch(RuntimeException $ex) {
-            return [
-                'error' => 'invalid_client',
-                'error_description' => 'No application has been registered with this client ID.',
-            ];
-        }
-
-        return $this->oauth2Ctx->redeemAuthorisationCode($appInfo, $isAuthed, $code, $codeVerifier);
-    }
-
-    /**
-     * @return array{
-     *  access_token: string,
-     *  token_type: 'Bearer',
-     *  expires_in?: int,
-     *  scope?: string,
-     *  refresh_token?: string,
-     * }|array{ error: string, error_description: string }
-     */
-    #[RpcAction('hanyuu:oauth2:createBearerToken:refreshToken')]
-    public function procCreateBearerTokenRefreshToken(string $appId, bool $isAuthed, string $refreshToken, ?string $scope = null): array {
-        try {
-            $appInfo = $this->oauth2Ctx->appsCtx->apps->getAppInfo(appId: $appId, deleted: false);
-        } catch(RuntimeException $ex) {
-            return [
-                'error' => 'invalid_client',
-                'error_description' => 'No application has been registered with this client ID.',
-            ];
-        }
-
-        return $this->oauth2Ctx->redeemRefreshToken($appInfo, $isAuthed, $refreshToken, $scope);
-    }
-
-    /**
-     * @return array{
-     *  access_token: string,
-     *  token_type: 'Bearer',
-     *  expires_in?: int,
-     *  scope?: string,
-     *  refresh_token?: string,
-     * }|array{ error: string, error_description: string }
-     */
-    #[RpcAction('hanyuu:oauth2:createBearerToken:clientCredentials')]
-    public function procCreateBearerTokenClientCreds(string $appId, bool $isAuthed, ?string $scope = null): array {
-        try {
-            $appInfo = $this->oauth2Ctx->appsCtx->apps->getAppInfo(appId: $appId, deleted: false);
-        } catch(RuntimeException $ex) {
-            return [
-                'error' => 'invalid_client',
-                'error_description' => 'No application has been registered with this client ID.',
-            ];
-        }
-
-        return $this->oauth2Ctx->redeemClientCredentials($appInfo, $isAuthed, $scope);
-    }
-
-    /**
-     * @return array{
-     *  access_token: string,
-     *  token_type: 'Bearer',
-     *  expires_in?: int,
-     *  scope?: string,
-     *  refresh_token?: string,
-     * }|array{ error: string, error_description: string }
-     */
-    #[RpcAction('hanyuu:oauth2:createBearerToken:deviceCode')]
-    public function procCreateBearerTokenDeviceCode(string $appId, bool $isAuthed, string $deviceCode): array {
-        try {
-            $appInfo = $this->oauth2Ctx->appsCtx->apps->getAppInfo(appId: $appId, deleted: false);
-        } catch(RuntimeException $ex) {
-            return [
-                'error' => 'invalid_client',
-                'error_description' => 'No application has been registered with this client ID.',
-            ];
-        }
-
-        return $this->oauth2Ctx->redeemDeviceCode($appInfo, $isAuthed, $deviceCode);
-    }
-}
diff --git a/src/Users/UsersRpcHandler.php b/src/Users/UsersRpcHandler.php
deleted file mode 100644
index e6e3ad8f..00000000
--- a/src/Users/UsersRpcHandler.php
+++ /dev/null
@@ -1,120 +0,0 @@
-<?php
-namespace Misuzu\Users;
-
-use RuntimeException;
-use Misuzu\SiteInfo;
-use Misuzu\Users\Assets\UserAvatarAsset;
-use RPCii\Server\{RpcHandler,RpcHandlerCommon,RpcQuery};
-use Index\XArray;
-use Index\Colour\{Colour,ColourRgb};
-use Index\Urls\UrlRegistry;
-
-final class UsersRpcHandler implements RpcHandler {
-    use RpcHandlerCommon;
-
-    public function __construct(
-        private SiteInfo $siteInfo,
-        private UrlRegistry $urls,
-        private UsersContext $usersCtx
-    ) {}
-
-    /**
-     * @return array{error: string}|array{
-     *  id: string,
-     *  name: string,
-     *  email?: string,
-     *  colour_raw: int,
-     *  colour_css: string,
-     *  rank: int,
-     *  country_code: string,
-     *  roles?: string[],
-     *  is_super?: bool,
-     *  title?: string,
-     *  created_at: string,
-     *  last_active_at?: string,
-     *  profile_url: string,
-     *  avatar_url: string,
-     *  avatar_urls?: array{res: int, url: string}[],
-     *  is_deleted?: bool
-     * }
-     */
-    #[RpcQuery('misuzu:users:getUser')]
-    public function queryGetUser(string $userId, bool $includeEMailAddress = false): array {
-        try {
-            $userInfo = $this->usersCtx->getUserInfo($userId, UsersData::GET_USER_ID);
-        } catch(RuntimeException) {
-            return ['error' => 'notfound'];
-        }
-
-        // TODO: there should be some kinda privacy controls for users
-
-
-        $colour = $this->usersCtx->getUserColour($userInfo);
-        if($colour->inherits) {
-            $colourRaw = null;
-            $colourCSS = (string)$colour;
-        } else {
-            $colourRaw = Colour::toRawRgb($colour);
-            $colourCSS = (string)ColourRgb::convert($colour);
-        }
-
-        $baseUrl = $this->siteInfo->url;
-
-        $output = [];
-
-        $output['id'] = $userInfo->id;
-        $output['name'] = $userInfo->name;
-        if($includeEMailAddress)
-            $output['email'] = $userInfo->emailAddress;
-
-        $output['colour_raw'] = $colourRaw;
-        $output['colour_css'] = $colourCSS;
-        $output['country_code'] = $userInfo->countryCode;
-
-        if($this->usersCtx->hasActiveBan($userInfo)) {
-            $output['rank'] = 0;
-            $output['roles'] = ['x-banned'];
-        } else {
-            $roles = XArray::select(
-                $this->usersCtx->roles->getRoles(userInfo: $userInfo, hasString: true, orderByRank: true),
-                fn($roleInfo) => $roleInfo->string,
-            );
-
-            $output['rank'] = $this->usersCtx->getUserRank($userInfo);
-            if(!empty($roles))
-                $output['roles'] = $roles;
-            if($userInfo->super)
-                $output['is_super'] = true;
-        }
-
-        if(!empty($userInfo->title))
-            $output['title'] = $userInfo->title;
-
-        $output['created_at'] = $userInfo->createdAt->toIso8601ZuluString();
-        if($userInfo->lastActiveTime !== null)
-            $output['last_active_at'] = $userInfo->lastActiveAt->toIso8601ZuluString();
-
-        $output['profile_url'] = $baseUrl . $this->urls->format('user-profile', ['user' => $userInfo->id]);
-        $output['avatar_url'] = $baseUrl . $this->urls->format('user-avatar', ['user' => $userInfo->id]);
-
-        /* Remove the following later */
-        $avatars = [];
-        $formatAvatarUrl = fn($res = 0) => (
-            $baseUrl . $this->urls->format('user-avatar', ['user' => $userInfo->id, 'res' => $res])
-        );
-
-        $avatars[] = ['res' => 0, 'url' => $formatAvatarUrl()];
-        foreach(UserAvatarAsset::DIMENSIONS as $res)
-            $avatars[] = ['res' => $res, 'url' => $formatAvatarUrl($res)];
-
-        $avatars = array_reverse($avatars);
-
-        $output['avatar_urls'] = $avatars;
-        /* / */
-
-        if($userInfo->deleted)
-            $output['is_deleted'] = true;
-
-        return $output;
-    }
-}