improve client
This commit is contained in:
		
							parent
							
								
									f4bc539cd7
								
							
						
					
					
						commit
						654d0306cf
					
				| 
						 | 
				
			
			@ -19,25 +19,27 @@ func (client *Client) auth(password string) error {
 | 
			
		|||
	}
 | 
			
		||||
	//auth:
 | 
			
		||||
	mechanism := ""
 | 
			
		||||
	challenge := &messages.SASLChallenge{}
 | 
			
		||||
	response := &messages.SASLResponse{}
 | 
			
		||||
	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 == "SCRAM-SHA-1" {
 | 
			
		||||
			/*
 | 
			
		||||
				mechanism = m
 | 
			
		||||
				TODO
 | 
			
		||||
				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 {
 | 
			
		||||
			client.Out.Encode(&messages.SASLAuth{
 | 
			
		||||
				Mechanism: m,
 | 
			
		||||
			})
 | 
			
		||||
			if err := client.ReadElement(challenge); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			b, err := base64.StdEncoding.DecodeString(string(ch))
 | 
			
		||||
			b, err := base64.StdEncoding.DecodeString(challenge.Body)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			@ -62,29 +64,37 @@ func (client *Client) auth(password string) error {
 | 
			
		|||
			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)))
 | 
			
		||||
			response.Body = base64.StdEncoding.EncodeToString([]byte(message))
 | 
			
		||||
			client.Out.Encode(response)
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		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))
 | 
			
		||||
			client.Out.Encode(&messages.SASLAuth{
 | 
			
		||||
				Mechanism: "PLAIN",
 | 
			
		||||
				Body:      string(enc),
 | 
			
		||||
			})
 | 
			
		||||
 | 
			
		||||
			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)
 | 
			
		||||
		return fmt.Errorf("PLAIN authentication is not an option: %s", 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)
 | 
			
		||||
	fail := messages.SASLFailure{}
 | 
			
		||||
	if err := client.In.DecodeElement(&fail, element); err == nil {
 | 
			
		||||
		return errors.New(messages.XMLChildrenString(fail) + " : " + fail.Body)
 | 
			
		||||
	}
 | 
			
		||||
	if err := client.In.DecodeElement(&messages.SASLSuccess{}, element); err != nil {
 | 
			
		||||
		return errors.New("auth failed - with unexpected answer")
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,7 +11,6 @@ import (
 | 
			
		|||
 | 
			
		||||
	"dev.sum7.eu/genofire/yaja/messages"
 | 
			
		||||
	"dev.sum7.eu/genofire/yaja/model"
 | 
			
		||||
	"dev.sum7.eu/genofire/yaja/server/utils"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Client holds XMPP connection opitons
 | 
			
		||||
| 
						 | 
				
			
			@ -24,7 +23,7 @@ type Client struct {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func NewClient(jid *model.JID, password string) (*Client, error) {
 | 
			
		||||
	return NewClientProtocolDuration(jid, password, "tcp", -1)
 | 
			
		||||
	return NewClientProtocolDuration(jid, password, "tcp", 0)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewClientProtocolDuration(jid *model.JID, password string, proto string, timeout time.Duration) (*Client, error) {
 | 
			
		||||
| 
						 | 
				
			
			@ -43,12 +42,7 @@ func NewClientProtocolDuration(jid *model.JID, password string, proto string, ti
 | 
			
		|||
	if len(a) == 1 {
 | 
			
		||||
		addr += ":5222"
 | 
			
		||||
	}
 | 
			
		||||
	var conn net.Conn
 | 
			
		||||
	if timeout >= 0 {
 | 
			
		||||
		conn, err = net.DialTimeout(proto, addr, timeout)
 | 
			
		||||
	} else {
 | 
			
		||||
		conn, err = net.Dial(proto, addr)
 | 
			
		||||
	}
 | 
			
		||||
	conn, err := net.DialTimeout(proto, addr, timeout)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -124,12 +118,11 @@ func (client *Client) connect(password string) error {
 | 
			
		|||
	if err := tlsconn.VerifyHostname(client.JID.Domain); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	err := client.auth(password)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
	if err := client.auth(password); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	_, err = client.startStream()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
 | 
			
		||||
	if _, err := client.startStream(); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	// bind to resource
 | 
			
		||||
| 
						 | 
				
			
			@ -137,23 +130,22 @@ func (client *Client) connect(password string) error {
 | 
			
		|||
	if client.JID.Resource != "" {
 | 
			
		||||
		bind.Resource = client.JID.Resource
 | 
			
		||||
	}
 | 
			
		||||
	client.Out.Encode(&messages.IQClient{
 | 
			
		||||
	if err := client.Out.Encode(&messages.IQClient{
 | 
			
		||||
		Type: messages.IQTypeSet,
 | 
			
		||||
		To:   model.NewJID(client.JID.Domain),
 | 
			
		||||
		From: client.JID,
 | 
			
		||||
		ID:   utils.CreateCookieString(),
 | 
			
		||||
		To:   model.NewJID(client.JID.Domain),
 | 
			
		||||
		Bind: bind,
 | 
			
		||||
	})
 | 
			
		||||
	}); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var iq messages.IQClient
 | 
			
		||||
	if err := client.ReadElement(&iq); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if iq.Error != nil {
 | 
			
		||||
		if iq.Error.Type == messages.ErrorClientTypeCancel && iq.Error.ServiceUnavailable != nil {
 | 
			
		||||
			//TODO binding service unavailable
 | 
			
		||||
		} else {
 | 
			
		||||
			return errors.New(fmt.Sprintf("recv error on iq>bind: %s[%s]: %s -> %v", iq.Error.Code, iq.Error.Type, iq.Error.Text, iq.Error.Other))
 | 
			
		||||
		if iq.Error.ServiceUnavailable == nil {
 | 
			
		||||
			return errors.New(fmt.Sprintf("recv error on iq>bind: %s[%s]: %s -> %s -> %s", iq.Error.Code, iq.Error.Type, iq.Error.Text, messages.XMLChildrenString(iq.Error.StanzaErrorGroup), messages.XMLChildrenString(iq.Error.Other)))
 | 
			
		||||
		}
 | 
			
		||||
	} else if iq.Bind == nil {
 | 
			
		||||
		return errors.New("<iq> result missing <bind>")
 | 
			
		||||
| 
						 | 
				
			
			@ -162,10 +154,8 @@ 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(fmt.Sprintf("%v", iq.Other))
 | 
			
		||||
		return errors.New(messages.XMLChildrenString(iq.Other))
 | 
			
		||||
	}
 | 
			
		||||
	// set status
 | 
			
		||||
	err = client.Send(&messages.PresenceClient{Show: messages.ShowTypeXA, Status: "online"})
 | 
			
		||||
 | 
			
		||||
	return err
 | 
			
		||||
	return client.Send(&messages.PresenceClient{Show: messages.PresenceShowXA, Status: "online"})
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,7 +5,6 @@ import (
 | 
			
		|||
	"log"
 | 
			
		||||
 | 
			
		||||
	"dev.sum7.eu/genofire/yaja/messages"
 | 
			
		||||
	"dev.sum7.eu/genofire/yaja/server/utils"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (client *Client) Read() (*xml.StartElement, error) {
 | 
			
		||||
| 
						 | 
				
			
			@ -51,25 +50,16 @@ func (client *Client) Send(p interface{}) error {
 | 
			
		|||
	msg, ok := p.(*messages.MessageClient)
 | 
			
		||||
	if ok {
 | 
			
		||||
		msg.From = client.JID
 | 
			
		||||
		if msg.ID == "" {
 | 
			
		||||
			msg.ID = utils.CreateCookieString()
 | 
			
		||||
		}
 | 
			
		||||
		return client.Out.Encode(msg)
 | 
			
		||||
	}
 | 
			
		||||
	iq, ok := p.(*messages.IQClient)
 | 
			
		||||
	if ok {
 | 
			
		||||
		iq.From = client.JID
 | 
			
		||||
		if iq.ID == "" {
 | 
			
		||||
			iq.ID = utils.CreateCookieString()
 | 
			
		||||
		}
 | 
			
		||||
		return client.Out.Encode(iq)
 | 
			
		||||
	}
 | 
			
		||||
	pc, ok := p.(*messages.PresenceClient)
 | 
			
		||||
	if ok {
 | 
			
		||||
		pc.From = client.JID
 | 
			
		||||
		if pc.ID == "" {
 | 
			
		||||
			pc.ID = utils.CreateCookieString()
 | 
			
		||||
		}
 | 
			
		||||
		return client.Out.Encode(pc)
 | 
			
		||||
	}
 | 
			
		||||
	return client.Out.Encode(p)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,7 +6,6 @@ import (
 | 
			
		|||
	log "github.com/sirupsen/logrus"
 | 
			
		||||
 | 
			
		||||
	"dev.sum7.eu/genofire/yaja/messages"
 | 
			
		||||
	"dev.sum7.eu/genofire/yaja/server/utils"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (t *Tester) StartBot(status *Status) {
 | 
			
		||||
| 
						 | 
				
			
			@ -24,7 +23,7 @@ func (t *Tester) StartBot(status *Status) {
 | 
			
		|||
		errMSG := &messages.StreamError{}
 | 
			
		||||
		err = status.client.In.DecodeElement(errMSG, element)
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			logCTX.Errorf("recv stream error: %s: %v", errMSG.Text, errMSG.Any)
 | 
			
		||||
			logCTX.Errorf("recv stream error: %s: %s", errMSG.Text, messages.XMLChildrenString(errMSG.Any))
 | 
			
		||||
			status.client.Close()
 | 
			
		||||
			status.Login = false
 | 
			
		||||
			return
 | 
			
		||||
| 
						 | 
				
			
			@ -40,7 +39,7 @@ func (t *Tester) StartBot(status *Status) {
 | 
			
		|||
				iq.From = status.client.JID
 | 
			
		||||
				status.client.Out.Encode(iq)
 | 
			
		||||
			} else {
 | 
			
		||||
				logCTX.Warnf("unsupport iq recv: %v", iq)
 | 
			
		||||
				logCTX.Warnf("recv iq unsupport: %s", messages.XMLChildrenString(iq))
 | 
			
		||||
			}
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -51,7 +50,7 @@ func (t *Tester) StartBot(status *Status) {
 | 
			
		|||
			sender := pres.From
 | 
			
		||||
			logPres := logCTX.WithField("from", sender.Full())
 | 
			
		||||
			if pres.Type == messages.PresenceTypeSubscribe {
 | 
			
		||||
				logPres.Debugf("recv subscribe")
 | 
			
		||||
				logPres.Debugf("recv presence subscribe")
 | 
			
		||||
				pres.Type = messages.PresenceTypeSubscribed
 | 
			
		||||
				pres.To = sender
 | 
			
		||||
				pres.From = nil
 | 
			
		||||
| 
						 | 
				
			
			@ -59,17 +58,19 @@ func (t *Tester) StartBot(status *Status) {
 | 
			
		|||
				logPres.Debugf("accept new subscribe")
 | 
			
		||||
 | 
			
		||||
				pres.Type = messages.PresenceTypeSubscribe
 | 
			
		||||
				pres.ID = utils.CreateCookieString()
 | 
			
		||||
				pres.ID = ""
 | 
			
		||||
				status.client.Out.Encode(pres)
 | 
			
		||||
				logPres.Info("request also subscribe")
 | 
			
		||||
			} else if pres.Type == messages.PresenceTypeSubscribed {
 | 
			
		||||
				logPres.Info("recv accepted subscribe")
 | 
			
		||||
				logPres.Info("recv presence accepted subscribe")
 | 
			
		||||
			} else if pres.Type == messages.PresenceTypeUnsubscribe {
 | 
			
		||||
				logPres.Info("recv remove subscribe")
 | 
			
		||||
				logPres.Info("recv presence remove subscribe")
 | 
			
		||||
			} else if pres.Type == messages.PresenceTypeUnsubscribed {
 | 
			
		||||
				logPres.Info("recv removed subscribe")
 | 
			
		||||
				logPres.Info("recv presence removed subscribe")
 | 
			
		||||
			} else if pres.Type == messages.PresenceTypeUnavailable {
 | 
			
		||||
				logPres.Debug("recv presence unavailable")
 | 
			
		||||
			} else {
 | 
			
		||||
				logCTX.Warnf("unsupported presence recv: %v", pres)
 | 
			
		||||
				logCTX.Warnf("recv presence unsupported: %s -> %s", pres.Type, messages.XMLChildrenString(pres))
 | 
			
		||||
			}
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -82,7 +83,13 @@ func (t *Tester) StartBot(status *Status) {
 | 
			
		|||
		}
 | 
			
		||||
		logCTX = logCTX.WithField("from", msg.From.Full()).WithField("msg-recv", msg.Body)
 | 
			
		||||
		if msg.Error != nil {
 | 
			
		||||
			logCTX.Debugf("recv msg with error %s[%s]: %s -> %v -> %v", msg.Error.Code, msg.Error.Type, msg.Error.Text, msg.Error.StanzaErrorGroup, msg.Error.Other)
 | 
			
		||||
			if msg.Error.Type == "auth" {
 | 
			
		||||
				logCTX.Warnf("recv msg with error not auth")
 | 
			
		||||
				status.Login = false
 | 
			
		||||
				status.client.Close()
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			logCTX.Debugf("recv msg with error %s[%s]: %s -> %s -> %s", msg.Error.Code, msg.Error.Type, msg.Error.Text, messages.XMLChildrenString(msg.Error.StanzaErrorGroup), messages.XMLChildrenString(msg.Error.Other))
 | 
			
		||||
			continue
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,7 +9,6 @@ import (
 | 
			
		|||
	"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 {
 | 
			
		||||
| 
						 | 
				
			
			@ -149,12 +148,12 @@ func (t *Tester) CheckStatus() {
 | 
			
		|||
					logCTXTo.Debug("could not recv msg")
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			msg = utils.CreateCookieString()
 | 
			
		||||
			msg = messages.CreateCookieString()
 | 
			
		||||
			logCTXTo = logCTXTo.WithField("msg-send", msg)
 | 
			
		||||
 | 
			
		||||
			own.client.Send(&messages.MessageClient{
 | 
			
		||||
				Body: "checkmsg " + msg,
 | 
			
		||||
				Type: messages.ChatTypeChat,
 | 
			
		||||
				Type: messages.MessageTypeChat,
 | 
			
		||||
				To:   s.JID,
 | 
			
		||||
			})
 | 
			
		||||
			own.MessageForConnection[s.JID.Bare()] = msg
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,35 @@
 | 
			
		|||
package messages
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/xml"
 | 
			
		||||
 | 
			
		||||
	"dev.sum7.eu/genofire/yaja/model"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type MessageType string
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	MessageTypeChat      MessageType = "chat"
 | 
			
		||||
	MessageTypeGroupchat MessageType = "groupchat"
 | 
			
		||||
	MessageTypeError     MessageType = "error"
 | 
			
		||||
	MessageTypeHeadline  MessageType = "headline"
 | 
			
		||||
	MessageTypeNormal    MessageType = "normal"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// MessageClient element
 | 
			
		||||
type MessageClient struct {
 | 
			
		||||
	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    MessageType `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
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -6,15 +6,6 @@ import (
 | 
			
		|||
	"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
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
| 
						 | 
				
			
			@ -27,13 +18,13 @@ const (
 | 
			
		|||
	PresenceTypeError        PresenceType = "error"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type ShowType string
 | 
			
		||||
type PresenceShow string
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	ShowTypeAway ShowType = "away"
 | 
			
		||||
	ShowTypeChat ShowType = "chat"
 | 
			
		||||
	ShowTypeDND  ShowType = "dnd"
 | 
			
		||||
	ShowTypeXA   ShowType = "xa"
 | 
			
		||||
	PresenceShowAway PresenceShow = "away"
 | 
			
		||||
	PresenceShowChat PresenceShow = "chat"
 | 
			
		||||
	PresenceShowDND  PresenceShow = "dnd"
 | 
			
		||||
	PresenceShowXA   PresenceShow = "xa"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// PresenceClient element
 | 
			
		||||
| 
						 | 
				
			
			@ -45,39 +36,11 @@ type PresenceClient struct {
 | 
			
		|||
	Type    PresenceType `xml:"type,attr,omitempty"`
 | 
			
		||||
	Lang    string       `xml:"lang,attr,omitempty"`
 | 
			
		||||
 | 
			
		||||
	Show     ShowType `xml:"show,omitempty"`   // away, chat, dnd, xa
 | 
			
		||||
	Status   string   `xml:"status,omitempty"` // sb []clientText
 | 
			
		||||
	Priority string   `xml:"priority,omitempty"`
 | 
			
		||||
	Show     PresenceShow `xml:"show,omitempty"`   // away, chat, dnd, xa
 | 
			
		||||
	Status   string       `xml:"status,omitempty"` // sb []clientText
 | 
			
		||||
	Priority string       `xml:"priority,omitempty"`
 | 
			
		||||
	// Caps     *ClientCaps  `xml:"c"`
 | 
			
		||||
	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    *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
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,14 @@
 | 
			
		|||
package messages
 | 
			
		||||
 | 
			
		||||
import "encoding/xml"
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/xml"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// RFC 3920  C.4  SASL name space
 | 
			
		||||
type SASLMechanisms struct {
 | 
			
		||||
	XMLName   xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl mechanisms"`
 | 
			
		||||
	Mechanism []string `xml:"mechanism"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SASLAuth element
 | 
			
		||||
type SASLAuth struct {
 | 
			
		||||
| 
						 | 
				
			
			@ -9,8 +17,44 @@ type SASLAuth struct {
 | 
			
		|||
	Body      string   `xml:",chardata"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RFC 3920  C.4  SASL name space
 | 
			
		||||
type SASLMechanisms struct {
 | 
			
		||||
	XMLName   xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl mechanisms"`
 | 
			
		||||
	Mechanism []string `xml:"mechanism"`
 | 
			
		||||
// SASLChallenge element
 | 
			
		||||
type SASLChallenge struct {
 | 
			
		||||
	XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl challenge"`
 | 
			
		||||
	Body    string   `xml:",chardata"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SASLResponse element
 | 
			
		||||
type SASLResponse struct {
 | 
			
		||||
	XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl response"`
 | 
			
		||||
	Body    string   `xml:",chardata"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SASLSuccess element
 | 
			
		||||
type SASLSuccess struct {
 | 
			
		||||
	XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl success"`
 | 
			
		||||
	Body    string   `xml:",chardata"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SASLAbout element
 | 
			
		||||
type SASLAbout struct {
 | 
			
		||||
	XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl abort"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SASLFailure element
 | 
			
		||||
type SASLFailure struct {
 | 
			
		||||
	XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl failure"`
 | 
			
		||||
 | 
			
		||||
	Aborted              *xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl aborted"`
 | 
			
		||||
	AccountDisabled      *xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl account-disabled"`
 | 
			
		||||
	CredentialsExpired   *xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl credentials-expired"`
 | 
			
		||||
	EncryptionRequired   *xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl encryption-required"`
 | 
			
		||||
	IncorrectEncoding    *xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl incorrect-encoding"`
 | 
			
		||||
	InvalidAuthzid       *xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl invalid-authzid"`
 | 
			
		||||
	InvalidMechanism     *xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl invalid-mechanism"`
 | 
			
		||||
	MalformedRequest     *xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl malformed-request"`
 | 
			
		||||
	MechanismTooWeak     *xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl mechanism-too-weak"`
 | 
			
		||||
	NotAuthorized        *xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl not-authorized"`
 | 
			
		||||
	TemporaryAuthFailure *xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl temporary-auth-failure"`
 | 
			
		||||
 | 
			
		||||
	Body string `xml:",chardata"`
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,66 @@
 | 
			
		|||
package messages
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/rand"
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"encoding/xml"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"reflect"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Delay struct {
 | 
			
		||||
	Stamp string `xml:"stamp,attr"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type XMLElement struct {
 | 
			
		||||
	XMLName  xml.Name
 | 
			
		||||
	InnerXML string `xml:",innerxml"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func XMLChildrenString(o interface{}) (result string) {
 | 
			
		||||
	first := true
 | 
			
		||||
	val := reflect.ValueOf(o)
 | 
			
		||||
	if val.Kind() == reflect.Interface && !val.IsNil() {
 | 
			
		||||
		elm := val.Elem()
 | 
			
		||||
		if elm.Kind() == reflect.Ptr && !elm.IsNil() && elm.Elem().Kind() == reflect.Ptr {
 | 
			
		||||
			val = elm
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if val.Kind() != reflect.Struct {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	// struct
 | 
			
		||||
	for i := 0; i < val.NumField(); i++ {
 | 
			
		||||
		valueField := val.Field(i)
 | 
			
		||||
		if valueField.Kind() == reflect.Interface && !valueField.IsNil() {
 | 
			
		||||
			elm := valueField.Elem()
 | 
			
		||||
			if elm.Kind() == reflect.Ptr && !elm.IsNil() && elm.Elem().Kind() == reflect.Ptr {
 | 
			
		||||
				valueField = elm
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if xmlElement, ok := valueField.Interface().(*xml.Name); ok && xmlElement != nil {
 | 
			
		||||
			if first {
 | 
			
		||||
				first = false
 | 
			
		||||
			} else {
 | 
			
		||||
				result += ", "
 | 
			
		||||
			}
 | 
			
		||||
			result += xmlElement.Local
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Cookie is used to give a unique identifier to each request.
 | 
			
		||||
type Cookie uint64
 | 
			
		||||
 | 
			
		||||
func CreateCookie() Cookie {
 | 
			
		||||
	var buf [8]byte
 | 
			
		||||
	if _, err := rand.Reader.Read(buf[:]); err != nil {
 | 
			
		||||
		panic("Failed to read random bytes: " + err.Error())
 | 
			
		||||
	}
 | 
			
		||||
	return Cookie(binary.LittleEndian.Uint64(buf[:]))
 | 
			
		||||
}
 | 
			
		||||
func CreateCookieString() string {
 | 
			
		||||
	return fmt.Sprintf("%x", CreateCookie())
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -51,7 +51,7 @@ func (iex IQExtensions) Process(element *xml.StartElement, client *utils.Client)
 | 
			
		|||
 | 
			
		||||
	// not extensions found
 | 
			
		||||
	if count != 1 {
 | 
			
		||||
		log.Debugf("%s - %s: %v", msg.XMLName.Space, msg.Type, msg.Other)
 | 
			
		||||
		log.Debugf("%s - %s: %s", msg.XMLName.Space, msg.Type, messages.XMLChildrenString(msg.Other))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return true
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -44,7 +44,7 @@ func (state *Start) Process() State {
 | 
			
		|||
 | 
			
		||||
	fmt.Fprintf(state.Client.Conn, `<?xml version='1.0'?>
 | 
			
		||||
		<stream:stream id='%x' version='1.0' xmlns='%s' xmlns:stream='%s'>`,
 | 
			
		||||
		utils.CreateCookie(), messages.NSClient, messages.NSStream)
 | 
			
		||||
		messages.CreateCookie(), messages.NSClient, messages.NSStream)
 | 
			
		||||
 | 
			
		||||
	fmt.Fprintf(state.Client.Conn, `<stream:features>
 | 
			
		||||
			<starttls xmlns='%s'>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -73,7 +73,7 @@ func (state *TLSStream) Process() state.State {
 | 
			
		|||
					<mechanism>PLAIN</mechanism>
 | 
			
		||||
				</mechanisms>
 | 
			
		||||
			</stream:features>`,
 | 
			
		||||
			utils.CreateCookie(), messages.NSClient, messages.NSStream,
 | 
			
		||||
			messages.CreateCookie(), messages.NSClient, messages.NSStream,
 | 
			
		||||
			messages.NSSASL, messages.NSFeaturesIQRegister)
 | 
			
		||||
	} else {
 | 
			
		||||
		fmt.Fprintf(state.Client.Conn, `<?xml version='1.0'?>
 | 
			
		||||
| 
						 | 
				
			
			@ -83,7 +83,7 @@ func (state *TLSStream) Process() state.State {
 | 
			
		|||
					<mechanism>PLAIN</mechanism>
 | 
			
		||||
				</mechanisms>
 | 
			
		||||
			</stream:features>`,
 | 
			
		||||
			utils.CreateCookie(), messages.NSClient, messages.NSStream,
 | 
			
		||||
			messages.CreateCookie(), messages.NSClient, messages.NSStream,
 | 
			
		||||
			messages.NSSASL)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -174,7 +174,7 @@ func (state *AuthedStart) Process() state.State {
 | 
			
		|||
					<required/>
 | 
			
		||||
				</bind>
 | 
			
		||||
			</stream:features>`,
 | 
			
		||||
		messages.NSStream, state.Client.JID.Domain, utils.CreateCookie(), messages.NSClient,
 | 
			
		||||
		messages.NSStream, state.Client.JID.Domain, messages.CreateCookie(), messages.NSClient,
 | 
			
		||||
		messages.NSBind)
 | 
			
		||||
 | 
			
		||||
	return state.Next
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -97,7 +97,7 @@ func (state *TLSStream) Process() state.State {
 | 
			
		|||
			</mechanisms>
 | 
			
		||||
			<bidi xmlns='urn:xmpp:features:bidi'/>
 | 
			
		||||
		</stream:features>`,
 | 
			
		||||
		utils.CreateCookie(), messages.NSClient, messages.NSStream,
 | 
			
		||||
		messages.CreateCookie(), messages.NSClient, messages.NSStream,
 | 
			
		||||
		messages.NSSASL)
 | 
			
		||||
 | 
			
		||||
	return state.Next
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,25 +1,7 @@
 | 
			
		|||
package utils
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/rand"
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"dev.sum7.eu/genofire/yaja/model"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Cookie is used to give a unique identifier to each request.
 | 
			
		||||
type Cookie uint64
 | 
			
		||||
 | 
			
		||||
func CreateCookie() Cookie {
 | 
			
		||||
	var buf [8]byte
 | 
			
		||||
	if _, err := rand.Reader.Read(buf[:]); err != nil {
 | 
			
		||||
		panic("Failed to read random bytes: " + err.Error())
 | 
			
		||||
	}
 | 
			
		||||
	return Cookie(binary.LittleEndian.Uint64(buf[:]))
 | 
			
		||||
}
 | 
			
		||||
func CreateCookieString() string {
 | 
			
		||||
	return fmt.Sprintf("%x", CreateCookie())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type DomainRegisterAllowed func(*model.JID) bool
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Reference in New Issue