package main import ( "crypto/rand" "encoding/base64" "encoding/json" "fmt" "strings" "unicode" log "github.com/sirupsen/logrus" "golang.org/x/crypto/nacl/secretbox" . "git.deuxfleurs.fr/Deuxfleurs/easybridge/connector" ) const EASYBRIDGE_SYSTEM_PROTOCOL string = "✯◡✯" func ezbrMxId() string { return fmt.Sprintf("@%s:%s", registration.SenderLocalpart, config.MatrixDomain) } func ezbrSystemRoom(user_mx_id string) (string, error) { mx_room_id, err := dbGetMxPmRoom(EASYBRIDGE_SYSTEM_PROTOCOL, UserID("Easybridge"), ezbrMxId(), user_mx_id, "easybridge") if err != nil { return "", err } widget_kv_key := "ezbr_widget_on:" + mx_room_id if config.WebURL != "" && dbKvGet(widget_kv_key) != "yes" { widget := map[string]interface{}{ "type": "m.easybridge", "url": config.WebURL, "name": "Easybridge account configuration dashboard", } err = mx.PutStateAs(mx_room_id, "im.vector.modular.widgets", "ezbr_widget", widget, ezbrMxId()) if err == nil { dbKvPut(widget_kv_key, "yes") } } return mx_room_id, nil } func ezbrSystemSend(user_mx_id string, msg string) { mx_room_id, err := ezbrSystemRoom(user_mx_id) if err == nil { err = mx.SendMessageAs(mx_room_id, "m.text", msg, ezbrMxId()) } if err != nil { log.Warnf("(%s) %s", user_mx_id, msg) } } func ezbrSystemSendf(user_mx_id string, format string, args ...interface{}) { ezbrSystemSend(user_mx_id, fmt.Sprintf(format, args...)) } // ---- func roomAlias(protocol string, id RoomID) string { what := fmt.Sprintf("%s_%s", safeStringForId(string(id)), protocol) return strings.Replace(config.NameFormat, "{}", what, 1) } func userMxId(protocol string, id UserID) string { what := fmt.Sprintf("%s_%s", safeStringForId(string(id)), protocol) return strings.Replace(config.NameFormat, "{}", what, 1) } func safeStringForId(in string) string { id2 := "" for _, c := range in { if c == '@' { id2 += "__" } else if c == ':' { id2 += "_" } else if unicode.IsDigit(c) || unicode.IsLetter(c) || c == '.' || c == '-' || c == '_' { id2 += string(c) } } 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 { bytes, err := json.Marshal(config) if err != nil { log.Fatal(err) } var nonce [24]byte _, err = rand.Read(nonce[:]) if err != nil { log.Fatal(err) } crypto := secretbox.Seal([]byte{}, bytes, &nonce, key) all := append(nonce[:], crypto...) return base64.StdEncoding.EncodeToString(all) } func decryptAccountConfig(data string, key *[32]byte) (map[string]string, error) { bytes, err := base64.StdEncoding.DecodeString(data) if err != nil { return nil, err } var nonce [24]byte copy(nonce[:], bytes[:24]) decoded, ok := secretbox.Open([]byte{}, bytes[24:], &nonce, key) if !ok { return nil, fmt.Errorf("Invalid key") } var config map[string]string err = json.Unmarshal(decoded, &config) return config, err }