// Copyright (c) 2022 Tulir Asokan // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package bridgeconfig import ( "fmt" "regexp" "strings" "maunium.net/go/mautrix/appservice" "maunium.net/go/mautrix/id" "maunium.net/go/mautrix/util" up "maunium.net/go/mautrix/util/configupgrade" ) type HomeserverSoftware string const ( SoftwareStandard HomeserverSoftware = "standard" SoftwareAsmux HomeserverSoftware = "asmux" SoftwareHungry HomeserverSoftware = "hungry" ) var AllowedHomeserverSoftware = map[HomeserverSoftware]bool{ SoftwareStandard: true, SoftwareAsmux: true, SoftwareHungry: true, } type HomeserverConfig struct { Address string `yaml:"address"` Domain string `yaml:"domain"` AsyncMedia bool `yaml:"async_media"` Software HomeserverSoftware `yaml:"software"` StatusEndpoint string `yaml:"status_endpoint"` MessageSendCheckpointEndpoint string `yaml:"message_send_checkpoint_endpoint"` WSProxy string `yaml:"websocket_proxy"` WSPingInterval int `yaml:"ping_interval_seconds"` } type AppserviceConfig struct { Address string `yaml:"address"` Hostname string `yaml:"hostname"` Port uint16 `yaml:"port"` Database DatabaseConfig `yaml:"database"` ID string `yaml:"id"` Bot BotUserConfig `yaml:"bot"` ASToken string `yaml:"as_token"` HSToken string `yaml:"hs_token"` EphemeralEvents bool `yaml:"ephemeral_events"` AsyncTransactions bool `yaml:"async_transactions"` } func (config *BaseConfig) MakeUserIDRegex(matcher string) *regexp.Regexp { usernamePlaceholder := strings.ToLower(util.RandomString(16)) usernameTemplate := fmt.Sprintf("@%s:%s", config.Bridge.FormatUsername(usernamePlaceholder), config.Homeserver.Domain) usernameTemplate = regexp.QuoteMeta(usernameTemplate) usernameTemplate = strings.Replace(usernameTemplate, usernamePlaceholder, matcher, 1) usernameTemplate = fmt.Sprintf("^%s$", usernameTemplate) return regexp.MustCompile(usernameTemplate) } // GenerateRegistration generates a registration file for the homeserver. func (config *BaseConfig) GenerateRegistration() *appservice.Registration { registration := appservice.CreateRegistration() config.AppService.HSToken = registration.ServerToken config.AppService.ASToken = registration.AppToken config.AppService.copyToRegistration(registration) registration.SenderLocalpart = util.RandomString(32) botRegex := regexp.MustCompile(fmt.Sprintf("^@%s:%s$", regexp.QuoteMeta(config.AppService.Bot.Username), regexp.QuoteMeta(config.Homeserver.Domain))) registration.Namespaces.UserIDs.Register(botRegex, true) registration.Namespaces.UserIDs.Register(config.MakeUserIDRegex(".*"), true) return registration } func (config *BaseConfig) MakeAppService() *appservice.AppService { as := appservice.Create() as.HomeserverDomain = config.Homeserver.Domain as.HomeserverURL = config.Homeserver.Address as.Host.Hostname = config.AppService.Hostname as.Host.Port = config.AppService.Port as.DefaultHTTPRetries = 4 as.Registration = config.AppService.GetRegistration() return as } // GetRegistration copies the data from the bridge config into an *appservice.Registration struct. // This can't be used with the homeserver, see GenerateRegistration for generating files for the homeserver. func (asc *AppserviceConfig) GetRegistration() *appservice.Registration { reg := &appservice.Registration{} asc.copyToRegistration(reg) reg.SenderLocalpart = asc.Bot.Username reg.ServerToken = asc.HSToken reg.AppToken = asc.ASToken return reg } func (asc *AppserviceConfig) copyToRegistration(registration *appservice.Registration) { registration.ID = asc.ID registration.URL = asc.Address falseVal := false registration.RateLimited = &falseVal registration.EphemeralEvents = asc.EphemeralEvents registration.SoruEphemeralEvents = asc.EphemeralEvents } type BotUserConfig struct { Username string `yaml:"username"` Displayname string `yaml:"displayname"` Avatar string `yaml:"avatar"` ParsedAvatar id.ContentURI `yaml:"-"` } type serializableBUC BotUserConfig func (buc *BotUserConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { var sbuc serializableBUC err := unmarshal(&sbuc) if err != nil { return err } *buc = (BotUserConfig)(sbuc) if buc.Avatar != "" && buc.Avatar != "remove" { buc.ParsedAvatar, err = id.ParseContentURI(buc.Avatar) if err != nil { return fmt.Errorf("%w in bot avatar", err) } } return nil } type DatabaseConfig struct { Type string `yaml:"type"` URI string `yaml:"uri"` MaxOpenConns int `yaml:"max_open_conns"` MaxIdleConns int `yaml:"max_idle_conns"` ConnMaxIdleTime string `yaml:"conn_max_idle_time"` ConnMaxLifetime string `yaml:"conn_max_lifetime"` } type BridgeConfig interface { FormatUsername(username string) string GetEncryptionConfig() EncryptionConfig GetCommandPrefix() string GetManagementRoomTexts() ManagementRoomTexts GetResendBridgeInfo() bool EnableMessageStatusEvents() bool EnableMessageErrorNotices() bool Validate() error } type EncryptionConfig struct { Allow bool `yaml:"allow"` Default bool `yaml:"default"` Require bool `yaml:"require"` Appservice bool `yaml:"appservice"` VerificationLevels struct { Receive id.TrustState `yaml:"receive"` Send id.TrustState `yaml:"send"` Share id.TrustState `yaml:"share"` } `yaml:"verification_levels"` AllowKeySharing bool `yaml:"allow_key_sharing"` Rotation struct { EnableCustom bool `yaml:"enable_custom"` Milliseconds int64 `yaml:"milliseconds"` Messages int `yaml:"messages"` } `yaml:"rotation"` } type ManagementRoomTexts struct { Welcome string `yaml:"welcome"` WelcomeConnected string `yaml:"welcome_connected"` WelcomeUnconnected string `yaml:"welcome_unconnected"` AdditionalHelp string `yaml:"additional_help"` } type BaseConfig struct { Homeserver HomeserverConfig `yaml:"homeserver"` AppService AppserviceConfig `yaml:"appservice"` Bridge BridgeConfig `yaml:"-"` Logging appservice.LogConfig `yaml:"logging"` } func doUpgrade(helper *up.Helper) { helper.Copy(up.Str, "homeserver", "address") helper.Copy(up.Str, "homeserver", "domain") if legacyAsmuxFlag, ok := helper.Get(up.Bool, "homeserver", "asmux"); ok && legacyAsmuxFlag == "true" { helper.Set(up.Str, string(SoftwareAsmux), "homeserver", "software") } else { helper.Copy(up.Str, "homeserver", "software") } helper.Copy(up.Str|up.Null, "homeserver", "status_endpoint") helper.Copy(up.Str|up.Null, "homeserver", "message_send_checkpoint_endpoint") helper.Copy(up.Bool, "homeserver", "async_media") helper.Copy(up.Str|up.Null, "homeserver", "websocket_proxy") helper.Copy(up.Int, "homeserver", "ping_interval_seconds") helper.Copy(up.Str, "appservice", "address") helper.Copy(up.Str, "appservice", "hostname") helper.Copy(up.Int, "appservice", "port") if dbType, ok := helper.Get(up.Str, "appservice", "database", "type"); ok && dbType == "sqlite3" { helper.Set(up.Str, "sqlite3-fk-wal", "appservice", "database", "type") } else { helper.Copy(up.Str, "appservice", "database", "type") } helper.Copy(up.Str, "appservice", "database", "uri") helper.Copy(up.Int, "appservice", "database", "max_open_conns") helper.Copy(up.Int, "appservice", "database", "max_idle_conns") helper.Copy(up.Str|up.Null, "appservice", "database", "max_conn_idle_time") helper.Copy(up.Str|up.Null, "appservice", "database", "max_conn_lifetime") helper.Copy(up.Str, "appservice", "id") helper.Copy(up.Str, "appservice", "bot", "username") helper.Copy(up.Str, "appservice", "bot", "displayname") helper.Copy(up.Str, "appservice", "bot", "avatar") helper.Copy(up.Bool, "appservice", "ephemeral_events") helper.Copy(up.Bool, "appservice", "async_transactions") helper.Copy(up.Str, "appservice", "as_token") helper.Copy(up.Str, "appservice", "hs_token") helper.Copy(up.Str, "logging", "directory") helper.Copy(up.Str|up.Null, "logging", "file_name_format") helper.Copy(up.Str|up.Timestamp, "logging", "file_date_format") helper.Copy(up.Int, "logging", "file_mode") helper.Copy(up.Str|up.Timestamp, "logging", "timestamp_format") helper.Copy(up.Str, "logging", "print_level") helper.Copy(up.Bool, "logging", "print_json") helper.Copy(up.Bool, "logging", "file_json") } // Upgrader is a config upgrader that copies the default fields in the homeserver, appservice and logging blocks. var Upgrader = up.SimpleUpgrader(doUpgrade)