diff --git a/client/auth.go b/client/auth.go
new file mode 100644
index 0000000..f2bc9fb
--- /dev/null
+++ b/client/auth.go
@@ -0,0 +1,119 @@
+package client
+
+import (
+ "crypto/md5"
+ "crypto/rand"
+ "encoding/base64"
+ "errors"
+ "fmt"
+ "math/big"
+ "strings"
+
+ "dev.sum7.eu/genofire/yaja/messages"
+)
+
+func (client *Client) auth(password string) error {
+ f, err := client.startStream()
+ if err != nil {
+ return err
+ }
+ //auth:
+ mechanism := ""
+ for _, m := range f.Mechanisms.Mechanism {
+ if m == "PLAIN" {
+ mechanism = m
+ // Plain authentication: send base64-encoded \x00 user \x00 password.
+ raw := "\x00" + client.JID.Local + "\x00" + password
+ enc := make([]byte, base64.StdEncoding.EncodedLen(len(raw)))
+ base64.StdEncoding.Encode(enc, []byte(raw))
+ fmt.Fprintf(client.conn, "%s\n", messages.NSSASL, enc)
+ break
+ }
+ if m == "DIGEST-MD5" {
+ mechanism = m
+ // Digest-MD5 authentication
+ fmt.Fprintf(client.conn, "\n", messages.NSSASL)
+ var ch string
+ if err := client.ReadElement(&ch); err != nil {
+ return err
+ }
+ b, err := base64.StdEncoding.DecodeString(string(ch))
+ if err != nil {
+ return err
+ }
+ tokens := map[string]string{}
+ for _, token := range strings.Split(string(b), ",") {
+ kv := strings.SplitN(strings.TrimSpace(token), "=", 2)
+ if len(kv) == 2 {
+ if kv[1][0] == '"' && kv[1][len(kv[1])-1] == '"' {
+ kv[1] = kv[1][1 : len(kv[1])-1]
+ }
+ tokens[kv[0]] = kv[1]
+ }
+ }
+ realm, _ := tokens["realm"]
+ nonce, _ := tokens["nonce"]
+ qop, _ := tokens["qop"]
+ charset, _ := tokens["charset"]
+ cnonceStr := cnonce()
+ digestURI := "xmpp/" + client.JID.Domain
+ nonceCount := fmt.Sprintf("%08x", 1)
+ digest := saslDigestResponse(client.JID.Local, realm, password, nonce, cnonceStr, "AUTHENTICATE", digestURI, nonceCount)
+ message := "username=\"" + client.JID.Local + "\", realm=\"" + realm + "\", nonce=\"" + nonce + "\", cnonce=\"" + cnonceStr +
+ "\", nc=" + nonceCount + ", qop=" + qop + ", digest-uri=\"" + digestURI + "\", response=" + digest + ", charset=" + charset
+
+ fmt.Fprintf(client.conn, "%s\n", messages.NSSASL, base64.StdEncoding.EncodeToString([]byte(message)))
+
+ err = client.ReadElement(&ch)
+ if err != nil {
+ return err
+ }
+ _, err = base64.StdEncoding.DecodeString(ch)
+ if err != nil {
+ return err
+ }
+ fmt.Fprintf(client.conn, "\n", messages.NSSASL)
+ break
+ }
+ }
+ if mechanism == "" {
+ return fmt.Errorf("PLAIN authentication is not an option: %v", f.Mechanisms.Mechanism)
+ }
+ element, err := client.Read()
+ if err != nil {
+ return err
+ }
+ if element.Name.Local != "success" {
+ return errors.New("auth failed: " + element.Name.Local)
+ }
+ return nil
+}
+
+func saslDigestResponse(username, realm, passwd, nonce, cnonceStr, authenticate, digestURI, nonceCountStr string) string {
+ h := func(text string) []byte {
+ h := md5.New()
+ h.Write([]byte(text))
+ return h.Sum(nil)
+ }
+ hex := func(bytes []byte) string {
+ return fmt.Sprintf("%x", bytes)
+ }
+ kd := func(secret, data string) []byte {
+ return h(secret + ":" + data)
+ }
+
+ a1 := string(h(username+":"+realm+":"+passwd)) + ":" + nonce + ":" + cnonceStr
+ a2 := authenticate + ":" + digestURI
+ response := hex(kd(hex(h(a1)), nonce+":"+nonceCountStr+":"+cnonceStr+":auth:"+hex(h(a2))))
+ return response
+}
+
+func cnonce() string {
+ randSize := big.NewInt(0)
+ randSize.Lsh(big.NewInt(1), 64)
+ cn, err := rand.Int(rand.Reader, randSize)
+ if err != nil {
+ return ""
+ }
+ return fmt.Sprintf("%016x", cn)
+}
diff --git a/client/client.go b/client/client.go
index 3a1eb30..9669251 100644
--- a/client/client.go
+++ b/client/client.go
@@ -1,14 +1,10 @@
package client
import (
- "crypto/md5"
- "crypto/rand"
"crypto/tls"
- "encoding/base64"
"encoding/xml"
"errors"
"fmt"
- "math/big"
"net"
"strings"
@@ -26,8 +22,23 @@ type Client struct {
JID *model.JID
}
-func NewClient(jid model.JID, password string) (*Client, error) {
- conn, err := net.Dial("tcp", jid.Domain+":5222")
+func NewClient(jid *model.JID, password string) (*Client, error) {
+ _, srvEntries, err := net.LookupSRV("xmpp-client", "tcp", jid.Domain)
+ addr := jid.Domain + ":5222"
+ if err == nil && len(srvEntries) > 0 {
+ bestSrv := srvEntries[0]
+ for _, srv := range srvEntries {
+ if srv.Priority <= bestSrv.Priority && srv.Weight >= bestSrv.Weight {
+ bestSrv = srv
+ addr = fmt.Sprintf("%s:%d", srv.Target, srv.Port)
+ }
+ }
+ }
+ a := strings.SplitN(addr, ":", 2)
+ if len(a) == 1 {
+ addr += ":5222"
+ }
+ conn, err := net.Dial("tcp", addr)
if err != nil {
return nil, err
}
@@ -36,7 +47,7 @@ func NewClient(jid model.JID, password string) (*Client, error) {
In: xml.NewDecoder(conn),
Out: xml.NewEncoder(conn),
- JID: &jid,
+ JID: jid,
}
if err = client.connect(password); err != nil {
@@ -54,51 +65,31 @@ func (c *Client) Close() error {
return nil
}
-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) ReadElement(p interface{}) error {
- element, err := client.Read()
- if err != nil {
- return err
- }
- return client.In.DecodeElement(p, element)
-}
-
-func (client *Client) init() error {
+func (client *Client) startStream() (*messages.StreamFeatures, error) {
// XMPP-Connection
_, err := fmt.Fprintf(client.conn, "\n"+
"\n",
model.XMLEscape(client.JID.Domain), messages.NSClient, messages.NSStream)
if err != nil {
- return err
+ return nil, err
}
element, err := client.Read()
if err != nil {
- return err
+ return nil, err
}
if element.Name.Space != messages.NSStream || element.Name.Local != "stream" {
- return errors.New("is not stream")
+ return nil, errors.New("is not stream")
}
- return nil
+ f := &messages.StreamFeatures{}
+ if err := client.ReadElement(f); err != nil {
+ return nil, err
+ }
+ return f, nil
}
+
func (client *Client) connect(password string) error {
- if err := client.init(); err != nil {
- return err
- }
- var f messages.StreamFeatures
- if err := client.ReadElement(&f); err != nil {
+ if _, err := client.startStream(); err != nil {
return err
}
if err := client.Out.Encode(&messages.TLSStartTLS{}); err != nil {
@@ -123,106 +114,25 @@ func (client *Client) connect(password string) error {
if err := tlsconn.VerifyHostname(client.JID.Domain); err != nil {
return err
}
- if err := client.init(); err != nil {
- return err
- }
- //auth:
- if err := client.ReadElement(&f); err != nil {
- return err
- }
- mechanism := ""
- for _, m := range f.Mechanisms.Mechanism {
- if m == "PLAIN" {
- mechanism = m
- // Plain authentication: send base64-encoded \x00 user \x00 password.
- raw := "\x00" + client.JID.Local + "\x00" + password
- enc := make([]byte, base64.StdEncoding.EncodedLen(len(raw)))
- base64.StdEncoding.Encode(enc, []byte(raw))
- fmt.Fprintf(client.conn, "%s\n", messages.NSSASL, enc)
- break
- }
- if m == "DIGEST-MD5" {
- mechanism = m
- // Digest-MD5 authentication
- fmt.Fprintf(client.conn, "\n", messages.NSSASL)
- var ch string
- if err := client.ReadElement(&ch); err != nil {
- return err
- }
- b, err := base64.StdEncoding.DecodeString(string(ch))
- if err != nil {
- return err
- }
- tokens := map[string]string{}
- for _, token := range strings.Split(string(b), ",") {
- kv := strings.SplitN(strings.TrimSpace(token), "=", 2)
- if len(kv) == 2 {
- if kv[1][0] == '"' && kv[1][len(kv[1])-1] == '"' {
- kv[1] = kv[1][1 : len(kv[1])-1]
- }
- tokens[kv[0]] = kv[1]
- }
- }
- realm, _ := tokens["realm"]
- nonce, _ := tokens["nonce"]
- qop, _ := tokens["qop"]
- charset, _ := tokens["charset"]
- cnonceStr := cnonce()
- digestURI := "xmpp/" + client.JID.Domain
- nonceCount := fmt.Sprintf("%08x", 1)
- digest := saslDigestResponse(client.JID.Local, realm, password, nonce, cnonceStr, "AUTHENTICATE", digestURI, nonceCount)
- message := "username=\"" + client.JID.Local + "\", realm=\"" + realm + "\", nonce=\"" + nonce + "\", cnonce=\"" + cnonceStr +
- "\", nc=" + nonceCount + ", qop=" + qop + ", digest-uri=\"" + digestURI + "\", response=" + digest + ", charset=" + charset
-
- fmt.Fprintf(client.conn, "%s\n", messages.NSSASL, base64.StdEncoding.EncodeToString([]byte(message)))
-
- err = client.ReadElement(&ch)
- if err != nil {
- return err
- }
- _, err = base64.StdEncoding.DecodeString(ch)
- if err != nil {
- return err
- }
- fmt.Fprintf(client.conn, "\n", messages.NSSASL)
- break
- }
- }
- if mechanism == "" {
- return fmt.Errorf("PLAIN authentication is not an option: %v", f.Mechanisms.Mechanism)
- }
- element, err := client.Read()
+ err := client.auth(password)
if err != nil {
return err
}
- if element.Name.Local != "success" {
- return errors.New("auth failed: " + element.Name.Local)
- }
-
- err = client.init()
+ _, err = client.startStream()
if err != nil {
return err
}
- if err := client.ReadElement(&f); err != nil {
- return err
- }
// bind to resource
- var msg string
- if client.JID.Resource == "" {
- msg = fmt.Sprintf("", messages.NSBind)
- } else {
- msg = fmt.Sprintf(
- `
- %s
- `,
- messages.NSBind, client.JID.Resource)
+ bind := &messages.Bind{}
+ if client.JID.Resource != "" {
+ bind.Resource = client.JID.Resource
}
client.Out.Encode(&messages.IQClient{
Type: messages.IQTypeSet,
- To: client.JID.Domain,
- From: client.JID.Full(),
+ To: model.NewJID(client.JID.Domain),
+ From: client.JID,
ID: utils.CreateCookieString(),
- Body: []byte(msg),
+ Bind: bind,
})
var iq messages.IQClient
@@ -237,39 +147,10 @@ func (client *Client) connect(password string) error {
client.JID.Domain = iq.Bind.JID.Domain
client.JID.Resource = iq.Bind.JID.Resource
} else {
- return errors.New(string(iq.Body))
+ return errors.New(fmt.Sprintf("%v", iq.Other))
}
// set status
- client.Out.Encode(&messages.PresenceClient{Show: "online", Status: "yaja client"})
+ client.Send(&messages.PresenceClient{Show: "online", Status: "yaja client"})
- return nil
-}
-
-func saslDigestResponse(username, realm, passwd, nonce, cnonceStr, authenticate, digestURI, nonceCountStr string) string {
- h := func(text string) []byte {
- h := md5.New()
- h.Write([]byte(text))
- return h.Sum(nil)
- }
- hex := func(bytes []byte) string {
- return fmt.Sprintf("%x", bytes)
- }
- kd := func(secret, data string) []byte {
- return h(secret + ":" + data)
- }
-
- a1 := string(h(username+":"+realm+":"+passwd)) + ":" + nonce + ":" + cnonceStr
- a2 := authenticate + ":" + digestURI
- response := hex(kd(hex(h(a1)), nonce+":"+nonceCountStr+":"+cnonceStr+":auth:"+hex(h(a2))))
- return response
-}
-
-func cnonce() string {
- randSize := big.NewInt(0)
- randSize.Lsh(big.NewInt(1), 64)
- cn, err := rand.Int(rand.Reader, randSize)
- if err != nil {
- return ""
- }
- return fmt.Sprintf("%016x", cn)
+ return err
}
diff --git a/client/comm.go b/client/comm.go
new file mode 100644
index 0000000..5fad5f8
--- /dev/null
+++ b/client/comm.go
@@ -0,0 +1,70 @@
+package client
+
+import (
+ "encoding/xml"
+ "log"
+
+ "dev.sum7.eu/genofire/yaja/messages"
+ "dev.sum7.eu/genofire/yaja/server/utils"
+)
+
+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) ReadElement(p interface{}) error {
+ element, err := client.Read()
+ if err != nil {
+ return err
+ }
+ var iq *messages.IQClient
+ iq, ok := p.(*messages.IQClient)
+ if !ok {
+ iq = &messages.IQClient{}
+ }
+ err = client.In.DecodeElement(iq, element)
+ if err == nil && iq.Ping != nil {
+ log.Println("answer ping")
+ iq.Type = messages.IQTypeResult
+ iq.To = iq.From
+ iq.From = client.JID
+ client.Out.Encode(iq)
+ return nil
+ }
+ if ok {
+ return err
+ }
+ return client.In.DecodeElement(p, element)
+}
+
+func (client *Client) Send(p interface{}) error {
+ msg, ok := p.(*messages.MessageClient)
+ if ok {
+ msg.From = client.JID
+ msg.ID = utils.CreateCookieString()
+ return client.Out.Encode(msg)
+ }
+ iq, ok := p.(*messages.IQClient)
+ if ok {
+ iq.From = client.JID
+ iq.ID = utils.CreateCookieString()
+ return client.Out.Encode(iq)
+ }
+ pc, ok := p.(*messages.PresenceClient)
+ if ok {
+ pc.From = client.JID
+ pc.ID = utils.CreateCookieString()
+ return client.Out.Encode(pc)
+ }
+ return client.Out.Encode(p)
+}
diff --git a/client/tls.go b/client/tls.go
new file mode 100644
index 0000000..edd51a9
--- /dev/null
+++ b/client/tls.go
@@ -0,0 +1,11 @@
+package client
+
+import "crypto/tls"
+
+func (client *Client) TLSConnectionState() *tls.ConnectionState {
+ if tlsconn, ok := client.conn.(*tls.Conn); ok {
+ state := tlsconn.ConnectionState()
+ return &state
+ }
+ return nil
+}
diff --git a/daemon/config.go b/daemon/config.go
index df9193f..15f7b3e 100644
--- a/daemon/config.go
+++ b/daemon/config.go
@@ -1,3 +1,10 @@
package daemon
-var configPath string
+import (
+ "dev.sum7.eu/genofire/golang-lib/worker"
+)
+
+var (
+ configPath string
+ statesaveWorker *worker.Worker
+)
diff --git a/daemon/server.go b/daemon/server.go
index 3c4c825..ed47aa0 100644
--- a/daemon/server.go
+++ b/daemon/server.go
@@ -25,7 +25,6 @@ import (
var (
serverConfig = &serverDaemon.Config{}
db = &database.State{}
- statesaveWorker *worker.Worker
srv *server.Server
certs *tls.Config
extensionsClient extension.Extensions
@@ -179,25 +178,27 @@ func reload() {
}
func init() {
+
extensionsClient = append(extensionsClient,
&extension.Message{},
&extension.Presence{},
extension.IQExtensions{
- &extension.IQPrivate{},
+ //&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()
- }},
+ //&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/tester.go b/daemon/tester.go
index 72946a2..d0beea6 100644
--- a/daemon/tester.go
+++ b/daemon/tester.go
@@ -8,6 +8,7 @@ import (
"syscall"
"time"
+ log "github.com/sirupsen/logrus"
"golang.org/x/crypto/acme/autocert"
"dev.sum7.eu/genofire/golang-lib/file"
@@ -15,12 +16,15 @@ import (
"dev.sum7.eu/genofire/yaja/client"
"dev.sum7.eu/genofire/yaja/daemon/tester"
"dev.sum7.eu/genofire/yaja/messages"
- log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
-var configTester = &tester.Config{}
+var (
+ configTester = &tester.Config{}
+ testerInstance = tester.NewTester()
+ testerWorker *worker.Worker
+)
// TesterCMD represents the serve command
var TesterCMD = &cobra.Command{
@@ -35,15 +39,10 @@ var TesterCMD = &cobra.Command{
log.SetLevel(configTester.Logging)
- if err := file.ReadJSON(configTester.StatePath, db); err != nil {
+ if err := file.ReadJSON(configTester.AccountsPath, testerInstance); err != nil {
log.Warn("unable to load state file:", err)
}
- statesaveWorker = worker.NewWorker(time.Minute, func() {
- file.SaveJSON(configTester.StatePath, db)
- log.Info("save state to:", configTester.StatePath)
- })
-
// https server to handle acme (by letsencrypt)
hs := &http.Server{
Addr: configTester.Webserver,
@@ -71,19 +70,27 @@ var TesterCMD = &cobra.Command{
if err != nil {
log.Fatal("unable to connect with main jabber client: ", err)
}
+ defer mainClient.Close()
for _, admin := range configTester.Admins {
- mainClient.Out.Encode(&messages.MessageClient{
- From: mainClient.JID.Full(),
- To: admin.Full(),
+ mainClient.Send(&messages.MessageClient{
+ To: admin,
Type: "chat",
Body: "yaja tester starts",
})
}
- go statesaveWorker.Start()
+ testerInstance.Start(mainClient, configTester.Client.Password)
+ testerInstance.CheckStatus()
+ testerWorker = worker.NewWorker(time.Minute, func() {
+ testerInstance.CheckStatus()
+ file.SaveJSON(configTester.AccountsPath, testerInstance)
+ file.SaveJSON(configTester.OutputPath, testerInstance.Output())
+ })
- log.Infoln("yaja tester started ")
+ go testerWorker.Start()
+
+ log.Info("yaja tester started ")
// Wait for INT/TERM
sigs := make(chan os.Signal, 1)
@@ -105,10 +112,11 @@ var TesterCMD = &cobra.Command{
}
func quitTester() {
+ testerWorker.Close()
+ testerInstance.Close()
srv.Close()
- statesaveWorker.Close()
- file.SaveJSON(configTester.StatePath, db)
+ file.SaveJSON(configTester.AccountsPath, db)
}
func init() {
diff --git a/daemon/tester/bot.go b/daemon/tester/bot.go
new file mode 100644
index 0000000..7229368
--- /dev/null
+++ b/daemon/tester/bot.go
@@ -0,0 +1,82 @@
+package tester
+
+import (
+ "strings"
+
+ log "github.com/sirupsen/logrus"
+
+ "dev.sum7.eu/genofire/yaja/messages"
+)
+
+func (t *Tester) StartBot(status *Status) {
+ for {
+ logCTX := log.WithField("jid", status.client.JID.Full())
+
+ element, err := status.client.Read()
+ if err != nil {
+ logCTX.Errorf("read client %s", err)
+ status.client.Close()
+ status.Login = false
+ return
+ }
+
+ iq := &messages.IQClient{}
+ err = status.client.In.DecodeElement(iq, element)
+ if err == nil {
+ if iq.Ping != nil {
+ logCTX.Debug("answer ping")
+ iq.Type = messages.IQTypeResult
+ iq.To = iq.From
+ iq.From = status.client.JID
+ status.client.Out.Encode(iq)
+ } else {
+ logCTX.Warnf("unsupport iq recv: %v", iq)
+ }
+ continue
+ }
+
+ pres := &messages.PresenceClient{}
+ err = status.client.In.DecodeElement(pres, element)
+ if err == nil {
+ if pres.Type == messages.PresenceTypeSubscribe {
+ pres.Type = messages.PresenceTypeSubscribed
+ pres.To = pres.From
+ status.client.Send(pres)
+ logCTX.WithField("from", pres.From.Full()).Info("accept new subscribe")
+ } else {
+ logCTX.Warnf("unsupported presence recv: %v", pres)
+ }
+ continue
+ }
+
+ msg := &messages.MessageClient{}
+ err = status.client.In.DecodeElement(msg, element)
+ if err != nil {
+ logCTX.Warnf("unsupport xml recv: %s", err)
+ continue
+ }
+ logCTX = logCTX.WithField("from", msg.From.Full()).WithField("msg", msg.Body)
+ if msg.Error != nil {
+ logCTX.Debugf("recv msg with error %s: %s", msg.Error.Code, msg.Error.Text)
+ continue
+
+ }
+
+ msgText := strings.SplitN(msg.Body, " ", 2)
+ switch msgText[0] {
+
+ case "ping":
+ status.client.Send(messages.MessageClient{Type: msg.Type, To: msg.From, Body: "pong"})
+
+ case "checkmsg":
+ if len(msgText) == 2 {
+ t.UpdateConnectionStatus(msg.From, status.client.JID, msgText[1])
+ } else {
+ logCTX.Debug("undetect")
+ }
+
+ default:
+ logCTX.Debug("undetect")
+ }
+ }
+}
diff --git a/daemon/tester/config.go b/daemon/tester/config.go
index 235c0dc..23da6a3 100644
--- a/daemon/tester/config.go
+++ b/daemon/tester/config.go
@@ -6,13 +6,14 @@ import (
)
type Config struct {
- TLSDir string `toml:"tlsdir"`
- StatePath string `toml:"state_path"`
- Logging log.Level `toml:"logging"`
- Webserver string `toml:"webserver"`
- Admins []model.JID `toml:"admins"`
- Client struct {
- JID model.JID `toml:"jid"`
- Password string `toml:"password"`
+ TLSDir string `toml:"tlsdir"`
+ AccountsPath string `toml:"accounts_path"`
+ OutputPath string `toml:"output_path"`
+ Logging log.Level `toml:"logging"`
+ Webserver string `toml:"webserver"`
+ Admins []*model.JID `toml:"admins"`
+ Client struct {
+ JID *model.JID `toml:"jid"`
+ Password string `toml:"password"`
} `toml:"client"`
}
diff --git a/daemon/tester/output.go b/daemon/tester/output.go
new file mode 100644
index 0000000..be52a50
--- /dev/null
+++ b/daemon/tester/output.go
@@ -0,0 +1,70 @@
+package tester
+
+import (
+ "fmt"
+ "strings"
+
+ "dev.sum7.eu/genofire/yaja/model"
+ "github.com/FreifunkBremen/yanic/lib/jsontime"
+)
+
+type Output struct {
+ Timestamp jsontime.Time `json:"timestamp"`
+ Status []*Status `json:"nodes"`
+ Links []*Link `json:"links"`
+}
+
+type Link struct {
+ Source string `json:"source"`
+ Target string `json:"target"`
+ FromSource bool `json:"from_source"`
+ FromTarget bool `json:"from_target"`
+}
+
+func (t *Tester) Output() *Output {
+ output := &Output{
+ Timestamp: jsontime.Now(),
+ Status: make([]*Status, 0),
+ Links: make([]*Link, 0),
+ }
+ links := make(map[string]*Link)
+
+ for from, status := range t.Status {
+ output.Status = append(output.Status, status)
+
+ for to, linkOK := range status.Connections {
+ var key string
+ // keep source and target in the same order
+ switchSourceTarget := strings.Compare(from, to) > 0
+ if switchSourceTarget {
+ key = fmt.Sprintf("%s-%s", from, to)
+ } else {
+ key = fmt.Sprintf("%s-%s", to, from)
+ }
+ if link := links[key]; link != nil {
+ if switchSourceTarget {
+ link.FromTarget = linkOK
+ } else {
+ link.FromSource = linkOK
+ }
+ continue
+ }
+ toJID := model.NewJID(to)
+ link := &Link{
+ Source: status.JID.Domain,
+ Target: toJID.Domain,
+ FromSource: linkOK,
+ FromTarget: false,
+ }
+ if switchSourceTarget {
+ link.Source = toJID.Domain
+ link.Target = status.JID.Domain
+ link.FromSource = false
+ link.FromTarget = linkOK
+ }
+ links[key] = link
+ output.Links = append(output.Links, link)
+ }
+ }
+ return output
+}
diff --git a/daemon/tester/status.go b/daemon/tester/status.go
new file mode 100644
index 0000000..1b05ac5
--- /dev/null
+++ b/daemon/tester/status.go
@@ -0,0 +1,52 @@
+package tester
+
+import (
+ "crypto/tls"
+
+ "dev.sum7.eu/genofire/yaja/client"
+ "dev.sum7.eu/genofire/yaja/model"
+)
+
+type Status struct {
+ client *client.Client
+ password string
+ JID *model.JID `json:"jid"`
+ Domain string `json:"domain"`
+ Login bool `json:"is_online"`
+ MessageForConnection map[string]string `json:"-"`
+ Connections map[string]bool `json:"-"`
+ TLSVersion string `json:"tls_version"`
+}
+
+func NewStatus(jid *model.JID, password string) *Status {
+ return &Status{
+ JID: jid,
+ Domain: jid.Domain,
+ MessageForConnection: make(map[string]string),
+ Connections: make(map[string]bool),
+ }
+}
+
+func (s *Status) Update() {
+ if s.client == nil || !s.Login {
+ s.Login = false
+ s.TLSVersion = ""
+ return
+ }
+ if tlsstate := s.client.TLSConnectionState(); tlsstate != nil {
+ switch tlsstate.Version {
+ case tls.VersionSSL30:
+ s.TLSVersion = "SSL 3.0"
+ case tls.VersionTLS10:
+ s.TLSVersion = "TLS 1.0"
+ case tls.VersionTLS11:
+ s.TLSVersion = "TLS 1.1"
+ case tls.VersionTLS12:
+ s.TLSVersion = "TLS 1.2"
+ default:
+ s.TLSVersion = "unknown " + string(tlsstate.Version)
+ }
+ } else {
+ s.TLSVersion = ""
+ }
+}
diff --git a/daemon/tester/tester.go b/daemon/tester/tester.go
new file mode 100644
index 0000000..9b7643d
--- /dev/null
+++ b/daemon/tester/tester.go
@@ -0,0 +1,152 @@
+package tester
+
+import (
+ log "github.com/sirupsen/logrus"
+
+ "dev.sum7.eu/genofire/yaja/client"
+ "dev.sum7.eu/genofire/yaja/messages"
+ "dev.sum7.eu/genofire/yaja/model"
+ "dev.sum7.eu/genofire/yaja/server/utils"
+)
+
+type Tester struct {
+ mainClient *client.Client
+ Accounts map[string]string `json:"accounts"`
+ Status map[string]*Status `json:"-"`
+}
+
+func NewTester() *Tester {
+ return &Tester{
+ Accounts: make(map[string]string),
+ Status: make(map[string]*Status),
+ }
+}
+
+func (t *Tester) Start(mainClient *client.Client, password string) {
+
+ t.mainClient = mainClient
+
+ status := NewStatus(mainClient.JID, password)
+ status.client = mainClient
+ status.Login = true
+ status.Update()
+
+ t.Status[mainClient.JID.Bare()] = status
+ go t.StartBot(status)
+
+ for jidString, passwd := range t.Accounts {
+ t.Connect(jidString, passwd)
+ }
+}
+func (t *Tester) Close() {
+ for _, s := range t.Status {
+ s.Login = false
+ s.client.Close()
+ }
+}
+
+func (t *Tester) Connect(jidString, password string) {
+ logCTX := log.WithField("jid", jidString)
+ jid := model.NewJID(jidString)
+ status, ok := t.Status[jidString]
+ if !ok {
+ status = NewStatus(jid, password)
+ t.Status[jidString] = status
+ } else if status.JID == nil {
+ status.JID = jid
+ }
+ if status.Login {
+ logCTX.Warn("is already loggedin")
+ return
+ }
+ c, err := client.NewClient(jid, password)
+ if err != nil {
+ logCTX.Warnf("could not connect client: %s", err)
+ } else {
+ logCTX.Info("client connected")
+ status.Login = true
+ status.client = c
+ status.Update()
+ go t.StartBot(status)
+ }
+}
+
+func (t *Tester) UpdateConnectionStatus(from, to *model.JID, recvmsg string) {
+ logCTX := log.WithFields(log.Fields{
+ "jid": to.Full(),
+ "from": from.Full(),
+ "recvmsg": recvmsg,
+ })
+
+ status, ok := t.Status[from.Bare()]
+ if !ok {
+ logCTX.Warn("recv wrong msg")
+ return
+ }
+ msg, ok := status.MessageForConnection[to.Bare()]
+ logCTX = logCTX.WithField("msg", msg)
+ if !ok || msg != recvmsg {
+ logCTX.Warn("recv wrong msg")
+ return
+ }
+ delete(status.MessageForConnection, to.Bare())
+ status.Connections[to.Bare()] = true
+ logCTX.Info("recv msg")
+
+}
+
+func (t *Tester) CheckStatus() {
+ send := 0
+ online := 0
+ connection := 0
+ for ownJID, own := range t.Status {
+ logCTX := log.WithField("jid", ownJID)
+ if !own.Login {
+ pass, ok := t.Accounts[ownJID]
+ if ok {
+ t.Connect(ownJID, pass)
+ }
+ if !own.Login {
+ continue
+ }
+ }
+ online++
+ for jid, s := range t.Status {
+ logCTXTo := logCTX.WithField("to", jid)
+ if jid == ownJID {
+ continue
+ }
+ connection++
+ if own.MessageForConnection == nil {
+ own.MessageForConnection = make(map[string]string)
+ }
+ msg, ok := own.MessageForConnection[jid]
+ if ok {
+ logCTXTo = logCTXTo.WithField("old-msg", msg)
+ own.Connections[jid] = false
+ if ok, exists := own.Connections[jid]; !exists || ok {
+ logCTXTo.Warn("could not recv msg")
+ } else {
+ logCTXTo.Debug("could not recv msg")
+ }
+ }
+ msg = utils.CreateCookieString()
+ logCTXTo = logCTXTo.WithField("msg", msg)
+
+ own.client.Send(&messages.MessageClient{
+ Body: "checkmsg " + msg,
+ Type: messages.ChatTypeChat,
+ To: s.JID,
+ })
+ own.MessageForConnection[jid] = msg
+ logCTXTo.Info("test send")
+ send++
+ }
+ }
+ log.WithFields(log.Fields{
+ "online": online,
+ "connection": connection,
+ "send": send,
+ "all": len(t.Status),
+ }).Info("checked complete")
+}
diff --git a/messages/connection.go b/messages/connection.go
index 1702a34..ba3b7d7 100644
--- a/messages/connection.go
+++ b/messages/connection.go
@@ -15,12 +15,6 @@ type StreamFeatures struct {
Session bool
}
-type StreamError struct {
- XMLName xml.Name `xml:"http://etherx.jabber.org/streams error"`
- Any xml.Name
- Text string
-}
-
// RFC 3920 C.3 TLS name space
type TLSStartTLS struct {
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-tls starttls"`
diff --git a/messages/error.go b/messages/error.go
index c250282..5e37d84 100644
--- a/messages/error.go
+++ b/messages/error.go
@@ -2,11 +2,19 @@ package messages
import "encoding/xml"
+type StreamError struct {
+ XMLName xml.Name `xml:"http://etherx.jabber.org/streams error"`
+ Text string
+
+ Any xml.Name `xml:",any"`
+}
+
// ErrorClient element
type ErrorClient struct {
XMLName xml.Name `xml:"jabber:client error"`
Code string `xml:"code,attr"`
Type string `xml:"type,attr"`
- Any xml.Name `xml:",any"`
Text string `xml:"text"`
+
+ Any xml.Name `xml:",any"`
}
diff --git a/messages/iq.go b/messages/iq.go
index 4703731..d1167d0 100644
--- a/messages/iq.go
+++ b/messages/iq.go
@@ -1,6 +1,10 @@
package messages
-import "encoding/xml"
+import (
+ "encoding/xml"
+
+ "dev.sum7.eu/genofire/yaja/model"
+)
type IQType string
@@ -13,13 +17,32 @@ const (
// IQ element - info/query
type IQClient struct {
- XMLName xml.Name `xml:"jabber:client iq"`
- From string `xml:"from,attr"`
- ID string `xml:"id,attr"`
- To string `xml:"to,attr"`
- Type IQType `xml:"type,attr"`
- Error *ErrorClient `xml:"error"`
- Bind Bind `xml:"bind"`
- Body []byte `xml:",innerxml"`
- // RosterRequest - better detection of iq's
+ XMLName xml.Name `xml:"jabber:client iq"`
+ From *model.JID `xml:"from,attr"`
+ ID string `xml:"id,attr"`
+ To *model.JID `xml:"to,attr"`
+ Type IQType `xml:"type,attr"`
+ Error *ErrorClient `xml:"error"`
+ Bind *Bind
+ Ping *Ping
+ PrivateQuery *IQPrivateQuery
+ PrivateRegister *IQPrivateRegister
+ // Any hasn't matched element
+ Other []XMLElement `xml:",any"`
+}
+
+type Ping struct {
+ XMLName xml.Name `xml:"urn:xmpp:ping ping"`
+}
+
+type IQPrivateQuery struct {
+ XMLName xml.Name `xml:"jabber:iq:private query"`
+ Body []byte `xml:",innerxml"`
+}
+
+type IQPrivateRegister struct {
+ XMLName xml.Name `xml:"jabber:iq:register query"`
+ Instructions string `xml:"instructions"`
+ Username string `xml:"username"`
+ Password string `xml:"password"`
}
diff --git a/messages/presence.go b/messages/presence.go
index 23d0cf7..9bc09da 100644
--- a/messages/presence.go
+++ b/messages/presence.go
@@ -1,6 +1,19 @@
package messages
-import "encoding/xml"
+import (
+ "encoding/xml"
+
+ "dev.sum7.eu/genofire/yaja/model"
+)
+
+type XMLElement struct {
+ XMLName xml.Name
+ InnerXML string `xml:",innerxml"`
+}
+
+type Delay struct {
+ Stamp string `xml:"stamp,attr"`
+}
type PresenceType string
@@ -16,30 +29,46 @@ const (
// PresenceClient element
type PresenceClient struct {
- XMLName xml.Name `xml:"jabber:client presence"`
- From string `xml:"from,attr,omitempty"`
- ID string `xml:"id,attr,omitempty"`
- To string `xml:"to,attr,omitempty"`
- Type string `xml:"type,attr,omitempty"`
- Lang string `xml:"lang,attr,omitempty"`
+ XMLName xml.Name `xml:"jabber:client presence"`
+ From *model.JID `xml:"from,attr,omitempty"`
+ ID string `xml:"id,attr,omitempty"`
+ To *model.JID `xml:"to,attr,omitempty"`
+ Type PresenceType `xml:"type,attr,omitempty"`
+ Lang string `xml:"lang,attr,omitempty"`
Show string `xml:"show,omitempty"` // away, chat, dnd, xa
Status string `xml:"status,omitempty"` // sb []clientText
Priority string `xml:"priority,omitempty"`
// Caps *ClientCaps `xml:"c"`
- Error *ErrorClient `xml:"error"`
- // Delay Delay `xml:"delay"`
+ Delay *Delay `xml:"delay"`
+
+ Error *ErrorClient
}
+type ChatType string
+
+const (
+ ChatTypeChat ChatType = "chat"
+ ChatTypeGroupchat ChatType = "groupchat"
+ ChatTypeError ChatType = "error"
+ ChatTypeHeadline ChatType = "headline"
+ ChatTypeNormal ChatType = "normal"
+)
+
// MessageClient element
type MessageClient struct {
- XMLName xml.Name `xml:"jabber:client message"`
- From string `xml:"from,attr,omitempty"`
- ID string `xml:"id,attr,omitempty"`
- To string `xml:"to,attr,omitempty"`
- Type string `xml:"type,attr,omitempty"`
- Lang string `xml:"lang,attr,omitempty"`
- Subject string `xml:"subject"`
- Body string `xml:"body"`
- Thread string `xml:"thread"`
+ XMLName xml.Name `xml:"jabber:client message"`
+ From *model.JID `xml:"from,attr,omitempty"`
+ ID string `xml:"id,attr,omitempty"`
+ To *model.JID `xml:"to,attr,omitempty"`
+ Type ChatType `xml:"type,attr,omitempty"`
+ Lang string `xml:"lang,attr,omitempty"`
+ Subject string `xml:"subject"`
+ Body string `xml:"body"`
+ Thread string `xml:"thread"`
+ // Any hasn't matched element
+ Other []XMLElement `xml:",any"`
+
+ Delay *Delay `xml:"delay"`
+ Error *ErrorClient
}
diff --git a/messages/roster.go b/messages/roster.go
new file mode 100644
index 0000000..ec240a1
--- /dev/null
+++ b/messages/roster.go
@@ -0,0 +1,19 @@
+package messages
+
+import (
+ "encoding/xml"
+
+ "dev.sum7.eu/genofire/yaja/model"
+)
+
+type ClientQuery struct {
+ Item []RosterItem
+}
+
+type RosterItem struct {
+ XMLName xml.Name `xml:"jabber:iq:roster item"`
+ JID *model.JID `xml:",attr"`
+ Name string `xml:",attr"`
+ Subscription string `xml:",attr"`
+ Group []string
+}
diff --git a/model/jid.go b/model/jid.go
index 4a5f025..4a693a0 100644
--- a/model/jid.go
+++ b/model/jid.go
@@ -36,6 +36,9 @@ func NewJID(jidString string) *JID {
// Bare get the "bare" jid
func (jid *JID) Bare() string {
+ if jid == nil {
+ return ""
+ }
if jid.Local != "" {
return jid.Local + "@" + jid.Domain
}
@@ -46,6 +49,9 @@ func (jid *JID) String() string { return jid.Bare() }
// Full get the "full" jid as string
func (jid *JID) Full() string {
+ if jid == nil {
+ return ""
+ }
if jid.Resource != "" {
return jid.Bare() + "/" + jid.Resource
}
diff --git a/server/extension/iq.go b/server/extension/iq.go
index d74b54e..eb9b4c8 100644
--- a/server/extension/iq.go
+++ b/server/extension/iq.go
@@ -51,7 +51,7 @@ func (iex IQExtensions) Process(element *xml.StartElement, client *utils.Client)
// not extensions found
if count != 1 {
- log.Debug(msg.XMLName.Space, " - ", msg.Type, ": ", string(msg.Body))
+ log.Debugf("%s - %s: %v", msg.XMLName.Space, msg.Type, msg.Other)
}
return true
diff --git a/server/extension/iq_disco.go b/server/extension/iq_disco.bak
similarity index 93%
rename from server/extension/iq_disco.go
rename to server/extension/iq_disco.bak
index 45e0e05..f548d45 100644
--- a/server/extension/iq_disco.go
+++ b/server/extension/iq_disco.bak
@@ -5,6 +5,7 @@ import (
"dev.sum7.eu/genofire/yaja/database"
"dev.sum7.eu/genofire/yaja/messages"
+ "dev.sum7.eu/genofire/yaja/model"
"dev.sum7.eu/genofire/yaja/server/utils"
)
@@ -59,8 +60,8 @@ func (ex *IQDisco) Get(msg *messages.IQClient, client *utils.Client) bool {
// reply
client.Messages <- &messages.IQClient{
Type: messages.IQTypeResult,
- To: client.JID.String(),
- From: client.JID.Domain,
+ To: client.JID,
+ From: model.NewJID(client.JID.Domain),
ID: msg.ID,
Body: queryByte,
}
diff --git a/server/extension/iq_discovery.go b/server/extension/iq_discovery.bak
similarity index 93%
rename from server/extension/iq_discovery.go
rename to server/extension/iq_discovery.bak
index c2c0288..60ff323 100644
--- a/server/extension/iq_discovery.go
+++ b/server/extension/iq_discovery.bak
@@ -4,6 +4,7 @@ import (
"encoding/xml"
"dev.sum7.eu/genofire/yaja/messages"
+ "dev.sum7.eu/genofire/yaja/model"
"dev.sum7.eu/genofire/yaja/server/utils"
)
@@ -59,8 +60,8 @@ func (ex *IQExtensionDiscovery) Get(msg *messages.IQClient, client *utils.Client
// replay
client.Messages <- &messages.IQClient{
Type: messages.IQTypeResult,
- To: client.JID.String(),
- From: client.JID.Domain,
+ To: client.JID,
+ From: model.NewJID(client.JID.Domain),
ID: msg.ID,
Body: queryByte,
}
diff --git a/server/extension/iq_last.go b/server/extension/iq_last.bak
similarity index 91%
rename from server/extension/iq_last.go
rename to server/extension/iq_last.bak
index b06c5c8..9cddba8 100644
--- a/server/extension/iq_last.go
+++ b/server/extension/iq_last.bak
@@ -4,6 +4,7 @@ import (
"encoding/xml"
"dev.sum7.eu/genofire/yaja/messages"
+ "dev.sum7.eu/genofire/yaja/model"
"dev.sum7.eu/genofire/yaja/server/utils"
)
@@ -47,8 +48,8 @@ func (ex *IQLast) Get(msg *messages.IQClient, client *utils.Client) bool {
// reply
client.Messages <- &messages.IQClient{
Type: messages.IQTypeResult,
- To: client.JID.String(),
- From: client.JID.Domain,
+ To: client.JID,
+ From: model.NewJID(client.JID.Domain),
ID: msg.ID,
Body: queryByte,
}
diff --git a/server/extension/iq_ping.go b/server/extension/iq_ping.go
index 0f65819..e589835 100644
--- a/server/extension/iq_ping.go
+++ b/server/extension/iq_ping.go
@@ -1,9 +1,8 @@
package extension
import (
- "encoding/xml"
-
"dev.sum7.eu/genofire/yaja/messages"
+ "dev.sum7.eu/genofire/yaja/model"
"dev.sum7.eu/genofire/yaja/server/utils"
)
@@ -16,20 +15,15 @@ func (ex *IQPing) Spaces() []string { return []string{"urn:xmpp:ping"} }
func (ex *IQPing) Get(msg *messages.IQClient, client *utils.Client) bool {
log := client.Log.WithField("extension", "ping").WithField("id", msg.ID)
- // ping encode
- type ping struct {
- XMLName xml.Name `xml:"urn:xmpp:ping ping"`
- }
- pq := &ping{}
- if err := xml.Unmarshal(msg.Body, pq); err != nil {
+ if msg.Ping == nil {
return false
}
// reply
client.Messages <- &messages.IQClient{
Type: messages.IQTypeResult,
- To: client.JID.String(),
- From: client.JID.Domain,
+ To: client.JID,
+ From: model.NewJID(client.JID.Domain),
ID: msg.ID,
}
diff --git a/server/extension/iq_private.go b/server/extension/iq_private.go
deleted file mode 100644
index 52eb8c5..0000000
--- a/server/extension/iq_private.go
+++ /dev/null
@@ -1,52 +0,0 @@
-package extension
-
-import (
- "encoding/xml"
-
- "dev.sum7.eu/genofire/yaja/messages"
- "dev.sum7.eu/genofire/yaja/server/utils"
-)
-
-type IQPrivate struct {
- IQExtension
-}
-
-type iqPrivateQuery struct {
- XMLName xml.Name `xml:"jabber:iq:private query"`
- Body []byte `xml:",innerxml"`
-}
-
-type iqPrivateExtension interface {
- Handle(*messages.IQClient, *iqPrivateQuery, *utils.Client) bool
-}
-
-func (ex *IQPrivate) Spaces() []string { return []string{"jabber:iq:private"} }
-
-func (ex *IQPrivate) Get(msg *messages.IQClient, client *utils.Client) bool {
- log := client.Log.WithField("extension", "private").WithField("id", msg.ID)
-
- // query encode
- q := &iqPrivateQuery{}
- if err := xml.Unmarshal(msg.Body, q); err != nil {
- return false
- }
-
- // run every extensions
- count := 0
- for _, e := range []iqPrivateExtension{
- &IQPrivateMetacontact{},
- &IQPrivateRoster{},
- &IQPrivateBookmark{},
- } {
- if e.Handle(msg, q, client) {
- count++
- }
- }
-
- // not extensions found
- if count != 1 {
- log.Debug(msg.XMLName.Space, " - ", msg.Type, ": ", string(q.Body))
- }
-
- return true
-}
diff --git a/server/extension/iq_private_bookmarks.go b/server/extension/iq_private_bookmarks.bak
similarity index 78%
rename from server/extension/iq_private_bookmarks.go
rename to server/extension/iq_private_bookmarks.bak
index 2fd55e5..b5ac095 100644
--- a/server/extension/iq_private_bookmarks.go
+++ b/server/extension/iq_private_bookmarks.bak
@@ -4,14 +4,15 @@ import (
"encoding/xml"
"dev.sum7.eu/genofire/yaja/messages"
+ "dev.sum7.eu/genofire/yaja/model"
"dev.sum7.eu/genofire/yaja/server/utils"
)
type IQPrivateBookmark struct {
- iqPrivateExtension
+ IQExtension
}
-func (ex *IQPrivateBookmark) Handle(msg *messages.IQClient, q *iqPrivateQuery, client *utils.Client) bool {
+func (ex *IQPrivateBookmark) Handle(msg *messages.IQClient, client *utils.Client) bool {
log := client.Log.WithField("extension", "private").WithField("id", msg.ID)
// storage encode
@@ -37,8 +38,8 @@ func (ex *IQPrivateBookmark) Handle(msg *messages.IQClient, q *iqPrivateQuery, c
// reply
client.Messages <- &messages.IQClient{
Type: messages.IQTypeResult,
- To: client.JID.String(),
- From: client.JID.Domain,
+ To: client.JID,
+ From: model.NewJID(client.JID.Domain),
ID: msg.ID,
Body: queryByte,
}
diff --git a/server/extension/iq_private_metacontacts.go b/server/extension/iq_private_metacontacts.bak
similarity index 79%
rename from server/extension/iq_private_metacontacts.go
rename to server/extension/iq_private_metacontacts.bak
index 5819cd9..b8db2ff 100644
--- a/server/extension/iq_private_metacontacts.go
+++ b/server/extension/iq_private_metacontacts.bak
@@ -4,14 +4,15 @@ import (
"encoding/xml"
"dev.sum7.eu/genofire/yaja/messages"
+ "dev.sum7.eu/genofire/yaja/model"
"dev.sum7.eu/genofire/yaja/server/utils"
)
type IQPrivateMetacontact struct {
- iqPrivateExtension
+ IQExtension
}
-func (ex *IQPrivateMetacontact) Handle(msg *messages.IQClient, q *iqPrivateQuery, client *utils.Client) bool {
+func (ex *IQPrivateMetacontact) Handle(msg *messages.IQClient, client *utils.Client) bool {
log := client.Log.WithField("extension", "private-metacontact").WithField("id", msg.ID)
// storage encode
@@ -38,8 +39,8 @@ func (ex *IQPrivateMetacontact) Handle(msg *messages.IQClient, q *iqPrivateQuery
// reply
client.Messages <- &messages.IQClient{
Type: messages.IQTypeResult,
- To: client.JID.String(),
- From: client.JID.Domain,
+ To: client.JID,
+ From: model.NewJID(client.JID.Domain),
ID: msg.ID,
Body: queryByte,
}
diff --git a/server/extension/iq_private_roster.go b/server/extension/iq_private_roster.bak
similarity index 81%
rename from server/extension/iq_private_roster.go
rename to server/extension/iq_private_roster.bak
index 61c67d3..2e13ca5 100644
--- a/server/extension/iq_private_roster.go
+++ b/server/extension/iq_private_roster.bak
@@ -4,14 +4,15 @@ import (
"encoding/xml"
"dev.sum7.eu/genofire/yaja/messages"
+ "dev.sum7.eu/genofire/yaja/model"
"dev.sum7.eu/genofire/yaja/server/utils"
)
type IQPrivateRoster struct {
- iqPrivateExtension
+ IQExtension
}
-func (ex *IQPrivateRoster) Handle(msg *messages.IQClient, q *iqPrivateQuery, client *utils.Client) bool {
+func (ex *IQPrivateRoster) Handle(msg *messages.IQClient, client *utils.Client) bool {
log := client.Log.WithField("extension", "private").WithField("id", msg.ID)
// roster encode
@@ -42,8 +43,8 @@ func (ex *IQPrivateRoster) Handle(msg *messages.IQClient, q *iqPrivateQuery, cli
// reply
client.Messages <- &messages.IQClient{
Type: messages.IQTypeResult,
- To: client.JID.String(),
- From: client.JID.Domain,
+ To: client.JID,
+ From: model.NewJID(client.JID.Domain),
ID: msg.ID,
Body: queryByte,
}
diff --git a/server/extension/iq_roster.go b/server/extension/iq_roster.bak
similarity index 100%
rename from server/extension/iq_roster.go
rename to server/extension/iq_roster.bak
diff --git a/server/toclient/connect.go b/server/toclient/connect.go
index eecf3d7..fed6e1f 100644
--- a/server/toclient/connect.go
+++ b/server/toclient/connect.go
@@ -3,12 +3,12 @@ package toclient
import (
"crypto/tls"
"encoding/base64"
- "encoding/xml"
"fmt"
"strings"
"dev.sum7.eu/genofire/yaja/database"
"dev.sum7.eu/genofire/yaja/messages"
+ "dev.sum7.eu/genofire/yaja/model"
"dev.sum7.eu/genofire/yaja/server/extension"
"dev.sum7.eu/genofire/yaja/server/state"
"dev.sum7.eu/genofire/yaja/server/utils"
@@ -213,28 +213,22 @@ func (state *AuthedStream) Process() state.State {
return nil
}
- var q messages.Bind
- err = xml.Unmarshal(msg.Body, q)
- if err != nil {
+ if msg.Bind == nil {
state.Client.Log.Warn("is no iq bind: ", err)
return nil
}
- if q.Resource == "" {
+ if msg.Bind.Resource == "" {
state.Client.JID.Resource = makeResource()
} else {
- state.Client.JID.Resource = q.Resource
+ state.Client.JID.Resource = msg.Bind.Resource
}
state.Client.Log = state.Client.Log.WithField("jid", state.Client.JID.Full())
state.Client.Out.Encode(&messages.IQClient{
Type: messages.IQTypeResult,
- To: state.Client.JID.String(),
- From: state.Client.JID.Domain,
+ To: state.Client.JID,
+ From: model.NewJID(state.Client.JID.Domain),
ID: msg.ID,
- Body: []byte(fmt.Sprintf(
- `
- %s
- `,
- messages.NSBind, state.Client.JID.Full())),
+ Bind: &messages.Bind{JID: state.Client.JID},
})
return state.Next
diff --git a/server/toclient/register.go b/server/toclient/register.go
index 54ca034..9cdbdab 100644
--- a/server/toclient/register.go
+++ b/server/toclient/register.go
@@ -2,7 +2,6 @@ package toclient
import (
"encoding/xml"
- "fmt"
"dev.sum7.eu/genofire/yaja/database"
"dev.sum7.eu/genofire/yaja/messages"
@@ -42,27 +41,21 @@ func (state *RegisterFormRequest) Process() state.State {
state.Client.Log.Warn("iq with error: ", msg.Error.Code)
return state
}
- type query struct {
- XMLName xml.Name `xml:"query"`
- }
- q := &query{}
- err := xml.Unmarshal(msg.Body, q)
- if q.XMLName.Space != messages.NSIQRegister || err != nil {
- state.Client.Log.Warn("is no iq register: ", err)
+ if msg.PrivateRegister == nil {
+ state.Client.Log.Warn("is no iq register")
return nil
}
state.Client.Out.Encode(&messages.IQClient{
Type: messages.IQTypeResult,
- To: state.Client.JID.String(),
- From: state.Client.JID.Domain,
+ To: state.Client.JID,
+ From: model.NewJID(state.Client.JID.Domain),
ID: msg.ID,
- Body: []byte(fmt.Sprintf(`
- Choose a username and password for use with this service.
-
-
-
- `, messages.NSIQRegister)),
+ PrivateRegister: &messages.IQPrivateRegister{
+ Instructions: "Choose a username and password for use with this service.",
+ Username: "",
+ Password: "",
+ },
})
return state.Next
}
@@ -103,32 +96,22 @@ func (state *RegisterRequest) Process() state.State {
state.Client.Log.Warn("iq with error: ", msg.Error.Code)
return state
}
- type query struct {
- XMLName xml.Name `xml:"query"`
- Username string `xml:"username"`
- Password string `xml:"password"`
- }
- q := &query{}
- err = xml.Unmarshal(msg.Body, q)
- if err != nil {
+ if msg.PrivateRegister == nil {
state.Client.Log.Warn("is no iq register: ", err)
return nil
}
- state.Client.JID.Local = q.Username
+ state.Client.JID.Local = msg.PrivateRegister.Username
state.Client.Log = state.Client.Log.WithField("jid", state.Client.JID.Full())
- account := model.NewAccount(state.Client.JID, q.Password)
+ account := model.NewAccount(state.Client.JID, msg.PrivateRegister.Password)
err = state.database.AddAccount(account)
if err != nil {
state.Client.Out.Encode(&messages.IQClient{
- Type: messages.IQTypeResult,
- To: state.Client.JID.String(),
- From: state.Client.JID.Domain,
- ID: msg.ID,
- Body: []byte(fmt.Sprintf(`
- %s
- %s
- `, messages.NSIQRegister, q.Username, q.Password)),
+ Type: messages.IQTypeResult,
+ To: state.Client.JID,
+ From: model.NewJID(state.Client.JID.Domain),
+ ID: msg.ID,
+ PrivateRegister: msg.PrivateRegister,
Error: &messages.ErrorClient{
Code: "409",
Type: "cancel",
@@ -143,8 +126,8 @@ func (state *RegisterRequest) Process() state.State {
}
state.Client.Out.Encode(&messages.IQClient{
Type: messages.IQTypeResult,
- To: state.Client.JID.String(),
- From: state.Client.JID.Domain,
+ To: state.Client.JID,
+ From: model.NewJID(state.Client.JID.Domain),
ID: msg.ID,
})