diff --git a/bot/handler.go b/bot/handler.go new file mode 100644 index 0000000..8a6c229 --- /dev/null +++ b/bot/handler.go @@ -0,0 +1,11 @@ +package bot + +import ( + "dev.sum7.eu/genofire/yaja/xmpp" +) + +type Handler interface { + Presence(*Bot, *xmpp.PresenceClient) bool + Message(*Bot, *xmpp.MessageClient) bool + IQ(*Bot, *xmpp.IQClient) bool +} diff --git a/bot/handler_commandmsg.go b/bot/handler_commandmsg.go new file mode 100644 index 0000000..9011107 --- /dev/null +++ b/bot/handler_commandmsg.go @@ -0,0 +1,36 @@ +package bot + +import ( + "strings" + + "dev.sum7.eu/genofire/yaja/xmpp" +) + +type CommandMessageHander struct { + Handler + Commands map[string]func(*Bot, *xmpp.MessageClient, string) +} + +func (h *CommandMessageHander) Presence(bot *Bot, pres *xmpp.PresenceClient) bool { + return false +} + +func (h *CommandMessageHander) Message(bot *Bot, msg *xmpp.MessageClient) bool { + msgText := strings.SplitN(msg.Body, " ", 2) + if msgText != nil { + cmd := msgText[0] + if f, ok := h.Commands[cmd]; ok { + args := "" + if len(msgText) >= 2 { + args = msgText[1] + } + f(bot, msg, args) + return true + } + } + return false +} + +func (h *CommandMessageHander) IQ(bot *Bot, iq *xmpp.IQClient) bool { + return false +} diff --git a/bot/handler_subscribe.go b/bot/handler_subscribe.go new file mode 100644 index 0000000..191e70f --- /dev/null +++ b/bot/handler_subscribe.go @@ -0,0 +1,51 @@ +package bot + +import ( + "dev.sum7.eu/genofire/yaja/xmpp" +) + +type SubscribeHander struct { + Handler + Disabled *bool +} + +func (h *SubscribeHander) Presence(bot *Bot, pres *xmpp.PresenceClient) bool { + /* + if *(h.Disabled) { + return false + } + */ + switch pres.Type { + case xmpp.PresenceTypeSubscribe: + pres.Type = xmpp.PresenceTypeSubscribed + pres.To = pres.From + pres.From = nil + //accept new subscribe + bot.client.Send(pres) + + pres.Type = xmpp.PresenceTypeSubscribe + pres.ID = "" + // request also subscribe + bot.client.Send(pres) + return true + case xmpp.PresenceTypeSubscribed: + return true + case xmpp.PresenceTypeUnsubscribe: + return true + case xmpp.PresenceTypeUnsubscribed: + return true + case xmpp.PresenceTypeUnavailable: + // ignore Unavailable messages + return true + default: + return false + } +} + +func (h *SubscribeHander) Message(bot *Bot, msg *xmpp.MessageClient) bool { + return false +} + +func (h *SubscribeHander) IQ(bot *Bot, iq *xmpp.IQClient) bool { + return false +} diff --git a/bot/main.go b/bot/main.go new file mode 100644 index 0000000..d9dfef4 --- /dev/null +++ b/bot/main.go @@ -0,0 +1,89 @@ +package bot + +import ( + log "github.com/sirupsen/logrus" + + "dev.sum7.eu/genofire/yaja/client" + "dev.sum7.eu/genofire/yaja/xmpp" + "dev.sum7.eu/genofire/yaja/xmpp/base" +) + +type Bot struct { + AutoSubscribe bool + Logging *log.Entry + Handlers []Handler + client *client.Client + jid *xmppbase.JID + password string +} + +func NewBot(jid *xmppbase.JID, password string) *Bot { + bot := Bot{ + jid: jid, + password: password, + Logging: log.WithField("jid", jid.String()), + } + bot.Handlers = append(bot.Handlers, &SubscribeHander{Disabled: &bot.AutoSubscribe}) + return &bot +} + +func (bot *Bot) Start() { + if bot.client == nil { + bot.client = &client.Client{ + JID: bot.jid, + Logging: bot.Logging, + } + if bot.client.Connect(bot.password) != nil { + bot.Logging.Fatal("was not able to connect") + } + go func() { + if err := bot.client.Start(); err != nil { + bot.Logging.Fatal("was not able to reconnect") + } + }() + } + bot.run() +} + +func (bot *Bot) run() { + for { + element, more := bot.client.Recv() + if !more { + bot.Logging.Info("could not recv msg, closed") + return + } + handled := false + + switch element.(type) { + case *xmpp.PresenceClient: + pres := element.(*xmpp.PresenceClient) + for _, f := range bot.Handlers { + if f.Presence(bot, pres) { + handled = true + break + } + } + case *xmpp.MessageClient: + msg := element.(*xmpp.MessageClient) + for _, f := range bot.Handlers { + if f.Message(bot, msg) { + handled = true + break + } + } + case *xmpp.IQClient: + iq := element.(*xmpp.IQClient) + for _, f := range bot.Handlers { + if f.IQ(bot, iq) { + handled = true + break + } + } + } + if handled { + bot.Logging.Debugf("recv handled: %v", element) + } else { + bot.Logging.Infof("recv unhandled: %v", element) + } + } +} diff --git a/bot/utils.go b/bot/utils.go new file mode 100644 index 0000000..edd35ce --- /dev/null +++ b/bot/utils.go @@ -0,0 +1,11 @@ +package bot + +import "dev.sum7.eu/genofire/yaja/xmpp" + +func (bot *Bot) Answer(msg *xmpp.MessageClient, body string) { + bot.client.Send(&xmpp.MessageClient{ + To: msg.From, + Type: msg.Type, + Body: body, + }) +} diff --git a/cmd/bot.go b/cmd/bot.go new file mode 100644 index 0000000..7242544 --- /dev/null +++ b/cmd/bot.go @@ -0,0 +1,76 @@ +package cmd + +import ( + "fmt" + "os" + "os/signal" + "syscall" + "time" + + "dev.sum7.eu/genofire/golang-lib/file" + log "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + + xmppbot "dev.sum7.eu/genofire/yaja/bot" + "dev.sum7.eu/genofire/yaja/xmpp" + "dev.sum7.eu/genofire/yaja/xmpp/base" +) + +type botConfig struct { + LogLevel log.Level `toml:"log_level"` + JID *xmppbase.JID `toml:"jid"` + Password string `toml:"password"` +} + +// BotCMD represents the serve command +var BotCMD = &cobra.Command{ + Use: "bot", + Short: "runs xmpp ping bot", + Example: "yaja bot -c /etc/yaja.conf", + Run: func(cmd *cobra.Command, args []string) { + + config := &botConfig{} + + if err := file.ReadTOML(configPath, config); err != nil { + log.Fatal("unable to load config file:", err) + } + log.SetLevel(config.LogLevel) + + log.Infof("yaja bot starting with jid: %s", config.JID.String()) + + pingBog := xmppbot.NewBot(config.JID, config.Password) + pingBog.AutoSubscribe = true + + botCounter := 0 + + pingBog.Handlers = append(pingBog.Handlers, &xmppbot.CommandMessageHander{Commands: map[string]func(*xmppbot.Bot, *xmpp.MessageClient, string){ + // reaction on command ping + "ping": func(bot *xmppbot.Bot, msg *xmpp.MessageClient, args string) { + bot.Answer(msg, "pong") + }, + // reaction on command time + "time": func(bot *xmppbot.Bot, msg *xmpp.MessageClient, args string) { + bot.Answer(msg, time.Now().String()) + }, + "inc": func(bot *xmppbot.Bot, msg *xmpp.MessageClient, args string) { + botCounter += 1 + bot.Answer(msg, fmt.Sprintf("counter: %d", botCounter)) + }, + }}) + + go pingBog.Start() + + log.Infoln("yaja bot started") + + // Wait for INT/TERM + sigs := make(chan os.Signal, 1) + signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) + sig := <-sigs + log.Println("received", sig) + }, +} + +func init() { + BotCMD.Flags().StringVarP(&configPath, "config", "c", "yaja-bot.conf", "path to configuration file") + RootCMD.AddCommand(BotCMD) +} diff --git a/daemon/config.go b/cmd/config.go similarity index 88% rename from daemon/config.go rename to cmd/config.go index 15f7b3e..c5da483 100644 --- a/daemon/config.go +++ b/cmd/config.go @@ -1,4 +1,4 @@ -package daemon +package cmd import ( "dev.sum7.eu/genofire/golang-lib/worker" diff --git a/cmd/daemon.go b/cmd/daemon.go deleted file mode 100644 index 0ef0a60..0000000 --- a/cmd/daemon.go +++ /dev/null @@ -1,18 +0,0 @@ -package cmd - -import ( - "dev.sum7.eu/genofire/yaja/daemon" - "github.com/spf13/cobra" -) - -// DaemonCMD represents the serve command -var DaemonCMD = &cobra.Command{ - Use: "daemon", - Short: "daemon of yaja", -} - -func init() { - DaemonCMD.AddCommand(daemon.ServerCMD) - DaemonCMD.AddCommand(daemon.TesterCMD) - RootCMD.AddCommand(DaemonCMD) -} diff --git a/daemon/tester.go b/cmd/tester.go similarity index 95% rename from daemon/tester.go rename to cmd/tester.go index ea62b05..50ac96e 100644 --- a/daemon/tester.go +++ b/cmd/tester.go @@ -1,4 +1,4 @@ -package daemon +package cmd import ( "os" @@ -10,7 +10,7 @@ import ( "dev.sum7.eu/genofire/golang-lib/file" "dev.sum7.eu/genofire/golang-lib/worker" "dev.sum7.eu/genofire/yaja/client" - "dev.sum7.eu/genofire/yaja/daemon/tester" + "dev.sum7.eu/genofire/yaja/cmd/tester" "dev.sum7.eu/genofire/yaja/xmpp" "github.com/spf13/cobra" @@ -98,12 +98,11 @@ var TesterCMD = &cobra.Command{ func quitTester() { testerWorker.Close() testerInstance.Close() - srv.Close() - file.SaveJSON(configTester.AccountsPath, db) + //file.SaveJSON(configTester.AccountsPath, db) } func init() { TesterCMD.Flags().StringVarP(&configPath, "config", "c", "yaja-tester.conf", "path to configuration file") - + RootCMD.AddCommand(TesterCMD) } diff --git a/daemon/tester/account.go b/cmd/tester/account.go similarity index 100% rename from daemon/tester/account.go rename to cmd/tester/account.go diff --git a/daemon/tester/bot.go b/cmd/tester/bot.go similarity index 100% rename from daemon/tester/bot.go rename to cmd/tester/bot.go diff --git a/daemon/tester/bot_test.go b/cmd/tester/bot_test.go similarity index 100% rename from daemon/tester/bot_test.go rename to cmd/tester/bot_test.go diff --git a/daemon/tester/config.go b/cmd/tester/config.go similarity index 100% rename from daemon/tester/config.go rename to cmd/tester/config.go diff --git a/daemon/tester/output.go b/cmd/tester/output.go similarity index 100% rename from daemon/tester/output.go rename to cmd/tester/output.go diff --git a/daemon/tester/status.go b/cmd/tester/status.go similarity index 100% rename from daemon/tester/status.go rename to cmd/tester/status.go diff --git a/daemon/tester/tester.go b/cmd/tester/tester.go similarity index 100% rename from daemon/tester/tester.go rename to cmd/tester/tester.go diff --git a/cmd/tester_test.go b/cmd/tester_test.go new file mode 100644 index 0000000..1d619dd --- /dev/null +++ b/cmd/tester_test.go @@ -0,0 +1 @@ +package cmd diff --git a/daemon/server.go b/daemon/server.go deleted file mode 100644 index 7f26db6..0000000 --- a/daemon/server.go +++ /dev/null @@ -1,204 +0,0 @@ -package daemon - -import ( - "crypto/tls" - "net/http" - "os" - "os/signal" - "syscall" - "time" - - "golang.org/x/crypto/acme/autocert" - - serverDaemon "dev.sum7.eu/genofire/yaja/daemon/server" - "dev.sum7.eu/genofire/yaja/database" - "dev.sum7.eu/genofire/yaja/server/extension" - - "dev.sum7.eu/genofire/golang-lib/file" - "dev.sum7.eu/genofire/golang-lib/worker" - "dev.sum7.eu/genofire/yaja/server" - log "github.com/sirupsen/logrus" - - "github.com/spf13/cobra" -) - -var ( - serverConfig = &serverDaemon.Config{} - db = &database.State{} - srv *server.Server - certs *tls.Config - extensionsClient extension.Extensions - extensionsServer extension.Extensions -) - -// ServerCMD represents the serve command -var ServerCMD = &cobra.Command{ - Use: "server", - Short: "runs xmpp server", - Example: "yaja daemon server -c /etc/yaja.conf", - Run: func(cmd *cobra.Command, args []string) { - - if err := file.ReadTOML(configPath, serverConfig); err != nil { - log.Fatal("unable to load config file:", err) - } - - log.SetLevel(serverConfig.Logging.Level) - - if err := file.ReadJSON(serverConfig.StatePath, db); err != nil { - log.Warn("unable to load state file:", err) - } - - statesaveWorker = worker.NewWorker(time.Minute, func() { - file.SaveJSON(serverConfig.StatePath, db) - log.Info("save state to:", serverConfig.StatePath) - }) - - m := autocert.Manager{ - Cache: autocert.DirCache(serverConfig.TLSDir), - Prompt: autocert.AcceptTOS, - } - - // https server to handle acme (by letsencrypt) - for _, addr := range serverConfig.Address.Webserver { - hs := &http.Server{ - Addr: addr, - TLSConfig: &tls.Config{GetCertificate: m.GetCertificate}, - } - go func(hs *http.Server, addr string) { - if err := hs.ListenAndServeTLS("", ""); err != http.ErrServerClosed { - log.Errorf("webserver with addr %s: %s", addr, err) - } - }(hs, addr) - } - - srv = &server.Server{ - TLSManager: &m, - Database: db, - ClientAddr: serverConfig.Address.Client, - ServerAddr: serverConfig.Address.Server, - LoggingClient: serverConfig.Logging.LevelClient, - LoggingServer: serverConfig.Logging.LevelServer, - RegisterEnable: serverConfig.Register.Enable, - RegisterDomains: serverConfig.Register.Domains, - ExtensionsServer: extensionsServer, - ExtensionsClient: extensionsClient, - } - - go statesaveWorker.Start() - go srv.Start() - - log.Infoln("yaja started ") - - // Wait for INT/TERM - sigs := make(chan os.Signal, 1) - signal.Notify(sigs, syscall.SIGTERM, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGUSR1) - for sig := range sigs { - log.Infoln("received", sig) - switch sig { - case syscall.SIGTERM: - log.Panic("terminated") - os.Exit(0) - case syscall.SIGQUIT: - quit() - case syscall.SIGHUP: - quit() - case syscall.SIGUSR1: - reload() - } - } - - }, -} - -func quit() { - srv.Close() - statesaveWorker.Close() - - file.SaveJSON(serverConfig.StatePath, db) -} - -func reload() { - log.Info("start reloading...") - var configNewData *serverDaemon.Config - - if err := file.ReadTOML(configPath, configNewData); err != nil { - log.Warn("unable to load config file:", err) - return - } - log.SetLevel(configNewData.Logging.Level) - srv.LoggingClient = configNewData.Logging.LevelClient - srv.LoggingServer = configNewData.Logging.LevelServer - srv.RegisterEnable = configNewData.Register.Enable - srv.RegisterDomains = configNewData.Register.Domains - - //TODO fetch changing address (to set restart) - - if configNewData.StatePath != serverConfig.StatePath { - statesaveWorker.Close() - statesaveWorker := worker.NewWorker(time.Minute, func() { - file.SaveJSON(configNewData.StatePath, db) - log.Info("save state to:", configNewData.StatePath) - }) - go statesaveWorker.Start() - } - - restartServer := false - - if configNewData.TLSDir != serverConfig.TLSDir { - - m := autocert.Manager{ - Cache: autocert.DirCache(serverConfig.TLSDir), - Prompt: autocert.AcceptTOS, - } - - certs = &tls.Config{GetCertificate: m.GetCertificate} - restartServer = true - } - if restartServer { - newServer := &server.Server{ - TLSConfig: certs, - Database: db, - ClientAddr: configNewData.Address.Client, - ServerAddr: configNewData.Address.Server, - LoggingClient: configNewData.Logging.LevelClient, - RegisterEnable: configNewData.Register.Enable, - RegisterDomains: configNewData.Register.Domains, - ExtensionsServer: extensionsServer, - ExtensionsClient: extensionsClient, - } - log.Warn("reloading need a restart:") - go newServer.Start() - //TODO should fetch new server error - srv.Close() - srv = newServer - } - - serverConfig = configNewData - log.Info("reloaded") -} - -func init() { - - extensionsClient = append(extensionsClient, - &extension.Message{}, - &extension.Presence{}, - extension.IQExtensions{ - //&extension.IQPrivateBookmark{}, - //&extension.IQPrivateMetacontact{}, - //&extension.IQPrivateRoster{}, - &extension.IQPing{}, - //&extension.IQLast{}, - //&extension.IQDisco{Database: db}, - //&extension.IQRoster{Database: db}, - //&extension.IQExtensionDiscovery{GetSpaces: func() []string { - // return extensionsClient.Spaces() - //}}, - }) - - extensionsServer = append(extensionsServer, - extension.IQExtensions{ - &extension.IQPing{}, - }) - ServerCMD.Flags().StringVarP(&configPath, "config", "c", "yaja-server.conf", "path to configuration file") - -} diff --git a/daemon/server/config.go b/daemon/server/config.go deleted file mode 100644 index 6dc45ba..0000000 --- a/daemon/server/config.go +++ /dev/null @@ -1,24 +0,0 @@ -package server - -import ( - log "github.com/sirupsen/logrus" -) - -type Config struct { - TLSDir string `toml:"tlsdir"` - StatePath string `toml:"state_path"` - Logging struct { - Level log.Level `toml:"level"` - LevelClient log.Level `toml:"level_client"` - LevelServer log.Level `toml:"level_server"` - } `toml:"logging"` - Register struct { - Enable bool `toml:"enable"` - Domains []string `toml:"domains"` - } `toml:"register"` - Address struct { - Webserver []string `toml:"webserver"` - Client []string `toml:"client"` - Server []string `toml:"server"` - } `toml:"address"` -} diff --git a/daemon/server/testdata/config_panic.conf b/daemon/server/testdata/config_panic.conf deleted file mode 100644 index 1ac449e..0000000 --- a/daemon/server/testdata/config_panic.conf +++ /dev/null @@ -1 +0,0 @@ -adsa diff --git a/daemon/tester_test.go b/daemon/tester_test.go deleted file mode 100644 index 9d30720..0000000 --- a/daemon/tester_test.go +++ /dev/null @@ -1 +0,0 @@ -package daemon diff --git a/server/extension/iq.go b/server/extension/iq.go deleted file mode 100644 index 9d72023..0000000 --- a/server/extension/iq.go +++ /dev/null @@ -1,58 +0,0 @@ -package extension - -import ( - "encoding/xml" - - "dev.sum7.eu/genofire/yaja/server/utils" - "dev.sum7.eu/genofire/yaja/xmpp" -) - -type IQExtensions []IQExtension - -type IQExtension interface { - Extension - Get(*xmpp.IQClient, *utils.Client) bool - Set(*xmpp.IQClient, *utils.Client) bool -} - -func (iex IQExtensions) Spaces() (result []string) { - for _, extension := range iex { - spaces := extension.Spaces() - result = append(result, spaces...) - } - return result -} - -func (iex IQExtensions) Process(element *xml.StartElement, client *utils.Client) bool { - log := client.Log.WithField("extension", "iq") - - // iq encode - var msg xmpp.IQClient - if err := client.In.DecodeElement(&msg, element); err != nil { - return false - } - - log = log.WithField("id", msg.ID) - - // run every extensions - count := 0 - for _, extension := range iex { - switch msg.Type { - case xmpp.IQTypeGet: - if extension.Get(&msg, client) { - count++ - } - case xmpp.IQTypeSet: - if extension.Set(&msg, client) { - count++ - } - } - } - - // not extensions found - if count != 1 { - log.Debugf("%s - %s: %s", msg.XMLName.Space, msg.Type, xmpp.XMLChildrenString(msg.Other)) - } - - return true -} diff --git a/server/extension/iq_disco.bak b/server/extension/iq_disco.bak deleted file mode 100644 index 5abe80e..0000000 --- a/server/extension/iq_disco.bak +++ /dev/null @@ -1,72 +0,0 @@ -package extension - -import ( - "encoding/xml" - - "dev.sum7.eu/genofire/yaja/database" - "dev.sum7.eu/genofire/yaja/xmpp" - "dev.sum7.eu/genofire/yaja/model" - "dev.sum7.eu/genofire/yaja/server/utils" -) - -type IQDisco struct { - IQExtension - Database *database.State -} - -func (ex *IQDisco) Spaces() []string { return []string{"http://jabber.org/protocol/disco#items"} } - -func (ex *IQDisco) Get(msg *xmpp.IQClient, client *utils.Client) bool { - log := client.Log.WithField("extension", "disco-item").WithField("id", msg.ID) - - // query encode - type query struct { - XMLName xml.Name `xml:"http://jabber.org/protocol/disco#items query"` - Body []byte `xml:",innerxml"` - } - q := &query{} - if err := xml.Unmarshal(msg.Body, q); err != nil { - return false - } - - // answer query - q.Body = []byte{} - - // build answer body - type item struct { - XMLName xml.Name `xml:"item"` - JID string `xml:"jid,attr"` - } - if acc := ex.Database.GetAccount(client.JID); acc != nil { - for jid, _ := range acc.Bookmarks { - itemByte, err := xml.Marshal(&item{ - JID: jid, - }) - if err != nil { - log.Warn(err) - continue - } - q.Body = append(q.Body, itemByte...) - } - } - - // decode query - queryByte, err := xml.Marshal(q) - if err != nil { - log.Warn(err) - return false - } - - // reply - client.Messages <- &xmpp.IQClient{ - Type: xmpp.IQTypeResult, - To: client.JID, - From: xmpp.NewJID(client.JID.Domain), - ID: msg.ID, - Body: queryByte, - } - - log.Debug("send") - - return true -} diff --git a/server/extension/iq_discovery.bak b/server/extension/iq_discovery.bak deleted file mode 100644 index 53b4aaf..0000000 --- a/server/extension/iq_discovery.bak +++ /dev/null @@ -1,72 +0,0 @@ -package extension - -import ( - "encoding/xml" - - "dev.sum7.eu/genofire/yaja/xmpp" - "dev.sum7.eu/genofire/yaja/model" - "dev.sum7.eu/genofire/yaja/server/utils" -) - -type IQExtensionDiscovery struct { - IQExtension - GetSpaces func() []string -} - -func (ex *IQExtensionDiscovery) Spaces() []string { return []string{} } - -func (ex *IQExtensionDiscovery) Get(msg *xmpp.IQClient, client *utils.Client) bool { - log := client.Log.WithField("extension", "roster").WithField("id", msg.ID) - - // query encode - type query struct { - XMLName xml.Name `xml:"http://jabber.org/protocol/disco#info query"` - Body []byte `xml:",innerxml"` - } - q := &query{} - if err := xml.Unmarshal(msg.Body, q); err != nil { - return false - } - - // answer query - q.Body = []byte{} - - // build answer body - type feature struct { - XMLName xml.Name `xml:"feature"` - Var string `xml:"var,attr"` - } - for _, namespace := range ex.GetSpaces() { - if namespace == "" { - continue - } - itemByte, err := xml.Marshal(&feature{ - Var: namespace, - }) - if err != nil { - log.Warn(err) - continue - } - q.Body = append(q.Body, itemByte...) - } - - // decode query - queryByte, err := xml.Marshal(q) - if err != nil { - log.Warn(err) - return false - } - - // replay - client.Messages <- &xmpp.IQClient{ - Type: xmpp.IQTypeResult, - To: client.JID, - From: xmpp.NewJID(client.JID.Domain), - ID: msg.ID, - Body: queryByte, - } - - log.Debug("send") - - return true -} diff --git a/server/extension/iq_last.bak b/server/extension/iq_last.bak deleted file mode 100644 index ed098e7..0000000 --- a/server/extension/iq_last.bak +++ /dev/null @@ -1,60 +0,0 @@ -package extension - -import ( - "encoding/xml" - - "dev.sum7.eu/genofire/yaja/xmpp" - "dev.sum7.eu/genofire/yaja/model" - "dev.sum7.eu/genofire/yaja/server/utils" -) - -//TODO Draft - -type IQLast struct { - IQExtension -} - -func (ex *IQLast) Spaces() []string { return []string{"jabber:iq:last"} } - -func (ex *IQLast) Get(msg *xmpp.IQClient, client *utils.Client) bool { - log := client.Log.WithField("extension", "last").WithField("id", msg.ID) - - // query encode - type query struct { - XMLName xml.Name `xml:"jabber:iq:last query"` - Seconds uint `xml:"seconds,attr,omitempty"` - Body []byte `xml:",innerxml"` - } - q := &query{} - if err := xml.Unmarshal(msg.Body, q); err != nil { - return false - } - - // answer query - q.Body = []byte{} - - // build answer body - type item struct { - XMLName xml.Name `xml:"item"` - JID string `xml:"jid,attr"` - } - // decode query - queryByte, err := xml.Marshal(q) - if err != nil { - log.Warn(err) - return false - } - - // reply - client.Messages <- &xmpp.IQClient{ - Type: xmpp.IQTypeResult, - To: client.JID, - From: xmpp.NewJID(client.JID.Domain), - ID: msg.ID, - Body: queryByte, - } - - log.Debug("send") - - return true -} diff --git a/server/extension/iq_ping.go b/server/extension/iq_ping.go deleted file mode 100644 index db00dbf..0000000 --- a/server/extension/iq_ping.go +++ /dev/null @@ -1,33 +0,0 @@ -package extension - -import ( - "dev.sum7.eu/genofire/yaja/server/utils" - "dev.sum7.eu/genofire/yaja/xmpp" - "dev.sum7.eu/genofire/yaja/xmpp/base" -) - -type IQPing struct { - IQExtension -} - -func (ex *IQPing) Spaces() []string { return []string{"urn:xmpp:ping"} } - -func (ex *IQPing) Get(msg *xmpp.IQClient, client *utils.Client) bool { - log := client.Log.WithField("extension", "ping").WithField("id", msg.ID) - - if msg.Ping == nil { - return false - } - - // reply - client.Messages <- &xmpp.IQClient{ - Type: xmpp.IQTypeResult, - To: client.JID, - From: xmppbase.NewJID(client.JID.Domain), - ID: msg.ID, - } - - log.Debug("send") - - return true -} diff --git a/server/extension/iq_private_bookmarks.bak b/server/extension/iq_private_bookmarks.bak deleted file mode 100644 index 02f4e64..0000000 --- a/server/extension/iq_private_bookmarks.bak +++ /dev/null @@ -1,50 +0,0 @@ -package extension - -import ( - "encoding/xml" - - "dev.sum7.eu/genofire/yaja/xmpp" - "dev.sum7.eu/genofire/yaja/model" - "dev.sum7.eu/genofire/yaja/server/utils" -) - -type IQPrivateBookmark struct { - IQExtension -} - -func (ex *IQPrivateBookmark) Handle(msg *xmpp.IQClient, client *utils.Client) bool { - log := client.Log.WithField("extension", "private").WithField("id", msg.ID) - - // storage encode - type storage struct { - XMLName xml.Name `xml:"storage:bookmarks storage"` - } - s := &storage{} - if err := xml.Unmarshal(q.Body, s); err != nil { - return false - } - /* - TODO full implement - */ - - queryByte, err := xml.Marshal(&iqPrivateQuery{ - Body: q.Body, - }) - if err != nil { - log.Warn(err) - return true - } - - // reply - client.Messages <- &xmpp.IQClient{ - Type: xmpp.IQTypeResult, - To: client.JID, - From: xmpp.NewJID(client.JID.Domain), - ID: msg.ID, - Body: queryByte, - } - - log.Debug("send") - - return true -} diff --git a/server/extension/iq_private_metacontacts.bak b/server/extension/iq_private_metacontacts.bak deleted file mode 100644 index d98043c..0000000 --- a/server/extension/iq_private_metacontacts.bak +++ /dev/null @@ -1,51 +0,0 @@ -package extension - -import ( - "encoding/xml" - - "dev.sum7.eu/genofire/yaja/xmpp" - "dev.sum7.eu/genofire/yaja/model" - "dev.sum7.eu/genofire/yaja/server/utils" -) - -type IQPrivateMetacontact struct { - IQExtension -} - -func (ex *IQPrivateMetacontact) Handle(msg *xmpp.IQClient, client *utils.Client) bool { - log := client.Log.WithField("extension", "private-metacontact").WithField("id", msg.ID) - - // storage encode - type storage struct { - XMLName xml.Name `xml:"storage:metacontacts storage"` - } - s := &storage{} - if err := xml.Unmarshal(q.Body, s); err != nil { - return false - } - /* - TODO full implement XEP-0209 - https://xmpp.org/extensions/xep-0209.html - */ - - queryByte, err := xml.Marshal(&iqPrivateQuery{ - Body: q.Body, - }) - if err != nil { - log.Warn(err) - return true - } - - // reply - client.Messages <- &xmpp.IQClient{ - Type: xmpp.IQTypeResult, - To: client.JID, - From: xmpp.NewJID(client.JID.Domain), - ID: msg.ID, - Body: queryByte, - } - - log.Debug("send") - - return true -} diff --git a/server/extension/iq_private_roster.bak b/server/extension/iq_private_roster.bak deleted file mode 100644 index db213ae..0000000 --- a/server/extension/iq_private_roster.bak +++ /dev/null @@ -1,55 +0,0 @@ -package extension - -import ( - "encoding/xml" - - "dev.sum7.eu/genofire/yaja/xmpp" - "dev.sum7.eu/genofire/yaja/model" - "dev.sum7.eu/genofire/yaja/server/utils" -) - -type IQPrivateRoster struct { - IQExtension -} - -func (ex *IQPrivateRoster) Handle(msg *xmpp.IQClient, client *utils.Client) bool { - log := client.Log.WithField("extension", "private").WithField("id", msg.ID) - - // roster encode - type roster struct { - XMLName xml.Name `xml:"roster:delimiter roster"` - Body []byte `xml:",innerxml"` - } - r := &roster{} - if err := xml.Unmarshal(q.Body, r); err != nil { - return false - } - - rosterByte, err := xml.Marshal(&roster{ - Body: []byte("::"), - }) - if err != nil { - log.Warn(err) - return true - } - queryByte, err := xml.Marshal(&iqPrivateQuery{ - Body: rosterByte, - }) - if err != nil { - log.Warn(err) - return true - } - - // reply - client.Messages <- &xmpp.IQClient{ - Type: xmpp.IQTypeResult, - To: client.JID, - From: xmpp.NewJID(client.JID.Domain), - ID: msg.ID, - Body: queryByte, - } - - log.Debug("send") - - return true -} diff --git a/server/extension/iq_roster.bak b/server/extension/iq_roster.bak deleted file mode 100644 index 20b9e99..0000000 --- a/server/extension/iq_roster.bak +++ /dev/null @@ -1,73 +0,0 @@ -package extension - -import ( - "encoding/xml" - - "dev.sum7.eu/genofire/yaja/database" - "dev.sum7.eu/genofire/yaja/xmpp" - "dev.sum7.eu/genofire/yaja/server/utils" -) - -type IQRoster struct { - IQExtension - Database *database.State -} - -func (ex *IQRoster) Spaces() []string { return []string{"jabber:iq:roster"} } - -func (ex *IQRoster) Get(msg *xmpp.IQClient, client *utils.Client) bool { - log := client.Log.WithField("extension", "roster").WithField("id", msg.ID) - - // query encode - type query struct { - XMLName xml.Name `xml:"jabber:iq:roster query"` - Version string `xml:"ver,attr"` - Body []byte `xml:",innerxml"` - } - q := &query{} - if err := xml.Unmarshal(msg.Body, q); err != nil { - return false - } - - // answer query - q.Body = []byte{} - q.Version = "1" - - // build answer body - type item struct { - XMLName xml.Name `xml:"item"` - JID string `xml:"jid,attr"` - } - if acc := ex.Database.GetAccount(client.JID); acc != nil { - for jid, _ := range acc.Roster { - itemByte, err := xml.Marshal(&item{ - JID: jid, - }) - if err != nil { - log.Warn(err) - continue - } - q.Body = append(q.Body, itemByte...) - } - } - - // decode query - queryByte, err := xml.Marshal(q) - if err != nil { - log.Warn(err) - return false - } - - // reply - client.Messages <- &xmpp.IQClient{ - Type: xmpp.IQTypeResult, - To: client.JID.String(), - From: client.JID.Domain, - ID: msg.ID, - Body: queryByte, - } - - log.Debug("send") - - return true -} diff --git a/server/extension/main.go b/server/extension/main.go deleted file mode 100644 index c5b99c3..0000000 --- a/server/extension/main.go +++ /dev/null @@ -1,38 +0,0 @@ -package extension - -import ( - "encoding/xml" - - "dev.sum7.eu/genofire/yaja/server/utils" -) - -type Extensions []Extension - -type Extension interface { - Process(*xml.StartElement, *utils.Client) bool - Spaces() []string -} - -func (ex Extensions) Spaces() (result []string) { - for _, extension := range ex { - result = append(result, extension.Spaces()...) - } - return result -} - -func (ex Extensions) Process(element *xml.StartElement, client *utils.Client) { - log := client.Log.WithField("extension", "all") - - // run every extensions - count := 0 - for _, extension := range ex { - if extension.Process(element, client) { - count++ - } - } - - // not extensions found - if count != 1 { - log.Debug(element) - } -} diff --git a/server/extension/message.go b/server/extension/message.go deleted file mode 100644 index 2c3f656..0000000 --- a/server/extension/message.go +++ /dev/null @@ -1,19 +0,0 @@ -package extension - -import ( - "encoding/xml" - - "dev.sum7.eu/genofire/yaja/server/utils" -) - -type Message struct { - Extension -} - -//TODO Draft - -func (m *Message) Spaces() []string { return []string{} } - -func (m *Message) Process(element *xml.StartElement, client *utils.Client) bool { - return false -} diff --git a/server/extension/presence.go b/server/extension/presence.go deleted file mode 100644 index 94e13a2..0000000 --- a/server/extension/presence.go +++ /dev/null @@ -1,32 +0,0 @@ -package extension - -import ( - "encoding/xml" - - "dev.sum7.eu/genofire/yaja/server/utils" - "dev.sum7.eu/genofire/yaja/xmpp" -) - -type Presence struct { - Extension -} - -//TODO Draft - -func (p *Presence) Spaces() []string { return []string{} } - -func (p *Presence) Process(element *xml.StartElement, client *utils.Client) bool { - log := client.Log.WithField("extension", "presence") - - // iq encode - var msg xmpp.PresenceClient - if err := client.In.DecodeElement(&msg, element); err != nil { - return false - } - client.Messages <- &xmpp.PresenceClient{ - ID: msg.ID, - } - log.Debug("send") - - return true -} diff --git a/server/server.go b/server/server.go deleted file mode 100644 index c8d2f49..0000000 --- a/server/server.go +++ /dev/null @@ -1,128 +0,0 @@ -package server - -import ( - "crypto/tls" - "net" - - log "github.com/sirupsen/logrus" - "golang.org/x/crypto/acme/autocert" - - "dev.sum7.eu/genofire/yaja/database" - "dev.sum7.eu/genofire/yaja/server/extension" - "dev.sum7.eu/genofire/yaja/server/toclient" - "dev.sum7.eu/genofire/yaja/server/toserver" - "dev.sum7.eu/genofire/yaja/server/utils" - "dev.sum7.eu/genofire/yaja/xmpp/base" -) - -type Server struct { - TLSConfig *tls.Config - TLSManager *autocert.Manager - ClientAddr []string - ServerAddr []string - Database *database.State - LoggingClient log.Level - LoggingServer log.Level - RegisterEnable bool - RegisterDomains []string - ExtensionsClient extension.Extensions - ExtensionsServer extension.Extensions -} - -func (srv *Server) Start() { - for _, addr := range srv.ServerAddr { - socket, err := net.Listen("tcp", addr) - if err != nil { - log.Warn("create server socket: ", err.Error()) - break - } - go srv.listenServer(socket) - } - - for _, addr := range srv.ClientAddr { - socket, err := net.Listen("tcp", addr) - if err != nil { - log.Warn("create client socket: ", err.Error()) - break - } - go srv.listenClient(socket) - } -} - -func (srv *Server) listenServer(s2s net.Listener) { - for { - conn, err := s2s.Accept() - if err != nil { - log.Warn("accepting server connection: ", err.Error()) - break - } - go srv.handleServer(conn) - } -} - -func (srv *Server) listenClient(c2s net.Listener) { - for { - conn, err := c2s.Accept() - if err != nil { - log.Warn("accepting client connection: ", err.Error()) - break - } - go srv.handleClient(conn) - } -} - -func (srv *Server) handleServer(conn net.Conn) { - log.Info("new server connection:", conn.RemoteAddr()) - - client := utils.NewClient(conn, srv.LoggingClient) - client.Log = client.Log.WithField("c", "s2s") - - state := toserver.ConnectionStartup(srv.Database, srv.TLSConfig, srv.TLSManager, srv.ExtensionsServer, client) - - for { - state = state.Process() - if state == nil { - client.Log.Info("disconnect") - client.Close() - return - } - // run next state - } -} - -func (srv *Server) handleClient(conn net.Conn) { - log.Info("new client connection:", conn.RemoteAddr()) - - client := utils.NewClient(conn, srv.LoggingServer) - client.Log = client.Log.WithField("c", "c2s") - - state := toclient.ConnectionStartup(srv.Database, srv.TLSConfig, srv.TLSManager, srv.DomainRegisterAllowed, srv.ExtensionsClient, client) - - for { - state = state.Process() - if state == nil { - client.Log.Info("disconnect") - client.Close() - //s.DisconnectBus <- Disconnect{Jid: client.jid} - return - } - // run next state - } -} - -func (srv *Server) DomainRegisterAllowed(jid *xmppbase.JID) bool { - if jid.Domain == "" { - return false - } - - for _, domain := range srv.RegisterDomains { - if domain == jid.Domain { - return !srv.RegisterEnable - } - } - return srv.RegisterEnable -} - -func (srv *Server) Close() { - -} diff --git a/server/state/connect.go b/server/state/connect.go deleted file mode 100644 index e3c212a..0000000 --- a/server/state/connect.go +++ /dev/null @@ -1,117 +0,0 @@ -package state - -import ( - "crypto/tls" - "fmt" - - "golang.org/x/crypto/acme/autocert" - - "dev.sum7.eu/genofire/yaja/server/utils" - "dev.sum7.eu/genofire/yaja/xmpp" - "dev.sum7.eu/genofire/yaja/xmpp/base" -) - -// Start state -type Start struct { - Next State - Client *utils.Client -} - -// Process message -func (state *Start) Process() State { - state.Client.Log = state.Client.Log.WithField("state", "stream") - state.Client.Log.Debug("running") - defer state.Client.Log.Debug("leave") - - element, err := state.Client.Read() - if err != nil { - state.Client.Log.Warn("unable to read: ", err) - return nil - } - if element.Name.Space != xmpp.NSStream || element.Name.Local != "stream" { - state.Client.Log.Warn("is no stream") - return state - } - for _, attr := range element.Attr { - if attr.Name.Local == "to" { - state.Client.JID = &xmppbase.JID{Domain: attr.Value} - state.Client.Log = state.Client.Log.WithField("jid", state.Client.JID.Full()) - } - } - if state.Client.JID == nil { - state.Client.Log.Warn("no 'to' domain readed") - return nil - } - - fmt.Fprintf(state.Client.Conn, ` - `, - xmpp.CreateCookie(), xmpp.NSClient, xmpp.NSStream) - - fmt.Fprintf(state.Client.Conn, ` - - - - `, - xmpp.NSStream) - - return state.Next -} - -// TLSUpgrade state -type TLSUpgrade struct { - Next State - Client *utils.Client - TLSConfig *tls.Config - TLSManager *autocert.Manager -} - -// Process message -func (state *TLSUpgrade) Process() State { - state.Client.Log = state.Client.Log.WithField("state", "tls upgrade") - state.Client.Log.Debug("running") - defer state.Client.Log.Debug("leave") - - element, err := state.Client.Read() - if err != nil { - state.Client.Log.Warn("unable to read: ", err) - return nil - } - if element.Name.Space != xmpp.NSStartTLS || element.Name.Local != "starttls" { - state.Client.Log.Warn("is no starttls", element) - return nil - } - fmt.Fprintf(state.Client.Conn, "", xmpp.NSStartTLS) - // perform the TLS handshake - var tlsConfig *tls.Config - if m := state.TLSManager; m != nil { - var cert *tls.Certificate - cert, err = m.GetCertificate(&tls.ClientHelloInfo{ServerName: state.Client.JID.Domain}) - if err != nil { - state.Client.Log.Warn("no cert in tls manger found: ", err) - return nil - } - tlsConfig = &tls.Config{ - Certificates: []tls.Certificate{*cert}, - } - } - if tlsConfig == nil { - tlsConfig = state.TLSConfig - if tlsConfig != nil { - tlsConfig.ServerName = state.Client.JID.Domain - } else { - state.Client.Log.Warn("no tls config found: ", err) - return nil - } - } - - tlsConn := tls.Server(state.Client.Conn, tlsConfig) - err = tlsConn.Handshake() - if err != nil { - state.Client.Log.Warn("unable to tls handshake: ", err) - return nil - } - // restart the Connection - state.Client.SetConnecting(tlsConn) - - return state.Next -} diff --git a/server/state/normal.go b/server/state/normal.go deleted file mode 100644 index 9c0bf80..0000000 --- a/server/state/normal.go +++ /dev/null @@ -1,49 +0,0 @@ -package state - -import ( - "dev.sum7.eu/genofire/yaja/server/extension" - "dev.sum7.eu/genofire/yaja/server/utils" -) - -// SendingClient state -type SendingClient struct { - Next State - Client *utils.Client -} - -// Process xmpp -func (state *SendingClient) Process() State { - state.Client.Log = state.Client.Log.WithField("state", "normal") - state.Client.Log.Debug("sending") - // sending - go func() { - select { - case msg := <-state.Client.Messages: - err := state.Client.Out.Encode(msg) - if err != nil { - state.Client.Log.Warn(err) - } - case <-state.Client.OnClose(): - return - } - }() - state.Client.Log.Debug("receiving") - return state.Next -} - -// ReceivingClient state -type ReceivingClient struct { - Extensions extension.Extensions - Client *utils.Client -} - -// Process xmpp -func (state *ReceivingClient) Process() State { - element, err := state.Client.Read() - if err != nil { - state.Client.Log.Warn("unable to read: ", err) - return nil - } - state.Extensions.Process(element, state.Client) - return state -} diff --git a/server/state/state.go b/server/state/state.go deleted file mode 100644 index 2157d04..0000000 --- a/server/state/state.go +++ /dev/null @@ -1,30 +0,0 @@ -package state - -import "dev.sum7.eu/genofire/yaja/server/utils" - -// State processes the stream and moves to the next state -type State interface { - Process() State -} - -// Start state -type Debug struct { - Next State - Client *utils.Client -} - -// Process message -func (state *Debug) Process() State { - state.Client.Log = state.Client.Log.WithField("state", "debug") - state.Client.Log.Debug("running") - defer state.Client.Log.Debug("leave") - - element, err := state.Client.Read() - if err != nil { - state.Client.Log.Warn("unable to read: ", err) - return nil - } - state.Client.Log.Info(element) - - return state.Next -} diff --git a/server/toclient/connect.go b/server/toclient/connect.go deleted file mode 100644 index 955e5ce..0000000 --- a/server/toclient/connect.go +++ /dev/null @@ -1,237 +0,0 @@ -package toclient - -import ( - "crypto/tls" - "encoding/base64" - "fmt" - "strings" - - "golang.org/x/crypto/acme/autocert" - - "dev.sum7.eu/genofire/yaja/database" - "dev.sum7.eu/genofire/yaja/server/extension" - "dev.sum7.eu/genofire/yaja/server/state" - "dev.sum7.eu/genofire/yaja/server/utils" - "dev.sum7.eu/genofire/yaja/xmpp" - "dev.sum7.eu/genofire/yaja/xmpp/base" - "dev.sum7.eu/genofire/yaja/xmpp/iq" -) - -// ConnectionStartup return steps through TCP TLS state -func ConnectionStartup(db *database.State, tlsconfig *tls.Config, tlsmgmt *autocert.Manager, registerAllowed utils.DomainRegisterAllowed, extensions extension.Extensions, c *utils.Client) state.State { - receiving := &state.ReceivingClient{Extensions: extensions, Client: c} - sending := &state.SendingClient{Next: receiving, Client: c} - authedstream := &AuthedStream{Next: sending, Client: c} - authedstart := &AuthedStart{Next: authedstream, Client: c} - tlsauth := &SASLAuth{ - Next: authedstart, - Client: c, - database: db, - domainRegisterAllowed: registerAllowed, - } - tlsstream := &TLSStream{ - Next: tlsauth, - Client: c, - domainRegisterAllowed: registerAllowed, - } - tlsupgrade := &state.TLSUpgrade{ - Next: tlsstream, - Client: c, - TLSConfig: tlsconfig, - TLSManager: tlsmgmt, - } - return &state.Start{Next: tlsupgrade, Client: c} -} - -// TLSStream state -type TLSStream struct { - Next state.State - Client *utils.Client - domainRegisterAllowed utils.DomainRegisterAllowed -} - -// Process xmpp -func (state *TLSStream) Process() state.State { - state.Client.Log = state.Client.Log.WithField("state", "tls stream") - state.Client.Log.Debug("running") - defer state.Client.Log.Debug("leave") - - element, err := state.Client.Read() - if err != nil { - state.Client.Log.Warn("unable to read: ", err) - return nil - } - if element.Name.Space != xmpp.NSStream || element.Name.Local != "stream" { - state.Client.Log.Warn("is no stream") - return state - } - - if state.domainRegisterAllowed(state.Client.JID) { - fmt.Fprintf(state.Client.Conn, ` - - - - - PLAIN - - `, - xmpp.CreateCookie(), xmpp.NSClient, xmpp.NSStream, - xmpp.NSSASL, xmppiq.NSFeatureRegister) - } else { - fmt.Fprintf(state.Client.Conn, ` - - - - PLAIN - - `, - xmpp.CreateCookie(), xmpp.NSClient, xmpp.NSStream, - xmpp.NSSASL) - } - - return state.Next -} - -// SASLAuth state -type SASLAuth struct { - Next state.State - Client *utils.Client - database *database.State - domainRegisterAllowed utils.DomainRegisterAllowed -} - -// Process xmpp -func (state *SASLAuth) Process() state.State { - state.Client.Log = state.Client.Log.WithField("state", "sasl auth") - state.Client.Log.Debug("running") - defer state.Client.Log.Debug("leave") - - // read the full auth stanza - element, err := state.Client.Read() - if err != nil { - state.Client.Log.Warn("unable to read: ", err) - return nil - } - var auth xmpp.SASLAuth - if err = state.Client.In.DecodeElement(&auth, element); err != nil { - state.Client.Log.Info("start substate for registration") - return &RegisterFormRequest{ - Next: &RegisterRequest{ - Next: state.Next, - Client: state.Client, - database: state.database, - domainRegisterAllowed: state.domainRegisterAllowed, - }, - Client: state.Client, - element: element, - domainRegisterAllowed: state.domainRegisterAllowed, - } - } - data, err := base64.StdEncoding.DecodeString(auth.Body) - if err != nil { - state.Client.Log.Warn("body decode: ", err) - return nil - } - info := strings.Split(string(data), "\x00") - // should check that info[1] starts with state.Client.JID - state.Client.JID.Local = info[1] - state.Client.Log = state.Client.Log.WithField("jid", state.Client.JID.Full()) - success, err := state.database.Authenticate(state.Client.JID, info[2]) - if err != nil { - state.Client.Log.Warn("auth: ", err) - return nil - } - if success { - state.Client.Log.Info("success auth") - fmt.Fprintf(state.Client.Conn, "", xmpp.NSSASL) - return state.Next - } - state.Client.Log.Warn("failed auth") - fmt.Fprintf(state.Client.Conn, "", xmpp.NSSASL) - return nil - -} - -// AuthedStart state -type AuthedStart struct { - Next state.State - Client *utils.Client -} - -// Process xmpp -func (state *AuthedStart) Process() state.State { - state.Client.Log = state.Client.Log.WithField("state", "authed started") - state.Client.Log.Debug("running") - defer state.Client.Log.Debug("leave") - - _, err := state.Client.Read() - if err != nil { - state.Client.Log.Warn("unable to read: ", err) - return nil - } - fmt.Fprintf(state.Client.Conn, ` - - - - - - `, - xmpp.NSStream, state.Client.JID.Domain, xmpp.CreateCookie(), xmpp.NSClient, - xmpp.NSBind) - - return state.Next -} - -// AuthedStream state -type AuthedStream struct { - Next state.State - Client *utils.Client -} - -// Process xmpp -func (state *AuthedStream) Process() state.State { - state.Client.Log = state.Client.Log.WithField("state", "authed stream") - state.Client.Log.Debug("running") - defer state.Client.Log.Debug("leave") - - // check that it's a bind request - // read bind request - element, err := state.Client.Read() - if err != nil { - state.Client.Log.Warn("unable to read: ", err) - return nil - } - var msg xmpp.IQClient - if err = state.Client.In.DecodeElement(&msg, element); err != nil { - state.Client.Log.Warn("is no iq: ", err) - return nil - } - if msg.Type != xmpp.IQTypeSet { - state.Client.Log.Warn("is no set iq") - return nil - } - if msg.Error != nil { - state.Client.Log.Warn("iq with error: ", msg.Error.Code) - return nil - } - - if msg.Bind == nil { - state.Client.Log.Warn("is no iq bind: ", err) - return nil - } - if msg.Bind.Resource == "" { - state.Client.JID.Resource = makeResource() - } else { - state.Client.JID.Resource = msg.Bind.Resource - } - state.Client.Log = state.Client.Log.WithField("jid", state.Client.JID.Full()) - state.Client.Out.Encode(&xmpp.IQClient{ - Type: xmpp.IQTypeResult, - To: state.Client.JID, - From: xmppbase.NewJID(state.Client.JID.Domain), - ID: msg.ID, - Bind: &xmpp.Bind{JID: state.Client.JID}, - }) - - return state.Next -} diff --git a/server/toclient/register.go b/server/toclient/register.go deleted file mode 100644 index f033425..0000000 --- a/server/toclient/register.go +++ /dev/null @@ -1,137 +0,0 @@ -package toclient - -import ( - "encoding/xml" - - "dev.sum7.eu/genofire/yaja/database" - "dev.sum7.eu/genofire/yaja/model" - "dev.sum7.eu/genofire/yaja/server/state" - "dev.sum7.eu/genofire/yaja/server/utils" - "dev.sum7.eu/genofire/yaja/xmpp" - "dev.sum7.eu/genofire/yaja/xmpp/base" - "dev.sum7.eu/genofire/yaja/xmpp/iq" -) - -type RegisterFormRequest struct { - Next state.State - Client *utils.Client - domainRegisterAllowed utils.DomainRegisterAllowed - element *xml.StartElement -} - -// Process message -func (state *RegisterFormRequest) Process() state.State { - state.Client.Log = state.Client.Log.WithField("state", "register form request") - state.Client.Log.Debug("running") - defer state.Client.Log.Debug("leave") - - if !state.domainRegisterAllowed(state.Client.JID) { - state.Client.Log.Error("unpossible to reach this state, register on this domain is not allowed") - return nil - } - - var msg xmpp.IQClient - if err := state.Client.In.DecodeElement(&msg, state.element); err != nil { - state.Client.Log.Warn("is no iq: ", err) - return state - } - if msg.Type != xmpp.IQTypeGet { - state.Client.Log.Warn("is no get iq") - return state - } - if msg.Error != nil { - state.Client.Log.Warn("iq with error: ", msg.Error.Code) - return state - } - - if msg.Register == nil { - state.Client.Log.Warn("is no iq register") - return nil - } - state.Client.Out.Encode(&xmpp.IQClient{ - Type: xmpp.IQTypeResult, - To: state.Client.JID, - From: xmppbase.NewJID(state.Client.JID.Domain), - ID: msg.ID, - Register: &xmppiq.Register{ - Instructions: "Choose a username and password for use with this service.", - Username: "", - Password: "", - }, - }) - return state.Next -} - -type RegisterRequest struct { - Next state.State - Client *utils.Client - database *database.State - domainRegisterAllowed utils.DomainRegisterAllowed -} - -// Process message -func (state *RegisterRequest) Process() state.State { - state.Client.Log = state.Client.Log.WithField("state", "register request") - state.Client.Log.Debug("running") - defer state.Client.Log.Debug("leave") - - if !state.domainRegisterAllowed(state.Client.JID) { - state.Client.Log.Error("unpossible to reach this state, register on this domain is not allowed") - return nil - } - - element, err := state.Client.Read() - if err != nil { - state.Client.Log.Warn("unable to read: ", err) - return nil - } - var msg xmpp.IQClient - if err = state.Client.In.DecodeElement(&msg, element); err != nil { - state.Client.Log.Warn("is no iq: ", err) - return state - } - if msg.Type != xmpp.IQTypeGet { - state.Client.Log.Warn("is no get iq") - return state - } - if msg.Error != nil { - state.Client.Log.Warn("iq with error: ", msg.Error.Code) - return state - } - if msg.Register == nil { - state.Client.Log.Warn("is no iq register: ", err) - return nil - } - - state.Client.JID.Local = msg.Register.Username - state.Client.Log = state.Client.Log.WithField("jid", state.Client.JID.Full()) - account := model.NewAccount(state.Client.JID, msg.Register.Password) - err = state.database.AddAccount(account) - if err != nil { - state.Client.Out.Encode(&xmpp.IQClient{ - Type: xmpp.IQTypeResult, - To: state.Client.JID, - From: xmppbase.NewJID(state.Client.JID.Domain), - ID: msg.ID, - Register: msg.Register, - Error: &xmpp.ErrorClient{ - Code: "409", - Type: "cancel", - StanzaErrorGroup: xmpp.StanzaErrorGroup{ - Conflict: &xml.Name{}, - }, - }, - }) - state.Client.Log.Warn("database error: ", err) - return state - } - state.Client.Out.Encode(&xmpp.IQClient{ - Type: xmpp.IQTypeResult, - To: state.Client.JID, - From: xmppbase.NewJID(state.Client.JID.Domain), - ID: msg.ID, - }) - - state.Client.Log.Infof("registered client %s", state.Client.JID.Bare()) - return state.Next -} diff --git a/server/toclient/utils.go b/server/toclient/utils.go deleted file mode 100644 index aec4fa4..0000000 --- a/server/toclient/utils.go +++ /dev/null @@ -1,14 +0,0 @@ -package toclient - -import ( - "crypto/rand" - "fmt" -) - -func makeResource() string { - var buf [16]byte - if _, err := rand.Reader.Read(buf[:]); err != nil { - panic("Failed to read random bytes: " + err.Error()) - } - return fmt.Sprintf("%x", buf) -} diff --git a/server/toserver/connect.go b/server/toserver/connect.go deleted file mode 100644 index 5cf1fe6..0000000 --- a/server/toserver/connect.go +++ /dev/null @@ -1,142 +0,0 @@ -package toserver - -import ( - "crypto/tls" - "encoding/base64" - "encoding/xml" - "fmt" - - "golang.org/x/crypto/acme/autocert" - - "dev.sum7.eu/genofire/yaja/database" - "dev.sum7.eu/genofire/yaja/server/extension" - "dev.sum7.eu/genofire/yaja/server/state" - "dev.sum7.eu/genofire/yaja/server/utils" - "dev.sum7.eu/genofire/yaja/xmpp" -) - -// ConnectionStartup return steps through TCP TLS state -func ConnectionStartup(db *database.State, tlsconfig *tls.Config, tlsmgmt *autocert.Manager, extensions extension.Extensions, c *utils.Client) state.State { - receiving := &state.ReceivingClient{Extensions: extensions, Client: c} - sending := &state.SendingClient{Next: receiving, Client: c} - tlsstream := &TLSStream{ - Next: sending, - Client: c, - } - tlsupgrade := &state.TLSUpgrade{ - Next: tlsstream, - Client: c, - TLSConfig: tlsconfig, - TLSManager: tlsmgmt, - } - dail := &Dailback{ - Next: tlsupgrade, - Client: c, - } - return &state.Start{Next: dail, Client: c} -} - -// TLSStream state -type Dailback struct { - Next state.State - Client *utils.Client -} - -// Process xmpp -func (state *Dailback) Process() state.State { - state.Client.Log = state.Client.Log.WithField("state", "dialback") - state.Client.Log.Debug("running") - defer state.Client.Log.Debug("leave") - - element, err := state.Client.Read() - if err != nil { - state.Client.Log.Warn("unable to read: ", err) - return nil - } - - // dailback encode - type dailback struct { - XMLName xml.Name `xml:"urn:xmpp:ping ping"` - } - db := &dailback{} - if err = state.Client.In.DecodeElement(db, element); err != nil { - return state.Next - } - - state.Client.Log.Info(db) - return state.Next -} - -// TLSStream state -type TLSStream struct { - Next state.State - Client *utils.Client - domainRegisterAllowed utils.DomainRegisterAllowed -} - -// Process xmpp -func (state *TLSStream) Process() state.State { - state.Client.Log = state.Client.Log.WithField("state", "tls stream") - state.Client.Log.Debug("running") - defer state.Client.Log.Debug("leave") - - element, err := state.Client.Read() - if err != nil { - state.Client.Log.Warn("unable to read: ", err) - return nil - } - if element.Name.Space != xmpp.NSStream || element.Name.Local != "stream" { - state.Client.Log.Warn("is no stream") - return state - } - - fmt.Fprintf(state.Client.Conn, ` - - - - EXTERNAL - - - `, - xmpp.CreateCookie(), xmpp.NSClient, xmpp.NSStream, - xmpp.NSSASL) - - return state.Next -} - -// SASLAuth state -type SASLAuth struct { - Next state.State - Client *utils.Client - database *database.State - domainRegisterAllowed utils.DomainRegisterAllowed -} - -// Process xmpp -func (state *SASLAuth) Process() state.State { - state.Client.Log = state.Client.Log.WithField("state", "sasl auth") - state.Client.Log.Debug("running") - defer state.Client.Log.Debug("leave") - - // read the full auth stanza - element, err := state.Client.Read() - if err != nil { - state.Client.Log.Warn("unable to read: ", err) - return nil - } - var auth xmpp.SASLAuth - if err = state.Client.In.DecodeElement(&auth, element); err != nil { - return nil - } - data, err := base64.StdEncoding.DecodeString(auth.Body) - if err != nil { - state.Client.Log.Warn("body decode: ", err) - return nil - } - - state.Client.Log.Debug(auth.Mechanism, string(data)) - - state.Client.Log.Info("success auth") - fmt.Fprintf(state.Client.Conn, "", xmpp.NSSASL) - return state.Next -} diff --git a/server/utils/client.go b/server/utils/client.go deleted file mode 100644 index c26cfa9..0000000 --- a/server/utils/client.go +++ /dev/null @@ -1,68 +0,0 @@ -package utils - -import ( - "encoding/xml" - "net" - - log "github.com/sirupsen/logrus" - - "dev.sum7.eu/genofire/yaja/model" - "dev.sum7.eu/genofire/yaja/xmpp/base" -) - -type Client struct { - Log *log.Entry - - Conn net.Conn - Out *xml.Encoder - In *xml.Decoder - - JID *xmppbase.JID - account *model.Account - - Messages chan interface{} - close chan interface{} -} - -func NewClient(conn net.Conn, level log.Level) *Client { - logger := log.New() - logger.SetLevel(level) - client := &Client{ - Conn: conn, - Log: log.NewEntry(logger), - In: xml.NewDecoder(conn), - Out: xml.NewEncoder(conn), - Messages: make(chan interface{}), - close: make(chan interface{}), - } - return client -} - -func (client *Client) SetConnecting(conn net.Conn) { - client.Conn = conn - client.In = xml.NewDecoder(conn) - client.Out = xml.NewEncoder(conn) -} - -func (client *Client) Read() (*xml.StartElement, error) { - for { - nextToken, err := client.In.Token() - if err != nil { - return nil, err - } - switch nextToken.(type) { - case xml.StartElement: - element := nextToken.(xml.StartElement) - return &element, nil - } - } -} - -func (client *Client) OnClose() <-chan interface{} { - return client.close -} - -func (client *Client) Close() { - client.close <- true - client.Conn.Close() -} diff --git a/server/utils/main.go b/server/utils/main.go deleted file mode 100644 index f163733..0000000 --- a/server/utils/main.go +++ /dev/null @@ -1,5 +0,0 @@ -package utils - -import "dev.sum7.eu/genofire/yaja/xmpp/base" - -type DomainRegisterAllowed func(*xmppbase.JID) bool diff --git a/yaja-bot_example.conf b/yaja-bot_example.conf new file mode 100644 index 0000000..b326724 --- /dev/null +++ b/yaja-bot_example.conf @@ -0,0 +1,3 @@ +log_level = "debug" +jid = "bot@chat.sum7.eu" +password = "test" diff --git a/yaja-server_example.conf b/yaja-server_example.conf deleted file mode 100644 index f6a5940..0000000 --- a/yaja-server_example.conf +++ /dev/null @@ -1,16 +0,0 @@ -tlsdir = "tmp/ssl" -state_path = "tmp/yaja.json" - -[logging] -level = 5 -level_client = 6 -level_server = 6 - -[register] -enable = true -domains = [] - -[address] -webserver = [":https"] -client = [":5222"] -server = [":5269"]