Files
mautrix-discord/bridge/bridge.go
Gary Kramlich c5f58afe71 End to bridge encryption implementation
So far this is passing my basic tests, but could use some testing from people
that are much more familiar with how this is supposed to work.

Refs #27
2022-04-27 12:43:12 -05:00

204 lines
4.1 KiB
Go

package bridge
import (
"errors"
"fmt"
"sync"
"time"
log "maunium.net/go/maulogger/v2"
"maunium.net/go/mautrix"
"maunium.net/go/mautrix/appservice"
"maunium.net/go/mautrix/id"
"gitlab.com/beeper/discord/config"
"gitlab.com/beeper/discord/database"
"gitlab.com/beeper/discord/version"
)
const (
reconnectDelay = 10 * time.Second
)
type Bridge struct {
Config *config.Config
log log.Logger
as *appservice.AppService
db *database.Database
eventProcessor *appservice.EventProcessor
matrixHandler *matrixHandler
bot *appservice.IntentAPI
provisioning *ProvisioningAPI
usersByMXID map[id.UserID]*User
usersByID map[string]*User
usersLock sync.Mutex
managementRooms map[id.RoomID]*User
managementRoomsLock sync.Mutex
portalsByMXID map[id.RoomID]*Portal
portalsByID map[database.PortalKey]*Portal
portalsLock sync.Mutex
puppets map[string]*Puppet
puppetsByCustomMXID map[id.UserID]*Puppet
puppetsLock sync.Mutex
StateStore *database.SQLStateStore
crypto Crypto
}
func New(cfg *config.Config) (*Bridge, error) {
// Create the logger.
logger, err := cfg.CreateLogger()
if err != nil {
return nil, err
}
logger.Infoln("Initializing version", version.String)
// Create and initialize the app service.
appservice, err := cfg.CreateAppService()
if err != nil {
return nil, err
}
appservice.Log = log.Sub("matrix")
appservice.Init()
// Create the bot.
bot := appservice.BotIntent()
// Setup the database.
db, err := cfg.CreateDatabase(logger)
if err != nil {
return nil, err
}
// Create the state store
logger.Debugln("Initializing state store")
stateStore := database.NewSQLStateStore(db)
appservice.StateStore = stateStore
// Create the bridge.
bridge := &Bridge{
as: appservice,
db: db,
bot: bot,
Config: cfg,
log: logger,
usersByMXID: make(map[id.UserID]*User),
usersByID: make(map[string]*User),
managementRooms: make(map[id.RoomID]*User),
portalsByMXID: make(map[id.RoomID]*Portal),
portalsByID: make(map[database.PortalKey]*Portal),
puppets: make(map[string]*Puppet),
puppetsByCustomMXID: make(map[id.UserID]*Puppet),
StateStore: stateStore,
}
bridge.crypto = NewCryptoHelper(bridge)
if cfg.Appservice.Provisioning.Enabled() {
bridge.provisioning = newProvisioningAPI(bridge)
}
// Setup the event processors
bridge.setupEvents()
return bridge, nil
}
func (b *Bridge) connect() error {
b.log.Debugln("Checking connection to homeserver")
for {
resp, err := b.bot.Whoami()
if err != nil {
if errors.Is(err, mautrix.MUnknownToken) {
b.log.Fatalln("Access token invalid. Is the registration installed in your homeserver correctly?")
return fmt.Errorf("invalid access token")
}
b.log.Errorfln("Failed to connect to homeserver : %v", err)
b.log.Errorfln("reconnecting in %s", reconnectDelay)
time.Sleep(reconnectDelay)
} else if resp.UserID != b.bot.UserID {
b.log.Fatalln("Unexpected user ID in whoami call: got %s, expected %s", resp.UserID, b.bot.UserID)
return fmt.Errorf("expected user id %q but got %q", b.bot.UserID, resp.UserID)
} else {
break
}
}
b.log.Debugln("Connected to homeserver")
return nil
}
func (b *Bridge) Start() error {
b.log.Infoln("Bridge started")
if err := b.connect(); err != nil {
return err
}
if b.crypto != nil {
if err := b.crypto.Init(); err != nil {
b.log.Fatalln("Error initializing end-to-bridge encryption:", err)
return err
}
}
b.log.Debugln("Starting application service HTTP server")
go b.as.Start()
b.log.Debugln("Starting event processor")
go b.eventProcessor.Start()
go b.updateBotProfile()
if b.crypto != nil {
go b.crypto.Start()
}
go b.startUsers()
// Finally tell the appservice we're ready
b.as.Ready = true
return nil
}
func (b *Bridge) Stop() {
if b.crypto != nil {
b.crypto.Stop()
}
b.as.Stop()
b.eventProcessor.Stop()
for _, user := range b.usersByMXID {
if user.Session == nil {
continue
}
b.log.Debugln("Disconnecting", user.MXID)
user.Session.Close()
}
b.log.Infoln("Bridge stopped")
}