xmpp.go
changeset 17 d269d9c0fc8e
parent 15 aa2cf77f0ed3
child 20 e119444a1119
equal deleted inserted replaced
16:b839e37b3f29 17:d269d9c0fc8e
     3 // license that can be found in the LICENSE file.
     3 // license that can be found in the LICENSE file.
     4 
     4 
     5 // This package implements a simple XMPP client according to RFCs 3920
     5 // This package implements a simple XMPP client according to RFCs 3920
     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 
       
     9 // TODO Figure out why the library doesn't exit when the server closes
       
    10 // its stream to us.
     8 
    11 
     9 import (
    12 import (
    10 	"bytes"
    13 	"bytes"
    11 	"fmt"
    14 	"fmt"
    12 	"io"
    15 	"io"
    28 
    31 
    29 	// DNS SRV names
    32 	// DNS SRV names
    30 	serverSrv = "xmpp-server"
    33 	serverSrv = "xmpp-server"
    31 	clientSrv = "xmpp-client"
    34 	clientSrv = "xmpp-client"
    32 
    35 
       
    36 	// TODO Make this a parameter to NewClient, not a
       
    37 	// constant. We should have both a log level and a
       
    38 	// syslog.Writer, so the app can control how much time we
       
    39 	// spend generating log messages, as well as where they go.
    33 	debug = true
    40 	debug = true
    34 )
    41 )
    35 
    42 
    36 // The client in a client-server XMPP connection.
    43 // The client in a client-server XMPP connection.
    37 type Client struct {
    44 type Client struct {
       
    45 	// This client's JID. This will be updated asynchronously when
       
    46 	// resource binding completes; at that time an iq stanza will
       
    47 	// be published on the In channel:
       
    48 	// <iq><bind><jid>jid</jid></bind></iq>
    38 	Jid JID
    49 	Jid JID
    39 	password string
    50 	password string
    40 	socket net.Conn
    51 	socket net.Conn
    41 	socketSync sync.WaitGroup
    52 	socketSync sync.WaitGroup
    42 	saslExpected string
    53 	saslExpected string
    43 	authDone bool
    54 	authDone bool
    44 	idMutex sync.Mutex
    55 	idMutex sync.Mutex
    45 	nextId int64
    56 	nextId int64
    46 	handlers chan *stanzaHandler
    57 	handlers chan *stanzaHandler
       
    58 	// Incoming XMPP stanzas from the server will be published on
       
    59 	// this channel. Information which is only used by this
       
    60 	// library to set up the XMPP stream will not appear here.
       
    61 	// TODO Make these channels of type Stanza.
    47 	In <-chan interface{}
    62 	In <-chan interface{}
       
    63 	// Outgoing XMPP stanzas to the server should be sent to this
       
    64 	// channel.
    48 	Out chan<- interface{}
    65 	Out chan<- interface{}
    49 	xmlOut chan<- interface{}
    66 	xmlOut chan<- interface{}
       
    67 	// TODO Remove this. Make a Stanza parser method available for
       
    68 	// use by interact.go and similar applications.
    50 	TextOut chan<- *string
    69 	TextOut chan<- *string
    51 }
    70 }
    52 var _ io.Closer = &Client{}
    71 var _ io.Closer = &Client{}
    53 
    72 
    54 // Connect to the appropriate server and authenticate as the given JID
    73 // Connect to the appropriate server and authenticate as the given JID
    55 // with the given password.
    74 // with the given password. This function will return as soon as a TCP
       
    75 // connection has been established, but before XMPP stream negotiation
       
    76 // has completed. The negotiation will occur asynchronously, and sends
       
    77 // to Client.Out will block until negotiation is complete.
    56 func NewClient(jid *JID, password string) (*Client, os.Error) {
    78 func NewClient(jid *JID, password string) (*Client, os.Error) {
    57 	// Resolve the domain in the JID.
    79 	// Resolve the domain in the JID.
    58 	_, srvs, err := net.LookupSRV(clientSrv, "tcp", jid.Domain)
    80 	_, srvs, err := net.LookupSRV(clientSrv, "tcp", jid.Domain)
    59 	if err != nil {
    81 	if err != nil {
    60 		return nil, os.NewError("LookupSrv " + jid.Domain +
    82 		return nil, os.NewError("LookupSrv " + jid.Domain +
   102 
   124 
   103 	// Initial handshake.
   125 	// Initial handshake.
   104 	hsOut := &Stream{To: jid.Domain, Version: Version}
   126 	hsOut := &Stream{To: jid.Domain, Version: Version}
   105 	cl.xmlOut <- hsOut
   127 	cl.xmlOut <- hsOut
   106 
   128 
   107 	// TODO Wait for initialization to finish.
       
   108 
       
   109 	cl.In = clIn
   129 	cl.In = clIn
   110 	cl.Out = clOut
   130 	cl.Out = clOut
   111 	cl.TextOut = textOut
   131 	cl.TextOut = textOut
   112 
   132 
   113 	return cl, nil
   133 	return cl, nil
   205 			f2(ch)
   225 			f2(ch)
   206 		}
   226 		}
   207 	}
   227 	}
   208 }
   228 }
   209 
   229 
       
   230 // This convenience function may be used to generate a unique id for
       
   231 // use in the Id fields of iq, message, and presence stanzas.
   210 func (cl *Client) NextId() string {
   232 func (cl *Client) NextId() string {
   211 	cl.idMutex.Lock()
   233 	cl.idMutex.Lock()
   212 	defer cl.idMutex.Unlock()
   234 	defer cl.idMutex.Unlock()
   213 	id := cl.nextId
   235 	id := cl.nextId
   214 	cl.nextId++
   236 	cl.nextId++