diff --git a/xmpp.go b/xmpp.go
index ec4f7d1..e669c92 100644
--- a/xmpp.go
+++ b/xmpp.go
@@ -38,11 +38,13 @@ const (
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"
+ nsSession = "urn:ietf:params:xml:ns:xmpp-session"
)
+// Default TLS configuration options
var DefaultConfig tls.Config
+// Cookie is a unique XMPP session identifier
type Cookie uint64
func getCookie() Cookie {
@@ -53,6 +55,7 @@ func getCookie() Cookie {
return Cookie(binary.LittleEndian.Uint64(buf[:]))
}
+// Client holds XMPP connection opitons
type Client struct {
conn net.Conn // connection to server
jid string // Jabber ID for our connection
@@ -211,6 +214,7 @@ func NewClient(host, user, passwd string, debug bool) (*Client, error) {
return opts.NewClient()
}
+// NewClientNoTLS creates a new client without TLS
func NewClientNoTLS(host, user, passwd string, debug bool) (*Client, error) {
opts := Options{
Host: host,
@@ -223,16 +227,15 @@ func NewClientNoTLS(host, user, passwd string, debug bool) (*Client, error) {
return opts.NewClient()
}
+// Close closes the XMPP connection
func (c *Client) Close() error {
if c.conn != (*tls.Conn)(nil) {
return c.conn.Close()
- } else {
- return nil
}
+ return nil
}
-func saslDigestResponse(username, realm, passwd, nonce, cnonceStr,
- authenticate, digestUri, nonceCountStr string) string {
+func saslDigestResponse(username, realm, passwd, nonce, cnonceStr, authenticate, digestURI, nonceCountStr string) string {
h := func(text string) []byte {
h := md5.New()
h.Write([]byte(text))
@@ -245,12 +248,9 @@ func saslDigestResponse(username, realm, passwd, nonce, cnonceStr,
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))))
+ 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
}
@@ -265,10 +265,11 @@ func cnonce() string {
}
func (c *Client) init(o *Options) error {
- 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})
+ // For debugging: the following causes the plaintext of the connection to be duplicated to stderr.
+ c.p = xml.NewDecoder(tee{c.conn, os.Stderr})
+ } else {
+ c.p = xml.NewDecoder(c.conn)
}
a := strings.SplitN(o.User, "@", 2)
@@ -284,7 +285,7 @@ func (c *Client) init(o *Options) error {
}
// If the server requires we STARTTLS, attempt to do so.
- if f, err = c.startTlsIfRequired(f, o, domain); err != nil {
+ if f, err = c.startTLSIfRequired(f, o, domain); err != nil {
return err
}
@@ -316,15 +317,13 @@ func (c *Client) init(o *Options) error {
raw := "\x00" + user + "\x00" + o.Password
enc := make([]byte, base64.StdEncoding.EncodedLen(len(raw)))
base64.StdEncoding.Encode(enc, []byte(raw))
- fmt.Fprintf(c.conn, "%s\n",
- nsSASL, enc)
+ fmt.Fprintf(c.conn, "%s\n", nsSASL, enc)
break
}
if m == "DIGEST-MD5" {
mechanism = m
// Digest-MD5 authentication
- fmt.Fprintf(c.conn, "\n",
- nsSASL)
+ fmt.Fprintf(c.conn, "\n", nsSASL)
var ch saslChallenge
if err = c.p.DecodeElement(&ch, nil); err != nil {
return errors.New("unmarshal : " + err.Error())
@@ -348,10 +347,11 @@ func (c *Client) init(o *Options) error {
qop, _ := tokens["qop"]
charset, _ := tokens["charset"]
cnonceStr := cnonce()
- digestUri := "xmpp/" + domain
+ 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
+ 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
fmt.Fprintf(c.conn, "%s\n", nsSASL, base64.StdEncoding.EncodeToString([]byte(message)))
@@ -368,7 +368,7 @@ func (c *Client) init(o *Options) error {
}
}
if mechanism == "" {
- return errors.New(fmt.Sprintf("PLAIN authentication is not an option: %v", f.Mechanisms.Mechanism))
+ return fmt.Errorf("PLAIN authentication is not an option: %v", f.Mechanisms.Mechanism)
}
// Next message should be either success or failure.
@@ -392,7 +392,7 @@ func (c *Client) init(o *Options) error {
return err
}
- //Generate uniq cookie
+ // Generate a uniqe cookie
cookie := getCookie()
// Send IQ message asking to bind to the local user name.
@@ -412,7 +412,7 @@ func (c *Client) init(o *Options) error {
if o.Session {
//if server support session, open it
- fmt.Fprintf(c.conn, "", xmlEscape(domain), cookie, NsSession)
+ fmt.Fprintf(c.conn, "", xmlEscape(domain), cookie, nsSession)
}
// We're connected and can now receive and send messages.
@@ -423,7 +423,7 @@ func (c *Client) init(o *Options) error {
// startTlsIfRequired examines the server's stream features and, if STARTTLS is required or supported, performs the TLS handshake.
// f will be updated if the handshake completes, as the new stream's features are typically different from the original.
-func (c *Client) startTlsIfRequired(f *streamFeatures, o *Options, domain string) (*streamFeatures, error) {
+func (c *Client) startTLSIfRequired(f *streamFeatures, o *Options, domain string) (*streamFeatures, error) {
// whether we start tls is a matter of opinion: the server's and the user's.
switch {
case f.StartTLS == nil:
@@ -465,12 +465,13 @@ func (c *Client) startTlsIfRequired(f *streamFeatures, o *Options, domain string
}
// startStream will start a new XML decoder for the connection, signal the start of a stream to the server and verify that the server has
-// also started the stream; if o.Debug is true, startStream will tee decoded XML data to stdout. The features advertised by the server
+// also started the stream; if o.Debug is true, startStream will tee decoded XML data to stderr. The features advertised by the server
// will be returned.
func (c *Client) startStream(o *Options, domain string) (*streamFeatures, error) {
- c.p = xml.NewDecoder(c.conn)
if o.Debug {
- c.p = xml.NewDecoder(tee{c.conn, os.Stdout})
+ c.p = xml.NewDecoder(tee{c.conn, os.Stderr})
+ } else {
+ c.p = xml.NewDecoder(c.conn)
}
_, err := fmt.Fprintf(c.conn, "\n"+
@@ -508,6 +509,7 @@ func (c *Client) IsEncrypted() bool {
return ok
}
+// Chat is an incoming or outgoing XMPP chat message
type Chat struct {
Remote string
Type string
@@ -515,6 +517,7 @@ type Chat struct {
Other []string
}
+// Presence is an XMPP presence message
type Presence struct {
From string
To string
@@ -546,7 +549,7 @@ func (c *Client) Send(chat Chat) (n int, err error) {
xmlEscape(chat.Remote), xmlEscape(chat.Type), xmlEscape(chat.Text))
}
-// Send origin
+// SendOrg sends the original text without being wrapped in an XMPP message stanza.
func (c *Client) SendOrg(org string) (n int, err error) {
return fmt.Fprint(c.conn, org)
}
@@ -625,7 +628,7 @@ type bindBind struct {
type clientMessage struct {
XMLName xml.Name `xml:"jabber:client message"`
From string `xml:"from,attr"`
- Id string `xml:"id,attr"`
+ ID string `xml:"id,attr"`
To string `xml:"to,attr"`
Type string `xml:"type,attr"` // chat, error, groupchat, headline, or normal
@@ -647,7 +650,7 @@ type clientText struct {
type clientPresence struct {
XMLName xml.Name `xml:"jabber:client presence"`
From string `xml:"from,attr"`
- Id string `xml:"id,attr"`
+ ID string `xml:"id,attr"`
To string `xml:"to,attr"`
Type string `xml:"type,attr"` // error, probe, subscribe, subscribed, unavailable, unsubscribe, unsubscribed
Lang string `xml:"lang,attr"`
@@ -661,7 +664,7 @@ type clientPresence struct {
type clientIQ struct { // info/query
XMLName xml.Name `xml:"jabber:client iq"`
From string `xml:",attr"`
- Id string `xml:",attr"`
+ ID string `xml:",attr"`
To string `xml:",attr"`
Type string `xml:",attr"` // error, get, result, set
Error clientError