diff options
| author | Mistivia <i@mistivia.com> | 2025-11-02 15:29:28 +0800 |
|---|---|---|
| committer | Mistivia <i@mistivia.com> | 2025-11-02 15:29:28 +0800 |
| commit | 9f42c2d5f911cb4e215d7873221e642ce7df4d61 (patch) | |
| tree | 6dac90a889a7402a9556d3d1bcc5cb53cdb9f123 /deprecated-webircgateway/pkg/webircgateway/gateway.go | |
| parent | fb2d9de539b660a261af19b1cbcceb7ee7980cb1 (diff) | |
deprecate webircdateway and ngircd
Diffstat (limited to 'deprecated-webircgateway/pkg/webircgateway/gateway.go')
| -rw-r--r-- | deprecated-webircgateway/pkg/webircgateway/gateway.go | 278 |
1 files changed, 278 insertions, 0 deletions
diff --git a/deprecated-webircgateway/pkg/webircgateway/gateway.go b/deprecated-webircgateway/pkg/webircgateway/gateway.go new file mode 100644 index 0000000..47169ef --- /dev/null +++ b/deprecated-webircgateway/pkg/webircgateway/gateway.go @@ -0,0 +1,278 @@ +package webircgateway + +import ( + "crypto/tls" + "encoding/json" + "fmt" + "net" + "net/http" + "os" + "strconv" + "strings" + "sync" + + "errors" + + "github.com/kiwiirc/webircgateway/pkg/identd" + "github.com/kiwiirc/webircgateway/pkg/proxy" + cmap "github.com/orcaman/concurrent-map" +) + +var ( + Version = "-" +) + +type Gateway struct { + Config *Config + HttpRouter *http.ServeMux + LogOutput chan string + messageTags *MessageTagManager + identdServ identd.Server + Clients cmap.ConcurrentMap + Acme *LEManager + Function string + httpSrvs []*http.Server + httpSrvsMu sync.Mutex + closeWg sync.WaitGroup +} + +func NewGateway(function string) *Gateway { + s := &Gateway{} + s.Function = function + s.Config = NewConfig(s) + s.HttpRouter = http.NewServeMux() + s.LogOutput = make(chan string, 5) + s.identdServ = identd.NewIdentdServer() + s.messageTags = NewMessageTagManager() + // Clients hold a map lookup for all the connected clients + s.Clients = cmap.New() + s.Acme = NewLetsEncryptManager(s) + + return s +} + +func (s *Gateway) Log(level int, format string, args ...interface{}) { + if level < s.Config.LogLevel { + return + } + + levels := [...]string{"L_DEBUG", "L_INFO", "L_WARN"} + line := fmt.Sprintf(levels[level-1]+" "+format, args...) + s.LogOutput <- line +} + +func (s *Gateway) Start() { + s.closeWg.Add(1) + + if s.Function == "gateway" { + s.maybeStartStaticFileServer() + s.initHttpRoutes() + s.maybeStartIdentd() + + for _, serverConfig := range s.Config.Servers { + go s.startServer(serverConfig) + } + } + + if s.Function == "proxy" { + proxy.Start(fmt.Sprintf("%s:%d", s.Config.Proxy.LocalAddr, s.Config.Proxy.Port)) + } +} + +func (s *Gateway) Close() { + hook := HookGatewayClosing{} + hook.Dispatch("gateway.closing") + + defer s.closeWg.Done() + + s.httpSrvsMu.Lock() + defer s.httpSrvsMu.Unlock() + + for _, httpSrv := range s.httpSrvs { + httpSrv.Close() + } +} + +func (s *Gateway) WaitClose() { + s.closeWg.Wait() +} + +func (s *Gateway) maybeStartStaticFileServer() { + if s.Config.Webroot != "" { + webroot := s.Config.ResolvePath(s.Config.Webroot) + s.Log(2, "Serving files from %s", webroot) + s.HttpRouter.Handle("/", http.FileServer(http.Dir(webroot))) + } +} + +func (s *Gateway) initHttpRoutes() error { + // Add all the transport routes + engineConfigured := false + for _, transport := range s.Config.ServerTransports { + switch transport { + case "kiwiirc": + t := &TransportKiwiirc{} + t.Init(s) + engineConfigured = true + case "websocket": + t := &TransportWebsocket{} + t.Init(s) + engineConfigured = true + case "sockjs": + t := &TransportSockjs{} + t.Init(s) + engineConfigured = true + default: + s.Log(3, "Invalid server engine: '%s'", transport) + } + } + + if !engineConfigured { + s.Log(3, "No server engines configured") + return errors.New("No server engines configured") + } + + // Add some general server info about this webircgateway instance + s.HttpRouter.HandleFunc("/webirc/info", func(w http.ResponseWriter, r *http.Request) { + out, _ := json.Marshal(map[string]interface{}{ + "name": "webircgateway", + "version": Version, + }) + + w.Write(out) + }) + + s.HttpRouter.HandleFunc("/webirc/_status", func(w http.ResponseWriter, r *http.Request) { + if !isPrivateIP(s.GetRemoteAddressFromRequest(r)) { + w.WriteHeader(403) + return + } + + out := "" + for item := range s.Clients.IterBuffered() { + c := item.Val.(*Client) + line := fmt.Sprintf( + "%s:%d %s %s!%s %s %s", + c.UpstreamConfig.Hostname, + c.UpstreamConfig.Port, + c.State, + c.IrcState.Nick, + c.IrcState.Username, + c.RemoteAddr, + c.RemoteHostname, + ) + + // Allow plugins to add their own status data + hook := HookStatus{} + hook.Client = c + hook.Line = line + hook.Dispatch("status.client") + if !hook.Halt { + out += hook.Line + "\n" + } + + } + + w.Write([]byte(out)) + }) + + return nil +} + +func (s *Gateway) maybeStartIdentd() { + if s.Config.Identd { + err := s.identdServ.Run() + if err != nil { + s.Log(3, "Error starting identd server: %s", err.Error()) + } else { + s.Log(2, "Identd server started") + } + } +} + +func (s *Gateway) startServer(conf ConfigServer) { + addr := fmt.Sprintf("%s:%d", conf.LocalAddr, conf.Port) + + if strings.HasPrefix(strings.ToLower(conf.LocalAddr), "tcp:") { + t := &TransportTcp{} + t.Init(s) + t.Start(conf.LocalAddr[4:] + ":" + strconv.Itoa(conf.Port)) + } else if conf.TLS && conf.LetsEncryptCacheDir == "" { + if conf.CertFile == "" || conf.KeyFile == "" { + s.Log(3, "'cert' and 'key' options must be set for TLS servers") + return + } + + tlsCert := s.Config.ResolvePath(conf.CertFile) + tlsKey := s.Config.ResolvePath(conf.KeyFile) + + s.Log(2, "Listening with TLS on %s", addr) + keyPair, keyPairErr := tls.LoadX509KeyPair(tlsCert, tlsKey) + if keyPairErr != nil { + s.Log(3, "Failed to listen with TLS, certificate error: %s", keyPairErr.Error()) + return + } + srv := &http.Server{ + Addr: addr, + TLSConfig: &tls.Config{ + Certificates: []tls.Certificate{keyPair}, + }, + Handler: s.HttpRouter, + } + s.httpSrvsMu.Lock() + s.httpSrvs = append(s.httpSrvs, srv) + s.httpSrvsMu.Unlock() + + // Don't use HTTP2 since it doesn't support websockets + srv.TLSNextProto = make(map[string]func(*http.Server, *tls.Conn, http.Handler)) + + err := srv.ListenAndServeTLS("", "") + if err != nil && err != http.ErrServerClosed { + s.Log(3, "Failed to listen with TLS: %s", err.Error()) + } + } else if conf.TLS && conf.LetsEncryptCacheDir != "" { + s.Log(2, "Listening with letsencrypt TLS on %s", addr) + leManager := s.Acme.Get(conf.LetsEncryptCacheDir) + srv := &http.Server{ + Addr: addr, + TLSConfig: &tls.Config{ + GetCertificate: leManager.GetCertificate, + }, + Handler: s.HttpRouter, + } + s.httpSrvsMu.Lock() + s.httpSrvs = append(s.httpSrvs, srv) + s.httpSrvsMu.Unlock() + + // Don't use HTTP2 since it doesn't support websockets + srv.TLSNextProto = make(map[string]func(*http.Server, *tls.Conn, http.Handler)) + + err := srv.ListenAndServeTLS("", "") + if err != nil && err != http.ErrServerClosed { + s.Log(3, "Listening with letsencrypt failed: %s", err.Error()) + } + } else if strings.HasPrefix(strings.ToLower(conf.LocalAddr), "unix:") { + socketFile := conf.LocalAddr[5:] + s.Log(2, "Listening on %s", socketFile) + os.Remove(socketFile) + server, serverErr := net.Listen("unix", socketFile) + if serverErr != nil { + s.Log(3, serverErr.Error()) + return + } + os.Chmod(socketFile, conf.BindMode) + http.Serve(server, s.HttpRouter) + } else { + s.Log(2, "Listening on %s", addr) + srv := &http.Server{Addr: addr, Handler: s.HttpRouter} + + s.httpSrvsMu.Lock() + s.httpSrvs = append(s.httpSrvs, srv) + s.httpSrvsMu.Unlock() + + err := srv.ListenAndServe() + if err != nil && err != http.ErrServerClosed { + s.Log(3, err.Error()) + } + } +} |
