From 2649e41c85283c680b9e1aa3294868b985aecc22 Mon Sep 17 00:00:00 2001 From: Alex Auvolat Date: Sat, 29 Feb 2020 10:51:09 +0100 Subject: [PATCH] Allow _ezbr_ to be put in suffix of usernames/room aliases instead of prefix --- README.md | 19 +++++++++++++++---- db.go | 23 +++++++++++++---------- main.go | 26 ++++++++++++++++---------- mxlib/client.go | 20 ++++++++++++++++++++ server.go | 2 +- util.go | 29 +++++++++++++++++++++++++++-- 6 files changed, 92 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 3540e48..c2bd29d 100644 --- a/README.md +++ b/README.md @@ -48,11 +48,22 @@ Easybridge is configured using a single JSON configuration file, which contains a dictionnary whose keys are the following: - `log_level`: what log level Easybridge runs with (trace, debug, info, warn, error, fatal, panic). **Warning:** in `trace` level, the content of all calls to the Matrix API and some other information will be dumped, exposing user's credentials and messages. In `debug` level, room join/leave information will be exposed. The `info` level (default) does not expose any user's private information. +- `easybridge_avatar`: path to the image that Easybridge uses as an avatar on Matrix + +### Matrix configuration + +- `registration`: path to the YAML appservice registration file - `appservice_bind_addr`: on what IP/port to bind as a Matrix app service (HTTP only, no HTTPS) +- `homeserver_url`: HTTP address of the Matrix homeserver +- `matrix_domain`: the domain name of the Matrix homeserver (i.e. the domain used in user identifiers, room identifiers, etc) +- `name_format`: the format of identifiers that are created on Matrix for users and room aliases. `{}` is replaced by the concatenation of user/room identifier and protocol. Typically you want either `_ezbr_{}` or `{}_ezbr`, the latter having the advantage that the briged user's names are then used as prefixes for the created identifiers. + +### Web interface configuration + - `web_bind_addr`: on what IP/port to bind for the web interface that allows adding and configuring accounts (HTTP only, no HTTPS, use a reverse proxy for that) - `web_url`: the outside HTTP/HTTPS address at which the web interface is made available. If set, a widget will be added in the Easybridge room so that users can configure the bridge without leaving the Riot client. -- `registration`: path to the YAML appservice registration file -- `homeserver_url`: HTTP address of the Matrix homeserver -- `matrix_domain`: the dmoain name of the Matrix homeserver (i.e. the domain used in user identifiers, room identifiers, etc) -- `db_type` and `db_path`: the database backend and path to use (see the [Gorm documentation](http://gorm.io/docs/connecting_to_the_database.html)) - `session_key`: a key with which session cookies are encrypted for the web interface + +### Storage configuration + +- `db_type` and `db_path`: the database backend and path to use (see the [Gorm documentation](http://gorm.io/docs/connecting_to_the_database.html)) diff --git a/db.go b/db.go index cdbdca1..42c9aee 100644 --- a/db.go +++ b/db.go @@ -213,18 +213,21 @@ func dbGetMxRoom(protocol string, roomId connector.RoomID) (string, error) { if must_create { alias := roomAlias(protocol, roomId) - // Lookup alias - mx_room_id, err := mx.DirectoryRoom(fmt.Sprintf("#%s:%s", alias, config.MatrixDomain)) - // If no alias found, create room + // Delete previous alias if it existed + prev_full_alias := fmt.Sprintf("#%s:%s", alias, config.MatrixDomain) + mx_room_id, err := mx.DirectoryRoom(prev_full_alias) + if err == nil { + mx.DirectoryDeleteRoom(prev_full_alias) + } + + // Create room + name := fmt.Sprintf("%s (%s)", roomId, protocol) + + mx_room_id, err = mx.CreateRoom(name, alias, []string{}) if err != nil { - name := fmt.Sprintf("%s (%s)", roomId, protocol) - - mx_room_id, err = mx.CreateRoom(name, alias, []string{}) - if err != nil { - log.Warnf("Could not create room for %s: %s", name, err) - return "", err - } + log.Warnf("Could not create room for %s: %s", name, err) + return "", err } room = DbRoomMap{ diff --git a/main.go b/main.go index d25a3d6..514bd4c 100644 --- a/main.go +++ b/main.go @@ -15,17 +15,22 @@ import ( ) type ConfigFile struct { - LogLevel string `json:"log_level"` - ASBindAddr string `json:"appservice_bind_addr"` - WebBindAddr string `json:"web_bind_addr"` - WebURL string `json:"web_url"` + LogLevel string `json:"log_level"` + Registration string `json:"registration"` + ASBindAddr string `json:"appservice_bind_addr"` Server string `json:"homeserver_url"` - DbType string `json:"db_type"` - DbPath string `json:"db_path"` MatrixDomain string `json:"matrix_domain"` - SessionKey string `json:"web_session_key"` - AvatarFile string `json:"easybridge_avatar"` + NameFormat string `json:"name_format"` + + WebBindAddr string `json:"web_bind_addr"` + WebURL string `json:"web_url"` + SessionKey string `json:"web_session_key"` + + DbType string `json:"db_type"` + DbPath string `json:"db_path"` + + AvatarFile string `json:"easybridge_avatar"` } var configFlag = flag.String("config", "./config.json", "Configuration file path") @@ -43,6 +48,7 @@ func readConfig() ConfigFile { WebBindAddr: "0.0.0.0:8281", Registration: "./registration.yaml", Server: "http://localhost:8008", + NameFormat: "{}_ezbr_", DbType: "sqlite3", DbPath: "easybridge.db", AvatarFile: "./easybridge.jpg", @@ -102,13 +108,13 @@ func readRegistration(file string) mxlib.Registration { Users: []mxlib.RegistrationNamespace{ mxlib.RegistrationNamespace{ Exclusive: true, - Regex: "@_ezbr_.*", + Regex: "@.*_ezbr_", }, }, Aliases: []mxlib.RegistrationNamespace{ mxlib.RegistrationNamespace{ Exclusive: true, - Regex: "#_ezbr_.*", + Regex: "#.*_ezbr_", }, }, Rooms: []mxlib.RegistrationNamespace{}, diff --git a/mxlib/client.go b/mxlib/client.go index 71bba3f..c111360 100644 --- a/mxlib/client.go +++ b/mxlib/client.go @@ -78,6 +78,17 @@ func (mx *Client) PostApiCall(endpoint string, data interface{}, response interf return mx.DoAndParse(req, response) } +func (mx *Client) DeleteApiCall(endpoint string, response interface{}) error { + log.Debugf("Matrix DELETE request: %s\n", endpoint) + + req, err := http.NewRequest("DELETE", mx.Server+endpoint, nil) + if err != nil { + return err + } + + return mx.DoAndParse(req, response) +} + func (mx *Client) DoAndParse(req *http.Request, response interface{}) error { if mx.Token != "" { req.Header.Add("Authorization", "Bearer "+mx.Token) @@ -181,6 +192,15 @@ func (mx *Client) DirectoryRoom(alias string) (string, error) { return rep.RoomId, nil } +func (mx *Client) DirectoryDeleteRoom(alias string) error { + var rep struct{} + err := mx.DeleteApiCall("/_matrix/client/r0/directory/room/"+url.QueryEscape(alias), &rep) + if err != nil { + return err + } + return nil +} + func (mx *Client) CreateRoom(name string, alias string, invite []string) (string, error) { rq := CreateRoomRequest{ Preset: "private_chat", diff --git a/server.go b/server.go index dc04006..028e7b8 100644 --- a/server.go +++ b/server.go @@ -102,7 +102,7 @@ func handleTxn(w http.ResponseWriter, r *http.Request) { for i := range txn.Events { ev := &txn.Events[i] - if strings.HasPrefix(ev.Sender, "@"+registration.SenderLocalpart) { + if isBridgedIdentifier(ev.Sender) { // Don't do anything with ezbr events that come back to us continue } diff --git a/util.go b/util.go index 5d86b8a..4e23bb1 100644 --- a/util.go +++ b/util.go @@ -5,6 +5,7 @@ import ( "encoding/base64" "encoding/json" "fmt" + "strings" "unicode" log "github.com/sirupsen/logrus" @@ -58,11 +59,13 @@ func ezbrSystemSendf(user_mx_id string, format string, args ...interface{}) { // ---- func roomAlias(protocol string, id RoomID) string { - return fmt.Sprintf("%s_%s__%s", registration.SenderLocalpart, safeStringForId(string(id)), protocol) + what := fmt.Sprintf("%s_%s", safeStringForId(string(id)), protocol) + return strings.Replace(config.NameFormat, "{}", what, 1) } func userMxId(protocol string, id UserID) string { - return fmt.Sprintf("%s_%s__%s", registration.SenderLocalpart, safeStringForId(string(id)), protocol) + what := fmt.Sprintf("%s_%s", safeStringForId(string(id)), protocol) + return strings.Replace(config.NameFormat, "{}", what, 1) } func safeStringForId(in string) string { @@ -79,6 +82,28 @@ func safeStringForId(in string) string { return id2 } +func isBridgedIdentifier(mxid string) bool { + if mxid[0] == '@' || mxid[0] == '#' { + return isBridgedIdentifier(mxid[1:]) + } + + if strings.Contains(mxid, ":") { + sp := strings.Split(mxid, ":") + return (sp[1] == config.MatrixDomain) && isBridgedIdentifier(sp[0]) + } + + nameformat_fixed_part := strings.Replace(config.NameFormat, "{}", "", 1) + if strings.HasPrefix(config.NameFormat, "{}") { + return strings.HasSuffix(mxid, nameformat_fixed_part) + } else if strings.HasSuffix(config.NameFormat, "{}") { + return strings.HasPrefix(mxid, nameformat_fixed_part) + } else { + // This is not supported + log.Fatal("Invalid name format %s, please put {} at the beginning or at the end", config.NameFormat) + return false + } +} + // ---- Encoding and encryption of account config func encryptAccountConfig(config map[string]string, key *[32]byte) string {