diff options
Diffstat (limited to 'src')
28 files changed, 466 insertions, 447 deletions
diff --git a/src/Controllers/LoginController.php b/src/Controllers/LoginController.php index c520e25..9ae3a48 100644 --- a/src/Controllers/LoginController.php +++ b/src/Controllers/LoginController.php @@ -9,10 +9,12 @@ use App\Errors\UnknownError; use App\Models\Device; use App\Models\Tokens; use App\Models\User; +use App\Support\Logger; +use App\Support\Parser; use App\Support\RequestValidator; -use App\Types\UserRegistrationKind; use Matrix\Data\LoginFlow; use Matrix\Enums\LoginType; +use Matrix\Enums\UserRegistrationKind; use Matrix\Responses\ClientLoginGetResponse; use Matrix\Responses\ClientLoginPostResponse; use Matrix\Responses\ClientRegisterPostResponse; @@ -37,6 +39,8 @@ class LoginController */ public function login(Request $request): Response { + Logger::logRequestToFile($request); + $body = json_decode($request->getContent(), true); RequestValidator::validateJson(); @@ -49,7 +53,11 @@ class LoginController } // get user id - $userId = $body["identifier"]["user"]; + $userId = Parser::parseUser($body["identifier"]["user"]); + if (empty($userId["server"])) { + #$userId = "@$userId[username]:$_ENV[DOMAIN]"; + $userId = "@$userId[username]:localhost"; + } #if ($loginType == LoginType::PASSWORD) {} diff --git a/src/Controllers/RoomController.php b/src/Controllers/RoomController.php index c02d5ce..dcc0415 100755 --- a/src/Controllers/RoomController.php +++ b/src/Controllers/RoomController.php @@ -7,18 +7,155 @@ use App\Errors\AppException; use App\Errors\ErrorCode; use App\Errors\UnauthorizedError; use App\Events\RoomMessageEvent; +use App\Models\RoomEvent; use App\Models\User; +use App\Support\Id; use App\Support\Parser; use App\Support\RequestValidator; -use App\Types\EventType; -use App\Types\MembershipState; -use App\Types\MessageType; +use Matrix\Enums\EventType; +use Matrix\Enums\MembershipState; +use Matrix\Enums\MessageType; +use Matrix\Enums\RoomGuestAccess; +use Matrix\Enums\RoomHistoryVisibility; +use Matrix\Enums\RoomJoinRule; +use Matrix\Enums\RoomVisibility; +use Matrix\Events\Room\CreateEvent; +use Matrix\Events\Room\GuestAccessEvent; +use Matrix\Events\Room\HistoryVisibilityEvent; +use Matrix\Events\Room\JoinRulesEvent; +use Matrix\Events\Room\MemberEvent; +use Matrix\Events\Room\NameEvent; +use Matrix\Events\Room\PowerLevelsEvent; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\JsonResponse; class RoomController { + public function createRoom(Request $request): Response + { + $user = User::authenticateWithRequest($request); + $body = json_decode($request->getContent(), true); + RequestValidator::validateJson(); + + $creationContent = $body["creation_content"] ?? []; + $initialState = $body["initial_state"] ?? []; + $invite = $body["invite"] ?? []; + $body["invite_3pid"] ?? []; + $body["is_direct"] ?? false; + $name = $body["name"] ?? ""; + $body["power_level_content_override"] ?? []; + $preset = $body["preset"] ?? ""; + $roomAliasName = $body["room_alias_name"] ?? ""; + $roomVersion = $body["room_version"] ?? "DEFAULT_ROOM_VERSION"; + $topic = $body["topic"] ?? ""; + $visibility = RoomVisibility::tryFrom($body["visibility"] ?? "private"); + + if (! $preset) { + $preset = $visibility->value . "_chat"; + } + + # TODO: get events for preset + # TODO: override preset events with initial state + # TODO: override events with name and topic if applicable + $state = []; + + if ($name) { + $state[EventType::ROOM_NAME->value] = $name; + } + + if ($topic) { + $state[EventType::ROOM_TOPIC->value] = $topic; + } + + // create room + $roomId = Id::generateRoomId(); + Database::getInstance()->query(<<<SQL + insert into rooms (id, name) values (:id, :name) + SQL, [ + "id" => $roomId, + "name" => $roomAliasName, # "#$roomAliasName:$_ENV[DOMAIN]", + ]); + + $roomCreateEvent = new RoomEvent(new CreateEvent( + eventId: Id::generateEventId(), + originServerTimestamp: time(), + roomId: $roomId, + sender: $user->getId(), + roomVersion: "12", + )); + $roomCreateEvent->insert(); + + $roomMemberEvent = new RoomEvent(new MemberEvent( + eventId: Id::generateEventId(), + originServerTimestamp: time(), + roomId: $roomId, + sender: $user->getId(), + stateKey: $user->getId(), + isDirect: false, + membership: MembershipState::JOIN, + displayName: $user->getName(), + )); + $roomMemberEvent->insert(); + + $roomPowerLevelsEvent = new RoomEvent(new PowerLevelsEvent( + eventId: Id::generateEventId(), + originServerTimestamp: time(), + roomId: $roomId, + sender: $user->getId(), + )); + $roomPowerLevelsEvent->insert(); + + $roomJoinRulesEvent = new RoomEvent(new JoinRulesEvent( + eventId: Id::generateEventId(), + originServerTimestamp: time(), + roomId: $roomId, + sender: $user->getId(), + joinRule: RoomJoinRule::INVITE, + )); + $roomJoinRulesEvent->insert(); + + $roomHistoryVisibilityEvent = new RoomEvent(new HistoryVisibilityEvent( + eventId: Id::generateEventId(), + originServerTimestamp: time(), + roomId: $roomId, + sender: $user->getId(), + historyVisibility: RoomHistoryVisibility::SHARED, + )); + $roomHistoryVisibilityEvent->insert(); + + $roomGuestAccessEvent = new RoomEvent(new GuestAccessEvent( + eventId: Id::generateEventId(), + originServerTimestamp: time(), + roomId: $roomId, + sender: $user->getId(), + guestAccess: RoomGuestAccess::CAN_JOIN, + )); + $roomGuestAccessEvent->insert(); + + $roomNameEvent = new RoomEvent(new NameEvent( + eventId: Id::generateEventId(), + originServerTimestamp: time(), + roomId: $roomId, + sender: $user->getId(), + name: $roomAliasName, + )); + $roomNameEvent->insert(); + + Database::getInstance()->query(<<<SQL + insert into room_memberships (room_id, user_id, state) + values (:room_id, :user_id, :state) + SQL, [ + "room_id" => $roomId, + "user_id" => $user->getId(), + "state" => MembershipState::JOIN->value, + ]); + + return new JsonResponse([ + "room_id" => $roomId, + ]); + } + /** * GET /_matrix/client/v3/directory/room/{roomAlias} * @@ -59,6 +196,17 @@ class RoomController ]); } + public function readMarkers(Request $request): Response + { + $user = User::authenticateWithRequest($request); + $body = json_decode($request->getContent(), true); + RequestValidator::validateJson(); + + $roomId = $request->attributes->get("roomId"); + + return new JsonResponse(); + } + /** * PUT /_matrix/client/v3/rooms/{roomId}/send/{eventType}/{txnId} * diff --git a/src/Controllers/ServerDiscoveryController.php b/src/Controllers/ServerDiscoveryController.php index f3b96b2..917df14 100644 --- a/src/Controllers/ServerDiscoveryController.php +++ b/src/Controllers/ServerDiscoveryController.php @@ -19,7 +19,7 @@ class ServerDiscoveryController { return new JsonResponse([ "m.homeserver" => [ - "base_url" => "https://$_ENV[DOMAIN]", + "base_url" => "http://$_ENV[DOMAIN]", ], ]); } diff --git a/src/Controllers/SyncController.php b/src/Controllers/SyncController.php index 243fab5..acebb11 100755 --- a/src/Controllers/SyncController.php +++ b/src/Controllers/SyncController.php @@ -3,13 +3,23 @@ namespace App\Controllers; use App\Database; -use App\Errors\UnauthorizedError; -use App\Events\PresenceEvent; -use App\Events\RoomMemberEvent; -use App\Events\RoomMessageEvent; +use App\Models\RoomEvent; use App\Models\User; -use App\Types\MembershipState; -use App\Types\PresenceState; +use Matrix\Data\AccountData; +use Matrix\Data\DeviceLists; +use Matrix\Data\Presence; +use Matrix\Data\Room\Ephemeral; +use Matrix\Data\Room\JoinedRoom; +use Matrix\Data\Room\RoomSummary; +use Matrix\Data\Room\Rooms; +use Matrix\Data\Room\State; +use Matrix\Data\Room\Timeline; +use Matrix\Data\Room\UnreadNotificationCounts; +use Matrix\Data\ToDevice; +use Matrix\Enums\MembershipState; +use Matrix\Enums\PresenceState; +use Matrix\Events\PresenceEvent; +use Matrix\Responses\ClientSyncGetResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\JsonResponse; @@ -24,99 +34,93 @@ class SyncController */ public function sync(Request $request): Response { - $accessToken = str_replace("Bearer ", "", $request->headers->get("authorization") ?: ""); - $user = User::fetchWithAccessToken($accessToken); - - if (empty($user)) { - throw new UnauthorizedError(); - } + $user = User::authenticateWithRequest($request); $filter = $request->query->get("filter", ""); $syncFullState = $request->query->get("full_state", false); $setPresence = PresenceState::tryFrom($request->query->get("set_presence") ?? "") ?? PresenceState::ONLINE; $since = $request->query->get("since", ""); $timeout = $request->query->get("timeout", 0); + $useStateAfter = $request->query->get("use_state_after", false); + + if (! empty($filter)) { + if (str_starts_with($filter, "{")) { + $filter = json_decode($filter, true); + } else { + $filter = Database::getInstance()->query("select * from filters where id=:id", ["id" => $filter])->fetch(); + } + } $rooms = Database::getInstance()->query(<<<SQL select * from rooms - SQL)->fetchAll(); - - $joinedRooms = new \stdClass(); - if (! empty($rooms)) { - $joinedRooms = []; - foreach ($rooms as $room) { - $joinedRooms[$room["id"]] = [ - "account_data" => [ - "events" => [], - ], - - "ephemeral" => [ - "events" => [], - ], - - "state" => [ - "events" => [], - ], - - "summary" => [ - "m.heroes" => [], - "m.invited_member_count" => 0, - "m.joined_member_count" => 1, - ], - - "timeline" => [ - "events" => [ - (new RoomMemberEvent($user->getId(), $user->getId(), MembershipState::JOIN))->toJsonEncodeable(), - (new RoomMessageEvent($user->getId(), "Hallo Test Nachricht"))->toJsonEncodeable(), - ], - "limited" => false, - "prev_batch" => "", - ], - - "unread_notifications" => [ - "highlight_count" => random_int(0, 1), - "notification_count" => random_int(0, 10), - ], - - "unread_thread_notifications" => new \stdClass(), - ]; + left join room_memberships + on rooms.id = room_memberships.room_id + where room_memberships.user_id = :user_id + SQL, [ + "user_id" => $user->getId(), + ])->fetchAll(); + + $invitedRooms = []; + $joinedRooms = []; + $knockedRooms = []; + $leftRooms = []; + + foreach ($rooms as $room) { + $events = Database::getInstance()->query(<<<SQL + select * from room_events + where room_id = :room_id + SQL, [ + "room_id" => $room["room_id"], + #"limit" => ($filter["room"]["timeline"]["limit"] ?? false) ? "limit " . $filter["room"]["timeline"]["limit"] : "", + ])->fetchAll(); + + if ($since === "" && MembershipState::tryFrom($room["state"]) === MembershipState::JOIN) { + $joinedRooms[$room["room_id"]] = new JoinedRoom( + accountData: new AccountData([]), + ephemeral: new Ephemeral([]), + state: new State([]), + summary: new RoomSummary( + heroes: [], + invitedMemberCount: 0, + joinedMemberCount: 1, + ), + timeline: new Timeline( + events: array_map([RoomEvent::class, "transformEvent"], $events), + limited: false,# $filter["room"]["timeline"]["limit"] ?? false, + previousBatch: null, + ), + unreadNotifications: new UnreadNotificationCounts(0, 0), + unreadThreadNotifications: [], + ); } } - return new JsonResponse([ - "account_data" => [ - "events" => [ + return new JsonResponse(new ClientSyncGetResponse( + nextBatch: "1", - ], - ], + accountData: new AccountData([]), - "device_lists" => [ - "changed" => [], - "left" => [], - ], + deviceLists: new DeviceLists([], []), - "device_one_time_keys_count" => [ + deviceOneTimeKeysCount: [ "signed_curve25519" => 10, ], - "next_batch" => "next_batch_id", - - "presence" => [ - "events" => [ - (new PresenceEvent(sender: $user->getId()))->toJsonEncodeable(), - ], - ], - - "rooms" => [ - "invite" => new \stdClass(), - "join" => $joinedRooms, - "knock" => new \stdClass(), - "leave" => new \stdClass(), - ], - - "to_device" => [ - "events" => [], - ], - ]); + presence: new Presence([ + new PresenceEvent( + sender: $user->getId(), + presence: $setPresence, + ), + ]), + + rooms: new Rooms( + $invitedRooms, + $joinedRooms, + $knockedRooms, + $leftRooms, + ), + + toDevice: new ToDevice([]), + )); } } diff --git a/src/Controllers/UserController.php b/src/Controllers/UserController.php index a056e1d..d102160 100755 --- a/src/Controllers/UserController.php +++ b/src/Controllers/UserController.php @@ -2,8 +2,10 @@ namespace App\Controllers; +use App\Database; use App\Errors\UnauthorizedError; use App\Models\User; +use App\Support\RequestValidator; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\JsonResponse; @@ -25,8 +27,31 @@ class UserController throw new UnauthorizedError(); } + $userId = $request->get("userId"); + if ($user->getId() !== $userId) { + throw new UnauthorizedError(); + } + + $body = json_decode($request->getContent(), true); + RequestValidator::validateJson(); + + $filterId = md5($userId . random_bytes(512)); + + Database::getInstance()->query(<<<SQL + insert into filters (id, account_data, event_fields, event_format, presence, room, user_id) + values (:id, :account_data, :event_fields, :event_format, :presence, :room, :user_id) + SQL, [ + "id" => $filterId, + "account_data" => isset($body["account_data"]) ? json_encode($body["account_data"]) : null, + "event_fields" => isset($body["event_fields"]) ? json_encode($body["event_fields"]) : null, + "event_format" => isset($body["event_format"]) ? json_encode($body["event_format"]) : null, + "presence" => isset($body["presence"]) ? json_encode($body["presence"]) : null, + "room" => isset($body["room"]) ? json_encode($body["room"]) : null, + "user_id" => $userId, + ]); + return new JsonResponse([ - "filter_id" => "1234", + "filter_id" => $filterId, ]); } } diff --git a/src/Database.php b/src/Database.php index e84abc1..4c26a93 100644 --- a/src/Database.php +++ b/src/Database.php @@ -2,9 +2,6 @@ namespace App; -use PDO; -use PDOStatement; - class Database { use Singleton; @@ -40,4 +37,9 @@ class Database return $statement; } + + public function getLastInsertId(string $name = ""): string|false + { + return $this->connection->lastInsertId($name); + } } diff --git a/src/Events/Event.php b/src/Events/Event.php deleted file mode 100644 index 7dbf242..0000000 --- a/src/Events/Event.php +++ /dev/null @@ -1,20 +0,0 @@ -<?php - -namespace App\Events; - -use App\Types\EventType; - -abstract class Event -{ - public function __construct( - protected EventType $type, - ) - {} - - abstract public function fromJson(string $json): self; - - /** - * @return array<string, mixed> - */ - abstract public function toJsonEncodeable(): array; -} diff --git a/src/Events/PresenceEvent.php b/src/Events/PresenceEvent.php deleted file mode 100644 index cd230c5..0000000 --- a/src/Events/PresenceEvent.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php - -namespace App\Events; - -use App\Types\EventType; -use App\Types\PresenceState; - -class PresenceEvent extends Event -{ - public function __construct( - private string $sender, - private string $avatarUrl = "mxc://localhost/wefuiwegh8742w", - private int $lastActiveAgo = 1234, - private bool $currentlyActive = false, - private PresenceState $presence = PresenceState::ONLINE, - private string $statusMessage = "", - ) - { - parent::__construct(EventType::PRESENCE); - } - - public function fromJson(string $json): self - { - } - - public function toJsonEncodeable(): array - { - return [ - "type" => $this->type, - "sender" => $this->sender, - "content" => [ - "avatar_url" => $this->avatarUrl, - "currently_active" => $this->currentlyActive, - "last_active_ago" => $this->lastActiveAgo, - "presence" => $this->presence, - "status_msg" => $this->statusMessage, - ], - ]; - } -} diff --git a/src/Events/RoomMemberEvent.php b/src/Events/RoomMemberEvent.php deleted file mode 100644 index 79ba0cc..0000000 --- a/src/Events/RoomMemberEvent.php +++ /dev/null @@ -1,36 +0,0 @@ -<?php - -namespace App\Events; - -use App\Types\EventType; -use App\Types\MembershipState; - -class RoomMemberEvent extends Event -{ - public function __construct( - private string $sender, - private string $stateKey, - private MembershipState $membership, - ) - { - parent::__construct(EventType::ROOM_MEMBER); - } - - public function fromJson(string $json): self - { - } - - public function toJsonEncodeable(): array - { - return [ - "type" => $this->type, - "sender" => $this->sender, - "state_key" => $this->stateKey, - "event_id" => "\$0", - "origin_server_ts" => time(), - "content" => [ - "membership" => $this->membership, - ], - ]; - } -} diff --git a/src/Events/RoomMessageEvent.php b/src/Events/RoomMessageEvent.php deleted file mode 100644 index 3ccc408..0000000 --- a/src/Events/RoomMessageEvent.php +++ /dev/null @@ -1,86 +0,0 @@ -<?php - -namespace App\Events; - -use App\Database; -use App\Support\ConnectsToDatabase; -use App\Types\EventType; -use App\Types\MembershipState; -use App\Types\MessageType; - -class RoomMessageEvent extends Event implements ConnectsToDatabase -{ - public function __construct( - private string $id = "", - private string $sender = "", - private \DateTime $originServerTimestamp = new \DateTime("now"), - private array $content = [], - private array $unsigned = [], - private string $roomId = "", - ) - { - parent::__construct(EventType::ROOM_MESSAGE); - } - - public function fromJson(string $json): self - { - } - - public function toJsonEncodeable(): array - { - return [ - "type" => $this->type, - "sender" => $this->sender, - "event_id" => $this->id, - "origin_server_ts" => $this->originServerTimestamp->format("U.v"), - "content" => [ - "body" => $this->body, - "msgtype" => MessageType::TEXT, - ], - "unsigned" => [ - "age" => 1234, - "membership" => MembershipState::JOIN, - ], - "room_id" => $this->roomId, - ]; - } - - public static function fromDatabase(array $row): self - { - } - - public static function fetch(): ?self - { - } - - public static function fetchAll(): array - { - } - - public function insert(): bool - { - return !! Database::getInstance()->query( - <<<SQL - insert into room_events (id, type, sender, origin_server_timestamp, content, unsigned, room_id) - values (:id, :type, :sender, to_timestamp(:origin_server_timestamp), :content, :unsigned, :room_id) - SQL, - [ - "id" => $this->id, - "type" => $this->type->value, - "sender" => $this->sender, - "origin_server_timestamp" => (new \DateTime("now"))->format("U.v"), - "content" => json_encode($this->content), - "unsigned" => json_encode($this->unsigned), - "room_id" => $this->roomId, - ] - ); - } - - public function update(): bool - { - } - - public function delete(): bool - { - } -} diff --git a/src/Events/RoomNameEvent.php b/src/Events/RoomNameEvent.php deleted file mode 100644 index 279e6c1..0000000 --- a/src/Events/RoomNameEvent.php +++ /dev/null @@ -1,33 +0,0 @@ -<?php - -namespace App\Events; - -use App\Types\EventType; - -class RoomNameEvent extends Event -{ - public function __construct( - private string $sender, - private string $stateKey, - private string $name, - ) - { - parent::__construct(EventType::ROOM_NAME); - } - - public function fromJson(string $json): self - { - } - - public function toJsonEncodeable(): array - { - return [ - "type" => $this->type, - "sender" => $this->sender, - "state_key" => $this->stateKey, - "content" => [ - "name" => $this->name, - ], - ]; - } -} diff --git a/src/Models/RoomEvent.php b/src/Models/RoomEvent.php new file mode 100644 index 0000000..96dc204 --- /dev/null +++ b/src/Models/RoomEvent.php @@ -0,0 +1,64 @@ +<?php + +namespace App\Models; + +use App\Database; +use Matrix\Enums\EventType; +use Matrix\Events\ClientEvent; +use Matrix\Events\StateEvent; + +class RoomEvent +{ + public function __construct( + private ClientEvent $event, + ) + {} + + /** + * @param array<string, mixed> $row + */ + public static function transformEvent(array $row): ClientEvent + { + return new ClientEvent( + content: json_decode($row["content"], true), + eventId: $row["id"], + originServerTimestamp: new \DateTime($row["origin_server_timestamp"])->getTimestamp(), + roomId: $row["room_id"], + sender: $row["sender"], + type: EventType::from($row["type"]), + unsigned: json_decode($row["unsigned"], true), + ); + } + + public function insert(): bool + { + if ($this->event instanceof StateEvent) { + return !! Database::getInstance()->query(<<<SQL + insert into room_events (id, content, type, sender, origin_server_timestamp, room_id, unsigned, state_key) + values (:id, :content, :type, :sender, to_timestamp(:origin_server_timestamp), :room_id, :unsigned, :state_key) + SQL, [ + "id" => $this->event->getId(), + "content" => json_encode($this->event->getContent()), + "type" => $this->event->getType()->value, + "sender" => $this->event->getSender(), + "origin_server_timestamp" => \DateTime::createFromTimestamp($this->event->getOriginServerTimestamp())->format("U.v"), + "room_id" => $this->event->getRoomId(), + "unsigned" => json_encode($this->event->getUnsigned()), + "state_key" => $this->event->getStateKey(), + ]); + } + + return !! Database::getInstance()->query(<<<SQL + insert into room_events (id, content, type, sender, origin_server_timestamp, room_id, unsigned) + values (:id, :content, :type, :sender, to_timestamp(:origin_server_timestamp), :room_id, :unsigned) + SQL, [ + "id" => $this->event->getId(), + "content" => json_encode($this->event->getContent()), + "type" => $this->event->getType()->value, + "sender" => $this->event->getSender(), + "origin_server_timestamp" => \DateTime::createFromTimestamp($this->event->getOriginServerTimestamp())->format("U.v"), + "room_id" => $this->event->getRoomId(), + "unsigned" => json_encode($this->event->getUnsigned()), + ]); + } +} diff --git a/src/Models/Tokens.php b/src/Models/Tokens.php index 4ad8e1d..ee912fb 100644 --- a/src/Models/Tokens.php +++ b/src/Models/Tokens.php @@ -37,9 +37,9 @@ class Tokens implements ConnectsToDatabase $isExpiredSql = ""; if ($isExpired) { - $isExpiredSql = "and expires_at <= current_timestamp"; + #$isExpiredSql = "and expires_at <= current_timestamp"; } else { - $isExpiredSql = "and expires_at > current_timestamp"; + #$isExpiredSql = "and expires_at > current_timestamp"; } $row = []; @@ -78,6 +78,26 @@ class Tokens implements ConnectsToDatabase public static function fetchAll(): array {} + public static function fetchWithAccessToken(string $accessToken): ?self + { + $row = Database::getInstance()->query( + <<<SQL + select * from tokens + where access_token=:access_token + order by created_at desc + SQL, + [ + "access_token" => $accessToken, + ] + )->fetch(); + + if (empty($row)) { + return null; + } + + return self::fromDatabase($row); + } + public static function fetchWithRefreshToken(string $refreshToken): ?self { $row = Database::getInstance()->query( @@ -140,6 +160,11 @@ class Tokens implements ConnectsToDatabase ); } + public function isExpired(): bool + { + return $this->expiresAt->format("U.v") <= time(); + } + public function getAccessToken(): string { return $this->accessToken; diff --git a/src/Models/User.php b/src/Models/User.php index c0c73f8..c92f5b3 100644 --- a/src/Models/User.php +++ b/src/Models/User.php @@ -3,19 +3,26 @@ namespace App\Models; use App\Database; +use App\Errors\AppException; +use App\Errors\ErrorCode; use App\Errors\UnauthorizedError; use App\Support\ConnectsToDatabase; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; class User implements ConnectsToDatabase { - public function __construct(private string $id) + public function __construct( + private string $id, + private string $name, + ) {} public static function fromDatabase(array $row): self { return new self( $row["id"], + $row["name"], ); } @@ -67,9 +74,9 @@ class User implements ConnectsToDatabase return self::fromDatabase($row); } - public static function new(string $id): self + public static function new(string $id, string $name): self { - return new self($id); + return new self($id, $name); } public static function authenticateWithRequest(Request $request): self @@ -81,6 +88,17 @@ class User implements ConnectsToDatabase throw new UnauthorizedError(); } + $tokens = Tokens::fetchWithAccessToken($accessToken); + + if (empty($tokens) /*|| $tokens->isExpired()*/) { + throw new AppException( + ErrorCode::UNKNOWN_TOKEN, + "Soft logged out", + Response::HTTP_UNAUTHORIZED, + ["soft_logout" => true], + ); + } + return $user; } @@ -88,11 +106,12 @@ class User implements ConnectsToDatabase { return !! Database::getInstance()->query( <<<SQL - insert into users (id) - values (:id) + insert into users (id, name) + values (:id, :name) SQL, [ "id" => $this->id, + "name" => $this->name, ] ); } @@ -108,10 +127,16 @@ class User implements ConnectsToDatabase return $this->id; } + public function getName(): string + { + return $this->name; + } + public function fetchDevice(string $id): ?Device { return Device::fetch($id, $this->id); } + /** * @return Device[] */ diff --git a/src/Router/Router.php b/src/Router/Router.php index 534b7f7..377821f 100644 --- a/src/Router/Router.php +++ b/src/Router/Router.php @@ -6,6 +6,7 @@ use App\Errors\ErrorCode; use App\Errors\ErrorResponse; use App\Errors\Exception; use App\Singleton; +use App\Support\Logger; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Exception\MethodNotAllowedException; @@ -38,14 +39,14 @@ class Router { $request = Request::createFromGlobals(); - if ($request->isMethod("OPTIONS")) { - $response = new Response(); - $response->headers->add([ - "Access-Control-Allow-Origin" => "*", - "Access-Control-Allow-Methods" => "GET, POST, PUT, DELETE, OPTIONS, PATCH, HEAD", - "Access-Control-Allow-Headers" => "X-Requested-With, Content-Type, Authorization", - ]); + $response = new Response(); + $response->headers->add([ + "Access-Control-Allow-Origin" => "*", + "Access-Control-Allow-Methods" => "GET, POST, PUT, DELETE, OPTIONS, PATCH, HEAD", + "Access-Control-Allow-Headers" => "X-Requested-With, Content-Type, Authorization", + ]); + if ($request->isMethod("OPTIONS")) { return $response; } @@ -64,22 +65,34 @@ class Router array_flip(["_controller", "_route"]) )); - return (new $class)->$method($request); + Logger::logRequestToFile($request); + + $response = (new $class)->$method($request); } catch (Exception $exception) { - return ErrorResponse::fromException($exception); + $response = ErrorResponse::fromException($exception); } catch (ResourceNotFoundException $exception) { - return new ErrorResponse(ErrorCode::NOT_FOUND, "404", Response::HTTP_NOT_FOUND); + $response = new ErrorResponse(ErrorCode::NOT_FOUND, "404", Response::HTTP_NOT_FOUND); } catch (MethodNotAllowedException $exception) { - return new ErrorResponse(ErrorCode::FORBIDDEN, "403", Response::HTTP_FORBIDDEN); + $response = new ErrorResponse(ErrorCode::FORBIDDEN, "403", Response::HTTP_FORBIDDEN); } catch (\LogicException $exception) { // display logic exceptions normally throw $exception; } catch (\Exception $exception) { - return new ErrorResponse( + $response = new ErrorResponse( ErrorCode::UNKNOWN, $exception->getMessage() ?: "Unknown error occured", Response::HTTP_INTERNAL_SERVER_ERROR ); + } catch (\Error $error) { + error_log($error->getMessage() ?: "Unknown error occured"); + + $response = new ErrorResponse( + ErrorCode::UNKNOWN, + $error->getMessage() ?: "Unknown error occured", + Response::HTTP_INTERNAL_SERVER_ERROR + ); } + + return $response; } /** diff --git a/src/Router/routes_client_server.php b/src/Router/routes_client_server.php index fd39d08..590520b 100644 --- a/src/Router/routes_client_server.php +++ b/src/Router/routes_client_server.php @@ -34,6 +34,10 @@ return function (RouteConfigurator $routes): void ->add("matrix_client_r0_login_types", "/_matrix/client/r0/login") ->controller($supportedLoginTypes) ->methods(["GET"]); + $routes + ->add("matrix_client_v3_login_types", "/_matrix/client/v3/login") + ->controller($supportedLoginTypes) + ->methods(["GET"]); $routes ->add("matrix_client_r0_login", "/_matrix/client/r0/login") @@ -89,4 +93,19 @@ return function (RouteConfigurator $routes): void ->add("matrix_client_r0_user_id_filter", "/_matrix/client/r0/user/{userId}/filter") ->controller([UserController::class, "uploadFilter"]) ->methods(["POST"]); + + $routes + ->add("matrix_client_v3_user_id_filter", "/_matrix/client/v3/user/{userId}/filter") + ->controller([UserController::class, "uploadFilter"]) + ->methods(["POST"]); + + $routes + ->add("matrix_client_v3_room_create", "/_matrix/client/v3/createRoom") + ->controller([RoomController::class, "createRoom"]) + ->methods(["POST"]); + + $routes + ->add("matrix_client_v3_rooms_id_read_markers", "/_matrix/client/v3/rooms/{roomId}/read_markers") + ->controller([RoomController::class, "readMarkers"]) + ->methods(["POST"]); }; diff --git a/src/Support/ArrayTransformable.php b/src/Support/ArrayTransformable.php deleted file mode 100644 index f1adf81..0000000 --- a/src/Support/ArrayTransformable.php +++ /dev/null @@ -1,11 +0,0 @@ -<?php - -namespace App\Support; - -interface ArrayTransformable -{ - /** - * @return array - */ - public function toArray(): array; -} diff --git a/src/Support/Id.php b/src/Support/Id.php new file mode 100644 index 0000000..dc23631 --- /dev/null +++ b/src/Support/Id.php @@ -0,0 +1,21 @@ +<?php + +namespace App\Support; + +class Id +{ + public static function generate(string $prefix = "", string $suffix = "", string $salt = ""): string + { + return $prefix . md5(time() . random_bytes(512)) . $suffix; + } + + public static function generateRoomId(): string + { + return self::generate(prefix: "!", suffix: ":" . $_ENV["DOMAIN"]); + } + + public static function generateEventId(): string + { + return self::generate(prefix: "$", suffix: ":" . $_ENV["DOMAIN"]); + } +} diff --git a/src/Support/Logger.php b/src/Support/Logger.php index c806157..b01af11 100644 --- a/src/Support/Logger.php +++ b/src/Support/Logger.php @@ -9,6 +9,7 @@ class Logger implements LoggerInterface { public static function logRequestToFile(Request $request): void { + @mkdir(dirname(dirname(__DIR__)) . "/.cache/log", recursive: true); $basePath = dirname(dirname(__DIR__)) . "/.cache/log/" . str_replace("/", "_", $request->getPathInfo()); file_put_contents( diff --git a/src/Types/AuthenticationType.php b/src/Types/AuthenticationType.php deleted file mode 100644 index dc768d0..0000000 --- a/src/Types/AuthenticationType.php +++ /dev/null @@ -1,14 +0,0 @@ -<?php - -namespace App\Types; - -enum AuthenticationType: string -{ - case PASSWORD = "m.login.password"; - case RECAPTCHA = "m.login.recaptcha"; - case SSO = "m.login.sso"; - case EMAIL_IDENTITY = "m.login.email.identity"; - case MSISDN = "m.login.msisdn"; - case DUMMY = "m.login.dummy"; - case REGISTRATION_TOKEN = "m.login.registration_token"; -} diff --git a/src/Types/EventType.php b/src/Types/EventType.php deleted file mode 100644 index d599c6f..0000000 --- a/src/Types/EventType.php +++ /dev/null @@ -1,11 +0,0 @@ -<?php - -namespace App\Types; - -enum EventType: string -{ - case PRESENCE = "m.presence"; - case ROOM_NAME = "m.room.name"; - case ROOM_MEMBER = "m.room.member"; - case ROOM_MESSAGE = "m.room.message"; -} diff --git a/src/Types/IdentifierType.php b/src/Types/IdentifierType.php deleted file mode 100644 index 93396fa..0000000 --- a/src/Types/IdentifierType.php +++ /dev/null @@ -1,10 +0,0 @@ -<?php - -namespace App\Types; - -enum IdentifierType: string -{ - case USER = "m.id.user"; - case THIRDPARTY = "m.id.thirdparty"; - case PHONE = "m.id.phone"; -} diff --git a/src/Types/LoginFlow.php b/src/Types/LoginFlow.php deleted file mode 100644 index bd948a8..0000000 --- a/src/Types/LoginFlow.php +++ /dev/null @@ -1,27 +0,0 @@ -<?php - -namespace App\Types; - -use App\Support\ArrayTransformable; - -class LoginFlow implements ArrayTransformable -{ - public function __construct( - private LoginType $type, - private bool $get_login_token = false, - ) - {} - - public function toArray(): array - { - $flow = [ - "type" => $this->type, - ]; - - if ($this->type == LoginType::TOKEN) { - $flow["get_login_token"] = $this->get_login_token; - } - - return $flow; - } -} diff --git a/src/Types/LoginType.php b/src/Types/LoginType.php deleted file mode 100644 index a5e3d8b..0000000 --- a/src/Types/LoginType.php +++ /dev/null @@ -1,9 +0,0 @@ -<?php - -namespace App\Types; - -enum LoginType: string -{ - case PASSWORD = "m.login.password"; - case TOKEN = "m.login.token"; -} diff --git a/src/Types/MembershipState.php b/src/Types/MembershipState.php deleted file mode 100644 index a4b2a1d..0000000 --- a/src/Types/MembershipState.php +++ /dev/null @@ -1,12 +0,0 @@ -<?php - -namespace App\Types; - -enum MembershipState: string -{ - case INVITE = "invite"; - case JOIN = "join"; - case LEAVE = "leave"; - case BAN = "ban"; - case KNOCK = "knock"; -} diff --git a/src/Types/MessageType.php b/src/Types/MessageType.php deleted file mode 100644 index ded17a0..0000000 --- a/src/Types/MessageType.php +++ /dev/null @@ -1,8 +0,0 @@ -<?php - -namespace App\Types; - -enum MessageType: string -{ - case TEXT = "m.text"; -} diff --git a/src/Types/PresenceState.php b/src/Types/PresenceState.php deleted file mode 100644 index 26eeac6..0000000 --- a/src/Types/PresenceState.php +++ /dev/null @@ -1,10 +0,0 @@ -<?php - -namespace App\Types; - -enum PresenceState: string -{ - case ONLINE = "online"; - case OFFLINE = "offline"; - case UNAVAILABLE = "unavailable"; -} diff --git a/src/Types/UserRegistrationKind.php b/src/Types/UserRegistrationKind.php deleted file mode 100644 index 23dee2f..0000000 --- a/src/Types/UserRegistrationKind.php +++ /dev/null @@ -1,9 +0,0 @@ -<?php - -namespace App\Types; - -enum UserRegistrationKind: string -{ - case USER = "user"; - case GUEST = "guest"; -} |
