diff --git a/.drone.yml b/.drone.yml index 728506c..bb60bac 100644 --- a/.drone.yml +++ b/.drone.yml @@ -153,12 +153,8 @@ type: docker steps: - name: build - image: golang:latest + image: hrfee/jfa-go-build-docker:latest commands: - - apt-get update -y - - apt-get install build-essential python3-pip curl software-properties-common sed upx gcc libgtk-3-dev libappindicator3-dev gcc-mingw-w64-x86-64 -y - - (curl -sL https://deb.nodesource.com/setup_14.x | bash -) - - apt-get install nodejs - curl -sL https://git.io/goreleaser > goreleaser - chmod +x goreleaser - ./scripts/version.sh ./goreleaser --snapshot --skip-publish --rm-dist diff --git a/.gitignore b/.gitignore index 4a94787..f923b57 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,5 @@ server.crt instructions-debian.txt cl.md ./telegram/ +mautrix/ +matacc.txt diff --git a/LICENSE b/LICENSE index f3247cf..3d8b6da 100644 --- a/LICENSE +++ b/LICENSE @@ -1,3 +1,5 @@ +---jfa-go--- + MIT License Copyright (c) 2021 Harvey Tindall @@ -19,3 +21,4 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + diff --git a/Makefile b/Makefile index e159a60..8be3882 100644 --- a/Makefile +++ b/Makefile @@ -18,22 +18,30 @@ else ifneq ($(UPDATER), off) LDFLAGS := $(LDFLAGS) -X main.updater=$(UPDATER) endif + + INTERNAL ?= on +TRAY ?= off +E2EE ?= off +TAGS := -tags " + ifeq ($(INTERNAL), on) - TAGS := DATA := data else DATA := build/data - TAGS := -tags external + TAGS := $(TAGS) external endif -TRAY ?= off -ifeq ($(INTERNAL)$(TRAY), offon) +ifeq ($(TRAY), on) TAGS := $(TAGS) tray -else ifeq ($(INTERNAL)$(TRAY), onon) - TAGS := -tags tray endif +ifeq ($(E2EE), on) + TAGS := $(TAGS) e2ee +endif + +TAGS := $(TAGS)" + OS := $(shell go env GOOS) ifeq ($(TRAY)$(OS), onwindows) LDFLAGS := $(LDFLAGS) -H=windowsgui diff --git a/api.go b/api.go index f204a64..fa41da0 100644 --- a/api.go +++ b/api.go @@ -1667,6 +1667,16 @@ func (app *appContext) GetConfig(gc *gin.Context) { } } } + if !MatrixE2EE() { + delete(resp.Sections["matrix"].Settings, "encryption") + for i, v := range resp.Sections["matrix"].Order { + if v == "encryption" { + sect := resp.Sections["matrix"] + sect.Order = append(sect.Order[:i], sect.Order[i+1:]...) + resp.Sections["matrix"] = sect + } + } + } for sectName, section := range resp.Sections { for settingName, setting := range section.Settings { val := app.config.Section(sectName).Key(settingName) @@ -2571,18 +2581,20 @@ func (app *appContext) MatrixConnect(gc *gin.Context) { if app.storage.matrix == nil { app.storage.matrix = map[string]MatrixUser{} } - roomID, err := app.matrix.CreateRoom(req.UserID) + roomID, encrypted, err := app.matrix.CreateRoom(req.UserID) if err != nil { app.err.Printf("Matrix: Failed to create room: %v", err) respondBool(500, false, gc) return } app.storage.matrix[req.JellyfinID] = MatrixUser{ - UserID: req.UserID, - RoomID: roomID, - Lang: "en-us", - Contact: true, + UserID: req.UserID, + RoomID: string(roomID), + Lang: "en-us", + Contact: true, + Encrypted: encrypted, } + app.matrix.isEncrypted[roomID] = encrypted if err := app.storage.storeMatrixUsers(); err != nil { app.err.Printf("Failed to store Matrix users: %v", err) respondBool(500, false, gc) diff --git a/config.go b/config.go index f1af63f..e3a8032 100644 --- a/config.go +++ b/config.go @@ -47,6 +47,9 @@ func (app *appContext) loadConfig() error { for _, key := range []string{"user_configuration", "user_displayprefs", "user_profiles", "ombi_template", "invites", "emails", "user_template", "custom_emails", "users", "telegram_users", "discord_users", "matrix_users", "announcements"} { app.config.Section("files").Key(key).SetValue(app.config.Section("files").Key(key).MustString(filepath.Join(app.dataPath, (key + ".json")))) } + for _, key := range []string{"matrix_sql"} { + app.config.Section("files").Key(key).SetValue(app.config.Section("files").Key(key).MustString(filepath.Join(app.dataPath, (key + ".db")))) + } app.URLBase = strings.TrimSuffix(app.config.Section("ui").Key("url_base").MustString(""), "/") app.config.Section("email").Key("no_username").SetValue(strconv.FormatBool(app.config.Section("email").Key("no_username").MustBool(false))) diff --git a/config/config-base.json b/config/config-base.json index 1c36b71..ac386ae 100644 --- a/config/config-base.json +++ b/config/config-base.json @@ -747,6 +747,16 @@ ], "value": "en-us", "description": "Default Matrix message language. Visit weblate if you'd like to translate." + }, + "encryption": { + "name": "End-to-end encryption (experimental)", + "required": false, + "requires_restart": true, + "depends_true": "enabled", + "advanced": true, + "type": "bool", + "value": false, + "description": "Enable end-to-end encryption for messages. Very experimental, currently does not support receiving commands (e.g !lang)." } } }, @@ -1354,6 +1364,14 @@ "value": "", "description": "Stores matrix user IDs and language preferences." }, + "matrix_sql": { + "name": "Matrix encryption DB", + "required": false, + "requires_restart": false, + "type": "text", + "value": "", + "description": "Stores cryptographic material for Matrix end-to-end encryption." + }, "discord_users": { "name": "Discord users", "required": false, diff --git a/email.go b/email.go index d5b4492..76b9d92 100644 --- a/email.go +++ b/email.go @@ -819,7 +819,7 @@ func (app *appContext) sendByID(email *Message, ID ...string) error { } } if mxChat, ok := app.storage.matrix[id]; ok && mxChat.Contact && matrixEnabled { - err = app.matrix.Send(email, mxChat.RoomID) + err = app.matrix.Send(email, mxChat) if err != nil { return err } diff --git a/external.go b/external.go index a34d5d2..f769125 100644 --- a/external.go +++ b/external.go @@ -12,8 +12,8 @@ import ( const binaryType = "external" -var localFS fs.FS -var langFS fs.FS +var localFS dirFS +var langFS dirFS // When using os.DirFS, even on Windows the separator seems to be '/'. // func FSJoin(elem ...string) string { return filepath.Join(elem...) } @@ -29,9 +29,23 @@ func FSJoin(elem ...string) string { return strings.TrimSuffix(path, sep) } +type dirFS string + +func (dir dirFS) Open(name string) (fs.File, error) { + return os.Open(string(dir) + "/" + name) +} + +func (dir dirFS) ReadFile(name string) ([]byte, error) { + return os.ReadFile(string(dir) + "/" + name) +} + +func (dir dirFS) ReadDir(name string) ([]fs.DirEntry, error) { + return os.ReadDir(string(dir) + "/" + name) +} + func loadFilesystems() { log.Println("Using external storage") executable, _ := os.Executable() - localFS = os.DirFS(filepath.Join(filepath.Dir(executable), "data")) - langFS = os.DirFS(filepath.Join(filepath.Dir(executable), "data", "lang")) + localFS = dirFS(filepath.Join(filepath.Dir(executable), "data")) + langFS = dirFS(filepath.Join(filepath.Dir(executable), "data", "lang")) } diff --git a/go.mod b/go.mod index cecafc7..8b4859e 100644 --- a/go.mod +++ b/go.mod @@ -29,6 +29,7 @@ require ( github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible github.com/golang/protobuf v1.4.3 // indirect github.com/gomarkdown/markdown v0.0.0-20210408062403-ad838ccf8cdd + github.com/google/go-cmp v0.5.3 // indirect github.com/google/uuid v1.1.2 // indirect github.com/hrfee/jfa-go/common v0.0.0-20210105184019-fdc97b4e86cc github.com/hrfee/jfa-go/docs v0.0.0-20201112212552-b6f3cd7c1f71 @@ -41,7 +42,7 @@ require ( github.com/lithammer/shortuuid/v3 v3.0.4 github.com/mailgun/mailgun-go/v4 v4.5.1 github.com/mailru/easyjson v0.7.7 // indirect - github.com/matrix-org/gomatrix v0.0.0-20210324163249-be2af5ef2e16 + github.com/mattn/go-sqlite3 v1.14.7 // indirect github.com/pkg/browser v0.0.0-20210606212950-a7b7a6107d32 github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 github.com/smartystreets/goconvey v1.6.4 // indirect @@ -51,10 +52,10 @@ require ( github.com/technoweenie/multipartstreamer v1.0.1 // indirect github.com/ugorji/go v1.2.0 // indirect github.com/writeas/go-strip-markdown v2.0.1+incompatible - golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9 // indirect golang.org/x/net v0.0.0-20210610132358-84b48f89b13b // indirect golang.org/x/sys v0.0.0-20210611083646-a4fc73990273 // indirect golang.org/x/tools v0.1.3 // indirect google.golang.org/protobuf v1.25.0 // indirect gopkg.in/ini.v1 v1.62.0 + maunium.net/go/mautrix v0.9.14 ) diff --git a/go.sum b/go.sum index e2657e7..7318009 100644 --- a/go.sum +++ b/go.sum @@ -7,15 +7,26 @@ github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tN github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= +github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= +github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= +github.com/btcsuite/btcutil v1.0.2 h1:9iZ1Terx9fMIOtq1VrwdqfsATL9MC2l8ZrUY6YZ2uts= +github.com/btcsuite/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts= +github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= +github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= +github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= +github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= +github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= github.com/bwmarrin/discordgo v0.23.2 h1:BzrtTktixGHIu9Tt7dEE6diysEF9HWnXeHuoJEt2fH4= github.com/bwmarrin/discordgo v0.23.2/go.mod h1:c1WtWUGN6nREDmzIpyTp/iD3VYt4Fpx+bVyfBG7JE+M= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -34,6 +45,7 @@ github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870 h1:E2s37DuLxFhQD github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0= github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg= github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/getlantern/context v0.0.0-20190109183933-c447772a6520 h1:NRUJuo3v3WGC/g5YiyF790gut6oQr5f3FBI88Wv0dx4= @@ -50,7 +62,6 @@ github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f h1:wrYrQttPS8FHIRSl github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f/go.mod h1:D5ao98qkA6pxftxoqzibIBBrLSUli+kYnJqrgBf9cIA= github.com/getlantern/systray v1.1.0 h1:U0wCEqseLi2ok1fE6b88gJklzriavPJixZysZPkZd/Y= github.com/getlantern/systray v1.1.0/go.mod h1:AecygODWIsBquJCJFop8MEQcJbWFfw/1yWbVabNgpCM= -github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/gzip v0.0.1 h1:ezvKOL6jH+jlzdHNE4h9h8q8uMpDQjyl0NN0Jd7jozc= github.com/gin-contrib/gzip v0.0.1/go.mod h1:fGBJBCdt6qCZuCAOwWuFhBB4OOq9EFqlo5dEaFhhu5w= @@ -128,8 +139,9 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3 h1:x95R7cp+rSeeqAMI2knLtQ0DKlaBhv2NrtrOvafPHRo= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= @@ -138,16 +150,20 @@ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGa github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hrfee/mediabrowser v0.3.5 h1:bOJlI2HLvw7v0c7mcRw5XDRMUHReQzk5z0EJYRyYjpo= github.com/hrfee/mediabrowser v0.3.5/go.mod h1:PnHZbdxmbv1wCVdAQyM7nwPwpVj9fdKx2EcET7sAk+U= github.com/itchyny/timefmt-go v0.1.2 h1:q0Xa4P5it6K6D7ISsbLAMwx1PnWlixDcJL6/sFs93Hs= github.com/itchyny/timefmt-go v0.1.2/go.mod h1:0osSSCQSASBJMsIZnhAaF1C2fCBTJZXrnj37mG8/c+A= +github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible h1:jdpOPRN1zP63Td1hDQbZW73xKmzDvZHzVdNYxhnTMDA= github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible/go.mod h1:1c7szIrayyPPB/987hsnvNzLushdWf4o/79s3P08L8A= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -156,6 +172,7 @@ github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= @@ -165,6 +182,8 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw= github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/lib/pq v1.9.0 h1:L8nSXQQzAYByakOFMTwpjRoHsMJklur4Gi59b6VivR8= +github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lithammer/shortuuid/v3 v3.0.4 h1:uj4xhotfY92Y1Oa6n6HUiFn87CdoEHYUlTy0+IgbLrs= github.com/lithammer/shortuuid/v3 v3.0.4/go.mod h1:RviRjexKqIzx/7r1peoAITm6m7gnif/h+0zmolKJjzw= github.com/mailgun/mailgun-go/v4 v4.5.1 h1:XrQQ/ZgqFvINRKy+eBqowLl7k3pQO6OCLpKphliMOFs= @@ -175,8 +194,6 @@ github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/matrix-org/gomatrix v0.0.0-20210324163249-be2af5ef2e16 h1:ZtO5uywdd5dLDCud4r0r55eP4j9FuUNpl60Gmntcop4= -github.com/matrix-org/gomatrix v0.0.0-20210324163249-be2af5ef2e16/go.mod h1:/gBX06Kw0exX1HrwmoBibFA98yBk/jxKpGVeyQbff+s= github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= @@ -185,6 +202,9 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/mattn/go-sqlite3 v1.14.7 h1:fxWBnXkxfM6sRiuH3bqJ4CfzZojMOLVc0UTsTglEghA= +github.com/mattn/go-sqlite3 v1.14.7/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -193,6 +213,9 @@ github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9 github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw= github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0= github.com/pkg/browser v0.0.0-20210606212950-a7b7a6107d32 h1:K3WnH8Ka32vWygzmjKEhz1zAVqckNoWDqX3azMxuiSA= @@ -202,10 +225,9 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EEf9cgbU6AtGPK4CTG3Zf6CKMNqf0MHTggAUA= github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog= @@ -231,6 +253,14 @@ github.com/swaggo/swag v1.7.0 h1:5bCA/MTLQoIqDXXyHfOpMeDvL9j68OY/udlK4pQoo4E= github.com/swaggo/swag v1.7.0/go.mod h1:BdPIL73gvS9NBsdi7M1JOxLvlbfvNRaBP8m6WT6Aajo= github.com/technoweenie/multipartstreamer v1.0.1 h1:XRztA5MXiR1TIRHxH2uNxXxaIkKQDeX7m2XsSOlQEnM= github.com/technoweenie/multipartstreamer v1.0.1/go.mod h1:jNVxdtShOxzAsukZwTSw6MDx5eUJoiEBsSvzDU9uzog= +github.com/tidwall/gjson v1.6.8 h1:CTmXMClGYPAmln7652e69B7OLXfTi5ABcPPwjIWUv7w= +github.com/tidwall/gjson v1.6.8/go.mod h1:zeFuBCIqD4sN/gmqBzZ4j7Jd6UcA2Fc56x7QFsv+8fI= +github.com/tidwall/match v1.0.3 h1:FQUVvBImDutD8wJLN6c5eMzWtjgONK9MwIBCOrUJKeE= +github.com/tidwall/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.0.2 h1:Z7S3cePv9Jwm1KwS0513MRaoUe3S01WPbLNV40pwWZU= +github.com/tidwall/pretty v1.0.2/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tidwall/sjson v1.1.5 h1:wsUceI/XDyZk3J1FUvuuYlK62zJv2HO2Pzb8A5EWdUE= +github.com/tidwall/sjson v1.1.5/go.mod h1:VuJzsZnTowhSxWdOgsAnb886i4AjEyTkk7tNtsL7EYE= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go v1.1.5-pre/go.mod h1:FwP/aQVg39TXzItUBMwnWp9T9gPQnXw4Poh4/oBQZ/0= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= @@ -243,23 +273,23 @@ github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLY github.com/ugorji/go/codec v1.1.13/go.mod h1:oNVt3Dq+FO91WNQ/9JnHKQP2QJxTzoN7wCBFCq1OeuU= github.com/ugorji/go/codec v1.2.0 h1:As6RccOIlbm9wHuWYMlB30dErcI+4WiKWsYsmPkyrUw= github.com/ugorji/go/codec v1.2.0/go.mod h1:dXvG35r7zTX6QImXOSFhGMmKtX+wJ7VTWzGvYQGIjBs= -github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ= -github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M= github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= github.com/writeas/go-strip-markdown v2.0.1+incompatible h1:IIqxTM5Jr7RzhigcL6FkrCNfXkvbR+Nbu1ls48pXYcw= github.com/writeas/go-strip-markdown v2.0.1+incompatible/go.mod h1:Rsyu10ZhbEK9pXdk8V6MVnZmTzRG0alMNLMwa0J01fE= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= golang.org/dl v0.0.0-20190829154251-82a15e2f2ead/go.mod h1:IUMfjQLJQd4UTqG1Z90tenwKoCX93Gn3MAQJMOSBsDQ= +golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9 h1:umElSU9WZirRdgu2yFHY0ayQkEnKiOC1TtM3fWXFnoU= -golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g= +golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= @@ -269,6 +299,7 @@ golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -282,6 +313,7 @@ golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= golang.org/x/net v0.0.0-20210610132358-84b48f89b13b h1:k+E048sYJHyVnsr1GDrRZWQ32D2C7lWs9JRc0bel53A= @@ -294,6 +326,7 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -302,6 +335,7 @@ golang.org/x/sys v0.0.0-20190610200419-93c9922d18ae/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -314,6 +348,7 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210611083646-a4fc73990273 h1:faDu4veV+8pcThn4fewv6TVlNCezafGoC1gM/mxQLbQ= golang.org/x/sys v0.0.0-20210611083646-a4fc73990273/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= @@ -362,11 +397,13 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -379,3 +416,6 @@ gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclp gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +maunium.net/go/maulogger/v2 v2.2.4/go.mod h1:TYWy7wKwz/tIXTpsx8G3mZseIRiC5DoMxSZazOHy68A= +maunium.net/go/mautrix v0.9.14 h1:2MMJ630VM+xfa4Q5AooMAhPG1+wQnQybSr/z8PlRZ8A= +maunium.net/go/mautrix v0.9.14/go.mod h1:7IzKfWvpQtN+W2Lzxc0rLvIxFM3ryKX6Ys3S/ZoWbg8= diff --git a/main.go b/main.go index 736a660..2c47deb 100644 --- a/main.go +++ b/main.go @@ -652,7 +652,8 @@ func main() { Exit(r) } }() - defer logOutput()() + f := logOutput() + defer f() printVersion() SOCK = filepath.Join(temp, SOCK) fmt.Println("Socket:", SOCK) diff --git a/matrix.go b/matrix.go index 58cf55b..576603d 100644 --- a/matrix.go +++ b/matrix.go @@ -1,22 +1,28 @@ package main import ( - "encoding/json" "fmt" "strings" + "time" "github.com/gomarkdown/markdown" - "github.com/matrix-org/gomatrix" + "maunium.net/go/mautrix" + "maunium.net/go/mautrix/event" + "maunium.net/go/mautrix/id" ) type MatrixDaemon struct { Stopped bool ShutdownChannel chan string - bot *gomatrix.Client - userID string + bot *mautrix.Client + userID id.UserID tokens map[string]UnverifiedUser // Map of tokens to users - languages map[string]string // Map of roomIDs to language codes + languages map[id.RoomID]string // Map of roomIDs to language codes + Encryption bool + isEncrypted map[id.RoomID]bool + crypto Crypto app *appContext + start int64 } type UnverifiedUser struct { @@ -25,25 +31,20 @@ type UnverifiedUser struct { } type MatrixUser struct { - RoomID string - UserID string - Lang string - Contact bool + RoomID string + Encrypted bool + UserID string + Lang string + Contact bool } -type MatrixIdentifier struct { - User string `json:"user"` - IdentType string `json:"type"` -} - -func (m MatrixIdentifier) Type() string { return m.IdentType } - -var matrixFilter = gomatrix.Filter{ - Room: gomatrix.RoomFilter{ - Timeline: gomatrix.FilterPart{ - Types: []string{ - "m.room.message", - "m.room.member", +var matrixFilter = mautrix.Filter{ + Room: mautrix.RoomFilter{ + Timeline: mautrix.FilterPart{ + Types: []event.Type{ + event.EventMessage, + event.EventEncrypted, + event.StateMember, }, }, }, @@ -53,8 +54,10 @@ var matrixFilter = gomatrix.Filter{ "room_id", "state_key", "sender", - "content.body", - "content.membership", + "content", + "timestamp", + // "content.body", + // "content.membership", }, } @@ -64,40 +67,43 @@ func newMatrixDaemon(app *appContext) (d *MatrixDaemon, err error) { token := matrix.Key("token").String() d = &MatrixDaemon{ ShutdownChannel: make(chan string), - userID: matrix.Key("user_id").String(), + userID: id.UserID(matrix.Key("user_id").String()), tokens: map[string]UnverifiedUser{}, - languages: map[string]string{}, + languages: map[id.RoomID]string{}, + isEncrypted: map[id.RoomID]bool{}, app: app, + start: time.Now().UnixNano() / 1e6, } - d.bot, err = gomatrix.NewClient(homeserver, d.userID, token) + d.bot, err = mautrix.NewClient(homeserver, d.userID, token) if err != nil { return } - filter, err := json.Marshal(matrixFilter) - if err != nil { - return - } - resp, err := d.bot.CreateFilter(filter) - d.bot.Store.SaveFilterID(d.userID, resp.FilterID) + // resp, err := d.bot.CreateFilter(&matrixFilter) + // if err != nil { + // return + // } + // d.bot.Store.SaveFilterID(d.userID, resp.FilterID) for _, user := range app.storage.matrix { if user.Lang != "" { - d.languages[user.RoomID] = user.Lang + d.languages[id.RoomID(user.RoomID)] = user.Lang } + d.isEncrypted[id.RoomID(user.RoomID)] = user.Encrypted } + err = InitMatrixCrypto(d) return } func (d *MatrixDaemon) generateAccessToken(homeserver, username, password string) (string, error) { - req := &gomatrix.ReqLogin{ - Type: "m.login.password", - Identifier: MatrixIdentifier{ - User: username, - IdentType: "m.id.user", + req := &mautrix.ReqLogin{ + Type: mautrix.AuthTypePassword, + Identifier: mautrix.UserIdentifier{ + Type: mautrix.IdentifierTypeUser, + User: username, }, Password: password, - DeviceID: "jfa-go-" + commit, + DeviceID: id.DeviceID("jfa-go-" + commit), } - bot, err := gomatrix.NewClient(homeserver, username, "") + bot, err := mautrix.NewClient(homeserver, id.UserID(username), "") if err != nil { return "", err } @@ -109,84 +115,95 @@ func (d *MatrixDaemon) generateAccessToken(homeserver, username, password string } func (d *MatrixDaemon) run() { + startTime := d.start d.app.info.Println("Starting Matrix bot daemon") - syncer := d.bot.Syncer.(*gomatrix.DefaultSyncer) - syncer.OnEventType("m.room.message", d.handleMessage) - // syncer.OnEventType("m.room.member", d.handleMembership) + syncer := d.bot.Syncer.(*mautrix.DefaultSyncer) + HandleSyncerCrypto(startTime, d, syncer) + syncer.OnEventType(event.EventMessage, d.handleMessage) + if err := d.bot.Sync(); err != nil { d.app.err.Printf("Matrix sync failed: %v", err) } } func (d *MatrixDaemon) Shutdown() { + CryptoShutdown(d) d.bot.StopSync() d.Stopped = true close(d.ShutdownChannel) } -func (d *MatrixDaemon) handleMessage(event *gomatrix.Event) { - if event.Sender == d.userID { +func (d *MatrixDaemon) handleMessage(source mautrix.EventSource, evt *event.Event) { + if evt.Timestamp < d.start { return } + if evt.Sender == d.userID { + return + } + fmt.Printf("RECV %+v\n", evt.Content) lang := "en-us" - if l, ok := d.languages[event.RoomID]; ok { + if l, ok := d.languages[evt.RoomID]; ok { if _, ok := d.app.storage.lang.Telegram[l]; ok { lang = l } } - sects := strings.Split(event.Content["body"].(string), " ") + sects := strings.Split(evt.Content.Raw["body"].(string), " ") switch sects[0] { case "!lang": if len(sects) == 2 { - d.commandLang(event, sects[1], lang) + d.commandLang(evt, sects[1], lang) } else { - d.commandLang(event, "", lang) + d.commandLang(evt, "", lang) } } } -func (d *MatrixDaemon) commandLang(event *gomatrix.Event, code, lang string) { +func (d *MatrixDaemon) commandLang(evt *event.Event, code, lang string) { if code == "" { list := "!lang \n" for c := range d.app.storage.lang.Telegram { list += fmt.Sprintf("%s: %s\n", c, d.app.storage.lang.Telegram[c].Meta.Name) } _, err := d.bot.SendText( - event.RoomID, + evt.RoomID, list, ) if err != nil { - d.app.err.Printf("Matrix: Failed to send message to \"%s\": %v", event.Sender, err) + d.app.err.Printf("Matrix: Failed to send message to \"%s\": %v", evt.Sender, err) } return } if _, ok := d.app.storage.lang.Telegram[code]; !ok { return } - d.languages[event.RoomID] = code - if u, ok := d.app.storage.matrix[event.RoomID]; ok { + d.languages[evt.RoomID] = code + if u, ok := d.app.storage.matrix[string(evt.RoomID)]; ok { u.Lang = code - d.app.storage.matrix[event.RoomID] = u + d.app.storage.matrix[string(evt.RoomID)] = u if err := d.app.storage.storeMatrixUsers(); err != nil { d.app.err.Printf("Matrix: Failed to store Matrix users: %v", err) } } } -func (d *MatrixDaemon) CreateRoom(userID string) (string, error) { - room, err := d.bot.CreateRoom(&gomatrix.ReqCreateRoom{ +func (d *MatrixDaemon) CreateRoom(userID string) (roomID id.RoomID, encrypted bool, err error) { + var room *mautrix.RespCreateRoom + room, err = d.bot.CreateRoom(&mautrix.ReqCreateRoom{ Visibility: "private", - Invite: []string{userID}, + Invite: []id.UserID{id.UserID(userID)}, Topic: d.app.config.Section("matrix").Key("topic").String(), + IsDirect: true, }) if err != nil { - return "", err + return } - return room.RoomID, nil + encrypted = EncryptRoom(d, room, id.UserID(userID)) + roomID = room.RoomID + return } func (d *MatrixDaemon) SendStart(userID string) (ok bool) { - roomID, err := d.CreateRoom(userID) + roomID, encrypted, err := d.CreateRoom(userID) if err != nil { d.app.err.Printf("Failed to create room for user \"%s\": %v", userID, err) return @@ -196,15 +213,19 @@ func (d *MatrixDaemon) SendStart(userID string) (ok bool) { d.tokens[pin] = UnverifiedUser{ false, &MatrixUser{ - RoomID: roomID, - UserID: userID, - Lang: lang, + RoomID: string(roomID), + UserID: userID, + Lang: lang, + Encrypted: encrypted, }, } - _, err = d.bot.SendText( + err = d.sendToRoom( + &event.MessageEventContent{ + MsgType: event.MsgText, + Body: d.app.storage.lang.Telegram[lang].Strings.get("matrixStartMessage") + "\n\n" + pin + "\n\n" + + d.app.storage.lang.Telegram[lang].Strings.template("languageMessage", tmpl{"command": "!lang"}), + }, roomID, - d.app.storage.lang.Telegram[lang].Strings.get("matrixStartMessage")+"\n\n"+pin+"\n\n"+ - d.app.storage.lang.Telegram[lang].Strings.template("languageMessage", tmpl{"command": "!lang"}), ) if err != nil { d.app.err.Printf("Matrix: Failed to send welcome message to \"%s\": %v", userID, err) @@ -214,18 +235,36 @@ func (d *MatrixDaemon) SendStart(userID string) (ok bool) { return } -func (d *MatrixDaemon) Send(message *Message, roomID ...string) (err error) { +func (d *MatrixDaemon) sendToRoom(content *event.MessageEventContent, roomID id.RoomID) (err error) { + if encrypted, ok := d.isEncrypted[roomID]; ok && encrypted { + err = SendEncrypted(d, content, roomID) + } else { + _, err = d.bot.SendMessageEvent(roomID, event.EventMessage, content, mautrix.ReqSendEvent{}) + } + return +} + +func (d *MatrixDaemon) send(content *event.MessageEventContent, roomID id.RoomID) (err error) { + _, err = d.bot.SendMessageEvent(roomID, event.EventMessage, content, mautrix.ReqSendEvent{}) + return +} + +func (d *MatrixDaemon) Send(message *Message, users ...MatrixUser) (err error) { md := "" if message.Markdown != "" { // Convert images to links md = string(markdown.ToHTML([]byte(strings.ReplaceAll(message.Markdown, "![", "[")), nil, renderer)) } - for _, id := range roomID { - if md != "" { - _, err = d.bot.SendFormattedText(id, message.Text, md) - } else { - _, err = d.bot.SendText(id, message.Text) - } + content := &event.MessageEventContent{ + MsgType: "m.text", + Body: message.Text, + } + if md != "" { + content.FormattedBody = md + content.Format = "org.matrix.custom.html" + } + for _, user := range users { + err = d.sendToRoom(content, id.RoomID(user.RoomID)) if err != nil { return } diff --git a/matrix_crypto.go b/matrix_crypto.go new file mode 100644 index 0000000..26ff7e3 --- /dev/null +++ b/matrix_crypto.go @@ -0,0 +1,224 @@ +// +build e2ee + +package main + +import ( + "fmt" + "strings" + + "maunium.net/go/mautrix" + "maunium.net/go/mautrix/crypto" + "maunium.net/go/mautrix/event" + "maunium.net/go/mautrix/id" +) + +type Crypto struct { + cryptoStore *crypto.GobStore + olm *crypto.OlmMachine +} + +func MatrixE2EE() bool { return true } + +type stateStore struct { + isEncrypted *map[id.RoomID]bool +} + +func (m *stateStore) IsEncrypted(roomID id.RoomID) bool { + // encrypted, ok := (*m.isEncrypted)[roomID] + // return ok && encrypted + return true +} + +func (m *stateStore) GetEncryptionEvent(roomID id.RoomID) *event.EncryptionEventContent { + return &event.EncryptionEventContent{ + Algorithm: id.AlgorithmMegolmV1, + RotationPeriodMillis: 7 * 24 * 60 * 60 * 1000, + RotationPeriodMessages: 100, + } +} + +// Users are assumed to only have one common channel with the bot, so we can stub this out. +func (m *stateStore) FindSharedRooms(userID id.UserID) []id.RoomID { + // for _, user := range m.app.storage.matrix { + // if id.UserID(user.UserID) == userID { + // return []id.RoomID{id.RoomID(user.RoomID)} + // } + // } + return []id.RoomID{} +} + +func (d *MatrixDaemon) getUserIDs(roomID id.RoomID) (list []id.UserID, err error) { + members, err := d.bot.JoinedMembers(roomID) + if err != nil { + return + } + list = make([]id.UserID, len(members.Joined)) + i := 0 + for id := range members.Joined { + list[i] = id + i++ + } + return +} + +type olmLogger struct { + app *appContext +} + +func (o olmLogger) Error(message string, args ...interface{}) { + o.app.err.Printf("OLM: "+message+"\n", args) +} + +func (o olmLogger) Warn(message string, args ...interface{}) { + o.app.info.Printf("OLM: "+message+"\n", args) +} + +func (o olmLogger) Debug(message string, args ...interface{}) { + o.app.debug.Printf("OLM: "+message+"\n", args) +} + +func (o olmLogger) Trace(message string, args ...interface{}) { + if strings.HasPrefix(message, "Got membership state event") { + return + } + o.app.debug.Printf("OLM [TRACE]: "+message+"\n", args) +} + +func InitMatrixCrypto(d *MatrixDaemon) (err error) { + d.Encryption = d.app.config.Section("matrix").Key("encryption").MustBool(false) + if !d.Encryption { + return + } + for _, user := range d.app.storage.matrix { + d.isEncrypted[id.RoomID(user.RoomID)] = user.Encrypted + } + dbPath := d.app.config.Section("files").Key("matrix_sql").String() + // If the db is maintained after restart, element reports "The secure channel with the sender was corrupted" when sending a message from the bot. + // This obviously isn't right, but it seems to work. + // Since its not really used anyway, just use the deprecated GobStore. This reduces cgo usage anyway. + // os.Remove(dbPath) + var cryptoStore *crypto.GobStore + cryptoStore, err = crypto.NewGobStore(dbPath) + // d.db, err = sql.Open("sqlite3", dbPath) + if err != nil { + return + } + olmLog := &olmLogger{d.app} + // deviceID := "jfa-go" + commit + // cryptoStore := crypto.NewSQLCryptoStore(d.db, "sqlite3", string(d.userID)+deviceID, id.DeviceID(deviceID), []byte("jfa-go"), olmLog) + // err = cryptoStore.CreateTables() + // if err != nil { + // return + // } + olm := crypto.NewOlmMachine(d.bot, olmLog, cryptoStore, &stateStore{&d.isEncrypted}) + olm.AllowUnverifiedDevices = true + err = d.crypto.olm.Load() + if err != nil { + return + } + d.crypto = Crypto{ + cryptoStore: cryptoStore, + olm: olm, + } + return +} + +func HandleSyncerCrypto(startTime int64, d *MatrixDaemon, syncer *mautrix.DefaultSyncer) { + if !d.Encryption { + return + } + syncer.OnSync(func(resp *mautrix.RespSync, since string) bool { + d.crypto.olm.ProcessSyncResponse(resp, since) + return true + }) + syncer.OnEventType(event.StateMember, func(source mautrix.EventSource, evt *event.Event) { + d.crypto.olm.HandleMemberEvent(evt) + // if evt.Content.AsMember().Membership != event.MembershipJoin { + // return + // } + // userIDs, err := d.getUserIDs(evt.RoomID) + // if err != nil || len(userIDs) < 2 { + // fmt.Println("FS", err) + // return + // } + // err = d.crypto.olm.ShareGroupSession(evt.RoomID, userIDs) + // if err != nil { + // fmt.Println("FS", err) + // return + // } + }) + syncer.OnEventType(event.EventEncrypted, func(source mautrix.EventSource, evt *event.Event) { + if evt.Timestamp < startTime { + return + } + fmt.Printf("%+v\n", d.crypto.cryptoStore.GroupSessions) + decrypted, err := d.crypto.olm.DecryptMegolmEvent(evt) + if err != nil { + d.app.err.Printf("Failed to decrypt Matrix message: %v", err) + return + } + d.handleMessage(source, decrypted) + }) +} + +func CryptoShutdown(d *MatrixDaemon) { + if d.Encryption { + d.crypto.olm.FlushStore() + } +} + +func EncryptRoom(d *MatrixDaemon, room *mautrix.RespCreateRoom, userID id.UserID) (encrypted bool) { + if !d.Encryption { + return + } + _, err := d.bot.SendStateEvent(room.RoomID, event.StateEncryption, "", &event.EncryptionEventContent{ + Algorithm: id.AlgorithmMegolmV1, + RotationPeriodMillis: 7 * 24 * 60 * 60 * 1000, + RotationPeriodMessages: 100, + }) + if err == nil { + encrypted = true + } else { + d.app.debug.Printf("Matrix: Failed to enable encryption in room: %v", err) + return + } + d.isEncrypted[room.RoomID] = encrypted + var userIDs []id.UserID + userIDs, err = d.getUserIDs(room.RoomID) + if err != nil { + return + } + userIDs = append(userIDs, userID) + err = d.crypto.olm.ShareGroupSession(room.RoomID, userIDs) + return +} + +func SendEncrypted(d *MatrixDaemon, content *event.MessageEventContent, roomID id.RoomID) (err error) { + if !d.Encryption { + err = d.send(content, roomID) + return + } + var encrypted *event.EncryptedEventContent + encrypted, err = d.crypto.olm.EncryptMegolmEvent(roomID, event.EventMessage, content) + if err == crypto.SessionExpired || err == crypto.SessionNotShared || err == crypto.NoGroupSession { + // err = d.crypto.olm.ShareGroupSession(id.RoomID(user.RoomID), []id.UserID{id.UserID(user.UserID), d.userID}) + var userIDs []id.UserID + userIDs, err = d.getUserIDs(roomID) + if err != nil { + return + } + err = d.crypto.olm.ShareGroupSession(roomID, userIDs) + if err != nil { + return + } + encrypted, err = d.crypto.olm.EncryptMegolmEvent(roomID, event.EventMessage, content) + } + if err != nil { + return + } + _, err = d.bot.SendMessageEvent(roomID, event.EventEncrypted, &event.Content{Parsed: encrypted}) + if err != nil { + return + } + return +} diff --git a/matrix_nocrypto.go b/matrix_nocrypto.go new file mode 100644 index 0000000..93b58e7 --- /dev/null +++ b/matrix_nocrypto.go @@ -0,0 +1,35 @@ +// +build !e2ee + +package main + +import ( + "maunium.net/go/mautrix" + "maunium.net/go/mautrix/event" + "maunium.net/go/mautrix/id" +) + +type Crypto struct{} + +func MatrixE2EE() bool { return false } + +func InitMatrixCrypto(d *MatrixDaemon) (err error) { + d.Encryption = false + return +} + +func HandleSyncerCrypto(startTime int64, d *MatrixDaemon, syncer *mautrix.DefaultSyncer) { + return +} + +func CryptoShutdown(d *MatrixDaemon) { + return +} + +func EncryptRoom(d *MatrixDaemon, room *mautrix.RespCreateRoom, userID id.UserID) (encrypted bool) { + return +} + +func SendEncrypted(d *MatrixDaemon, content *event.MessageEventContent, roomID id.RoomID) (err error) { + err = d.send(content, roomID) + return +} diff --git a/storage.go b/storage.go index 9c3b777..3296f5c 100644 --- a/storage.go +++ b/storage.go @@ -15,23 +15,23 @@ import ( ) type Storage struct { - timePattern string - invite_path, emails_path, policy_path, configuration_path, displayprefs_path, ombi_path, profiles_path, customEmails_path, users_path, telegram_path, discord_path, matrix_path, announcements_path string - users map[string]time.Time - invites Invites - profiles map[string]Profile - defaultProfile string - displayprefs, ombi_template map[string]interface{} - emails map[string]EmailAddress - telegram map[string]TelegramUser // Map of Jellyfin User IDs to telegram users. - discord map[string]DiscordUser // Map of Jellyfin user IDs to discord users. - matrix map[string]MatrixUser // Map of Jellyfin user IDs to Matrix users. - customEmails customEmails - policy mediabrowser.Policy - configuration mediabrowser.Configuration - lang Lang - announcements map[string]announcementTemplate - invitesLock, usersLock sync.Mutex + timePattern string + invite_path, emails_path, policy_path, configuration_path, displayprefs_path, ombi_path, profiles_path, customEmails_path, users_path, telegram_path, discord_path, matrix_path, announcements_path, matrix_sql_path string + users map[string]time.Time + invites Invites + profiles map[string]Profile + defaultProfile string + displayprefs, ombi_template map[string]interface{} + emails map[string]EmailAddress + telegram map[string]TelegramUser // Map of Jellyfin User IDs to telegram users. + discord map[string]DiscordUser // Map of Jellyfin user IDs to discord users. + matrix map[string]MatrixUser // Map of Jellyfin user IDs to Matrix users. + customEmails customEmails + policy mediabrowser.Policy + configuration mediabrowser.Configuration + lang Lang + announcements map[string]announcementTemplate + invitesLock, usersLock sync.Mutex } type TelegramUser struct {