M go.mod => go.mod +1 -0
@@ 13,6 13,7 @@ require (
github.com/mr-tron/base58 v1.2.0
github.com/multiformats/go-multiaddr v0.2.1
github.com/rivo/tview v0.0.0-20200528200248-fe953220389f
+ github.com/sirupsen/logrus v1.2.0
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b // indirect
maunium.net/go/mautrix v0.7.13
)
M go.sum => go.sum +8 -0
@@ 143,6 143,7 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/koron/go-ssdp v0.0.0-20191105050749-2e1c40ed0b5d h1:68u9r4wEvL3gYg2jvAOgROwZ3H+Y3hIDk4tbbmIjcYQ=
github.com/koron/go-ssdp v0.0.0-20191105050749-2e1c40ed0b5d/go.mod h1:5Ky9EC2xfoUKUor0Hjgi2BJhCSXJfMOFlmyYrVKGQMk=
@@ 153,6 154,7 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kyoh86/xdg v1.2.0 h1:CERuT/ShdTDj+A2UaX3hQ3mOV369+Sj+wyn2nIRIIkI=
github.com/kyoh86/xdg v1.2.0/go.mod h1:/mg8zwu1+qe76oTFUBnyS7rJzk7LLC0VGEzJyJ19DHs=
+github.com/lib/pq v1.7.0 h1:h93mCPfUSkaul3Ka/VG8uZdmW1uMHDGxzu0NWHuJmHY=
github.com/lib/pq v1.7.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/libp2p/go-addr-util v0.0.1 h1:TpTQm9cXVRVSKsYbgQ7GKc3KbbHVTnbostgGaDEP+88=
github.com/libp2p/go-addr-util v0.0.1/go.mod h1:4ac6O7n9rIAKB1dnd+s8IbbMXkt+oBpzX4/+RACcnlQ=
@@ 290,6 292,7 @@ github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.8 h1:3tS41NlGYSmhhe/8fhGRzc+z3AYCw1Fe1WAyLuujKs0=
github.com/mattn/go-runewidth v0.0.8/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
+github.com/mattn/go-sqlite3 v1.14.0 h1:mLyGNKR8+Vv9CAU7PphKa2hkEqxxhn8i32J6FPj1/QA=
github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/miekg/dns v1.1.12/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
@@ 379,6 382,7 @@ github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
+github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/smola/gocompat v0.2.0/go.mod h1:1B0MlxbmoZNo3h8guHp8HztB3BSYR5itql9qtVc0ypY=
github.com/spacemonkeygo/openssl v0.0.0-20181017203307-c2dcc5cca94a/go.mod h1:7AyxJNCJ7SBZ1MfVQCWD6Uqo2oubI2Eq2y2eqf+A5r0=
@@ 402,10 406,14 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
+github.com/tidwall/gjson v1.6.0 h1:9VEQWz6LLMUsUl6PueE49ir4Ka6CzLymOAZDxpFsTDc=
github.com/tidwall/gjson v1.6.0/go.mod h1:P256ACg0Mn+j1RXIDXoss50DeIABTYK1PULOJHhxOls=
+github.com/tidwall/match v1.0.1 h1:PnKP62LPNxHKTwvHHZZzdOAOCtsJTjo6dZLCwpKm5xc=
github.com/tidwall/match v1.0.1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
+github.com/tidwall/pretty v1.0.1 h1:WE4RBSZ1x6McVVC8S/Md+Qse8YUv6HRObAx6ke00NY8=
github.com/tidwall/pretty v1.0.1/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
+github.com/tidwall/sjson v1.1.1 h1:7h1vk049Jnd5EH9NyzNiEuwYW4b5qgreBbqRC19AS3U=
github.com/tidwall/sjson v1.1.1/go.mod h1:yvVuSnpEQv5cYIrO+AT6kw4QVfd5SDZoGIS7/5+fZFs=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 h1:EKhdznlJHPMoKr0XTrX+IlJs1LH3lyx2nfr1dOlZ79k=
A matrix/crypto_logger.go => matrix/crypto_logger.go +28 -0
@@ 0,0 1,28 @@
+package main
+
+import (
+ log "github.com/sirupsen/logrus"
+)
+
+// CryptoMachineLogger wraps around the usual logger, implementing the Logger interface needed by OlmMachine.
+type CryptoMachineLogger struct{}
+
+// Error formats and logs an error message.
+func (CryptoMachineLogger) Error(message string, args ...interface{}) {
+ log.Errorf(message, args...)
+}
+
+// Warn formats and logs a warning message.
+func (CryptoMachineLogger) Warn(message string, args ...interface{}) {
+ log.Warnf(message, args...)
+}
+
+// Debug formats and logs a debug message.
+func (CryptoMachineLogger) Debug(message string, args ...interface{}) {
+ log.Debugf(message, args...)
+}
+
+// Trace formats and logs a trace message.
+func (CryptoMachineLogger) Trace(message string, args ...interface{}) {
+ log.Tracef(message, args...)
+}
M matrix/main.go => matrix/main.go +125 -0
@@ 3,12 3,137 @@ package main
import (
"fmt"
"os"
+
+ "github.com/kyoh86/xdg"
+ log "github.com/sirupsen/logrus"
+ "maunium.net/go/mautrix"
+ "maunium.net/go/mautrix/crypto"
+ "maunium.net/go/mautrix/event"
)
+type MockSharedStore struct{}
+
+var StartMessage = event.Type{"xyz.tallyard.start", event.MessageEventType}
+var client *mautrix.Client
+var olmMachine *crypto.OlmMachine
+var gobStorePath string = xdg.DataHome() + "/tallyard/gob.dat"
+
+func debugCB(source mautrix.EventSource, evt *event.Event) {
+ fmt.Printf("%[5]d: <%[1]s> %[4]s (%[2]s/%[3]s)\n", evt.Sender, evt.Type.String(), evt.ID, evt.Content.AsMessage().Body, source)
+}
+
+func joinRoomCB(source mautrix.EventSource, evt *event.Event) {
+ debugCB(source, evt)
+
+ memberEvtContent := evt.Content.AsMember()
+ if memberEvtContent.Membership != event.MembershipInvite {
+ return
+ }
+
+ _, err := client.JoinRoomByID(evt.RoomID)
+ if err != nil {
+ panic(err)
+ }
+}
+
+func encryptedCB(source mautrix.EventSource, evt *event.Event) {
+ debugCB(source, evt)
+
+ encryptedEvtContent := evt.Content.AsEncrypted()
+ fmt.Println(encryptedEvtContent)
+
+ decrypted, err := olmMachine.DecryptMegolmEvent(evt)
+ if err != nil {
+ log.WithError(err).Warn("couldn't decrypt melogm event")
+ return
+ }
+ debugCB(source, decrypted)
+}
+
func main() {
data, err := GetData()
if err != nil {
fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
}
fmt.Println(data)
+
+ client, err = mautrix.NewClient(data.Homeserver, data.UserID, data.AccessToken)
+ if err != nil {
+ panic(err)
+ }
+
+ syncer := client.Syncer.(*mautrix.DefaultSyncer)
+ syncer.OnEventType(event.StateAliases, debugCB)
+ syncer.OnEventType(event.StateCanonicalAlias, debugCB)
+ syncer.OnEventType(event.StateCreate, debugCB)
+ syncer.OnEventType(event.StateJoinRules, debugCB)
+ syncer.OnEventType(event.StateHistoryVisibility, debugCB)
+ syncer.OnEventType(event.StateGuestAccess, debugCB)
+ syncer.OnEventType(event.StateMember, joinRoomCB)
+ syncer.OnEventType(event.StatePowerLevels, debugCB)
+ syncer.OnEventType(event.StateRoomName, debugCB)
+ syncer.OnEventType(event.StateTopic, debugCB)
+ syncer.OnEventType(event.StateRoomAvatar, debugCB)
+ syncer.OnEventType(event.StatePinnedEvents, debugCB)
+ syncer.OnEventType(event.StateTombstone, debugCB)
+ syncer.OnEventType(event.StateEncryption, debugCB)
+ syncer.OnEventType(event.EventRedaction, debugCB)
+ syncer.OnEventType(event.EventMessage, debugCB)
+ syncer.OnEventType(event.EventEncrypted, encryptedCB)
+ syncer.OnEventType(event.EventReaction, debugCB)
+ syncer.OnEventType(event.EventSticker, debugCB)
+ syncer.OnEventType(event.EphemeralEventReceipt, debugCB)
+ syncer.OnEventType(event.EphemeralEventTyping, debugCB)
+ syncer.OnEventType(event.EphemeralEventPresence, debugCB)
+ syncer.OnEventType(event.AccountDataDirectChats, debugCB)
+ syncer.OnEventType(event.AccountDataPushRules, debugCB)
+ syncer.OnEventType(event.AccountDataRoomTags, debugCB)
+ syncer.OnEventType(event.AccountDataFullyRead, debugCB)
+ syncer.OnEventType(event.AccountDataIgnoredUserList, debugCB)
+ syncer.OnEventType(event.ToDeviceRoomKey, debugCB)
+ syncer.OnEventType(event.ToDeviceRoomKeyRequest, debugCB)
+ syncer.OnEventType(event.ToDeviceForwardedRoomKey, debugCB)
+ syncer.OnEventType(event.ToDeviceEncrypted, debugCB)
+ syncer.OnEventType(event.ToDeviceRoomKeyWithheld, debugCB)
+ syncer.OnEventType(event.ToDeviceVerificationRequest, debugCB)
+ syncer.OnEventType(event.ToDeviceVerificationStart, debugCB)
+ syncer.OnEventType(event.ToDeviceVerificationAccept, debugCB)
+ syncer.OnEventType(event.ToDeviceVerificationKey, debugCB)
+ syncer.OnEventType(event.ToDeviceVerificationMAC, debugCB)
+ syncer.OnEventType(event.ToDeviceVerificationCancel, debugCB)
+ syncer.OnEventType(event.ToDeviceOrgMatrixRoomKeyWithheld, debugCB)
+
+ resp, err := client.JoinedRooms()
+ fmt.Println("Joined rooms:")
+ for _, joinedRoom := range resp.JoinedRooms {
+ fmt.Println(joinedRoom)
+ }
+
+ gobStore, err := crypto.NewGobStore(gobStorePath)
+ if err != nil {
+ panic(err)
+ }
+ stateStore := &TallyardStateStore{mautrix.NewInMemoryStore()}
+ logger := CryptoMachineLogger{}
+ olmMachine = crypto.NewOlmMachine(client, logger, gobStore, stateStore)
+ if err = olmMachine.Load(); err != nil {
+ panic(err)
+ }
+ syncer.OnEventType(event.StateMember, func(_ mautrix.EventSource, evt *event.Event) {
+ olmMachine.HandleMemberEvent(evt)
+ })
+ syncer.OnSync(func(resp *mautrix.RespSync, since string) bool {
+ stateStore.UpdateStateStore(resp)
+ olmMachine.ProcessSyncResponse(resp, since)
+ if err := olmMachine.CryptoStore.Flush(); err != nil {
+ log.WithError(err).Error("Could not flush crypto store")
+ }
+ return true
+ });
+
+ err = client.Sync()
+ if err != nil {
+ panic(err)
+ }
}
A matrix/state_store.go => matrix/state_store.go +90 -0
@@ 0,0 1,90 @@
+package main
+
+import (
+ "errors"
+
+ "maunium.net/go/mautrix"
+ "maunium.net/go/mautrix/event"
+ "maunium.net/go/mautrix/id"
+)
+
+type TallyardStateStore struct {
+ Storer *mautrix.InMemoryStore
+}
+
+// GetEncryptionEvent returns the encryption event for a room.
+func (ss *TallyardStateStore) GetEncryptionEvent(roomID id.RoomID) *event.EncryptionEventContent {
+ room := ss.Storer.LoadRoom(roomID)
+ if room == nil {
+ return nil
+ }
+ if evts, ok := room.State[event.StateEncryption]; ok {
+ if evt, ok := evts[""]; ok {
+ return evt.Content.AsEncryption()
+ }
+ }
+ return nil
+}
+
+// IsEncrypted returns whether a room has been encrypted.
+func (ss *TallyardStateStore) IsEncrypted(roomID id.RoomID) bool {
+ room := ss.Storer.LoadRoom(roomID)
+ if room == nil {
+ return false
+ }
+ _, ok := room.State[event.StateEncryption]
+ return ok
+}
+
+// FindSharedRooms returns a list of room IDs that the given user ID is also a member of.
+func (ss *TallyardStateStore) FindSharedRooms(userID id.UserID) []id.RoomID {
+ sharedRooms := make([]id.RoomID, 0)
+ for roomID, room := range ss.Storer.Rooms {
+ if room.GetMembershipState(userID) != event.MembershipLeave {
+ sharedRooms = append(sharedRooms, roomID)
+ }
+ }
+ return sharedRooms
+}
+
+// UpdateStateStore updates the internal state of NebStateStore from a /sync response.
+func (ss *TallyardStateStore) UpdateStateStore(resp *mautrix.RespSync) {
+ for roomID, evts := range resp.Rooms.Join {
+ room := ss.Storer.LoadRoom(roomID)
+ if room == nil {
+ room = mautrix.NewRoom(roomID)
+ ss.Storer.SaveRoom(room)
+ }
+ for _, i := range evts.State.Events {
+ room.UpdateState(i)
+ }
+ for _, i := range evts.Timeline.Events {
+ if i.Type.IsState() {
+ room.UpdateState(i)
+ }
+ }
+ }
+}
+
+// GetJoinedMembers returns a list of members that are currently in a room.
+func (ss *TallyardStateStore) GetJoinedMembers(roomID id.RoomID) ([]id.UserID, error) {
+ joinedMembers := make([]id.UserID, 0)
+ room := ss.Storer.LoadRoom(roomID)
+ if room == nil {
+ return nil, errors.New("unknown roomID")
+ }
+ memberEvents := room.State[event.StateMember]
+ if memberEvents == nil {
+ return nil, errors.New("no state member events found")
+ }
+ for stateKey, stateEvent := range memberEvents {
+ if stateEvent == nil {
+ continue
+ }
+ stateEvent.Content.ParseRaw(event.StateMember)
+ if stateEvent.Content.AsMember().Membership == event.MembershipJoin {
+ joinedMembers = append(joinedMembers, id.UserID(stateKey))
+ }
+ }
+ return joinedMembers, nil
+}