253 lines
8.4 KiB
Go
253 lines
8.4 KiB
Go
// 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)
|