forked from Mirrors/gomuks
187 lines
6.7 KiB
Go
187 lines
6.7 KiB
Go
// Copyright (c) 2024 Tulir Asokan
|
|
//
|
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
package database
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"errors"
|
|
"fmt"
|
|
"slices"
|
|
|
|
"go.mau.fi/util/dbutil"
|
|
"maunium.net/go/mautrix"
|
|
"maunium.net/go/mautrix/crypto"
|
|
"maunium.net/go/mautrix/event"
|
|
"maunium.net/go/mautrix/id"
|
|
)
|
|
|
|
const (
|
|
getMembershipQuery = `
|
|
SELECT membership FROM current_state
|
|
WHERE room_id = $1 AND event_type = 'm.room.member' AND state_key = $2
|
|
`
|
|
getStateEventContentQuery = `
|
|
SELECT event.content FROM current_state cs
|
|
LEFT JOIN event ON event.rowid = cs.event_rowid
|
|
WHERE cs.room_id = $1 AND cs.event_type = $2 AND cs.state_key = $3
|
|
`
|
|
getRoomJoinedMembersQuery = `
|
|
SELECT state_key FROM current_state
|
|
WHERE room_id = $1 AND event_type = 'm.room.member' AND membership = 'join'
|
|
`
|
|
getRoomJoinedOrInvitedMembersQuery = `
|
|
SELECT state_key FROM current_state
|
|
WHERE room_id = $1 AND event_type = 'm.room.member' AND membership IN ('join', 'invite')
|
|
`
|
|
getHasFetchedMembersQuery = `
|
|
SELECT has_member_list FROM room WHERE room_id = $1
|
|
`
|
|
isRoomEncryptedQuery = `
|
|
SELECT room.encryption_event IS NOT NULL FROM room WHERE room_id = $1
|
|
`
|
|
getRoomEncryptionEventQuery = `
|
|
SELECT room.encryption_event FROM room WHERE room_id = $1
|
|
`
|
|
findSharedRoomsQuery = `
|
|
SELECT room_id FROM current_state
|
|
WHERE event_type = 'm.room.member' AND state_key = $1 AND membership = 'join'
|
|
`
|
|
)
|
|
|
|
type ClientStateStore struct {
|
|
*Database
|
|
}
|
|
|
|
var _ mautrix.StateStore = (*ClientStateStore)(nil)
|
|
var _ mautrix.StateStoreUpdater = (*ClientStateStore)(nil)
|
|
var _ crypto.StateStore = (*ClientStateStore)(nil)
|
|
|
|
func (c *ClientStateStore) IsInRoom(ctx context.Context, roomID id.RoomID, userID id.UserID) bool {
|
|
return c.IsMembership(ctx, roomID, userID, event.MembershipJoin)
|
|
}
|
|
|
|
func (c *ClientStateStore) IsInvited(ctx context.Context, roomID id.RoomID, userID id.UserID) bool {
|
|
return c.IsMembership(ctx, roomID, userID, event.MembershipInvite, event.MembershipJoin)
|
|
}
|
|
|
|
func (c *ClientStateStore) IsMembership(ctx context.Context, roomID id.RoomID, userID id.UserID, allowedMemberships ...event.Membership) bool {
|
|
var membership event.Membership
|
|
err := c.QueryRow(ctx, getMembershipQuery, roomID, userID).Scan(&membership)
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
err = nil
|
|
membership = event.MembershipLeave
|
|
}
|
|
return slices.Contains(allowedMemberships, membership)
|
|
}
|
|
|
|
func (c *ClientStateStore) GetMember(ctx context.Context, roomID id.RoomID, userID id.UserID) (*event.MemberEventContent, error) {
|
|
content, err := c.TryGetMember(ctx, roomID, userID)
|
|
if content == nil {
|
|
content = &event.MemberEventContent{Membership: event.MembershipLeave}
|
|
}
|
|
return content, err
|
|
}
|
|
|
|
func (c *ClientStateStore) TryGetMember(ctx context.Context, roomID id.RoomID, userID id.UserID) (content *event.MemberEventContent, err error) {
|
|
err = c.QueryRow(ctx, getStateEventContentQuery, roomID, event.StateMember.Type, userID).Scan(&dbutil.JSON{Data: &content})
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
err = nil
|
|
}
|
|
return
|
|
}
|
|
|
|
func (c *ClientStateStore) IsConfusableName(ctx context.Context, roomID id.RoomID, currentUser id.UserID, name string) ([]id.UserID, error) {
|
|
//TODO implement me
|
|
panic("implement me")
|
|
}
|
|
|
|
func (c *ClientStateStore) GetPowerLevels(ctx context.Context, roomID id.RoomID) (content *event.PowerLevelsEventContent, err error) {
|
|
err = c.QueryRow(ctx, getStateEventContentQuery, roomID, event.StatePowerLevels.Type, "").Scan(&dbutil.JSON{Data: &content})
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
err = nil
|
|
}
|
|
return
|
|
}
|
|
|
|
func (c *ClientStateStore) GetRoomJoinedMembers(ctx context.Context, roomID id.RoomID) ([]id.UserID, error) {
|
|
rows, err := c.Query(ctx, getRoomJoinedMembersQuery, roomID)
|
|
return dbutil.NewRowIterWithError(rows, dbutil.ScanSingleColumn[id.UserID], err).AsList()
|
|
}
|
|
|
|
func (c *ClientStateStore) GetRoomJoinedOrInvitedMembers(ctx context.Context, roomID id.RoomID) ([]id.UserID, error) {
|
|
rows, err := c.Query(ctx, getRoomJoinedOrInvitedMembersQuery, roomID)
|
|
return dbutil.NewRowIterWithError(rows, dbutil.ScanSingleColumn[id.UserID], err).AsList()
|
|
}
|
|
|
|
func (c *ClientStateStore) HasFetchedMembers(ctx context.Context, roomID id.RoomID) (hasFetched bool, err error) {
|
|
//err = c.QueryRow(ctx, getHasFetchedMembersQuery, roomID).Scan(&hasFetched)
|
|
//if errors.Is(err, sql.ErrNoRows) {
|
|
// err = nil
|
|
//}
|
|
//return
|
|
return false, fmt.Errorf("not implemented")
|
|
}
|
|
|
|
func (c *ClientStateStore) MarkMembersFetched(ctx context.Context, roomID id.RoomID) error {
|
|
return fmt.Errorf("not implemented")
|
|
}
|
|
|
|
func (c *ClientStateStore) GetAllMembers(ctx context.Context, roomID id.RoomID) (map[id.UserID]*event.MemberEventContent, error) {
|
|
return nil, fmt.Errorf("not implemented")
|
|
}
|
|
|
|
func (c *ClientStateStore) IsEncrypted(ctx context.Context, roomID id.RoomID) (isEncrypted bool, err error) {
|
|
err = c.QueryRow(ctx, isRoomEncryptedQuery, roomID).Scan(&isEncrypted)
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
err = nil
|
|
}
|
|
return
|
|
}
|
|
|
|
func (c *ClientStateStore) GetEncryptionEvent(ctx context.Context, roomID id.RoomID) (content *event.EncryptionEventContent, err error) {
|
|
err = c.QueryRow(ctx, getRoomEncryptionEventQuery, roomID).
|
|
Scan(&dbutil.JSON{Data: &content})
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
err = nil
|
|
}
|
|
return
|
|
}
|
|
|
|
func (c *ClientStateStore) FindSharedRooms(ctx context.Context, userID id.UserID) ([]id.RoomID, error) {
|
|
// TODO for multiuser support, this might need to filter by the local user's membership
|
|
rows, err := c.Query(ctx, findSharedRoomsQuery, userID)
|
|
return dbutil.NewRowIterWithError(rows, dbutil.ScanSingleColumn[id.RoomID], err).AsList()
|
|
}
|
|
|
|
// Update methods are all intentionally no-ops as the state store wants to have the full event
|
|
|
|
func (c *ClientStateStore) SetMembership(ctx context.Context, roomID id.RoomID, userID id.UserID, membership event.Membership) error {
|
|
return nil
|
|
}
|
|
|
|
func (c *ClientStateStore) SetMember(ctx context.Context, roomID id.RoomID, userID id.UserID, member *event.MemberEventContent) error {
|
|
return nil
|
|
}
|
|
|
|
func (c *ClientStateStore) ClearCachedMembers(ctx context.Context, roomID id.RoomID, memberships ...event.Membership) error {
|
|
return nil
|
|
}
|
|
|
|
func (c *ClientStateStore) SetPowerLevels(ctx context.Context, roomID id.RoomID, levels *event.PowerLevelsEventContent) error {
|
|
return nil
|
|
}
|
|
|
|
func (c *ClientStateStore) SetEncryptionEvent(ctx context.Context, roomID id.RoomID, content *event.EncryptionEventContent) error {
|
|
return nil
|
|
}
|
|
|
|
func (c *ClientStateStore) UpdateState(ctx context.Context, evt *event.Event) {}
|
|
|
|
func (c *ClientStateStore) ReplaceCachedMembers(ctx context.Context, roomID id.RoomID, evts []*event.Event, onlyMemberships ...event.Membership) error {
|
|
return nil
|
|
}
|