diff --git a/account.go b/account.go index c17fb2f..6785fb7 100644 --- a/account.go +++ b/account.go @@ -2,6 +2,7 @@ package main import ( "fmt" + "reflect" "strings" "sync" @@ -15,22 +16,63 @@ type Account struct { AccountName string Protocol string Config map[string]string - Conn Connector + Conn Connector JoinedRooms map[RoomID]bool } var accountsLock sync.Mutex var registeredAccounts = map[string]map[string]*Account{} -func AddAccount(a *Account) { +func SetAccount(mxid string, name string, protocol string, config map[string]string) error { accountsLock.Lock() defer accountsLock.Unlock() - if _, ok := registeredAccounts[a.MatrixUser]; !ok { - registeredAccounts[a.MatrixUser] = make(map[string]*Account) + if _, ok := registeredAccounts[mxid]; !ok { + registeredAccounts[mxid] = make(map[string]*Account) } - registeredAccounts[a.MatrixUser][a.AccountName] = a + accounts := registeredAccounts[mxid] + + if prev_acct, ok := accounts[name]; ok { + if protocol != prev_acct.Protocol { + return fmt.Errorf("Wrong protocol") + } + if !reflect.DeepEqual(config, prev_acct.Config) { + prev_acct.Config = config + go prev_acct.connect() + } + } else { + conn := createConnector(protocol) + if conn == nil { + return fmt.Errorf("Could not create connector for protocol %s", protocol) + } + account := &Account{ + MatrixUser: mxid, + AccountName: name, + Protocol: protocol, + Config: config, + Conn: conn, + JoinedRooms: map[RoomID]bool{}, + } + conn.SetHandler(account) + + accounts[name] = account + go account.connect() + } + return nil +} + +func ListAccounts(mxUser string) []*Account { + accountsLock.Lock() + defer accountsLock.Unlock() + + ret := []*Account{} + if accts, ok := registeredAccounts[mxUser]; ok { + for _, acct := range accts { + ret = append(ret, acct) + } + } + return ret } func FindAccount(mxUser string, name string) *Account { @@ -70,6 +112,24 @@ func RemoveAccount(mxUser string, name string) { } } +func SaveDbAccounts(mxid string, key *[32]byte) { + accountsLock.Lock() + defer accountsLock.Unlock() + + if accounts, ok := registeredAccounts[mxid]; ok { + for name, acct := range accounts { + var entry DbAccountConfig + db.Where(&DbAccountConfig{ + MxUserID: mxid, + Name: name, + }).Assign(&DbAccountConfig{ + Protocol: acct.Protocol, + Config: encryptAccountConfig(acct.Config, key), + }).FirstOrCreate(&entry) + } + } +} + // ---- func (a *Account) ezbrMessagef(format string, args ...interface{}) { @@ -78,10 +138,10 @@ func (a *Account) ezbrMessagef(format string, args ...interface{}) { ezbrSystemSend(a.MatrixUser, msg) } -func (a *Account) connect(config map[string]string) { +func (a *Account) connect() { ezbrSystemSendf(a.MatrixUser, "Connecting to account %s (%s)", a.AccountName, a.Protocol) - err := a.Conn.Configure(config) + err := a.Conn.Configure(a.Config) if err != nil { ezbrSystemSendf(a.MatrixUser, "%s (%s) cannot connect: %s", a.AccountName, a.Protocol, err.Error()) return diff --git a/main.go b/main.go index a8a349c..5448e89 100644 --- a/main.go +++ b/main.go @@ -15,10 +15,6 @@ import ( log "github.com/sirupsen/logrus" "gopkg.in/yaml.v2" - "git.deuxfleurs.fr/Deuxfleurs/easybridge/connector" - "git.deuxfleurs.fr/Deuxfleurs/easybridge/connector/irc" - "git.deuxfleurs.fr/Deuxfleurs/easybridge/connector/mattermost" - "git.deuxfleurs.fr/Deuxfleurs/easybridge/connector/xmpp" "git.deuxfleurs.fr/Deuxfleurs/easybridge/mxlib" ) @@ -174,22 +170,9 @@ func main() { StartWeb() for user, accounts := range config.Accounts { + mxid := fmt.Sprintf("@%s:%s", user, config.MatrixDomain) for name, params := range accounts { - conn := createConnector(params.Protocol) - if conn == nil { - log.Fatalf("Could not create connector for protocol %s", params.Protocol) - } - account := &Account{ - MatrixUser: fmt.Sprintf("@%s:%s", user, config.MatrixDomain), - AccountName: name, - Protocol: params.Protocol, - Config: params.Config, - Conn: conn, - JoinedRooms: map[connector.RoomID]bool{}, - } - conn.SetHandler(account) - AddAccount(account) - go account.connect(params.Config) + SetAccount(mxid, name, params.Protocol, params.Config) } } @@ -198,16 +181,3 @@ func main() { log.Fatal(err) } } - -func createConnector(protocol string) connector.Connector { - switch protocol { - case "irc": - return &irc.IRC{} - case "xmpp": - return &xmpp.XMPP{} - case "mattermost": - return &mattermost.Mattermost{} - default: - return nil - } -} diff --git a/util.go b/util.go index 323f99d..6193d4d 100644 --- a/util.go +++ b/util.go @@ -11,6 +11,9 @@ import ( "golang.org/x/crypto/nacl/secretbox" . "git.deuxfleurs.fr/Deuxfleurs/easybridge/connector" + "git.deuxfleurs.fr/Deuxfleurs/easybridge/connector/irc" + "git.deuxfleurs.fr/Deuxfleurs/easybridge/connector/mattermost" + "git.deuxfleurs.fr/Deuxfleurs/easybridge/connector/xmpp" ) const EASYBRIDGE_SYSTEM_PROTOCOL string = "✯◡✯" @@ -98,3 +101,18 @@ func decryptAccountConfig(data string, key *[32]byte) (map[string]string, error) err = json.Unmarshal(decoded, &config) return config, err } + +// ---- + +func createConnector(protocol string) Connector { + switch protocol { + case "irc": + return &irc.IRC{} + case "xmpp": + return &xmpp.XMPP{} + case "mattermost": + return &mattermost.Mattermost{} + default: + return nil + } +} diff --git a/web.go b/web.go index bff8acc..74dd1f8 100644 --- a/web.go +++ b/web.go @@ -11,7 +11,6 @@ import ( "github.com/gorilla/sessions" "golang.org/x/crypto/argon2" - "git.deuxfleurs.fr/Deuxfleurs/easybridge/connector" "git.deuxfleurs.fr/Deuxfleurs/easybridge/mxlib" ) @@ -81,7 +80,7 @@ func checkLogin(w http.ResponseWriter, r *http.Request) *LoginInfo { type HomeData struct { Login *LoginInfo - Accounts map[string]*Account + Accounts []*Account } func handleHome(w http.ResponseWriter, r *http.Request) { @@ -92,11 +91,9 @@ func handleHome(w http.ResponseWriter, r *http.Request) { return } - accountsLock.Lock() - defer accountsLock.Unlock() templateHome.Execute(w, &HomeData{ Login: login, - Accounts: registeredAccounts[login.MxId], + Accounts: ListAccounts(login.MxId), }) } @@ -154,7 +151,9 @@ func handleLogin(w http.ResponseWriter, r *http.Request) *LoginInfo { key_slice := argon2.IDKey([]byte(password), []byte("EZBRIDGE account store"), 3, 64*1024, 4, 32) copy(key[:], key_slice[:]) userKeys[mxid] = key - syncDbAccounts(mxid, key) + + SaveDbAccounts(mxid, key) + LoadDbAccounts(mxid, key) // Successfully logged in, save it to session session, err := sessionsStore.Get(r, SESSION_NAME) @@ -179,57 +178,19 @@ func handleLogin(w http.ResponseWriter, r *http.Request) *LoginInfo { } } -func syncDbAccounts(mxid string, key *[32]byte) { - accountsLock.Lock() - defer accountsLock.Unlock() - - // 1. Save all accounts that we have - var accounts map[string]*Account - if accts, ok := registeredAccounts[mxid]; ok { - accounts = accts - for name, acct := range accts { - var entry DbAccountConfig - db.Where(&DbAccountConfig{ - MxUserID: mxid, - Name: name, - }).Assign(&DbAccountConfig{ - Protocol: acct.Protocol, - Config: encryptAccountConfig(acct.Config, key), - }).FirstOrCreate(&entry) - } - } else { - accounts = make(map[string]*Account) - registeredAccounts[mxid] = accounts - } - - // 2. Load and start missing accounts +func LoadDbAccounts(mxid string, key *[32]byte) { var allAccounts []DbAccountConfig db.Where(&DbAccountConfig{MxUserID: mxid}).Find(&allAccounts) for _, acct := range allAccounts { - if _, ok := accounts[acct.Name]; !ok { - config, err := decryptAccountConfig(acct.Config, key) - if err != nil { - ezbrSystemSendf("Could not decrypt stored configuration for account %s", acct.Name) - continue - } - conn := createConnector(acct.Protocol) - if conn == nil { - ezbrSystemSendf("Could not create connector for protocol %s", acct.Protocol) - continue - } - account := &Account{ - MatrixUser: mxid, - AccountName: acct.Name, - Protocol: acct.Protocol, - Config: config, - Conn: conn, - JoinedRooms: map[connector.RoomID]bool{}, - } - conn.SetHandler(account) + config, err := decryptAccountConfig(acct.Config, key) + if err != nil { + ezbrSystemSendf("Could not decrypt stored configuration for account %s", acct.Name) + continue + } - accounts[acct.Name] = account - - go account.connect(config) + err = SetAccount(mxid, acct.Name, acct.Protocol, config) + if err != nil { + ezbrSystemSendf("Could not setup account %s: %s", acct.Name, err.Error()) } } }