first working tester-daemon
This commit is contained in:
parent
a67d26d5a2
commit
5076f53d81
|
@ -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, "<auth xmlns='%s' mechanism='PLAIN'>%s</auth>\n", messages.NSSASL, enc)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if m == "DIGEST-MD5" {
|
||||||
|
mechanism = m
|
||||||
|
// Digest-MD5 authentication
|
||||||
|
fmt.Fprintf(client.conn, "<auth xmlns='%s' mechanism='DIGEST-MD5'/>\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, "<response xmlns='%s'>%s</response>\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, "<response xmlns='%s'/>\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)
|
||||||
|
}
|
199
client/client.go
199
client/client.go
|
@ -1,14 +1,10 @@
|
||||||
package client
|
package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/md5"
|
|
||||||
"crypto/rand"
|
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"encoding/base64"
|
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
@ -26,8 +22,23 @@ type Client struct {
|
||||||
JID *model.JID
|
JID *model.JID
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClient(jid model.JID, password string) (*Client, error) {
|
func NewClient(jid *model.JID, password string) (*Client, error) {
|
||||||
conn, err := net.Dial("tcp", jid.Domain+":5222")
|
_, 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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -36,7 +47,7 @@ func NewClient(jid model.JID, password string) (*Client, error) {
|
||||||
In: xml.NewDecoder(conn),
|
In: xml.NewDecoder(conn),
|
||||||
Out: xml.NewEncoder(conn),
|
Out: xml.NewEncoder(conn),
|
||||||
|
|
||||||
JID: &jid,
|
JID: jid,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = client.connect(password); err != nil {
|
if err = client.connect(password); err != nil {
|
||||||
|
@ -54,51 +65,31 @@ func (c *Client) Close() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (client *Client) Read() (*xml.StartElement, error) {
|
func (client *Client) startStream() (*messages.StreamFeatures, 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 {
|
|
||||||
// XMPP-Connection
|
// XMPP-Connection
|
||||||
_, err := fmt.Fprintf(client.conn, "<?xml version='1.0'?>\n"+
|
_, err := fmt.Fprintf(client.conn, "<?xml version='1.0'?>\n"+
|
||||||
"<stream:stream to='%s' xmlns='%s'\n"+
|
"<stream:stream to='%s' xmlns='%s'\n"+
|
||||||
" xmlns:stream='%s' version='1.0'>\n",
|
" xmlns:stream='%s' version='1.0'>\n",
|
||||||
model.XMLEscape(client.JID.Domain), messages.NSClient, messages.NSStream)
|
model.XMLEscape(client.JID.Domain), messages.NSClient, messages.NSStream)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
element, err := client.Read()
|
element, err := client.Read()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
if element.Name.Space != messages.NSStream || element.Name.Local != "stream" {
|
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 {
|
func (client *Client) connect(password string) error {
|
||||||
if err := client.init(); err != nil {
|
if _, err := client.startStream(); err != nil {
|
||||||
return err
|
|
||||||
}
|
|
||||||
var f messages.StreamFeatures
|
|
||||||
if err := client.ReadElement(&f); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := client.Out.Encode(&messages.TLSStartTLS{}); err != nil {
|
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 {
|
if err := tlsconn.VerifyHostname(client.JID.Domain); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := client.init(); err != nil {
|
err := client.auth(password)
|
||||||
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, "<auth xmlns='%s' mechanism='PLAIN'>%s</auth>\n", messages.NSSASL, enc)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if m == "DIGEST-MD5" {
|
|
||||||
mechanism = m
|
|
||||||
// Digest-MD5 authentication
|
|
||||||
fmt.Fprintf(client.conn, "<auth xmlns='%s' mechanism='DIGEST-MD5'/>\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, "<response xmlns='%s'>%s</response>\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, "<response xmlns='%s'/>\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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if element.Name.Local != "success" {
|
_, err = client.startStream()
|
||||||
return errors.New("auth failed: " + element.Name.Local)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = client.init()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := client.ReadElement(&f); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// bind to resource
|
// bind to resource
|
||||||
var msg string
|
bind := &messages.Bind{}
|
||||||
if client.JID.Resource == "" {
|
if client.JID.Resource != "" {
|
||||||
msg = fmt.Sprintf("<bind xmlns='%s'></bind>", messages.NSBind)
|
bind.Resource = client.JID.Resource
|
||||||
} else {
|
|
||||||
msg = fmt.Sprintf(
|
|
||||||
`<bind xmlns='%s'>
|
|
||||||
<resource>%s</resource>
|
|
||||||
</bind>`,
|
|
||||||
messages.NSBind, client.JID.Resource)
|
|
||||||
}
|
}
|
||||||
client.Out.Encode(&messages.IQClient{
|
client.Out.Encode(&messages.IQClient{
|
||||||
Type: messages.IQTypeSet,
|
Type: messages.IQTypeSet,
|
||||||
To: client.JID.Domain,
|
To: model.NewJID(client.JID.Domain),
|
||||||
From: client.JID.Full(),
|
From: client.JID,
|
||||||
ID: utils.CreateCookieString(),
|
ID: utils.CreateCookieString(),
|
||||||
Body: []byte(msg),
|
Bind: bind,
|
||||||
})
|
})
|
||||||
|
|
||||||
var iq messages.IQClient
|
var iq messages.IQClient
|
||||||
|
@ -237,39 +147,10 @@ func (client *Client) connect(password string) error {
|
||||||
client.JID.Domain = iq.Bind.JID.Domain
|
client.JID.Domain = iq.Bind.JID.Domain
|
||||||
client.JID.Resource = iq.Bind.JID.Resource
|
client.JID.Resource = iq.Bind.JID.Resource
|
||||||
} else {
|
} else {
|
||||||
return errors.New(string(iq.Body))
|
return errors.New(fmt.Sprintf("%v", iq.Other))
|
||||||
}
|
}
|
||||||
// set status
|
// set status
|
||||||
client.Out.Encode(&messages.PresenceClient{Show: "online", Status: "yaja client"})
|
client.Send(&messages.PresenceClient{Show: "online", Status: "yaja client"})
|
||||||
|
|
||||||
return nil
|
return err
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -1,3 +1,10 @@
|
||||||
package daemon
|
package daemon
|
||||||
|
|
||||||
var configPath string
|
import (
|
||||||
|
"dev.sum7.eu/genofire/golang-lib/worker"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
configPath string
|
||||||
|
statesaveWorker *worker.Worker
|
||||||
|
)
|
||||||
|
|
|
@ -25,7 +25,6 @@ import (
|
||||||
var (
|
var (
|
||||||
serverConfig = &serverDaemon.Config{}
|
serverConfig = &serverDaemon.Config{}
|
||||||
db = &database.State{}
|
db = &database.State{}
|
||||||
statesaveWorker *worker.Worker
|
|
||||||
srv *server.Server
|
srv *server.Server
|
||||||
certs *tls.Config
|
certs *tls.Config
|
||||||
extensionsClient extension.Extensions
|
extensionsClient extension.Extensions
|
||||||
|
@ -179,25 +178,27 @@ func reload() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|
||||||
extensionsClient = append(extensionsClient,
|
extensionsClient = append(extensionsClient,
|
||||||
&extension.Message{},
|
&extension.Message{},
|
||||||
&extension.Presence{},
|
&extension.Presence{},
|
||||||
extension.IQExtensions{
|
extension.IQExtensions{
|
||||||
&extension.IQPrivate{},
|
//&extension.IQPrivateBookmark{},
|
||||||
|
//&extension.IQPrivateMetacontact{},
|
||||||
|
//&extension.IQPrivateRoster{},
|
||||||
&extension.IQPing{},
|
&extension.IQPing{},
|
||||||
&extension.IQLast{},
|
//&extension.IQLast{},
|
||||||
&extension.IQDisco{Database: db},
|
//&extension.IQDisco{Database: db},
|
||||||
&extension.IQRoster{Database: db},
|
//&extension.IQRoster{Database: db},
|
||||||
&extension.IQExtensionDiscovery{GetSpaces: func() []string {
|
//&extension.IQExtensionDiscovery{GetSpaces: func() []string {
|
||||||
return extensionsClient.Spaces()
|
// return extensionsClient.Spaces()
|
||||||
}},
|
//}},
|
||||||
})
|
})
|
||||||
|
|
||||||
extensionsServer = append(extensionsServer,
|
extensionsServer = append(extensionsServer,
|
||||||
extension.IQExtensions{
|
extension.IQExtensions{
|
||||||
&extension.IQPing{},
|
&extension.IQPing{},
|
||||||
})
|
})
|
||||||
|
|
||||||
ServerCMD.Flags().StringVarP(&configPath, "config", "c", "yaja-server.conf", "Path to configuration file")
|
ServerCMD.Flags().StringVarP(&configPath, "config", "c", "yaja-server.conf", "Path to configuration file")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
"golang.org/x/crypto/acme/autocert"
|
"golang.org/x/crypto/acme/autocert"
|
||||||
|
|
||||||
"dev.sum7.eu/genofire/golang-lib/file"
|
"dev.sum7.eu/genofire/golang-lib/file"
|
||||||
|
@ -15,12 +16,15 @@ import (
|
||||||
"dev.sum7.eu/genofire/yaja/client"
|
"dev.sum7.eu/genofire/yaja/client"
|
||||||
"dev.sum7.eu/genofire/yaja/daemon/tester"
|
"dev.sum7.eu/genofire/yaja/daemon/tester"
|
||||||
"dev.sum7.eu/genofire/yaja/messages"
|
"dev.sum7.eu/genofire/yaja/messages"
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
var configTester = &tester.Config{}
|
var (
|
||||||
|
configTester = &tester.Config{}
|
||||||
|
testerInstance = tester.NewTester()
|
||||||
|
testerWorker *worker.Worker
|
||||||
|
)
|
||||||
|
|
||||||
// TesterCMD represents the serve command
|
// TesterCMD represents the serve command
|
||||||
var TesterCMD = &cobra.Command{
|
var TesterCMD = &cobra.Command{
|
||||||
|
@ -35,15 +39,10 @@ var TesterCMD = &cobra.Command{
|
||||||
|
|
||||||
log.SetLevel(configTester.Logging)
|
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)
|
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)
|
// https server to handle acme (by letsencrypt)
|
||||||
hs := &http.Server{
|
hs := &http.Server{
|
||||||
Addr: configTester.Webserver,
|
Addr: configTester.Webserver,
|
||||||
|
@ -71,19 +70,27 @@ var TesterCMD = &cobra.Command{
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("unable to connect with main jabber client: ", err)
|
log.Fatal("unable to connect with main jabber client: ", err)
|
||||||
}
|
}
|
||||||
|
defer mainClient.Close()
|
||||||
|
|
||||||
for _, admin := range configTester.Admins {
|
for _, admin := range configTester.Admins {
|
||||||
mainClient.Out.Encode(&messages.MessageClient{
|
mainClient.Send(&messages.MessageClient{
|
||||||
From: mainClient.JID.Full(),
|
To: admin,
|
||||||
To: admin.Full(),
|
|
||||||
Type: "chat",
|
Type: "chat",
|
||||||
Body: "yaja tester starts",
|
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
|
// Wait for INT/TERM
|
||||||
sigs := make(chan os.Signal, 1)
|
sigs := make(chan os.Signal, 1)
|
||||||
|
@ -105,10 +112,11 @@ var TesterCMD = &cobra.Command{
|
||||||
}
|
}
|
||||||
|
|
||||||
func quitTester() {
|
func quitTester() {
|
||||||
|
testerWorker.Close()
|
||||||
|
testerInstance.Close()
|
||||||
srv.Close()
|
srv.Close()
|
||||||
statesaveWorker.Close()
|
|
||||||
|
|
||||||
file.SaveJSON(configTester.StatePath, db)
|
file.SaveJSON(configTester.AccountsPath, db)
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|
|
@ -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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,13 +6,14 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
TLSDir string `toml:"tlsdir"`
|
TLSDir string `toml:"tlsdir"`
|
||||||
StatePath string `toml:"state_path"`
|
AccountsPath string `toml:"accounts_path"`
|
||||||
Logging log.Level `toml:"logging"`
|
OutputPath string `toml:"output_path"`
|
||||||
Webserver string `toml:"webserver"`
|
Logging log.Level `toml:"logging"`
|
||||||
Admins []model.JID `toml:"admins"`
|
Webserver string `toml:"webserver"`
|
||||||
Client struct {
|
Admins []*model.JID `toml:"admins"`
|
||||||
JID model.JID `toml:"jid"`
|
Client struct {
|
||||||
Password string `toml:"password"`
|
JID *model.JID `toml:"jid"`
|
||||||
|
Password string `toml:"password"`
|
||||||
} `toml:"client"`
|
} `toml:"client"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -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 = ""
|
||||||
|
}
|
||||||
|
}
|
|
@ -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")
|
||||||
|
}
|
|
@ -15,12 +15,6 @@ type StreamFeatures struct {
|
||||||
Session bool
|
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
|
// RFC 3920 C.3 TLS name space
|
||||||
type TLSStartTLS struct {
|
type TLSStartTLS struct {
|
||||||
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-tls starttls"`
|
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-tls starttls"`
|
||||||
|
|
|
@ -2,11 +2,19 @@ package messages
|
||||||
|
|
||||||
import "encoding/xml"
|
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
|
// ErrorClient element
|
||||||
type ErrorClient struct {
|
type ErrorClient struct {
|
||||||
XMLName xml.Name `xml:"jabber:client error"`
|
XMLName xml.Name `xml:"jabber:client error"`
|
||||||
Code string `xml:"code,attr"`
|
Code string `xml:"code,attr"`
|
||||||
Type string `xml:"type,attr"`
|
Type string `xml:"type,attr"`
|
||||||
Any xml.Name `xml:",any"`
|
|
||||||
Text string `xml:"text"`
|
Text string `xml:"text"`
|
||||||
|
|
||||||
|
Any xml.Name `xml:",any"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
package messages
|
package messages
|
||||||
|
|
||||||
import "encoding/xml"
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
|
||||||
|
"dev.sum7.eu/genofire/yaja/model"
|
||||||
|
)
|
||||||
|
|
||||||
type IQType string
|
type IQType string
|
||||||
|
|
||||||
|
@ -13,13 +17,32 @@ const (
|
||||||
|
|
||||||
// IQ element - info/query
|
// IQ element - info/query
|
||||||
type IQClient struct {
|
type IQClient struct {
|
||||||
XMLName xml.Name `xml:"jabber:client iq"`
|
XMLName xml.Name `xml:"jabber:client iq"`
|
||||||
From string `xml:"from,attr"`
|
From *model.JID `xml:"from,attr"`
|
||||||
ID string `xml:"id,attr"`
|
ID string `xml:"id,attr"`
|
||||||
To string `xml:"to,attr"`
|
To *model.JID `xml:"to,attr"`
|
||||||
Type IQType `xml:"type,attr"`
|
Type IQType `xml:"type,attr"`
|
||||||
Error *ErrorClient `xml:"error"`
|
Error *ErrorClient `xml:"error"`
|
||||||
Bind Bind `xml:"bind"`
|
Bind *Bind
|
||||||
Body []byte `xml:",innerxml"`
|
Ping *Ping
|
||||||
// RosterRequest - better detection of iq's
|
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"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,19 @@
|
||||||
package messages
|
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
|
type PresenceType string
|
||||||
|
|
||||||
|
@ -16,30 +29,46 @@ const (
|
||||||
|
|
||||||
// PresenceClient element
|
// PresenceClient element
|
||||||
type PresenceClient struct {
|
type PresenceClient struct {
|
||||||
XMLName xml.Name `xml:"jabber:client presence"`
|
XMLName xml.Name `xml:"jabber:client presence"`
|
||||||
From string `xml:"from,attr,omitempty"`
|
From *model.JID `xml:"from,attr,omitempty"`
|
||||||
ID string `xml:"id,attr,omitempty"`
|
ID string `xml:"id,attr,omitempty"`
|
||||||
To string `xml:"to,attr,omitempty"`
|
To *model.JID `xml:"to,attr,omitempty"`
|
||||||
Type string `xml:"type,attr,omitempty"`
|
Type PresenceType `xml:"type,attr,omitempty"`
|
||||||
Lang string `xml:"lang,attr,omitempty"`
|
Lang string `xml:"lang,attr,omitempty"`
|
||||||
|
|
||||||
Show string `xml:"show,omitempty"` // away, chat, dnd, xa
|
Show string `xml:"show,omitempty"` // away, chat, dnd, xa
|
||||||
Status string `xml:"status,omitempty"` // sb []clientText
|
Status string `xml:"status,omitempty"` // sb []clientText
|
||||||
Priority string `xml:"priority,omitempty"`
|
Priority string `xml:"priority,omitempty"`
|
||||||
// Caps *ClientCaps `xml:"c"`
|
// 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
|
// MessageClient element
|
||||||
type MessageClient struct {
|
type MessageClient struct {
|
||||||
XMLName xml.Name `xml:"jabber:client message"`
|
XMLName xml.Name `xml:"jabber:client message"`
|
||||||
From string `xml:"from,attr,omitempty"`
|
From *model.JID `xml:"from,attr,omitempty"`
|
||||||
ID string `xml:"id,attr,omitempty"`
|
ID string `xml:"id,attr,omitempty"`
|
||||||
To string `xml:"to,attr,omitempty"`
|
To *model.JID `xml:"to,attr,omitempty"`
|
||||||
Type string `xml:"type,attr,omitempty"`
|
Type ChatType `xml:"type,attr,omitempty"`
|
||||||
Lang string `xml:"lang,attr,omitempty"`
|
Lang string `xml:"lang,attr,omitempty"`
|
||||||
Subject string `xml:"subject"`
|
Subject string `xml:"subject"`
|
||||||
Body string `xml:"body"`
|
Body string `xml:"body"`
|
||||||
Thread string `xml:"thread"`
|
Thread string `xml:"thread"`
|
||||||
|
// Any hasn't matched element
|
||||||
|
Other []XMLElement `xml:",any"`
|
||||||
|
|
||||||
|
Delay *Delay `xml:"delay"`
|
||||||
|
Error *ErrorClient
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -36,6 +36,9 @@ func NewJID(jidString string) *JID {
|
||||||
|
|
||||||
// Bare get the "bare" jid
|
// Bare get the "bare" jid
|
||||||
func (jid *JID) Bare() string {
|
func (jid *JID) Bare() string {
|
||||||
|
if jid == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
if jid.Local != "" {
|
if jid.Local != "" {
|
||||||
return jid.Local + "@" + jid.Domain
|
return jid.Local + "@" + jid.Domain
|
||||||
}
|
}
|
||||||
|
@ -46,6 +49,9 @@ func (jid *JID) String() string { return jid.Bare() }
|
||||||
|
|
||||||
// Full get the "full" jid as string
|
// Full get the "full" jid as string
|
||||||
func (jid *JID) Full() string {
|
func (jid *JID) Full() string {
|
||||||
|
if jid == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
if jid.Resource != "" {
|
if jid.Resource != "" {
|
||||||
return jid.Bare() + "/" + jid.Resource
|
return jid.Bare() + "/" + jid.Resource
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,7 @@ func (iex IQExtensions) Process(element *xml.StartElement, client *utils.Client)
|
||||||
|
|
||||||
// not extensions found
|
// not extensions found
|
||||||
if count != 1 {
|
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
|
return true
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
|
|
||||||
"dev.sum7.eu/genofire/yaja/database"
|
"dev.sum7.eu/genofire/yaja/database"
|
||||||
"dev.sum7.eu/genofire/yaja/messages"
|
"dev.sum7.eu/genofire/yaja/messages"
|
||||||
|
"dev.sum7.eu/genofire/yaja/model"
|
||||||
"dev.sum7.eu/genofire/yaja/server/utils"
|
"dev.sum7.eu/genofire/yaja/server/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -59,8 +60,8 @@ func (ex *IQDisco) Get(msg *messages.IQClient, client *utils.Client) bool {
|
||||||
// reply
|
// reply
|
||||||
client.Messages <- &messages.IQClient{
|
client.Messages <- &messages.IQClient{
|
||||||
Type: messages.IQTypeResult,
|
Type: messages.IQTypeResult,
|
||||||
To: client.JID.String(),
|
To: client.JID,
|
||||||
From: client.JID.Domain,
|
From: model.NewJID(client.JID.Domain),
|
||||||
ID: msg.ID,
|
ID: msg.ID,
|
||||||
Body: queryByte,
|
Body: queryByte,
|
||||||
}
|
}
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
|
|
||||||
"dev.sum7.eu/genofire/yaja/messages"
|
"dev.sum7.eu/genofire/yaja/messages"
|
||||||
|
"dev.sum7.eu/genofire/yaja/model"
|
||||||
"dev.sum7.eu/genofire/yaja/server/utils"
|
"dev.sum7.eu/genofire/yaja/server/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -59,8 +60,8 @@ func (ex *IQExtensionDiscovery) Get(msg *messages.IQClient, client *utils.Client
|
||||||
// replay
|
// replay
|
||||||
client.Messages <- &messages.IQClient{
|
client.Messages <- &messages.IQClient{
|
||||||
Type: messages.IQTypeResult,
|
Type: messages.IQTypeResult,
|
||||||
To: client.JID.String(),
|
To: client.JID,
|
||||||
From: client.JID.Domain,
|
From: model.NewJID(client.JID.Domain),
|
||||||
ID: msg.ID,
|
ID: msg.ID,
|
||||||
Body: queryByte,
|
Body: queryByte,
|
||||||
}
|
}
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
|
|
||||||
"dev.sum7.eu/genofire/yaja/messages"
|
"dev.sum7.eu/genofire/yaja/messages"
|
||||||
|
"dev.sum7.eu/genofire/yaja/model"
|
||||||
"dev.sum7.eu/genofire/yaja/server/utils"
|
"dev.sum7.eu/genofire/yaja/server/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -47,8 +48,8 @@ func (ex *IQLast) Get(msg *messages.IQClient, client *utils.Client) bool {
|
||||||
// reply
|
// reply
|
||||||
client.Messages <- &messages.IQClient{
|
client.Messages <- &messages.IQClient{
|
||||||
Type: messages.IQTypeResult,
|
Type: messages.IQTypeResult,
|
||||||
To: client.JID.String(),
|
To: client.JID,
|
||||||
From: client.JID.Domain,
|
From: model.NewJID(client.JID.Domain),
|
||||||
ID: msg.ID,
|
ID: msg.ID,
|
||||||
Body: queryByte,
|
Body: queryByte,
|
||||||
}
|
}
|
|
@ -1,9 +1,8 @@
|
||||||
package extension
|
package extension
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/xml"
|
|
||||||
|
|
||||||
"dev.sum7.eu/genofire/yaja/messages"
|
"dev.sum7.eu/genofire/yaja/messages"
|
||||||
|
"dev.sum7.eu/genofire/yaja/model"
|
||||||
"dev.sum7.eu/genofire/yaja/server/utils"
|
"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 {
|
func (ex *IQPing) Get(msg *messages.IQClient, client *utils.Client) bool {
|
||||||
log := client.Log.WithField("extension", "ping").WithField("id", msg.ID)
|
log := client.Log.WithField("extension", "ping").WithField("id", msg.ID)
|
||||||
|
|
||||||
// ping encode
|
if msg.Ping == nil {
|
||||||
type ping struct {
|
|
||||||
XMLName xml.Name `xml:"urn:xmpp:ping ping"`
|
|
||||||
}
|
|
||||||
pq := &ping{}
|
|
||||||
if err := xml.Unmarshal(msg.Body, pq); err != nil {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// reply
|
// reply
|
||||||
client.Messages <- &messages.IQClient{
|
client.Messages <- &messages.IQClient{
|
||||||
Type: messages.IQTypeResult,
|
Type: messages.IQTypeResult,
|
||||||
To: client.JID.String(),
|
To: client.JID,
|
||||||
From: client.JID.Domain,
|
From: model.NewJID(client.JID.Domain),
|
||||||
ID: msg.ID,
|
ID: msg.ID,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -4,14 +4,15 @@ import (
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
|
|
||||||
"dev.sum7.eu/genofire/yaja/messages"
|
"dev.sum7.eu/genofire/yaja/messages"
|
||||||
|
"dev.sum7.eu/genofire/yaja/model"
|
||||||
"dev.sum7.eu/genofire/yaja/server/utils"
|
"dev.sum7.eu/genofire/yaja/server/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
type IQPrivateBookmark struct {
|
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)
|
log := client.Log.WithField("extension", "private").WithField("id", msg.ID)
|
||||||
|
|
||||||
// storage encode
|
// storage encode
|
||||||
|
@ -37,8 +38,8 @@ func (ex *IQPrivateBookmark) Handle(msg *messages.IQClient, q *iqPrivateQuery, c
|
||||||
// reply
|
// reply
|
||||||
client.Messages <- &messages.IQClient{
|
client.Messages <- &messages.IQClient{
|
||||||
Type: messages.IQTypeResult,
|
Type: messages.IQTypeResult,
|
||||||
To: client.JID.String(),
|
To: client.JID,
|
||||||
From: client.JID.Domain,
|
From: model.NewJID(client.JID.Domain),
|
||||||
ID: msg.ID,
|
ID: msg.ID,
|
||||||
Body: queryByte,
|
Body: queryByte,
|
||||||
}
|
}
|
|
@ -4,14 +4,15 @@ import (
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
|
|
||||||
"dev.sum7.eu/genofire/yaja/messages"
|
"dev.sum7.eu/genofire/yaja/messages"
|
||||||
|
"dev.sum7.eu/genofire/yaja/model"
|
||||||
"dev.sum7.eu/genofire/yaja/server/utils"
|
"dev.sum7.eu/genofire/yaja/server/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
type IQPrivateMetacontact struct {
|
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)
|
log := client.Log.WithField("extension", "private-metacontact").WithField("id", msg.ID)
|
||||||
|
|
||||||
// storage encode
|
// storage encode
|
||||||
|
@ -38,8 +39,8 @@ func (ex *IQPrivateMetacontact) Handle(msg *messages.IQClient, q *iqPrivateQuery
|
||||||
// reply
|
// reply
|
||||||
client.Messages <- &messages.IQClient{
|
client.Messages <- &messages.IQClient{
|
||||||
Type: messages.IQTypeResult,
|
Type: messages.IQTypeResult,
|
||||||
To: client.JID.String(),
|
To: client.JID,
|
||||||
From: client.JID.Domain,
|
From: model.NewJID(client.JID.Domain),
|
||||||
ID: msg.ID,
|
ID: msg.ID,
|
||||||
Body: queryByte,
|
Body: queryByte,
|
||||||
}
|
}
|
|
@ -4,14 +4,15 @@ import (
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
|
|
||||||
"dev.sum7.eu/genofire/yaja/messages"
|
"dev.sum7.eu/genofire/yaja/messages"
|
||||||
|
"dev.sum7.eu/genofire/yaja/model"
|
||||||
"dev.sum7.eu/genofire/yaja/server/utils"
|
"dev.sum7.eu/genofire/yaja/server/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
type IQPrivateRoster struct {
|
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)
|
log := client.Log.WithField("extension", "private").WithField("id", msg.ID)
|
||||||
|
|
||||||
// roster encode
|
// roster encode
|
||||||
|
@ -42,8 +43,8 @@ func (ex *IQPrivateRoster) Handle(msg *messages.IQClient, q *iqPrivateQuery, cli
|
||||||
// reply
|
// reply
|
||||||
client.Messages <- &messages.IQClient{
|
client.Messages <- &messages.IQClient{
|
||||||
Type: messages.IQTypeResult,
|
Type: messages.IQTypeResult,
|
||||||
To: client.JID.String(),
|
To: client.JID,
|
||||||
From: client.JID.Domain,
|
From: model.NewJID(client.JID.Domain),
|
||||||
ID: msg.ID,
|
ID: msg.ID,
|
||||||
Body: queryByte,
|
Body: queryByte,
|
||||||
}
|
}
|
|
@ -3,12 +3,12 @@ package toclient
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/xml"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"dev.sum7.eu/genofire/yaja/database"
|
"dev.sum7.eu/genofire/yaja/database"
|
||||||
"dev.sum7.eu/genofire/yaja/messages"
|
"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/extension"
|
||||||
"dev.sum7.eu/genofire/yaja/server/state"
|
"dev.sum7.eu/genofire/yaja/server/state"
|
||||||
"dev.sum7.eu/genofire/yaja/server/utils"
|
"dev.sum7.eu/genofire/yaja/server/utils"
|
||||||
|
@ -213,28 +213,22 @@ func (state *AuthedStream) Process() state.State {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var q messages.Bind
|
if msg.Bind == nil {
|
||||||
err = xml.Unmarshal(msg.Body, q)
|
|
||||||
if err != nil {
|
|
||||||
state.Client.Log.Warn("is no iq bind: ", err)
|
state.Client.Log.Warn("is no iq bind: ", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if q.Resource == "" {
|
if msg.Bind.Resource == "" {
|
||||||
state.Client.JID.Resource = makeResource()
|
state.Client.JID.Resource = makeResource()
|
||||||
} else {
|
} 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.Log = state.Client.Log.WithField("jid", state.Client.JID.Full())
|
||||||
state.Client.Out.Encode(&messages.IQClient{
|
state.Client.Out.Encode(&messages.IQClient{
|
||||||
Type: messages.IQTypeResult,
|
Type: messages.IQTypeResult,
|
||||||
To: state.Client.JID.String(),
|
To: state.Client.JID,
|
||||||
From: state.Client.JID.Domain,
|
From: model.NewJID(state.Client.JID.Domain),
|
||||||
ID: msg.ID,
|
ID: msg.ID,
|
||||||
Body: []byte(fmt.Sprintf(
|
Bind: &messages.Bind{JID: state.Client.JID},
|
||||||
`<bind xmlns='%s'>
|
|
||||||
<jid>%s</jid>
|
|
||||||
</bind>`,
|
|
||||||
messages.NSBind, state.Client.JID.Full())),
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return state.Next
|
return state.Next
|
||||||
|
|
|
@ -2,7 +2,6 @@ package toclient
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"dev.sum7.eu/genofire/yaja/database"
|
"dev.sum7.eu/genofire/yaja/database"
|
||||||
"dev.sum7.eu/genofire/yaja/messages"
|
"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)
|
state.Client.Log.Warn("iq with error: ", msg.Error.Code)
|
||||||
return state
|
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 {
|
if msg.PrivateRegister == nil {
|
||||||
state.Client.Log.Warn("is no iq register: ", err)
|
state.Client.Log.Warn("is no iq register")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
state.Client.Out.Encode(&messages.IQClient{
|
state.Client.Out.Encode(&messages.IQClient{
|
||||||
Type: messages.IQTypeResult,
|
Type: messages.IQTypeResult,
|
||||||
To: state.Client.JID.String(),
|
To: state.Client.JID,
|
||||||
From: state.Client.JID.Domain,
|
From: model.NewJID(state.Client.JID.Domain),
|
||||||
ID: msg.ID,
|
ID: msg.ID,
|
||||||
Body: []byte(fmt.Sprintf(`<query xmlns='%s'><instructions>
|
PrivateRegister: &messages.IQPrivateRegister{
|
||||||
Choose a username and password for use with this service.
|
Instructions: "Choose a username and password for use with this service.",
|
||||||
</instructions>
|
Username: "",
|
||||||
<username/>
|
Password: "",
|
||||||
<password/>
|
},
|
||||||
</query>`, messages.NSIQRegister)),
|
|
||||||
})
|
})
|
||||||
return state.Next
|
return state.Next
|
||||||
}
|
}
|
||||||
|
@ -103,32 +96,22 @@ func (state *RegisterRequest) Process() state.State {
|
||||||
state.Client.Log.Warn("iq with error: ", msg.Error.Code)
|
state.Client.Log.Warn("iq with error: ", msg.Error.Code)
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
type query struct {
|
if msg.PrivateRegister == nil {
|
||||||
XMLName xml.Name `xml:"query"`
|
|
||||||
Username string `xml:"username"`
|
|
||||||
Password string `xml:"password"`
|
|
||||||
}
|
|
||||||
q := &query{}
|
|
||||||
err = xml.Unmarshal(msg.Body, q)
|
|
||||||
if err != nil {
|
|
||||||
state.Client.Log.Warn("is no iq register: ", err)
|
state.Client.Log.Warn("is no iq register: ", err)
|
||||||
return nil
|
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())
|
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)
|
err = state.database.AddAccount(account)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
state.Client.Out.Encode(&messages.IQClient{
|
state.Client.Out.Encode(&messages.IQClient{
|
||||||
Type: messages.IQTypeResult,
|
Type: messages.IQTypeResult,
|
||||||
To: state.Client.JID.String(),
|
To: state.Client.JID,
|
||||||
From: state.Client.JID.Domain,
|
From: model.NewJID(state.Client.JID.Domain),
|
||||||
ID: msg.ID,
|
ID: msg.ID,
|
||||||
Body: []byte(fmt.Sprintf(`<query xmlns='%s'>
|
PrivateRegister: msg.PrivateRegister,
|
||||||
<username>%s</username>
|
|
||||||
<password>%s</password>
|
|
||||||
</query>`, messages.NSIQRegister, q.Username, q.Password)),
|
|
||||||
Error: &messages.ErrorClient{
|
Error: &messages.ErrorClient{
|
||||||
Code: "409",
|
Code: "409",
|
||||||
Type: "cancel",
|
Type: "cancel",
|
||||||
|
@ -143,8 +126,8 @@ func (state *RegisterRequest) Process() state.State {
|
||||||
}
|
}
|
||||||
state.Client.Out.Encode(&messages.IQClient{
|
state.Client.Out.Encode(&messages.IQClient{
|
||||||
Type: messages.IQTypeResult,
|
Type: messages.IQTypeResult,
|
||||||
To: state.Client.JID.String(),
|
To: state.Client.JID,
|
||||||
From: state.Client.JID.Domain,
|
From: model.NewJID(state.Client.JID.Domain),
|
||||||
ID: msg.ID,
|
ID: msg.ID,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
Reference in New Issue