mirror of
https://github.com/matrix-org/dendrite.git
synced 2025-12-16 11:23:11 -06:00
Merge branch 'master' into event-redaction
This commit is contained in:
commit
8610c97dec
|
|
@ -20,6 +20,39 @@ should pick up any unit test and run it). There are also [scripts](scripts) for
|
||||||
[linting](scripts/find-lint.sh) and doing a [build/test/lint
|
[linting](scripts/find-lint.sh) and doing a [build/test/lint
|
||||||
run](scripts/build-test-lint.sh).
|
run](scripts/build-test-lint.sh).
|
||||||
|
|
||||||
|
## Continuous Integration
|
||||||
|
|
||||||
|
When a Pull Request is submitted, continuous integration jobs are run
|
||||||
|
automatically to ensure the code builds and is relatively well-written. Checks
|
||||||
|
are run on [Buildkite](https://buildkite.com/matrix-dot-org/dendrite/) and
|
||||||
|
[CircleCI](https://circleci.com/gh/matrix-org/dendrite/).
|
||||||
|
|
||||||
|
If a job fails, click the "details" button and you should be taken to the job's
|
||||||
|
logs.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Scroll down to the failing step and you should see some log output. Scan
|
||||||
|
the logs until you find what it's complaining about, fix it, submit a new
|
||||||
|
commit, then rinse and repeat until CI passes.
|
||||||
|
|
||||||
|
### Running CI Tests Locally
|
||||||
|
|
||||||
|
To save waiting for CI to finish after every commit, it is ideal to run the
|
||||||
|
checks locally before pushing, fixing errors first. This also saves other
|
||||||
|
people time as only so many PRs can be tested at a given time.
|
||||||
|
|
||||||
|
To execute what Buildkite tests, simply run `./scripts/build-test-lint.sh`.
|
||||||
|
This script will build the code, lint it, and run `go test ./...` with race
|
||||||
|
condition checking enabled. If something needs to be changed, fix it and then
|
||||||
|
run the script again until it no longer complains. Be warned that the linting
|
||||||
|
can take a significant amount of CPU and RAM.
|
||||||
|
|
||||||
|
CircleCI simply runs [Sytest](https://github.com/matrix-org/sytest) with a test
|
||||||
|
whitelist. See
|
||||||
|
[docs/sytest.md](https://github.com/matrix-org/dendrite/blob/master/docs/sytest.md#using-a-sytest-docker-image)
|
||||||
|
for instructions on setting it up to run locally.
|
||||||
|
|
||||||
|
|
||||||
## Picking Things To Do
|
## Picking Things To Do
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
# Dendrite [](https://buildkite.com/matrix-dot-org/dendrite) [](https://circleci.com/gh/matrix-org/dendrite) [](https://matrix.to/#/#dendrite-dev:matrix.org) [](https://matrix.to/#/#dendrite:matrix.org)
|
# Dendrite [](https://buildkite.com/matrix-dot-org/dendrite) [](https://circleci.com/gh/matrix-org/dendrite) [](https://matrix.to/#/#dendrite-dev:matrix.org) [](https://matrix.to/#/#dendrite:matrix.org)
|
||||||
|
|
||||||
Dendrite will be a matrix homeserver written in go.
|
Dendrite will be a matrix homeserver written in go.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,13 +20,13 @@ package api
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"errors"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
|
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
|
||||||
"github.com/matrix-org/dendrite/clientapi/auth/storage/accounts"
|
"github.com/matrix-org/dendrite/clientapi/auth/storage/accounts"
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
|
|
||||||
|
"github.com/matrix-org/dendrite/common"
|
||||||
commonHTTP "github.com/matrix-org/dendrite/common/http"
|
commonHTTP "github.com/matrix-org/dendrite/common/http"
|
||||||
opentracing "github.com/opentracing/opentracing-go"
|
opentracing "github.com/opentracing/opentracing-go"
|
||||||
)
|
)
|
||||||
|
|
@ -164,7 +164,7 @@ func RetrieveUserProfile(
|
||||||
|
|
||||||
// If no user exists, return
|
// If no user exists, return
|
||||||
if !userResp.UserIDExists {
|
if !userResp.UserIDExists {
|
||||||
return nil, errors.New("no known profile for given user ID")
|
return nil, common.ErrProfileNoExists
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to query the user from the local database again
|
// Try to query the user from the local database again
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
|
|
||||||
package authtypes
|
package authtypes
|
||||||
|
|
||||||
// Profile represents the profile for a Matrix account on this home server.
|
// Profile represents the profile for a Matrix account.
|
||||||
type Profile struct {
|
type Profile struct {
|
||||||
Localpart string
|
Localpart string
|
||||||
DisplayName string
|
DisplayName string
|
||||||
|
|
|
||||||
|
|
@ -230,7 +230,7 @@ func (d *Database) newMembership(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only "join" membership events can be considered as new memberships
|
// Only "join" membership events can be considered as new memberships
|
||||||
if membership == "join" {
|
if membership == gomatrixserverlib.Join {
|
||||||
if err := d.saveMembership(ctx, txn, localpart, roomID, eventID); err != nil {
|
if err := d.saveMembership(ctx, txn, localpart, roomID, eventID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -55,10 +55,6 @@ const (
|
||||||
presetPublicChat = "public_chat"
|
presetPublicChat = "public_chat"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
joinRulePublic = "public"
|
|
||||||
joinRuleInvite = "invite"
|
|
||||||
)
|
|
||||||
const (
|
const (
|
||||||
historyVisibilityShared = "shared"
|
historyVisibilityShared = "shared"
|
||||||
// TODO: These should be implemented once history visibility is implemented
|
// TODO: These should be implemented once history visibility is implemented
|
||||||
|
|
@ -201,7 +197,7 @@ func createRoom(
|
||||||
}
|
}
|
||||||
|
|
||||||
membershipContent := common.MemberContent{
|
membershipContent := common.MemberContent{
|
||||||
Membership: "join",
|
Membership: gomatrixserverlib.Join,
|
||||||
DisplayName: profile.DisplayName,
|
DisplayName: profile.DisplayName,
|
||||||
AvatarURL: profile.AvatarURL,
|
AvatarURL: profile.AvatarURL,
|
||||||
}
|
}
|
||||||
|
|
@ -209,19 +205,19 @@ func createRoom(
|
||||||
var joinRules, historyVisibility string
|
var joinRules, historyVisibility string
|
||||||
switch r.Preset {
|
switch r.Preset {
|
||||||
case presetPrivateChat:
|
case presetPrivateChat:
|
||||||
joinRules = joinRuleInvite
|
joinRules = gomatrixserverlib.Invite
|
||||||
historyVisibility = historyVisibilityShared
|
historyVisibility = historyVisibilityShared
|
||||||
case presetTrustedPrivateChat:
|
case presetTrustedPrivateChat:
|
||||||
joinRules = joinRuleInvite
|
joinRules = gomatrixserverlib.Invite
|
||||||
historyVisibility = historyVisibilityShared
|
historyVisibility = historyVisibilityShared
|
||||||
// TODO If trusted_private_chat, all invitees are given the same power level as the room creator.
|
// TODO If trusted_private_chat, all invitees are given the same power level as the room creator.
|
||||||
case presetPublicChat:
|
case presetPublicChat:
|
||||||
joinRules = joinRulePublic
|
joinRules = gomatrixserverlib.Public
|
||||||
historyVisibility = historyVisibilityShared
|
historyVisibility = historyVisibilityShared
|
||||||
default:
|
default:
|
||||||
// Default room rules, r.Preset was previously checked for valid values so
|
// Default room rules, r.Preset was previously checked for valid values so
|
||||||
// only a request with no preset should end up here.
|
// only a request with no preset should end up here.
|
||||||
joinRules = joinRuleInvite
|
joinRules = gomatrixserverlib.Invite
|
||||||
historyVisibility = historyVisibilityShared
|
historyVisibility = historyVisibilityShared
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -164,13 +164,36 @@ func SetLocalAlias(
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveLocalAlias implements DELETE /directory/room/{roomAlias}
|
// RemoveLocalAlias implements DELETE /directory/room/{roomAlias}
|
||||||
// TODO: Check if the user has the power level to remove an alias
|
|
||||||
func RemoveLocalAlias(
|
func RemoveLocalAlias(
|
||||||
req *http.Request,
|
req *http.Request,
|
||||||
device *authtypes.Device,
|
device *authtypes.Device,
|
||||||
alias string,
|
alias string,
|
||||||
aliasAPI roomserverAPI.RoomserverAliasAPI,
|
aliasAPI roomserverAPI.RoomserverAliasAPI,
|
||||||
) util.JSONResponse {
|
) util.JSONResponse {
|
||||||
|
|
||||||
|
creatorQueryReq := roomserverAPI.GetCreatorIDForAliasRequest{
|
||||||
|
Alias: alias,
|
||||||
|
}
|
||||||
|
var creatorQueryRes roomserverAPI.GetCreatorIDForAliasResponse
|
||||||
|
if err := aliasAPI.GetCreatorIDForAlias(req.Context(), &creatorQueryReq, &creatorQueryRes); err != nil {
|
||||||
|
return httputil.LogThenError(req, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if creatorQueryRes.UserID == "" {
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusNotFound,
|
||||||
|
JSON: jsonerror.NotFound("Alias does not exist"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if creatorQueryRes.UserID != device.UserID {
|
||||||
|
// TODO: Still allow deletion if user is admin
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusForbidden,
|
||||||
|
JSON: jsonerror.Forbidden("You do not have permission to delete this alias"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
queryReq := roomserverAPI.RemoveRoomAliasRequest{
|
queryReq := roomserverAPI.RemoveRoomAliasRequest{
|
||||||
Alias: alias,
|
Alias: alias,
|
||||||
UserID: device.UserID,
|
UserID: device.UserID,
|
||||||
|
|
|
||||||
127
clientapi/routing/getevent.go
Normal file
127
clientapi/routing/getevent.go
Normal file
|
|
@ -0,0 +1,127 @@
|
||||||
|
// Copyright 2019 Alex Chen
|
||||||
|
//
|
||||||
|
// 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/clientapi/auth/authtypes"
|
||||||
|
"github.com/matrix-org/dendrite/clientapi/httputil"
|
||||||
|
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
||||||
|
"github.com/matrix-org/dendrite/common/config"
|
||||||
|
"github.com/matrix-org/dendrite/roomserver/api"
|
||||||
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
|
"github.com/matrix-org/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
type getEventRequest struct {
|
||||||
|
req *http.Request
|
||||||
|
device *authtypes.Device
|
||||||
|
roomID string
|
||||||
|
eventID string
|
||||||
|
cfg config.Dendrite
|
||||||
|
federation *gomatrixserverlib.FederationClient
|
||||||
|
keyRing gomatrixserverlib.KeyRing
|
||||||
|
requestedEvent gomatrixserverlib.Event
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetEvent implements GET /_matrix/client/r0/rooms/{roomId}/event/{eventId}
|
||||||
|
// https://matrix.org/docs/spec/client_server/r0.4.0.html#get-matrix-client-r0-rooms-roomid-event-eventid
|
||||||
|
func GetEvent(
|
||||||
|
req *http.Request,
|
||||||
|
device *authtypes.Device,
|
||||||
|
roomID string,
|
||||||
|
eventID string,
|
||||||
|
cfg config.Dendrite,
|
||||||
|
queryAPI api.RoomserverQueryAPI,
|
||||||
|
federation *gomatrixserverlib.FederationClient,
|
||||||
|
keyRing gomatrixserverlib.KeyRing,
|
||||||
|
) util.JSONResponse {
|
||||||
|
eventsReq := api.QueryEventsByIDRequest{
|
||||||
|
EventIDs: []string{eventID},
|
||||||
|
}
|
||||||
|
var eventsResp api.QueryEventsByIDResponse
|
||||||
|
err := queryAPI.QueryEventsByID(req.Context(), &eventsReq, &eventsResp)
|
||||||
|
if err != nil {
|
||||||
|
return httputil.LogThenError(req, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(eventsResp.Events) == 0 {
|
||||||
|
// Event not found locally
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusNotFound,
|
||||||
|
JSON: jsonerror.NotFound("The event was not found or you do not have permission to read this event"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
requestedEvent := eventsResp.Events[0]
|
||||||
|
|
||||||
|
r := getEventRequest{
|
||||||
|
req: req,
|
||||||
|
device: device,
|
||||||
|
roomID: roomID,
|
||||||
|
eventID: eventID,
|
||||||
|
cfg: cfg,
|
||||||
|
federation: federation,
|
||||||
|
keyRing: keyRing,
|
||||||
|
requestedEvent: requestedEvent,
|
||||||
|
}
|
||||||
|
|
||||||
|
stateReq := api.QueryStateAfterEventsRequest{
|
||||||
|
RoomID: r.requestedEvent.RoomID(),
|
||||||
|
PrevEventIDs: r.requestedEvent.PrevEventIDs(),
|
||||||
|
StateToFetch: []gomatrixserverlib.StateKeyTuple{{
|
||||||
|
EventType: gomatrixserverlib.MRoomMember,
|
||||||
|
StateKey: device.UserID,
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
var stateResp api.QueryStateAfterEventsResponse
|
||||||
|
if err := queryAPI.QueryStateAfterEvents(req.Context(), &stateReq, &stateResp); err != nil {
|
||||||
|
return httputil.LogThenError(req, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !stateResp.RoomExists {
|
||||||
|
util.GetLogger(req.Context()).Errorf("Expected to find room for event %s but failed", r.requestedEvent.EventID())
|
||||||
|
return jsonerror.InternalServerError()
|
||||||
|
}
|
||||||
|
|
||||||
|
if !stateResp.PrevEventsExist {
|
||||||
|
// Missing some events locally; stateResp.StateEvents unavailable.
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusNotFound,
|
||||||
|
JSON: jsonerror.NotFound("The event was not found or you do not have permission to read this event"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, stateEvent := range stateResp.StateEvents {
|
||||||
|
if stateEvent.StateKeyEquals(r.device.UserID) {
|
||||||
|
membership, err := stateEvent.Membership()
|
||||||
|
if err != nil {
|
||||||
|
return httputil.LogThenError(req, err)
|
||||||
|
}
|
||||||
|
if membership == gomatrixserverlib.Join {
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusOK,
|
||||||
|
JSON: gomatrixserverlib.ToClientEvent(r.requestedEvent, gomatrixserverlib.FormatAll),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusNotFound,
|
||||||
|
JSON: jsonerror.NotFound("The event was not found or you do not have permission to read this event"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -70,7 +70,7 @@ func JoinRoomByIDOrAlias(
|
||||||
return httputil.LogThenError(req, err)
|
return httputil.LogThenError(req, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
content["membership"] = "join"
|
content["membership"] = gomatrixserverlib.Join
|
||||||
content["displayname"] = profile.DisplayName
|
content["displayname"] = profile.DisplayName
|
||||||
content["avatar_url"] = profile.AvatarURL
|
content["avatar_url"] = profile.AvatarURL
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,7 @@ func SendMembership(
|
||||||
var returnData interface{} = struct{}{}
|
var returnData interface{} = struct{}{}
|
||||||
|
|
||||||
// The join membership requires the room id to be sent in the response
|
// The join membership requires the room id to be sent in the response
|
||||||
if membership == "join" {
|
if membership == gomatrixserverlib.Join {
|
||||||
returnData = struct {
|
returnData = struct {
|
||||||
RoomID string `json:"room_id"`
|
RoomID string `json:"room_id"`
|
||||||
}{roomID}
|
}{roomID}
|
||||||
|
|
@ -141,7 +141,7 @@ func buildMembershipEvent(
|
||||||
|
|
||||||
// "unban" or "kick" isn't a valid membership value, change it to "leave"
|
// "unban" or "kick" isn't a valid membership value, change it to "leave"
|
||||||
if membership == "unban" || membership == "kick" {
|
if membership == "unban" || membership == "kick" {
|
||||||
membership = "leave"
|
membership = gomatrixserverlib.Leave
|
||||||
}
|
}
|
||||||
|
|
||||||
content := common.MemberContent{
|
content := common.MemberContent{
|
||||||
|
|
@ -192,7 +192,7 @@ func loadProfile(
|
||||||
func getMembershipStateKey(
|
func getMembershipStateKey(
|
||||||
body threepid.MembershipRequest, device *authtypes.Device, membership string,
|
body threepid.MembershipRequest, device *authtypes.Device, membership string,
|
||||||
) (stateKey string, reason string, err error) {
|
) (stateKey string, reason string, err error) {
|
||||||
if membership == "ban" || membership == "unban" || membership == "kick" || membership == "invite" {
|
if membership == gomatrixserverlib.Ban || membership == "unban" || membership == "kick" || membership == gomatrixserverlib.Invite {
|
||||||
// If we're in this case, the state key is contained in the request body,
|
// If we're in this case, the state key is contained in the request body,
|
||||||
// possibly along with a reason (for "kick" and "ban") so we need to parse
|
// possibly along with a reason (for "kick" and "ban") so we need to parse
|
||||||
// it
|
// it
|
||||||
|
|
|
||||||
|
|
@ -30,43 +30,61 @@ import (
|
||||||
"github.com/matrix-org/dendrite/roomserver/api"
|
"github.com/matrix-org/dendrite/roomserver/api"
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
|
|
||||||
|
"github.com/matrix-org/gomatrix"
|
||||||
"github.com/matrix-org/util"
|
"github.com/matrix-org/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetProfile implements GET /profile/{userID}
|
// GetProfile implements GET /profile/{userID}
|
||||||
func GetProfile(
|
func GetProfile(
|
||||||
req *http.Request, accountDB *accounts.Database, userID string, asAPI appserviceAPI.AppServiceQueryAPI,
|
req *http.Request, accountDB *accounts.Database, cfg *config.Dendrite,
|
||||||
|
userID string,
|
||||||
|
asAPI appserviceAPI.AppServiceQueryAPI,
|
||||||
|
federation *gomatrixserverlib.FederationClient,
|
||||||
) util.JSONResponse {
|
) util.JSONResponse {
|
||||||
profile, err := appserviceAPI.RetrieveUserProfile(req.Context(), userID, asAPI, accountDB)
|
profile, err := getProfile(req.Context(), accountDB, cfg, userID, asAPI, federation)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if err == common.ErrProfileNoExists {
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusNotFound,
|
||||||
|
JSON: jsonerror.NotFound("The user does not exist or does not have a profile"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return httputil.LogThenError(req, err)
|
return httputil.LogThenError(req, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
res := common.ProfileResponse{
|
|
||||||
AvatarURL: profile.AvatarURL,
|
|
||||||
DisplayName: profile.DisplayName,
|
|
||||||
}
|
|
||||||
return util.JSONResponse{
|
return util.JSONResponse{
|
||||||
Code: http.StatusOK,
|
Code: http.StatusOK,
|
||||||
JSON: res,
|
JSON: common.ProfileResponse{
|
||||||
|
AvatarURL: profile.AvatarURL,
|
||||||
|
DisplayName: profile.DisplayName,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAvatarURL implements GET /profile/{userID}/avatar_url
|
// GetAvatarURL implements GET /profile/{userID}/avatar_url
|
||||||
func GetAvatarURL(
|
func GetAvatarURL(
|
||||||
req *http.Request, accountDB *accounts.Database, userID string, asAPI appserviceAPI.AppServiceQueryAPI,
|
req *http.Request, accountDB *accounts.Database, cfg *config.Dendrite,
|
||||||
|
userID string, asAPI appserviceAPI.AppServiceQueryAPI,
|
||||||
|
federation *gomatrixserverlib.FederationClient,
|
||||||
) util.JSONResponse {
|
) util.JSONResponse {
|
||||||
profile, err := appserviceAPI.RetrieveUserProfile(req.Context(), userID, asAPI, accountDB)
|
profile, err := getProfile(req.Context(), accountDB, cfg, userID, asAPI, federation)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if err == common.ErrProfileNoExists {
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusNotFound,
|
||||||
|
JSON: jsonerror.NotFound("The user does not exist or does not have a profile"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return httputil.LogThenError(req, err)
|
return httputil.LogThenError(req, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
res := common.AvatarURL{
|
|
||||||
AvatarURL: profile.AvatarURL,
|
|
||||||
}
|
|
||||||
return util.JSONResponse{
|
return util.JSONResponse{
|
||||||
Code: http.StatusOK,
|
Code: http.StatusOK,
|
||||||
JSON: res,
|
JSON: common.AvatarURL{
|
||||||
|
AvatarURL: profile.AvatarURL,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -152,18 +170,27 @@ func SetAvatarURL(
|
||||||
|
|
||||||
// GetDisplayName implements GET /profile/{userID}/displayname
|
// GetDisplayName implements GET /profile/{userID}/displayname
|
||||||
func GetDisplayName(
|
func GetDisplayName(
|
||||||
req *http.Request, accountDB *accounts.Database, userID string, asAPI appserviceAPI.AppServiceQueryAPI,
|
req *http.Request, accountDB *accounts.Database, cfg *config.Dendrite,
|
||||||
|
userID string, asAPI appserviceAPI.AppServiceQueryAPI,
|
||||||
|
federation *gomatrixserverlib.FederationClient,
|
||||||
) util.JSONResponse {
|
) util.JSONResponse {
|
||||||
profile, err := appserviceAPI.RetrieveUserProfile(req.Context(), userID, asAPI, accountDB)
|
profile, err := getProfile(req.Context(), accountDB, cfg, userID, asAPI, federation)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if err == common.ErrProfileNoExists {
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusNotFound,
|
||||||
|
JSON: jsonerror.NotFound("The user does not exist or does not have a profile"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return httputil.LogThenError(req, err)
|
return httputil.LogThenError(req, err)
|
||||||
}
|
}
|
||||||
res := common.DisplayName{
|
|
||||||
DisplayName: profile.DisplayName,
|
|
||||||
}
|
|
||||||
return util.JSONResponse{
|
return util.JSONResponse{
|
||||||
Code: http.StatusOK,
|
Code: http.StatusOK,
|
||||||
JSON: res,
|
JSON: common.DisplayName{
|
||||||
|
DisplayName: profile.DisplayName,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -247,6 +274,48 @@ func SetDisplayName(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getProfile gets the full profile of a user by querying the database or a
|
||||||
|
// remote homeserver.
|
||||||
|
// Returns an error when something goes wrong or specifically
|
||||||
|
// common.ErrProfileNoExists when the profile doesn't exist.
|
||||||
|
func getProfile(
|
||||||
|
ctx context.Context, accountDB *accounts.Database, cfg *config.Dendrite,
|
||||||
|
userID string,
|
||||||
|
asAPI appserviceAPI.AppServiceQueryAPI,
|
||||||
|
federation *gomatrixserverlib.FederationClient,
|
||||||
|
) (*authtypes.Profile, error) {
|
||||||
|
localpart, domain, err := gomatrixserverlib.SplitID('@', userID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if domain != cfg.Matrix.ServerName {
|
||||||
|
profile, fedErr := federation.LookupProfile(ctx, domain, userID, "")
|
||||||
|
if fedErr != nil {
|
||||||
|
if x, ok := fedErr.(gomatrix.HTTPError); ok {
|
||||||
|
if x.Code == http.StatusNotFound {
|
||||||
|
return nil, common.ErrProfileNoExists
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fedErr
|
||||||
|
}
|
||||||
|
|
||||||
|
return &authtypes.Profile{
|
||||||
|
Localpart: localpart,
|
||||||
|
DisplayName: profile.DisplayName,
|
||||||
|
AvatarURL: profile.AvatarURL,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
profile, err := appserviceAPI.RetrieveUserProfile(ctx, userID, asAPI, accountDB)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return profile, nil
|
||||||
|
}
|
||||||
|
|
||||||
func buildMembershipEvents(
|
func buildMembershipEvents(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
memberships []authtypes.Membership,
|
memberships []authtypes.Membership,
|
||||||
|
|
@ -264,7 +333,7 @@ func buildMembershipEvents(
|
||||||
}
|
}
|
||||||
|
|
||||||
content := common.MemberContent{
|
content := common.MemberContent{
|
||||||
Membership: "join",
|
Membership: gomatrixserverlib.Join,
|
||||||
}
|
}
|
||||||
|
|
||||||
content.DisplayName = newProfile.DisplayName
|
content.DisplayName = newProfile.DisplayName
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,7 @@ func Setup(
|
||||||
}),
|
}),
|
||||||
).Methods(http.MethodPost, http.MethodOptions)
|
).Methods(http.MethodPost, http.MethodOptions)
|
||||||
r0mux.Handle("/join/{roomIDOrAlias}",
|
r0mux.Handle("/join/{roomIDOrAlias}",
|
||||||
common.MakeAuthAPI("join", authData, func(req *http.Request, device *authtypes.Device) util.JSONResponse {
|
common.MakeAuthAPI(gomatrixserverlib.Join, authData, func(req *http.Request, device *authtypes.Device) util.JSONResponse {
|
||||||
vars, err := common.URLDecodeMapValues(mux.Vars(req))
|
vars, err := common.URLDecodeMapValues(mux.Vars(req))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return util.ErrorResponse(err)
|
return util.ErrorResponse(err)
|
||||||
|
|
@ -132,6 +132,15 @@ func Setup(
|
||||||
nil, cfg, queryAPI, producer, transactionsCache)
|
nil, cfg, queryAPI, producer, transactionsCache)
|
||||||
}),
|
}),
|
||||||
).Methods(http.MethodPut, http.MethodOptions)
|
).Methods(http.MethodPut, http.MethodOptions)
|
||||||
|
r0mux.Handle("/rooms/{roomID}/event/{eventID}",
|
||||||
|
common.MakeAuthAPI("rooms_get_event", authData, func(req *http.Request, device *authtypes.Device) util.JSONResponse {
|
||||||
|
vars, err := common.URLDecodeMapValues(mux.Vars(req))
|
||||||
|
if err != nil {
|
||||||
|
return util.ErrorResponse(err)
|
||||||
|
}
|
||||||
|
return GetEvent(req, device, vars["roomID"], vars["eventID"], cfg, queryAPI, federation, keyRing)
|
||||||
|
}),
|
||||||
|
).Methods(http.MethodGet, http.MethodOptions)
|
||||||
r0mux.Handle("/rooms/{roomID}/state/{eventType:[^/]+/?}",
|
r0mux.Handle("/rooms/{roomID}/state/{eventType:[^/]+/?}",
|
||||||
common.MakeAuthAPI("send_message", authData, func(req *http.Request, device *authtypes.Device) util.JSONResponse {
|
common.MakeAuthAPI("send_message", authData, func(req *http.Request, device *authtypes.Device) util.JSONResponse {
|
||||||
vars, err := common.URLDecodeMapValues(mux.Vars(req))
|
vars, err := common.URLDecodeMapValues(mux.Vars(req))
|
||||||
|
|
@ -305,7 +314,7 @@ func Setup(
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return util.ErrorResponse(err)
|
return util.ErrorResponse(err)
|
||||||
}
|
}
|
||||||
return GetProfile(req, accountDB, vars["userID"], asAPI)
|
return GetProfile(req, accountDB, &cfg, vars["userID"], asAPI, federation)
|
||||||
}),
|
}),
|
||||||
).Methods(http.MethodGet, http.MethodOptions)
|
).Methods(http.MethodGet, http.MethodOptions)
|
||||||
|
|
||||||
|
|
@ -315,7 +324,7 @@ func Setup(
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return util.ErrorResponse(err)
|
return util.ErrorResponse(err)
|
||||||
}
|
}
|
||||||
return GetAvatarURL(req, accountDB, vars["userID"], asAPI)
|
return GetAvatarURL(req, accountDB, &cfg, vars["userID"], asAPI, federation)
|
||||||
}),
|
}),
|
||||||
).Methods(http.MethodGet, http.MethodOptions)
|
).Methods(http.MethodGet, http.MethodOptions)
|
||||||
|
|
||||||
|
|
@ -337,7 +346,7 @@ func Setup(
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return util.ErrorResponse(err)
|
return util.ErrorResponse(err)
|
||||||
}
|
}
|
||||||
return GetDisplayName(req, accountDB, vars["userID"], asAPI)
|
return GetDisplayName(req, accountDB, &cfg, vars["userID"], asAPI, federation)
|
||||||
}),
|
}),
|
||||||
).Methods(http.MethodGet, http.MethodOptions)
|
).Methods(http.MethodGet, http.MethodOptions)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ func SendEvent(
|
||||||
) util.JSONResponse {
|
) util.JSONResponse {
|
||||||
if txnID != nil {
|
if txnID != nil {
|
||||||
// Try to fetch response from transactionsCache
|
// Try to fetch response from transactionsCache
|
||||||
if res, ok := txnCache.FetchTransaction(*txnID); ok {
|
if res, ok := txnCache.FetchTransaction(device.AccessToken, *txnID); ok {
|
||||||
return *res
|
return *res
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -83,7 +83,7 @@ func SendEvent(
|
||||||
}
|
}
|
||||||
// Add response to transactionsCache
|
// Add response to transactionsCache
|
||||||
if txnID != nil {
|
if txnID != nil {
|
||||||
txnCache.AddTransaction(*txnID, &res)
|
txnCache.AddTransaction(device.AccessToken, *txnID, &res)
|
||||||
}
|
}
|
||||||
|
|
||||||
return res
|
return res
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,7 @@ func CheckAndProcessInvite(
|
||||||
producer *producers.RoomserverProducer, membership string, roomID string,
|
producer *producers.RoomserverProducer, membership string, roomID string,
|
||||||
evTime time.Time,
|
evTime time.Time,
|
||||||
) (inviteStoredOnIDServer bool, err error) {
|
) (inviteStoredOnIDServer bool, err error) {
|
||||||
if membership != "invite" || (body.Address == "" && body.IDServer == "" && body.Medium == "") {
|
if membership != gomatrixserverlib.Invite || (body.Address == "" && body.IDServer == "" && body.Medium == "") {
|
||||||
// If none of the 3PID-specific fields are supplied, it's a standard invite
|
// If none of the 3PID-specific fields are supplied, it's a standard invite
|
||||||
// so return nil for it to be processed as such
|
// so return nil for it to be processed as such
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,7 @@ func main() {
|
||||||
// Build a m.room.member event.
|
// Build a m.room.member event.
|
||||||
b.Type = "m.room.member"
|
b.Type = "m.room.member"
|
||||||
b.StateKey = userID
|
b.StateKey = userID
|
||||||
b.SetContent(map[string]string{"membership": "join"}) // nolint: errcheck
|
b.SetContent(map[string]string{"membership": gomatrixserverlib.Join}) // nolint: errcheck
|
||||||
b.AuthEvents = []gomatrixserverlib.EventReference{create}
|
b.AuthEvents = []gomatrixserverlib.EventReference{create}
|
||||||
member := buildAndOutput()
|
member := buildAndOutput()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,14 @@ import (
|
||||||
// DefaultCleanupPeriod represents the default time duration after which cacheCleanService runs.
|
// DefaultCleanupPeriod represents the default time duration after which cacheCleanService runs.
|
||||||
const DefaultCleanupPeriod time.Duration = 30 * time.Minute
|
const DefaultCleanupPeriod time.Duration = 30 * time.Minute
|
||||||
|
|
||||||
type txnsMap map[string]*util.JSONResponse
|
type txnsMap map[CacheKey]*util.JSONResponse
|
||||||
|
|
||||||
|
// CacheKey is the type for the key in a transactions cache.
|
||||||
|
// This is needed because the spec requires transaction IDs to have a per-access token scope.
|
||||||
|
type CacheKey struct {
|
||||||
|
AccessToken string
|
||||||
|
TxnID string
|
||||||
|
}
|
||||||
|
|
||||||
// Cache represents a temporary store for response entries.
|
// Cache represents a temporary store for response entries.
|
||||||
// Entries are evicted after a certain period, defined by cleanupPeriod.
|
// Entries are evicted after a certain period, defined by cleanupPeriod.
|
||||||
|
|
@ -50,14 +57,14 @@ func NewWithCleanupPeriod(cleanupPeriod time.Duration) *Cache {
|
||||||
return &t
|
return &t
|
||||||
}
|
}
|
||||||
|
|
||||||
// FetchTransaction looks up an entry for txnID in Cache.
|
// FetchTransaction looks up an entry for the (accessToken, txnID) tuple in Cache.
|
||||||
// Looks in both the txnMaps.
|
// Looks in both the txnMaps.
|
||||||
// Returns (JSON response, true) if txnID is found, else the returned bool is false.
|
// Returns (JSON response, true) if txnID is found, else the returned bool is false.
|
||||||
func (t *Cache) FetchTransaction(txnID string) (*util.JSONResponse, bool) {
|
func (t *Cache) FetchTransaction(accessToken, txnID string) (*util.JSONResponse, bool) {
|
||||||
t.RLock()
|
t.RLock()
|
||||||
defer t.RUnlock()
|
defer t.RUnlock()
|
||||||
for _, txns := range t.txnsMaps {
|
for _, txns := range t.txnsMaps {
|
||||||
res, ok := txns[txnID]
|
res, ok := txns[CacheKey{accessToken, txnID}]
|
||||||
if ok {
|
if ok {
|
||||||
return res, true
|
return res, true
|
||||||
}
|
}
|
||||||
|
|
@ -65,13 +72,13 @@ func (t *Cache) FetchTransaction(txnID string) (*util.JSONResponse, bool) {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddTransaction adds an entry for txnID in Cache for later access.
|
// AddTransaction adds an entry for the (accessToken, txnID) tuple in Cache.
|
||||||
// Adds to the front txnMap.
|
// Adds to the front txnMap.
|
||||||
func (t *Cache) AddTransaction(txnID string, res *util.JSONResponse) {
|
func (t *Cache) AddTransaction(accessToken, txnID string, res *util.JSONResponse) {
|
||||||
t.Lock()
|
t.Lock()
|
||||||
defer t.Unlock()
|
defer t.Unlock()
|
||||||
|
|
||||||
t.txnsMaps[0][txnID] = res
|
t.txnsMaps[0][CacheKey{accessToken, txnID}] = res
|
||||||
}
|
}
|
||||||
|
|
||||||
// cacheCleanService is responsible for cleaning up entries after cleanupPeriod.
|
// cacheCleanService is responsible for cleaning up entries after cleanupPeriod.
|
||||||
|
|
|
||||||
|
|
@ -24,27 +24,54 @@ type fakeType struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
fakeAccessToken = "aRandomAccessToken"
|
||||||
|
fakeAccessToken2 = "anotherRandomAccessToken"
|
||||||
fakeTxnID = "aRandomTxnID"
|
fakeTxnID = "aRandomTxnID"
|
||||||
fakeResponse = &util.JSONResponse{Code: http.StatusOK, JSON: fakeType{ID: "0"}}
|
fakeResponse = &util.JSONResponse{
|
||||||
|
Code: http.StatusOK, JSON: fakeType{ID: "0"},
|
||||||
|
}
|
||||||
|
fakeResponse2 = &util.JSONResponse{
|
||||||
|
Code: http.StatusOK, JSON: fakeType{ID: "1"},
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestCache creates a New Cache and tests AddTransaction & FetchTransaction
|
// TestCache creates a New Cache and tests AddTransaction & FetchTransaction
|
||||||
func TestCache(t *testing.T) {
|
func TestCache(t *testing.T) {
|
||||||
fakeTxnCache := New()
|
fakeTxnCache := New()
|
||||||
fakeTxnCache.AddTransaction(fakeTxnID, fakeResponse)
|
fakeTxnCache.AddTransaction(fakeAccessToken, fakeTxnID, fakeResponse)
|
||||||
|
|
||||||
// Add entries for noise.
|
// Add entries for noise.
|
||||||
for i := 1; i <= 100; i++ {
|
for i := 1; i <= 100; i++ {
|
||||||
fakeTxnCache.AddTransaction(
|
fakeTxnCache.AddTransaction(
|
||||||
|
fakeAccessToken,
|
||||||
fakeTxnID+string(i),
|
fakeTxnID+string(i),
|
||||||
&util.JSONResponse{Code: http.StatusOK, JSON: fakeType{ID: string(i)}},
|
&util.JSONResponse{Code: http.StatusOK, JSON: fakeType{ID: string(i)}},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
testResponse, ok := fakeTxnCache.FetchTransaction(fakeTxnID)
|
testResponse, ok := fakeTxnCache.FetchTransaction(fakeAccessToken, fakeTxnID)
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Error("Failed to retrieve entry for txnID: ", fakeTxnID)
|
t.Error("Failed to retrieve entry for txnID: ", fakeTxnID)
|
||||||
} else if testResponse.JSON != fakeResponse.JSON {
|
} else if testResponse.JSON != fakeResponse.JSON {
|
||||||
t.Error("Fetched response incorrect. Expected: ", fakeResponse.JSON, " got: ", testResponse.JSON)
|
t.Error("Fetched response incorrect. Expected: ", fakeResponse.JSON, " got: ", testResponse.JSON)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestCacheScope ensures transactions with the same transaction ID are not shared
|
||||||
|
// across multiple access tokens.
|
||||||
|
func TestCacheScope(t *testing.T) {
|
||||||
|
cache := New()
|
||||||
|
cache.AddTransaction(fakeAccessToken, fakeTxnID, fakeResponse)
|
||||||
|
cache.AddTransaction(fakeAccessToken2, fakeTxnID, fakeResponse2)
|
||||||
|
|
||||||
|
if res, ok := cache.FetchTransaction(fakeAccessToken, fakeTxnID); !ok {
|
||||||
|
t.Errorf("failed to retrieve entry for (%s, %s)", fakeAccessToken, fakeTxnID)
|
||||||
|
} else if res.JSON != fakeResponse.JSON {
|
||||||
|
t.Errorf("Wrong cache entry for (%s, %s). Expected: %v; got: %v", fakeAccessToken, fakeTxnID, fakeResponse.JSON, res.JSON)
|
||||||
|
}
|
||||||
|
if res, ok := cache.FetchTransaction(fakeAccessToken2, fakeTxnID); !ok {
|
||||||
|
t.Errorf("failed to retrieve entry for (%s, %s)", fakeAccessToken, fakeTxnID)
|
||||||
|
} else if res.JSON != fakeResponse2.JSON {
|
||||||
|
t.Errorf("Wrong cache entry for (%s, %s). Expected: %v; got: %v", fakeAccessToken, fakeTxnID, fakeResponse2.JSON, res.JSON)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,14 @@
|
||||||
package common
|
package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ErrProfileNoExists is returned when trying to lookup a user's profile that
|
||||||
|
// doesn't exist locally.
|
||||||
|
var ErrProfileNoExists = errors.New("no known profile for given user ID")
|
||||||
|
|
||||||
// AccountData represents account data sent from the client API server to the
|
// AccountData represents account data sent from the client API server to the
|
||||||
// sync API server
|
// sync API server
|
||||||
type AccountData struct {
|
type AccountData struct {
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ docker-compose up kafka zookeeper postgres
|
||||||
and the following dendrite components
|
and the following dendrite components
|
||||||
|
|
||||||
```
|
```
|
||||||
docker-compose up client_api media_api sync_api room_server public_rooms_api
|
docker-compose up client_api media_api sync_api room_server public_rooms_api typing_server
|
||||||
docker-compose up client_api_proxy
|
docker-compose up client_api_proxy
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -114,6 +114,7 @@ listen:
|
||||||
media_api: "media_api:7774"
|
media_api: "media_api:7774"
|
||||||
public_rooms_api: "public_rooms_api:7775"
|
public_rooms_api: "public_rooms_api:7775"
|
||||||
federation_sender: "federation_sender:7776"
|
federation_sender: "federation_sender:7776"
|
||||||
|
typing_server: "typing_server:7777"
|
||||||
|
|
||||||
# The configuration for tracing the dendrite components.
|
# The configuration for tracing the dendrite components.
|
||||||
tracing:
|
tracing:
|
||||||
|
|
|
||||||
|
|
@ -95,6 +95,16 @@ services:
|
||||||
networks:
|
networks:
|
||||||
- internal
|
- internal
|
||||||
|
|
||||||
|
typing_server:
|
||||||
|
container_name: dendrite_typing_server
|
||||||
|
hostname: typing_server
|
||||||
|
entrypoint: ["bash", "./docker/services/typing-server.sh"]
|
||||||
|
build: ./
|
||||||
|
volumes:
|
||||||
|
- ..:/build
|
||||||
|
networks:
|
||||||
|
- internal
|
||||||
|
|
||||||
federation_api_proxy:
|
federation_api_proxy:
|
||||||
container_name: dendrite_federation_api_proxy
|
container_name: dendrite_federation_api_proxy
|
||||||
hostname: federation_api_proxy
|
hostname: federation_api_proxy
|
||||||
|
|
|
||||||
5
docker/services/typing-server.sh
Normal file
5
docker/services/typing-server.sh
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
bash ./docker/build.sh
|
||||||
|
|
||||||
|
./bin/dendrite-typing-server --config=dendrite.yaml
|
||||||
BIN
docs/images/details-button-location.jpg
Normal file
BIN
docs/images/details-button-location.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 25 KiB |
|
|
@ -58,7 +58,7 @@ func MakeJoin(
|
||||||
Type: "m.room.member",
|
Type: "m.room.member",
|
||||||
StateKey: &userID,
|
StateKey: &userID,
|
||||||
}
|
}
|
||||||
err = builder.SetContent(map[string]interface{}{"membership": "join"})
|
err = builder.SetContent(map[string]interface{}{"membership": gomatrixserverlib.Join})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return httputil.LogThenError(httpReq, err)
|
return httputil.LogThenError(httpReq, err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ func MakeLeave(
|
||||||
Type: "m.room.member",
|
Type: "m.room.member",
|
||||||
StateKey: &userID,
|
StateKey: &userID,
|
||||||
}
|
}
|
||||||
err = builder.SetContent(map[string]interface{}{"membership": "leave"})
|
err = builder.SetContent(map[string]interface{}{"membership": gomatrixserverlib.Leave})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return httputil.LogThenError(httpReq, err)
|
return httputil.LogThenError(httpReq, err)
|
||||||
}
|
}
|
||||||
|
|
@ -153,7 +153,7 @@ func SendLeave(
|
||||||
mem, err := event.Membership()
|
mem, err := event.Membership()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return httputil.LogThenError(httpReq, err)
|
return httputil.LogThenError(httpReq, err)
|
||||||
} else if mem != "leave" {
|
} else if mem != gomatrixserverlib.Leave {
|
||||||
return util.JSONResponse{
|
return util.JSONResponse{
|
||||||
Code: http.StatusBadRequest,
|
Code: http.StatusBadRequest,
|
||||||
JSON: jsonerror.BadJSON("The membership in the event content must be set to leave"),
|
JSON: jsonerror.BadJSON("The membership in the event content must be set to leave"),
|
||||||
|
|
|
||||||
|
|
@ -202,7 +202,7 @@ func createInviteFrom3PIDInvite(
|
||||||
content := common.MemberContent{
|
content := common.MemberContent{
|
||||||
AvatarURL: profile.AvatarURL,
|
AvatarURL: profile.AvatarURL,
|
||||||
DisplayName: profile.DisplayName,
|
DisplayName: profile.DisplayName,
|
||||||
Membership: "invite",
|
Membership: gomatrixserverlib.Invite,
|
||||||
ThirdPartyInvite: &common.TPInvite{
|
ThirdPartyInvite: &common.TPInvite{
|
||||||
Signed: inv.Signed,
|
Signed: inv.Signed,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -233,7 +233,7 @@ func joinedHostsFromEvents(evs []gomatrixserverlib.Event) ([]types.JoinedHost, e
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if membership != "join" {
|
if membership != gomatrixserverlib.Join {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
_, serverName, err := gomatrixserverlib.SplitID('@', *ev.StateKey())
|
_, serverName, err := gomatrixserverlib.SplitID('@', *ev.StateKey())
|
||||||
|
|
|
||||||
2
go.mod
2
go.mod
|
|
@ -24,7 +24,7 @@ require (
|
||||||
github.com/lib/pq v0.0.0-20170918175043-23da1db4f16d
|
github.com/lib/pq v0.0.0-20170918175043-23da1db4f16d
|
||||||
github.com/matrix-org/dugong v0.0.0-20171220115018-ea0a4690a0d5
|
github.com/matrix-org/dugong v0.0.0-20171220115018-ea0a4690a0d5
|
||||||
github.com/matrix-org/gomatrix v0.0.0-20190528120928-7df988a63f26
|
github.com/matrix-org/gomatrix v0.0.0-20190528120928-7df988a63f26
|
||||||
github.com/matrix-org/gomatrixserverlib v0.0.0-20190727114627-340519e0d9e3
|
github.com/matrix-org/gomatrixserverlib v0.0.0-20190805173246-3a2199d5ecd6
|
||||||
github.com/matrix-org/naffka v0.0.0-20171115094957-662bfd0841d0
|
github.com/matrix-org/naffka v0.0.0-20171115094957-662bfd0841d0
|
||||||
github.com/matrix-org/util v0.0.0-20171127121716-2e2df66af2f5
|
github.com/matrix-org/util v0.0.0-20171127121716-2e2df66af2f5
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1
|
github.com/matttproud/golang_protobuf_extensions v1.0.1
|
||||||
|
|
|
||||||
4
go.sum
4
go.sum
|
|
@ -56,8 +56,8 @@ github.com/matrix-org/gomatrixserverlib v0.0.0-20190619132215-178ed5e3b8e2 h1:pY
|
||||||
github.com/matrix-org/gomatrixserverlib v0.0.0-20190619132215-178ed5e3b8e2/go.mod h1:sf0RcKOdiwJeTti7A313xsaejNUGYDq02MQZ4JD4w/E=
|
github.com/matrix-org/gomatrixserverlib v0.0.0-20190619132215-178ed5e3b8e2/go.mod h1:sf0RcKOdiwJeTti7A313xsaejNUGYDq02MQZ4JD4w/E=
|
||||||
github.com/matrix-org/gomatrixserverlib v0.0.0-20190724145009-a6df10ef35d6 h1:B8n1H5Wb1B5jwLzTylBpY0kJCMRqrofT7PmOw4aJFJA=
|
github.com/matrix-org/gomatrixserverlib v0.0.0-20190724145009-a6df10ef35d6 h1:B8n1H5Wb1B5jwLzTylBpY0kJCMRqrofT7PmOw4aJFJA=
|
||||||
github.com/matrix-org/gomatrixserverlib v0.0.0-20190724145009-a6df10ef35d6/go.mod h1:sf0RcKOdiwJeTti7A313xsaejNUGYDq02MQZ4JD4w/E=
|
github.com/matrix-org/gomatrixserverlib v0.0.0-20190724145009-a6df10ef35d6/go.mod h1:sf0RcKOdiwJeTti7A313xsaejNUGYDq02MQZ4JD4w/E=
|
||||||
github.com/matrix-org/gomatrixserverlib v0.0.0-20190727114627-340519e0d9e3 h1:KFsMDKAyyyA04qJFNrhOpnIGfjPZRu6797ylL2cG4zs=
|
github.com/matrix-org/gomatrixserverlib v0.0.0-20190805173246-3a2199d5ecd6 h1:xr69Hk6QM3RIN6JSvx3RpDowBGpHpDDqhqXCeySwYow=
|
||||||
github.com/matrix-org/gomatrixserverlib v0.0.0-20190727114627-340519e0d9e3/go.mod h1:sf0RcKOdiwJeTti7A313xsaejNUGYDq02MQZ4JD4w/E=
|
github.com/matrix-org/gomatrixserverlib v0.0.0-20190805173246-3a2199d5ecd6/go.mod h1:sf0RcKOdiwJeTti7A313xsaejNUGYDq02MQZ4JD4w/E=
|
||||||
github.com/matrix-org/naffka v0.0.0-20171115094957-662bfd0841d0 h1:p7WTwG+aXM86+yVrYAiCMW3ZHSmotVvuRbjtt3jC+4A=
|
github.com/matrix-org/naffka v0.0.0-20171115094957-662bfd0841d0 h1:p7WTwG+aXM86+yVrYAiCMW3ZHSmotVvuRbjtt3jC+4A=
|
||||||
github.com/matrix-org/naffka v0.0.0-20171115094957-662bfd0841d0/go.mod h1:cXoYQIENbdWIQHt1SyCo6Bl3C3raHwJ0wgVrXHSqf+A=
|
github.com/matrix-org/naffka v0.0.0-20171115094957-662bfd0841d0/go.mod h1:cXoYQIENbdWIQHt1SyCo6Bl3C3raHwJ0wgVrXHSqf+A=
|
||||||
github.com/matrix-org/util v0.0.0-20171013132526-8b1c8ab81986 h1:TiWl4hLvezAhRPM8tPcPDFTysZ7k4T/1J4GPp/iqlZo=
|
github.com/matrix-org/util v0.0.0-20171013132526-8b1c8ab81986 h1:TiWl4hLvezAhRPM8tPcPDFTysZ7k4T/1J4GPp/iqlZo=
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ import (
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/clientapi/httputil"
|
"github.com/matrix-org/dendrite/clientapi/httputil"
|
||||||
"github.com/matrix-org/dendrite/publicroomsapi/storage"
|
"github.com/matrix-org/dendrite/publicroomsapi/storage"
|
||||||
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
|
|
||||||
"github.com/matrix-org/util"
|
"github.com/matrix-org/util"
|
||||||
)
|
)
|
||||||
|
|
@ -39,7 +40,7 @@ func GetVisibility(
|
||||||
|
|
||||||
var v roomVisibility
|
var v roomVisibility
|
||||||
if isPublic {
|
if isPublic {
|
||||||
v.Visibility = "public"
|
v.Visibility = gomatrixserverlib.Public
|
||||||
} else {
|
} else {
|
||||||
v.Visibility = "private"
|
v.Visibility = "private"
|
||||||
}
|
}
|
||||||
|
|
@ -61,7 +62,7 @@ func SetVisibility(
|
||||||
return *reqErr
|
return *reqErr
|
||||||
}
|
}
|
||||||
|
|
||||||
isPublic := v.Visibility == "public"
|
isPublic := v.Visibility == gomatrixserverlib.Public
|
||||||
if err := publicRoomsDatabase.SetRoomVisibility(req.Context(), isPublic, roomID); err != nil {
|
if err := publicRoomsDatabase.SetRoomVisibility(req.Context(), isPublic, roomID); err != nil {
|
||||||
return httputil.LogThenError(req, err)
|
return httputil.LogThenError(req, err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -185,7 +185,7 @@ func (d *PublicRoomsServerDatabase) updateNumJoinedUsers(
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if membership != "join" {
|
if membership != gomatrixserverlib.Join {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,13 +33,16 @@ import (
|
||||||
type RoomserverAliasAPIDatabase interface {
|
type RoomserverAliasAPIDatabase interface {
|
||||||
// Save a given room alias with the room ID it refers to.
|
// Save a given room alias with the room ID it refers to.
|
||||||
// Returns an error if there was a problem talking to the database.
|
// Returns an error if there was a problem talking to the database.
|
||||||
SetRoomAlias(ctx context.Context, alias string, roomID string) error
|
SetRoomAlias(ctx context.Context, alias string, roomID string, creatorUserID string) error
|
||||||
// Look up the room ID a given alias refers to.
|
// Look up the room ID a given alias refers to.
|
||||||
// Returns an error if there was a problem talking to the database.
|
// Returns an error if there was a problem talking to the database.
|
||||||
GetRoomIDForAlias(ctx context.Context, alias string) (string, error)
|
GetRoomIDForAlias(ctx context.Context, alias string) (string, error)
|
||||||
// Look up all aliases referring to a given room ID.
|
// Look up all aliases referring to a given room ID.
|
||||||
// Returns an error if there was a problem talking to the database.
|
// Returns an error if there was a problem talking to the database.
|
||||||
GetAliasesForRoomID(ctx context.Context, roomID string) ([]string, error)
|
GetAliasesForRoomID(ctx context.Context, roomID string) ([]string, error)
|
||||||
|
// Get the user ID of the creator of an alias.
|
||||||
|
// Returns an error if there was a problem talking to the database.
|
||||||
|
GetCreatorIDForAlias(ctx context.Context, alias string) (string, error)
|
||||||
// Remove a given room alias.
|
// Remove a given room alias.
|
||||||
// Returns an error if there was a problem talking to the database.
|
// Returns an error if there was a problem talking to the database.
|
||||||
RemoveRoomAlias(ctx context.Context, alias string) error
|
RemoveRoomAlias(ctx context.Context, alias string) error
|
||||||
|
|
@ -73,7 +76,7 @@ func (r *RoomserverAliasAPI) SetRoomAlias(
|
||||||
response.AliasExists = false
|
response.AliasExists = false
|
||||||
|
|
||||||
// Save the new alias
|
// Save the new alias
|
||||||
if err := r.DB.SetRoomAlias(ctx, request.Alias, request.RoomID); err != nil {
|
if err := r.DB.SetRoomAlias(ctx, request.Alias, request.RoomID, request.UserID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -133,6 +136,22 @@ func (r *RoomserverAliasAPI) GetAliasesForRoomID(
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetCreatorIDForAlias implements alias.RoomserverAliasAPI
|
||||||
|
func (r *RoomserverAliasAPI) GetCreatorIDForAlias(
|
||||||
|
ctx context.Context,
|
||||||
|
request *roomserverAPI.GetCreatorIDForAliasRequest,
|
||||||
|
response *roomserverAPI.GetCreatorIDForAliasResponse,
|
||||||
|
) error {
|
||||||
|
// Look up the aliases in the database for the given RoomID
|
||||||
|
creatorID, err := r.DB.GetCreatorIDForAlias(ctx, request.Alias)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
response.UserID = creatorID
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// RemoveRoomAlias implements alias.RoomserverAliasAPI
|
// RemoveRoomAlias implements alias.RoomserverAliasAPI
|
||||||
func (r *RoomserverAliasAPI) RemoveRoomAlias(
|
func (r *RoomserverAliasAPI) RemoveRoomAlias(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
|
|
@ -277,6 +296,20 @@ func (r *RoomserverAliasAPI) SetupHTTP(servMux *http.ServeMux) {
|
||||||
return util.JSONResponse{Code: http.StatusOK, JSON: &response}
|
return util.JSONResponse{Code: http.StatusOK, JSON: &response}
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
servMux.Handle(
|
||||||
|
roomserverAPI.RoomserverGetCreatorIDForAliasPath,
|
||||||
|
common.MakeInternalAPI("GetCreatorIDForAlias", func(req *http.Request) util.JSONResponse {
|
||||||
|
var request roomserverAPI.GetCreatorIDForAliasRequest
|
||||||
|
var response roomserverAPI.GetCreatorIDForAliasResponse
|
||||||
|
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
|
||||||
|
return util.ErrorResponse(err)
|
||||||
|
}
|
||||||
|
if err := r.GetCreatorIDForAlias(req.Context(), &request, &response); err != nil {
|
||||||
|
return util.ErrorResponse(err)
|
||||||
|
}
|
||||||
|
return util.JSONResponse{Code: http.StatusOK, JSON: &response}
|
||||||
|
}),
|
||||||
|
)
|
||||||
servMux.Handle(
|
servMux.Handle(
|
||||||
roomserverAPI.RoomserverGetAliasesForRoomIDPath,
|
roomserverAPI.RoomserverGetAliasesForRoomIDPath,
|
||||||
common.MakeInternalAPI("getAliasesForRoomID", func(req *http.Request) util.JSONResponse {
|
common.MakeInternalAPI("getAliasesForRoomID", func(req *http.Request) util.JSONResponse {
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ type MockRoomserverAliasAPIDatabase struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// These methods can be essentially noop
|
// These methods can be essentially noop
|
||||||
func (db MockRoomserverAliasAPIDatabase) SetRoomAlias(ctx context.Context, alias string, roomID string) error {
|
func (db MockRoomserverAliasAPIDatabase) SetRoomAlias(ctx context.Context, alias string, roomID string, creatorUserID string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -43,6 +43,12 @@ func (db MockRoomserverAliasAPIDatabase) RemoveRoomAlias(ctx context.Context, al
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (db *MockRoomserverAliasAPIDatabase) GetCreatorIDForAlias(
|
||||||
|
ctx context.Context, alias string,
|
||||||
|
) (string, error) {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
// This method needs to change depending on test case
|
// This method needs to change depending on test case
|
||||||
func (db *MockRoomserverAliasAPIDatabase) GetRoomIDForAlias(
|
func (db *MockRoomserverAliasAPIDatabase) GetRoomIDForAlias(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,18 @@ type GetAliasesForRoomIDResponse struct {
|
||||||
Aliases []string `json:"aliases"`
|
Aliases []string `json:"aliases"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetCreatorIDForAliasRequest is a request to GetCreatorIDForAlias
|
||||||
|
type GetCreatorIDForAliasRequest struct {
|
||||||
|
// The alias we want to find the creator of
|
||||||
|
Alias string `json:"alias"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCreatorIDForAliasResponse is a response to GetCreatorIDForAlias
|
||||||
|
type GetCreatorIDForAliasResponse struct {
|
||||||
|
// The user ID of the alias creator
|
||||||
|
UserID string `json:"user_id"`
|
||||||
|
}
|
||||||
|
|
||||||
// RemoveRoomAliasRequest is a request to RemoveRoomAlias
|
// RemoveRoomAliasRequest is a request to RemoveRoomAlias
|
||||||
type RemoveRoomAliasRequest struct {
|
type RemoveRoomAliasRequest struct {
|
||||||
// ID of the user removing the alias
|
// ID of the user removing the alias
|
||||||
|
|
@ -96,6 +108,13 @@ type RoomserverAliasAPI interface {
|
||||||
response *GetAliasesForRoomIDResponse,
|
response *GetAliasesForRoomIDResponse,
|
||||||
) error
|
) error
|
||||||
|
|
||||||
|
// Get the user ID of the creator of an alias
|
||||||
|
GetCreatorIDForAlias(
|
||||||
|
ctx context.Context,
|
||||||
|
req *GetCreatorIDForAliasRequest,
|
||||||
|
response *GetCreatorIDForAliasResponse,
|
||||||
|
) error
|
||||||
|
|
||||||
// Remove a room alias
|
// Remove a room alias
|
||||||
RemoveRoomAlias(
|
RemoveRoomAlias(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
|
|
@ -113,6 +132,9 @@ const RoomserverGetRoomIDForAliasPath = "/api/roomserver/GetRoomIDForAlias"
|
||||||
// RoomserverGetAliasesForRoomIDPath is the HTTP path for the GetAliasesForRoomID API.
|
// RoomserverGetAliasesForRoomIDPath is the HTTP path for the GetAliasesForRoomID API.
|
||||||
const RoomserverGetAliasesForRoomIDPath = "/api/roomserver/GetAliasesForRoomID"
|
const RoomserverGetAliasesForRoomIDPath = "/api/roomserver/GetAliasesForRoomID"
|
||||||
|
|
||||||
|
// RoomserverGetCreatorIDForAliasPath is the HTTP path for the GetCreatorIDForAlias API.
|
||||||
|
const RoomserverGetCreatorIDForAliasPath = "/api/roomserver/GetCreatorIDForAlias"
|
||||||
|
|
||||||
// RoomserverRemoveRoomAliasPath is the HTTP path for the RemoveRoomAlias API.
|
// RoomserverRemoveRoomAliasPath is the HTTP path for the RemoveRoomAlias API.
|
||||||
const RoomserverRemoveRoomAliasPath = "/api/roomserver/removeRoomAlias"
|
const RoomserverRemoveRoomAliasPath = "/api/roomserver/removeRoomAlias"
|
||||||
|
|
||||||
|
|
@ -169,6 +191,19 @@ func (h *httpRoomserverAliasAPI) GetAliasesForRoomID(
|
||||||
return commonHTTP.PostJSON(ctx, span, h.httpClient, apiURL, request, response)
|
return commonHTTP.PostJSON(ctx, span, h.httpClient, apiURL, request, response)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetCreatorIDForAlias implements RoomserverAliasAPI
|
||||||
|
func (h *httpRoomserverAliasAPI) GetCreatorIDForAlias(
|
||||||
|
ctx context.Context,
|
||||||
|
request *GetCreatorIDForAliasRequest,
|
||||||
|
response *GetCreatorIDForAliasResponse,
|
||||||
|
) error {
|
||||||
|
span, ctx := opentracing.StartSpanFromContext(ctx, "GetCreatorIDForAlias")
|
||||||
|
defer span.Finish()
|
||||||
|
|
||||||
|
apiURL := h.roomserverURL + RoomserverGetCreatorIDForAliasPath
|
||||||
|
return commonHTTP.PostJSON(ctx, span, h.httpClient, apiURL, request, response)
|
||||||
|
}
|
||||||
|
|
||||||
// RemoveRoomAlias implements RoomserverAliasAPI
|
// RemoveRoomAlias implements RoomserverAliasAPI
|
||||||
func (h *httpRoomserverAliasAPI) RemoveRoomAlias(
|
func (h *httpRoomserverAliasAPI) RemoveRoomAlias(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ func IsServerAllowed(
|
||||||
) bool {
|
) bool {
|
||||||
for _, ev := range authEvents {
|
for _, ev := range authEvents {
|
||||||
membership, err := ev.Membership()
|
membership, err := ev.Membership()
|
||||||
if err != nil || membership != "join" {
|
if err != nil || membership != gomatrixserverlib.Join {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,13 +23,6 @@ import (
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Membership values
|
|
||||||
// TODO: Factor these out somewhere sensible?
|
|
||||||
const join = "join"
|
|
||||||
const leave = "leave"
|
|
||||||
const invite = "invite"
|
|
||||||
const ban = "ban"
|
|
||||||
|
|
||||||
// updateMembership updates the current membership and the invites for each
|
// updateMembership updates the current membership and the invites for each
|
||||||
// user affected by a change in the current state of the room.
|
// user affected by a change in the current state of the room.
|
||||||
// Returns a list of output events to write to the kafka log to inform the
|
// Returns a list of output events to write to the kafka log to inform the
|
||||||
|
|
@ -91,8 +84,8 @@ func updateMembership(
|
||||||
) ([]api.OutputEvent, error) {
|
) ([]api.OutputEvent, error) {
|
||||||
var err error
|
var err error
|
||||||
// Default the membership to Leave if no event was added or removed.
|
// Default the membership to Leave if no event was added or removed.
|
||||||
oldMembership := leave
|
oldMembership := gomatrixserverlib.Leave
|
||||||
newMembership := leave
|
newMembership := gomatrixserverlib.Leave
|
||||||
|
|
||||||
if remove != nil {
|
if remove != nil {
|
||||||
oldMembership, err = remove.Membership()
|
oldMembership, err = remove.Membership()
|
||||||
|
|
@ -106,7 +99,7 @@ func updateMembership(
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if oldMembership == newMembership && newMembership != join {
|
if oldMembership == newMembership && newMembership != gomatrixserverlib.Join {
|
||||||
// If the membership is the same then nothing changed and we can return
|
// If the membership is the same then nothing changed and we can return
|
||||||
// immediately, unless it's a Join update (e.g. profile update).
|
// immediately, unless it's a Join update (e.g. profile update).
|
||||||
return updates, nil
|
return updates, nil
|
||||||
|
|
@ -118,11 +111,11 @@ func updateMembership(
|
||||||
}
|
}
|
||||||
|
|
||||||
switch newMembership {
|
switch newMembership {
|
||||||
case invite:
|
case gomatrixserverlib.Invite:
|
||||||
return updateToInviteMembership(mu, add, updates)
|
return updateToInviteMembership(mu, add, updates)
|
||||||
case join:
|
case gomatrixserverlib.Join:
|
||||||
return updateToJoinMembership(mu, add, updates)
|
return updateToJoinMembership(mu, add, updates)
|
||||||
case leave, ban:
|
case gomatrixserverlib.Leave, gomatrixserverlib.Ban:
|
||||||
return updateToLeaveMembership(mu, add, newMembership, updates)
|
return updateToLeaveMembership(mu, add, newMembership, updates)
|
||||||
default:
|
default:
|
||||||
panic(fmt.Errorf(
|
panic(fmt.Errorf(
|
||||||
|
|
@ -183,7 +176,7 @@ func updateToJoinMembership(
|
||||||
for _, eventID := range retired {
|
for _, eventID := range retired {
|
||||||
orie := api.OutputRetireInviteEvent{
|
orie := api.OutputRetireInviteEvent{
|
||||||
EventID: eventID,
|
EventID: eventID,
|
||||||
Membership: join,
|
Membership: gomatrixserverlib.Join,
|
||||||
RetiredByEventID: add.EventID(),
|
RetiredByEventID: add.EventID(),
|
||||||
TargetUserID: *add.StateKey(),
|
TargetUserID: *add.StateKey(),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -359,7 +359,7 @@ func (r *RoomserverQueryAPI) getMembershipsBeforeEventNID(
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if membership == "join" {
|
if membership == gomatrixserverlib.Join {
|
||||||
events = append(events, event)
|
events = append(events, event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,14 +25,16 @@ CREATE TABLE IF NOT EXISTS roomserver_room_aliases (
|
||||||
-- Alias of the room
|
-- Alias of the room
|
||||||
alias TEXT NOT NULL PRIMARY KEY,
|
alias TEXT NOT NULL PRIMARY KEY,
|
||||||
-- Room ID the alias refers to
|
-- Room ID the alias refers to
|
||||||
room_id TEXT NOT NULL
|
room_id TEXT NOT NULL,
|
||||||
|
-- User ID of the creator of this alias
|
||||||
|
creator_id TEXT NOT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS roomserver_room_id_idx ON roomserver_room_aliases(room_id);
|
CREATE INDEX IF NOT EXISTS roomserver_room_id_idx ON roomserver_room_aliases(room_id);
|
||||||
`
|
`
|
||||||
|
|
||||||
const insertRoomAliasSQL = "" +
|
const insertRoomAliasSQL = "" +
|
||||||
"INSERT INTO roomserver_room_aliases (alias, room_id) VALUES ($1, $2)"
|
"INSERT INTO roomserver_room_aliases (alias, room_id, creator_id) VALUES ($1, $2, $3)"
|
||||||
|
|
||||||
const selectRoomIDFromAliasSQL = "" +
|
const selectRoomIDFromAliasSQL = "" +
|
||||||
"SELECT room_id FROM roomserver_room_aliases WHERE alias = $1"
|
"SELECT room_id FROM roomserver_room_aliases WHERE alias = $1"
|
||||||
|
|
@ -40,6 +42,9 @@ const selectRoomIDFromAliasSQL = "" +
|
||||||
const selectAliasesFromRoomIDSQL = "" +
|
const selectAliasesFromRoomIDSQL = "" +
|
||||||
"SELECT alias FROM roomserver_room_aliases WHERE room_id = $1"
|
"SELECT alias FROM roomserver_room_aliases WHERE room_id = $1"
|
||||||
|
|
||||||
|
const selectCreatorIDFromAliasSQL = "" +
|
||||||
|
"SELECT creator_id FROM roomserver_room_aliases WHERE alias = $1"
|
||||||
|
|
||||||
const deleteRoomAliasSQL = "" +
|
const deleteRoomAliasSQL = "" +
|
||||||
"DELETE FROM roomserver_room_aliases WHERE alias = $1"
|
"DELETE FROM roomserver_room_aliases WHERE alias = $1"
|
||||||
|
|
||||||
|
|
@ -47,6 +52,7 @@ type roomAliasesStatements struct {
|
||||||
insertRoomAliasStmt *sql.Stmt
|
insertRoomAliasStmt *sql.Stmt
|
||||||
selectRoomIDFromAliasStmt *sql.Stmt
|
selectRoomIDFromAliasStmt *sql.Stmt
|
||||||
selectAliasesFromRoomIDStmt *sql.Stmt
|
selectAliasesFromRoomIDStmt *sql.Stmt
|
||||||
|
selectCreatorIDFromAliasStmt *sql.Stmt
|
||||||
deleteRoomAliasStmt *sql.Stmt
|
deleteRoomAliasStmt *sql.Stmt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -59,14 +65,15 @@ func (s *roomAliasesStatements) prepare(db *sql.DB) (err error) {
|
||||||
{&s.insertRoomAliasStmt, insertRoomAliasSQL},
|
{&s.insertRoomAliasStmt, insertRoomAliasSQL},
|
||||||
{&s.selectRoomIDFromAliasStmt, selectRoomIDFromAliasSQL},
|
{&s.selectRoomIDFromAliasStmt, selectRoomIDFromAliasSQL},
|
||||||
{&s.selectAliasesFromRoomIDStmt, selectAliasesFromRoomIDSQL},
|
{&s.selectAliasesFromRoomIDStmt, selectAliasesFromRoomIDSQL},
|
||||||
|
{&s.selectCreatorIDFromAliasStmt, selectCreatorIDFromAliasSQL},
|
||||||
{&s.deleteRoomAliasStmt, deleteRoomAliasSQL},
|
{&s.deleteRoomAliasStmt, deleteRoomAliasSQL},
|
||||||
}.prepare(db)
|
}.prepare(db)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *roomAliasesStatements) insertRoomAlias(
|
func (s *roomAliasesStatements) insertRoomAlias(
|
||||||
ctx context.Context, alias string, roomID string,
|
ctx context.Context, alias string, roomID string, creatorUserID string,
|
||||||
) (err error) {
|
) (err error) {
|
||||||
_, err = s.insertRoomAliasStmt.ExecContext(ctx, alias, roomID)
|
_, err = s.insertRoomAliasStmt.ExecContext(ctx, alias, roomID, creatorUserID)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -101,6 +108,16 @@ func (s *roomAliasesStatements) selectAliasesFromRoomID(
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *roomAliasesStatements) selectCreatorIDFromAlias(
|
||||||
|
ctx context.Context, alias string,
|
||||||
|
) (creatorID string, err error) {
|
||||||
|
err = s.selectCreatorIDFromAliasStmt.QueryRowContext(ctx, alias).Scan(&creatorID)
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func (s *roomAliasesStatements) deleteRoomAlias(
|
func (s *roomAliasesStatements) deleteRoomAlias(
|
||||||
ctx context.Context, alias string,
|
ctx context.Context, alias string,
|
||||||
) (err error) {
|
) (err error) {
|
||||||
|
|
|
||||||
|
|
@ -615,8 +615,8 @@ func (d *Database) GetInvitesForUser(
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetRoomAlias implements alias.RoomserverAliasAPIDB
|
// SetRoomAlias implements alias.RoomserverAliasAPIDB
|
||||||
func (d *Database) SetRoomAlias(ctx context.Context, alias string, roomID string) error {
|
func (d *Database) SetRoomAlias(ctx context.Context, alias string, roomID string, creatorUserID string) error {
|
||||||
return d.statements.insertRoomAlias(ctx, alias, roomID)
|
return d.statements.insertRoomAlias(ctx, alias, roomID, creatorUserID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRoomIDForAlias implements alias.RoomserverAliasAPIDB
|
// GetRoomIDForAlias implements alias.RoomserverAliasAPIDB
|
||||||
|
|
@ -629,6 +629,13 @@ func (d *Database) GetAliasesForRoomID(ctx context.Context, roomID string) ([]st
|
||||||
return d.statements.selectAliasesFromRoomID(ctx, roomID)
|
return d.statements.selectAliasesFromRoomID(ctx, roomID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetCreatorIDForAlias implements alias.RoomserverAliasAPIDB
|
||||||
|
func (d *Database) GetCreatorIDForAlias(
|
||||||
|
ctx context.Context, alias string,
|
||||||
|
) (string, error) {
|
||||||
|
return d.statements.selectCreatorIDFromAlias(ctx, alias)
|
||||||
|
}
|
||||||
|
|
||||||
// RemoveRoomAlias implements alias.RoomserverAliasAPIDB
|
// RemoveRoomAlias implements alias.RoomserverAliasAPIDB
|
||||||
func (d *Database) RemoveRoomAlias(ctx context.Context, alias string) error {
|
func (d *Database) RemoveRoomAlias(ctx context.Context, alias string) error {
|
||||||
return d.statements.deleteRoomAlias(ctx, alias)
|
return d.statements.deleteRoomAlias(ctx, alias)
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,10 @@ func OnIncomingStateRequest(req *http.Request, db *storage.SyncServerDatasource,
|
||||||
// TODO(#287): Auth request and handle the case where the user has left (where
|
// TODO(#287): Auth request and handle the case where the user has left (where
|
||||||
// we should return the state at the poin they left)
|
// we should return the state at the poin they left)
|
||||||
|
|
||||||
stateEvents, err := db.GetStateEventsForRoom(req.Context(), roomID)
|
stateFilterPart := gomatrixserverlib.DefaultFilterPart()
|
||||||
|
// TODO: stateFilterPart should not limit the number of state events (or only limits abusive number of events)
|
||||||
|
|
||||||
|
stateEvents, err := db.GetStateEventsForRoom(req.Context(), roomID, &stateFilterPart)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return httputil.LogThenError(req, err)
|
return httputil.LogThenError(req, err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,9 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
|
||||||
|
"github.com/lib/pq"
|
||||||
"github.com/matrix-org/dendrite/common"
|
"github.com/matrix-org/dendrite/common"
|
||||||
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
)
|
)
|
||||||
|
|
||||||
const accountDataSchema = `
|
const accountDataSchema = `
|
||||||
|
|
@ -41,7 +43,7 @@ CREATE TABLE IF NOT EXISTS syncapi_account_data_type (
|
||||||
CONSTRAINT syncapi_account_data_unique UNIQUE (user_id, room_id, type)
|
CONSTRAINT syncapi_account_data_unique UNIQUE (user_id, room_id, type)
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE UNIQUE INDEX IF NOT EXISTS syncapi_account_data_id_idx ON syncapi_account_data_type(id);
|
CREATE UNIQUE INDEX IF NOT EXISTS syncapi_account_data_id_idx ON syncapi_account_data_type(id, type);
|
||||||
`
|
`
|
||||||
|
|
||||||
const insertAccountDataSQL = "" +
|
const insertAccountDataSQL = "" +
|
||||||
|
|
@ -53,7 +55,9 @@ const insertAccountDataSQL = "" +
|
||||||
const selectAccountDataInRangeSQL = "" +
|
const selectAccountDataInRangeSQL = "" +
|
||||||
"SELECT room_id, type FROM syncapi_account_data_type" +
|
"SELECT room_id, type FROM syncapi_account_data_type" +
|
||||||
" WHERE user_id = $1 AND id > $2 AND id <= $3" +
|
" WHERE user_id = $1 AND id > $2 AND id <= $3" +
|
||||||
" ORDER BY id ASC"
|
" AND ( $4::text[] IS NULL OR type LIKE ANY($4) )" +
|
||||||
|
" AND ( $5::text[] IS NULL OR NOT(type LIKE ANY($5)) )" +
|
||||||
|
" ORDER BY id ASC LIMIT $6"
|
||||||
|
|
||||||
const selectMaxAccountDataIDSQL = "" +
|
const selectMaxAccountDataIDSQL = "" +
|
||||||
"SELECT MAX(id) FROM syncapi_account_data_type"
|
"SELECT MAX(id) FROM syncapi_account_data_type"
|
||||||
|
|
@ -93,6 +97,7 @@ func (s *accountDataStatements) selectAccountDataInRange(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
userID string,
|
userID string,
|
||||||
oldPos, newPos int64,
|
oldPos, newPos int64,
|
||||||
|
accountDataFilterPart *gomatrixserverlib.FilterPart,
|
||||||
) (data map[string][]string, err error) {
|
) (data map[string][]string, err error) {
|
||||||
data = make(map[string][]string)
|
data = make(map[string][]string)
|
||||||
|
|
||||||
|
|
@ -103,7 +108,11 @@ func (s *accountDataStatements) selectAccountDataInRange(
|
||||||
oldPos--
|
oldPos--
|
||||||
}
|
}
|
||||||
|
|
||||||
rows, err := s.selectAccountDataInRangeStmt.QueryContext(ctx, userID, oldPos, newPos)
|
rows, err := s.selectAccountDataInRangeStmt.QueryContext(ctx, userID, oldPos, newPos,
|
||||||
|
pq.StringArray(filterConvertTypeWildcardToSQL(accountDataFilterPart.Types)),
|
||||||
|
pq.StringArray(filterConvertTypeWildcardToSQL(accountDataFilterPart.NotTypes)),
|
||||||
|
accountDataFilterPart.Limit,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ package storage
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
"github.com/lib/pq"
|
"github.com/lib/pq"
|
||||||
"github.com/matrix-org/dendrite/common"
|
"github.com/matrix-org/dendrite/common"
|
||||||
|
|
@ -32,6 +33,10 @@ CREATE TABLE IF NOT EXISTS syncapi_current_room_state (
|
||||||
event_id TEXT NOT NULL,
|
event_id TEXT NOT NULL,
|
||||||
-- The state event type e.g 'm.room.member'
|
-- The state event type e.g 'm.room.member'
|
||||||
type TEXT NOT NULL,
|
type TEXT NOT NULL,
|
||||||
|
-- The 'sender' property of the event.
|
||||||
|
sender TEXT NOT NULL,
|
||||||
|
-- true if the event content contains a url key
|
||||||
|
contains_url BOOL NOT NULL,
|
||||||
-- The state_key value for this state event e.g ''
|
-- The state_key value for this state event e.g ''
|
||||||
state_key TEXT NOT NULL,
|
state_key TEXT NOT NULL,
|
||||||
-- The JSON for the event. Stored as TEXT because this should be valid UTF-8.
|
-- The JSON for the event. Stored as TEXT because this should be valid UTF-8.
|
||||||
|
|
@ -46,16 +51,16 @@ CREATE TABLE IF NOT EXISTS syncapi_current_room_state (
|
||||||
CONSTRAINT syncapi_room_state_unique UNIQUE (room_id, type, state_key)
|
CONSTRAINT syncapi_room_state_unique UNIQUE (room_id, type, state_key)
|
||||||
);
|
);
|
||||||
-- for event deletion
|
-- for event deletion
|
||||||
CREATE UNIQUE INDEX IF NOT EXISTS syncapi_event_id_idx ON syncapi_current_room_state(event_id);
|
CREATE UNIQUE INDEX IF NOT EXISTS syncapi_event_id_idx ON syncapi_current_room_state(event_id, room_id, type, sender, contains_url);
|
||||||
-- for querying membership states of users
|
-- for querying membership states of users
|
||||||
CREATE INDEX IF NOT EXISTS syncapi_membership_idx ON syncapi_current_room_state(type, state_key, membership) WHERE membership IS NOT NULL AND membership != 'leave';
|
CREATE INDEX IF NOT EXISTS syncapi_membership_idx ON syncapi_current_room_state(type, state_key, membership) WHERE membership IS NOT NULL AND membership != 'leave';
|
||||||
`
|
`
|
||||||
|
|
||||||
const upsertRoomStateSQL = "" +
|
const upsertRoomStateSQL = "" +
|
||||||
"INSERT INTO syncapi_current_room_state (room_id, event_id, type, state_key, event_json, membership, added_at)" +
|
"INSERT INTO syncapi_current_room_state (room_id, event_id, type, sender, contains_url, state_key, event_json, membership, added_at)" +
|
||||||
" VALUES ($1, $2, $3, $4, $5, $6, $7)" +
|
" VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)" +
|
||||||
" ON CONFLICT ON CONSTRAINT syncapi_room_state_unique" +
|
" ON CONFLICT ON CONSTRAINT syncapi_room_state_unique" +
|
||||||
" DO UPDATE SET event_id = $2, event_json = $5, membership = $6, added_at = $7"
|
" DO UPDATE SET event_id = $2, sender=$4, contains_url=$5, event_json = $7, membership = $8, added_at = $9"
|
||||||
|
|
||||||
const deleteRoomStateByEventIDSQL = "" +
|
const deleteRoomStateByEventIDSQL = "" +
|
||||||
"DELETE FROM syncapi_current_room_state WHERE event_id = $1"
|
"DELETE FROM syncapi_current_room_state WHERE event_id = $1"
|
||||||
|
|
@ -64,7 +69,13 @@ const selectRoomIDsWithMembershipSQL = "" +
|
||||||
"SELECT room_id FROM syncapi_current_room_state WHERE type = 'm.room.member' AND state_key = $1 AND membership = $2"
|
"SELECT room_id FROM syncapi_current_room_state WHERE type = 'm.room.member' AND state_key = $1 AND membership = $2"
|
||||||
|
|
||||||
const selectCurrentStateSQL = "" +
|
const selectCurrentStateSQL = "" +
|
||||||
"SELECT event_json FROM syncapi_current_room_state WHERE room_id = $1"
|
"SELECT event_json FROM syncapi_current_room_state WHERE room_id = $1" +
|
||||||
|
" AND ( $2::text[] IS NULL OR sender = ANY($2) )" +
|
||||||
|
" AND ( $3::text[] IS NULL OR NOT(sender = ANY($3)) )" +
|
||||||
|
" AND ( $4::text[] IS NULL OR type LIKE ANY($4) )" +
|
||||||
|
" AND ( $5::text[] IS NULL OR NOT(type LIKE ANY($5)) )" +
|
||||||
|
" AND ( $6::bool IS NULL OR contains_url = $6 )" +
|
||||||
|
" LIMIT $7"
|
||||||
|
|
||||||
const selectJoinedUsersSQL = "" +
|
const selectJoinedUsersSQL = "" +
|
||||||
"SELECT room_id, state_key FROM syncapi_current_room_state WHERE type = 'm.room.member' AND membership = 'join'"
|
"SELECT room_id, state_key FROM syncapi_current_room_state WHERE type = 'm.room.member' AND membership = 'join'"
|
||||||
|
|
@ -166,9 +177,17 @@ func (s *currentRoomStateStatements) selectRoomIDsWithMembership(
|
||||||
// CurrentState returns all the current state events for the given room.
|
// CurrentState returns all the current state events for the given room.
|
||||||
func (s *currentRoomStateStatements) selectCurrentState(
|
func (s *currentRoomStateStatements) selectCurrentState(
|
||||||
ctx context.Context, txn *sql.Tx, roomID string,
|
ctx context.Context, txn *sql.Tx, roomID string,
|
||||||
|
stateFilterPart *gomatrixserverlib.FilterPart,
|
||||||
) ([]gomatrixserverlib.Event, error) {
|
) ([]gomatrixserverlib.Event, error) {
|
||||||
stmt := common.TxStmt(txn, s.selectCurrentStateStmt)
|
stmt := common.TxStmt(txn, s.selectCurrentStateStmt)
|
||||||
rows, err := stmt.QueryContext(ctx, roomID)
|
rows, err := stmt.QueryContext(ctx, roomID,
|
||||||
|
pq.StringArray(stateFilterPart.Senders),
|
||||||
|
pq.StringArray(stateFilterPart.NotSenders),
|
||||||
|
pq.StringArray(filterConvertTypeWildcardToSQL(stateFilterPart.Types)),
|
||||||
|
pq.StringArray(filterConvertTypeWildcardToSQL(stateFilterPart.NotTypes)),
|
||||||
|
stateFilterPart.ContainsURL,
|
||||||
|
stateFilterPart.Limit,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -189,12 +208,23 @@ func (s *currentRoomStateStatements) upsertRoomState(
|
||||||
ctx context.Context, txn *sql.Tx,
|
ctx context.Context, txn *sql.Tx,
|
||||||
event gomatrixserverlib.Event, membership *string, addedAt int64,
|
event gomatrixserverlib.Event, membership *string, addedAt int64,
|
||||||
) error {
|
) error {
|
||||||
|
// Parse content as JSON and search for an "url" key
|
||||||
|
containsURL := false
|
||||||
|
var content map[string]interface{}
|
||||||
|
if json.Unmarshal(event.Content(), &content) != nil {
|
||||||
|
// Set containsURL to true if url is present
|
||||||
|
_, containsURL = content["url"]
|
||||||
|
}
|
||||||
|
|
||||||
|
// upsert state event
|
||||||
stmt := common.TxStmt(txn, s.upsertRoomStateStmt)
|
stmt := common.TxStmt(txn, s.upsertRoomStateStmt)
|
||||||
_, err := stmt.ExecContext(
|
_, err := stmt.ExecContext(
|
||||||
ctx,
|
ctx,
|
||||||
event.RoomID(),
|
event.RoomID(),
|
||||||
event.EventID(),
|
event.EventID(),
|
||||||
event.Type(),
|
event.Type(),
|
||||||
|
event.Sender(),
|
||||||
|
containsURL,
|
||||||
*event.StateKey(),
|
*event.StateKey(),
|
||||||
event.JSON(),
|
event.JSON(),
|
||||||
membership,
|
membership,
|
||||||
|
|
|
||||||
36
syncapi/storage/filtering.go
Normal file
36
syncapi/storage/filtering.go
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
// Copyright 2017 Thibaut CHARLES
|
||||||
|
//
|
||||||
|
// 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 (
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// filterConvertWildcardToSQL converts wildcards as defined in
|
||||||
|
// https://matrix.org/docs/spec/client_server/r0.3.0.html#post-matrix-client-r0-user-userid-filter
|
||||||
|
// to SQL wildcards that can be used with LIKE()
|
||||||
|
func filterConvertTypeWildcardToSQL(values []string) []string {
|
||||||
|
if values == nil {
|
||||||
|
// Return nil instead of []string{} so IS NULL can work correctly when
|
||||||
|
// the return value is passed into SQL queries
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ret := make([]string, len(values))
|
||||||
|
for i := range values {
|
||||||
|
ret[i] = strings.Replace(values[i], "*", "%", -1)
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
@ -17,6 +17,7 @@ package storage
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"encoding/json"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/roomserver/api"
|
"github.com/matrix-org/dendrite/roomserver/api"
|
||||||
|
|
@ -43,6 +44,12 @@ CREATE TABLE IF NOT EXISTS syncapi_output_room_events (
|
||||||
room_id TEXT NOT NULL,
|
room_id TEXT NOT NULL,
|
||||||
-- The JSON for the event. Stored as TEXT because this should be valid UTF-8.
|
-- The JSON for the event. Stored as TEXT because this should be valid UTF-8.
|
||||||
event_json TEXT NOT NULL,
|
event_json TEXT NOT NULL,
|
||||||
|
-- The event type e.g 'm.room.member'.
|
||||||
|
type TEXT NOT NULL,
|
||||||
|
-- The 'sender' property of the event.
|
||||||
|
sender TEXT NOT NULL,
|
||||||
|
-- true if the event content contains a url key.
|
||||||
|
contains_url BOOL NOT NULL,
|
||||||
-- A list of event IDs which represent a delta of added/removed room state. This can be NULL
|
-- A list of event IDs which represent a delta of added/removed room state. This can be NULL
|
||||||
-- if there is no delta.
|
-- if there is no delta.
|
||||||
add_state_ids TEXT[],
|
add_state_ids TEXT[],
|
||||||
|
|
@ -56,8 +63,8 @@ CREATE UNIQUE INDEX IF NOT EXISTS syncapi_event_id_idx ON syncapi_output_room_ev
|
||||||
|
|
||||||
const insertEventSQL = "" +
|
const insertEventSQL = "" +
|
||||||
"INSERT INTO syncapi_output_room_events (" +
|
"INSERT INTO syncapi_output_room_events (" +
|
||||||
" room_id, event_id, event_json, add_state_ids, remove_state_ids, device_id, transaction_id" +
|
"room_id, event_id, event_json, type, sender, contains_url, add_state_ids, remove_state_ids, device_id, transaction_id" +
|
||||||
") VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING id"
|
") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) RETURNING id"
|
||||||
|
|
||||||
const selectEventsSQL = "" +
|
const selectEventsSQL = "" +
|
||||||
"SELECT id, event_json, device_id, transaction_id" +
|
"SELECT id, event_json, device_id, transaction_id" +
|
||||||
|
|
@ -76,7 +83,13 @@ const selectStateInRangeSQL = "" +
|
||||||
"SELECT id, event_json, add_state_ids, remove_state_ids" +
|
"SELECT id, event_json, add_state_ids, remove_state_ids" +
|
||||||
" FROM syncapi_output_room_events" +
|
" FROM syncapi_output_room_events" +
|
||||||
" WHERE (id > $1 AND id <= $2) AND (add_state_ids IS NOT NULL OR remove_state_ids IS NOT NULL)" +
|
" WHERE (id > $1 AND id <= $2) AND (add_state_ids IS NOT NULL OR remove_state_ids IS NOT NULL)" +
|
||||||
" ORDER BY id ASC"
|
" AND ( $3::text[] IS NULL OR sender = ANY($3) )" +
|
||||||
|
" AND ( $4::text[] IS NULL OR NOT(sender = ANY($4)) )" +
|
||||||
|
" AND ( $5::text[] IS NULL OR type LIKE ANY($5) )" +
|
||||||
|
" AND ( $6::text[] IS NULL OR NOT(type LIKE ANY($6)) )" +
|
||||||
|
" AND ( $7::bool IS NULL OR contains_url = $7 )" +
|
||||||
|
" ORDER BY id ASC" +
|
||||||
|
" LIMIT $8"
|
||||||
|
|
||||||
type outputRoomEventsStatements struct {
|
type outputRoomEventsStatements struct {
|
||||||
insertEventStmt *sql.Stmt
|
insertEventStmt *sql.Stmt
|
||||||
|
|
@ -114,10 +127,19 @@ func (s *outputRoomEventsStatements) prepare(db *sql.DB) (err error) {
|
||||||
// two positions, only the most recent state is returned.
|
// two positions, only the most recent state is returned.
|
||||||
func (s *outputRoomEventsStatements) selectStateInRange(
|
func (s *outputRoomEventsStatements) selectStateInRange(
|
||||||
ctx context.Context, txn *sql.Tx, oldPos, newPos int64,
|
ctx context.Context, txn *sql.Tx, oldPos, newPos int64,
|
||||||
|
stateFilterPart *gomatrixserverlib.FilterPart,
|
||||||
) (map[string]map[string]bool, map[string]streamEvent, error) {
|
) (map[string]map[string]bool, map[string]streamEvent, error) {
|
||||||
stmt := common.TxStmt(txn, s.selectStateInRangeStmt)
|
stmt := common.TxStmt(txn, s.selectStateInRangeStmt)
|
||||||
|
|
||||||
rows, err := stmt.QueryContext(ctx, oldPos, newPos)
|
rows, err := stmt.QueryContext(
|
||||||
|
ctx, oldPos, newPos,
|
||||||
|
pq.StringArray(stateFilterPart.Senders),
|
||||||
|
pq.StringArray(stateFilterPart.NotSenders),
|
||||||
|
pq.StringArray(filterConvertTypeWildcardToSQL(stateFilterPart.Types)),
|
||||||
|
pq.StringArray(filterConvertTypeWildcardToSQL(stateFilterPart.NotTypes)),
|
||||||
|
stateFilterPart.ContainsURL,
|
||||||
|
stateFilterPart.Limit,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -205,12 +227,23 @@ func (s *outputRoomEventsStatements) insertEvent(
|
||||||
txnID = &transactionID.TransactionID
|
txnID = &transactionID.TransactionID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parse content as JSON and search for an "url" key
|
||||||
|
containsURL := false
|
||||||
|
var content map[string]interface{}
|
||||||
|
if json.Unmarshal(event.Content(), &content) != nil {
|
||||||
|
// Set containsURL to true if url is present
|
||||||
|
_, containsURL = content["url"]
|
||||||
|
}
|
||||||
|
|
||||||
stmt := common.TxStmt(txn, s.insertEventStmt)
|
stmt := common.TxStmt(txn, s.insertEventStmt)
|
||||||
err = stmt.QueryRowContext(
|
err = stmt.QueryRowContext(
|
||||||
ctx,
|
ctx,
|
||||||
event.RoomID(),
|
event.RoomID(),
|
||||||
event.EventID(),
|
event.EventID(),
|
||||||
event.JSON(),
|
event.JSON(),
|
||||||
|
event.Type(),
|
||||||
|
event.Sender(),
|
||||||
|
containsURL,
|
||||||
pq.StringArray(addState),
|
pq.StringArray(addState),
|
||||||
pq.StringArray(removeState),
|
pq.StringArray(removeState),
|
||||||
deviceID,
|
deviceID,
|
||||||
|
|
|
||||||
|
|
@ -35,12 +35,6 @@ import (
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
membershipJoin = "join"
|
|
||||||
membershipLeave = "leave"
|
|
||||||
membershipBan = "ban"
|
|
||||||
)
|
|
||||||
|
|
||||||
type stateDelta struct {
|
type stateDelta struct {
|
||||||
roomID string
|
roomID string
|
||||||
stateEvents []gomatrixserverlib.Event
|
stateEvents []gomatrixserverlib.Event
|
||||||
|
|
@ -227,10 +221,10 @@ func (d *SyncServerDatasource) GetStateEvent(
|
||||||
// Returns an empty slice if no state events could be found for this room.
|
// Returns an empty slice if no state events could be found for this room.
|
||||||
// Returns an error if there was an issue with the retrieval.
|
// Returns an error if there was an issue with the retrieval.
|
||||||
func (d *SyncServerDatasource) GetStateEventsForRoom(
|
func (d *SyncServerDatasource) GetStateEventsForRoom(
|
||||||
ctx context.Context, roomID string,
|
ctx context.Context, roomID string, stateFilterPart *gomatrixserverlib.FilterPart,
|
||||||
) (stateEvents []gomatrixserverlib.Event, err error) {
|
) (stateEvents []gomatrixserverlib.Event, err error) {
|
||||||
err = common.WithTransaction(d.db, func(txn *sql.Tx) error {
|
err = common.WithTransaction(d.db, func(txn *sql.Tx) error {
|
||||||
stateEvents, err = d.roomstate.selectCurrentState(ctx, txn, roomID)
|
stateEvents, err = d.roomstate.selectCurrentState(ctx, txn, roomID, stateFilterPart)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -290,6 +284,8 @@ func (d *SyncServerDatasource) addPDUDeltaToResponse(
|
||||||
var succeeded bool
|
var succeeded bool
|
||||||
defer common.EndTransaction(txn, &succeeded)
|
defer common.EndTransaction(txn, &succeeded)
|
||||||
|
|
||||||
|
stateFilterPart := gomatrixserverlib.DefaultFilterPart() // TODO: use filter provided in request
|
||||||
|
|
||||||
// Work out which rooms to return in the response. This is done by getting not only the currently
|
// Work out which rooms to return in the response. This is done by getting not only the currently
|
||||||
// joined rooms, but also which rooms have membership transitions for this user between the 2 PDU stream positions.
|
// joined rooms, but also which rooms have membership transitions for this user between the 2 PDU stream positions.
|
||||||
// This works out what the 'state' key should be for each room as well as which membership block
|
// This works out what the 'state' key should be for each room as well as which membership block
|
||||||
|
|
@ -297,9 +293,13 @@ func (d *SyncServerDatasource) addPDUDeltaToResponse(
|
||||||
var deltas []stateDelta
|
var deltas []stateDelta
|
||||||
var joinedRoomIDs []string
|
var joinedRoomIDs []string
|
||||||
if !wantFullState {
|
if !wantFullState {
|
||||||
deltas, joinedRoomIDs, err = d.getStateDeltas(ctx, &device, txn, fromPos, toPos, device.UserID)
|
deltas, joinedRoomIDs, err = d.getStateDeltas(
|
||||||
|
ctx, &device, txn, fromPos, toPos, device.UserID, &stateFilterPart,
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
deltas, joinedRoomIDs, err = d.getStateDeltasForFullStateSync(ctx, &device, txn, fromPos, toPos, device.UserID)
|
deltas, joinedRoomIDs, err = d.getStateDeltasForFullStateSync(
|
||||||
|
ctx, &device, txn, fromPos, toPos, device.UserID, &stateFilterPart,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -395,7 +395,7 @@ func (d *SyncServerDatasource) IncrementalSync(
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
joinedRoomIDs, err = d.roomstate.selectRoomIDsWithMembership(
|
joinedRoomIDs, err = d.roomstate.selectRoomIDsWithMembership(
|
||||||
ctx, nil, device.UserID, membershipJoin,
|
ctx, nil, device.UserID, gomatrixserverlib.Join,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -444,15 +444,17 @@ func (d *SyncServerDatasource) getResponseWithPDUsForCompleteSync(
|
||||||
res = types.NewResponse(toPos)
|
res = types.NewResponse(toPos)
|
||||||
|
|
||||||
// Extract room state and recent events for all rooms the user is joined to.
|
// Extract room state and recent events for all rooms the user is joined to.
|
||||||
joinedRoomIDs, err = d.roomstate.selectRoomIDsWithMembership(ctx, txn, userID, membershipJoin)
|
joinedRoomIDs, err = d.roomstate.selectRoomIDsWithMembership(ctx, txn, userID, gomatrixserverlib.Join)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stateFilterPart := gomatrixserverlib.DefaultFilterPart() // TODO: use filter provided in request
|
||||||
|
|
||||||
// Build up a /sync response. Add joined rooms.
|
// Build up a /sync response. Add joined rooms.
|
||||||
for _, roomID := range joinedRoomIDs {
|
for _, roomID := range joinedRoomIDs {
|
||||||
var stateEvents []gomatrixserverlib.Event
|
var stateEvents []gomatrixserverlib.Event
|
||||||
stateEvents, err = d.roomstate.selectCurrentState(ctx, txn, roomID)
|
stateEvents, err = d.roomstate.selectCurrentState(ctx, txn, roomID, &stateFilterPart)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -541,8 +543,9 @@ var txReadOnlySnapshot = sql.TxOptions{
|
||||||
// If there was an issue with the retrieval, returns an error
|
// If there was an issue with the retrieval, returns an error
|
||||||
func (d *SyncServerDatasource) GetAccountDataInRange(
|
func (d *SyncServerDatasource) GetAccountDataInRange(
|
||||||
ctx context.Context, userID string, oldPos, newPos int64,
|
ctx context.Context, userID string, oldPos, newPos int64,
|
||||||
|
accountDataFilterPart *gomatrixserverlib.FilterPart,
|
||||||
) (map[string][]string, error) {
|
) (map[string][]string, error) {
|
||||||
return d.accountData.selectAccountDataInRange(ctx, userID, oldPos, newPos)
|
return d.accountData.selectAccountDataInRange(ctx, userID, oldPos, newPos, accountDataFilterPart)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpsertAccountData keeps track of new or updated account data, by saving the type
|
// UpsertAccountData keeps track of new or updated account data, by saving the type
|
||||||
|
|
@ -647,7 +650,7 @@ func (d *SyncServerDatasource) addRoomDeltaToResponse(
|
||||||
res *types.Response,
|
res *types.Response,
|
||||||
) error {
|
) error {
|
||||||
endPos := toPos
|
endPos := toPos
|
||||||
if delta.membershipPos > 0 && delta.membership == membershipLeave {
|
if delta.membershipPos > 0 && delta.membership == gomatrixserverlib.Leave {
|
||||||
// make sure we don't leak recent events after the leave event.
|
// make sure we don't leak recent events after the leave event.
|
||||||
// TODO: History visibility makes this somewhat complex to handle correctly. For example:
|
// TODO: History visibility makes this somewhat complex to handle correctly. For example:
|
||||||
// TODO: This doesn't work for join -> leave in a single /sync request (see events prior to join).
|
// TODO: This doesn't work for join -> leave in a single /sync request (see events prior to join).
|
||||||
|
|
@ -694,7 +697,7 @@ func (d *SyncServerDatasource) addRoomDeltaToResponse(
|
||||||
}
|
}
|
||||||
|
|
||||||
switch delta.membership {
|
switch delta.membership {
|
||||||
case membershipJoin:
|
case gomatrixserverlib.Join:
|
||||||
jr := types.NewJoinResponse()
|
jr := types.NewJoinResponse()
|
||||||
// Use the short form of batch token for prev_batch
|
// Use the short form of batch token for prev_batch
|
||||||
jr.Timeline.PrevBatch = strconv.FormatInt(prevPDUPos, 10)
|
jr.Timeline.PrevBatch = strconv.FormatInt(prevPDUPos, 10)
|
||||||
|
|
@ -702,9 +705,9 @@ func (d *SyncServerDatasource) addRoomDeltaToResponse(
|
||||||
jr.Timeline.Limited = false // TODO: if len(events) >= numRecents + 1 and then set limited:true
|
jr.Timeline.Limited = false // TODO: if len(events) >= numRecents + 1 and then set limited:true
|
||||||
jr.State.Events = gomatrixserverlib.ToClientEvents(delta.stateEvents, gomatrixserverlib.FormatSync)
|
jr.State.Events = gomatrixserverlib.ToClientEvents(delta.stateEvents, gomatrixserverlib.FormatSync)
|
||||||
res.Rooms.Join[delta.roomID] = *jr
|
res.Rooms.Join[delta.roomID] = *jr
|
||||||
case membershipLeave:
|
case gomatrixserverlib.Leave:
|
||||||
fallthrough // transitions to leave are the same as ban
|
fallthrough // transitions to leave are the same as ban
|
||||||
case membershipBan:
|
case gomatrixserverlib.Ban:
|
||||||
// TODO: recentEvents may contain events that this user is not allowed to see because they are
|
// TODO: recentEvents may contain events that this user is not allowed to see because they are
|
||||||
// no longer in the room.
|
// no longer in the room.
|
||||||
lr := types.NewLeaveResponse()
|
lr := types.NewLeaveResponse()
|
||||||
|
|
@ -812,6 +815,7 @@ func (d *SyncServerDatasource) fetchMissingStateEvents(
|
||||||
func (d *SyncServerDatasource) getStateDeltas(
|
func (d *SyncServerDatasource) getStateDeltas(
|
||||||
ctx context.Context, device *authtypes.Device, txn *sql.Tx,
|
ctx context.Context, device *authtypes.Device, txn *sql.Tx,
|
||||||
fromPos, toPos int64, userID string,
|
fromPos, toPos int64, userID string,
|
||||||
|
stateFilterPart *gomatrixserverlib.FilterPart,
|
||||||
) ([]stateDelta, []string, error) {
|
) ([]stateDelta, []string, error) {
|
||||||
// Implement membership change algorithm: https://github.com/matrix-org/synapse/blob/v0.19.3/synapse/handlers/sync.py#L821
|
// Implement membership change algorithm: https://github.com/matrix-org/synapse/blob/v0.19.3/synapse/handlers/sync.py#L821
|
||||||
// - Get membership list changes for this user in this sync response
|
// - Get membership list changes for this user in this sync response
|
||||||
|
|
@ -824,7 +828,7 @@ func (d *SyncServerDatasource) getStateDeltas(
|
||||||
var deltas []stateDelta
|
var deltas []stateDelta
|
||||||
|
|
||||||
// get all the state events ever between these two positions
|
// get all the state events ever between these two positions
|
||||||
stateNeeded, eventMap, err := d.events.selectStateInRange(ctx, txn, fromPos, toPos)
|
stateNeeded, eventMap, err := d.events.selectStateInRange(ctx, txn, fromPos, toPos, stateFilterPart)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -841,10 +845,10 @@ func (d *SyncServerDatasource) getStateDeltas(
|
||||||
// the 'state' part of the response though, so is transparent modulo bandwidth concerns as it is not added to
|
// the 'state' part of the response though, so is transparent modulo bandwidth concerns as it is not added to
|
||||||
// the timeline.
|
// the timeline.
|
||||||
if membership := getMembershipFromEvent(&ev.Event, userID); membership != "" {
|
if membership := getMembershipFromEvent(&ev.Event, userID); membership != "" {
|
||||||
if membership == membershipJoin {
|
if membership == gomatrixserverlib.Join {
|
||||||
// send full room state down instead of a delta
|
// send full room state down instead of a delta
|
||||||
var s []streamEvent
|
var s []streamEvent
|
||||||
s, err = d.currentStateStreamEventsForRoom(ctx, txn, roomID)
|
s, err = d.currentStateStreamEventsForRoom(ctx, txn, roomID, stateFilterPart)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -864,13 +868,13 @@ func (d *SyncServerDatasource) getStateDeltas(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add in currently joined rooms
|
// Add in currently joined rooms
|
||||||
joinedRoomIDs, err := d.roomstate.selectRoomIDsWithMembership(ctx, txn, userID, membershipJoin)
|
joinedRoomIDs, err := d.roomstate.selectRoomIDsWithMembership(ctx, txn, userID, gomatrixserverlib.Join)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
for _, joinedRoomID := range joinedRoomIDs {
|
for _, joinedRoomID := range joinedRoomIDs {
|
||||||
deltas = append(deltas, stateDelta{
|
deltas = append(deltas, stateDelta{
|
||||||
membership: membershipJoin,
|
membership: gomatrixserverlib.Join,
|
||||||
stateEvents: streamEventsToEvents(device, state[joinedRoomID]),
|
stateEvents: streamEventsToEvents(device, state[joinedRoomID]),
|
||||||
roomID: joinedRoomID,
|
roomID: joinedRoomID,
|
||||||
})
|
})
|
||||||
|
|
@ -886,8 +890,9 @@ func (d *SyncServerDatasource) getStateDeltas(
|
||||||
func (d *SyncServerDatasource) getStateDeltasForFullStateSync(
|
func (d *SyncServerDatasource) getStateDeltasForFullStateSync(
|
||||||
ctx context.Context, device *authtypes.Device, txn *sql.Tx,
|
ctx context.Context, device *authtypes.Device, txn *sql.Tx,
|
||||||
fromPos, toPos int64, userID string,
|
fromPos, toPos int64, userID string,
|
||||||
|
stateFilterPart *gomatrixserverlib.FilterPart,
|
||||||
) ([]stateDelta, []string, error) {
|
) ([]stateDelta, []string, error) {
|
||||||
joinedRoomIDs, err := d.roomstate.selectRoomIDsWithMembership(ctx, txn, userID, "join")
|
joinedRoomIDs, err := d.roomstate.selectRoomIDsWithMembership(ctx, txn, userID, gomatrixserverlib.Join)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -897,19 +902,19 @@ func (d *SyncServerDatasource) getStateDeltasForFullStateSync(
|
||||||
|
|
||||||
// Add full states for all joined rooms
|
// Add full states for all joined rooms
|
||||||
for _, joinedRoomID := range joinedRoomIDs {
|
for _, joinedRoomID := range joinedRoomIDs {
|
||||||
s, stateErr := d.currentStateStreamEventsForRoom(ctx, txn, joinedRoomID)
|
s, stateErr := d.currentStateStreamEventsForRoom(ctx, txn, joinedRoomID, stateFilterPart)
|
||||||
if stateErr != nil {
|
if stateErr != nil {
|
||||||
return nil, nil, stateErr
|
return nil, nil, stateErr
|
||||||
}
|
}
|
||||||
deltas = append(deltas, stateDelta{
|
deltas = append(deltas, stateDelta{
|
||||||
membership: "join",
|
membership: gomatrixserverlib.Join,
|
||||||
stateEvents: streamEventsToEvents(device, s),
|
stateEvents: streamEventsToEvents(device, s),
|
||||||
roomID: joinedRoomID,
|
roomID: joinedRoomID,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get all the state events ever between these two positions
|
// Get all the state events ever between these two positions
|
||||||
stateNeeded, eventMap, err := d.events.selectStateInRange(ctx, txn, fromPos, toPos)
|
stateNeeded, eventMap, err := d.events.selectStateInRange(ctx, txn, fromPos, toPos, stateFilterPart)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -921,7 +926,7 @@ func (d *SyncServerDatasource) getStateDeltasForFullStateSync(
|
||||||
for roomID, stateStreamEvents := range state {
|
for roomID, stateStreamEvents := range state {
|
||||||
for _, ev := range stateStreamEvents {
|
for _, ev := range stateStreamEvents {
|
||||||
if membership := getMembershipFromEvent(&ev.Event, userID); membership != "" {
|
if membership := getMembershipFromEvent(&ev.Event, userID); membership != "" {
|
||||||
if membership != "join" { // We've already added full state for all joined rooms above.
|
if membership != gomatrixserverlib.Join { // We've already added full state for all joined rooms above.
|
||||||
deltas = append(deltas, stateDelta{
|
deltas = append(deltas, stateDelta{
|
||||||
membership: membership,
|
membership: membership,
|
||||||
membershipPos: ev.streamPosition,
|
membershipPos: ev.streamPosition,
|
||||||
|
|
@ -940,8 +945,9 @@ func (d *SyncServerDatasource) getStateDeltasForFullStateSync(
|
||||||
|
|
||||||
func (d *SyncServerDatasource) currentStateStreamEventsForRoom(
|
func (d *SyncServerDatasource) currentStateStreamEventsForRoom(
|
||||||
ctx context.Context, txn *sql.Tx, roomID string,
|
ctx context.Context, txn *sql.Tx, roomID string,
|
||||||
|
stateFilterPart *gomatrixserverlib.FilterPart,
|
||||||
) ([]streamEvent, error) {
|
) ([]streamEvent, error) {
|
||||||
allState, err := d.roomstate.selectCurrentState(ctx, txn, roomID)
|
allState, err := d.roomstate.selectCurrentState(ctx, txn, roomID, stateFilterPart)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -93,16 +93,16 @@ func (n *Notifier) OnNewEvent(
|
||||||
} else {
|
} else {
|
||||||
// Keep the joined user map up-to-date
|
// Keep the joined user map up-to-date
|
||||||
switch membership {
|
switch membership {
|
||||||
case "invite":
|
case gomatrixserverlib.Invite:
|
||||||
usersToNotify = append(usersToNotify, targetUserID)
|
usersToNotify = append(usersToNotify, targetUserID)
|
||||||
case "join":
|
case gomatrixserverlib.Join:
|
||||||
// Manually append the new user's ID so they get notified
|
// Manually append the new user's ID so they get notified
|
||||||
// along all members in the room
|
// along all members in the room
|
||||||
usersToNotify = append(usersToNotify, targetUserID)
|
usersToNotify = append(usersToNotify, targetUserID)
|
||||||
n.addJoinedUser(ev.RoomID(), targetUserID)
|
n.addJoinedUser(ev.RoomID(), targetUserID)
|
||||||
case "leave":
|
case gomatrixserverlib.Leave:
|
||||||
fallthrough
|
fallthrough
|
||||||
case "ban":
|
case gomatrixserverlib.Ban:
|
||||||
n.removeJoinedUser(ev.RoomID(), targetUserID)
|
n.removeJoinedUser(ev.RoomID(), targetUserID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -141,12 +141,14 @@ func (rp *RequestPool) currentSyncForUser(req syncRequest, latestPos types.SyncP
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err = rp.appendAccountData(res, req.device.UserID, req, latestPos.PDUPosition)
|
accountDataFilter := gomatrixserverlib.DefaultFilterPart() // TODO: use filter provided in req instead
|
||||||
|
res, err = rp.appendAccountData(res, req.device.UserID, req, latestPos.PDUPosition, &accountDataFilter)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rp *RequestPool) appendAccountData(
|
func (rp *RequestPool) appendAccountData(
|
||||||
data *types.Response, userID string, req syncRequest, currentPos int64,
|
data *types.Response, userID string, req syncRequest, currentPos int64,
|
||||||
|
accountDataFilter *gomatrixserverlib.FilterPart,
|
||||||
) (*types.Response, error) {
|
) (*types.Response, error) {
|
||||||
// TODO: Account data doesn't have a sync position of its own, meaning that
|
// TODO: Account data doesn't have a sync position of its own, meaning that
|
||||||
// account data might be sent multiple time to the client if multiple account
|
// account data might be sent multiple time to the client if multiple account
|
||||||
|
|
@ -180,7 +182,7 @@ func (rp *RequestPool) appendAccountData(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sync is not initial, get all account data since the latest sync
|
// Sync is not initial, get all account data since the latest sync
|
||||||
dataTypes, err := rp.db.GetAccountDataInRange(req.ctx, userID, req.since.PDUPosition, currentPos)
|
dataTypes, err := rp.db.GetAccountDataInRange(req.ctx, userID, req.since.PDUPosition, currentPos, accountDataFilter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
5
testfile
5
testfile
|
|
@ -159,12 +159,17 @@ Inbound federation rejects remote attempts to kick local users to rooms
|
||||||
An event which redacts itself should be ignored
|
An event which redacts itself should be ignored
|
||||||
A pair of events which redact each other should be ignored
|
A pair of events which redact each other should be ignored
|
||||||
Full state sync includes joined rooms
|
Full state sync includes joined rooms
|
||||||
|
A message sent after an initial sync appears in the timeline of an incremental sync.
|
||||||
Can add tag
|
Can add tag
|
||||||
Can remove tag
|
Can remove tag
|
||||||
Can list tags for a room
|
Can list tags for a room
|
||||||
Tags appear in an initial v2 /sync
|
Tags appear in an initial v2 /sync
|
||||||
Newly updated tags appear in an incremental v2 /sync
|
Newly updated tags appear in an incremental v2 /sync
|
||||||
Deleted tags appear in an incremental v2 /sync
|
Deleted tags appear in an incremental v2 /sync
|
||||||
|
/event/ on non world readable room does not work
|
||||||
|
Outbound federation can query profile data
|
||||||
|
/event/ on joined room works
|
||||||
|
/event/ does not allow access to events before the user joined
|
||||||
POST /rooms/:room_id/redact/:event_id as original message sender redacts message
|
POST /rooms/:room_id/redact/:event_id as original message sender redacts message
|
||||||
POST /rooms/:room_id/redact/:event_id as random user does not redact message
|
POST /rooms/:room_id/redact/:event_id as random user does not redact message
|
||||||
POST /redact disallows redaction of event in different room
|
POST /redact disallows redaction of event in different room
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue