xmpp.go
changeset 98 c9cc4eda6dce
parent 72 53f15893a1a7
child 100 24231ff0016c
equal deleted inserted replaced
88:d2ec96c80efe 98:c9cc4eda6dce
     6 // and 3921, plus the various XEPs at http://xmpp.org/protocols/.
     6 // and 3921, plus the various XEPs at http://xmpp.org/protocols/.
     7 package xmpp
     7 package xmpp
     8 
     8 
     9 import (
     9 import (
    10 	"bytes"
    10 	"bytes"
       
    11 	"encoding/xml"
       
    12 	"errors"
    11 	"fmt"
    13 	"fmt"
    12 	"io"
    14 	"io"
       
    15 	"log/syslog"
    13 	"net"
    16 	"net"
    14 	"os"
       
    15 	"sync"
    17 	"sync"
    16 	"syslog"
       
    17 	"xml"
       
    18 )
    18 )
    19 
    19 
    20 const (
    20 const (
    21 	// Version of RFC 3920 that we implement.
    21 	// Version of RFC 3920 that we implement.
    22 	Version = "1.0"
    22 	Version = "1.0"
    23 
    23 
    24 	// Various XML namespaces.
    24 	// Various XML namespaces.
       
    25 	NsClient  = "jabber:client"
    25 	NsStreams = "urn:ietf:params:xml:ns:xmpp-streams"
    26 	NsStreams = "urn:ietf:params:xml:ns:xmpp-streams"
    26 	NsStream  = "http://etherx.jabber.org/streams"
    27 	NsStream  = "http://etherx.jabber.org/streams"
    27 	NsTLS     = "urn:ietf:params:xml:ns:xmpp-tls"
    28 	NsTLS     = "urn:ietf:params:xml:ns:xmpp-tls"
    28 	NsSASL    = "urn:ietf:params:xml:ns:xmpp-sasl"
    29 	NsSASL    = "urn:ietf:params:xml:ns:xmpp-sasl"
    29 	NsBind    = "urn:ietf:params:xml:ns:xmpp-bind"
    30 	NsBind    = "urn:ietf:params:xml:ns:xmpp-bind"
   104 // with the given password. This function will return as soon as a TCP
   105 // with the given password. This function will return as soon as a TCP
   105 // connection has been established, but before XMPP stream negotiation
   106 // connection has been established, but before XMPP stream negotiation
   106 // has completed. The negotiation will occur asynchronously, and any
   107 // has completed. The negotiation will occur asynchronously, and any
   107 // send operation to Client.Out will block until negotiation (resource
   108 // send operation to Client.Out will block until negotiation (resource
   108 // binding) is complete.
   109 // binding) is complete.
   109 func NewClient(jid *JID, password string, exts []Extension) (*Client,
   110 func NewClient(jid *JID, password string, exts []Extension) (*Client, error) {
   110 os.Error) {
       
   111 	// Include the mandatory extensions.
   111 	// Include the mandatory extensions.
   112 	exts = append(exts, rosterExt)
   112 	exts = append(exts, rosterExt)
   113 	exts = append(exts, bindExt)
   113 	exts = append(exts, bindExt)
   114 
   114 
   115 	// Resolve the domain in the JID.
   115 	// Resolve the domain in the JID.
   116 	_, srvs, err := net.LookupSRV(clientSrv, "tcp", jid.Domain)
   116 	_, srvs, err := net.LookupSRV(clientSrv, "tcp", jid.Domain)
   117 	if err != nil {
   117 	if err != nil {
   118 		return nil, os.NewError("LookupSrv " + jid.Domain +
   118 		return nil, errors.New("LookupSrv " + jid.Domain +
   119 			": " + err.String())
   119 			": " + err.Error())
   120 	}
   120 	}
   121 
   121 
   122 	var tcp *net.TCPConn
   122 	var tcp *net.TCPConn
   123 	for _, srv := range srvs {
   123 	for _, srv := range srvs {
   124 		addrStr := fmt.Sprintf("%s:%d", srv.Target, srv.Port)
   124 		addrStr := fmt.Sprintf("%s:%d", srv.Target, srv.Port)
   125 		addr, err := net.ResolveTCPAddr("tcp", addrStr)
   125 		addr, err := net.ResolveTCPAddr("tcp", addrStr)
   126 		if err != nil {
   126 		if err != nil {
   127 			err = os.NewError(fmt.Sprintf("ResolveTCPAddr(%s): %s",
   127 			err = fmt.Errorf("ResolveTCPAddr(%s): %s",
   128 				addrStr, err.String()))
   128 				addrStr, err.Error())
   129 			continue
   129 			continue
   130 		}
   130 		}
   131 		tcp, err = net.DialTCP("tcp", nil, addr)
   131 		tcp, err = net.DialTCP("tcp", nil, addr)
   132 		if err != nil {
   132 		if err != nil {
   133 			err = os.NewError(fmt.Sprintf("DialTCP(%s): %s",
   133 			err = fmt.Errorf("DialTCP(%s): %s",
   134 				addr, err.String()))
   134 				addr, err)
   135 			continue
   135 			continue
   136 		}
   136 		}
   137 	}
   137 	}
   138 	if tcp == nil {
   138 	if tcp == nil {
   139 		return nil, err
   139 		return nil, err
   269 // Start an XMPP session. A typical XMPP client should call this
   269 // Start an XMPP session. A typical XMPP client should call this
   270 // immediately after creating the Client in order to start the
   270 // immediately after creating the Client in order to start the
   271 // session, retrieve the roster, and broadcast an initial
   271 // session, retrieve the roster, and broadcast an initial
   272 // presence. The presence can be as simple as a newly-initialized
   272 // presence. The presence can be as simple as a newly-initialized
   273 // Presence struct.  See RFC 3921, Section 3.
   273 // Presence struct.  See RFC 3921, Section 3.
   274 func (cl *Client) StartSession(getRoster bool, pr *Presence) os.Error {
   274 func (cl *Client) StartSession(getRoster bool, pr *Presence) error {
   275 	id := <-Id
   275 	id := <-Id
   276 	iq := &Iq{To: cl.Jid.Domain, Id: id, Type: "set", Nested: []interface{}{Generic{XMLName: xml.Name{Space: NsSession, Local: "session"}}}}
   276 	iq := &Iq{To: cl.Jid.Domain, Id: id, Type: "set", Nested: []interface{}{Generic{XMLName: xml.Name{Space: NsSession, Local: "session"}}}}
   277 	ch := make(chan os.Error)
   277 	ch := make(chan error)
   278 	f := func(st Stanza) bool {
   278 	f := func(st Stanza) bool {
   279 		if st.GetType() == "error" {
   279 		iq, ok := st.(*Iq)
       
   280 		if !ok {
       
   281 			if Log != nil {
       
   282 				Log.Err("iq reply not iq; can't start session")
       
   283 			}
       
   284 			ch <- errors.New("bad session start reply")
       
   285 			return false
       
   286 		}
       
   287 		if iq.Type == "error" {
   280 			if Log != nil {
   288 			if Log != nil {
   281 				Log.Err(fmt.Sprintf("Can't start session: %v",
   289 				Log.Err(fmt.Sprintf("Can't start session: %v",
   282 					st))
   290 					iq))
   283 			}
   291 			}
   284 			ch <- st.GetError()
   292 			ch <- iq.Error
   285 			return false
   293 			return false
   286 		}
   294 		}
   287 		ch <- nil
   295 		ch <- nil
   288 		return false
   296 		return false
   289 	}
   297 	}