From acf1dbc9269f627002f0fb23c8fe376a7edeaf1f Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Thu, 2 Jul 2020 16:14:49 +0100 Subject: [PATCH] Remove all of publicroomsapi --- .../main.go | 0 .../storage/postgreswithdht/storage.go | 164 ---------- .../storage/postgreswithpubsub/storage.go | 179 ----------- cmd/dendrite-demo-libp2p/storage/storage.go | 62 ---- publicroomsapi/README.md | 5 - publicroomsapi/consumers/roomserver.go | 99 ------- publicroomsapi/directory/directory.go | 120 -------- publicroomsapi/directory/public_rooms.go | 262 ---------------- publicroomsapi/publicroomsapi.go | 51 ---- publicroomsapi/routing/routing.go | 79 ----- publicroomsapi/storage/interface.go | 32 -- publicroomsapi/storage/postgres/prepare.go | 36 --- .../storage/postgres/public_rooms_table.go | 280 ------------------ publicroomsapi/storage/postgres/storage.go | 259 ---------------- publicroomsapi/storage/sqlite3/prepare.go | 36 --- .../storage/sqlite3/public_rooms_table.go | 273 ----------------- publicroomsapi/storage/sqlite3/storage.go | 265 ----------------- publicroomsapi/storage/storage.go | 45 --- publicroomsapi/storage/storage_wasm.go | 39 --- publicroomsapi/types/types.go | 24 -- 20 files changed, 2310 deletions(-) rename cmd/{dendrite-public-rooms-api-server => dendrite-current-state-server}/main.go (100%) delete mode 100644 cmd/dendrite-demo-libp2p/storage/postgreswithdht/storage.go delete mode 100644 cmd/dendrite-demo-libp2p/storage/postgreswithpubsub/storage.go delete mode 100644 cmd/dendrite-demo-libp2p/storage/storage.go delete mode 100644 publicroomsapi/README.md delete mode 100644 publicroomsapi/consumers/roomserver.go delete mode 100644 publicroomsapi/directory/directory.go delete mode 100644 publicroomsapi/directory/public_rooms.go delete mode 100644 publicroomsapi/publicroomsapi.go delete mode 100644 publicroomsapi/routing/routing.go delete mode 100644 publicroomsapi/storage/interface.go delete mode 100644 publicroomsapi/storage/postgres/prepare.go delete mode 100644 publicroomsapi/storage/postgres/public_rooms_table.go delete mode 100644 publicroomsapi/storage/postgres/storage.go delete mode 100644 publicroomsapi/storage/sqlite3/prepare.go delete mode 100644 publicroomsapi/storage/sqlite3/public_rooms_table.go delete mode 100644 publicroomsapi/storage/sqlite3/storage.go delete mode 100644 publicroomsapi/storage/storage.go delete mode 100644 publicroomsapi/storage/storage_wasm.go delete mode 100644 publicroomsapi/types/types.go diff --git a/cmd/dendrite-public-rooms-api-server/main.go b/cmd/dendrite-current-state-server/main.go similarity index 100% rename from cmd/dendrite-public-rooms-api-server/main.go rename to cmd/dendrite-current-state-server/main.go diff --git a/cmd/dendrite-demo-libp2p/storage/postgreswithdht/storage.go b/cmd/dendrite-demo-libp2p/storage/postgreswithdht/storage.go deleted file mode 100644 index d2cb36a8b..000000000 --- a/cmd/dendrite-demo-libp2p/storage/postgreswithdht/storage.go +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright 2020 The Matrix.org Foundation C.I.C. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package postgreswithdht - -import ( - "context" - "encoding/json" - "fmt" - "sync" - "sync/atomic" - "time" - - "github.com/matrix-org/dendrite/publicroomsapi/storage/postgres" - "github.com/matrix-org/gomatrixserverlib" - - dht "github.com/libp2p/go-libp2p-kad-dht" -) - -const DHTInterval = time.Second * 10 - -// PublicRoomsServerDatabase represents a public rooms server database. -type PublicRoomsServerDatabase struct { - dht *dht.IpfsDHT - postgres.PublicRoomsServerDatabase - ourRoomsContext context.Context // our current value in the DHT - ourRoomsCancel context.CancelFunc // cancel when we want to expire our value - foundRooms map[string]gomatrixserverlib.PublicRoom // additional rooms we have learned about from the DHT - foundRoomsMutex sync.RWMutex // protects foundRooms - maintenanceTimer *time.Timer // - roomsAdvertised atomic.Value // stores int - roomsDiscovered atomic.Value // stores int -} - -// NewPublicRoomsServerDatabase creates a new public rooms server database. -func NewPublicRoomsServerDatabase(dataSourceName string, dht *dht.IpfsDHT, localServerName gomatrixserverlib.ServerName) (*PublicRoomsServerDatabase, error) { - pg, err := postgres.NewPublicRoomsServerDatabase(dataSourceName, nil, localServerName) - if err != nil { - return nil, err - } - provider := PublicRoomsServerDatabase{ - dht: dht, - PublicRoomsServerDatabase: *pg, - } - go provider.ResetDHTMaintenance() - provider.roomsAdvertised.Store(0) - provider.roomsDiscovered.Store(0) - return &provider, nil -} - -func (d *PublicRoomsServerDatabase) GetRoomVisibility(ctx context.Context, roomID string) (bool, error) { - return d.PublicRoomsServerDatabase.GetRoomVisibility(ctx, roomID) -} - -func (d *PublicRoomsServerDatabase) SetRoomVisibility(ctx context.Context, visible bool, roomID string) error { - d.ResetDHTMaintenance() - return d.PublicRoomsServerDatabase.SetRoomVisibility(ctx, visible, roomID) -} - -func (d *PublicRoomsServerDatabase) CountPublicRooms(ctx context.Context) (int64, error) { - count, err := d.PublicRoomsServerDatabase.CountPublicRooms(ctx) - if err != nil { - return 0, err - } - d.foundRoomsMutex.RLock() - defer d.foundRoomsMutex.RUnlock() - return count + int64(len(d.foundRooms)), nil -} - -func (d *PublicRoomsServerDatabase) GetPublicRooms(ctx context.Context, offset int64, limit int16, filter string) ([]gomatrixserverlib.PublicRoom, error) { - realfilter := filter - if realfilter == "__local__" { - realfilter = "" - } - rooms, err := d.PublicRoomsServerDatabase.GetPublicRooms(ctx, offset, limit, realfilter) - if err != nil { - return []gomatrixserverlib.PublicRoom{}, err - } - if filter != "__local__" { - d.foundRoomsMutex.RLock() - defer d.foundRoomsMutex.RUnlock() - for _, room := range d.foundRooms { - rooms = append(rooms, room) - } - } - return rooms, nil -} - -func (d *PublicRoomsServerDatabase) UpdateRoomFromEvents(ctx context.Context, eventsToAdd []gomatrixserverlib.Event, eventsToRemove []gomatrixserverlib.Event) error { - return d.PublicRoomsServerDatabase.UpdateRoomFromEvents(ctx, eventsToAdd, eventsToRemove) -} - -func (d *PublicRoomsServerDatabase) UpdateRoomFromEvent(ctx context.Context, event gomatrixserverlib.Event) error { - return d.PublicRoomsServerDatabase.UpdateRoomFromEvent(ctx, event) -} - -func (d *PublicRoomsServerDatabase) ResetDHTMaintenance() { - if d.maintenanceTimer != nil && !d.maintenanceTimer.Stop() { - <-d.maintenanceTimer.C - } - d.Interval() -} - -func (d *PublicRoomsServerDatabase) Interval() { - if err := d.AdvertiseRoomsIntoDHT(); err != nil { - // fmt.Println("Failed to advertise room in DHT:", err) - } - if err := d.FindRoomsInDHT(); err != nil { - // fmt.Println("Failed to find rooms in DHT:", err) - } - fmt.Println("Found", d.roomsDiscovered.Load(), "room(s), advertised", d.roomsAdvertised.Load(), "room(s)") - d.maintenanceTimer = time.AfterFunc(DHTInterval, d.Interval) -} - -func (d *PublicRoomsServerDatabase) AdvertiseRoomsIntoDHT() error { - dbCtx, dbCancel := context.WithTimeout(context.Background(), 3*time.Second) - _ = dbCancel - ourRooms, err := d.GetPublicRooms(dbCtx, 0, 1024, "__local__") - if err != nil { - return err - } - if j, err := json.Marshal(ourRooms); err == nil { - d.roomsAdvertised.Store(len(ourRooms)) - d.ourRoomsContext, d.ourRoomsCancel = context.WithCancel(context.Background()) - if err := d.dht.PutValue(d.ourRoomsContext, "/matrix/publicRooms", j); err != nil { - return err - } - } - return nil -} - -func (d *PublicRoomsServerDatabase) FindRoomsInDHT() error { - d.foundRoomsMutex.Lock() - searchCtx, searchCancel := context.WithTimeout(context.Background(), 10*time.Second) - defer searchCancel() - defer d.foundRoomsMutex.Unlock() - results, err := d.dht.GetValues(searchCtx, "/matrix/publicRooms", 1024) - if err != nil { - return err - } - d.foundRooms = make(map[string]gomatrixserverlib.PublicRoom) - for _, result := range results { - var received []gomatrixserverlib.PublicRoom - if err := json.Unmarshal(result.Val, &received); err != nil { - return err - } - for _, room := range received { - d.foundRooms[room.RoomID] = room - } - } - d.roomsDiscovered.Store(len(d.foundRooms)) - return nil -} diff --git a/cmd/dendrite-demo-libp2p/storage/postgreswithpubsub/storage.go b/cmd/dendrite-demo-libp2p/storage/postgreswithpubsub/storage.go deleted file mode 100644 index cf642eb38..000000000 --- a/cmd/dendrite-demo-libp2p/storage/postgreswithpubsub/storage.go +++ /dev/null @@ -1,179 +0,0 @@ -// Copyright 2020 The Matrix.org Foundation C.I.C. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package postgreswithpubsub - -import ( - "context" - "encoding/json" - "fmt" - "sync" - "sync/atomic" - "time" - - "github.com/matrix-org/dendrite/publicroomsapi/storage/postgres" - "github.com/matrix-org/gomatrixserverlib" - - pubsub "github.com/libp2p/go-libp2p-pubsub" -) - -const MaintenanceInterval = time.Second * 10 - -type discoveredRoom struct { - time time.Time - room gomatrixserverlib.PublicRoom -} - -// PublicRoomsServerDatabase represents a public rooms server database. -type PublicRoomsServerDatabase struct { - postgres.PublicRoomsServerDatabase // - pubsub *pubsub.PubSub // - subscription *pubsub.Subscription // - foundRooms map[string]discoveredRoom // additional rooms we have learned about from the DHT - foundRoomsMutex sync.RWMutex // protects foundRooms - maintenanceTimer *time.Timer // - roomsAdvertised atomic.Value // stores int -} - -// NewPublicRoomsServerDatabase creates a new public rooms server database. -func NewPublicRoomsServerDatabase(dataSourceName string, pubsub *pubsub.PubSub, localServerName gomatrixserverlib.ServerName) (*PublicRoomsServerDatabase, error) { - pg, err := postgres.NewPublicRoomsServerDatabase(dataSourceName, nil, localServerName) - if err != nil { - return nil, err - } - provider := PublicRoomsServerDatabase{ - pubsub: pubsub, - PublicRoomsServerDatabase: *pg, - foundRooms: make(map[string]discoveredRoom), - } - if topic, err := pubsub.Join("/matrix/publicRooms"); err != nil { - return nil, err - } else if sub, err := topic.Subscribe(); err == nil { - provider.subscription = sub - go provider.MaintenanceTimer() - go provider.FindRooms() - provider.roomsAdvertised.Store(0) - return &provider, nil - } else { - return nil, err - } -} - -func (d *PublicRoomsServerDatabase) GetRoomVisibility(ctx context.Context, roomID string) (bool, error) { - return d.PublicRoomsServerDatabase.GetRoomVisibility(ctx, roomID) -} - -func (d *PublicRoomsServerDatabase) SetRoomVisibility(ctx context.Context, visible bool, roomID string) error { - d.MaintenanceTimer() - return d.PublicRoomsServerDatabase.SetRoomVisibility(ctx, visible, roomID) -} - -func (d *PublicRoomsServerDatabase) CountPublicRooms(ctx context.Context) (int64, error) { - d.foundRoomsMutex.RLock() - defer d.foundRoomsMutex.RUnlock() - return int64(len(d.foundRooms)), nil -} - -func (d *PublicRoomsServerDatabase) GetPublicRooms(ctx context.Context, offset int64, limit int16, filter string) ([]gomatrixserverlib.PublicRoom, error) { - var rooms []gomatrixserverlib.PublicRoom - if filter == "__local__" { - if r, err := d.PublicRoomsServerDatabase.GetPublicRooms(ctx, offset, limit, ""); err == nil { - rooms = append(rooms, r...) - } else { - return []gomatrixserverlib.PublicRoom{}, err - } - } else { - d.foundRoomsMutex.RLock() - defer d.foundRoomsMutex.RUnlock() - for _, room := range d.foundRooms { - rooms = append(rooms, room.room) - } - } - return rooms, nil -} - -func (d *PublicRoomsServerDatabase) UpdateRoomFromEvents(ctx context.Context, eventsToAdd []gomatrixserverlib.Event, eventsToRemove []gomatrixserverlib.Event) error { - return d.PublicRoomsServerDatabase.UpdateRoomFromEvents(ctx, eventsToAdd, eventsToRemove) -} - -func (d *PublicRoomsServerDatabase) UpdateRoomFromEvent(ctx context.Context, event gomatrixserverlib.Event) error { - return d.PublicRoomsServerDatabase.UpdateRoomFromEvent(ctx, event) -} - -func (d *PublicRoomsServerDatabase) MaintenanceTimer() { - if d.maintenanceTimer != nil && !d.maintenanceTimer.Stop() { - <-d.maintenanceTimer.C - } - d.Interval() -} - -func (d *PublicRoomsServerDatabase) Interval() { - d.foundRoomsMutex.Lock() - for k, v := range d.foundRooms { - if time.Since(v.time) > time.Minute { - delete(d.foundRooms, k) - } - } - d.foundRoomsMutex.Unlock() - if err := d.AdvertiseRooms(); err != nil { - fmt.Println("Failed to advertise room in DHT:", err) - } - d.foundRoomsMutex.RLock() - defer d.foundRoomsMutex.RUnlock() - fmt.Println("Found", len(d.foundRooms), "room(s), advertised", d.roomsAdvertised.Load(), "room(s)") - d.maintenanceTimer = time.AfterFunc(MaintenanceInterval, d.Interval) -} - -func (d *PublicRoomsServerDatabase) AdvertiseRooms() error { - dbCtx, dbCancel := context.WithTimeout(context.Background(), 3*time.Second) - _ = dbCancel - ourRooms, err := d.GetPublicRooms(dbCtx, 0, 1024, "__local__") - if err != nil { - return err - } - advertised := 0 - for _, room := range ourRooms { - if j, err := json.Marshal(room); err == nil { - if topic, err := d.pubsub.Join("/matrix/publicRooms"); err != nil { - fmt.Println("Failed to subscribe to topic:", err) - } else if err := topic.Publish(context.TODO(), j); err != nil { - fmt.Println("Failed to publish public room:", err) - } else { - advertised++ - } - } - } - - d.roomsAdvertised.Store(advertised) - return nil -} - -func (d *PublicRoomsServerDatabase) FindRooms() { - for { - msg, err := d.subscription.Next(context.Background()) - if err != nil { - continue - } - received := discoveredRoom{ - time: time.Now(), - } - if err := json.Unmarshal(msg.Data, &received.room); err != nil { - fmt.Println("Unmarshal error:", err) - continue - } - d.foundRoomsMutex.Lock() - d.foundRooms[received.room.RoomID] = received - d.foundRoomsMutex.Unlock() - } -} diff --git a/cmd/dendrite-demo-libp2p/storage/storage.go b/cmd/dendrite-demo-libp2p/storage/storage.go deleted file mode 100644 index 2d8dc1817..000000000 --- a/cmd/dendrite-demo-libp2p/storage/storage.go +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2020 The Matrix.org Foundation C.I.C. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package storage - -import ( - "net/url" - - dht "github.com/libp2p/go-libp2p-kad-dht" - pubsub "github.com/libp2p/go-libp2p-pubsub" - "github.com/matrix-org/dendrite/cmd/dendrite-demo-libp2p/storage/postgreswithdht" - "github.com/matrix-org/dendrite/cmd/dendrite-demo-libp2p/storage/postgreswithpubsub" - "github.com/matrix-org/dendrite/publicroomsapi/storage" - "github.com/matrix-org/dendrite/publicroomsapi/storage/sqlite3" - "github.com/matrix-org/gomatrixserverlib" -) - -const schemePostgres = "postgres" -const schemeFile = "file" - -// NewPublicRoomsServerDatabase opens a database connection. -func NewPublicRoomsServerDatabaseWithDHT(dataSourceName string, dht *dht.IpfsDHT, localServerName gomatrixserverlib.ServerName) (storage.Database, error) { - uri, err := url.Parse(dataSourceName) - if err != nil { - return postgreswithdht.NewPublicRoomsServerDatabase(dataSourceName, dht, localServerName) - } - switch uri.Scheme { - case schemePostgres: - return postgreswithdht.NewPublicRoomsServerDatabase(dataSourceName, dht, localServerName) - case schemeFile: - return sqlite3.NewPublicRoomsServerDatabase(dataSourceName, localServerName) - default: - return postgreswithdht.NewPublicRoomsServerDatabase(dataSourceName, dht, localServerName) - } -} - -// NewPublicRoomsServerDatabase opens a database connection. -func NewPublicRoomsServerDatabaseWithPubSub(dataSourceName string, pubsub *pubsub.PubSub, localServerName gomatrixserverlib.ServerName) (storage.Database, error) { - uri, err := url.Parse(dataSourceName) - if err != nil { - return postgreswithpubsub.NewPublicRoomsServerDatabase(dataSourceName, pubsub, localServerName) - } - switch uri.Scheme { - case schemePostgres: - return postgreswithpubsub.NewPublicRoomsServerDatabase(dataSourceName, pubsub, localServerName) - case schemeFile: - return sqlite3.NewPublicRoomsServerDatabase(dataSourceName, localServerName) - default: - return postgreswithpubsub.NewPublicRoomsServerDatabase(dataSourceName, pubsub, localServerName) - } -} diff --git a/publicroomsapi/README.md b/publicroomsapi/README.md deleted file mode 100644 index 594fe29c5..000000000 --- a/publicroomsapi/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Public rooms API - -This server is responsible for serving requests hitting `/publicRooms` and `/directory/list/room/{roomID}` as per: - -https://matrix.org/docs/spec/client_server/r0.2.0.html#listing-rooms diff --git a/publicroomsapi/consumers/roomserver.go b/publicroomsapi/consumers/roomserver.go deleted file mode 100644 index b9686d56d..000000000 --- a/publicroomsapi/consumers/roomserver.go +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright 2017 Vector Creations Ltd -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package consumers - -import ( - "context" - "encoding/json" - - "github.com/Shopify/sarama" - "github.com/matrix-org/dendrite/internal" - "github.com/matrix-org/dendrite/internal/config" - "github.com/matrix-org/dendrite/publicroomsapi/storage" - "github.com/matrix-org/dendrite/roomserver/api" - "github.com/matrix-org/gomatrixserverlib" - log "github.com/sirupsen/logrus" -) - -// OutputRoomEventConsumer consumes events that originated in the room server. -type OutputRoomEventConsumer struct { - rsAPI api.RoomserverInternalAPI - rsConsumer *internal.ContinualConsumer - db storage.Database -} - -// NewOutputRoomEventConsumer creates a new OutputRoomEventConsumer. Call Start() to begin consuming from room servers. -func NewOutputRoomEventConsumer( - cfg *config.Dendrite, - kafkaConsumer sarama.Consumer, - store storage.Database, - rsAPI api.RoomserverInternalAPI, -) *OutputRoomEventConsumer { - consumer := internal.ContinualConsumer{ - Topic: string(cfg.Kafka.Topics.OutputRoomEvent), - Consumer: kafkaConsumer, - PartitionStore: store, - } - s := &OutputRoomEventConsumer{ - rsConsumer: &consumer, - db: store, - rsAPI: rsAPI, - } - consumer.ProcessMessage = s.onMessage - - return s -} - -// Start consuming from room servers -func (s *OutputRoomEventConsumer) Start() error { - return s.rsConsumer.Start() -} - -// onMessage is called when the sync server receives a new event from the room server output log. -func (s *OutputRoomEventConsumer) onMessage(msg *sarama.ConsumerMessage) error { - // Parse out the event JSON - var output api.OutputEvent - if err := json.Unmarshal(msg.Value, &output); err != nil { - // If the message was invalid, log it and move on to the next message in the stream - log.WithError(err).Errorf("roomserver output log: message parse failure") - return nil - } - - if output.Type != api.OutputTypeNewRoomEvent { - log.WithField("type", output.Type).Debug( - "roomserver output log: ignoring unknown output type", - ) - return nil - } - - var remQueryRes api.QueryEventsByIDResponse - if len(output.NewRoomEvent.RemovesStateEventIDs) > 0 { - remQueryReq := api.QueryEventsByIDRequest{EventIDs: output.NewRoomEvent.RemovesStateEventIDs} - if err := s.rsAPI.QueryEventsByID(context.TODO(), &remQueryReq, &remQueryRes); err != nil { - log.Warn(err) - return err - } - } - - var addQueryEvents, remQueryEvents []gomatrixserverlib.Event - for _, headeredEvent := range output.NewRoomEvent.AddsState() { - addQueryEvents = append(addQueryEvents, headeredEvent.Event) - } - for _, headeredEvent := range remQueryRes.Events { - remQueryEvents = append(remQueryEvents, headeredEvent.Event) - } - - return s.db.UpdateRoomFromEvents(context.TODO(), addQueryEvents, remQueryEvents) -} diff --git a/publicroomsapi/directory/directory.go b/publicroomsapi/directory/directory.go deleted file mode 100644 index 8b68279aa..000000000 --- a/publicroomsapi/directory/directory.go +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright 2017 Vector Creations Ltd -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package directory - -import ( - "net/http" - - "github.com/matrix-org/dendrite/roomserver/api" - userapi "github.com/matrix-org/dendrite/userapi/api" - - "github.com/matrix-org/dendrite/clientapi/httputil" - "github.com/matrix-org/dendrite/clientapi/jsonerror" - "github.com/matrix-org/dendrite/publicroomsapi/storage" - "github.com/matrix-org/gomatrixserverlib" - - "github.com/matrix-org/util" -) - -type roomVisibility struct { - Visibility string `json:"visibility"` -} - -// GetVisibility implements GET /directory/list/room/{roomID} -func GetVisibility( - req *http.Request, publicRoomsDatabase storage.Database, - roomID string, -) util.JSONResponse { - isPublic, err := publicRoomsDatabase.GetRoomVisibility(req.Context(), roomID) - if err != nil { - util.GetLogger(req.Context()).WithError(err).Error("publicRoomsDatabase.GetRoomVisibility failed") - return jsonerror.InternalServerError() - } - - var v roomVisibility - if isPublic { - v.Visibility = gomatrixserverlib.Public - } else { - v.Visibility = "private" - } - - return util.JSONResponse{ - Code: http.StatusOK, - JSON: v, - } -} - -// SetVisibility implements PUT /directory/list/room/{roomID} -// TODO: Allow admin users to edit the room visibility -func SetVisibility( - req *http.Request, publicRoomsDatabase storage.Database, rsAPI api.RoomserverInternalAPI, dev *userapi.Device, - roomID string, -) util.JSONResponse { - queryMembershipReq := api.QueryMembershipForUserRequest{ - RoomID: roomID, - UserID: dev.UserID, - } - var queryMembershipRes api.QueryMembershipForUserResponse - err := rsAPI.QueryMembershipForUser(req.Context(), &queryMembershipReq, &queryMembershipRes) - if err != nil { - util.GetLogger(req.Context()).WithError(err).Error("could not query membership for user") - return jsonerror.InternalServerError() - } - // Check if user id is in room - if !queryMembershipRes.IsInRoom { - return util.JSONResponse{ - Code: http.StatusForbidden, - JSON: jsonerror.Forbidden("user does not belong to room"), - } - } - queryEventsReq := api.QueryLatestEventsAndStateRequest{ - RoomID: roomID, - StateToFetch: []gomatrixserverlib.StateKeyTuple{{ - EventType: gomatrixserverlib.MRoomPowerLevels, - StateKey: "", - }}, - } - var queryEventsRes api.QueryLatestEventsAndStateResponse - err = rsAPI.QueryLatestEventsAndState(req.Context(), &queryEventsReq, &queryEventsRes) - if err != nil || len(queryEventsRes.StateEvents) == 0 { - util.GetLogger(req.Context()).WithError(err).Error("could not query events from room") - return jsonerror.InternalServerError() - } - - // NOTSPEC: Check if the user's power is greater than power required to change m.room.aliases event - power, _ := gomatrixserverlib.NewPowerLevelContentFromEvent(queryEventsRes.StateEvents[0].Event) - if power.UserLevel(dev.UserID) < power.EventLevel(gomatrixserverlib.MRoomAliases, true) { - return util.JSONResponse{ - Code: http.StatusForbidden, - JSON: jsonerror.Forbidden("userID doesn't have power level to change visibility"), - } - } - - var v roomVisibility - if reqErr := httputil.UnmarshalJSONRequest(req, &v); reqErr != nil { - return *reqErr - } - - isPublic := v.Visibility == gomatrixserverlib.Public - if err := publicRoomsDatabase.SetRoomVisibility(req.Context(), isPublic, roomID); err != nil { - util.GetLogger(req.Context()).WithError(err).Error("publicRoomsDatabase.SetRoomVisibility failed") - return jsonerror.InternalServerError() - } - - return util.JSONResponse{ - Code: http.StatusOK, - JSON: struct{}{}, - } -} diff --git a/publicroomsapi/directory/public_rooms.go b/publicroomsapi/directory/public_rooms.go deleted file mode 100644 index df9df8ff9..000000000 --- a/publicroomsapi/directory/public_rooms.go +++ /dev/null @@ -1,262 +0,0 @@ -// Copyright 2017 Vector Creations Ltd -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package directory - -import ( - "context" - "math/rand" - "net/http" - "sort" - "strconv" - "sync" - "time" - - "github.com/matrix-org/dendrite/clientapi/httputil" - "github.com/matrix-org/dendrite/clientapi/jsonerror" - "github.com/matrix-org/dendrite/publicroomsapi/storage" - "github.com/matrix-org/dendrite/publicroomsapi/types" - "github.com/matrix-org/gomatrixserverlib" - "github.com/matrix-org/util" -) - -type PublicRoomReq struct { - Since string `json:"since,omitempty"` - Limit int16 `json:"limit,omitempty"` - Filter filter `json:"filter,omitempty"` -} - -type filter struct { - SearchTerms string `json:"generic_search_term,omitempty"` -} - -// GetPostPublicRooms implements GET and POST /publicRooms -func GetPostPublicRooms( - req *http.Request, publicRoomDatabase storage.Database, -) util.JSONResponse { - var request PublicRoomReq - if fillErr := fillPublicRoomsReq(req, &request); fillErr != nil { - return *fillErr - } - response, err := publicRooms(req.Context(), request, publicRoomDatabase) - if err != nil { - return jsonerror.InternalServerError() - } - return util.JSONResponse{ - Code: http.StatusOK, - JSON: response, - } -} - -// GetPostPublicRoomsWithExternal is the same as GetPostPublicRooms but also mixes in public rooms from the provider supplied. -func GetPostPublicRoomsWithExternal( - req *http.Request, publicRoomDatabase storage.Database, fedClient *gomatrixserverlib.FederationClient, - extRoomsProvider types.ExternalPublicRoomsProvider, -) util.JSONResponse { - var request PublicRoomReq - if fillErr := fillPublicRoomsReq(req, &request); fillErr != nil { - return *fillErr - } - response, err := publicRooms(req.Context(), request, publicRoomDatabase) - if err != nil { - return jsonerror.InternalServerError() - } - - if request.Since != "" { - // TODO: handle pagination tokens sensibly rather than ignoring them. - // ignore paginated requests since we don't handle them yet over federation. - // Only the initial request will contain federated rooms. - return util.JSONResponse{ - Code: http.StatusOK, - JSON: response, - } - } - - // If we have already hit the limit on the number of rooms, bail. - var limit int - if request.Limit > 0 { - limit = int(request.Limit) - len(response.Chunk) - if limit <= 0 { - return util.JSONResponse{ - Code: http.StatusOK, - JSON: response, - } - } - } - - // downcasting `limit` is safe as we know it isn't bigger than request.Limit which is int16 - fedRooms := bulkFetchPublicRoomsFromServers(req.Context(), fedClient, extRoomsProvider.Homeservers(), int16(limit)) - response.Chunk = append(response.Chunk, fedRooms...) - - // de-duplicate rooms with the same room ID. We can join the room via any of these aliases as we know these servers - // are alive and well, so we arbitrarily pick one (purposefully shuffling them to spread the load a bit) - var publicRooms []gomatrixserverlib.PublicRoom - haveRoomIDs := make(map[string]bool) - rand.Shuffle(len(response.Chunk), func(i, j int) { - response.Chunk[i], response.Chunk[j] = response.Chunk[j], response.Chunk[i] - }) - for _, r := range response.Chunk { - if haveRoomIDs[r.RoomID] { - continue - } - haveRoomIDs[r.RoomID] = true - publicRooms = append(publicRooms, r) - } - // sort by member count - sort.SliceStable(publicRooms, func(i, j int) bool { - return publicRooms[i].JoinedMembersCount > publicRooms[j].JoinedMembersCount - }) - - response.Chunk = publicRooms - - return util.JSONResponse{ - Code: http.StatusOK, - JSON: response, - } -} - -// bulkFetchPublicRoomsFromServers fetches public rooms from the list of homeservers. -// Returns a list of public rooms up to the limit specified. -func bulkFetchPublicRoomsFromServers( - ctx context.Context, fedClient *gomatrixserverlib.FederationClient, homeservers []string, limit int16, -) (publicRooms []gomatrixserverlib.PublicRoom) { - // follow pipeline semantics, see https://blog.golang.org/pipelines for more info. - // goroutines send rooms to this channel - roomCh := make(chan gomatrixserverlib.PublicRoom, int(limit)) - // signalling channel to tell goroutines to stop sending rooms and quit - done := make(chan bool) - // signalling to say when we can close the room channel - var wg sync.WaitGroup - wg.Add(len(homeservers)) - // concurrently query for public rooms - for _, hs := range homeservers { - go func(homeserverDomain string) { - defer wg.Done() - util.GetLogger(ctx).WithField("hs", homeserverDomain).Info("Querying HS for public rooms") - fres, err := fedClient.GetPublicRooms(ctx, gomatrixserverlib.ServerName(homeserverDomain), int(limit), "", false, "") - if err != nil { - util.GetLogger(ctx).WithError(err).WithField("hs", homeserverDomain).Warn( - "bulkFetchPublicRoomsFromServers: failed to query hs", - ) - return - } - for _, room := range fres.Chunk { - // atomically send a room or stop - select { - case roomCh <- room: - case <-done: - util.GetLogger(ctx).WithError(err).WithField("hs", homeserverDomain).Info("Interrupted whilst sending rooms") - return - } - } - }(hs) - } - - // Close the room channel when the goroutines have quit so we don't leak, but don't let it stop the in-flight request. - // This also allows the request to fail fast if all HSes experience errors as it will cause the room channel to be - // closed. - go func() { - wg.Wait() - util.GetLogger(ctx).Info("Cleaning up resources") - close(roomCh) - }() - - // fan-in results with timeout. We stop when we reach the limit. -FanIn: - for len(publicRooms) < int(limit) || limit == 0 { - // add a room or timeout - select { - case room, ok := <-roomCh: - if !ok { - util.GetLogger(ctx).Info("All homeservers have been queried, returning results.") - break FanIn - } - publicRooms = append(publicRooms, room) - case <-time.After(15 * time.Second): // we've waited long enough, let's tell the client what we got. - util.GetLogger(ctx).Info("Waited 15s for federated public rooms, returning early") - break FanIn - case <-ctx.Done(): // the client hung up on us, let's stop. - util.GetLogger(ctx).Info("Client hung up, returning early") - break FanIn - } - } - // tell goroutines to stop - close(done) - - return publicRooms -} - -func publicRooms(ctx context.Context, request PublicRoomReq, publicRoomDatabase storage.Database) (*gomatrixserverlib.RespPublicRooms, error) { - var response gomatrixserverlib.RespPublicRooms - var limit int16 - var offset int64 - limit = request.Limit - offset, err := strconv.ParseInt(request.Since, 10, 64) - // ParseInt returns 0 and an error when trying to parse an empty string - // In that case, we want to assign 0 so we ignore the error - if err != nil && len(request.Since) > 0 { - util.GetLogger(ctx).WithError(err).Error("strconv.ParseInt failed") - return nil, err - } - - est, err := publicRoomDatabase.CountPublicRooms(ctx) - if err != nil { - util.GetLogger(ctx).WithError(err).Error("publicRoomDatabase.CountPublicRooms failed") - return nil, err - } - response.TotalRoomCountEstimate = int(est) - - if offset > 0 { - response.PrevBatch = strconv.Itoa(int(offset) - 1) - } - nextIndex := int(offset) + int(limit) - if response.TotalRoomCountEstimate > nextIndex { - response.NextBatch = strconv.Itoa(nextIndex) - } - - if response.Chunk, err = publicRoomDatabase.GetPublicRooms( - ctx, offset, limit, request.Filter.SearchTerms, - ); err != nil { - util.GetLogger(ctx).WithError(err).Error("publicRoomDatabase.GetPublicRooms failed") - return nil, err - } - - return &response, nil -} - -// fillPublicRoomsReq fills the Limit, Since and Filter attributes of a GET or POST request -// on /publicRooms by parsing the incoming HTTP request -// Filter is only filled for POST requests -func fillPublicRoomsReq(httpReq *http.Request, request *PublicRoomReq) *util.JSONResponse { - if httpReq.Method == http.MethodGet { - limit, err := strconv.Atoi(httpReq.FormValue("limit")) - // Atoi returns 0 and an error when trying to parse an empty string - // In that case, we want to assign 0 so we ignore the error - if err != nil && len(httpReq.FormValue("limit")) > 0 { - util.GetLogger(httpReq.Context()).WithError(err).Error("strconv.Atoi failed") - reqErr := jsonerror.InternalServerError() - return &reqErr - } - request.Limit = int16(limit) - request.Since = httpReq.FormValue("since") - return nil - } else if httpReq.Method == http.MethodPost { - return httputil.UnmarshalJSONRequest(httpReq, request) - } - - return &util.JSONResponse{ - Code: http.StatusMethodNotAllowed, - JSON: jsonerror.NotFound("Bad method"), - } -} diff --git a/publicroomsapi/publicroomsapi.go b/publicroomsapi/publicroomsapi.go deleted file mode 100644 index b9baa1056..000000000 --- a/publicroomsapi/publicroomsapi.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2017 Vector Creations Ltd -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package publicroomsapi - -import ( - "github.com/Shopify/sarama" - "github.com/gorilla/mux" - "github.com/matrix-org/dendrite/internal/config" - "github.com/matrix-org/dendrite/publicroomsapi/consumers" - "github.com/matrix-org/dendrite/publicroomsapi/routing" - "github.com/matrix-org/dendrite/publicroomsapi/storage" - "github.com/matrix-org/dendrite/publicroomsapi/types" - roomserverAPI "github.com/matrix-org/dendrite/roomserver/api" - userapi "github.com/matrix-org/dendrite/userapi/api" - "github.com/matrix-org/gomatrixserverlib" - "github.com/sirupsen/logrus" -) - -// AddPublicRoutes sets up and registers HTTP handlers for the PublicRoomsAPI -// component. -func AddPublicRoutes( - router *mux.Router, - cfg *config.Dendrite, - consumer sarama.Consumer, - userAPI userapi.UserInternalAPI, - publicRoomsDB storage.Database, - rsAPI roomserverAPI.RoomserverInternalAPI, - fedClient *gomatrixserverlib.FederationClient, - extRoomsProvider types.ExternalPublicRoomsProvider, -) { - rsConsumer := consumers.NewOutputRoomEventConsumer( - cfg, consumer, publicRoomsDB, rsAPI, - ) - if err := rsConsumer.Start(); err != nil { - logrus.WithError(err).Panic("failed to start public rooms server consumer") - } - - routing.Setup(router, userAPI, publicRoomsDB, rsAPI, fedClient, extRoomsProvider) -} diff --git a/publicroomsapi/routing/routing.go b/publicroomsapi/routing/routing.go deleted file mode 100644 index 9c82d3508..000000000 --- a/publicroomsapi/routing/routing.go +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2017 Vector Creations Ltd -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package routing - -import ( - "net/http" - - "github.com/matrix-org/dendrite/internal/httputil" - "github.com/matrix-org/dendrite/roomserver/api" - userapi "github.com/matrix-org/dendrite/userapi/api" - - "github.com/gorilla/mux" - "github.com/matrix-org/dendrite/publicroomsapi/directory" - "github.com/matrix-org/dendrite/publicroomsapi/storage" - "github.com/matrix-org/dendrite/publicroomsapi/types" - "github.com/matrix-org/gomatrixserverlib" - "github.com/matrix-org/util" -) - -const pathPrefixR0 = "/client/r0" - -// Setup configures the given mux with publicroomsapi server listeners -// -// Due to Setup being used to call many other functions, a gocyclo nolint is -// applied: -// nolint: gocyclo -func Setup( - publicAPIMux *mux.Router, userAPI userapi.UserInternalAPI, publicRoomsDB storage.Database, rsAPI api.RoomserverInternalAPI, - fedClient *gomatrixserverlib.FederationClient, extRoomsProvider types.ExternalPublicRoomsProvider, -) { - r0mux := publicAPIMux.PathPrefix(pathPrefixR0).Subrouter() - - r0mux.Handle("/directory/list/room/{roomID}", - httputil.MakeExternalAPI("directory_list", func(req *http.Request) util.JSONResponse { - vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) - if err != nil { - return util.ErrorResponse(err) - } - return directory.GetVisibility(req, publicRoomsDB, vars["roomID"]) - }), - ).Methods(http.MethodGet, http.MethodOptions) - // TODO: Add AS support - r0mux.Handle("/directory/list/room/{roomID}", - httputil.MakeAuthAPI("directory_list", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse { - vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) - if err != nil { - return util.ErrorResponse(err) - } - return directory.SetVisibility(req, publicRoomsDB, rsAPI, device, vars["roomID"]) - }), - ).Methods(http.MethodPut, http.MethodOptions) - r0mux.Handle("/publicRooms", - httputil.MakeExternalAPI("public_rooms", func(req *http.Request) util.JSONResponse { - if extRoomsProvider != nil { - return directory.GetPostPublicRoomsWithExternal(req, publicRoomsDB, fedClient, extRoomsProvider) - } - return directory.GetPostPublicRooms(req, publicRoomsDB) - }), - ).Methods(http.MethodGet, http.MethodPost, http.MethodOptions) - - // Federation - TODO: should this live here or in federation API? It's sure easier if it's here so here it is. - publicAPIMux.Handle("/federation/v1/publicRooms", - httputil.MakeExternalAPI("federation_public_rooms", func(req *http.Request) util.JSONResponse { - return directory.GetPostPublicRooms(req, publicRoomsDB) - }), - ).Methods(http.MethodGet) -} diff --git a/publicroomsapi/storage/interface.go b/publicroomsapi/storage/interface.go deleted file mode 100644 index 0ca6f455c..000000000 --- a/publicroomsapi/storage/interface.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2020 The Matrix.org Foundation C.I.C. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package storage - -import ( - "context" - - "github.com/matrix-org/dendrite/internal" - "github.com/matrix-org/gomatrixserverlib" -) - -type Database interface { - internal.PartitionStorer - GetRoomVisibility(ctx context.Context, roomID string) (bool, error) - SetRoomVisibility(ctx context.Context, visible bool, roomID string) error - CountPublicRooms(ctx context.Context) (int64, error) - GetPublicRooms(ctx context.Context, offset int64, limit int16, filter string) ([]gomatrixserverlib.PublicRoom, error) - UpdateRoomFromEvents(ctx context.Context, eventsToAdd []gomatrixserverlib.Event, eventsToRemove []gomatrixserverlib.Event) error - UpdateRoomFromEvent(ctx context.Context, event gomatrixserverlib.Event) error -} diff --git a/publicroomsapi/storage/postgres/prepare.go b/publicroomsapi/storage/postgres/prepare.go deleted file mode 100644 index 70b6e5161..000000000 --- a/publicroomsapi/storage/postgres/prepare.go +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2017-2018 New Vector Ltd -// Copyright 2019-2020 The Matrix.org Foundation C.I.C. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package postgres - -import ( - "database/sql" -) - -// a statementList is a list of SQL statements to prepare and a pointer to where to store the resulting prepared statement. -type statementList []struct { - statement **sql.Stmt - sql string -} - -// prepare the SQL for each statement in the list and assign the result to the prepared statement. -func (s statementList) prepare(db *sql.DB) (err error) { - for _, statement := range s { - if *statement.statement, err = db.Prepare(statement.sql); err != nil { - return - } - } - return -} diff --git a/publicroomsapi/storage/postgres/public_rooms_table.go b/publicroomsapi/storage/postgres/public_rooms_table.go deleted file mode 100644 index 39e355368..000000000 --- a/publicroomsapi/storage/postgres/public_rooms_table.go +++ /dev/null @@ -1,280 +0,0 @@ -// Copyright 2017-2018 New Vector Ltd -// Copyright 2019-2020 The Matrix.org Foundation C.I.C. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package postgres - -import ( - "context" - "database/sql" - "errors" - "fmt" - - "github.com/matrix-org/dendrite/internal" - "github.com/matrix-org/gomatrixserverlib" - - "github.com/lib/pq" -) - -var editableAttributes = []string{ - "aliases", - "canonical_alias", - "name", - "topic", - "world_readable", - "guest_can_join", - "avatar_url", - "visibility", -} - -const publicRoomsSchema = ` --- Stores all of the rooms with data needed to create the server's room directory -CREATE TABLE IF NOT EXISTS publicroomsapi_public_rooms( - -- The room's ID - room_id TEXT NOT NULL PRIMARY KEY, - -- Number of joined members in the room - joined_members INTEGER NOT NULL DEFAULT 0, - -- Aliases of the room (empty array if none) - aliases TEXT[] NOT NULL DEFAULT '{}'::TEXT[], - -- Canonical alias of the room (empty string if none) - canonical_alias TEXT NOT NULL DEFAULT '', - -- Name of the room (empty string if none) - name TEXT NOT NULL DEFAULT '', - -- Topic of the room (empty string if none) - topic TEXT NOT NULL DEFAULT '', - -- Is the room world readable? - world_readable BOOLEAN NOT NULL DEFAULT false, - -- Can guest join the room? - guest_can_join BOOLEAN NOT NULL DEFAULT false, - -- URL of the room avatar (empty string if none) - avatar_url TEXT NOT NULL DEFAULT '', - -- Visibility of the room: true means the room is publicly visible, false - -- means the room is private - visibility BOOLEAN NOT NULL DEFAULT false -); -` - -const countPublicRoomsSQL = "" + - "SELECT COUNT(*) FROM publicroomsapi_public_rooms" + - " WHERE visibility = true" - -const selectPublicRoomsSQL = "" + - "SELECT room_id, joined_members, aliases, canonical_alias, name, topic, world_readable, guest_can_join, avatar_url" + - " FROM publicroomsapi_public_rooms WHERE visibility = true" + - " ORDER BY joined_members DESC" + - " OFFSET $1" - -const selectPublicRoomsWithLimitSQL = "" + - "SELECT room_id, joined_members, aliases, canonical_alias, name, topic, world_readable, guest_can_join, avatar_url" + - " FROM publicroomsapi_public_rooms WHERE visibility = true" + - " ORDER BY joined_members DESC" + - " OFFSET $1 LIMIT $2" - -const selectPublicRoomsWithFilterSQL = "" + - "SELECT room_id, joined_members, aliases, canonical_alias, name, topic, world_readable, guest_can_join, avatar_url" + - " FROM publicroomsapi_public_rooms" + - " WHERE visibility = true" + - " AND (LOWER(name) LIKE LOWER($1)" + - " OR LOWER(topic) LIKE LOWER($1)" + - " OR LOWER(ARRAY_TO_STRING(aliases, ',')) LIKE LOWER($1))" + - " ORDER BY joined_members DESC" + - " OFFSET $2" - -const selectPublicRoomsWithLimitAndFilterSQL = "" + - "SELECT room_id, joined_members, aliases, canonical_alias, name, topic, world_readable, guest_can_join, avatar_url" + - " FROM publicroomsapi_public_rooms" + - " WHERE visibility = true" + - " AND (LOWER(name) LIKE LOWER($1)" + - " OR LOWER(topic) LIKE LOWER($1)" + - " OR LOWER(ARRAY_TO_STRING(aliases, ',')) LIKE LOWER($1))" + - " ORDER BY joined_members DESC" + - " OFFSET $2 LIMIT $3" - -const selectRoomVisibilitySQL = "" + - "SELECT visibility FROM publicroomsapi_public_rooms" + - " WHERE room_id = $1" - -const insertNewRoomSQL = "" + - "INSERT INTO publicroomsapi_public_rooms(room_id)" + - " VALUES ($1)" - -const incrementJoinedMembersInRoomSQL = "" + - "UPDATE publicroomsapi_public_rooms" + - " SET joined_members = joined_members + 1" + - " WHERE room_id = $1" - -const decrementJoinedMembersInRoomSQL = "" + - "UPDATE publicroomsapi_public_rooms" + - " SET joined_members = joined_members - 1" + - " WHERE room_id = $1" - -const updateRoomAttributeSQL = "" + - "UPDATE publicroomsapi_public_rooms" + - " SET %s = $1" + - " WHERE room_id = $2" - -type publicRoomsStatements struct { - countPublicRoomsStmt *sql.Stmt - selectPublicRoomsStmt *sql.Stmt - selectPublicRoomsWithLimitStmt *sql.Stmt - selectPublicRoomsWithFilterStmt *sql.Stmt - selectPublicRoomsWithLimitAndFilterStmt *sql.Stmt - selectRoomVisibilityStmt *sql.Stmt - insertNewRoomStmt *sql.Stmt - incrementJoinedMembersInRoomStmt *sql.Stmt - decrementJoinedMembersInRoomStmt *sql.Stmt - updateRoomAttributeStmts map[string]*sql.Stmt -} - -func (s *publicRoomsStatements) prepare(db *sql.DB) (err error) { - _, err = db.Exec(publicRoomsSchema) - if err != nil { - return - } - - stmts := statementList{ - {&s.countPublicRoomsStmt, countPublicRoomsSQL}, - {&s.selectPublicRoomsStmt, selectPublicRoomsSQL}, - {&s.selectPublicRoomsWithLimitStmt, selectPublicRoomsWithLimitSQL}, - {&s.selectPublicRoomsWithFilterStmt, selectPublicRoomsWithFilterSQL}, - {&s.selectPublicRoomsWithLimitAndFilterStmt, selectPublicRoomsWithLimitAndFilterSQL}, - {&s.selectRoomVisibilityStmt, selectRoomVisibilitySQL}, - {&s.insertNewRoomStmt, insertNewRoomSQL}, - {&s.incrementJoinedMembersInRoomStmt, incrementJoinedMembersInRoomSQL}, - {&s.decrementJoinedMembersInRoomStmt, decrementJoinedMembersInRoomSQL}, - } - - if err = stmts.prepare(db); err != nil { - return - } - - s.updateRoomAttributeStmts = make(map[string]*sql.Stmt) - for _, editable := range editableAttributes { - stmt := fmt.Sprintf(updateRoomAttributeSQL, editable) - if s.updateRoomAttributeStmts[editable], err = db.Prepare(stmt); err != nil { - return - } - } - - return -} - -func (s *publicRoomsStatements) countPublicRooms(ctx context.Context) (nb int64, err error) { - err = s.countPublicRoomsStmt.QueryRowContext(ctx).Scan(&nb) - return -} - -func (s *publicRoomsStatements) selectPublicRooms( - ctx context.Context, offset int64, limit int16, filter string, -) ([]gomatrixserverlib.PublicRoom, error) { - var rows *sql.Rows - var err error - - if len(filter) > 0 { - pattern := "%" + filter + "%" - if limit == 0 { - rows, err = s.selectPublicRoomsWithFilterStmt.QueryContext( - ctx, pattern, offset, - ) - } else { - rows, err = s.selectPublicRoomsWithLimitAndFilterStmt.QueryContext( - ctx, pattern, offset, limit, - ) - } - } else { - if limit == 0 { - rows, err = s.selectPublicRoomsStmt.QueryContext(ctx, offset) - } else { - rows, err = s.selectPublicRoomsWithLimitStmt.QueryContext( - ctx, offset, limit, - ) - } - } - - if err != nil { - return []gomatrixserverlib.PublicRoom{}, nil - } - defer internal.CloseAndLogIfError(ctx, rows, "selectPublicRooms: rows.close() failed") - - rooms := []gomatrixserverlib.PublicRoom{} - for rows.Next() { - var r gomatrixserverlib.PublicRoom - var aliases pq.StringArray - - err = rows.Scan( - &r.RoomID, &r.JoinedMembersCount, &aliases, &r.CanonicalAlias, - &r.Name, &r.Topic, &r.WorldReadable, &r.GuestCanJoin, &r.AvatarURL, - ) - if err != nil { - return rooms, err - } - - r.Aliases = aliases - - rooms = append(rooms, r) - } - - return rooms, rows.Err() -} - -func (s *publicRoomsStatements) selectRoomVisibility( - ctx context.Context, roomID string, -) (v bool, err error) { - err = s.selectRoomVisibilityStmt.QueryRowContext(ctx, roomID).Scan(&v) - return -} - -func (s *publicRoomsStatements) insertNewRoom( - ctx context.Context, roomID string, -) error { - _, err := s.insertNewRoomStmt.ExecContext(ctx, roomID) - return err -} - -func (s *publicRoomsStatements) incrementJoinedMembersInRoom( - ctx context.Context, roomID string, -) error { - _, err := s.incrementJoinedMembersInRoomStmt.ExecContext(ctx, roomID) - return err -} - -func (s *publicRoomsStatements) decrementJoinedMembersInRoom( - ctx context.Context, roomID string, -) error { - _, err := s.decrementJoinedMembersInRoomStmt.ExecContext(ctx, roomID) - return err -} - -func (s *publicRoomsStatements) updateRoomAttribute( - ctx context.Context, attrName string, attrValue attributeValue, roomID string, -) error { - stmt, isEditable := s.updateRoomAttributeStmts[attrName] - - if !isEditable { - return errors.New("Cannot edit " + attrName) - } - - var value interface{} - switch v := attrValue.(type) { - case []string: - value = pq.StringArray(v) - case bool, string: - value = attrValue - default: - return errors.New("Unsupported attribute type, must be bool, string or []string") - } - - _, err := stmt.ExecContext(ctx, value, roomID) - return err -} diff --git a/publicroomsapi/storage/postgres/storage.go b/publicroomsapi/storage/postgres/storage.go deleted file mode 100644 index 36c6aec64..000000000 --- a/publicroomsapi/storage/postgres/storage.go +++ /dev/null @@ -1,259 +0,0 @@ -// Copyright 2017-2018 New Vector Ltd -// Copyright 2019-2020 The Matrix.org Foundation C.I.C. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package postgres - -import ( - "context" - "database/sql" - "encoding/json" - - "github.com/matrix-org/dendrite/internal/eventutil" - "github.com/matrix-org/dendrite/internal/sqlutil" - - "github.com/matrix-org/gomatrixserverlib" -) - -// PublicRoomsServerDatabase represents a public rooms server database. -type PublicRoomsServerDatabase struct { - db *sql.DB - sqlutil.PartitionOffsetStatements - statements publicRoomsStatements - localServerName gomatrixserverlib.ServerName -} - -type attributeValue interface{} - -// NewPublicRoomsServerDatabase creates a new public rooms server database. -func NewPublicRoomsServerDatabase(dataSourceName string, dbProperties sqlutil.DbProperties, localServerName gomatrixserverlib.ServerName) (*PublicRoomsServerDatabase, error) { - var db *sql.DB - var err error - if db, err = sqlutil.Open("postgres", dataSourceName, dbProperties); err != nil { - return nil, err - } - storage := PublicRoomsServerDatabase{ - db: db, - localServerName: localServerName, - } - if err = storage.PartitionOffsetStatements.Prepare(db, "publicroomsapi"); err != nil { - return nil, err - } - if err = storage.statements.prepare(db); err != nil { - return nil, err - } - return &storage, nil -} - -// GetRoomVisibility returns the room visibility as a boolean: true if the room -// is publicly visible, false if not. -// Returns an error if the retrieval failed. -func (d *PublicRoomsServerDatabase) GetRoomVisibility( - ctx context.Context, roomID string, -) (bool, error) { - return d.statements.selectRoomVisibility(ctx, roomID) -} - -// SetRoomVisibility updates the visibility attribute of a room. This attribute -// must be set to true if the room is publicly visible, false if not. -// Returns an error if the update failed. -func (d *PublicRoomsServerDatabase) SetRoomVisibility( - ctx context.Context, visible bool, roomID string, -) error { - return d.statements.updateRoomAttribute(ctx, "visibility", visible, roomID) -} - -// CountPublicRooms returns the number of room set as publicly visible on the server. -// Returns an error if the retrieval failed. -func (d *PublicRoomsServerDatabase) CountPublicRooms(ctx context.Context) (int64, error) { - return d.statements.countPublicRooms(ctx) -} - -// GetPublicRooms returns an array containing the local rooms set as publicly visible, ordered by their number -// of joined members. This array can be limited by a given number of elements, and offset by a given value. -// If the limit is 0, doesn't limit the number of results. If the offset is 0 too, the array contains all -// the rooms set as publicly visible on the server. -// Returns an error if the retrieval failed. -func (d *PublicRoomsServerDatabase) GetPublicRooms( - ctx context.Context, offset int64, limit int16, filter string, -) ([]gomatrixserverlib.PublicRoom, error) { - return d.statements.selectPublicRooms(ctx, offset, limit, filter) -} - -// UpdateRoomFromEvents iterate over a slice of state events and call -// UpdateRoomFromEvent on each of them to update the database representation of -// the rooms updated by each event. -// The slice of events to remove is used to update the number of joined members -// for the room in the database. -// If the update triggered by one of the events failed, aborts the process and -// returns an error. -func (d *PublicRoomsServerDatabase) UpdateRoomFromEvents( - ctx context.Context, - eventsToAdd []gomatrixserverlib.Event, - eventsToRemove []gomatrixserverlib.Event, -) error { - for _, event := range eventsToAdd { - if err := d.UpdateRoomFromEvent(ctx, event); err != nil { - return err - } - } - - for _, event := range eventsToRemove { - if event.Type() == "m.room.member" { - if err := d.updateNumJoinedUsers(ctx, event, true); err != nil { - return err - } - } - } - - return nil -} - -// UpdateRoomFromEvent updates the database representation of a room from a Matrix event, by -// checking the event's type to know which attribute to change and using the event's content -// to define the new value of the attribute. -// If the event doesn't match with any property used to compute the public room directory, -// does nothing. -// If something went wrong during the process, returns an error. -func (d *PublicRoomsServerDatabase) UpdateRoomFromEvent( - ctx context.Context, event gomatrixserverlib.Event, -) error { - // Process the event according to its type - switch event.Type() { - case "m.room.create": - return d.statements.insertNewRoom(ctx, event.RoomID()) - case "m.room.member": - return d.updateNumJoinedUsers(ctx, event, false) - case "m.room.aliases": - return d.updateRoomAliases(ctx, event) - case "m.room.canonical_alias": - var content eventutil.CanonicalAliasContent - field := &(content.Alias) - attrName := "canonical_alias" - return d.updateStringAttribute(ctx, attrName, event, &content, field) - case "m.room.name": - var content eventutil.NameContent - field := &(content.Name) - attrName := "name" - return d.updateStringAttribute(ctx, attrName, event, &content, field) - case "m.room.topic": - var content eventutil.TopicContent - field := &(content.Topic) - attrName := "topic" - return d.updateStringAttribute(ctx, attrName, event, &content, field) - case "m.room.avatar": - var content eventutil.AvatarContent - field := &(content.URL) - attrName := "avatar_url" - return d.updateStringAttribute(ctx, attrName, event, &content, field) - case "m.room.history_visibility": - var content eventutil.HistoryVisibilityContent - field := &(content.HistoryVisibility) - attrName := "world_readable" - strForTrue := "world_readable" - return d.updateBooleanAttribute(ctx, attrName, event, &content, field, strForTrue) - case "m.room.guest_access": - var content eventutil.GuestAccessContent - field := &(content.GuestAccess) - attrName := "guest_can_join" - strForTrue := "can_join" - return d.updateBooleanAttribute(ctx, attrName, event, &content, field, strForTrue) - } - - // If the event type didn't match, return with no error - return nil -} - -// updateNumJoinedUsers updates the number of joined user in the database representation -// of a room using a given "m.room.member" Matrix event. -// If the membership property of the event isn't "join", ignores it and returs nil. -// If the remove parameter is set to false, increments the joined members counter in the -// database, if set to truem decrements it. -// Returns an error if the update failed. -func (d *PublicRoomsServerDatabase) updateNumJoinedUsers( - ctx context.Context, membershipEvent gomatrixserverlib.Event, remove bool, -) error { - membership, err := membershipEvent.Membership() - if err != nil { - return err - } - - if membership != gomatrixserverlib.Join { - return nil - } - - if remove { - return d.statements.decrementJoinedMembersInRoom(ctx, membershipEvent.RoomID()) - } - return d.statements.incrementJoinedMembersInRoom(ctx, membershipEvent.RoomID()) -} - -// updateStringAttribute updates a given string attribute in the database -// representation of a room using a given string data field from content of the -// Matrix event triggering the update. -// Returns an error if decoding the Matrix event's content or updating the attribute -// failed. -func (d *PublicRoomsServerDatabase) updateStringAttribute( - ctx context.Context, attrName string, event gomatrixserverlib.Event, - content interface{}, field *string, -) error { - if err := json.Unmarshal(event.Content(), content); err != nil { - return err - } - - return d.statements.updateRoomAttribute(ctx, attrName, *field, event.RoomID()) -} - -// updateBooleanAttribute updates a given boolean attribute in the database -// representation of a room using a given string data field from content of the -// Matrix event triggering the update. -// The attribute is set to true if the field matches a given string, false if not. -// Returns an error if decoding the Matrix event's content or updating the attribute -// failed. -func (d *PublicRoomsServerDatabase) updateBooleanAttribute( - ctx context.Context, attrName string, event gomatrixserverlib.Event, - content interface{}, field *string, strForTrue string, -) error { - if err := json.Unmarshal(event.Content(), content); err != nil { - return err - } - - var attrValue bool - if *field == strForTrue { - attrValue = true - } else { - attrValue = false - } - - return d.statements.updateRoomAttribute(ctx, attrName, attrValue, event.RoomID()) -} - -// updateRoomAliases decodes the content of a "m.room.aliases" Matrix event and update the list of aliases of -// a given room with it. -// Returns an error if decoding the Matrix event or updating the list failed. -func (d *PublicRoomsServerDatabase) updateRoomAliases( - ctx context.Context, aliasesEvent gomatrixserverlib.Event, -) error { - if aliasesEvent.StateKey() == nil || *aliasesEvent.StateKey() != string(d.localServerName) { - return nil // only store our own aliases - } - var content eventutil.AliasesContent - if err := json.Unmarshal(aliasesEvent.Content(), &content); err != nil { - return err - } - - return d.statements.updateRoomAttribute( - ctx, "aliases", content.Aliases, aliasesEvent.RoomID(), - ) -} diff --git a/publicroomsapi/storage/sqlite3/prepare.go b/publicroomsapi/storage/sqlite3/prepare.go deleted file mode 100644 index 482dfa2b9..000000000 --- a/publicroomsapi/storage/sqlite3/prepare.go +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2017-2018 New Vector Ltd -// Copyright 2019-2020 The Matrix.org Foundation C.I.C. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package sqlite3 - -import ( - "database/sql" -) - -// a statementList is a list of SQL statements to prepare and a pointer to where to store the resulting prepared statement. -type statementList []struct { - statement **sql.Stmt - sql string -} - -// prepare the SQL for each statement in the list and assign the result to the prepared statement. -func (s statementList) prepare(db *sql.DB) (err error) { - for _, statement := range s { - if *statement.statement, err = db.Prepare(statement.sql); err != nil { - return - } - } - return -} diff --git a/publicroomsapi/storage/sqlite3/public_rooms_table.go b/publicroomsapi/storage/sqlite3/public_rooms_table.go deleted file mode 100644 index 7b332e175..000000000 --- a/publicroomsapi/storage/sqlite3/public_rooms_table.go +++ /dev/null @@ -1,273 +0,0 @@ -// Copyright 2017-2018 New Vector Ltd -// Copyright 2019-2020 The Matrix.org Foundation C.I.C. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package sqlite3 - -import ( - "context" - "database/sql" - "encoding/json" - "errors" - "fmt" - - "github.com/matrix-org/dendrite/internal" - "github.com/matrix-org/gomatrixserverlib" -) - -var editableAttributes = []string{ - "aliases", - "canonical_alias", - "name", - "topic", - "world_readable", - "guest_can_join", - "avatar_url", - "visibility", -} - -const publicRoomsSchema = ` --- Stores all of the rooms with data needed to create the server's room directory -CREATE TABLE IF NOT EXISTS publicroomsapi_public_rooms( - room_id TEXT NOT NULL PRIMARY KEY, - joined_members INTEGER NOT NULL DEFAULT 0, - aliases TEXT NOT NULL DEFAULT '', - canonical_alias TEXT NOT NULL DEFAULT '', - name TEXT NOT NULL DEFAULT '', - topic TEXT NOT NULL DEFAULT '', - world_readable BOOLEAN NOT NULL DEFAULT false, - guest_can_join BOOLEAN NOT NULL DEFAULT false, - avatar_url TEXT NOT NULL DEFAULT '', - visibility BOOLEAN NOT NULL DEFAULT false -); -` - -const countPublicRoomsSQL = "" + - "SELECT COUNT(*) FROM publicroomsapi_public_rooms" + - " WHERE visibility = true" - -const selectPublicRoomsSQL = "" + - "SELECT room_id, joined_members, aliases, canonical_alias, name, topic, world_readable, guest_can_join, avatar_url" + - " FROM publicroomsapi_public_rooms WHERE visibility = true" + - " ORDER BY joined_members DESC" + - " LIMIT 30 OFFSET $1" - -const selectPublicRoomsWithLimitSQL = "" + - "SELECT room_id, joined_members, aliases, canonical_alias, name, topic, world_readable, guest_can_join, avatar_url" + - " FROM publicroomsapi_public_rooms WHERE visibility = true" + - " ORDER BY joined_members DESC" + - " LIMIT $1 OFFSET $2" - -const selectPublicRoomsWithFilterSQL = "" + - "SELECT room_id, joined_members, aliases, canonical_alias, name, topic, world_readable, guest_can_join, avatar_url" + - " FROM publicroomsapi_public_rooms" + - " WHERE visibility = true" + - " AND (LOWER(name) LIKE LOWER($1)" + - " OR LOWER(topic) LIKE LOWER($1)" + - " OR LOWER(aliases) LIKE LOWER($1))" + // TODO: Is there a better way to search aliases? - " ORDER BY joined_members DESC" + - " LIMIT 30 OFFSET $2" - -const selectPublicRoomsWithLimitAndFilterSQL = "" + - "SELECT room_id, joined_members, aliases, canonical_alias, name, topic, world_readable, guest_can_join, avatar_url" + - " FROM publicroomsapi_public_rooms" + - " WHERE visibility = true" + - " AND (LOWER(name) LIKE LOWER($1)" + - " OR LOWER(topic) LIKE LOWER($1)" + - " OR LOWER(aliases) LIKE LOWER($1))" + // TODO: Is there a better way to search aliases? - " ORDER BY joined_members DESC" + - " LIMIT $3 OFFSET $2" - -const selectRoomVisibilitySQL = "" + - "SELECT visibility FROM publicroomsapi_public_rooms" + - " WHERE room_id = $1" - -const insertNewRoomSQL = "" + - "INSERT INTO publicroomsapi_public_rooms(room_id)" + - " VALUES ($1)" - -const incrementJoinedMembersInRoomSQL = "" + - "UPDATE publicroomsapi_public_rooms" + - " SET joined_members = joined_members + 1" + - " WHERE room_id = $1" - -const decrementJoinedMembersInRoomSQL = "" + - "UPDATE publicroomsapi_public_rooms" + - " SET joined_members = joined_members - 1" + - " WHERE room_id = $1" - -const updateRoomAttributeSQL = "" + - "UPDATE publicroomsapi_public_rooms" + - " SET %s = $1" + - " WHERE room_id = $2" - -type publicRoomsStatements struct { - countPublicRoomsStmt *sql.Stmt - selectPublicRoomsStmt *sql.Stmt - selectPublicRoomsWithLimitStmt *sql.Stmt - selectPublicRoomsWithFilterStmt *sql.Stmt - selectPublicRoomsWithLimitAndFilterStmt *sql.Stmt - selectRoomVisibilityStmt *sql.Stmt - insertNewRoomStmt *sql.Stmt - incrementJoinedMembersInRoomStmt *sql.Stmt - decrementJoinedMembersInRoomStmt *sql.Stmt - updateRoomAttributeStmts map[string]*sql.Stmt -} - -func (s *publicRoomsStatements) prepare(db *sql.DB) (err error) { - _, err = db.Exec(publicRoomsSchema) - if err != nil { - return - } - - stmts := statementList{ - {&s.countPublicRoomsStmt, countPublicRoomsSQL}, - {&s.selectPublicRoomsStmt, selectPublicRoomsSQL}, - {&s.selectPublicRoomsWithLimitStmt, selectPublicRoomsWithLimitSQL}, - {&s.selectPublicRoomsWithFilterStmt, selectPublicRoomsWithFilterSQL}, - {&s.selectPublicRoomsWithLimitAndFilterStmt, selectPublicRoomsWithLimitAndFilterSQL}, - {&s.selectRoomVisibilityStmt, selectRoomVisibilitySQL}, - {&s.insertNewRoomStmt, insertNewRoomSQL}, - {&s.incrementJoinedMembersInRoomStmt, incrementJoinedMembersInRoomSQL}, - {&s.decrementJoinedMembersInRoomStmt, decrementJoinedMembersInRoomSQL}, - } - - if err = stmts.prepare(db); err != nil { - return - } - - s.updateRoomAttributeStmts = make(map[string]*sql.Stmt) - for _, editable := range editableAttributes { - stmt := fmt.Sprintf(updateRoomAttributeSQL, editable) - if s.updateRoomAttributeStmts[editable], err = db.Prepare(stmt); err != nil { - return - } - } - - return -} - -func (s *publicRoomsStatements) countPublicRooms(ctx context.Context) (nb int64, err error) { - err = s.countPublicRoomsStmt.QueryRowContext(ctx).Scan(&nb) - return -} - -func (s *publicRoomsStatements) selectPublicRooms( - ctx context.Context, offset int64, limit int16, filter string, -) ([]gomatrixserverlib.PublicRoom, error) { - var rows *sql.Rows - var err error - - if len(filter) > 0 { - pattern := "%" + filter + "%" - if limit == 0 { - rows, err = s.selectPublicRoomsWithFilterStmt.QueryContext( - ctx, pattern, offset, - ) - } else { - rows, err = s.selectPublicRoomsWithLimitAndFilterStmt.QueryContext( - ctx, pattern, limit, offset, - ) - } - } else { - if limit == 0 { - rows, err = s.selectPublicRoomsStmt.QueryContext(ctx, offset) - } else { - rows, err = s.selectPublicRoomsWithLimitStmt.QueryContext( - ctx, limit, offset, - ) - } - } - - if err != nil { - return []gomatrixserverlib.PublicRoom{}, nil - } - defer internal.CloseAndLogIfError(ctx, rows, "selectPublicRooms failed to close rows") - - rooms := []gomatrixserverlib.PublicRoom{} - for rows.Next() { - var r gomatrixserverlib.PublicRoom - var aliasesJSON string - - err = rows.Scan( - &r.RoomID, &r.JoinedMembersCount, &aliasesJSON, &r.CanonicalAlias, - &r.Name, &r.Topic, &r.WorldReadable, &r.GuestCanJoin, &r.AvatarURL, - ) - if err != nil { - return rooms, err - } - - if len(aliasesJSON) > 0 { - if err := json.Unmarshal([]byte(aliasesJSON), &r.Aliases); err != nil { - return rooms, err - } - } - - rooms = append(rooms, r) - } - - return rooms, nil -} - -func (s *publicRoomsStatements) selectRoomVisibility( - ctx context.Context, roomID string, -) (v bool, err error) { - err = s.selectRoomVisibilityStmt.QueryRowContext(ctx, roomID).Scan(&v) - return -} - -func (s *publicRoomsStatements) insertNewRoom( - ctx context.Context, roomID string, -) error { - _, err := s.insertNewRoomStmt.ExecContext(ctx, roomID) - return err -} - -func (s *publicRoomsStatements) incrementJoinedMembersInRoom( - ctx context.Context, roomID string, -) error { - _, err := s.incrementJoinedMembersInRoomStmt.ExecContext(ctx, roomID) - return err -} - -func (s *publicRoomsStatements) decrementJoinedMembersInRoom( - ctx context.Context, roomID string, -) error { - _, err := s.decrementJoinedMembersInRoomStmt.ExecContext(ctx, roomID) - return err -} - -func (s *publicRoomsStatements) updateRoomAttribute( - ctx context.Context, attrName string, attrValue attributeValue, roomID string, -) error { - stmt, isEditable := s.updateRoomAttributeStmts[attrName] - - if !isEditable { - return errors.New("Cannot edit " + attrName) - } - - var value interface{} - switch v := attrValue.(type) { - case []string: - b, _ := json.Marshal(v) - value = string(b) - case bool, string: - value = attrValue - default: - return errors.New("Unsupported attribute type, must be bool, string or []string") - } - - _, err := stmt.ExecContext(ctx, value, roomID) - return err -} diff --git a/publicroomsapi/storage/sqlite3/storage.go b/publicroomsapi/storage/sqlite3/storage.go deleted file mode 100644 index 5c685d131..000000000 --- a/publicroomsapi/storage/sqlite3/storage.go +++ /dev/null @@ -1,265 +0,0 @@ -// Copyright 2017-2018 New Vector Ltd -// Copyright 2019-2020 The Matrix.org Foundation C.I.C. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package sqlite3 - -import ( - "context" - "database/sql" - "encoding/json" - - _ "github.com/mattn/go-sqlite3" - - "github.com/matrix-org/dendrite/internal/eventutil" - "github.com/matrix-org/dendrite/internal/sqlutil" - - "github.com/matrix-org/gomatrixserverlib" -) - -// PublicRoomsServerDatabase represents a public rooms server database. -type PublicRoomsServerDatabase struct { - db *sql.DB - sqlutil.PartitionOffsetStatements - statements publicRoomsStatements - localServerName gomatrixserverlib.ServerName -} - -type attributeValue interface{} - -// NewPublicRoomsServerDatabase creates a new public rooms server database. -func NewPublicRoomsServerDatabase(dataSourceName string, localServerName gomatrixserverlib.ServerName) (*PublicRoomsServerDatabase, error) { - var db *sql.DB - var err error - cs, err := sqlutil.ParseFileURI(dataSourceName) - if err != nil { - return nil, err - } - if db, err = sqlutil.Open(sqlutil.SQLiteDriverName(), cs, nil); err != nil { - return nil, err - } - storage := PublicRoomsServerDatabase{ - db: db, - localServerName: localServerName, - } - if err = storage.PartitionOffsetStatements.Prepare(db, "publicroomsapi"); err != nil { - return nil, err - } - if err = storage.statements.prepare(db); err != nil { - return nil, err - } - return &storage, nil -} - -// GetRoomVisibility returns the room visibility as a boolean: true if the room -// is publicly visible, false if not. -// Returns an error if the retrieval failed. -func (d *PublicRoomsServerDatabase) GetRoomVisibility( - ctx context.Context, roomID string, -) (bool, error) { - return d.statements.selectRoomVisibility(ctx, roomID) -} - -// SetRoomVisibility updates the visibility attribute of a room. This attribute -// must be set to true if the room is publicly visible, false if not. -// Returns an error if the update failed. -func (d *PublicRoomsServerDatabase) SetRoomVisibility( - ctx context.Context, visible bool, roomID string, -) error { - return d.statements.updateRoomAttribute(ctx, "visibility", visible, roomID) -} - -// CountPublicRooms returns the number of room set as publicly visible on the server. -// Returns an error if the retrieval failed. -func (d *PublicRoomsServerDatabase) CountPublicRooms(ctx context.Context) (int64, error) { - return d.statements.countPublicRooms(ctx) -} - -// GetPublicRooms returns an array containing the local rooms set as publicly visible, ordered by their number -// of joined members. This array can be limited by a given number of elements, and offset by a given value. -// If the limit is 0, doesn't limit the number of results. If the offset is 0 too, the array contains all -// the rooms set as publicly visible on the server. -// Returns an error if the retrieval failed. -func (d *PublicRoomsServerDatabase) GetPublicRooms( - ctx context.Context, offset int64, limit int16, filter string, -) ([]gomatrixserverlib.PublicRoom, error) { - return d.statements.selectPublicRooms(ctx, offset, limit, filter) -} - -// UpdateRoomFromEvents iterate over a slice of state events and call -// UpdateRoomFromEvent on each of them to update the database representation of -// the rooms updated by each event. -// The slice of events to remove is used to update the number of joined members -// for the room in the database. -// If the update triggered by one of the events failed, aborts the process and -// returns an error. -func (d *PublicRoomsServerDatabase) UpdateRoomFromEvents( - ctx context.Context, - eventsToAdd []gomatrixserverlib.Event, - eventsToRemove []gomatrixserverlib.Event, -) error { - for _, event := range eventsToAdd { - if err := d.UpdateRoomFromEvent(ctx, event); err != nil { - return err - } - } - - for _, event := range eventsToRemove { - if event.Type() == "m.room.member" { - if err := d.updateNumJoinedUsers(ctx, event, true); err != nil { - return err - } - } - } - - return nil -} - -// UpdateRoomFromEvent updates the database representation of a room from a Matrix event, by -// checking the event's type to know which attribute to change and using the event's content -// to define the new value of the attribute. -// If the event doesn't match with any property used to compute the public room directory, -// does nothing. -// If something went wrong during the process, returns an error. -func (d *PublicRoomsServerDatabase) UpdateRoomFromEvent( - ctx context.Context, event gomatrixserverlib.Event, -) error { - // Process the event according to its type - switch event.Type() { - case "m.room.create": - return d.statements.insertNewRoom(ctx, event.RoomID()) - case "m.room.member": - return d.updateNumJoinedUsers(ctx, event, false) - case "m.room.aliases": - return d.updateRoomAliases(ctx, event) - case "m.room.canonical_alias": - var content eventutil.CanonicalAliasContent - field := &(content.Alias) - attrName := "canonical_alias" - return d.updateStringAttribute(ctx, attrName, event, &content, field) - case "m.room.name": - var content eventutil.NameContent - field := &(content.Name) - attrName := "name" - return d.updateStringAttribute(ctx, attrName, event, &content, field) - case "m.room.topic": - var content eventutil.TopicContent - field := &(content.Topic) - attrName := "topic" - return d.updateStringAttribute(ctx, attrName, event, &content, field) - case "m.room.avatar": - var content eventutil.AvatarContent - field := &(content.URL) - attrName := "avatar_url" - return d.updateStringAttribute(ctx, attrName, event, &content, field) - case "m.room.history_visibility": - var content eventutil.HistoryVisibilityContent - field := &(content.HistoryVisibility) - attrName := "world_readable" - strForTrue := "world_readable" - return d.updateBooleanAttribute(ctx, attrName, event, &content, field, strForTrue) - case "m.room.guest_access": - var content eventutil.GuestAccessContent - field := &(content.GuestAccess) - attrName := "guest_can_join" - strForTrue := "can_join" - return d.updateBooleanAttribute(ctx, attrName, event, &content, field, strForTrue) - } - - // If the event type didn't match, return with no error - return nil -} - -// updateNumJoinedUsers updates the number of joined user in the database representation -// of a room using a given "m.room.member" Matrix event. -// If the membership property of the event isn't "join", ignores it and returs nil. -// If the remove parameter is set to false, increments the joined members counter in the -// database, if set to truem decrements it. -// Returns an error if the update failed. -func (d *PublicRoomsServerDatabase) updateNumJoinedUsers( - ctx context.Context, membershipEvent gomatrixserverlib.Event, remove bool, -) error { - membership, err := membershipEvent.Membership() - if err != nil { - return err - } - - if membership != gomatrixserverlib.Join { - return nil - } - - if remove { - return d.statements.decrementJoinedMembersInRoom(ctx, membershipEvent.RoomID()) - } - return d.statements.incrementJoinedMembersInRoom(ctx, membershipEvent.RoomID()) -} - -// updateStringAttribute updates a given string attribute in the database -// representation of a room using a given string data field from content of the -// Matrix event triggering the update. -// Returns an error if decoding the Matrix event's content or updating the attribute -// failed. -func (d *PublicRoomsServerDatabase) updateStringAttribute( - ctx context.Context, attrName string, event gomatrixserverlib.Event, - content interface{}, field *string, -) error { - if err := json.Unmarshal(event.Content(), content); err != nil { - return err - } - - return d.statements.updateRoomAttribute(ctx, attrName, *field, event.RoomID()) -} - -// updateBooleanAttribute updates a given boolean attribute in the database -// representation of a room using a given string data field from content of the -// Matrix event triggering the update. -// The attribute is set to true if the field matches a given string, false if not. -// Returns an error if decoding the Matrix event's content or updating the attribute -// failed. -func (d *PublicRoomsServerDatabase) updateBooleanAttribute( - ctx context.Context, attrName string, event gomatrixserverlib.Event, - content interface{}, field *string, strForTrue string, -) error { - if err := json.Unmarshal(event.Content(), content); err != nil { - return err - } - - var attrValue bool - if *field == strForTrue { - attrValue = true - } else { - attrValue = false - } - - return d.statements.updateRoomAttribute(ctx, attrName, attrValue, event.RoomID()) -} - -// updateRoomAliases decodes the content of a "m.room.aliases" Matrix event and update the list of aliases of -// a given room with it. -// Returns an error if decoding the Matrix event or updating the list failed. -func (d *PublicRoomsServerDatabase) updateRoomAliases( - ctx context.Context, aliasesEvent gomatrixserverlib.Event, -) error { - if aliasesEvent.StateKey() == nil || *aliasesEvent.StateKey() != string(d.localServerName) { - return nil // only store our own aliases - } - var content eventutil.AliasesContent - if err := json.Unmarshal(aliasesEvent.Content(), &content); err != nil { - return err - } - - return d.statements.updateRoomAttribute( - ctx, "aliases", content.Aliases, aliasesEvent.RoomID(), - ) -} diff --git a/publicroomsapi/storage/storage.go b/publicroomsapi/storage/storage.go deleted file mode 100644 index f66188040..000000000 --- a/publicroomsapi/storage/storage.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2020 The Matrix.org Foundation C.I.C. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build !wasm - -package storage - -import ( - "net/url" - - "github.com/matrix-org/dendrite/internal/sqlutil" - "github.com/matrix-org/dendrite/publicroomsapi/storage/postgres" - "github.com/matrix-org/dendrite/publicroomsapi/storage/sqlite3" - "github.com/matrix-org/gomatrixserverlib" -) - -const schemePostgres = "postgres" -const schemeFile = "file" - -// NewPublicRoomsServerDatabase opens a database connection. -func NewPublicRoomsServerDatabase(dataSourceName string, dbProperties sqlutil.DbProperties, localServerName gomatrixserverlib.ServerName) (Database, error) { - uri, err := url.Parse(dataSourceName) - if err != nil { - return postgres.NewPublicRoomsServerDatabase(dataSourceName, dbProperties, localServerName) - } - switch uri.Scheme { - case schemePostgres: - return postgres.NewPublicRoomsServerDatabase(dataSourceName, dbProperties, localServerName) - case schemeFile: - return sqlite3.NewPublicRoomsServerDatabase(dataSourceName, localServerName) - default: - return postgres.NewPublicRoomsServerDatabase(dataSourceName, dbProperties, localServerName) - } -} diff --git a/publicroomsapi/storage/storage_wasm.go b/publicroomsapi/storage/storage_wasm.go deleted file mode 100644 index 70ceeaf85..000000000 --- a/publicroomsapi/storage/storage_wasm.go +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2020 The Matrix.org Foundation C.I.C. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package storage - -import ( - "fmt" - "net/url" - - "github.com/matrix-org/dendrite/publicroomsapi/storage/sqlite3" - "github.com/matrix-org/gomatrixserverlib" -) - -// NewPublicRoomsServerDatabase opens a database connection. -func NewPublicRoomsServerDatabase(dataSourceName string, localServerName gomatrixserverlib.ServerName) (Database, error) { - uri, err := url.Parse(dataSourceName) - if err != nil { - return nil, err - } - switch uri.Scheme { - case "postgres": - return nil, fmt.Errorf("Cannot use postgres implementation") - case "file": - return sqlite3.NewPublicRoomsServerDatabase(dataSourceName, localServerName) - default: - return nil, fmt.Errorf("Cannot use postgres implementation") - } -} diff --git a/publicroomsapi/types/types.go b/publicroomsapi/types/types.go deleted file mode 100644 index 11cb0d204..000000000 --- a/publicroomsapi/types/types.go +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2017 Vector Creations Ltd -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package types - -// ExternalPublicRoomsProvider provides a list of homeservers who should be queried -// periodically for a list of public rooms on their server. -type ExternalPublicRoomsProvider interface { - // The list of homeserver domains to query. These servers will receive a request - // via this API: https://matrix.org/docs/spec/server_server/latest#public-room-directory - // This will be called -on demand- by clients, so cache appropriately! - Homeservers() []string -}