structs.go
changeset 98 c9cc4eda6dce
parent 88 d2ec96c80efe
child 100 24231ff0016c
--- a/structs.go	Mon Jan 23 21:54:41 2012 -0700
+++ b/structs.go	Sun Dec 16 13:03:03 2012 -0700
@@ -8,14 +8,14 @@
 
 import (
 	"bytes"
+	"encoding/xml"
+	"errors"
 	"flag"
 	"fmt"
-	"go-idn.googlecode.com/hg/src/stringprep"
-	"io"
-	"os"
+	// BUG(cjyar): We should use stringprep
+	// "code.google.com/p/go-idn/src/stringprep"
 	"regexp"
 	"strings"
-	"xml"
 )
 
 // JID represents an entity that can communicate with other
@@ -32,31 +32,28 @@
 
 // XMPP's <stream:stream> XML element
 type stream struct {
-	To      string `xml:"attr"`
-	From    string `xml:"attr"`
-	Id      string `xml:"attr"`
-	Lang    string `xml:"attr"`
-	Version string `xml:"attr"`
+	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 _ xml.Marshaler = &stream{}
 var _ fmt.Stringer = &stream{}
 
 // <stream:error>
 type streamError struct {
-	Any  Generic
+	XMLName xml.Name `xml:"http://etherx.jabber.org/streams error"`
+	Any  Generic     `xml:",any"`
 	Text *errText
 }
 
-var _ xml.Marshaler = &streamError{}
-
 type errText struct {
-	Lang string `xml:"attr"`
-	Text string `xml:"chardata"`
+	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"`
 }
 
-var _ xml.Marshaler = &errText{}
-
 type Features struct {
 	Starttls   *starttls
 	Mechanisms mechs
@@ -84,88 +81,84 @@
 // One of the three core XMPP stanza types: iq, message, presence. See
 // RFC3920, section 9.
 type Stanza interface {
-	// Returns "iq", "message", or "presence".
-	GetName() string
-	// The to attribute.
-	GetTo() string
-	// The from attribute.
-	GetFrom() string
-	// The id attribute.
+	// // Returns "iq", "message", or "presence".
+	// GetName() string
+	// // The to attribute.
+	// GetTo() string
+	// // The from attribute.
+	// GetFrom() string
+	// The id attribute. TODO maybe remove this.
 	GetId() string
-	// The type attribute.
-	GetType() string
-	// The xml:lang attribute.
-	GetLang() string
-	// A nested error element, if any.
-	GetError() *Error
-	// Zero or more (non-error) nested elements. These will be in
-	// namespaces managed by extensions.
-	GetNested() []interface{}
+	// // The type attribute.
+	// GetType() string
+	// // The xml:lang attribute.
+	// GetLang() string
+	// // A nested error element, if any.
+	// GetError() *Error
+	// // Zero or more (non-error) nested elements. These will be in
+	// // namespaces managed by extensions.
+	// GetNested() []interface{}
 	addNested(interface{})
 	innerxml() string
 }
 
 // message stanza
 type Message struct {
-	To       string `xml:"attr"`
-	From     string `xml:"attr"`
-	Id       string `xml:"attr"`
-	Type     string `xml:"attr"`
-	Lang     string `xml:"attr"`
-	Innerxml string `xml:"innerxml"`
+	XMLName  xml.Name `xml:"message"`
+	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
-	Subject  *Generic
-	Body     *Generic
-	Thread   *Generic
+	Subject  *Generic `xml:"subject"`
+	Body     *Generic `xml:"body"`
+	Thread   *Generic `xml:"thread"`
 	Nested   []interface{}
 }
-
-var _ xml.Marshaler = &Message{}
 var _ Stanza = &Message{}
 
 // presence stanza
 type Presence struct {
-	To       string `xml:"attr"`
-	From     string `xml:"attr"`
-	Id       string `xml:"attr"`
-	Type     string `xml:"attr"`
-	Lang     string `xml:"attr"`
-	Innerxml string `xml:"innerxml"`
+	XMLName  xml.Name `xml:"presence"`
+	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
-	Show     *Generic
-	Status   *Generic
-	Priority *Generic
+	Show     *Generic `xml:"show"`
+	Status   *Generic `xml:"status"`
+	Priority *Generic `xml:"priority"`
 	Nested   []interface{}
 }
-
-var _ xml.Marshaler = &Presence{}
 var _ Stanza = &Presence{}
 
 // iq stanza
 type Iq struct {
-	To       string `xml:"attr"`
-	From     string `xml:"attr"`
-	Id       string `xml:"attr"`
-	Type     string `xml:"attr"`
-	Lang     string `xml:"attr"`
-	Innerxml string `xml:"innerxml"`
+	XMLName  xml.Name `xml:"iq"`
+	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{}
 }
-
-var _ xml.Marshaler = &Iq{}
 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:"attr"`
+	Type string `xml:"type,attr"`
 	// Any nested element, if present.
 	Any *Generic
 }
-
-var _ os.Error = &Error{}
+var _ error = &Error{}
 
 // Used for resource binding as a nested element inside <iq/>.
 type bindIq struct {
@@ -177,10 +170,9 @@
 // Holds an XML element not described by the more specific types.
 type Generic struct {
 	XMLName  xml.Name
-	Any      *Generic
-	Chardata string `xml:"chardata"`
+	Any      *Generic `xml:",any"`
+	Chardata string `xml:",chardata"`
 }
-
 var _ fmt.Stringer = &Generic{}
 
 func (jid *JID) String() string {
@@ -196,39 +188,58 @@
 
 // Set implements flag.Value. It returns true if it successfully
 // parses the string.
-func (jid *JID) Set(val string) bool {
+func (jid *JID) Set(val string) error {
 	r := regexp.MustCompile("^(([^@/]+)@)?([^@/]+)(/([^@/]+))?$")
 	parts := r.FindStringSubmatch(val)
 	if parts == nil {
-		return false
+		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])
-	return true
-}
-
-func (s *stream) MarshalXML() ([]byte, os.Error) {
-	buf := bytes.NewBuffer(nil)
-	buf.WriteString("<stream:stream")
-	writeField(buf, "xmlns", "jabber:client")
-	writeField(buf, "xmlns:stream", NsStream)
-	writeField(buf, "to", s.To)
-	writeField(buf, "from", s.From)
-	writeField(buf, "id", s.Id)
-	writeField(buf, "xml:lang", s.Lang)
-	writeField(buf, "version", s.Version)
-	buf.WriteString(">")
-	// We never write </stream:stream>
-	return buf.Bytes(), nil
+	// 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 {
-	result, _ := s.MarshalXML()
-	return string(result)
+	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, os.Error) {
+func parseStream(se xml.StartElement) (*stream, error) {
 	s := &stream{}
 	for _, attr := range se.Attr {
 		switch strings.ToLower(attr.Name.Local) {
@@ -247,38 +258,6 @@
 	return s, nil
 }
 
-func (s *streamError) MarshalXML() ([]byte, os.Error) {
-	buf := bytes.NewBuffer(nil)
-	buf.WriteString("<stream:error>")
-	xml.Marshal(buf, s.Any)
-	if s.Text != nil {
-		xml.Marshal(buf, s.Text)
-	}
-	buf.WriteString("</stream:error>")
-	return buf.Bytes(), nil
-}
-
-func (e *errText) MarshalXML() ([]byte, os.Error) {
-	buf := bytes.NewBuffer(nil)
-	buf.WriteString("<text")
-	writeField(buf, "xmlns", NsStreams)
-	writeField(buf, "xml:lang", e.Lang)
-	buf.WriteString(">")
-	xml.Escape(buf, []byte(e.Text))
-	buf.WriteString("</text>")
-	return buf.Bytes(), nil
-}
-
-func writeField(w io.Writer, field, value string) {
-	if value != "" {
-		io.WriteString(w, " ")
-		io.WriteString(w, field)
-		io.WriteString(w, `="`)
-		xml.Escape(w, []byte(value))
-		io.WriteString(w, `"`)
-	}
-}
-
 func (u *Generic) String() string {
 	if u == nil {
 		return "nil"
@@ -292,107 +271,21 @@
 		u.XMLName.Local)
 }
 
-func marshalXML(st Stanza) ([]byte, os.Error) {
-	buf := bytes.NewBuffer(nil)
-	buf.WriteString("<")
-	buf.WriteString(st.GetName())
-	if st.GetTo() != "" {
-		writeField(buf, "to", st.GetTo())
-	}
-	if st.GetFrom() != "" {
-		writeField(buf, "from", st.GetFrom())
-	}
-	if st.GetId() != "" {
-		writeField(buf, "id", st.GetId())
-	}
-	if st.GetType() != "" {
-		writeField(buf, "type", st.GetType())
-	}
-	if st.GetLang() != "" {
-		writeField(buf, "xml:lang", st.GetLang())
-	}
-	buf.WriteString(">")
-
-	if m, ok := st.(*Message); ok {
-		err := xml.Marshal(buf, m.Subject)
-		if err != nil {
-			return nil, err
-		}
-		err = xml.Marshal(buf, m.Body)
-		if err != nil {
-			return nil, err
-		}
-		err = xml.Marshal(buf, m.Thread)
-		if err != nil {
-			return nil, err
-		}
-	}
-	if p, ok := st.(*Presence); ok {
-		err := xml.Marshal(buf, p.Show)
-		if err != nil {
-			return nil, err
+func (er *Error) Error() string {
+	buf, err := xml.Marshal(er)
+	if err != nil {
+		if Log != nil {
+			Log.Err("double bad error couldn't marshal error")
 		}
-		err = xml.Marshal(buf, p.Status)
-		if err != nil {
-			return nil, err
-		}
-		err = xml.Marshal(buf, p.Priority)
-		if err != nil {
-			return nil, err
-		}
-	}
-	if nested := st.GetNested(); nested != nil {
-		for _, n := range nested {
-			xml.Marshal(buf, n)
-		}
-	} else if st.innerxml() != "" {
-		buf.WriteString(st.innerxml())
+		return "unreadable error"
 	}
-
-	buf.WriteString("</")
-	buf.WriteString(st.GetName())
-	buf.WriteString(">")
-	return buf.Bytes(), nil
-}
-
-func (er *Error) String() string {
-	buf := bytes.NewBuffer(nil)
-	xml.Marshal(buf, er)
-	return buf.String()
-}
-
-func (m *Message) GetName() string {
-	return "message"
-}
-
-func (m *Message) GetTo() string {
-	return m.To
-}
-
-func (m *Message) GetFrom() string {
-	return m.From
+	return string(buf)
 }
 
 func (m *Message) GetId() string {
 	return m.Id
 }
 
-func (m *Message) GetType() string {
-	return m.Type
-}
-
-func (m *Message) GetLang() string {
-	return m.Lang
-}
-
-func (m *Message) GetError() *Error {
-	return m.Error
-}
-
-func (m *Message) GetNested() []interface{} {
-	return m.Nested
-}
-
 func (m *Message) addNested(n interface{}) {
 	m.Nested = append(m.Nested, n)
 }
@@ -401,42 +294,10 @@
 	return m.Innerxml
 }
 
-func (m *Message) MarshalXML() ([]byte, os.Error) {
-	return marshalXML(m)
-}
-
-func (p *Presence) GetName() string {
-	return "presence"
-}
-
-func (p *Presence) GetTo() string {
-	return p.To
-}
-
-func (p *Presence) GetFrom() string {
-	return p.From
-}
-
 func (p *Presence) GetId() string {
 	return p.Id
 }
 
-func (p *Presence) GetType() string {
-	return p.Type
-}
-
-func (p *Presence) GetLang() string {
-	return p.Lang
-}
-
-func (p *Presence) GetError() *Error {
-	return p.Error
-}
-
-func (p *Presence) GetNested() []interface{} {
-	return p.Nested
-}
-
 func (p *Presence) addNested(n interface{}) {
 	p.Nested = append(p.Nested, n)
 }
@@ -445,42 +306,10 @@
 	return p.Innerxml
 }
 
-func (p *Presence) MarshalXML() ([]byte, os.Error) {
-	return marshalXML(p)
-}
-
-func (iq *Iq) GetName() string {
-	return "iq"
-}
-
-func (iq *Iq) GetTo() string {
-	return iq.To
-}
-
-func (iq *Iq) GetFrom() string {
-	return iq.From
-}
-
 func (iq *Iq) GetId() string {
 	return iq.Id
 }
 
-func (iq *Iq) GetType() string {
-	return iq.Type
-}
-
-func (iq *Iq) GetLang() string {
-	return iq.Lang
-}
-
-func (iq *Iq) GetError() *Error {
-	return iq.Error
-}
-
-func (iq *Iq) GetNested() []interface{} {
-	return iq.Nested
-}
-
 func (iq *Iq) addNested(n interface{}) {
 	iq.Nested = append(iq.Nested, n)
 }
@@ -489,22 +318,19 @@
 	return iq.Innerxml
 }
 
-func (iq *Iq) MarshalXML() ([]byte, os.Error) {
-	return marshalXML(iq)
-}
 
 // Parse a string into a struct implementing Stanza -- this will be
 // either an Iq, a Message, or a Presence.
-func ParseStanza(str string) (Stanza, os.Error) {
+func ParseStanza(str string) (Stanza, error) {
 	r := strings.NewReader(str)
-	p := xml.NewParser(r)
+	p := xml.NewDecoder(r)
 	tok, err := p.Token()
 	if err != nil {
 		return nil, err
 	}
 	se, ok := tok.(xml.StartElement)
 	if !ok {
-		return nil, os.NewError("Not a start element")
+		return nil, errors.New("Not a start element")
 	}
 	var stan Stanza
 	switch se.Name.Local {
@@ -515,9 +341,9 @@
 	case "presence":
 		stan = &Presence{}
 	default:
-		return nil, os.NewError("Not iq, message, or presence")
+		return nil, errors.New("Not iq, message, or presence")
 	}
-	err = p.Unmarshal(stan, &se)
+	err = p.DecodeElement(stan, &se)
 	if err != nil {
 		return nil, err
 	}