xmpp.go
changeset 6 8e425e340ca1
parent 5 faef59c8db05
child 8 30a7752cf8f7
--- a/xmpp.go	Sat Dec 24 13:11:36 2011 -0700
+++ b/xmpp.go	Sun Dec 25 18:46:13 2011 -0700
@@ -7,6 +7,7 @@
 package xmpp
 
 import (
+	"bytes"
 	"fmt"
 	"io"
 	"log"
@@ -18,6 +19,7 @@
 const (
 	serverSrv = "xmpp-server"
 	clientSrv = "xmpp-client"
+	debug = true
 )
 
 // The client in a client-server XMPP connection.
@@ -60,23 +62,39 @@
 		return nil, err
 	}
 
-	cl := Client{}
+	cl := new(Client)
 	cl.tcp = c
 	cl.in = make(chan interface{})
 	cl.In = cl.in
+	cl.out = make(chan interface{})
+	cl.Out = cl.out
 	// TODO Send readXml a reader that we can close when we
 	// negotiate TLS.
-	go readXml(cl.tcp, cl.in)
-	// TODO go writeXml(&cl)
+	go readXml(cl.tcp, cl.in, debug)
+	go writeXml(cl.tcp, cl.out, debug)
 
-	return &cl, nil
+	return cl, nil
 }
 
 func (c *Client) Close() os.Error {
+	close(c.in)
+	close(c.out)
 	return c.tcp.Close()
 }
 
-func readXml(r io.Reader, ch chan<- interface{}) {
+// TODO Delete; for use only by interact.go:
+func ReadXml(r io.ReadCloser, ch chan<- interface{}, dbg bool) {
+	readXml(r, ch, dbg)
+}
+
+func readXml(r io.Reader, ch chan<- interface{}, dbg bool) {
+	defer close(ch)
+	if dbg {
+		pr, pw := io.Pipe()
+		go tee(r, pw, "S: ")
+		r = pr
+	}
+
 	p := xml.NewParser(r)
 	for {
 		// Sniff the next token on the stream.
@@ -95,8 +113,8 @@
 
 		// Allocate the appropriate structure for this token.
 		var obj interface{}
-		switch se.Name.Space + se.Name.Local {
-		case "stream stream":
+		switch se.Name.Space + " " + se.Name.Local {
+		case nsStream + " stream":
 			st, err := parseStream(se)
 			if err != nil {
 				log.Printf("unmarshal stream: %v",
@@ -105,7 +123,7 @@
 			}
 			ch <- st
 			continue
-		case nsStreams + " stream:error":
+		case "stream error":
 			obj = &StreamError{}
 		default:
 			obj = &Unrecognized{}
@@ -122,3 +140,51 @@
 		ch <- obj
 	}
 }
+
+func writeXml(w io.Writer, ch <-chan interface{}, dbg bool) {
+	if dbg {
+		pr, pw := io.Pipe()
+		go tee(pr, w, "C: ")
+		w = pw
+	}
+
+	for obj := range ch {
+		err := xml.Marshal(w, obj)
+		if err != nil {
+			log.Printf("write: %v", err)
+			break
+		}
+	}
+}
+
+func tee(r io.Reader, w io.Writer, prefix string) {
+	defer func(xs ...interface{}) {
+		for _, x := range xs {
+			if c, ok := x.(io.Closer) ; ok {
+				c.Close()
+			}
+		}
+	}(r, w)
+
+	buf := bytes.NewBuffer(nil)
+	for {
+		var c [1]byte
+		n, _ := r.Read(c[:])
+		if n == 0 {
+			break
+		}
+		n, _ = w.Write(c[:])
+		if n == 0 {
+			break
+		}
+		buf.Write(c[:])
+		if c[0] == '\n' {
+			fmt.Printf("%s%s", prefix, buf.String())
+			buf.Reset()
+		}
+	}
+	leftover := buf.String()
+	if leftover != "" {
+		fmt.Printf("%s%s\n", prefix, leftover)
+	}
+}