From af110491a0f6918de29550b2ff1b7dc25d94f8cf Mon Sep 17 00:00:00 2001
From: Specode <0x0000e000@gmail.com>
Date: Fri, 18 Oct 2013 15:49:41 +0800
Subject: [PATCH 01/10] add debug options

---
 xmpp.go | 16 ++++++++++++----
 1 file changed, 12 insertions(+), 4 deletions(-)

diff --git a/xmpp.go b/xmpp.go
index b5edcf1..90db6ba 100644
--- a/xmpp.go
+++ b/xmpp.go
@@ -115,6 +115,9 @@ type Options struct {
 	// NoTLS disables TLS and specifies that a plain old unencrypted TCP connection should
 	// be used.
 	NoTLS bool
+
+	// Debug output
+	Debug bool
 }
 
 // NewClient establishes a new Client connection based on a set of Options.
@@ -153,21 +156,23 @@ func (o Options) NewClient() (*Client, error) {
 // NewClient creates a new connection to a host given as "hostname" or "hostname:port".
 // If host is not specified, the  DNS SRV should be used to find the host from the domainpart of the JID.
 // Default the port to 5222.
-func NewClient(host, user, passwd string) (*Client, error) {
+func NewClient(host, user, passwd string, debug bool) (*Client, error) {
 	opts := Options{
 		Host:     host,
 		User:     user,
 		Password: passwd,
+		Debug:    debug,
 	}
 	return opts.NewClient()
 }
 
-func NewClientNoTLS(host, user, passwd string) (*Client, error) {
+func NewClientNoTLS(host, user, passwd string, debug bool) (*Client, error) {
 	opts := Options{
 		Host:     host,
 		User:     user,
 		Password: passwd,
 		NoTLS:    true,
+		Debug:    debug,
 	}
 	return opts.NewClient()
 }
@@ -210,9 +215,11 @@ func cnonce() string {
 }
 
 func (c *Client) init(o *Options) error {
-	// For debugging: the following causes the plaintext of the connection to be duplicated to stdout.
-	//c.p = xml.NewDecoder(tee{c.conn, os.Stdout})
 	c.p = xml.NewDecoder(c.conn)
+	// For debugging: the following causes the plaintext of the connection to be duplicated to stdout.
+	if o.Debug {
+		c.p = xml.NewDecoder(tee{c.conn, os.Stdout})
+	}
 
 	a := strings.SplitN(o.User, "@", 2)
 	if len(a) != 2 {
@@ -617,6 +624,7 @@ func (t tee) Read(p []byte) (n int, err error) {
 	n, err = t.r.Read(p)
 	if n > 0 {
 		t.w.Write(p[0:n])
+		t.w.Write([]byte("\n"))
 	}
 	return
 }

From c88c22763aea019b4e99ade78a6526af89a089b3 Mon Sep 17 00:00:00 2001
From: Specode <0x0000e000@gmail.com>
Date: Fri, 18 Oct 2013 15:52:01 +0800
Subject: [PATCH 02/10] add SendOrg for send origin text

---
 xmpp.go | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/xmpp.go b/xmpp.go
index 90db6ba..3f9f527 100644
--- a/xmpp.go
+++ b/xmpp.go
@@ -400,6 +400,11 @@ func (c *Client) Send(chat Chat) {
 		xmlEscape(chat.Remote), xmlEscape(chat.Type), xmlEscape(chat.Text))
 }
 
+// Send origin
+func (c *Client) SendOrg(org string) {
+	fmt.Fprint(c.conn, org)
+}
+
 // RFC 3920  C.1  Streams name space
 type streamFeatures struct {
 	XMLName    xml.Name `xml:"http://etherx.jabber.org/streams features"`

From ba140e5eb7ffffedd38612720fd36788ea89e175 Mon Sep 17 00:00:00 2001
From: Specode <0x0000e000@gmail.com>
Date: Mon, 21 Oct 2013 03:07:59 +0000
Subject: [PATCH 03/10] fix example

---
 example/example-gui.go | 4 ++--
 example/example.go     | 9 +++++----
 2 files changed, 7 insertions(+), 6 deletions(-)

diff --git a/example/example-gui.go b/example/example-gui.go
index f29bdf0..0787cfc 100644
--- a/example/example-gui.go
+++ b/example/example-gui.go
@@ -1,8 +1,8 @@
 package main
 
 import (
-	"github.com/mattn/go-xmpp"
 	"github.com/mattn/go-gtk/gtk"
+	"github.com/mattn/go-xmpp"
 	"log"
 	"os"
 	"strings"
@@ -67,7 +67,7 @@ func main() {
 		os.Exit(0)
 	}
 
-	talk, err := xmpp.NewClient("talk.google.com:443", username_, password_)
+	talk, err := xmpp.NewClient("talk.google.com:443", username_, password_, false)
 	if err != nil {
 		log.Fatal(err)
 	}
diff --git a/example/example.go b/example/example.go
index 5702a74..f69099f 100644
--- a/example/example.go
+++ b/example/example.go
@@ -2,18 +2,19 @@ package main
 
 import (
 	"bufio"
-	"fmt"
 	"flag"
+	"fmt"
 	"github.com/mattn/go-xmpp"
 	"log"
 	"os"
 	"strings"
 )
 
-var server   = flag.String("server", "talk.google.com:443", "server")
+var server = flag.String("server", "talk.google.com:443", "server")
 var username = flag.String("username", "", "username")
 var password = flag.String("password", "", "password")
 var notls = flag.Bool("notls", false, "No TLS")
+var debug = flag.Bool("debug", false, "debug output")
 
 func main() {
 	flag.Usage = func() {
@@ -29,9 +30,9 @@ func main() {
 	var talk *xmpp.Client
 	var err error
 	if *notls {
-		talk, err = xmpp.NewClientNoTLS(*server, *username, *password)
+		talk, err = xmpp.NewClientNoTLS(*server, *username, *password, *debug)
 	} else {
-		talk, err = xmpp.NewClient(*server, *username, *password)
+		talk, err = xmpp.NewClient(*server, *username, *password, *debug)
 	}
 	if err != nil {
 		log.Fatal(err)

From cbdf478ba76b7c62f6ac8c2f6343f6cfa0f8d8bc Mon Sep 17 00:00:00 2001
From: Specode <0x0000e000@gmail.com>
Date: Wed, 30 Oct 2013 17:13:02 +0800
Subject: [PATCH 04/10] clientMessage add ",any" tag, use for hasn't matched
 element

---
 xmpp.go | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/xmpp.go b/xmpp.go
index 3f9f527..2a705cb 100644
--- a/xmpp.go
+++ b/xmpp.go
@@ -367,6 +367,7 @@ type Chat struct {
 	Remote string
 	Type   string
 	Text   string
+	Other  []string
 }
 
 type Presence struct {
@@ -385,7 +386,7 @@ func (c *Client) Recv() (event interface{}, err error) {
 		}
 		switch v := val.(type) {
 		case *clientMessage:
-			return Chat{v.From, v.Type, v.Body}, nil
+			return Chat{v.From, v.Type, v.Body, v.Other}, nil
 		case *clientPresence:
 			return Presence{v.From, v.To, v.Type, v.Show}, nil
 		}
@@ -488,6 +489,11 @@ type clientMessage struct {
 	Subject string `xml:"subject"`
 	Body    string `xml:"body"`
 	Thread  string `xml:"thread"`
+
+	Other []string `xml:",any"`
+
+	// // Any hasn't matched element
+	// Other string `xml:",any"`
 }
 
 type clientText struct {

From 64821d5df97100c4e61c60280d65d60a4fd623d1 Mon Sep 17 00:00:00 2001
From: Specode <0x0000e000@gmail.com>
Date: Wed, 30 Oct 2013 17:14:11 +0800
Subject: [PATCH 05/10] clientMessage add ",any" tag, use for hasn't matched
 element

---
 xmpp.go | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/xmpp.go b/xmpp.go
index 2a705cb..52c1e66 100644
--- a/xmpp.go
+++ b/xmpp.go
@@ -490,10 +490,8 @@ type clientMessage struct {
 	Body    string `xml:"body"`
 	Thread  string `xml:"thread"`
 
+	// Any hasn't matched element
 	Other []string `xml:",any"`
-
-	// // Any hasn't matched element
-	// Other string `xml:",any"`
 }
 
 type clientText struct {

From b67dc40516077760b3d69e92635ca92c5e22ffe5 Mon Sep 17 00:00:00 2001
From: Specode <0x0000e000@gmail.com>
Date: Mon, 4 Nov 2013 15:13:55 +0800
Subject: [PATCH 06/10] fix some error handling

---
 xmpp.go | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/xmpp.go b/xmpp.go
index 52c1e66..ab5eade 100644
--- a/xmpp.go
+++ b/xmpp.go
@@ -314,6 +314,9 @@ func (c *Client) init(o *Options) error {
 
 	// Next message should be either success or failure.
 	name, val, err := next(c.p)
+	if err != nil {
+		return err
+	}
 	switch v := val.(type) {
 	case *saslSuccess:
 	case *saslFailure:
@@ -535,8 +538,8 @@ type clientError struct {
 func nextStart(p *xml.Decoder) (xml.StartElement, error) {
 	for {
 		t, err := p.Token()
-		if err != nil {
-			log.Fatal("token", err)
+		if err != nil && err != io.EOF {
+			return nil, err
 		}
 		switch t := t.(type) {
 		case xml.StartElement:

From 8a80c8abe319ccc0e61b63684158bffd1a61ca5a Mon Sep 17 00:00:00 2001
From: Specode <0x0000e000@gmail.com>
Date: Tue, 5 Nov 2013 10:03:26 +0800
Subject: [PATCH 07/10] fix bug

---
 xmpp.go | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/xmpp.go b/xmpp.go
index ab5eade..6786432 100644
--- a/xmpp.go
+++ b/xmpp.go
@@ -23,7 +23,6 @@ import (
 	"errors"
 	"fmt"
 	"io"
-	"log"
 	"math/big"
 	"net"
 	"net/http"
@@ -539,7 +538,7 @@ func nextStart(p *xml.Decoder) (xml.StartElement, error) {
 	for {
 		t, err := p.Token()
 		if err != nil && err != io.EOF {
-			return nil, err
+			return xml.StartElement{}, err
 		}
 		switch t := t.(type) {
 		case xml.StartElement:

From e71f933d7c7642c5bac14ff9b164aaa30ab6b681 Mon Sep 17 00:00:00 2001
From: Martin Bonnin <martin@mbonnin.net>
Date: Wed, 16 Apr 2014 17:25:20 +0200
Subject: [PATCH 08/10] fix digest-md5 with some ejabberd server

* it looke like some response parameters need to be quoted
* so quote username, realm, nonce, etc, ... as in http://tools.ietf.org/html/rfc3920#ref-SASL
---
 xmpp.go | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/xmpp.go b/xmpp.go
index 6786432..65f0841 100644
--- a/xmpp.go
+++ b/xmpp.go
@@ -292,7 +292,8 @@ func (c *Client) init(o *Options) error {
 			digestUri := "xmpp/" + domain
 			nonceCount := fmt.Sprintf("%08x", 1)
 			digest := saslDigestResponse(user, realm, o.Password, nonce, cnonceStr, "AUTHENTICATE", digestUri, nonceCount)
-			message := "username=" + user + ", realm=" + realm + ", nonce=" + nonce + ", cnonce=" + cnonceStr + ", nc=" + nonceCount + ", qop=" + qop + ", digest-uri=" + digestUri + ", response=" + digest + ", charset=" + charset
+			message := "username=\"" + user + "\", realm=\"" + realm + "\", nonce=\"" + nonce + "\", cnonce=\"" + cnonceStr + "\", nc=" + nonceCount + ", qop=" + qop + ", digest-uri=\"" + digestUri + "\", response=" + digest + ", charset=" + charset
+
 			fmt.Fprintf(c.conn, "<response xmlns='%s'>%s</response>\n", nsSASL, base64.StdEncoding.EncodeToString([]byte(message)))
 
 			var rspauth saslRspAuth

From 8a08b956bbcb0bdd88b6eba7bc9b8a146b1cef8c Mon Sep 17 00:00:00 2001
From: Ilya Beda <ir4y.ix@gmail.com>
Date: Wed, 23 Apr 2014 00:05:35 +0800
Subject: [PATCH 09/10] Ejabberd compatibility

Add unique cookie for iq requests
Add session parameter
---
 example/example.go | 15 ++++++++++-----
 xmpp.go            | 41 +++++++++++++++++++++++++++++++++--------
 2 files changed, 43 insertions(+), 13 deletions(-)

diff --git a/example/example.go b/example/example.go
index f69099f..3981192 100644
--- a/example/example.go
+++ b/example/example.go
@@ -15,6 +15,7 @@ var username = flag.String("username", "", "username")
 var password = flag.String("password", "", "password")
 var notls = flag.Bool("notls", false, "No TLS")
 var debug = flag.Bool("debug", false, "debug output")
+var session = flag.Bool("session", false, "use server session")
 
 func main() {
 	flag.Usage = func() {
@@ -29,11 +30,15 @@ func main() {
 
 	var talk *xmpp.Client
 	var err error
-	if *notls {
-		talk, err = xmpp.NewClientNoTLS(*server, *username, *password, *debug)
-	} else {
-		talk, err = xmpp.NewClient(*server, *username, *password, *debug)
-	}
+	options := xmpp.Options{Host: *server,
+		User:     *username,
+		Password: *password,
+		NoTLS:    *notls,
+		Debug:    *debug,
+		Session:  *session}
+
+	talk, err = options.NewClient()
+
 	if err != nil {
 		log.Fatal(err)
 	}
diff --git a/xmpp.go b/xmpp.go
index 65f0841..6515fcc 100644
--- a/xmpp.go
+++ b/xmpp.go
@@ -19,6 +19,7 @@ import (
 	"crypto/rand"
 	"crypto/tls"
 	"encoding/base64"
+	"encoding/binary"
 	"encoding/xml"
 	"errors"
 	"fmt"
@@ -32,15 +33,26 @@ import (
 )
 
 const (
-	nsStream = "http://etherx.jabber.org/streams"
-	nsTLS    = "urn:ietf:params:xml:ns:xmpp-tls"
-	nsSASL   = "urn:ietf:params:xml:ns:xmpp-sasl"
-	nsBind   = "urn:ietf:params:xml:ns:xmpp-bind"
-	nsClient = "jabber:client"
+	nsStream  = "http://etherx.jabber.org/streams"
+	nsTLS     = "urn:ietf:params:xml:ns:xmpp-tls"
+	nsSASL    = "urn:ietf:params:xml:ns:xmpp-sasl"
+	nsBind    = "urn:ietf:params:xml:ns:xmpp-bind"
+	nsClient  = "jabber:client"
+	NsSession = "urn:ietf:params:xml:ns:xmpp-session"
 )
 
 var DefaultConfig tls.Config
 
+type Cookie uint64
+
+func getCookie() 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[:]))
+}
+
 type Client struct {
 	conn   net.Conn // connection to server
 	jid    string   // Jabber ID for our connection
@@ -117,6 +129,9 @@ type Options struct {
 
 	// Debug output
 	Debug bool
+
+	//Use server sessions
+	Session bool
 }
 
 // NewClient establishes a new Client connection based on a set of Options.
@@ -161,6 +176,7 @@ func NewClient(host, user, passwd string, debug bool) (*Client, error) {
 		User:     user,
 		Password: passwd,
 		Debug:    debug,
+		Session:  false,
 	}
 	return opts.NewClient()
 }
@@ -172,6 +188,7 @@ func NewClientNoTLS(host, user, passwd string, debug bool) (*Client, error) {
 		Password: passwd,
 		NoTLS:    true,
 		Debug:    debug,
+		Session:  false,
 	}
 	return opts.NewClient()
 }
@@ -343,14 +360,17 @@ func (c *Client) init(o *Options) error {
 	}
 	if err = c.p.DecodeElement(&f, nil); err != nil {
 		// TODO: often stream stop.
-		//return os.NewError("unmarshal <features>: " + err.String())
+		//return errors.New("unmarshal <features>: " + err.Error())
 	}
 
+	//Generate uniq cookie
+	cookie := getCookie()
+
 	// Send IQ message asking to bind to the local user name.
 	if o.Resource == "" {
-		fmt.Fprintf(c.conn, "<iq type='set' id='x'><bind xmlns='%s'></bind></iq>\n", nsBind)
+		fmt.Fprintf(c.conn, "<iq type='set' id='%x'><bind xmlns='%s'></bind></iq>\n", cookie, nsBind)
 	} else {
-		fmt.Fprintf(c.conn, "<iq type='set' id='x'><bind xmlns='%s'><resource>%s</resource></bind></iq>\n", nsBind, o.Resource)
+		fmt.Fprintf(c.conn, "<iq type='set' id='%x'><bind xmlns='%s'><resource>%s</resource></bind></iq>\n", cookie, nsBind, o.Resource)
 	}
 	var iq clientIQ
 	if err = c.p.DecodeElement(&iq, nil); err != nil {
@@ -361,6 +381,11 @@ func (c *Client) init(o *Options) error {
 	}
 	c.jid = iq.Bind.Jid // our local id
 
+	if o.Session {
+		//if server support session, open it
+		fmt.Fprintf(c.conn, "<iq to='%s' type='set' id='%x'><session xmlns='%s'/></iq>", xmlEscape(domain), cookie, NsSession)
+	}
+
 	// We're connected and can now receive and send messages.
 	fmt.Fprintf(c.conn, "<presence xml:lang='en'><show>xa</show><status>I for one welcome our new codebot overlords.</status></presence>")
 	return nil

From 0fd114068f2a065bdd566a7a02a45ef6109bc22b Mon Sep 17 00:00:00 2001
From: Falaleev Maxim <max@studio107.ru>
Date: Mon, 15 Sep 2014 10:12:12 +0400
Subject: [PATCH 10/10] Add anonymous auth

---
 xmpp.go | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/xmpp.go b/xmpp.go
index 6515fcc..00d86b5 100644
--- a/xmpp.go
+++ b/xmpp.go
@@ -268,6 +268,19 @@ func (c *Client) init(o *Options) error {
 	}
 	mechanism := ""
 	for _, m := range f.Mechanisms.Mechanism {
+		if m == "ANONYMOUS" {
+			mechanism = m
+			fmt.Fprintf(c.conn, "<auth xmlns='%s' mechanism='ANONYMOUS' />\n", nsSASL)
+			break
+		}
+
+		a := strings.SplitN(o.User, "@", 2)
+		if len(a) != 2 {
+			return errors.New("xmpp: invalid username (want user@domain): " + o.User)
+		}
+		user := a[0]
+		domain := a[1]
+
 		if m == "PLAIN" {
 			mechanism = m
 			// Plain authentication: send base64-encoded \x00 user \x00 password.