diff -r 626c390682fc -r b4bd77d58a3e xmpp/structs.go --- 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 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{}) }