Moved the library code into an xmpp directory.
authorChris Jones <christian.jones@sri.com>
Sat, 07 Sep 2013 10:04:44 -0700
changeset 126 367e76b3028e
parent 125 f464f14e39a7
child 127 a8f9a0c07fc8
Moved the library code into an xmpp directory.
filter.go
id.go
log.go
roster.go
roster_test.go
stream.go
stream_test.go
structs.go
structs_test.go
xmpp.go
xmpp/filter.go
xmpp/id.go
xmpp/log.go
xmpp/roster.go
xmpp/roster_test.go
xmpp/stream.go
xmpp/stream_test.go
xmpp/structs.go
xmpp/structs_test.go
xmpp/xmpp.go
xmpp/xmpp_test.go
xmpp_test.go
--- a/filter.go	Mon Sep 02 20:46:23 2013 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +0,0 @@
-package xmpp
-
-// Manages the stack of filters that can read and modify stanzas on
-// their way from the remote to the application.
-
-// Receive new filters on filterAdd; those new filters get added to
-// the top of the stack. Receive stanzas at the bottom of the stack on
-// input. Send stanzas out the top of the stack on output.
-func filterMgr(filterAdd <-chan Filter, input <-chan Stanza, output chan<- Stanza) {
-	botFiltIn := output
-	topFiltOut := input
-
-loop:
-	for {
-		select {
-		case stan, ok := <-input:
-			if !ok {
-				break loop
-			}
-			botFiltIn <- stan
-
-		case stan, ok := <-topFiltOut:
-			if !ok {
-				break loop
-			}
-			output <- stan
-
-		case filt := <-filterAdd:
-			newTop := make(chan Stanza)
-			go filt(topFiltOut, newTop)
-			topFiltOut = newTop
-		}
-	}
-	close(botFiltIn)
-}
-
-// Starts the filter chain. Filters will all interpose themselves
-// between srvIn and cliOut.
-func (cl *Client) startFilters(srvIn, cliIn <-chan Stanza) (<-chan Stanza, <-chan Stanza) {
-	cliOut := make(chan Stanza)
-	srvOut := make(chan Stanza)
-	go filterMgr(cl.sendFilterAdd, srvIn, cliOut)
-	go filterMgr(cl.recvFilterAdd, cliIn, srvOut)
-	return cliOut, srvOut
-}
-
-// AddRecvFilter adds a new filter to the top of the stack through which
-// incoming stanzas travel on their way up to the client.
-func (cl *Client) AddRecvFilter(filt Filter) {
-	if filt == nil {
-		return
-	}
-	cl.recvFilterAdd <- filt
-}
-
-// AddSendFilter adds a new filter to the top of the stack through
-// which outgoing stanzas travel on their way down from the client to
-// the network.
-func (cl *Client) AddSendFilter(filt Filter) {
-	if filt == nil {
-		return
-	}
-	cl.sendFilterAdd <- filt
-}
--- a/id.go	Mon Sep 02 20:46:23 2013 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,29 +0,0 @@
-package xmpp
-
-// Code to generate unique IDs for outgoing messages.
-
-import (
-	"fmt"
-)
-
-var id <-chan string
-
-func init() {
-	// Start the unique id generator.
-	idCh := make(chan string)
-	id = idCh
-	go func(ch chan<- string) {
-		id := int64(1)
-		for {
-			str := fmt.Sprintf("id_%d", id)
-			ch <- str
-			id++
-		}
-	}(idCh)
-}
-
-// This function may be used as a convenient way to generate a unique
-// id for an outgoing iq, message, or presence stanza.
-func NextId() string {
-	return <-id
-}
--- a/log.go	Mon Sep 02 20:46:23 2013 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,37 +0,0 @@
-// Copyright 2011 The Go Authors.  All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Control over logging from the XMPP library.
-
-package xmpp
-
-var (
-	// If any of these are non-nil when NewClient() is called,
-	// they will be used to log messages of the indicated
-	// severity.
-	Warn  Logger = &noLog{}
-	Info  Logger = &noLog{}
-	Debug Logger = &noLog{}
-)
-
-// Anything implementing Logger can receive log messages from the XMPP
-// library. The default implementation doesn't log anything; it
-// efficiently discards all messages.
-type Logger interface {
-	Log(v ...interface{})
-	Logf(fmt string, v ...interface{})
-}
-
-type noLog struct {
-	flags  int
-	prefix string
-}
-
-var _ Logger = &noLog{}
-
-func (l *noLog) Log(v ...interface{}) {
-}
-
-func (l *noLog) Logf(fmt string, v ...interface{}) {
-}
--- a/roster.go	Mon Sep 02 20:46:23 2013 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,155 +0,0 @@
-// Copyright 2011 The Go Authors.  All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package xmpp
-
-// This file contains support for roster management, RFC 3921, Section 7.
-
-import (
-	"encoding/xml"
-)
-
-// Roster query/result
-type RosterQuery struct {
-	XMLName xml.Name     `xml:"jabber:iq:roster query"`
-	Item    []RosterItem `xml:"item"`
-}
-
-// See RFC 3921, Section 7.1.
-type RosterItem struct {
-	XMLName      xml.Name `xml:"jabber:iq:roster item"`
-	Jid          string   `xml:"jid,attr"`
-	Subscription string   `xml:"subscription,attr"`
-	Name         string   `xml:"name,attr"`
-	Group        []string
-}
-
-type rosterCb struct {
-	id string
-	cb func()
-}
-
-type Roster struct {
-	Extension
-	get chan []RosterItem
-	callbacks chan rosterCb
-	toServer chan Stanza
-}
-
-type rosterClient struct {
-	rosterChan   <-chan []RosterItem
-	rosterUpdate chan<- RosterItem
-}
-
-// Implicitly becomes part of NewClient's extStanza arg.
-func newRosterQuery(name *xml.Name) interface{} {
-	return &RosterQuery{}
-}
-
-func (r *Roster) rosterMgr(upd <-chan Stanza) {
-	roster := make(map[string]RosterItem)
-	waits := make(map[string]func())
-	var snapshot []RosterItem
-	for {
-		select {
-		case stan, ok := <- upd:
-			if !ok {
-				return
-			}
-			hdr := stan.GetHeader()
-			if f := waits[hdr.Id] ; f != nil {
-				delete(waits, hdr.Id)
-				f()
-			}
-			iq, ok := stan.(*Iq)
-			if iq.Type != "set" {
-				continue
-			}
-			var rq *RosterQuery
-			for _, ele := range iq.Nested {
-				if q, ok := ele.(*RosterQuery); ok {
-					rq = q
-					break
-				}
-			}
-			if rq == nil {
-				continue
-			}
-			for _, item := range rq.Item {
-				roster[item.Jid] = item
-			}
-			snapshot = []RosterItem{}
-			for _, ri := range roster {
-				snapshot = append(snapshot, ri)
-			}
-		case r.get <- snapshot:
-		case cb := <- r.callbacks:
-			waits[cb.id] = cb.cb
-		}
-	}
-}
-
-func (r *Roster) makeFilters() (Filter, Filter) {
-	rosterUpdate := make(chan Stanza)
-	go r.rosterMgr(rosterUpdate)
-	recv := func(in <-chan Stanza, out chan<- Stanza) {
-		defer close(out)
-		for stan := range in {
-			rosterUpdate <- stan
-			out <- stan
-		}
-	}
-	send := func(in <-chan Stanza, out chan<- Stanza) {
-		defer close(out)
-		for {
-			select {
-			case stan, ok := <- in:
-				if !ok {
-					return
-				}
-				out <- stan
-			case stan := <- r.toServer:
-				out <- stan
-			}
-		}
-	}
-	return recv, send
-}
-
-func newRosterExt() *Roster {
-	r := Roster{}
-	r.StanzaHandlers = make(map[string]func(*xml.Name) interface{})
-	r.StanzaHandlers[NsRoster] = newRosterQuery
-	r.RecvFilter, r.SendFilter = r.makeFilters()
-	r.get = make(chan []RosterItem)
-	r.callbacks = make(chan rosterCb)
-	r.toServer = make(chan Stanza)
-	return &r
-}
-
-// Return the most recent snapshot of the roster status. This is
-// updated automatically as roster updates are received from the
-// server, but especially in response to calls to Update().
-func (r *Roster) Get() []RosterItem {
-	return <-r.get
-}
-
-// Synchronously fetch this entity's roster from the server and cache
-// that information. The client can access the roster by watching for
-// RosterQuery objects or by calling Get().
-func (r *Roster) Update() {
-	iq := &Iq{Header: Header{Type: "get", Id: NextId(),
-		Nested: []interface{}{RosterQuery{}}}}
-	waitchan := make(chan int)
-	done := func() {
-		close(waitchan)
-	}
-	r.waitFor(iq.Id, done)
-	r.toServer <- iq
-	<-waitchan
-}
-
-func (r *Roster) waitFor(id string, cb func()) {
-	r.callbacks <- rosterCb{id: id, cb: cb}
-}
--- a/roster_test.go	Mon Sep 02 20:46:23 2013 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,55 +0,0 @@
-// Copyright 2011 The Go Authors.  All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package xmpp
-
-import (
-	"encoding/xml"
-	"reflect"
-	"testing"
-)
-
-// This is mostly just tests of the roster data structures.
-
-func TestRosterIqMarshal(t *testing.T) {
-	iq := &Iq{Header: Header{From: "from", Lang: "en",
-		Nested: []interface{}{RosterQuery{}}}}
-	exp := `<iq from="from" xml:lang="en"><query xmlns="` +
-		NsRoster + `"></query></iq>`
-	assertMarshal(t, exp, iq)
-}
-
-func TestRosterIqUnmarshal(t *testing.T) {
-	str := `<iq from="from" xml:lang="en"><query xmlns="` +
-		NsRoster + `"><item jid="a@b.c"/></query></iq>`
-	iq := Iq{}
-	xml.Unmarshal([]byte(str), &iq)
-	m := map[string]func(*xml.Name) interface{}{NsRoster: newRosterQuery}
-	err := parseExtended(&iq.Header, m)
-	if err != nil {
-		t.Fatalf("parseExtended: %v", err)
-	}
-	assertEquals(t, "iq", iq.XMLName.Local)
-	assertEquals(t, "from", iq.From)
-	assertEquals(t, "en", iq.Lang)
-	nested := iq.Nested
-	if nested == nil {
-		t.Fatalf("nested nil")
-	}
-	if len(nested) != 1 {
-		t.Fatalf("wrong size nested(%d): %v", len(nested),
-			nested)
-	}
-	var rq *RosterQuery
-	rq, ok := nested[0].(*RosterQuery)
-	if !ok {
-		t.Fatalf("nested not RosterQuery: %v",
-			reflect.TypeOf(nested))
-	}
-	if len(rq.Item) != 1 {
-		t.Fatalf("Wrong # items: %v", rq.Item)
-	}
-	item := rq.Item[0]
-	assertEquals(t, "a@b.c", item.Jid)
-}
--- a/stream.go	Mon Sep 02 20:46:23 2013 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,609 +0,0 @@
-// Copyright 2011 The Go Authors.  All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// 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), or send
-// those events to the client.
-
-package xmpp
-
-import (
-	"crypto/md5"
-	"crypto/rand"
-	"crypto/tls"
-	"encoding/base64"
-	"encoding/xml"
-	"fmt"
-	"io"
-	"math/big"
-	"net"
-	"regexp"
-	"strings"
-	"time"
-)
-
-// 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
-}
-
-func (cl *Client) readTransport(w io.WriteCloser) {
-	defer w.Close()
-	p := make([]byte, 1024)
-	for {
-		if cl.socket == nil {
-			cl.waitForSocket()
-		}
-		cl.socket.SetReadDeadline(time.Now().Add(time.Second))
-		nr, err := cl.socket.Read(p)
-		if nr == 0 {
-			if errno, ok := err.(*net.OpError); ok {
-				if errno.Timeout() {
-					continue
-				}
-			}
-			Warn.Logf("read: %s", err)
-			break
-		}
-		nw, err := w.Write(p[:nr])
-		if nw < nr {
-			Warn.Logf("read: %s", err)
-			break
-		}
-	}
-}
-
-func (cl *Client) writeTransport(r io.Reader) {
-	defer cl.socket.Close()
-	p := make([]byte, 1024)
-	for {
-		nr, err := r.Read(p)
-		if nr == 0 {
-			Warn.Logf("write: %s", err)
-			break
-		}
-		nw, err := cl.socket.Write(p[:nr])
-		if nw < nr {
-			Warn.Logf("write: %s", err)
-			break
-		}
-	}
-}
-
-func readXml(r io.Reader, ch chan<- interface{},
-	extStanza map[string]func(*xml.Name) interface{}) {
-	if _, ok := Debug.(*noLog); !ok {
-		pr, pw := io.Pipe()
-		go tee(r, pw, "S: ")
-		r = pr
-	}
-	defer close(ch)
-
-	// This trick loads our namespaces into the parser.
-	nsstr := fmt.Sprintf(`<a xmlns="%s" xmlns:stream="%s">`,
-		NsClient, NsStream)
-	nsrdr := strings.NewReader(nsstr)
-	p := xml.NewDecoder(io.MultiReader(nsrdr, r))
-	p.Token()
-
-Loop:
-	for {
-		// Sniff the next token on the stream.
-		t, err := p.Token()
-		if t == nil {
-			if err != io.EOF {
-				Warn.Logf("read: %s", err)
-			}
-			break
-		}
-		var se xml.StartElement
-		var ok bool
-		if se, ok = t.(xml.StartElement); !ok {
-			continue
-		}
-
-		// Allocate the appropriate structure for this token.
-		var obj interface{}
-		switch se.Name.Space + " " + se.Name.Local {
-		case NsStream + " stream":
-			st, err := parseStream(se)
-			if err != nil {
-				Warn.Logf("unmarshal stream: %s", err)
-				break Loop
-			}
-			ch <- st
-			continue
-		case "stream error", NsStream + " error":
-			obj = &streamError{}
-		case NsStream + " features":
-			obj = &Features{}
-		case NsTLS + " proceed", NsTLS + " failure":
-			obj = &starttls{}
-		case NsSASL + " challenge", NsSASL + " failure",
-			NsSASL + " success":
-			obj = &auth{}
-		case NsClient + " iq":
-			obj = &Iq{}
-		case NsClient + " message":
-			obj = &Message{}
-		case NsClient + " presence":
-			obj = &Presence{}
-		default:
-			obj = &Generic{}
-			Info.Logf("Ignoring unrecognized: %s %s", se.Name.Space,
-				se.Name.Local)
-		}
-
-		// Read the complete XML stanza.
-		err = p.DecodeElement(obj, &se)
-		if err != nil {
-			Warn.Logf("unmarshal: %s", err)
-			break Loop
-		}
-
-		// If it's a Stanza, we try to unmarshal its innerxml
-		// into objects of the appropriate respective
-		// types. This is specified by our extensions.
-		if st, ok := obj.(Stanza); ok {
-			err = parseExtended(st.GetHeader(), extStanza)
-			if err != nil {
-				Warn.Logf("ext unmarshal: %s", err)
-				break Loop
-			}
-		}
-
-		// Put it on the channel.
-		ch <- obj
-	}
-}
-
-func parseExtended(st *Header, extStanza map[string]func(*xml.Name) interface{}) error {
-	// Now parse the stanza's innerxml to find the string that we
-	// can unmarshal this nested element from.
-	reader := strings.NewReader(st.Innerxml)
-	p := xml.NewDecoder(reader)
-	for {
-		t, err := p.Token()
-		if err == io.EOF {
-			break
-		}
-		if err != nil {
-			return err
-		}
-		if se, ok := t.(xml.StartElement); ok {
-			if con, ok := extStanza[se.Name.Space]; ok {
-				// Call the indicated constructor.
-				nested := con(&se.Name)
-
-				// Unmarshal the nested element and
-				// stuff it back into the stanza.
-				err := p.DecodeElement(nested, &se)
-				if err != nil {
-					return err
-				}
-				st.Nested = append(st.Nested, nested)
-			}
-		}
-	}
-
-	return nil
-}
-
-func writeXml(w io.Writer, ch <-chan interface{}) {
-	if _, ok := Debug.(*noLog); !ok {
-		pr, pw := io.Pipe()
-		go tee(pr, w, "C: ")
-		w = pw
-	}
-	defer func(w io.Writer) {
-		if c, ok := w.(io.Closer); ok {
-			c.Close()
-		}
-	}(w)
-
-	enc := xml.NewEncoder(w)
-
-	for obj := range ch {
-		if st, ok := obj.(*stream); ok {
-			_, err := w.Write([]byte(st.String()))
-			if err != nil {
-				Warn.Logf("write: %s", err)
-			}
-		} else {
-			err := enc.Encode(obj)
-			if err != nil {
-				Warn.Logf("marshal: %s", err)
-				break
-			}
-		}
-	}
-}
-
-func (cl *Client) readStream(srvIn <-chan interface{}, cliOut chan<- Stanza) {
-	defer close(cliOut)
-
-	handlers := make(map[string]func(Stanza) bool)
-Loop:
-	for {
-		select {
-		case h := <-cl.handlers:
-			handlers[h.id] = h.f
-		case x, ok := <-srvIn:
-			if !ok {
-				break Loop
-			}
-			switch obj := x.(type) {
-			case *stream:
-				handleStream(obj)
-			case *streamError:
-				cl.handleStreamError(obj)
-			case *Features:
-				cl.handleFeatures(obj)
-			case *starttls:
-				cl.handleTls(obj)
-			case *auth:
-				cl.handleSasl(obj)
-			case Stanza:
-				send := true
-				id := obj.GetHeader().Id
-				if handlers[id] != nil {
-					f := handlers[id]
-					delete(handlers, id)
-					send = f(obj)
-				}
-				if send {
-					cliOut <- obj
-				}
-			default:
-				Warn.Logf("Unhandled non-stanza: %T %#v", x, x)
-			}
-		}
-	}
-}
-
-// This loop is paused until resource binding is complete. Otherwise
-// the app might inject something inappropriate into our negotiations
-// with the server. The control channel controls this loop's
-// activity.
-func writeStream(srvOut chan<- interface{}, cliIn <-chan Stanza,
-	control <-chan int) {
-	defer close(srvOut)
-
-	var input <-chan Stanza
-Loop:
-	for {
-		select {
-		case status := <-control:
-			switch status {
-			case 0:
-				input = nil
-			case 1:
-				input = cliIn
-			case -1:
-				break Loop
-			}
-		case x, ok := <-input:
-			if !ok {
-				break Loop
-			}
-			if x == nil {
-				Info.Log("Refusing to send nil stanza")
-				continue
-			}
-			srvOut <- x
-		}
-	}
-}
-
-func handleStream(ss *stream) {
-}
-
-func (cl *Client) handleStreamError(se *streamError) {
-	Info.Logf("Received stream error: %v", se)
-	close(cl.Out)
-}
-
-func (cl *Client) handleFeatures(fe *Features) {
-	cl.Features = fe
-	if fe.Starttls != nil {
-		start := &starttls{XMLName: xml.Name{Space: NsTLS,
-			Local: "starttls"}}
-		cl.xmlOut <- start
-		return
-	}
-
-	if len(fe.Mechanisms.Mechanism) > 0 {
-		cl.chooseSasl(fe)
-		return
-	}
-
-	if fe.Bind != nil {
-		cl.bind(fe.Bind)
-		return
-	}
-}
-
-// readTransport() is running concurrently. We need to stop it,
-// negotiate TLS, then start it again. It calls waitForSocket() in
-// its inner loop; see below.
-func (cl *Client) handleTls(t *starttls) {
-	tcp := cl.socket
-
-	// Set the socket to nil, and wait for the reader routine to
-	// signal that it's paused.
-	cl.socket = nil
-	cl.socketSync.Add(1)
-	cl.socketSync.Wait()
-
-	// Negotiate TLS with the server.
-	tls := tls.Client(tcp, &TlsConfig)
-
-	// Make the TLS connection available to the reader, and wait
-	// for it to signal that it's working again.
-	cl.socketSync.Add(1)
-	cl.socket = tls
-	cl.socketSync.Wait()
-
-	Info.Log("TLS negotiation succeeded.")
-	cl.Features = nil
-
-	// Now re-send the initial handshake message to start the new
-	// session.
-	hsOut := &stream{To: cl.Jid.Domain, Version: XMPPVersion}
-	cl.xmlOut <- hsOut
-}
-
-// Synchronize with handleTls(). Called from readTransport() when
-// cl.socket is nil.
-func (cl *Client) waitForSocket() {
-	// Signal that we've stopped reading from the socket.
-	cl.socketSync.Done()
-
-	// Wait until the socket is available again.
-	for cl.socket == nil {
-		time.Sleep(1e8)
-	}
-
-	// Signal that we're going back to the read loop.
-	cl.socketSync.Done()
-}
-
-// BUG(cjyar): Doesn't implement TLS/SASL EXTERNAL.
-func (cl *Client) chooseSasl(fe *Features) {
-	var digestMd5 bool
-	for _, m := range fe.Mechanisms.Mechanism {
-		switch strings.ToLower(m) {
-		case "digest-md5":
-			digestMd5 = true
-		}
-	}
-
-	if digestMd5 {
-		auth := &auth{XMLName: xml.Name{Space: NsSASL, Local: "auth"}, Mechanism: "DIGEST-MD5"}
-		cl.xmlOut <- auth
-	}
-}
-
-func (cl *Client) handleSasl(srv *auth) {
-	switch strings.ToLower(srv.XMLName.Local) {
-	case "challenge":
-		b64 := base64.StdEncoding
-		str, err := b64.DecodeString(srv.Chardata)
-		if err != nil {
-			Warn.Logf("SASL challenge decode: %s", err)
-			return
-		}
-		srvMap := parseSasl(string(str))
-
-		if cl.saslExpected == "" {
-			cl.saslDigest1(srvMap)
-		} else {
-			cl.saslDigest2(srvMap)
-		}
-	case "failure":
-		Info.Log("SASL authentication failed")
-	case "success":
-		Info.Log("Sasl authentication succeeded")
-		cl.Features = nil
-		ss := &stream{To: cl.Jid.Domain, Version: XMPPVersion}
-		cl.xmlOut <- ss
-	}
-}
-
-func (cl *Client) saslDigest1(srvMap map[string]string) {
-	// Make sure it supports qop=auth
-	var hasAuth bool
-	for _, qop := range strings.Fields(srvMap["qop"]) {
-		if qop == "auth" {
-			hasAuth = true
-		}
-	}
-	if !hasAuth {
-		Warn.Log("Server doesn't support SASL auth")
-		return
-	}
-
-	// Pick a realm.
-	var realm string
-	if srvMap["realm"] != "" {
-		realm = strings.Fields(srvMap["realm"])[0]
-	}
-
-	passwd := cl.password
-	nonce := srvMap["nonce"]
-	digestUri := "xmpp/" + cl.Jid.Domain
-	nonceCount := int32(1)
-	nonceCountStr := fmt.Sprintf("%08x", nonceCount)
-
-	// Begin building the response. Username is
-	// user@domain or just domain.
-	var username string
-	if cl.Jid.Node == "" {
-		username = cl.Jid.Domain
-	} else {
-		username = cl.Jid.Node
-	}
-
-	// Generate our own nonce from random data.
-	randSize := big.NewInt(0)
-	randSize.Lsh(big.NewInt(1), 64)
-	cnonce, err := rand.Int(rand.Reader, randSize)
-	if err != nil {
-		Warn.Logf("SASL rand: %s", err)
-		return
-	}
-	cnonceStr := fmt.Sprintf("%016x", cnonce)
-
-	/* Now encode the actual password response, as well as the
-	 * expected next challenge from the server. */
-	response := saslDigestResponse(username, realm, passwd, nonce,
-		cnonceStr, "AUTHENTICATE", digestUri, nonceCountStr)
-	next := saslDigestResponse(username, realm, passwd, nonce,
-		cnonceStr, "", digestUri, nonceCountStr)
-	cl.saslExpected = next
-
-	// Build the map which will be encoded.
-	clMap := make(map[string]string)
-	clMap["realm"] = `"` + realm + `"`
-	clMap["username"] = `"` + username + `"`
-	clMap["nonce"] = `"` + nonce + `"`
-	clMap["cnonce"] = `"` + cnonceStr + `"`
-	clMap["nc"] = nonceCountStr
-	clMap["qop"] = "auth"
-	clMap["digest-uri"] = `"` + digestUri + `"`
-	clMap["response"] = response
-	if srvMap["charset"] == "utf-8" {
-		clMap["charset"] = "utf-8"
-	}
-
-	// Encode the map and send it.
-	clStr := packSasl(clMap)
-	b64 := base64.StdEncoding
-	clObj := &auth{XMLName: xml.Name{Space: NsSASL, Local: "response"}, Chardata: b64.EncodeToString([]byte(clStr))}
-	cl.xmlOut <- clObj
-}
-
-func (cl *Client) saslDigest2(srvMap map[string]string) {
-	if cl.saslExpected == srvMap["rspauth"] {
-		clObj := &auth{XMLName: xml.Name{Space: NsSASL, Local: "response"}}
-		cl.xmlOut <- clObj
-	} else {
-		clObj := &auth{XMLName: xml.Name{Space: NsSASL, Local: "failure"}, Any: &Generic{XMLName: xml.Name{Space: NsSASL,
-			Local: "abort"}}}
-		cl.xmlOut <- clObj
-	}
-}
-
-// Takes a string like `key1=value1,key2="value2"...` and returns a
-// key/value map.
-func parseSasl(in string) map[string]string {
-	re := regexp.MustCompile(`([^=]+)="?([^",]+)"?,?`)
-	strs := re.FindAllStringSubmatch(in, -1)
-	m := make(map[string]string)
-	for _, pair := range strs {
-		key := strings.ToLower(string(pair[1]))
-		value := string(pair[2])
-		m[key] = value
-	}
-	return m
-}
-
-// Inverse of parseSasl().
-func packSasl(m map[string]string) string {
-	var terms []string
-	for key, value := range m {
-		if key == "" || value == "" || value == `""` {
-			continue
-		}
-		terms = append(terms, key+"="+value)
-	}
-	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 {
-		h := md5.New()
-		h.Write([]byte(text))
-		return h.Sum(nil)
-	}
-	hex := func(bytes []byte) string {
-		return fmt.Sprintf("%x", bytes)
-	}
-	kd := func(secret, data string) []byte {
-		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))))
-	return response
-}
-
-// Send a request to bind a resource. RFC 3920, section 7.
-func (cl *Client) bind(bindAdv *bindIq) {
-	res := cl.Jid.Resource
-	bindReq := &bindIq{}
-	if res != "" {
-		bindReq.Resource = &res
-	}
-	msg := &Iq{Header: Header{Type: "set", Id: NextId(),
-		Nested: []interface{}{bindReq}}}
-	f := func(st Stanza) bool {
-		iq, ok := st.(*Iq)
-		if !ok {
-			Warn.Log("non-iq response")
-		}
-		if iq.Type == "error" {
-			Warn.Log("Resource binding failed")
-			return false
-		}
-		var bindRepl *bindIq
-		for _, ele := range iq.Nested {
-			if b, ok := ele.(*bindIq); ok {
-				bindRepl = b
-				break
-			}
-		}
-		if bindRepl == nil {
-			Warn.Logf("Bad bind reply: %#v", iq)
-			return false
-		}
-		jidStr := bindRepl.Jid
-		if jidStr == nil || *jidStr == "" {
-			Warn.Log("Can't bind empty resource")
-			return false
-		}
-		jid := new(JID)
-		if err := jid.Set(*jidStr); err != nil {
-			Warn.Logf("Can't parse JID %s: %s", *jidStr, err)
-			return false
-		}
-		cl.Jid = *jid
-		Info.Logf("Bound resource: %s", cl.Jid.String())
-		cl.bindDone()
-		return false
-	}
-	cl.HandleStanza(msg.Id, f)
-	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. The stanza handler
-// must not read from that channel, as deliveries on it cannot proceed
-// until the handler returns true or false.
-func (cl *Client) HandleStanza(id string, f func(Stanza) bool) {
-	h := &stanzaHandler{id: id, f: f}
-	cl.handlers <- h
-}
--- a/stream_test.go	Mon Sep 02 20:46:23 2013 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,19 +0,0 @@
-// Copyright 2011 The Go Authors.  All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package xmpp
-
-import (
-	"testing"
-)
-
-func TestSaslDigest(t *testing.T) {
-	// These values are from RFC2831, section 4.
-	obs := saslDigestResponse("chris", "elwood.innosoft.com",
-		"secret", "OA6MG9tEQGm2hh", "OA6MHXh6VqTrRk",
-		"AUTHENTICATE", "imap/elwood.innosoft.com",
-		"00000001")
-	exp := "d388dad90d4bbd760a152321f2143af7"
-	assertEquals(t, exp, obs)
-}
--- a/structs.go	Mon Sep 02 20:46:23 2013 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,276 +0,0 @@
-// Copyright 2011 The Go Authors.  All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package xmpp
-
-// This file contains data structures.
-
-import (
-	"bytes"
-	"encoding/xml"
-	"flag"
-	"fmt"
-	// BUG(cjyar): Doesn't use stringprep. Could try the implementation at
-	// "code.google.com/p/go-idn/src/stringprep"
-	"regexp"
-	"strings"
-)
-
-// JID represents an entity that can communicate with other
-// entities. It looks like node@domain/resource. Node and resource are
-// sometimes optional.
-type JID struct {
-	Node     string
-	Domain   string
-	Resource string
-}
-
-var _ fmt.Stringer = &JID{}
-var _ flag.Value = &JID{}
-
-// XMPP's <stream:stream> XML element
-type stream struct {
-	XMLName xml.Name `xml:"stream=http://etherx.jabber.org/streams stream"`
-	To      string   `xml:"to,attr"`
-	From    string   `xml:"from,attr"`
-	Id      string   `xml:"id,attr"`
-	Lang    string   `xml:"http://www.w3.org/XML/1998/namespace lang,attr"`
-	Version string   `xml:"version,attr"`
-}
-
-var _ fmt.Stringer = &stream{}
-
-// <stream:error>
-type streamError struct {
-	XMLName xml.Name `xml:"http://etherx.jabber.org/streams error"`
-	Any     Generic  `xml:",any"`
-	Text    *errText
-}
-
-type errText struct {
-	XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-streams text"`
-	Lang    string   `xml:"http://www.w3.org/XML/1998/namespace lang,attr"`
-	Text    string   `xml:",chardata"`
-}
-
-type Features struct {
-	Starttls   *starttls `xml:"urn:ietf:params:xml:ns:xmpp-tls starttls"`
-	Mechanisms mechs     `xml:"urn:ietf:params:xml:ns:xmpp-sasl mechanisms"`
-	Bind       *bindIq
-	Session    *Generic
-	Any        *Generic
-}
-
-type starttls struct {
-	XMLName  xml.Name
-	Required *string
-}
-
-type mechs struct {
-	Mechanism []string `xml:"urn:ietf:params:xml:ns:xmpp-sasl mechanism"`
-}
-
-type auth struct {
-	XMLName   xml.Name
-	Chardata  string `xml:",chardata"`
-	Mechanism string `xml:"mechanism,attr,omitempty"`
-	Any       *Generic
-}
-
-type Stanza interface {
-	GetHeader() *Header
-}
-
-// One of the three core XMPP stanza types: iq, message, presence. See
-// RFC3920, section 9.
-type Header struct {
-	To       string `xml:"to,attr,omitempty"`
-	From     string `xml:"from,attr,omitempty"`
-	Id       string `xml:"id,attr,omitempty"`
-	Type     string `xml:"type,attr,omitempty"`
-	Lang     string `xml:"http://www.w3.org/XML/1998/namespace lang,attr,omitempty"`
-	Innerxml string `xml:",innerxml"`
-	Error    *Error
-	Nested   []interface{}
-}
-
-// message stanza
-type Message struct {
-	XMLName xml.Name `xml:"jabber:client message"`
-	Header
-	Subject *Generic `xml:"jabber:client subject"`
-	Body    *Generic `xml:"jabber:client body"`
-	Thread  *Generic `xml:"jabber:client thread"`
-}
-
-var _ Stanza = &Message{}
-
-// presence stanza
-type Presence struct {
-	XMLName xml.Name `xml:"presence"`
-	Header
-	Show     *Generic `xml:"jabber:client show"`
-	Status   *Generic `xml:"jabber:client status"`
-	Priority *Generic `xml:"jabber:client priority"`
-}
-
-var _ Stanza = &Presence{}
-
-// iq stanza
-type Iq struct {
-	XMLName xml.Name `xml:"iq"`
-	Header
-}
-
-var _ Stanza = &Iq{}
-
-// Describes an XMPP stanza error. See RFC 3920, Section 9.3.
-type Error struct {
-	XMLName xml.Name `xml:"error"`
-	// The error type attribute.
-	Type string `xml:"type,attr"`
-	// Any nested element, if present.
-	Any *Generic
-}
-
-var _ error = &Error{}
-
-// Used for resource binding as a nested element inside <iq/>.
-type bindIq struct {
-	XMLName  xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-bind bind"`
-	Resource *string  `xml:"resource"`
-	Jid      *string  `xml:"jid"`
-}
-
-// Holds an XML element not described by the more specific types.
-type Generic struct {
-	XMLName  xml.Name
-	Any      *Generic `xml:",any"`
-	Chardata string   `xml:",chardata"`
-}
-
-var _ fmt.Stringer = &Generic{}
-
-func (jid *JID) String() string {
-	result := jid.Domain
-	if jid.Node != "" {
-		result = jid.Node + "@" + result
-	}
-	if jid.Resource != "" {
-		result = result + "/" + jid.Resource
-	}
-	return result
-}
-
-// Set implements flag.Value. It returns true if it successfully
-// parses the string.
-func (jid *JID) Set(val string) error {
-	r := regexp.MustCompile("^(([^@/]+)@)?([^@/]+)(/([^@/]+))?$")
-	parts := r.FindStringSubmatch(val)
-	if parts == nil {
-		return fmt.Errorf("%s doesn't match user@domain/resource", val)
-	}
-	// jid.Node = stringprep.Nodeprep(parts[2])
-	// jid.Domain = stringprep.Nodeprep(parts[3])
-	// jid.Resource = stringprep.Resourceprep(parts[5])
-	jid.Node = parts[2]
-	jid.Domain = parts[3]
-	jid.Resource = parts[5]
-	return nil
-}
-
-func (s *stream) String() string {
-	var buf bytes.Buffer
-	buf.WriteString(`<stream:stream xmlns="`)
-	buf.WriteString(NsClient)
-	buf.WriteString(`" xmlns:stream="`)
-	buf.WriteString(NsStream)
-	buf.WriteString(`"`)
-	if s.To != "" {
-		buf.WriteString(` to="`)
-		xml.Escape(&buf, []byte(s.To))
-		buf.WriteString(`"`)
-	}
-	if s.From != "" {
-		buf.WriteString(` from="`)
-		xml.Escape(&buf, []byte(s.From))
-		buf.WriteString(`"`)
-	}
-	if s.Id != "" {
-		buf.WriteString(` id="`)
-		xml.Escape(&buf, []byte(s.Id))
-		buf.WriteString(`"`)
-	}
-	if s.Lang != "" {
-		buf.WriteString(` xml:lang="`)
-		xml.Escape(&buf, []byte(s.Lang))
-		buf.WriteString(`"`)
-	}
-	if s.Version != "" {
-		buf.WriteString(` version="`)
-		xml.Escape(&buf, []byte(s.Version))
-		buf.WriteString(`"`)
-	}
-	buf.WriteString(">")
-	return buf.String()
-}
-
-func parseStream(se xml.StartElement) (*stream, error) {
-	s := &stream{}
-	for _, attr := range se.Attr {
-		switch strings.ToLower(attr.Name.Local) {
-		case "to":
-			s.To = attr.Value
-		case "from":
-			s.From = attr.Value
-		case "id":
-			s.Id = attr.Value
-		case "lang":
-			s.Lang = attr.Value
-		case "version":
-			s.Version = attr.Value
-		}
-	}
-	return s, nil
-}
-
-func (iq *Iq) GetHeader() *Header {
-	return &iq.Header
-}
-
-func (m *Message) GetHeader() *Header {
-	return &m.Header
-}
-
-func (p *Presence) GetHeader() *Header {
-	return &p.Header
-}
-
-func (u *Generic) String() string {
-	if u == nil {
-		return "nil"
-	}
-	var sub string
-	if u.Any != nil {
-		sub = u.Any.String()
-	}
-	return fmt.Sprintf("<%s %s>%s%s</%s %s>", u.XMLName.Space,
-		u.XMLName.Local, sub, u.Chardata, u.XMLName.Space,
-		u.XMLName.Local)
-}
-
-func (er *Error) Error() string {
-	buf, err := xml.Marshal(er)
-	if err != nil {
-		Warn.Log("double bad error: couldn't marshal error")
-		return "unreadable error"
-	}
-	return string(buf)
-}
-
-var bindExt Extension = Extension{StanzaHandlers: map[string]func(*xml.Name) interface{}{NsBind: newBind}}
-
-func newBind(name *xml.Name) interface{} {
-	return &bindIq{}
-}
--- a/structs_test.go	Mon Sep 02 20:46:23 2013 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,143 +0,0 @@
-// Copyright 2011 The Go Authors.  All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package xmpp
-
-import (
-	"bytes"
-	"encoding/xml"
-	"fmt"
-	"os"
-	"reflect"
-	"runtime"
-	"strings"
-	"testing"
-)
-
-func assertEquals(t *testing.T, expected, observed string) {
-	if expected != observed {
-		file := "unknown"
-		line := 0
-		_, file, line, _ = runtime.Caller(1)
-		fmt.Fprintf(os.Stderr, "%s:%d: Expected:\n%s\nObserved:\n%s\n",
-			file, line, expected, observed)
-		t.Fail()
-	}
-}
-
-func TestJid(t *testing.T) {
-	str := "user@domain/res"
-	jid := &JID{}
-	if err := jid.Set(str); err != nil {
-		t.Errorf("Set(%s) failed: %s", str, err)
-	}
-	assertEquals(t, "user", jid.Node)
-	assertEquals(t, "domain", jid.Domain)
-	assertEquals(t, "res", jid.Resource)
-	assertEquals(t, str, jid.String())
-
-	str = "domain.tld"
-	if err := jid.Set(str); err != nil {
-		t.Errorf("Set(%s) failed: %s", str, err)
-	}
-	if jid.Node != "" {
-		t.Errorf("Node: %v\n", jid.Node)
-	}
-	assertEquals(t, "domain.tld", jid.Domain)
-	if jid.Resource != "" {
-		t.Errorf("Resource: %v\n", jid.Resource)
-	}
-	assertEquals(t, str, jid.String())
-}
-
-func assertMarshal(t *testing.T, expected string, marshal interface{}) {
-	var buf bytes.Buffer
-	enc := xml.NewEncoder(&buf)
-	err := enc.Encode(marshal)
-	if err != nil {
-		t.Errorf("Marshal error for %s: %s", marshal, err)
-	}
-	observed := buf.String()
-	if expected != observed {
-		file := "unknown"
-		line := 0
-		_, file, line, _ = runtime.Caller(1)
-		fmt.Fprintf(os.Stderr, "%s:%d: Expected:\n%s\nObserved:\n%s\n",
-			file, line, expected, observed)
-		t.Fail()
-	}
-}
-
-func TestStreamMarshal(t *testing.T) {
-	s := &stream{To: "bob"}
-	exp := `<stream:stream xmlns="` + NsClient +
-		`" xmlns:stream="` + NsStream + `" to="bob">`
-	assertEquals(t, exp, s.String())
-
-	s = &stream{To: "bob", From: "alice", Id: "#3", Version: "5.3"}
-	exp = `<stream:stream xmlns="` + NsClient +
-		`" xmlns:stream="` + NsStream + `" to="bob" from="alice"` +
-		` id="#3" version="5.3">`
-	assertEquals(t, exp, s.String())
-
-	s = &stream{Lang: "en_US"}
-	exp = `<stream:stream xmlns="` + NsClient +
-		`" xmlns:stream="` + NsStream + `" xml:lang="en_US">`
-	assertEquals(t, exp, s.String())
-}
-
-func TestStreamErrorMarshal(t *testing.T) {
-	name := xml.Name{Space: NsStreams, Local: "ack"}
-	e := &streamError{Any: Generic{XMLName: name}}
-	exp := `<error xmlns="` + NsStream + `"><ack xmlns="` + NsStreams +
-		`"></ack></error>`
-	assertMarshal(t, exp, e)
-
-	txt := errText{Lang: "pt", Text: "things happen"}
-	e = &streamError{Any: Generic{XMLName: name}, Text: &txt}
-	exp = `<error xmlns="` + NsStream + `"><ack xmlns="` + NsStreams +
-		`"></ack><text xmlns="` + NsStreams +
-		`" xml:lang="pt">things happen</text></error>`
-	assertMarshal(t, exp, e)
-}
-
-func TestIqMarshal(t *testing.T) {
-	iq := &Iq{Header: Header{Type: "set", Id: "3",
-		Nested: []interface{}{Generic{XMLName: xml.Name{Space: NsBind,
-			Local: "bind"}}}}}
-	exp := `<iq id="3" type="set"><bind xmlns="` + NsBind +
-		`"></bind></iq>`
-	assertMarshal(t, exp, iq)
-}
-
-func TestMarshalEscaping(t *testing.T) {
-	msg := &Message{Body: &Generic{XMLName: xml.Name{Local: "body"},
-		Chardata: `&<!-- "`}}
-	exp := `<message xmlns="jabber:client"><body>&amp;&lt;!-- &#34;</body></message>`
-	assertMarshal(t, exp, msg)
-}
-
-func TestUnmarshalMessage(t *testing.T) {
-	str := `<message to="a@b.c"><body>foo!</body></message>`
-	r := strings.NewReader(str)
-	ch := make(chan interface{})
-	go readXml(r, ch, make(map[string]func(*xml.Name) interface{}))
-	obs := <-ch
-	exp := &Message{XMLName: xml.Name{Local: "message", Space: "jabber:client"},
-		Header: Header{To: "a@b.c", Innerxml: "<body>foo!</body>"},
-		Body: &Generic{XMLName: xml.Name{Local: "body", Space: "jabber:client"},
-			Chardata: "foo!"}}
-	if !reflect.DeepEqual(obs, exp) {
-		t.Errorf("read %s\ngot:  %#v\nwant: %#v\n", str, obs, exp)
-	}
-	obsMsg, ok := obs.(*Message)
-	if !ok {
-		t.Fatalf("Not a Message: %T", obs)
-	}
-	obsBody := obsMsg.Body
-	expBody := exp.Body
-	if !reflect.DeepEqual(obsBody, expBody) {
-		t.Errorf("body\ngot:  %#v\nwant: %#v\n", obsBody, expBody)
-	}
-}
--- a/xmpp.go	Mon Sep 02 20:46:23 2013 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,261 +0,0 @@
-// Copyright 2011 The Go Authors.  All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// This package implements a simple XMPP client according to RFCs 3920
-// and 3921, plus the various XEPs at http://xmpp.org/protocols/. The
-// implementation is structured as a stack of layers, with TCP at the
-// bottom and the application at the top. The application receives and
-// sends structures representing XMPP stanzas. Additional stanza
-// parsers can be inserted into the stack of layers as extensions.
-package xmpp
-
-import (
-	"bytes"
-	"crypto/tls"
-	"encoding/xml"
-	"errors"
-	"fmt"
-	"io"
-	"net"
-	"sync"
-)
-
-const (
-	// Version of RFC 3920 that we implement.
-	XMPPVersion = "1.0"
-
-	// Various XML namespaces.
-	NsClient  = "jabber:client"
-	NsStreams = "urn:ietf:params:xml:ns:xmpp-streams"
-	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"
-	NsSession = "urn:ietf:params:xml:ns:xmpp-session"
-	NsRoster  = "jabber:iq:roster"
-
-	// DNS SRV names
-	serverSrv = "xmpp-server"
-	clientSrv = "xmpp-client"
-)
-
-// A filter can modify the XMPP traffic to or from the remote
-// server. It's part of an Extension. The filter function will be
-// called in a new goroutine, so it doesn't need to return. The filter
-// should close its output when its input is closed.
-type Filter func(in <-chan Stanza, out chan<- Stanza)
-
-// Extensions can add stanza filters and/or new XML element types.
-type Extension struct {
-	// Maps from an XML namespace to a function which constructs a
-	// structure to hold the contents of stanzas in that
-	// namespace.
-	StanzaHandlers map[string]func(*xml.Name) interface{}
-	// If non-nil, will be called once to start the filter
-	// running. RecvFilter intercepts incoming messages on their
-	// way from the remote server to the application; SendFilter
-	// intercepts messages going the other direction.
-	RecvFilter Filter
-	SendFilter Filter
-}
-
-// Allows the user to override the TLS configuration.
-var TlsConfig tls.Config
-
-// The client in a client-server XMPP connection.
-type Client struct {
-	// This client's JID. This will be updated asynchronously by
-	// the time StartSession() returns.
-	Jid          JID
-	password     string
-	socket       net.Conn
-	socketSync   sync.WaitGroup
-	saslExpected string
-	authDone     bool
-	handlers     chan *stanzaHandler
-	inputControl chan int
-	// Incoming XMPP stanzas from the remote will be published on
-	// this channel. Information which is used by this library to
-	// set up the XMPP stream will not appear here.
-	In <-chan Stanza
-	// Outgoing XMPP stanzas to the server should be sent to this
-	// channel.
-	Out    chan<- Stanza
-	xmlOut chan<- interface{}
-	// The client's roster is also known as the buddy list. It's
-	// the set of contacts which are known to this JID, or which
-	// this JID is known to.
-	Roster Roster
-	// Features advertised by the remote. This will be updated
-	// asynchronously as new features are received throughout the
-	// connection process. It should not be updated once
-	// StartSession() returns.
-	Features  *Features
-	sendFilterAdd, recvFilterAdd chan Filter
-}
-
-// Connect to the appropriate server and authenticate as the given JID
-// with the given password. This function will return as soon as a TCP
-// connection has been established, but before XMPP stream negotiation
-// has completed. The negotiation will occur asynchronously, and any
-// send operation to Client.Out will block until negotiation (resource
-// binding) is complete.
-func NewClient(jid *JID, password string, exts []Extension) (*Client, error) {
-	// Include the mandatory extensions.
-	roster := newRosterExt()
-	exts = append(exts, roster.Extension)
-	exts = append(exts, bindExt)
-
-	// Resolve the domain in the JID.
-	_, srvs, err := net.LookupSRV(clientSrv, "tcp", jid.Domain)
-	if err != nil {
-		return nil, errors.New("LookupSrv " + jid.Domain +
-			": " + err.Error())
-	}
-
-	var tcp *net.TCPConn
-	for _, srv := range srvs {
-		addrStr := fmt.Sprintf("%s:%d", srv.Target, srv.Port)
-		addr, err := net.ResolveTCPAddr("tcp", addrStr)
-		if err != nil {
-			err = fmt.Errorf("ResolveTCPAddr(%s): %s",
-				addrStr, err.Error())
-			continue
-		}
-		tcp, err = net.DialTCP("tcp", nil, addr)
-		if err == nil {
-			break
-		}
-		err = fmt.Errorf("DialTCP(%s): %s", addr, err)
-	}
-	if tcp == nil {
-		return nil, err
-	}
-
-	cl := new(Client)
-	cl.Roster = *roster
-	cl.password = password
-	cl.Jid = *jid
-	cl.socket = tcp
-	cl.handlers = make(chan *stanzaHandler, 100)
-	cl.inputControl = make(chan int)
-
-	extStanza := make(map[string]func(*xml.Name) interface{})
-	for _, ext := range exts {
-		for k, v := range ext.StanzaHandlers {
-			extStanza[k] = v
-		}
-	}
-
-	// Start the transport handler, initially unencrypted.
-	recvReader, recvWriter := io.Pipe()
-	sendReader, sendWriter := io.Pipe()
-	go cl.readTransport(recvWriter)
-	go cl.writeTransport(sendReader)
-
-	// Start the reader and writer that convert to and from XML.
-	recvXml := make(chan interface{})
-	go readXml(recvReader, recvXml, extStanza)
-	sendXml := make(chan interface{})
-	cl.xmlOut = sendXml
-	go writeXml(sendWriter, sendXml)
-
-	// Start the reader and writer that convert between XML and
-	// XMPP stanzas.
-	recvRawXmpp := make(chan Stanza)
-	go cl.readStream(recvXml, recvRawXmpp)
-	sendRawXmpp := make(chan Stanza)
-	go writeStream(sendXml, sendRawXmpp, cl.inputControl)
-
-	// Start the manager for the filters that can modify what the
-	// app sees.
-	recvFiltXmpp := make(chan Stanza)
-	cl.In = recvFiltXmpp
-	go filterMgr(cl.recvFilterAdd, recvRawXmpp, recvFiltXmpp)
-	sendFiltXmpp := make(chan Stanza)
-	cl.Out = sendFiltXmpp
-	go filterMgr(cl.sendFilterAdd, sendFiltXmpp, sendFiltXmpp)
-
-	// Initial handshake.
-	hsOut := &stream{To: jid.Domain, Version: XMPPVersion}
-	cl.xmlOut <- hsOut
-
-	return cl, nil
-}
-
-func tee(r io.Reader, w io.Writer, prefix string) {
-	defer func(w io.Writer) {
-		if c, ok := w.(io.Closer); ok {
-			c.Close()
-		}
-	}(w)
-
-	buf := bytes.NewBuffer([]uint8(prefix))
-	for {
-		var c [1]byte
-		n, _ := r.Read(c[:])
-		if n == 0 {
-			break
-		}
-		n, _ = w.Write(c[:n])
-		if n == 0 {
-			break
-		}
-		buf.Write(c[:n])
-		if c[0] == '\n' || c[0] == '>' {
-			Debug.Log(buf)
-			buf = bytes.NewBuffer([]uint8(prefix))
-		}
-	}
-	leftover := buf.String()
-	if leftover != "" {
-		Debug.Log(buf)
-	}
-}
-
-// bindDone is called when we've finished resource binding (and all
-// the negotiations that precede it). Now we can start accepting
-// traffic from the app.
-func (cl *Client) bindDone() {
-	cl.inputControl <- 1
-}
-
-// Start an XMPP session. A typical XMPP client should call this
-// immediately after creating the Client in order to start the
-// session, retrieve the roster, and broadcast an initial
-// presence. The presence can be as simple as a newly-initialized
-// Presence struct.  See RFC 3921, Section 3. After calling this, a
-// normal client will want to call Roster.Update().
-func (cl *Client) StartSession(pr *Presence) error {
-	id := NextId()
-	iq := &Iq{Header: Header{To: cl.Jid.Domain, Id: id, Type: "set",
-		Nested: []interface{}{Generic{XMLName: xml.Name{Space: NsSession, Local: "session"}}}}}
-	ch := make(chan error)
-	f := func(st Stanza) bool {
-		iq, ok := st.(*Iq)
-		if !ok {
-			Warn.Log("iq reply not iq; can't start session")
-			ch <- errors.New("bad session start reply")
-			return false
-		}
-		if iq.Type == "error" {
-			Warn.Logf("Can't start session: %v", iq)
-			ch <- iq.Error
-			return false
-		}
-		ch <- nil
-		return false
-	}
-	cl.HandleStanza(id, f)
-	cl.Out <- iq
-
-	// Now wait until the callback is called.
-	if err := <-ch; err != nil {
-		return err
-	}
-	if pr != nil {
-		cl.Out <- pr
-	}
-	return nil
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/xmpp/filter.go	Sat Sep 07 10:04:44 2013 -0700
@@ -0,0 +1,64 @@
+package xmpp
+
+// Manages the stack of filters that can read and modify stanzas on
+// their way from the remote to the application.
+
+// Receive new filters on filterAdd; those new filters get added to
+// the top of the stack. Receive stanzas at the bottom of the stack on
+// input. Send stanzas out the top of the stack on output.
+func filterMgr(filterAdd <-chan Filter, input <-chan Stanza, output chan<- Stanza) {
+	botFiltIn := output
+	topFiltOut := input
+
+loop:
+	for {
+		select {
+		case stan, ok := <-input:
+			if !ok {
+				break loop
+			}
+			botFiltIn <- stan
+
+		case stan, ok := <-topFiltOut:
+			if !ok {
+				break loop
+			}
+			output <- stan
+
+		case filt := <-filterAdd:
+			newTop := make(chan Stanza)
+			go filt(topFiltOut, newTop)
+			topFiltOut = newTop
+		}
+	}
+	close(botFiltIn)
+}
+
+// Starts the filter chain. Filters will all interpose themselves
+// between srvIn and cliOut.
+func (cl *Client) startFilters(srvIn, cliIn <-chan Stanza) (<-chan Stanza, <-chan Stanza) {
+	cliOut := make(chan Stanza)
+	srvOut := make(chan Stanza)
+	go filterMgr(cl.sendFilterAdd, srvIn, cliOut)
+	go filterMgr(cl.recvFilterAdd, cliIn, srvOut)
+	return cliOut, srvOut
+}
+
+// AddRecvFilter adds a new filter to the top of the stack through which
+// incoming stanzas travel on their way up to the client.
+func (cl *Client) AddRecvFilter(filt Filter) {
+	if filt == nil {
+		return
+	}
+	cl.recvFilterAdd <- filt
+}
+
+// AddSendFilter adds a new filter to the top of the stack through
+// which outgoing stanzas travel on their way down from the client to
+// the network.
+func (cl *Client) AddSendFilter(filt Filter) {
+	if filt == nil {
+		return
+	}
+	cl.sendFilterAdd <- filt
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/xmpp/id.go	Sat Sep 07 10:04:44 2013 -0700
@@ -0,0 +1,29 @@
+package xmpp
+
+// Code to generate unique IDs for outgoing messages.
+
+import (
+	"fmt"
+)
+
+var id <-chan string
+
+func init() {
+	// Start the unique id generator.
+	idCh := make(chan string)
+	id = idCh
+	go func(ch chan<- string) {
+		id := int64(1)
+		for {
+			str := fmt.Sprintf("id_%d", id)
+			ch <- str
+			id++
+		}
+	}(idCh)
+}
+
+// This function may be used as a convenient way to generate a unique
+// id for an outgoing iq, message, or presence stanza.
+func NextId() string {
+	return <-id
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/xmpp/log.go	Sat Sep 07 10:04:44 2013 -0700
@@ -0,0 +1,37 @@
+// Copyright 2011 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Control over logging from the XMPP library.
+
+package xmpp
+
+var (
+	// If any of these are non-nil when NewClient() is called,
+	// they will be used to log messages of the indicated
+	// severity.
+	Warn  Logger = &noLog{}
+	Info  Logger = &noLog{}
+	Debug Logger = &noLog{}
+)
+
+// Anything implementing Logger can receive log messages from the XMPP
+// library. The default implementation doesn't log anything; it
+// efficiently discards all messages.
+type Logger interface {
+	Log(v ...interface{})
+	Logf(fmt string, v ...interface{})
+}
+
+type noLog struct {
+	flags  int
+	prefix string
+}
+
+var _ Logger = &noLog{}
+
+func (l *noLog) Log(v ...interface{}) {
+}
+
+func (l *noLog) Logf(fmt string, v ...interface{}) {
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/xmpp/roster.go	Sat Sep 07 10:04:44 2013 -0700
@@ -0,0 +1,155 @@
+// Copyright 2011 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xmpp
+
+// This file contains support for roster management, RFC 3921, Section 7.
+
+import (
+	"encoding/xml"
+)
+
+// Roster query/result
+type RosterQuery struct {
+	XMLName xml.Name     `xml:"jabber:iq:roster query"`
+	Item    []RosterItem `xml:"item"`
+}
+
+// See RFC 3921, Section 7.1.
+type RosterItem struct {
+	XMLName      xml.Name `xml:"jabber:iq:roster item"`
+	Jid          string   `xml:"jid,attr"`
+	Subscription string   `xml:"subscription,attr"`
+	Name         string   `xml:"name,attr"`
+	Group        []string
+}
+
+type rosterCb struct {
+	id string
+	cb func()
+}
+
+type Roster struct {
+	Extension
+	get chan []RosterItem
+	callbacks chan rosterCb
+	toServer chan Stanza
+}
+
+type rosterClient struct {
+	rosterChan   <-chan []RosterItem
+	rosterUpdate chan<- RosterItem
+}
+
+// Implicitly becomes part of NewClient's extStanza arg.
+func newRosterQuery(name *xml.Name) interface{} {
+	return &RosterQuery{}
+}
+
+func (r *Roster) rosterMgr(upd <-chan Stanza) {
+	roster := make(map[string]RosterItem)
+	waits := make(map[string]func())
+	var snapshot []RosterItem
+	for {
+		select {
+		case stan, ok := <- upd:
+			if !ok {
+				return
+			}
+			hdr := stan.GetHeader()
+			if f := waits[hdr.Id] ; f != nil {
+				delete(waits, hdr.Id)
+				f()
+			}
+			iq, ok := stan.(*Iq)
+			if iq.Type != "set" {
+				continue
+			}
+			var rq *RosterQuery
+			for _, ele := range iq.Nested {
+				if q, ok := ele.(*RosterQuery); ok {
+					rq = q
+					break
+				}
+			}
+			if rq == nil {
+				continue
+			}
+			for _, item := range rq.Item {
+				roster[item.Jid] = item
+			}
+			snapshot = []RosterItem{}
+			for _, ri := range roster {
+				snapshot = append(snapshot, ri)
+			}
+		case r.get <- snapshot:
+		case cb := <- r.callbacks:
+			waits[cb.id] = cb.cb
+		}
+	}
+}
+
+func (r *Roster) makeFilters() (Filter, Filter) {
+	rosterUpdate := make(chan Stanza)
+	go r.rosterMgr(rosterUpdate)
+	recv := func(in <-chan Stanza, out chan<- Stanza) {
+		defer close(out)
+		for stan := range in {
+			rosterUpdate <- stan
+			out <- stan
+		}
+	}
+	send := func(in <-chan Stanza, out chan<- Stanza) {
+		defer close(out)
+		for {
+			select {
+			case stan, ok := <- in:
+				if !ok {
+					return
+				}
+				out <- stan
+			case stan := <- r.toServer:
+				out <- stan
+			}
+		}
+	}
+	return recv, send
+}
+
+func newRosterExt() *Roster {
+	r := Roster{}
+	r.StanzaHandlers = make(map[string]func(*xml.Name) interface{})
+	r.StanzaHandlers[NsRoster] = newRosterQuery
+	r.RecvFilter, r.SendFilter = r.makeFilters()
+	r.get = make(chan []RosterItem)
+	r.callbacks = make(chan rosterCb)
+	r.toServer = make(chan Stanza)
+	return &r
+}
+
+// Return the most recent snapshot of the roster status. This is
+// updated automatically as roster updates are received from the
+// server, but especially in response to calls to Update().
+func (r *Roster) Get() []RosterItem {
+	return <-r.get
+}
+
+// Synchronously fetch this entity's roster from the server and cache
+// that information. The client can access the roster by watching for
+// RosterQuery objects or by calling Get().
+func (r *Roster) Update() {
+	iq := &Iq{Header: Header{Type: "get", Id: NextId(),
+		Nested: []interface{}{RosterQuery{}}}}
+	waitchan := make(chan int)
+	done := func() {
+		close(waitchan)
+	}
+	r.waitFor(iq.Id, done)
+	r.toServer <- iq
+	<-waitchan
+}
+
+func (r *Roster) waitFor(id string, cb func()) {
+	r.callbacks <- rosterCb{id: id, cb: cb}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/xmpp/roster_test.go	Sat Sep 07 10:04:44 2013 -0700
@@ -0,0 +1,55 @@
+// Copyright 2011 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xmpp
+
+import (
+	"encoding/xml"
+	"reflect"
+	"testing"
+)
+
+// This is mostly just tests of the roster data structures.
+
+func TestRosterIqMarshal(t *testing.T) {
+	iq := &Iq{Header: Header{From: "from", Lang: "en",
+		Nested: []interface{}{RosterQuery{}}}}
+	exp := `<iq from="from" xml:lang="en"><query xmlns="` +
+		NsRoster + `"></query></iq>`
+	assertMarshal(t, exp, iq)
+}
+
+func TestRosterIqUnmarshal(t *testing.T) {
+	str := `<iq from="from" xml:lang="en"><query xmlns="` +
+		NsRoster + `"><item jid="a@b.c"/></query></iq>`
+	iq := Iq{}
+	xml.Unmarshal([]byte(str), &iq)
+	m := map[string]func(*xml.Name) interface{}{NsRoster: newRosterQuery}
+	err := parseExtended(&iq.Header, m)
+	if err != nil {
+		t.Fatalf("parseExtended: %v", err)
+	}
+	assertEquals(t, "iq", iq.XMLName.Local)
+	assertEquals(t, "from", iq.From)
+	assertEquals(t, "en", iq.Lang)
+	nested := iq.Nested
+	if nested == nil {
+		t.Fatalf("nested nil")
+	}
+	if len(nested) != 1 {
+		t.Fatalf("wrong size nested(%d): %v", len(nested),
+			nested)
+	}
+	var rq *RosterQuery
+	rq, ok := nested[0].(*RosterQuery)
+	if !ok {
+		t.Fatalf("nested not RosterQuery: %v",
+			reflect.TypeOf(nested))
+	}
+	if len(rq.Item) != 1 {
+		t.Fatalf("Wrong # items: %v", rq.Item)
+	}
+	item := rq.Item[0]
+	assertEquals(t, "a@b.c", item.Jid)
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/xmpp/stream.go	Sat Sep 07 10:04:44 2013 -0700
@@ -0,0 +1,609 @@
+// Copyright 2011 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// 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), or send
+// those events to the client.
+
+package xmpp
+
+import (
+	"crypto/md5"
+	"crypto/rand"
+	"crypto/tls"
+	"encoding/base64"
+	"encoding/xml"
+	"fmt"
+	"io"
+	"math/big"
+	"net"
+	"regexp"
+	"strings"
+	"time"
+)
+
+// 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
+}
+
+func (cl *Client) readTransport(w io.WriteCloser) {
+	defer w.Close()
+	p := make([]byte, 1024)
+	for {
+		if cl.socket == nil {
+			cl.waitForSocket()
+		}
+		cl.socket.SetReadDeadline(time.Now().Add(time.Second))
+		nr, err := cl.socket.Read(p)
+		if nr == 0 {
+			if errno, ok := err.(*net.OpError); ok {
+				if errno.Timeout() {
+					continue
+				}
+			}
+			Warn.Logf("read: %s", err)
+			break
+		}
+		nw, err := w.Write(p[:nr])
+		if nw < nr {
+			Warn.Logf("read: %s", err)
+			break
+		}
+	}
+}
+
+func (cl *Client) writeTransport(r io.Reader) {
+	defer cl.socket.Close()
+	p := make([]byte, 1024)
+	for {
+		nr, err := r.Read(p)
+		if nr == 0 {
+			Warn.Logf("write: %s", err)
+			break
+		}
+		nw, err := cl.socket.Write(p[:nr])
+		if nw < nr {
+			Warn.Logf("write: %s", err)
+			break
+		}
+	}
+}
+
+func readXml(r io.Reader, ch chan<- interface{},
+	extStanza map[string]func(*xml.Name) interface{}) {
+	if _, ok := Debug.(*noLog); !ok {
+		pr, pw := io.Pipe()
+		go tee(r, pw, "S: ")
+		r = pr
+	}
+	defer close(ch)
+
+	// This trick loads our namespaces into the parser.
+	nsstr := fmt.Sprintf(`<a xmlns="%s" xmlns:stream="%s">`,
+		NsClient, NsStream)
+	nsrdr := strings.NewReader(nsstr)
+	p := xml.NewDecoder(io.MultiReader(nsrdr, r))
+	p.Token()
+
+Loop:
+	for {
+		// Sniff the next token on the stream.
+		t, err := p.Token()
+		if t == nil {
+			if err != io.EOF {
+				Warn.Logf("read: %s", err)
+			}
+			break
+		}
+		var se xml.StartElement
+		var ok bool
+		if se, ok = t.(xml.StartElement); !ok {
+			continue
+		}
+
+		// Allocate the appropriate structure for this token.
+		var obj interface{}
+		switch se.Name.Space + " " + se.Name.Local {
+		case NsStream + " stream":
+			st, err := parseStream(se)
+			if err != nil {
+				Warn.Logf("unmarshal stream: %s", err)
+				break Loop
+			}
+			ch <- st
+			continue
+		case "stream error", NsStream + " error":
+			obj = &streamError{}
+		case NsStream + " features":
+			obj = &Features{}
+		case NsTLS + " proceed", NsTLS + " failure":
+			obj = &starttls{}
+		case NsSASL + " challenge", NsSASL + " failure",
+			NsSASL + " success":
+			obj = &auth{}
+		case NsClient + " iq":
+			obj = &Iq{}
+		case NsClient + " message":
+			obj = &Message{}
+		case NsClient + " presence":
+			obj = &Presence{}
+		default:
+			obj = &Generic{}
+			Info.Logf("Ignoring unrecognized: %s %s", se.Name.Space,
+				se.Name.Local)
+		}
+
+		// Read the complete XML stanza.
+		err = p.DecodeElement(obj, &se)
+		if err != nil {
+			Warn.Logf("unmarshal: %s", err)
+			break Loop
+		}
+
+		// If it's a Stanza, we try to unmarshal its innerxml
+		// into objects of the appropriate respective
+		// types. This is specified by our extensions.
+		if st, ok := obj.(Stanza); ok {
+			err = parseExtended(st.GetHeader(), extStanza)
+			if err != nil {
+				Warn.Logf("ext unmarshal: %s", err)
+				break Loop
+			}
+		}
+
+		// Put it on the channel.
+		ch <- obj
+	}
+}
+
+func parseExtended(st *Header, extStanza map[string]func(*xml.Name) interface{}) error {
+	// Now parse the stanza's innerxml to find the string that we
+	// can unmarshal this nested element from.
+	reader := strings.NewReader(st.Innerxml)
+	p := xml.NewDecoder(reader)
+	for {
+		t, err := p.Token()
+		if err == io.EOF {
+			break
+		}
+		if err != nil {
+			return err
+		}
+		if se, ok := t.(xml.StartElement); ok {
+			if con, ok := extStanza[se.Name.Space]; ok {
+				// Call the indicated constructor.
+				nested := con(&se.Name)
+
+				// Unmarshal the nested element and
+				// stuff it back into the stanza.
+				err := p.DecodeElement(nested, &se)
+				if err != nil {
+					return err
+				}
+				st.Nested = append(st.Nested, nested)
+			}
+		}
+	}
+
+	return nil
+}
+
+func writeXml(w io.Writer, ch <-chan interface{}) {
+	if _, ok := Debug.(*noLog); !ok {
+		pr, pw := io.Pipe()
+		go tee(pr, w, "C: ")
+		w = pw
+	}
+	defer func(w io.Writer) {
+		if c, ok := w.(io.Closer); ok {
+			c.Close()
+		}
+	}(w)
+
+	enc := xml.NewEncoder(w)
+
+	for obj := range ch {
+		if st, ok := obj.(*stream); ok {
+			_, err := w.Write([]byte(st.String()))
+			if err != nil {
+				Warn.Logf("write: %s", err)
+			}
+		} else {
+			err := enc.Encode(obj)
+			if err != nil {
+				Warn.Logf("marshal: %s", err)
+				break
+			}
+		}
+	}
+}
+
+func (cl *Client) readStream(srvIn <-chan interface{}, cliOut chan<- Stanza) {
+	defer close(cliOut)
+
+	handlers := make(map[string]func(Stanza) bool)
+Loop:
+	for {
+		select {
+		case h := <-cl.handlers:
+			handlers[h.id] = h.f
+		case x, ok := <-srvIn:
+			if !ok {
+				break Loop
+			}
+			switch obj := x.(type) {
+			case *stream:
+				handleStream(obj)
+			case *streamError:
+				cl.handleStreamError(obj)
+			case *Features:
+				cl.handleFeatures(obj)
+			case *starttls:
+				cl.handleTls(obj)
+			case *auth:
+				cl.handleSasl(obj)
+			case Stanza:
+				send := true
+				id := obj.GetHeader().Id
+				if handlers[id] != nil {
+					f := handlers[id]
+					delete(handlers, id)
+					send = f(obj)
+				}
+				if send {
+					cliOut <- obj
+				}
+			default:
+				Warn.Logf("Unhandled non-stanza: %T %#v", x, x)
+			}
+		}
+	}
+}
+
+// This loop is paused until resource binding is complete. Otherwise
+// the app might inject something inappropriate into our negotiations
+// with the server. The control channel controls this loop's
+// activity.
+func writeStream(srvOut chan<- interface{}, cliIn <-chan Stanza,
+	control <-chan int) {
+	defer close(srvOut)
+
+	var input <-chan Stanza
+Loop:
+	for {
+		select {
+		case status := <-control:
+			switch status {
+			case 0:
+				input = nil
+			case 1:
+				input = cliIn
+			case -1:
+				break Loop
+			}
+		case x, ok := <-input:
+			if !ok {
+				break Loop
+			}
+			if x == nil {
+				Info.Log("Refusing to send nil stanza")
+				continue
+			}
+			srvOut <- x
+		}
+	}
+}
+
+func handleStream(ss *stream) {
+}
+
+func (cl *Client) handleStreamError(se *streamError) {
+	Info.Logf("Received stream error: %v", se)
+	close(cl.Out)
+}
+
+func (cl *Client) handleFeatures(fe *Features) {
+	cl.Features = fe
+	if fe.Starttls != nil {
+		start := &starttls{XMLName: xml.Name{Space: NsTLS,
+			Local: "starttls"}}
+		cl.xmlOut <- start
+		return
+	}
+
+	if len(fe.Mechanisms.Mechanism) > 0 {
+		cl.chooseSasl(fe)
+		return
+	}
+
+	if fe.Bind != nil {
+		cl.bind(fe.Bind)
+		return
+	}
+}
+
+// readTransport() is running concurrently. We need to stop it,
+// negotiate TLS, then start it again. It calls waitForSocket() in
+// its inner loop; see below.
+func (cl *Client) handleTls(t *starttls) {
+	tcp := cl.socket
+
+	// Set the socket to nil, and wait for the reader routine to
+	// signal that it's paused.
+	cl.socket = nil
+	cl.socketSync.Add(1)
+	cl.socketSync.Wait()
+
+	// Negotiate TLS with the server.
+	tls := tls.Client(tcp, &TlsConfig)
+
+	// Make the TLS connection available to the reader, and wait
+	// for it to signal that it's working again.
+	cl.socketSync.Add(1)
+	cl.socket = tls
+	cl.socketSync.Wait()
+
+	Info.Log("TLS negotiation succeeded.")
+	cl.Features = nil
+
+	// Now re-send the initial handshake message to start the new
+	// session.
+	hsOut := &stream{To: cl.Jid.Domain, Version: XMPPVersion}
+	cl.xmlOut <- hsOut
+}
+
+// Synchronize with handleTls(). Called from readTransport() when
+// cl.socket is nil.
+func (cl *Client) waitForSocket() {
+	// Signal that we've stopped reading from the socket.
+	cl.socketSync.Done()
+
+	// Wait until the socket is available again.
+	for cl.socket == nil {
+		time.Sleep(1e8)
+	}
+
+	// Signal that we're going back to the read loop.
+	cl.socketSync.Done()
+}
+
+// BUG(cjyar): Doesn't implement TLS/SASL EXTERNAL.
+func (cl *Client) chooseSasl(fe *Features) {
+	var digestMd5 bool
+	for _, m := range fe.Mechanisms.Mechanism {
+		switch strings.ToLower(m) {
+		case "digest-md5":
+			digestMd5 = true
+		}
+	}
+
+	if digestMd5 {
+		auth := &auth{XMLName: xml.Name{Space: NsSASL, Local: "auth"}, Mechanism: "DIGEST-MD5"}
+		cl.xmlOut <- auth
+	}
+}
+
+func (cl *Client) handleSasl(srv *auth) {
+	switch strings.ToLower(srv.XMLName.Local) {
+	case "challenge":
+		b64 := base64.StdEncoding
+		str, err := b64.DecodeString(srv.Chardata)
+		if err != nil {
+			Warn.Logf("SASL challenge decode: %s", err)
+			return
+		}
+		srvMap := parseSasl(string(str))
+
+		if cl.saslExpected == "" {
+			cl.saslDigest1(srvMap)
+		} else {
+			cl.saslDigest2(srvMap)
+		}
+	case "failure":
+		Info.Log("SASL authentication failed")
+	case "success":
+		Info.Log("Sasl authentication succeeded")
+		cl.Features = nil
+		ss := &stream{To: cl.Jid.Domain, Version: XMPPVersion}
+		cl.xmlOut <- ss
+	}
+}
+
+func (cl *Client) saslDigest1(srvMap map[string]string) {
+	// Make sure it supports qop=auth
+	var hasAuth bool
+	for _, qop := range strings.Fields(srvMap["qop"]) {
+		if qop == "auth" {
+			hasAuth = true
+		}
+	}
+	if !hasAuth {
+		Warn.Log("Server doesn't support SASL auth")
+		return
+	}
+
+	// Pick a realm.
+	var realm string
+	if srvMap["realm"] != "" {
+		realm = strings.Fields(srvMap["realm"])[0]
+	}
+
+	passwd := cl.password
+	nonce := srvMap["nonce"]
+	digestUri := "xmpp/" + cl.Jid.Domain
+	nonceCount := int32(1)
+	nonceCountStr := fmt.Sprintf("%08x", nonceCount)
+
+	// Begin building the response. Username is
+	// user@domain or just domain.
+	var username string
+	if cl.Jid.Node == "" {
+		username = cl.Jid.Domain
+	} else {
+		username = cl.Jid.Node
+	}
+
+	// Generate our own nonce from random data.
+	randSize := big.NewInt(0)
+	randSize.Lsh(big.NewInt(1), 64)
+	cnonce, err := rand.Int(rand.Reader, randSize)
+	if err != nil {
+		Warn.Logf("SASL rand: %s", err)
+		return
+	}
+	cnonceStr := fmt.Sprintf("%016x", cnonce)
+
+	/* Now encode the actual password response, as well as the
+	 * expected next challenge from the server. */
+	response := saslDigestResponse(username, realm, passwd, nonce,
+		cnonceStr, "AUTHENTICATE", digestUri, nonceCountStr)
+	next := saslDigestResponse(username, realm, passwd, nonce,
+		cnonceStr, "", digestUri, nonceCountStr)
+	cl.saslExpected = next
+
+	// Build the map which will be encoded.
+	clMap := make(map[string]string)
+	clMap["realm"] = `"` + realm + `"`
+	clMap["username"] = `"` + username + `"`
+	clMap["nonce"] = `"` + nonce + `"`
+	clMap["cnonce"] = `"` + cnonceStr + `"`
+	clMap["nc"] = nonceCountStr
+	clMap["qop"] = "auth"
+	clMap["digest-uri"] = `"` + digestUri + `"`
+	clMap["response"] = response
+	if srvMap["charset"] == "utf-8" {
+		clMap["charset"] = "utf-8"
+	}
+
+	// Encode the map and send it.
+	clStr := packSasl(clMap)
+	b64 := base64.StdEncoding
+	clObj := &auth{XMLName: xml.Name{Space: NsSASL, Local: "response"}, Chardata: b64.EncodeToString([]byte(clStr))}
+	cl.xmlOut <- clObj
+}
+
+func (cl *Client) saslDigest2(srvMap map[string]string) {
+	if cl.saslExpected == srvMap["rspauth"] {
+		clObj := &auth{XMLName: xml.Name{Space: NsSASL, Local: "response"}}
+		cl.xmlOut <- clObj
+	} else {
+		clObj := &auth{XMLName: xml.Name{Space: NsSASL, Local: "failure"}, Any: &Generic{XMLName: xml.Name{Space: NsSASL,
+			Local: "abort"}}}
+		cl.xmlOut <- clObj
+	}
+}
+
+// Takes a string like `key1=value1,key2="value2"...` and returns a
+// key/value map.
+func parseSasl(in string) map[string]string {
+	re := regexp.MustCompile(`([^=]+)="?([^",]+)"?,?`)
+	strs := re.FindAllStringSubmatch(in, -1)
+	m := make(map[string]string)
+	for _, pair := range strs {
+		key := strings.ToLower(string(pair[1]))
+		value := string(pair[2])
+		m[key] = value
+	}
+	return m
+}
+
+// Inverse of parseSasl().
+func packSasl(m map[string]string) string {
+	var terms []string
+	for key, value := range m {
+		if key == "" || value == "" || value == `""` {
+			continue
+		}
+		terms = append(terms, key+"="+value)
+	}
+	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 {
+		h := md5.New()
+		h.Write([]byte(text))
+		return h.Sum(nil)
+	}
+	hex := func(bytes []byte) string {
+		return fmt.Sprintf("%x", bytes)
+	}
+	kd := func(secret, data string) []byte {
+		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))))
+	return response
+}
+
+// Send a request to bind a resource. RFC 3920, section 7.
+func (cl *Client) bind(bindAdv *bindIq) {
+	res := cl.Jid.Resource
+	bindReq := &bindIq{}
+	if res != "" {
+		bindReq.Resource = &res
+	}
+	msg := &Iq{Header: Header{Type: "set", Id: NextId(),
+		Nested: []interface{}{bindReq}}}
+	f := func(st Stanza) bool {
+		iq, ok := st.(*Iq)
+		if !ok {
+			Warn.Log("non-iq response")
+		}
+		if iq.Type == "error" {
+			Warn.Log("Resource binding failed")
+			return false
+		}
+		var bindRepl *bindIq
+		for _, ele := range iq.Nested {
+			if b, ok := ele.(*bindIq); ok {
+				bindRepl = b
+				break
+			}
+		}
+		if bindRepl == nil {
+			Warn.Logf("Bad bind reply: %#v", iq)
+			return false
+		}
+		jidStr := bindRepl.Jid
+		if jidStr == nil || *jidStr == "" {
+			Warn.Log("Can't bind empty resource")
+			return false
+		}
+		jid := new(JID)
+		if err := jid.Set(*jidStr); err != nil {
+			Warn.Logf("Can't parse JID %s: %s", *jidStr, err)
+			return false
+		}
+		cl.Jid = *jid
+		Info.Logf("Bound resource: %s", cl.Jid.String())
+		cl.bindDone()
+		return false
+	}
+	cl.HandleStanza(msg.Id, f)
+	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. The stanza handler
+// must not read from that channel, as deliveries on it cannot proceed
+// until the handler returns true or false.
+func (cl *Client) HandleStanza(id string, f func(Stanza) bool) {
+	h := &stanzaHandler{id: id, f: f}
+	cl.handlers <- h
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/xmpp/stream_test.go	Sat Sep 07 10:04:44 2013 -0700
@@ -0,0 +1,19 @@
+// Copyright 2011 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xmpp
+
+import (
+	"testing"
+)
+
+func TestSaslDigest(t *testing.T) {
+	// These values are from RFC2831, section 4.
+	obs := saslDigestResponse("chris", "elwood.innosoft.com",
+		"secret", "OA6MG9tEQGm2hh", "OA6MHXh6VqTrRk",
+		"AUTHENTICATE", "imap/elwood.innosoft.com",
+		"00000001")
+	exp := "d388dad90d4bbd760a152321f2143af7"
+	assertEquals(t, exp, obs)
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/xmpp/structs.go	Sat Sep 07 10:04:44 2013 -0700
@@ -0,0 +1,276 @@
+// Copyright 2011 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xmpp
+
+// This file contains data structures.
+
+import (
+	"bytes"
+	"encoding/xml"
+	"flag"
+	"fmt"
+	// BUG(cjyar): Doesn't use stringprep. Could try the implementation at
+	// "code.google.com/p/go-idn/src/stringprep"
+	"regexp"
+	"strings"
+)
+
+// JID represents an entity that can communicate with other
+// entities. It looks like node@domain/resource. Node and resource are
+// sometimes optional.
+type JID struct {
+	Node     string
+	Domain   string
+	Resource string
+}
+
+var _ fmt.Stringer = &JID{}
+var _ flag.Value = &JID{}
+
+// XMPP's <stream:stream> XML element
+type stream struct {
+	XMLName xml.Name `xml:"stream=http://etherx.jabber.org/streams stream"`
+	To      string   `xml:"to,attr"`
+	From    string   `xml:"from,attr"`
+	Id      string   `xml:"id,attr"`
+	Lang    string   `xml:"http://www.w3.org/XML/1998/namespace lang,attr"`
+	Version string   `xml:"version,attr"`
+}
+
+var _ fmt.Stringer = &stream{}
+
+// <stream:error>
+type streamError struct {
+	XMLName xml.Name `xml:"http://etherx.jabber.org/streams error"`
+	Any     Generic  `xml:",any"`
+	Text    *errText
+}
+
+type errText struct {
+	XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-streams text"`
+	Lang    string   `xml:"http://www.w3.org/XML/1998/namespace lang,attr"`
+	Text    string   `xml:",chardata"`
+}
+
+type Features struct {
+	Starttls   *starttls `xml:"urn:ietf:params:xml:ns:xmpp-tls starttls"`
+	Mechanisms mechs     `xml:"urn:ietf:params:xml:ns:xmpp-sasl mechanisms"`
+	Bind       *bindIq
+	Session    *Generic
+	Any        *Generic
+}
+
+type starttls struct {
+	XMLName  xml.Name
+	Required *string
+}
+
+type mechs struct {
+	Mechanism []string `xml:"urn:ietf:params:xml:ns:xmpp-sasl mechanism"`
+}
+
+type auth struct {
+	XMLName   xml.Name
+	Chardata  string `xml:",chardata"`
+	Mechanism string `xml:"mechanism,attr,omitempty"`
+	Any       *Generic
+}
+
+type Stanza interface {
+	GetHeader() *Header
+}
+
+// One of the three core XMPP stanza types: iq, message, presence. See
+// RFC3920, section 9.
+type Header struct {
+	To       string `xml:"to,attr,omitempty"`
+	From     string `xml:"from,attr,omitempty"`
+	Id       string `xml:"id,attr,omitempty"`
+	Type     string `xml:"type,attr,omitempty"`
+	Lang     string `xml:"http://www.w3.org/XML/1998/namespace lang,attr,omitempty"`
+	Innerxml string `xml:",innerxml"`
+	Error    *Error
+	Nested   []interface{}
+}
+
+// message stanza
+type Message struct {
+	XMLName xml.Name `xml:"jabber:client message"`
+	Header
+	Subject *Generic `xml:"jabber:client subject"`
+	Body    *Generic `xml:"jabber:client body"`
+	Thread  *Generic `xml:"jabber:client thread"`
+}
+
+var _ Stanza = &Message{}
+
+// presence stanza
+type Presence struct {
+	XMLName xml.Name `xml:"presence"`
+	Header
+	Show     *Generic `xml:"jabber:client show"`
+	Status   *Generic `xml:"jabber:client status"`
+	Priority *Generic `xml:"jabber:client priority"`
+}
+
+var _ Stanza = &Presence{}
+
+// iq stanza
+type Iq struct {
+	XMLName xml.Name `xml:"iq"`
+	Header
+}
+
+var _ Stanza = &Iq{}
+
+// Describes an XMPP stanza error. See RFC 3920, Section 9.3.
+type Error struct {
+	XMLName xml.Name `xml:"error"`
+	// The error type attribute.
+	Type string `xml:"type,attr"`
+	// Any nested element, if present.
+	Any *Generic
+}
+
+var _ error = &Error{}
+
+// Used for resource binding as a nested element inside <iq/>.
+type bindIq struct {
+	XMLName  xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-bind bind"`
+	Resource *string  `xml:"resource"`
+	Jid      *string  `xml:"jid"`
+}
+
+// Holds an XML element not described by the more specific types.
+type Generic struct {
+	XMLName  xml.Name
+	Any      *Generic `xml:",any"`
+	Chardata string   `xml:",chardata"`
+}
+
+var _ fmt.Stringer = &Generic{}
+
+func (jid *JID) String() string {
+	result := jid.Domain
+	if jid.Node != "" {
+		result = jid.Node + "@" + result
+	}
+	if jid.Resource != "" {
+		result = result + "/" + jid.Resource
+	}
+	return result
+}
+
+// Set implements flag.Value. It returns true if it successfully
+// parses the string.
+func (jid *JID) Set(val string) error {
+	r := regexp.MustCompile("^(([^@/]+)@)?([^@/]+)(/([^@/]+))?$")
+	parts := r.FindStringSubmatch(val)
+	if parts == nil {
+		return fmt.Errorf("%s doesn't match user@domain/resource", val)
+	}
+	// jid.Node = stringprep.Nodeprep(parts[2])
+	// jid.Domain = stringprep.Nodeprep(parts[3])
+	// jid.Resource = stringprep.Resourceprep(parts[5])
+	jid.Node = parts[2]
+	jid.Domain = parts[3]
+	jid.Resource = parts[5]
+	return nil
+}
+
+func (s *stream) String() string {
+	var buf bytes.Buffer
+	buf.WriteString(`<stream:stream xmlns="`)
+	buf.WriteString(NsClient)
+	buf.WriteString(`" xmlns:stream="`)
+	buf.WriteString(NsStream)
+	buf.WriteString(`"`)
+	if s.To != "" {
+		buf.WriteString(` to="`)
+		xml.Escape(&buf, []byte(s.To))
+		buf.WriteString(`"`)
+	}
+	if s.From != "" {
+		buf.WriteString(` from="`)
+		xml.Escape(&buf, []byte(s.From))
+		buf.WriteString(`"`)
+	}
+	if s.Id != "" {
+		buf.WriteString(` id="`)
+		xml.Escape(&buf, []byte(s.Id))
+		buf.WriteString(`"`)
+	}
+	if s.Lang != "" {
+		buf.WriteString(` xml:lang="`)
+		xml.Escape(&buf, []byte(s.Lang))
+		buf.WriteString(`"`)
+	}
+	if s.Version != "" {
+		buf.WriteString(` version="`)
+		xml.Escape(&buf, []byte(s.Version))
+		buf.WriteString(`"`)
+	}
+	buf.WriteString(">")
+	return buf.String()
+}
+
+func parseStream(se xml.StartElement) (*stream, error) {
+	s := &stream{}
+	for _, attr := range se.Attr {
+		switch strings.ToLower(attr.Name.Local) {
+		case "to":
+			s.To = attr.Value
+		case "from":
+			s.From = attr.Value
+		case "id":
+			s.Id = attr.Value
+		case "lang":
+			s.Lang = attr.Value
+		case "version":
+			s.Version = attr.Value
+		}
+	}
+	return s, nil
+}
+
+func (iq *Iq) GetHeader() *Header {
+	return &iq.Header
+}
+
+func (m *Message) GetHeader() *Header {
+	return &m.Header
+}
+
+func (p *Presence) GetHeader() *Header {
+	return &p.Header
+}
+
+func (u *Generic) String() string {
+	if u == nil {
+		return "nil"
+	}
+	var sub string
+	if u.Any != nil {
+		sub = u.Any.String()
+	}
+	return fmt.Sprintf("<%s %s>%s%s</%s %s>", u.XMLName.Space,
+		u.XMLName.Local, sub, u.Chardata, u.XMLName.Space,
+		u.XMLName.Local)
+}
+
+func (er *Error) Error() string {
+	buf, err := xml.Marshal(er)
+	if err != nil {
+		Warn.Log("double bad error: couldn't marshal error")
+		return "unreadable error"
+	}
+	return string(buf)
+}
+
+var bindExt Extension = Extension{StanzaHandlers: map[string]func(*xml.Name) interface{}{NsBind: newBind}}
+
+func newBind(name *xml.Name) interface{} {
+	return &bindIq{}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/xmpp/structs_test.go	Sat Sep 07 10:04:44 2013 -0700
@@ -0,0 +1,143 @@
+// Copyright 2011 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xmpp
+
+import (
+	"bytes"
+	"encoding/xml"
+	"fmt"
+	"os"
+	"reflect"
+	"runtime"
+	"strings"
+	"testing"
+)
+
+func assertEquals(t *testing.T, expected, observed string) {
+	if expected != observed {
+		file := "unknown"
+		line := 0
+		_, file, line, _ = runtime.Caller(1)
+		fmt.Fprintf(os.Stderr, "%s:%d: Expected:\n%s\nObserved:\n%s\n",
+			file, line, expected, observed)
+		t.Fail()
+	}
+}
+
+func TestJid(t *testing.T) {
+	str := "user@domain/res"
+	jid := &JID{}
+	if err := jid.Set(str); err != nil {
+		t.Errorf("Set(%s) failed: %s", str, err)
+	}
+	assertEquals(t, "user", jid.Node)
+	assertEquals(t, "domain", jid.Domain)
+	assertEquals(t, "res", jid.Resource)
+	assertEquals(t, str, jid.String())
+
+	str = "domain.tld"
+	if err := jid.Set(str); err != nil {
+		t.Errorf("Set(%s) failed: %s", str, err)
+	}
+	if jid.Node != "" {
+		t.Errorf("Node: %v\n", jid.Node)
+	}
+	assertEquals(t, "domain.tld", jid.Domain)
+	if jid.Resource != "" {
+		t.Errorf("Resource: %v\n", jid.Resource)
+	}
+	assertEquals(t, str, jid.String())
+}
+
+func assertMarshal(t *testing.T, expected string, marshal interface{}) {
+	var buf bytes.Buffer
+	enc := xml.NewEncoder(&buf)
+	err := enc.Encode(marshal)
+	if err != nil {
+		t.Errorf("Marshal error for %s: %s", marshal, err)
+	}
+	observed := buf.String()
+	if expected != observed {
+		file := "unknown"
+		line := 0
+		_, file, line, _ = runtime.Caller(1)
+		fmt.Fprintf(os.Stderr, "%s:%d: Expected:\n%s\nObserved:\n%s\n",
+			file, line, expected, observed)
+		t.Fail()
+	}
+}
+
+func TestStreamMarshal(t *testing.T) {
+	s := &stream{To: "bob"}
+	exp := `<stream:stream xmlns="` + NsClient +
+		`" xmlns:stream="` + NsStream + `" to="bob">`
+	assertEquals(t, exp, s.String())
+
+	s = &stream{To: "bob", From: "alice", Id: "#3", Version: "5.3"}
+	exp = `<stream:stream xmlns="` + NsClient +
+		`" xmlns:stream="` + NsStream + `" to="bob" from="alice"` +
+		` id="#3" version="5.3">`
+	assertEquals(t, exp, s.String())
+
+	s = &stream{Lang: "en_US"}
+	exp = `<stream:stream xmlns="` + NsClient +
+		`" xmlns:stream="` + NsStream + `" xml:lang="en_US">`
+	assertEquals(t, exp, s.String())
+}
+
+func TestStreamErrorMarshal(t *testing.T) {
+	name := xml.Name{Space: NsStreams, Local: "ack"}
+	e := &streamError{Any: Generic{XMLName: name}}
+	exp := `<error xmlns="` + NsStream + `"><ack xmlns="` + NsStreams +
+		`"></ack></error>`
+	assertMarshal(t, exp, e)
+
+	txt := errText{Lang: "pt", Text: "things happen"}
+	e = &streamError{Any: Generic{XMLName: name}, Text: &txt}
+	exp = `<error xmlns="` + NsStream + `"><ack xmlns="` + NsStreams +
+		`"></ack><text xmlns="` + NsStreams +
+		`" xml:lang="pt">things happen</text></error>`
+	assertMarshal(t, exp, e)
+}
+
+func TestIqMarshal(t *testing.T) {
+	iq := &Iq{Header: Header{Type: "set", Id: "3",
+		Nested: []interface{}{Generic{XMLName: xml.Name{Space: NsBind,
+			Local: "bind"}}}}}
+	exp := `<iq id="3" type="set"><bind xmlns="` + NsBind +
+		`"></bind></iq>`
+	assertMarshal(t, exp, iq)
+}
+
+func TestMarshalEscaping(t *testing.T) {
+	msg := &Message{Body: &Generic{XMLName: xml.Name{Local: "body"},
+		Chardata: `&<!-- "`}}
+	exp := `<message xmlns="jabber:client"><body>&amp;&lt;!-- &#34;</body></message>`
+	assertMarshal(t, exp, msg)
+}
+
+func TestUnmarshalMessage(t *testing.T) {
+	str := `<message to="a@b.c"><body>foo!</body></message>`
+	r := strings.NewReader(str)
+	ch := make(chan interface{})
+	go readXml(r, ch, make(map[string]func(*xml.Name) interface{}))
+	obs := <-ch
+	exp := &Message{XMLName: xml.Name{Local: "message", Space: "jabber:client"},
+		Header: Header{To: "a@b.c", Innerxml: "<body>foo!</body>"},
+		Body: &Generic{XMLName: xml.Name{Local: "body", Space: "jabber:client"},
+			Chardata: "foo!"}}
+	if !reflect.DeepEqual(obs, exp) {
+		t.Errorf("read %s\ngot:  %#v\nwant: %#v\n", str, obs, exp)
+	}
+	obsMsg, ok := obs.(*Message)
+	if !ok {
+		t.Fatalf("Not a Message: %T", obs)
+	}
+	obsBody := obsMsg.Body
+	expBody := exp.Body
+	if !reflect.DeepEqual(obsBody, expBody) {
+		t.Errorf("body\ngot:  %#v\nwant: %#v\n", obsBody, expBody)
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/xmpp/xmpp.go	Sat Sep 07 10:04:44 2013 -0700
@@ -0,0 +1,261 @@
+// Copyright 2011 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This package implements a simple XMPP client according to RFCs 3920
+// and 3921, plus the various XEPs at http://xmpp.org/protocols/. The
+// implementation is structured as a stack of layers, with TCP at the
+// bottom and the application at the top. The application receives and
+// sends structures representing XMPP stanzas. Additional stanza
+// parsers can be inserted into the stack of layers as extensions.
+package xmpp
+
+import (
+	"bytes"
+	"crypto/tls"
+	"encoding/xml"
+	"errors"
+	"fmt"
+	"io"
+	"net"
+	"sync"
+)
+
+const (
+	// Version of RFC 3920 that we implement.
+	XMPPVersion = "1.0"
+
+	// Various XML namespaces.
+	NsClient  = "jabber:client"
+	NsStreams = "urn:ietf:params:xml:ns:xmpp-streams"
+	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"
+	NsSession = "urn:ietf:params:xml:ns:xmpp-session"
+	NsRoster  = "jabber:iq:roster"
+
+	// DNS SRV names
+	serverSrv = "xmpp-server"
+	clientSrv = "xmpp-client"
+)
+
+// A filter can modify the XMPP traffic to or from the remote
+// server. It's part of an Extension. The filter function will be
+// called in a new goroutine, so it doesn't need to return. The filter
+// should close its output when its input is closed.
+type Filter func(in <-chan Stanza, out chan<- Stanza)
+
+// Extensions can add stanza filters and/or new XML element types.
+type Extension struct {
+	// Maps from an XML namespace to a function which constructs a
+	// structure to hold the contents of stanzas in that
+	// namespace.
+	StanzaHandlers map[string]func(*xml.Name) interface{}
+	// If non-nil, will be called once to start the filter
+	// running. RecvFilter intercepts incoming messages on their
+	// way from the remote server to the application; SendFilter
+	// intercepts messages going the other direction.
+	RecvFilter Filter
+	SendFilter Filter
+}
+
+// Allows the user to override the TLS configuration.
+var TlsConfig tls.Config
+
+// The client in a client-server XMPP connection.
+type Client struct {
+	// This client's JID. This will be updated asynchronously by
+	// the time StartSession() returns.
+	Jid          JID
+	password     string
+	socket       net.Conn
+	socketSync   sync.WaitGroup
+	saslExpected string
+	authDone     bool
+	handlers     chan *stanzaHandler
+	inputControl chan int
+	// Incoming XMPP stanzas from the remote will be published on
+	// this channel. Information which is used by this library to
+	// set up the XMPP stream will not appear here.
+	In <-chan Stanza
+	// Outgoing XMPP stanzas to the server should be sent to this
+	// channel.
+	Out    chan<- Stanza
+	xmlOut chan<- interface{}
+	// The client's roster is also known as the buddy list. It's
+	// the set of contacts which are known to this JID, or which
+	// this JID is known to.
+	Roster Roster
+	// Features advertised by the remote. This will be updated
+	// asynchronously as new features are received throughout the
+	// connection process. It should not be updated once
+	// StartSession() returns.
+	Features  *Features
+	sendFilterAdd, recvFilterAdd chan Filter
+}
+
+// Connect to the appropriate server and authenticate as the given JID
+// with the given password. This function will return as soon as a TCP
+// connection has been established, but before XMPP stream negotiation
+// has completed. The negotiation will occur asynchronously, and any
+// send operation to Client.Out will block until negotiation (resource
+// binding) is complete.
+func NewClient(jid *JID, password string, exts []Extension) (*Client, error) {
+	// Include the mandatory extensions.
+	roster := newRosterExt()
+	exts = append(exts, roster.Extension)
+	exts = append(exts, bindExt)
+
+	// Resolve the domain in the JID.
+	_, srvs, err := net.LookupSRV(clientSrv, "tcp", jid.Domain)
+	if err != nil {
+		return nil, errors.New("LookupSrv " + jid.Domain +
+			": " + err.Error())
+	}
+
+	var tcp *net.TCPConn
+	for _, srv := range srvs {
+		addrStr := fmt.Sprintf("%s:%d", srv.Target, srv.Port)
+		addr, err := net.ResolveTCPAddr("tcp", addrStr)
+		if err != nil {
+			err = fmt.Errorf("ResolveTCPAddr(%s): %s",
+				addrStr, err.Error())
+			continue
+		}
+		tcp, err = net.DialTCP("tcp", nil, addr)
+		if err == nil {
+			break
+		}
+		err = fmt.Errorf("DialTCP(%s): %s", addr, err)
+	}
+	if tcp == nil {
+		return nil, err
+	}
+
+	cl := new(Client)
+	cl.Roster = *roster
+	cl.password = password
+	cl.Jid = *jid
+	cl.socket = tcp
+	cl.handlers = make(chan *stanzaHandler, 100)
+	cl.inputControl = make(chan int)
+
+	extStanza := make(map[string]func(*xml.Name) interface{})
+	for _, ext := range exts {
+		for k, v := range ext.StanzaHandlers {
+			extStanza[k] = v
+		}
+	}
+
+	// Start the transport handler, initially unencrypted.
+	recvReader, recvWriter := io.Pipe()
+	sendReader, sendWriter := io.Pipe()
+	go cl.readTransport(recvWriter)
+	go cl.writeTransport(sendReader)
+
+	// Start the reader and writer that convert to and from XML.
+	recvXml := make(chan interface{})
+	go readXml(recvReader, recvXml, extStanza)
+	sendXml := make(chan interface{})
+	cl.xmlOut = sendXml
+	go writeXml(sendWriter, sendXml)
+
+	// Start the reader and writer that convert between XML and
+	// XMPP stanzas.
+	recvRawXmpp := make(chan Stanza)
+	go cl.readStream(recvXml, recvRawXmpp)
+	sendRawXmpp := make(chan Stanza)
+	go writeStream(sendXml, sendRawXmpp, cl.inputControl)
+
+	// Start the manager for the filters that can modify what the
+	// app sees.
+	recvFiltXmpp := make(chan Stanza)
+	cl.In = recvFiltXmpp
+	go filterMgr(cl.recvFilterAdd, recvRawXmpp, recvFiltXmpp)
+	sendFiltXmpp := make(chan Stanza)
+	cl.Out = sendFiltXmpp
+	go filterMgr(cl.sendFilterAdd, sendFiltXmpp, sendFiltXmpp)
+
+	// Initial handshake.
+	hsOut := &stream{To: jid.Domain, Version: XMPPVersion}
+	cl.xmlOut <- hsOut
+
+	return cl, nil
+}
+
+func tee(r io.Reader, w io.Writer, prefix string) {
+	defer func(w io.Writer) {
+		if c, ok := w.(io.Closer); ok {
+			c.Close()
+		}
+	}(w)
+
+	buf := bytes.NewBuffer([]uint8(prefix))
+	for {
+		var c [1]byte
+		n, _ := r.Read(c[:])
+		if n == 0 {
+			break
+		}
+		n, _ = w.Write(c[:n])
+		if n == 0 {
+			break
+		}
+		buf.Write(c[:n])
+		if c[0] == '\n' || c[0] == '>' {
+			Debug.Log(buf)
+			buf = bytes.NewBuffer([]uint8(prefix))
+		}
+	}
+	leftover := buf.String()
+	if leftover != "" {
+		Debug.Log(buf)
+	}
+}
+
+// bindDone is called when we've finished resource binding (and all
+// the negotiations that precede it). Now we can start accepting
+// traffic from the app.
+func (cl *Client) bindDone() {
+	cl.inputControl <- 1
+}
+
+// Start an XMPP session. A typical XMPP client should call this
+// immediately after creating the Client in order to start the
+// session, retrieve the roster, and broadcast an initial
+// presence. The presence can be as simple as a newly-initialized
+// Presence struct.  See RFC 3921, Section 3. After calling this, a
+// normal client will want to call Roster.Update().
+func (cl *Client) StartSession(pr *Presence) error {
+	id := NextId()
+	iq := &Iq{Header: Header{To: cl.Jid.Domain, Id: id, Type: "set",
+		Nested: []interface{}{Generic{XMLName: xml.Name{Space: NsSession, Local: "session"}}}}}
+	ch := make(chan error)
+	f := func(st Stanza) bool {
+		iq, ok := st.(*Iq)
+		if !ok {
+			Warn.Log("iq reply not iq; can't start session")
+			ch <- errors.New("bad session start reply")
+			return false
+		}
+		if iq.Type == "error" {
+			Warn.Logf("Can't start session: %v", iq)
+			ch <- iq.Error
+			return false
+		}
+		ch <- nil
+		return false
+	}
+	cl.HandleStanza(id, f)
+	cl.Out <- iq
+
+	// Now wait until the callback is called.
+	if err := <-ch; err != nil {
+		return err
+	}
+	if pr != nil {
+		cl.Out <- pr
+	}
+	return nil
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/xmpp/xmpp_test.go	Sat Sep 07 10:04:44 2013 -0700
@@ -0,0 +1,102 @@
+// Copyright 2011 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xmpp
+
+import (
+	"bytes"
+	"encoding/xml"
+	"reflect"
+	"strings"
+	"sync"
+	"testing"
+)
+
+func TestReadError(t *testing.T) {
+	r := strings.NewReader(`<stream:error><bad-foo xmlns="blah"/>` +
+		`</stream:error>`)
+	ch := make(chan interface{})
+	go readXml(r, ch, make(map[string]func(*xml.Name) interface{}))
+	x := <-ch
+	se, ok := x.(*streamError)
+	if !ok {
+		t.Fatalf("not StreamError: %T", x)
+	}
+	assertEquals(t, "bad-foo", se.Any.XMLName.Local)
+	assertEquals(t, "blah", se.Any.XMLName.Space)
+	if se.Text != nil {
+		t.Errorf("text not nil: %v", se.Text)
+	}
+
+	r = strings.NewReader(`<stream:error><bad-foo xmlns="blah"/>` +
+		`<text xml:lang="en" xmlns="` + NsStreams +
+		`">Error text</text></stream:error>`)
+	ch = make(chan interface{})
+	go readXml(r, ch, make(map[string]func(*xml.Name) interface{}))
+	x = <-ch
+	se, ok = x.(*streamError)
+	if !ok {
+		t.Fatalf("not StreamError: %v", reflect.TypeOf(x))
+	}
+	assertEquals(t, "bad-foo", se.Any.XMLName.Local)
+	assertEquals(t, "blah", se.Any.XMLName.Space)
+	assertEquals(t, "Error text", se.Text.Text)
+	assertEquals(t, "en", se.Text.Lang)
+}
+
+func TestReadStream(t *testing.T) {
+	r := strings.NewReader(`<stream:stream to="foo.com" ` +
+		`from="bar.org" id="42"` +
+		`xmlns="` + NsClient + `" xmlns:stream="` + NsStream +
+		`" version="1.0">`)
+	ch := make(chan interface{})
+	go readXml(r, ch, make(map[string]func(*xml.Name) interface{}))
+	x := <-ch
+	ss, ok := x.(*stream)
+	if !ok {
+		t.Fatalf("not stream: %v", reflect.TypeOf(x))
+	}
+	assertEquals(t, "foo.com", ss.To)
+	assertEquals(t, "bar.org", ss.From)
+	assertEquals(t, "42", ss.Id)
+	assertEquals(t, "1.0", ss.Version)
+}
+
+func testWrite(obj interface{}) string {
+	w := bytes.NewBuffer(nil)
+	ch := make(chan interface{})
+	var wg sync.WaitGroup
+	wg.Add(1)
+	go func() {
+		defer wg.Done()
+		writeXml(w, ch)
+	}()
+	ch <- obj
+	close(ch)
+	wg.Wait()
+	return w.String()
+}
+
+func TestWriteError(t *testing.T) {
+	se := &streamError{Any: Generic{XMLName: xml.Name{Local: "blah"}}}
+	str := testWrite(se)
+	exp := `<error xmlns="` + NsStream + `"><blah></blah></error>`
+	assertEquals(t, exp, str)
+
+	se = &streamError{Any: Generic{XMLName: xml.Name{Space: NsStreams, Local: "foo"}}, Text: &errText{Lang: "ru", Text: "Пошёл ты"}}
+	str = testWrite(se)
+	exp = `<error xmlns="` + NsStream + `"><foo xmlns="` + NsStreams +
+		`"></foo><text xmlns="` + NsStreams +
+		`" xml:lang="ru">Пошёл ты</text></error>`
+	assertEquals(t, exp, str)
+}
+
+func TestWriteStream(t *testing.T) {
+	ss := &stream{To: "foo.org", From: "bar.com", Id: "42", Lang: "en", Version: "1.0"}
+	str := testWrite(ss)
+	exp := `<stream:stream xmlns="` + NsClient +
+		`" xmlns:stream="` + NsStream + `" to="foo.org"` +
+		` from="bar.com" id="42" xml:lang="en" version="1.0">`
+	assertEquals(t, exp, str)
+}
--- a/xmpp_test.go	Mon Sep 02 20:46:23 2013 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,102 +0,0 @@
-// Copyright 2011 The Go Authors.  All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package xmpp
-
-import (
-	"bytes"
-	"encoding/xml"
-	"reflect"
-	"strings"
-	"sync"
-	"testing"
-)
-
-func TestReadError(t *testing.T) {
-	r := strings.NewReader(`<stream:error><bad-foo xmlns="blah"/>` +
-		`</stream:error>`)
-	ch := make(chan interface{})
-	go readXml(r, ch, make(map[string]func(*xml.Name) interface{}))
-	x := <-ch
-	se, ok := x.(*streamError)
-	if !ok {
-		t.Fatalf("not StreamError: %T", x)
-	}
-	assertEquals(t, "bad-foo", se.Any.XMLName.Local)
-	assertEquals(t, "blah", se.Any.XMLName.Space)
-	if se.Text != nil {
-		t.Errorf("text not nil: %v", se.Text)
-	}
-
-	r = strings.NewReader(`<stream:error><bad-foo xmlns="blah"/>` +
-		`<text xml:lang="en" xmlns="` + NsStreams +
-		`">Error text</text></stream:error>`)
-	ch = make(chan interface{})
-	go readXml(r, ch, make(map[string]func(*xml.Name) interface{}))
-	x = <-ch
-	se, ok = x.(*streamError)
-	if !ok {
-		t.Fatalf("not StreamError: %v", reflect.TypeOf(x))
-	}
-	assertEquals(t, "bad-foo", se.Any.XMLName.Local)
-	assertEquals(t, "blah", se.Any.XMLName.Space)
-	assertEquals(t, "Error text", se.Text.Text)
-	assertEquals(t, "en", se.Text.Lang)
-}
-
-func TestReadStream(t *testing.T) {
-	r := strings.NewReader(`<stream:stream to="foo.com" ` +
-		`from="bar.org" id="42"` +
-		`xmlns="` + NsClient + `" xmlns:stream="` + NsStream +
-		`" version="1.0">`)
-	ch := make(chan interface{})
-	go readXml(r, ch, make(map[string]func(*xml.Name) interface{}))
-	x := <-ch
-	ss, ok := x.(*stream)
-	if !ok {
-		t.Fatalf("not stream: %v", reflect.TypeOf(x))
-	}
-	assertEquals(t, "foo.com", ss.To)
-	assertEquals(t, "bar.org", ss.From)
-	assertEquals(t, "42", ss.Id)
-	assertEquals(t, "1.0", ss.Version)
-}
-
-func testWrite(obj interface{}) string {
-	w := bytes.NewBuffer(nil)
-	ch := make(chan interface{})
-	var wg sync.WaitGroup
-	wg.Add(1)
-	go func() {
-		defer wg.Done()
-		writeXml(w, ch)
-	}()
-	ch <- obj
-	close(ch)
-	wg.Wait()
-	return w.String()
-}
-
-func TestWriteError(t *testing.T) {
-	se := &streamError{Any: Generic{XMLName: xml.Name{Local: "blah"}}}
-	str := testWrite(se)
-	exp := `<error xmlns="` + NsStream + `"><blah></blah></error>`
-	assertEquals(t, exp, str)
-
-	se = &streamError{Any: Generic{XMLName: xml.Name{Space: NsStreams, Local: "foo"}}, Text: &errText{Lang: "ru", Text: "Пошёл ты"}}
-	str = testWrite(se)
-	exp = `<error xmlns="` + NsStream + `"><foo xmlns="` + NsStreams +
-		`"></foo><text xmlns="` + NsStreams +
-		`" xml:lang="ru">Пошёл ты</text></error>`
-	assertEquals(t, exp, str)
-}
-
-func TestWriteStream(t *testing.T) {
-	ss := &stream{To: "foo.org", From: "bar.com", Id: "42", Lang: "en", Version: "1.0"}
-	str := testWrite(ss)
-	exp := `<stream:stream xmlns="` + NsClient +
-		`" xmlns:stream="` + NsStream + `" to="foo.org"` +
-		` from="bar.com" id="42" xml:lang="en" version="1.0">`
-	assertEquals(t, exp, str)
-}