| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766 |
- package main
- import (
- "context"
- "errors"
- "fmt"
- "log/slog"
- "math/rand"
- "os"
- "strings"
- "time"
- "github.com/kelseyhightower/envconfig"
- "golang.org/x/time/rate"
- "github.com/mk6i/open-oscar-server/config"
- "github.com/mk6i/open-oscar-server/foodgroup"
- "github.com/mk6i/open-oscar-server/server/http"
- "github.com/mk6i/open-oscar-server/server/icq_legacy"
- "github.com/mk6i/open-oscar-server/server/kerberos"
- "github.com/mk6i/open-oscar-server/server/oscar"
- oscarmiddleware "github.com/mk6i/open-oscar-server/server/oscar/middleware"
- "github.com/mk6i/open-oscar-server/server/toc"
- "github.com/mk6i/open-oscar-server/server/webapi"
- "github.com/mk6i/open-oscar-server/server/webapi/handlers"
- "github.com/mk6i/open-oscar-server/state"
- "github.com/mk6i/open-oscar-server/wire"
- )
- // Container groups together common dependencies.
- type Container struct {
- cfg config.Config
- chatSessionManager *state.InMemoryChatSessionManager
- hmacCookieBaker state.HMACCookieBaker
- icbmSvc *foodgroup.ICBMService
- inMemorySessionManager *state.InMemorySessionManager
- logger *slog.Logger
- rateLimitClasses wire.RateLimitClasses
- snacRateLimits wire.SNACRateLimits
- sqLiteUserStore *state.SQLiteUserStore
- webAPISessionManager *state.WebAPISessionManager
- Listeners []config.Listener
- feedbagSvc *foodgroup.FeedbagService
- icqService *foodgroup.ICQService
- }
- // MakeCommonDeps creates common dependencies used by the food group services.
- func MakeCommonDeps() (Container, error) {
- c := Container{}
- if err := validateConfigMigration(); err != nil {
- return c, fmt.Errorf("unable to validate config migration: %s", err.Error())
- }
- err := envconfig.Process("", &c.cfg)
- if err != nil {
- return c, fmt.Errorf("unable to process app config: %s", err.Error())
- }
- if err := c.cfg.Validate(); err != nil {
- return c, fmt.Errorf("configuration validation failed: %s", err.Error())
- }
- c.Listeners, err = c.cfg.ParseListenersCfg()
- if err != nil {
- return c, fmt.Errorf("unable to parse listener config: %s", err.Error())
- }
- c.sqLiteUserStore, err = state.NewSQLiteUserStore(c.cfg.DBPath)
- if err != nil {
- return c, fmt.Errorf("unable to create feedbag store: %s", err.Error())
- }
- c.hmacCookieBaker, err = state.NewHMACCookieBaker()
- if err != nil {
- return c, fmt.Errorf("unable to create HMAC cookie baker: %s", err.Error())
- }
- c.logger = oscarmiddleware.NewLogger(c.cfg)
- c.inMemorySessionManager = state.NewInMemorySessionManager(c.logger)
- c.chatSessionManager = state.NewInMemoryChatSessionManager(c.logger)
- c.webAPISessionManager = state.NewWebAPISessionManager()
- c.rateLimitClasses = wire.DefaultRateLimitClasses()
- c.snacRateLimits = wire.DefaultSNACRateLimits()
- c.feedbagSvc = foodgroup.NewFeedbagService(
- c.logger,
- c.inMemorySessionManager,
- c.sqLiteUserStore,
- c.sqLiteUserStore,
- c.sqLiteUserStore,
- c.inMemorySessionManager,
- c.sqLiteUserStore,
- c.sqLiteUserStore,
- c.sqLiteUserStore,
- )
- c.icbmSvc = foodgroup.NewICBMService(
- c.sqLiteUserStore,
- c.inMemorySessionManager,
- c.sqLiteUserStore,
- c.sqLiteUserStore,
- c.inMemorySessionManager,
- c.sqLiteUserStore,
- c.sqLiteUserStore,
- c.sqLiteUserStore,
- c.snacRateLimits,
- c.logger,
- )
- c.icqService = foodgroup.NewICQService(
- c.inMemorySessionManager,
- c.sqLiteUserStore,
- c.sqLiteUserStore,
- c.logger,
- c.inMemorySessionManager,
- c.sqLiteUserStore,
- )
- c.feedbagSvc.BridgeICBMService(c.icbmSvc)
- c.icbmSvc.BridgeFeedbagService(c.feedbagSvc)
- c.icqService.BridgeFeedbagService(c.feedbagSvc)
- return c, nil
- }
- func validateConfigMigration() error {
- // Old environment variables that should be removed
- oldEnvVars := []string{
- "API_HOST",
- "API_PORT",
- "KERBEROS_PORT",
- "ALERT_PORT",
- "AUTH_PORT",
- "BART_PORT",
- "BOS_PORT",
- "CHAT_NAV_PORT",
- "CHAT_PORT",
- "ADMIN_PORT",
- "ODIR_PORT",
- "OSCAR_HOST",
- "TOC_HOST",
- "TOC_PORT",
- }
- // New environment variables that should be present
- newEnvVars := []string{
- "API_LISTENER",
- "OSCAR_ADVERTISED_LISTENERS_PLAIN",
- "OSCAR_LISTENERS",
- "TOC_LISTENERS",
- }
- var oldEnvVarsFound []string
- var newEnvVarsMissing []string
- // Check for old environment variables that should be removed
- for _, envVar := range oldEnvVars {
- if os.Getenv(envVar) != "" {
- oldEnvVarsFound = append(oldEnvVarsFound, envVar)
- }
- }
- // Check for new environment variables that should be present
- for _, envVar := range newEnvVars {
- if os.Getenv(envVar) == "" {
- newEnvVarsMissing = append(newEnvVarsMissing, envVar)
- }
- }
- // If there are any issues, return an error with details
- if len(oldEnvVarsFound) > 0 || len(newEnvVarsMissing) > 0 {
- var errorMsg strings.Builder
- errorMsg.WriteString("Open OSCAR Server v0.19.0 introduced some breaking configuration changes that you need to fix.\n")
- if len(oldEnvVarsFound) > 0 {
- errorMsg.WriteString("\nOld environment variables that must be removed:\n\n")
- for _, envVar := range oldEnvVarsFound {
- fmt.Fprintf(&errorMsg, " - %s\n", envVar)
- }
- }
- if len(newEnvVarsMissing) > 0 {
- errorMsg.WriteString("\nNew environment variables that must be provided:\n\n")
- for _, envVar := range newEnvVarsMissing {
- fmt.Fprintf(&errorMsg, " - %s\n", envVar)
- }
- // Generate export commands based on old environment variables
- errorMsg.WriteString("\nCopy/paste this updated configuration into your settings file:\n\n")
- if contains(newEnvVarsMissing, "API_LISTENER") {
- apiHost := getEnvOrDefault("API_HOST", "127.0.0.1")
- apiPort := getEnvOrDefault("API_PORT", "8080")
- fmt.Fprintf(&errorMsg, "export API_LISTENER=%s:%s\n", apiHost, apiPort)
- }
- if contains(newEnvVarsMissing, "OSCAR_ADVERTISED_LISTENERS_PLAIN") {
- oscarHost := getEnvOrDefault("OSCAR_HOST", "127.0.0.1")
- authPort := getEnvOrDefault("AUTH_PORT", "5190")
- fmt.Fprintf(&errorMsg, "export OSCAR_ADVERTISED_LISTENERS_PLAIN=LOCAL://%s:%s\n", oscarHost, authPort)
- }
- if contains(newEnvVarsMissing, "OSCAR_LISTENERS") {
- authPort := getEnvOrDefault("AUTH_PORT", "5190")
- fmt.Fprintf(&errorMsg, "export OSCAR_LISTENERS=LOCAL://0.0.0.0:%s\n", authPort)
- }
- if contains(newEnvVarsMissing, "KERBEROS_LISTENERS") {
- kerberosPort := getEnvOrDefault("KERBEROS_PORT", "1088")
- fmt.Fprintf(&errorMsg, "export KERBEROS_LISTENERS=LOCAL://0.0.0.0:%s\n", kerberosPort)
- }
- if contains(newEnvVarsMissing, "TOC_LISTENERS") {
- tocHost := getEnvOrDefault("TOC_HOST", "0.0.0.0")
- tocPort := getEnvOrDefault("TOC_PORT", "9898")
- fmt.Fprintf(&errorMsg, "export TOC_LISTENERS=%s:%s\n", tocHost, tocPort)
- }
- }
- return errors.New(errorMsg.String())
- }
- return nil
- }
- // Helper function to check if a slice contains a string
- func contains(slice []string, item string) bool {
- for _, s := range slice {
- if s == item {
- return true
- }
- }
- return false
- }
- // Helper function to get environment variable or return default
- func getEnvOrDefault(key, defaultValue string) string {
- if value := os.Getenv(key); value != "" {
- return value
- }
- return defaultValue
- }
- // OSCAR creates an OSCAR server for the OSCAR food group.
- func OSCAR(deps Container) *oscar.Server {
- logger := deps.logger.With("svc", "OSCAR")
- adminService := foodgroup.NewAdminService(
- deps.sqLiteUserStore,
- deps.sqLiteUserStore,
- deps.sqLiteUserStore,
- deps.inMemorySessionManager,
- deps.inMemorySessionManager,
- deps.logger,
- )
- authService := foodgroup.NewAuthService(
- deps.cfg,
- deps.inMemorySessionManager,
- deps.inMemorySessionManager,
- deps.chatSessionManager,
- deps.sqLiteUserStore,
- deps.hmacCookieBaker,
- deps.chatSessionManager,
- deps.sqLiteUserStore,
- deps.sqLiteUserStore,
- deps.sqLiteUserStore,
- deps.rateLimitClasses,
- state.NewAccountCreator(deps.sqLiteUserStore.InsertUser),
- logger,
- )
- bartService := foodgroup.NewBARTService(
- logger,
- deps.sqLiteUserStore,
- deps.inMemorySessionManager,
- deps.sqLiteUserStore,
- deps.inMemorySessionManager,
- )
- buddyService := foodgroup.NewBuddyService(
- deps.inMemorySessionManager,
- deps.sqLiteUserStore,
- deps.sqLiteUserStore,
- deps.inMemorySessionManager,
- deps.sqLiteUserStore,
- deps.sqLiteUserStore,
- )
- chatService := foodgroup.NewChatService(deps.chatSessionManager)
- chatNavService := foodgroup.NewChatNavService(logger, deps.sqLiteUserStore)
- permitDenyService := foodgroup.NewPermitDenyService(
- deps.sqLiteUserStore,
- deps.sqLiteUserStore,
- deps.sqLiteUserStore,
- deps.inMemorySessionManager,
- deps.inMemorySessionManager,
- )
- locateService := foodgroup.NewLocateService(
- deps.sqLiteUserStore,
- deps.inMemorySessionManager,
- deps.sqLiteUserStore,
- deps.sqLiteUserStore,
- deps.inMemorySessionManager,
- deps.sqLiteUserStore,
- )
- oServiceService := foodgroup.NewOServiceService(
- deps.cfg,
- deps.inMemorySessionManager,
- logger,
- deps.hmacCookieBaker,
- deps.sqLiteUserStore,
- deps.sqLiteUserStore,
- deps.inMemorySessionManager,
- deps.sqLiteUserStore,
- deps.snacRateLimits,
- deps.chatSessionManager,
- deps.sqLiteUserStore,
- deps.sqLiteUserStore,
- deps.sqLiteUserStore,
- )
- userLookupService := foodgroup.NewUserLookupService(deps.sqLiteUserStore)
- statsService := foodgroup.NewStatsService()
- oDirService := foodgroup.NewODirService(logger, deps.sqLiteUserStore)
- if err := deps.sqLiteUserStore.ClearBuddyListRegistry(context.Background()); err != nil {
- panic(err)
- }
- return oscar.NewServer(
- authService,
- deps.sqLiteUserStore,
- deps.chatSessionManager,
- buddyService,
- logger,
- oServiceService,
- oscar.Handler{
- AdminService: adminService,
- BARTService: bartService,
- BuddyService: buddyService,
- ChatNavService: chatNavService,
- ChatService: chatService,
- FeedbagService: deps.feedbagSvc,
- ICBMService: deps.icbmSvc,
- ICQService: deps.icqService,
- LocateService: locateService,
- ODirService: oDirService,
- OServiceService: oServiceService,
- PermitDenyService: permitDenyService,
- StatsService: statsService,
- UserLookupService: userLookupService,
- RouteLogger: oscarmiddleware.RouteLogger{
- Logger: logger,
- },
- }.Handle,
- oServiceService,
- deps.snacRateLimits,
- oscar.NewIPRateLimiter(rate.Every(1*time.Minute), 10, 1*time.Minute),
- deps.Listeners,
- deps.icbmSvc.RestoreWarningLevel,
- deps.icbmSvc.UpdateWarnLevel,
- )
- }
- // KerberosAPI creates an HTTP server for the Kerberos server.
- func KerberosAPI(deps Container) *kerberos.Server {
- logger := deps.logger.With("svc", "Kerberos")
- authService := foodgroup.NewAuthService(
- deps.cfg,
- deps.inMemorySessionManager,
- deps.inMemorySessionManager,
- deps.chatSessionManager,
- deps.sqLiteUserStore,
- deps.hmacCookieBaker,
- deps.chatSessionManager,
- deps.sqLiteUserStore,
- deps.sqLiteUserStore,
- deps.sqLiteUserStore,
- deps.rateLimitClasses,
- state.NewAccountCreator(deps.sqLiteUserStore.InsertUser),
- logger,
- )
- return kerberos.NewKerberosServer(deps.Listeners, logger, authService)
- }
- // MgmtAPI creates an HTTP server for the management API.
- func MgmtAPI(deps Container) *http.Server {
- bld := config.Build{
- Version: version,
- Commit: commit,
- Date: date,
- }
- logger := deps.logger.With("svc", "API")
- buddyService := foodgroup.NewBuddyService(
- deps.inMemorySessionManager,
- deps.sqLiteUserStore,
- deps.sqLiteUserStore,
- deps.inMemorySessionManager,
- deps.sqLiteUserStore,
- deps.sqLiteUserStore,
- )
- return http.NewManagementAPI(
- bld,
- deps.cfg.APIListener,
- deps.sqLiteUserStore, // userManager
- deps.inMemorySessionManager, // sessionRetriever
- buddyService,
- deps.sqLiteUserStore, // chatRoomRetriever
- deps.sqLiteUserStore, // chatRoomCreator
- deps.sqLiteUserStore, // chatRoomDeleter
- deps.chatSessionManager, // chatSessionRetriever
- deps.sqLiteUserStore, // directoryManager
- deps.inMemorySessionManager, // messageRelayer
- deps.sqLiteUserStore, // bartAssetManager
- deps.sqLiteUserStore, // feedbagRetriever
- deps.sqLiteUserStore, // feedbagManager
- deps.sqLiteUserStore, // accountManager
- deps.sqLiteUserStore, // profileRetriever
- deps.sqLiteUserStore, // webAPIKeyManager
- deps.sqLiteUserStore, // icqProfileManager
- state.NewAccountCreator(deps.sqLiteUserStore.InsertUser),
- logger,
- )
- }
- // TOC creates a TOC server.
- func TOC(deps Container) *toc.Server {
- logger := deps.logger.With("svc", "TOC")
- return toc.NewServer(
- deps.cfg.TOCListeners,
- logger,
- toc.OSCARProxy{
- AdminService: foodgroup.NewAdminService(
- deps.sqLiteUserStore,
- deps.sqLiteUserStore,
- deps.sqLiteUserStore,
- deps.inMemorySessionManager,
- deps.inMemorySessionManager,
- deps.logger,
- ),
- AuthService: foodgroup.NewAuthService(
- deps.cfg,
- deps.inMemorySessionManager,
- deps.inMemorySessionManager,
- deps.chatSessionManager,
- deps.sqLiteUserStore,
- deps.hmacCookieBaker,
- deps.chatSessionManager,
- deps.sqLiteUserStore,
- deps.sqLiteUserStore,
- deps.sqLiteUserStore,
- deps.rateLimitClasses,
- state.NewAccountCreator(deps.sqLiteUserStore.InsertUser),
- logger,
- ),
- BuddyListRegistry: deps.sqLiteUserStore,
- BuddyService: foodgroup.NewBuddyService(
- deps.inMemorySessionManager,
- deps.sqLiteUserStore,
- deps.sqLiteUserStore,
- deps.inMemorySessionManager,
- deps.sqLiteUserStore,
- deps.sqLiteUserStore,
- ),
- ChatSessionManager: deps.chatSessionManager,
- CookieBaker: deps.hmacCookieBaker,
- DirSearchService: foodgroup.NewODirService(logger, deps.sqLiteUserStore),
- ICBMService: deps.icbmSvc,
- LocateService: foodgroup.NewLocateService(
- deps.sqLiteUserStore,
- deps.inMemorySessionManager,
- deps.sqLiteUserStore,
- deps.sqLiteUserStore,
- deps.inMemorySessionManager,
- deps.sqLiteUserStore,
- ),
- Logger: logger,
- OServiceService: foodgroup.NewOServiceService(
- deps.cfg,
- deps.inMemorySessionManager,
- logger,
- deps.hmacCookieBaker,
- deps.sqLiteUserStore,
- deps.sqLiteUserStore,
- deps.inMemorySessionManager,
- deps.sqLiteUserStore,
- deps.snacRateLimits,
- deps.chatSessionManager,
- deps.sqLiteUserStore,
- deps.sqLiteUserStore,
- deps.sqLiteUserStore,
- ),
- PermitDenyService: foodgroup.NewPermitDenyService(
- deps.sqLiteUserStore,
- deps.sqLiteUserStore,
- deps.sqLiteUserStore,
- deps.inMemorySessionManager,
- deps.inMemorySessionManager,
- ),
- TOCConfigStore: deps.sqLiteUserStore,
- ChatService: foodgroup.NewChatService(deps.chatSessionManager),
- ChatNavService: foodgroup.NewChatNavService(logger, deps.sqLiteUserStore),
- FeedbagManager: deps.sqLiteUserStore,
- FeedbagService: deps.feedbagSvc,
- SNACRateLimits: deps.snacRateLimits,
- HTTPIPRateLimiter: toc.NewIPRateLimiter(rate.Every(1*time.Minute), 10, 1*time.Minute),
- SessionRetriever: deps.inMemorySessionManager,
- RandIntn: rand.Intn,
- },
- toc.NewIPRateLimiter(rate.Every(1*time.Minute), 10, 1*time.Minute),
- deps.icbmSvc.RestoreWarningLevel,
- deps.icbmSvc.UpdateWarnLevel,
- )
- }
- // WebAPI creates an HTTP server for the webapi protocol.
- func WebAPI(deps Container) *webapi.Server {
- logger := deps.logger.With("svc", "webapi")
- // Create feedbag adapter for WebAPI
- feedbagAdapter := &webapi.FeedbagAdapter{
- Store: deps.sqLiteUserStore,
- }
- // Create WebAPI buddy list manager (local to WebAPI)
- buddyListManager := handlers.NewBuddyListManager(
- deps.feedbagSvc,
- deps.inMemorySessionManager,
- logger,
- )
- // Create the OSCAR buddy broadcaster for WebAPI to use
- oscarBuddyBroadcaster := foodgroup.NewBuddyService(
- deps.inMemorySessionManager,
- deps.sqLiteUserStore,
- deps.sqLiteUserStore,
- deps.inMemorySessionManager,
- deps.sqLiteUserStore,
- deps.sqLiteUserStore,
- )
- handler := webapi.Handler{
- AdminService: foodgroup.NewAdminService(
- deps.sqLiteUserStore,
- deps.sqLiteUserStore,
- deps.sqLiteUserStore,
- deps.inMemorySessionManager,
- deps.inMemorySessionManager,
- deps.logger,
- ),
- AuthService: foodgroup.NewAuthService(
- deps.cfg,
- deps.inMemorySessionManager,
- deps.inMemorySessionManager,
- deps.chatSessionManager,
- deps.sqLiteUserStore,
- deps.hmacCookieBaker,
- deps.chatSessionManager,
- deps.sqLiteUserStore,
- deps.sqLiteUserStore,
- deps.sqLiteUserStore,
- deps.rateLimitClasses,
- state.NewAccountCreator(deps.sqLiteUserStore.InsertUser),
- logger,
- ),
- BuddyListRegistry: deps.sqLiteUserStore,
- BuddyService: foodgroup.NewBuddyService(
- deps.inMemorySessionManager,
- deps.sqLiteUserStore,
- deps.sqLiteUserStore,
- deps.inMemorySessionManager,
- deps.sqLiteUserStore,
- deps.sqLiteUserStore,
- ),
- CookieBaker: deps.hmacCookieBaker,
- DirSearchService: foodgroup.NewODirService(logger, deps.sqLiteUserStore),
- ICBMService: deps.icbmSvc,
- LocateService: foodgroup.NewLocateService(
- deps.sqLiteUserStore,
- deps.inMemorySessionManager,
- deps.sqLiteUserStore,
- deps.sqLiteUserStore,
- deps.inMemorySessionManager,
- deps.sqLiteUserStore,
- ),
- Logger: logger,
- OServiceService: foodgroup.NewOServiceService(
- deps.cfg,
- deps.inMemorySessionManager,
- logger,
- deps.hmacCookieBaker,
- deps.sqLiteUserStore,
- deps.sqLiteUserStore,
- deps.inMemorySessionManager,
- deps.sqLiteUserStore,
- deps.snacRateLimits,
- deps.chatSessionManager,
- deps.sqLiteUserStore,
- deps.sqLiteUserStore,
- deps.sqLiteUserStore,
- ),
- PermitDenyService: foodgroup.NewPermitDenyService(
- deps.sqLiteUserStore,
- deps.sqLiteUserStore,
- deps.sqLiteUserStore,
- deps.inMemorySessionManager,
- deps.inMemorySessionManager,
- ),
- TOCConfigStore: deps.sqLiteUserStore,
- ChatService: foodgroup.NewChatService(deps.chatSessionManager),
- ChatNavService: foodgroup.NewChatNavService(logger, deps.sqLiteUserStore),
- SNACRateLimits: deps.snacRateLimits,
- // New fields for WebAPI handlers
- SessionRetriever: deps.inMemorySessionManager,
- FeedbagRetriever: feedbagAdapter,
- FeedbagManager: feedbagAdapter,
- // Phase 2 additions
- MessageRelayer: deps.inMemorySessionManager,
- OfflineMessageManager: deps.sqLiteUserStore,
- BuddyBroadcaster: oscarBuddyBroadcaster,
- ProfileManager: deps.sqLiteUserStore,
- RelationshipFetcher: deps.sqLiteUserStore,
- // Phase 3 additions
- PreferenceManager: deps.sqLiteUserStore.NewWebPreferenceManager(),
- PermitDenyManager: deps.sqLiteUserStore.NewWebPermitDenyManager(),
- // Phase 4 additions for OSCAR Bridge
- OSCARBridgeStore: deps.sqLiteUserStore.NewOSCARBridgeStore(),
- OSCARConfig: webapi.NewOSCARConfigAdapter(deps.cfg),
- // Phase 5 additions for buddy list and messaging
- BuddyListManager: buddyListManager,
- // Phase 5 additions for chat rooms
- ChatManager: deps.sqLiteUserStore.NewWebAPIChatManager(logger, deps.webAPISessionManager),
- ChatSessionManager: deps.chatSessionManager,
- RecalcWarning: deps.icbmSvc.RestoreWarningLevel,
- LowerWarnLevel: deps.icbmSvc.UpdateWarnLevel,
- FeedbagService: deps.feedbagSvc,
- }
- // Pass SQLiteUserStore as the API key validator (it implements middleware.APIKeyValidator)
- return webapi.NewServer([]string{"0.0.0.0:80"}, logger, handler, deps.sqLiteUserStore, deps.webAPISessionManager)
- }
- // ICQLegacy creates a legacy ICQ server for v2-v5 protocols.
- func ICQLegacy(deps Container) *icq_legacy.LegacyServer {
- logger := deps.logger.With("svc", "ICQLegacy")
- // Create session manager
- sessionManager := icq_legacy.NewLegacySessionManager(
- deps.inMemorySessionManager,
- deps.cfg.ICQLegacy,
- logger,
- )
- authService := foodgroup.NewAuthService(
- deps.cfg,
- deps.inMemorySessionManager,
- deps.inMemorySessionManager,
- deps.chatSessionManager,
- deps.sqLiteUserStore,
- deps.hmacCookieBaker,
- deps.chatSessionManager,
- deps.sqLiteUserStore,
- deps.sqLiteUserStore,
- deps.sqLiteUserStore,
- deps.rateLimitClasses,
- state.NewAccountCreator(deps.sqLiteUserStore.InsertUser),
- logger,
- )
- buddyService := foodgroup.NewBuddyService(
- deps.inMemorySessionManager,
- deps.sqLiteUserStore,
- deps.sqLiteUserStore,
- deps.inMemorySessionManager,
- deps.sqLiteUserStore,
- deps.sqLiteUserStore,
- )
- // Create the ICQ legacy service
- icqLegacyService := icq_legacy.NewICQLegacyService(
- authService,
- deps.sqLiteUserStore, // userManager
- deps.sqLiteUserStore, // accountManager
- deps.inMemorySessionManager, // sessionRetriever
- deps.inMemorySessionManager, // messageRelayer
- buddyService, // buddyBroadcaster
- deps.sqLiteUserStore, // offlineMessageManager
- deps.sqLiteUserStore, // userFinder
- deps.sqLiteUserStore, // userUpdater
- deps.sqLiteUserStore, // feedbagManager
- deps.sqLiteUserStore, // relationshipFetcher
- deps.sqLiteUserStore, // buddyListRegistry
- buddyService, // buddyService
- deps.icbmSvc,
- sessionManager, // legacySessionManager
- logger,
- )
- // Create handlers (sender will be set after server creation)
- v2PacketBuilder := icq_legacy.NewV2PacketBuilder()
- v3PacketBuilder := icq_legacy.NewV3PacketBuilder(sessionManager, deps.cfg.ICQLegacy.DirectConnectionEnabled)
- v4PacketBuilder := icq_legacy.NewV4PacketBuilder(sessionManager, deps.cfg.ICQLegacy.DirectConnectionEnabled)
- v5PacketBuilder := icq_legacy.NewV5PacketBuilder(sessionManager, deps.cfg.ICQLegacy.DirectConnectionEnabled)
- v1Handler := icq_legacy.NewV1Handler(sessionManager, icqLegacyService, nil, logger)
- v2Handler := icq_legacy.NewV2Handler(sessionManager, icqLegacyService, nil, v2PacketBuilder, logger)
- v3Handler := icq_legacy.NewV3Handler(sessionManager, icqLegacyService, nil, v3PacketBuilder, logger)
- v4Handler := icq_legacy.NewV4Handler(sessionManager, icqLegacyService, nil, v4PacketBuilder, logger)
- v5Handler := icq_legacy.NewV5Handler(sessionManager, icqLegacyService, nil, v5PacketBuilder, logger)
- // Create protocol dispatcher
- dispatcher := icq_legacy.NewProtocolDispatcher(
- v1Handler,
- v2Handler,
- v3Handler,
- v4Handler,
- v5Handler,
- deps.cfg.ICQLegacy,
- logger,
- )
- // Create server
- server := icq_legacy.NewLegacyServer(
- deps.cfg.ICQLegacy,
- sessionManager,
- dispatcher,
- logger,
- )
- // Set the packet sender on handlers (circular dependency resolution)
- v1Handler.SetSender(server)
- v2Handler.SetSender(server)
- v3Handler.SetSender(server)
- v4Handler.SetSender(server)
- v5Handler.SetSender(server)
- // Set the dispatcher on handlers for cross-protocol messaging
- v1Handler.SetDispatcher(dispatcher)
- v2Handler.SetDispatcher(dispatcher)
- v3Handler.SetDispatcher(dispatcher)
- v4Handler.SetDispatcher(dispatcher)
- v5Handler.SetDispatcher(dispatcher)
- // Wire up OSCAR->legacy message bridge so OSCAR status notifications
- // reach legacy clients via the session message pump
- legacyBridge := icq_legacy.NewLegacyMessageBridge(sessionManager, dispatcher, deps.sqLiteUserStore, logger)
- // Set the bridge on the session manager so it can start the OSCAR message
- // pump for each new legacy session (converts BuddyArrived/Departed SNACs
- // into legacy protocol packets)
- sessionManager.SetBridge(legacyBridge)
- // Set the expired session callback so timed-out sessions notify contacts
- // and OSCAR clients — same as a graceful logoff.
- sessionManager.SetOnSessionExpired(func(session *icq_legacy.LegacySession) {
- logger.Info("session expired, notifying contacts",
- "uin", session.UIN,
- )
- // Notify legacy contacts
- sessionManager.BroadcastToContacts(session, func(contact *icq_legacy.LegacySession) {
- _ = dispatcher.SendUserOffline(contact, session.UIN)
- })
- // Notify OSCAR clients
- ctx := context.Background()
- _ = icqLegacyService.NotifyUserOffline(ctx, session.UIN)
- })
- return server
- }
|