diff --git a/appservice/storage/sqlite3/storage.go b/appservice/storage/sqlite3/storage.go index 275c66526..2238b3ff9 100644 --- a/appservice/storage/sqlite3/storage.go +++ b/appservice/storage/sqlite3/storage.go @@ -37,7 +37,11 @@ type Database struct { func NewDatabase(dataSourceName string) (*Database, error) { var result Database var err error - if result.db, err = sqlutil.Open(internal.SQLiteDriverName(), dataSourceName, nil); err != nil { + cs, err := sqlutil.ParseFileURI(dataSourceName) + if err != nil { + return nil, err + } + if result.db, err = sqlutil.Open(internal.SQLiteDriverName(), cs, nil); err != nil { return nil, err } if err = result.prepare(); err != nil { diff --git a/appservice/storage/storage_wasm.go b/appservice/storage/storage_wasm.go index de1acf92f..a6144b435 100644 --- a/appservice/storage/storage_wasm.go +++ b/appservice/storage/storage_wasm.go @@ -34,7 +34,7 @@ func NewDatabase( case "postgres": return nil, fmt.Errorf("Cannot use postgres implementation") case "file": - return sqlite3.NewDatabase(uri.Path) + return sqlite3.NewDatabase(dataSourceName) default: return nil, fmt.Errorf("Cannot use postgres implementation") } diff --git a/appservice/workers/transaction_scheduler.go b/appservice/workers/transaction_scheduler.go index 208056192..63ec58aa1 100644 --- a/appservice/workers/transaction_scheduler.go +++ b/appservice/workers/transaction_scheduler.go @@ -21,6 +21,7 @@ import ( "fmt" "math" "net/http" + "net/url" "time" "github.com/matrix-org/dendrite/appservice/storage" @@ -207,9 +208,15 @@ func send( txnID int, transaction []byte, ) error { - // POST a transaction to our AS - address := fmt.Sprintf("%s/transactions/%d", appservice.URL, txnID) - resp, err := client.Post(address, "application/json", bytes.NewBuffer(transaction)) + // PUT a transaction to our AS + // https://matrix.org/docs/spec/application_service/r0.1.2#put-matrix-app-v1-transactions-txnid + address := fmt.Sprintf("%s/transactions/%d?access_token=%s", appservice.URL, txnID, url.QueryEscape(appservice.HSToken)) + req, err := http.NewRequest("PUT", address, bytes.NewBuffer(transaction)) + if err != nil { + return err + } + req.Header.Set("Content-Type", "application/json") + resp, err := client.Do(req) if err != nil { return err } diff --git a/are-we-synapse-yet.list b/are-we-synapse-yet.list index cb90d6282..c088c8b5e 100644 --- a/are-we-synapse-yet.list +++ b/are-we-synapse-yet.list @@ -455,6 +455,19 @@ rmv User can invite remote user to room with version 5 rmv Remote user can backfill in a room with version 5 rmv Can reject invites over federation for rooms with version 5 rmv Can receive redactions from regular users over federation in room version 5 +rmv User can create and send/receive messages in a room with version 6 +rmv User can create and send/receive messages in a room with version 6 (2 subtests) +rmv local user can join room with version 6 +rmv User can invite local user to room with version 6 +rmv remote user can join room with version 6 +rmv User can invite remote user to room with version 6 +rmv Remote user can backfill in a room with version 6 +rmv Can reject invites over federation for rooms with version 6 +rmv Can receive redactions from regular users over federation in room version 6 +rmv Inbound federation rejects invites which include invalid JSON for room version 6 +rmv Outbound federation rejects invite response which include invalid JSON for room version 6 +rmv Inbound federation rejects invite rejections which include invalid JSON for room version 6 +rmv Server rejects invalid JSON in a version 6 room pre Presence changes are reported to local room members f,pre Presence changes are also reported to remote room members pre Presence changes to UNAVAILABLE are reported to local room members @@ -536,11 +549,11 @@ std Can recv device messages until they are acknowledged std Device messages with the same txn_id are deduplicated std Device messages wake up /sync std Can recv device messages over federation -std Device messages over federation wake up /sync +fsd Device messages over federation wake up /sync std Can send messages with a wildcard device id std Can send messages with a wildcard device id to two devices std Wildcard device messages wake up /sync -std Wildcard device messages over federation wake up /sync +fsd Wildcard device messages over federation wake up /sync adm /whois nsp /purge_history nsp /purge_history by ts @@ -578,6 +591,7 @@ frv A pair of servers can establish a join in a v2 room fsj Outbound federation rejects send_join responses with no m.room.create event frv Outbound federation rejects m.room.create events with an unknown room version fsj Event with an invalid signature in the send_join response should not cause room join to fail +fsj Inbound: send_join rejects invalid JSON for room version 6 fed Outbound federation can send events fed Inbound federation can receive events fed Inbound federation can receive redacted events @@ -636,6 +650,7 @@ fst Name/topic keys are correct fau Remote servers cannot set power levels in rooms without existing powerlevels fau Remote servers should reject attempts by non-creators to set the power levels fau Inbound federation rejects typing notifications from wrong remote +fau Users cannot set notifications powerlevel higher than their own fed Forward extremities remain so even after the next events are populated as outliers fau Banned servers cannot send events fau Banned servers cannot /make_join @@ -833,4 +848,9 @@ gst Guest user can call /events on another world_readable room (SYN-606) gst Real user can call /events on another world_readable room (SYN-606) gst Events come down the correct room pub Asking for a remote rooms list, but supplying the local server's name, returns the local rooms list -std Can send a to-device message to two users which both receive it using /sync \ No newline at end of file +std Can send a to-device message to two users which both receive it using /sync +fme Outbound federation will ignore a missing event with bad JSON for room version 6 +fbk Outbound federation rejects backfill containing invalid JSON for events in room version 6 +jso Invalid JSON integers +jso Invalid JSON floats +jso Invalid JSON special values \ No newline at end of file diff --git a/are-we-synapse-yet.py b/are-we-synapse-yet.py index ffed8d384..5d5128479 100755 --- a/are-we-synapse-yet.py +++ b/are-we-synapse-yet.py @@ -50,6 +50,7 @@ test_mappings = { "fpb": "Public Room API", "fdk": "Device Key APIs", "fed": "Federation API", + "fsd": "Send-to-Device APIs", }, "client_apis": { @@ -99,6 +100,7 @@ test_mappings = { "ign": "Ignore Users", "udr": "User Directory APIs", "app": "Application Services API", + "jso": "Enforced canonical JSON", }, } diff --git a/clientapi/auth/storage/accounts/sqlite3/storage.go b/clientapi/auth/storage/accounts/sqlite3/storage.go index 5233001f2..31426e471 100644 --- a/clientapi/auth/storage/accounts/sqlite3/storage.go +++ b/clientapi/auth/storage/accounts/sqlite3/storage.go @@ -48,7 +48,11 @@ type Database struct { func NewDatabase(dataSourceName string, serverName gomatrixserverlib.ServerName) (*Database, error) { var db *sql.DB var err error - if db, err = sqlutil.Open(internal.SQLiteDriverName(), dataSourceName, nil); err != nil { + cs, err := sqlutil.ParseFileURI(dataSourceName) + if err != nil { + return nil, err + } + if db, err = sqlutil.Open(internal.SQLiteDriverName(), cs, nil); err != nil { return nil, err } partitions := internal.PartitionOffsetStatements{} diff --git a/clientapi/auth/storage/accounts/storage_wasm.go b/clientapi/auth/storage/accounts/storage_wasm.go index 7cf50de79..81e77cf79 100644 --- a/clientapi/auth/storage/accounts/storage_wasm.go +++ b/clientapi/auth/storage/accounts/storage_wasm.go @@ -36,7 +36,7 @@ func NewDatabase( case "postgres": return nil, fmt.Errorf("Cannot use postgres implementation") case "file": - return sqlite3.NewDatabase(uri.Path, serverName) + return sqlite3.NewDatabase(dataSourceName, serverName) default: return nil, fmt.Errorf("Cannot use postgres implementation") } diff --git a/clientapi/auth/storage/devices/sqlite3/storage.go b/clientapi/auth/storage/devices/sqlite3/storage.go index 5cd894517..e05a53b46 100644 --- a/clientapi/auth/storage/devices/sqlite3/storage.go +++ b/clientapi/auth/storage/devices/sqlite3/storage.go @@ -41,7 +41,11 @@ type Database struct { func NewDatabase(dataSourceName string, serverName gomatrixserverlib.ServerName) (*Database, error) { var db *sql.DB var err error - if db, err = sqlutil.Open(internal.SQLiteDriverName(), dataSourceName, nil); err != nil { + cs, err := sqlutil.ParseFileURI(dataSourceName) + if err != nil { + return nil, err + } + if db, err = sqlutil.Open(internal.SQLiteDriverName(), cs, nil); err != nil { return nil, err } d := devicesStatements{} diff --git a/clientapi/auth/storage/devices/storage_wasm.go b/clientapi/auth/storage/devices/storage_wasm.go index c89ad887b..14c19e74b 100644 --- a/clientapi/auth/storage/devices/storage_wasm.go +++ b/clientapi/auth/storage/devices/storage_wasm.go @@ -36,7 +36,7 @@ func NewDatabase( case "postgres": return nil, fmt.Errorf("Cannot use postgres implementation") case "file": - return sqlite3.NewDatabase(uri.Path, serverName) + return sqlite3.NewDatabase(dataSourceName, serverName) default: return nil, fmt.Errorf("Cannot use postgres implementation") } diff --git a/federationapi/routing/routing.go b/federationapi/routing/routing.go index b75a7941d..e6c6df658 100644 --- a/federationapi/routing/routing.go +++ b/federationapi/routing/routing.go @@ -37,6 +37,9 @@ const ( ) // Setup registers HTTP handlers with the given ServeMux. +// The provided publicAPIMux MUST have `UseEncodedPath()` enabled or else routes will incorrectly +// path unescape twice (once from the router, once from MakeFedAPI). We need to have this enabled +// so we can decode paths like foo/bar%2Fbaz as [foo, bar/baz] - by default it will decode to [foo, bar, baz] // // Due to Setup being used to call many other functions, a gocyclo nolint is // applied: @@ -76,11 +79,7 @@ func Setup( v1fedmux.Handle("/send/{txnID}", internal.MakeFedAPI( "federation_send", cfg.Matrix.ServerName, keys, wakeup, - func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest) util.JSONResponse { - vars, err := internal.URLDecodeMapValues(mux.Vars(httpReq)) - if err != nil { - return util.ErrorResponse(err) - } + func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest, vars map[string]string) util.JSONResponse { return Send( httpReq, request, gomatrixserverlib.TransactionID(vars["txnID"]), cfg, rsAPI, producer, eduProducer, keys, federation, @@ -90,11 +89,7 @@ func Setup( v2fedmux.Handle("/invite/{roomID}/{eventID}", internal.MakeFedAPI( "federation_invite", cfg.Matrix.ServerName, keys, wakeup, - func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest) util.JSONResponse { - vars, err := internal.URLDecodeMapValues(mux.Vars(httpReq)) - if err != nil { - return util.ErrorResponse(err) - } + func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest, vars map[string]string) util.JSONResponse { return Invite( httpReq, request, vars["roomID"], vars["eventID"], cfg, producer, keys, @@ -110,11 +105,7 @@ func Setup( v1fedmux.Handle("/exchange_third_party_invite/{roomID}", internal.MakeFedAPI( "exchange_third_party_invite", cfg.Matrix.ServerName, keys, wakeup, - func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest) util.JSONResponse { - vars, err := internal.URLDecodeMapValues(mux.Vars(httpReq)) - if err != nil { - return util.ErrorResponse(err) - } + func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest, vars map[string]string) util.JSONResponse { return ExchangeThirdPartyInvite( httpReq, request, vars["roomID"], rsAPI, cfg, federation, producer, ) @@ -123,11 +114,7 @@ func Setup( v1fedmux.Handle("/event/{eventID}", internal.MakeFedAPI( "federation_get_event", cfg.Matrix.ServerName, keys, wakeup, - func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest) util.JSONResponse { - vars, err := internal.URLDecodeMapValues(mux.Vars(httpReq)) - if err != nil { - return util.ErrorResponse(err) - } + func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest, vars map[string]string) util.JSONResponse { return GetEvent( httpReq.Context(), request, rsAPI, vars["eventID"], cfg.Matrix.ServerName, ) @@ -136,11 +123,7 @@ func Setup( v1fedmux.Handle("/state/{roomID}", internal.MakeFedAPI( "federation_get_state", cfg.Matrix.ServerName, keys, wakeup, - func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest) util.JSONResponse { - vars, err := internal.URLDecodeMapValues(mux.Vars(httpReq)) - if err != nil { - return util.ErrorResponse(err) - } + func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest, vars map[string]string) util.JSONResponse { return GetState( httpReq.Context(), request, rsAPI, vars["roomID"], ) @@ -149,11 +132,7 @@ func Setup( v1fedmux.Handle("/state_ids/{roomID}", internal.MakeFedAPI( "federation_get_state_ids", cfg.Matrix.ServerName, keys, wakeup, - func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest) util.JSONResponse { - vars, err := internal.URLDecodeMapValues(mux.Vars(httpReq)) - if err != nil { - return util.ErrorResponse(err) - } + func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest, vars map[string]string) util.JSONResponse { return GetStateIDs( httpReq.Context(), request, rsAPI, vars["roomID"], ) @@ -162,8 +141,7 @@ func Setup( v1fedmux.Handle("/event_auth/{roomID}/{eventID}", internal.MakeFedAPI( "federation_get_event_auth", cfg.Matrix.ServerName, keys, wakeup, - func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest) util.JSONResponse { - vars := mux.Vars(httpReq) + func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest, vars map[string]string) util.JSONResponse { return GetEventAuth( httpReq.Context(), request, rsAPI, vars["roomID"], vars["eventID"], ) @@ -172,7 +150,7 @@ func Setup( v1fedmux.Handle("/query/directory", internal.MakeFedAPI( "federation_query_room_alias", cfg.Matrix.ServerName, keys, wakeup, - func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest) util.JSONResponse { + func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest, vars map[string]string) util.JSONResponse { return RoomAliasToID( httpReq, federation, cfg, rsAPI, fsAPI, ) @@ -181,7 +159,7 @@ func Setup( v1fedmux.Handle("/query/profile", internal.MakeFedAPI( "federation_query_profile", cfg.Matrix.ServerName, keys, wakeup, - func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest) util.JSONResponse { + func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest, vars map[string]string) util.JSONResponse { return GetProfile( httpReq, accountDB, cfg, asAPI, ) @@ -190,11 +168,7 @@ func Setup( v1fedmux.Handle("/user/devices/{userID}", internal.MakeFedAPI( "federation_user_devices", cfg.Matrix.ServerName, keys, wakeup, - func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest) util.JSONResponse { - vars, err := internal.URLDecodeMapValues(mux.Vars(httpReq)) - if err != nil { - return util.ErrorResponse(err) - } + func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest, vars map[string]string) util.JSONResponse { return GetUserDevices( httpReq, deviceDB, vars["userID"], ) @@ -203,11 +177,7 @@ func Setup( v1fedmux.Handle("/make_join/{roomID}/{eventID}", internal.MakeFedAPI( "federation_make_join", cfg.Matrix.ServerName, keys, wakeup, - func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest) util.JSONResponse { - vars, err := internal.URLDecodeMapValues(mux.Vars(httpReq)) - if err != nil { - return util.ErrorResponse(err) - } + func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest, vars map[string]string) util.JSONResponse { roomID := vars["roomID"] eventID := vars["eventID"] queryVars := httpReq.URL.Query() @@ -232,11 +202,7 @@ func Setup( v1fedmux.Handle("/send_join/{roomID}/{eventID}", internal.MakeFedAPI( "federation_send_join", cfg.Matrix.ServerName, keys, wakeup, - func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest) util.JSONResponse { - vars, err := internal.URLDecodeMapValues(mux.Vars(httpReq)) - if err != nil { - return util.ErrorResponse(err) - } + func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest, vars map[string]string) util.JSONResponse { roomID := vars["roomID"] eventID := vars["eventID"] res := SendJoin( @@ -254,11 +220,7 @@ func Setup( v2fedmux.Handle("/send_join/{roomID}/{eventID}", internal.MakeFedAPI( "federation_send_join", cfg.Matrix.ServerName, keys, wakeup, - func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest) util.JSONResponse { - vars, err := internal.URLDecodeMapValues(mux.Vars(httpReq)) - if err != nil { - return util.ErrorResponse(err) - } + func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest, vars map[string]string) util.JSONResponse { roomID := vars["roomID"] eventID := vars["eventID"] return SendJoin( @@ -269,11 +231,7 @@ func Setup( v1fedmux.Handle("/make_leave/{roomID}/{eventID}", internal.MakeFedAPI( "federation_make_leave", cfg.Matrix.ServerName, keys, wakeup, - func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest) util.JSONResponse { - vars, err := internal.URLDecodeMapValues(mux.Vars(httpReq)) - if err != nil { - return util.ErrorResponse(err) - } + func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest, vars map[string]string) util.JSONResponse { roomID := vars["roomID"] eventID := vars["eventID"] return MakeLeave( @@ -284,11 +242,7 @@ func Setup( v2fedmux.Handle("/send_leave/{roomID}/{eventID}", internal.MakeFedAPI( "federation_send_leave", cfg.Matrix.ServerName, keys, wakeup, - func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest) util.JSONResponse { - vars, err := internal.URLDecodeMapValues(mux.Vars(httpReq)) - if err != nil { - return util.ErrorResponse(err) - } + func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest, vars map[string]string) util.JSONResponse { roomID := vars["roomID"] eventID := vars["eventID"] return SendLeave( @@ -306,22 +260,14 @@ func Setup( v1fedmux.Handle("/get_missing_events/{roomID}", internal.MakeFedAPI( "federation_get_missing_events", cfg.Matrix.ServerName, keys, wakeup, - func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest) util.JSONResponse { - vars, err := internal.URLDecodeMapValues(mux.Vars(httpReq)) - if err != nil { - return util.ErrorResponse(err) - } + func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest, vars map[string]string) util.JSONResponse { return GetMissingEvents(httpReq, request, rsAPI, vars["roomID"]) }, )).Methods(http.MethodPost) v1fedmux.Handle("/backfill/{roomID}", internal.MakeFedAPI( "federation_backfill", cfg.Matrix.ServerName, keys, wakeup, - func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest) util.JSONResponse { - vars, err := internal.URLDecodeMapValues(mux.Vars(httpReq)) - if err != nil { - return util.ErrorResponse(err) - } + func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest, vars map[string]string) util.JSONResponse { return Backfill(httpReq, request, rsAPI, vars["roomID"], cfg) }, )).Methods(http.MethodGet) diff --git a/federationsender/storage/sqlite3/storage.go b/federationsender/storage/sqlite3/storage.go index dc60ebb01..8699fc19a 100644 --- a/federationsender/storage/sqlite3/storage.go +++ b/federationsender/storage/sqlite3/storage.go @@ -38,7 +38,11 @@ type Database struct { func NewDatabase(dataSourceName string) (*Database, error) { var result Database var err error - if result.db, err = sqlutil.Open(internal.SQLiteDriverName(), dataSourceName, nil); err != nil { + cs, err := sqlutil.ParseFileURI(dataSourceName) + if err != nil { + return nil, err + } + if result.db, err = sqlutil.Open(internal.SQLiteDriverName(), cs, nil); err != nil { return nil, err } if err = result.prepare(); err != nil { diff --git a/federationsender/storage/storage_wasm.go b/federationsender/storage/storage_wasm.go index 3d071bfef..f593fd448 100644 --- a/federationsender/storage/storage_wasm.go +++ b/federationsender/storage/storage_wasm.go @@ -33,7 +33,7 @@ func NewDatabase( } switch uri.Scheme { case "file": - return sqlite3.NewDatabase(uri.Path) + return sqlite3.NewDatabase(dataSourceName) case "postgres": return nil, fmt.Errorf("Cannot use postgres implementation") default: diff --git a/internal/basecomponent/base.go b/internal/basecomponent/base.go index 0fc95e824..42d236f85 100644 --- a/internal/basecomponent/base.go +++ b/internal/basecomponent/base.go @@ -103,7 +103,18 @@ func NewBaseDendrite(cfg *config.Dendrite, componentName string, enableHTTPAPIs })} } - httpmux := mux.NewRouter() + // Ideally we would only use SkipClean on routes which we know can allow '/' but due to + // https://github.com/gorilla/mux/issues/460 we have to attach this at the top router. + // When used in conjunction with UseEncodedPath() we get the behaviour we want when parsing + // path parameters: + // /foo/bar%2Fbaz == [foo, bar%2Fbaz] (from UseEncodedPath) + // /foo/bar%2F%2Fbaz == [foo, bar%2F%2Fbaz] (from SkipClean) + // In particular, rooms v3 event IDs are not urlsafe and can include '/' and because they + // are randomly generated it results in flakey tests. + // We need to be careful with media APIs if they read from a filesystem to make sure they + // are not inadvertently reading paths without cleaning, else this could introduce a + // directory traversal attack e.g /../../../etc/passwd + httpmux := mux.NewRouter().SkipClean(true) return &BaseDendrite{ componentName: componentName, @@ -266,12 +277,9 @@ func setupNaffka(cfg *config.Dendrite) (sarama.Consumer, sarama.SyncProducer) { uri, err := url.Parse(string(cfg.Database.Naffka)) if err != nil || uri.Scheme == "file" { var cs string - if uri.Opaque != "" { // file:filename.db - cs = uri.Opaque - } else if uri.Path != "" { // file:///path/to/filename.db - cs = uri.Path - } else { - logrus.Panic("file uri has no filename") + cs, err = sqlutil.ParseFileURI(string(cfg.Database.Naffka)) + if err != nil { + logrus.WithError(err).Panic("Failed to parse naffka database file URI") } db, err = sqlutil.Open(internal.SQLiteDriverName(), cs, nil) if err != nil { diff --git a/internal/httpapi.go b/internal/httpapi.go index bacd1768a..991a98619 100644 --- a/internal/httpapi.go +++ b/internal/httpapi.go @@ -174,7 +174,7 @@ func MakeFedAPI( serverName gomatrixserverlib.ServerName, keyRing gomatrixserverlib.KeyRing, wakeup *FederationWakeups, - f func(*http.Request, *gomatrixserverlib.FederationRequest) util.JSONResponse, + f func(*http.Request, *gomatrixserverlib.FederationRequest, map[string]string) util.JSONResponse, ) http.Handler { h := func(req *http.Request) util.JSONResponse { fedReq, errResp := gomatrixserverlib.VerifyHTTPRequest( @@ -184,7 +184,12 @@ func MakeFedAPI( return errResp } go wakeup.Wakeup(req.Context(), fedReq.Origin()) - return f(req, fedReq) + vars, err := URLDecodeMapValues(mux.Vars(req)) + if err != nil { + return util.ErrorResponse(err) + } + + return f(req, fedReq, vars) } return MakeExternalAPI(metricsName, h) } diff --git a/internal/sqlutil/uri.go b/internal/sqlutil/uri.go new file mode 100644 index 000000000..f72e02426 --- /dev/null +++ b/internal/sqlutil/uri.go @@ -0,0 +1,24 @@ +package sqlutil + +import ( + "fmt" + "net/url" +) + +// ParseFileURI returns the filepath in the given file: URI. Specifically, this will handle +// both relative (file:foo.db) and absolute (file:///path/to/foo) paths. +func ParseFileURI(dataSourceName string) (string, error) { + uri, err := url.Parse(dataSourceName) + if err != nil { + return "", err + } + var cs string + if uri.Opaque != "" { // file:filename.db + cs = uri.Opaque + } else if uri.Path != "" { // file:///path/to/filename.db + cs = uri.Path + } else { + return "", fmt.Errorf("invalid file uri: %s", dataSourceName) + } + return cs, nil +} diff --git a/mediaapi/storage/sqlite3/storage.go b/mediaapi/storage/sqlite3/storage.go index 6da7132d8..ea81f9120 100644 --- a/mediaapi/storage/sqlite3/storage.go +++ b/mediaapi/storage/sqlite3/storage.go @@ -37,7 +37,11 @@ type Database struct { func Open(dataSourceName string) (*Database, error) { var d Database var err error - if d.db, err = sqlutil.Open(internal.SQLiteDriverName(), dataSourceName, nil); err != nil { + cs, err := sqlutil.ParseFileURI(dataSourceName) + if err != nil { + return nil, err + } + if d.db, err = sqlutil.Open(internal.SQLiteDriverName(), cs, nil); err != nil { return nil, err } if err = d.statements.prepare(d.db); err != nil { diff --git a/mediaapi/storage/storage_wasm.go b/mediaapi/storage/storage_wasm.go index aa188997f..78de2cb82 100644 --- a/mediaapi/storage/storage_wasm.go +++ b/mediaapi/storage/storage_wasm.go @@ -35,7 +35,7 @@ func Open( case "postgres": return nil, fmt.Errorf("Cannot use postgres implementation") case "file": - return sqlite3.Open(uri.Path) + return sqlite3.Open(dataSourceName) default: return nil, fmt.Errorf("Cannot use postgres implementation") } diff --git a/publicroomsapi/storage/sqlite3/storage.go b/publicroomsapi/storage/sqlite3/storage.go index 898101127..f51051823 100644 --- a/publicroomsapi/storage/sqlite3/storage.go +++ b/publicroomsapi/storage/sqlite3/storage.go @@ -42,7 +42,11 @@ type attributeValue interface{} func NewPublicRoomsServerDatabase(dataSourceName string, localServerName gomatrixserverlib.ServerName) (*PublicRoomsServerDatabase, error) { var db *sql.DB var err error - if db, err = sqlutil.Open(internal.SQLiteDriverName(), dataSourceName, nil); err != nil { + cs, err := sqlutil.ParseFileURI(dataSourceName) + if err != nil { + return nil, err + } + if db, err = sqlutil.Open(internal.SQLiteDriverName(), cs, nil); err != nil { return nil, err } storage := PublicRoomsServerDatabase{ diff --git a/publicroomsapi/storage/storage_wasm.go b/publicroomsapi/storage/storage_wasm.go index fa3356c03..70ceeaf85 100644 --- a/publicroomsapi/storage/storage_wasm.go +++ b/publicroomsapi/storage/storage_wasm.go @@ -32,7 +32,7 @@ func NewPublicRoomsServerDatabase(dataSourceName string, localServerName gomatri case "postgres": return nil, fmt.Errorf("Cannot use postgres implementation") case "file": - return sqlite3.NewPublicRoomsServerDatabase(uri.Path, localServerName) + return sqlite3.NewPublicRoomsServerDatabase(dataSourceName, localServerName) default: return nil, fmt.Errorf("Cannot use postgres implementation") } diff --git a/roomserver/storage/sqlite3/storage.go b/roomserver/storage/sqlite3/storage.go index 54a2f2658..5596b5e3c 100644 --- a/roomserver/storage/sqlite3/storage.go +++ b/roomserver/storage/sqlite3/storage.go @@ -18,8 +18,6 @@ package sqlite3 import ( "context" "database/sql" - "errors" - "net/url" "github.com/matrix-org/dendrite/internal/sqlutil" @@ -50,18 +48,10 @@ type Database struct { // nolint: gocyclo func Open(dataSourceName string) (*Database, error) { var d Database - uri, err := url.Parse(dataSourceName) + cs, err := sqlutil.ParseFileURI(dataSourceName) if err != nil { return nil, err } - var cs string - if uri.Opaque != "" { // file:filename.db - cs = uri.Opaque - } else if uri.Path != "" { // file:///path/to/filename.db - cs = uri.Path - } else { - return nil, errors.New("no filename or path in connect string") - } if d.db, err = sqlutil.Open(internal.SQLiteDriverName(), cs, nil); err != nil { return nil, err } diff --git a/serverkeyapi/storage/sqlite3/keydb.go b/serverkeyapi/storage/sqlite3/keydb.go index a73b6f80a..ab423cff6 100644 --- a/serverkeyapi/storage/sqlite3/keydb.go +++ b/serverkeyapi/storage/sqlite3/keydb.go @@ -17,8 +17,6 @@ package sqlite3 import ( "context" - "errors" - "net/url" "time" "golang.org/x/crypto/ed25519" @@ -46,18 +44,10 @@ func NewDatabase( serverKey ed25519.PublicKey, serverKeyID gomatrixserverlib.KeyID, ) (*Database, error) { - uri, err := url.Parse(dataSourceName) + cs, err := sqlutil.ParseFileURI(dataSourceName) if err != nil { return nil, err } - var cs string - if uri.Opaque != "" { // file:filename.db - cs = uri.Opaque - } else if uri.Path != "" { // file:///path/to/filename.db - cs = uri.Path - } else { - return nil, errors.New("no filename or path in connect string") - } db, err := sqlutil.Open(internal.SQLiteDriverName(), cs, nil) if err != nil { return nil, err diff --git a/syncapi/storage/sqlite3/syncserver.go b/syncapi/storage/sqlite3/syncserver.go index 5ba07617e..38ce5bcf0 100644 --- a/syncapi/storage/sqlite3/syncserver.go +++ b/syncapi/storage/sqlite3/syncserver.go @@ -17,8 +17,6 @@ package sqlite3 import ( "database/sql" - "errors" - "net/url" "github.com/matrix-org/dendrite/internal/sqlutil" @@ -43,18 +41,10 @@ type SyncServerDatasource struct { // nolint: gocyclo func NewDatabase(dataSourceName string) (*SyncServerDatasource, error) { var d SyncServerDatasource - uri, err := url.Parse(dataSourceName) + cs, err := sqlutil.ParseFileURI(dataSourceName) if err != nil { return nil, err } - var cs string - if uri.Opaque != "" { // file:filename.db - cs = uri.Opaque - } else if uri.Path != "" { // file:///path/to/filename.db - cs = uri.Path - } else { - return nil, errors.New("no filename or path in connect string") - } if d.db, err = sqlutil.Open(internal.SQLiteDriverName(), cs, nil); err != nil { return nil, err }