xmpp/structs.go
changeset 183 b4bd77d58a3e
parent 180 3010996c1928
--- a/xmpp/structs.go	Sun Feb 09 09:50:38 2014 -0700
+++ b/xmpp/structs.go	Sun Feb 09 09:52:28 2014 -0700
@@ -5,26 +5,19 @@
 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"
+	"log"
 	"reflect"
-	"regexp"
 	"strings"
 )
 
+// BUG(cjyar): Doesn't use stringprep. Could try the implementation at
+// "code.google.com/p/go-idn/src/stringprep"
+
 // 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{}
+type JID string
 
 // XMPP's <stream:stream> XML element
 type stream struct {
@@ -82,8 +75,8 @@
 // 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"`
+	To       JID    `xml:"to,attr,omitempty"`
+	From     JID    `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"`
@@ -96,9 +89,9 @@
 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"`
+	Subject []Text `xml:"jabber:client subject"`
+	Body    []Text `xml:"jabber:client body"`
+	Thread  *Data  `xml:"jabber:client thread"`
 }
 
 var _ Stanza = &Message{}
@@ -107,9 +100,9 @@
 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"`
+	Show     *Data  `xml:"jabber:client show"`
+	Status   []Text `xml:"jabber:client status"`
+	Priority *Data  `xml:"jabber:client priority"`
 }
 
 var _ Stanza = &Presence{}
@@ -137,7 +130,23 @@
 type bindIq struct {
 	XMLName  xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-bind bind"`
 	Resource *string  `xml:"resource"`
-	Jid      *string  `xml:"jid"`
+	Jid      *JID     `xml:"jid"`
+}
+
+// Holds human-readable text, with an optional language
+// specification. Generally multiple instances of these can be found
+// together, allowing the software to choose which language to present
+// to the user.
+type Text struct {
+	XMLName  xml.Name
+	Lang     string `xml:"http://www.w3.org/XML/1998/namespace lang,attr,omitempty"`
+	Chardata string `xml:",chardata"`
+}
+
+// Non-human-readable content of some sort, used by the protocol.
+type Data struct {
+	XMLName  xml.Name
+	Chardata string `xml:",chardata"`
 }
 
 // Holds an XML element not described by the more specific types.
@@ -149,32 +158,38 @@
 
 var _ fmt.Stringer = &Generic{}
 
-func (jid *JID) String() string {
-	result := jid.Domain
-	if jid.Node != "" {
-		result = jid.Node + "@" + result
+func (j JID) Node() string {
+	at := strings.Index(string(j), "@")
+	if at == -1 {
+		return ""
 	}
-	if jid.Resource != "" {
-		result = result + "/" + jid.Resource
-	}
-	return result
+	return string(j[:at])
 }
 
-// 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)
+func (j JID) Domain() string {
+	at := strings.Index(string(j), "@")
+	slash := strings.LastIndex(string(j), "/")
+	if slash == -1 {
+		slash = len(j)
 	}
-	// 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
+	return string(j[at+1 : slash])
+}
+
+func (j JID) Resource() string {
+	slash := strings.LastIndex(string(j), "/")
+	if slash == -1 {
+		return ""
+	}
+	return string(j[slash+1:])
+}
+
+// Returns the bare JID, which is the JID without the resource part.
+func (j JID) Bare() JID {
+	node := j.Node()
+	if node == "" {
+		return JID(j.Domain())
+	}
+	return JID(fmt.Sprintf("%s@%s", node, j.Domain()))
 }
 
 func (s *stream) String() string {
@@ -260,7 +275,7 @@
 func (er *Error) Error() string {
 	buf, err := xml.Marshal(er)
 	if err != nil {
-		Warn.Log("double bad error: couldn't marshal error")
+		log.Println("double bad error: couldn't marshal error")
 		return "unreadable error"
 	}
 	return string(buf)
@@ -269,7 +284,7 @@
 var bindExt Extension = Extension{}
 
 func init() {
-	bindExt.StanzaHandlers = make(map[xml.Name]reflect.Type)
+	bindExt.StanzaTypes = make(map[xml.Name]reflect.Type)
 	bName := xml.Name{Space: NsBind, Local: "bind"}
-	bindExt.StanzaHandlers[bName] = reflect.TypeOf(bindIq{})
+	bindExt.StanzaTypes[bName] = reflect.TypeOf(bindIq{})
 }