hashKey = file_get_contents($hashKeyPath); } public function phpFile(Response $response, Request $request) { $query = $request->getQueryParams(); if(isset($query['emotes'])) return $this->emotes($response, $request); if(isset($query['bans']) && is_string($query['bans'])) return $this->bans($response, $request->withHeader('X-SharpChat-Signature', $query['bans'])); $body = $request->getParsedBody(); if(isset($body['bump'], $body['hash']) && is_string($body['bump']) && is_string($body['hash'])) return $this->bump( $response, $request->withHeader('X-SharpChat-Signature', $body['hash']) ->withBody(Stream::create($body['bump'])) ); $source = isset($body['user_id']) ? $body : $query; if(isset($source['user_id'], $source['token'], $source['ip'], $source['hash']) && is_string($source['user_id']) && is_string($source['token']) && is_string($source['ip']) && is_string($source['hash'])) return $this->verify( $response, $request->withHeader('X-SharpChat-Signature', $source['hash']) ->withBody(Stream::create(json_encode([ 'user_id' => $source['user_id'], 'token' => $source['token'], 'ip' => $source['ip'], ]))) ); return $this->login($response, $request); } public function emotes(Response $response, Request $request): array { $response->setHeader('Access-Control-Allow-Origin', '*') ->setHeader('Access-Control-Allow-Methods', 'GET'); $raw = Emoticon::all(); $out = []; foreach($raw as $emote) { $strings = []; foreach($emote->getStrings() as $string) { $strings[] = sprintf(':%s:', $string->emote_string); } $out[] = [ 'Text' => $strings, 'Image' => $emote->getUrl(), 'Hierarchy' => $emote->getHierarchy(), ]; } return $out; } public function bans(Response $response, Request $request): array { $userHash = $request->getHeaderLine('X-SharpChat-Signature'); $realHash = hash_hmac('sha256', 'givemethebeans', $this->hashKey); if(!hash_equals($realHash, $userHash)) return []; return DB::prepare(' SELECT uw.`user_id` AS `id`, DATE_FORMAT(uw.`warning_duration`, \'%Y-%m-%dT%TZ\') AS `expires`, INET6_NTOA(uw.`user_ip`) AS `ip`, u.`username` FROM `msz_user_warnings` AS uw LEFT JOIN `msz_users` AS u ON u.`user_id` = uw.`user_id` WHERE uw.`warning_type` = 3 AND uw.`warning_duration` > NOW() ')->fetchAll(); } public function login(Response $response, Request $request) { if(!user_session_active()) { $response->redirect(url('auth-login')); return; } $params = $request->getQueryParams(); try { $token = ChatToken::create(user_session_current('user_id')); } catch(Exception $ex) { $response->setHeader('X-SharpChat-Error', $ex->getMessage()); return 500; } if(MSZ_DEBUG && isset($params['dump'])) { $ipAddr = $request->getServerParams()['REMOTE_ADDR']; $hash = hash_hmac('sha256', implode('#', [$token->getUserId(), $token->getToken(), $ipAddr]), $this->hashKey); $response->setText(sprintf( '/_sockchat.php?user_id=%d&token=%s&ip=%s&hash=%s', $token->getUserId(), $token->getToken(), urlencode($ipAddr), $hash )); return; } $cookieName = Config::get('sockChat.cookie', Config::TYPE_STR, 'sockchat_auth'); $cookieData = implode('_', [$token->getUserId(), $token->getToken()]); $cookieDomain = '.' . $request->getHeaderLine('Host'); setcookie($cookieName, $cookieData, $token->getExpirationTime(), '/', $cookieDomain); $configKey = isset($params['legacy']) ? 'sockChat.chatPath.legacy' : 'sockChat.chatPath.normal'; $chatPath = Config::get($configKey, Config::TYPE_STR, '/'); if(MSZ_DEBUG) { $response->setText(sprintf('Umi.Cookies.Set(\'%s\', \'%s\');', $cookieName, $cookieData)); } else { $response->redirect($chatPath); } } public function bump(Response $response, Request $request): void { $userHash = $request->getHeaderLine('X-SharpChat-Signature'); $bumpString = (string)$request->getBody(); $realHash = hash_hmac('sha256', $bumpString, $this->hashKey); if(!hash_equals($realHash, $userHash)) return; $bumpInfo = json_decode($bumpString); if(empty($bumpInfo)) return; foreach($bumpInfo as $bumpUser) user_bump_last_active($bumpUser->id, $bumpUser->ip); } public function verify(Response $response, Request $request): array { $userHash = $request->getHeaderLine('X-SharpChat-Signature'); if(strlen($userHash) !== 64) return ['success' => false, 'reason' => 'length']; $authInfo = json_decode((string)$request->getBody()); if(!isset($authInfo->user_id, $authInfo->token, $authInfo->ip)) return ['success' => false, 'reason' => 'data']; $realHash = hash_hmac('sha256', implode('#', [$authInfo->user_id, $authInfo->token, $authInfo->ip]), $this->hashKey); if(!hash_equals($realHash, $userHash)) return ['success' => false, 'reason' => 'hash']; $authMethod = mb_substr($authInfo->token, 0, 5); if($authMethod === 'PASS:') { // DEPRECATE THIS if(time() > 1577750400) return ['success' => false, 'reason' => 'unsupported']; if(user_password_verify_db($authInfo->user_id, mb_substr($authInfo->token, 5))) $userId = $authInfo->user_id; } elseif($authMethod === 'SESS:') { // IMPROVE THIS $tokenData = user_session_cookie_unpack(mb_substr($authInfo->token, 5), true)); user_session_start($authInfo->user_id, $tokenData['token']); if(user_session_active()) $userId = user_session_current('user_id'); } else { try { $token = ChatToken::get($authInfo->user_id, $authInfo->token); } catch(Exception $ex) { return ['success' => false, 'reason' => 'token']; } if($token->hasExpired()) { $token->delete(); return ['success' => false, 'reason' => 'expired']; } $userId = $token->getUserId(); } if(!isset($userId) || $userId < 1) return ['success' => false, 'reason' => 'unknown']; $userInfo = User::get($userId); if($userInfo === null || !$userInfo->hasUserId()) return ['success' => false, 'reason' => 'user']; $perms = self::PERMS_DEFAULT; if(perms_check_user(MSZ_PERMS_USER, $userInfo->user_id, MSZ_PERM_USER_MANAGE_USERS)) $perms |= self::PERMS_MANAGE_USERS; if(perms_check_user(MSZ_PERMS_USER, $userInfo->user_id, MSZ_PERM_USER_MANAGE_WARNINGS)) $perms |= self::PERMS_MANAGE_WARNS; if(perms_check_user(MSZ_PERMS_USER, $userInfo->user_id, MSZ_PERM_USER_CHANGE_BACKGROUND)) $perms |= self::PERMS_CHANGE_BACKG; if(perms_check_user(MSZ_PERMS_FORUM, $userInfo->user_id, MSZ_PERM_FORUM_MANAGE_FORUMS)) $perms |= self::PERMS_MANAGE_FORUM; return [ 'success' => true, 'user_id' => $userInfo->getUserId(), 'username' => $userInfo->getUsername(), 'colour_raw' => $userInfo->getColourRaw(), 'hierarchy' => $userInfo->getHierarchy(), 'is_silenced' => date('c', user_warning_check_expiration($userInfo->getUserId(), MSZ_WARN_SILENCE)), 'perms' => $perms, ]; } }