--- 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
}