diff -r b839e37b3f29 -r d269d9c0fc8e stream.go --- a/stream.go Wed Dec 28 11:57:29 2011 -0700 +++ b/stream.go Wed Dec 28 12:39:24 2011 -0700 @@ -5,7 +5,8 @@ // This file contains the three layers of processing for the // communication with the server: transport (where TLS happens), XML // (where strings are converted to go structures), and Stream (where -// we respond to XMPP events on behalf of the library client). +// we respond to XMPP events on behalf of the library client), or send +// those events to the client. package xmpp @@ -26,11 +27,16 @@ "xml" ) +// Callback to handle a stanza with a particular id. type stanzaHandler struct { id string + // Return true means pass this to the application f func(Stanza) bool } +// TODO Review all these *Client receiver methods. They should +// probably either all be receivers, or none. + func (cl *Client) readTransport(w io.Writer) { defer tryClose(cl.socket, w) cl.socket.SetReadTimeout(1e8) @@ -138,6 +144,10 @@ break } + // TODO If it's a Stanza, use reflection to search for + // any Unrecognized elements and fill in their + // attributes. + // Put it on the channel. ch <- obj } @@ -160,6 +170,8 @@ } } +// TODO This should go away. We shouldn't allow writing of +// unstructured data. func writeText(w io.Writer, ch <-chan *string) { if debug { pr, pw := io.Pipe() @@ -214,6 +226,9 @@ } } +// TODO Disable this loop until resource binding is +// complete. Otherwise the app might inject something weird into our +// negotiation stream. func writeStream(srvOut chan<- interface{}, cliIn <-chan interface{}) { defer tryClose(srvOut, cliIn) @@ -240,6 +255,7 @@ if fe.Bind != nil { cl.bind(fe.Bind) + return } } @@ -291,6 +307,7 @@ cl.socketSync.Done() } +// TODO Implement TLS/SASL EXTERNAL. func (cl *Client) chooseSasl(fe *Features) { var digestMd5 bool for _, m := range(fe.Mechanisms.Mechanism) { @@ -436,6 +453,7 @@ return m } +// Inverse of parseSasl(). func packSasl(m map[string]string) string { var terms []string for key, value := range(m) { @@ -447,6 +465,7 @@ return strings.Join(terms, ",") } +// Computes the response string for digest authentication. func saslDigestResponse(username, realm, passwd, nonce, cnonceStr, authenticate, digestUri, nonceCountStr string) string { h := func(text string) []byte { @@ -470,6 +489,7 @@ return response } +// Send a request to bind a resource. RFC 3920, section 7. func (cl *Client) bind(bind *Unrecognized) { res := cl.Jid.Resource msg := &Iq{Type: "set", Id: cl.NextId(), Any: @@ -512,6 +532,10 @@ cl.xmlOut <- msg } +// Register a callback to handle the next XMPP stanza (iq, message, or +// presence) with a given id. The provided function will not be called +// more than once. If it returns false, the stanza will not be made +// available on the normal Client.In channel. func (cl *Client) HandleStanza(id string, f func(Stanza) bool) { h := &stanzaHandler{id: id, f: f} cl.handlers <- h