xmpp/structs.go
changeset 126 367e76b3028e
parent 121 ebb86cbdd218
child 128 8342afcffc92
equal deleted inserted replaced
125:f464f14e39a7 126:367e76b3028e
       
     1 // Copyright 2011 The Go Authors.  All rights reserved.
       
     2 // Use of this source code is governed by a BSD-style
       
     3 // license that can be found in the LICENSE file.
       
     4 
       
     5 package xmpp
       
     6 
       
     7 // This file contains data structures.
       
     8 
       
     9 import (
       
    10 	"bytes"
       
    11 	"encoding/xml"
       
    12 	"flag"
       
    13 	"fmt"
       
    14 	// BUG(cjyar): Doesn't use stringprep. Could try the implementation at
       
    15 	// "code.google.com/p/go-idn/src/stringprep"
       
    16 	"regexp"
       
    17 	"strings"
       
    18 )
       
    19 
       
    20 // JID represents an entity that can communicate with other
       
    21 // entities. It looks like node@domain/resource. Node and resource are
       
    22 // sometimes optional.
       
    23 type JID struct {
       
    24 	Node     string
       
    25 	Domain   string
       
    26 	Resource string
       
    27 }
       
    28 
       
    29 var _ fmt.Stringer = &JID{}
       
    30 var _ flag.Value = &JID{}
       
    31 
       
    32 // XMPP's <stream:stream> XML element
       
    33 type stream struct {
       
    34 	XMLName xml.Name `xml:"stream=http://etherx.jabber.org/streams stream"`
       
    35 	To      string   `xml:"to,attr"`
       
    36 	From    string   `xml:"from,attr"`
       
    37 	Id      string   `xml:"id,attr"`
       
    38 	Lang    string   `xml:"http://www.w3.org/XML/1998/namespace lang,attr"`
       
    39 	Version string   `xml:"version,attr"`
       
    40 }
       
    41 
       
    42 var _ fmt.Stringer = &stream{}
       
    43 
       
    44 // <stream:error>
       
    45 type streamError struct {
       
    46 	XMLName xml.Name `xml:"http://etherx.jabber.org/streams error"`
       
    47 	Any     Generic  `xml:",any"`
       
    48 	Text    *errText
       
    49 }
       
    50 
       
    51 type errText struct {
       
    52 	XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-streams text"`
       
    53 	Lang    string   `xml:"http://www.w3.org/XML/1998/namespace lang,attr"`
       
    54 	Text    string   `xml:",chardata"`
       
    55 }
       
    56 
       
    57 type Features struct {
       
    58 	Starttls   *starttls `xml:"urn:ietf:params:xml:ns:xmpp-tls starttls"`
       
    59 	Mechanisms mechs     `xml:"urn:ietf:params:xml:ns:xmpp-sasl mechanisms"`
       
    60 	Bind       *bindIq
       
    61 	Session    *Generic
       
    62 	Any        *Generic
       
    63 }
       
    64 
       
    65 type starttls struct {
       
    66 	XMLName  xml.Name
       
    67 	Required *string
       
    68 }
       
    69 
       
    70 type mechs struct {
       
    71 	Mechanism []string `xml:"urn:ietf:params:xml:ns:xmpp-sasl mechanism"`
       
    72 }
       
    73 
       
    74 type auth struct {
       
    75 	XMLName   xml.Name
       
    76 	Chardata  string `xml:",chardata"`
       
    77 	Mechanism string `xml:"mechanism,attr,omitempty"`
       
    78 	Any       *Generic
       
    79 }
       
    80 
       
    81 type Stanza interface {
       
    82 	GetHeader() *Header
       
    83 }
       
    84 
       
    85 // One of the three core XMPP stanza types: iq, message, presence. See
       
    86 // RFC3920, section 9.
       
    87 type Header struct {
       
    88 	To       string `xml:"to,attr,omitempty"`
       
    89 	From     string `xml:"from,attr,omitempty"`
       
    90 	Id       string `xml:"id,attr,omitempty"`
       
    91 	Type     string `xml:"type,attr,omitempty"`
       
    92 	Lang     string `xml:"http://www.w3.org/XML/1998/namespace lang,attr,omitempty"`
       
    93 	Innerxml string `xml:",innerxml"`
       
    94 	Error    *Error
       
    95 	Nested   []interface{}
       
    96 }
       
    97 
       
    98 // message stanza
       
    99 type Message struct {
       
   100 	XMLName xml.Name `xml:"jabber:client message"`
       
   101 	Header
       
   102 	Subject *Generic `xml:"jabber:client subject"`
       
   103 	Body    *Generic `xml:"jabber:client body"`
       
   104 	Thread  *Generic `xml:"jabber:client thread"`
       
   105 }
       
   106 
       
   107 var _ Stanza = &Message{}
       
   108 
       
   109 // presence stanza
       
   110 type Presence struct {
       
   111 	XMLName xml.Name `xml:"presence"`
       
   112 	Header
       
   113 	Show     *Generic `xml:"jabber:client show"`
       
   114 	Status   *Generic `xml:"jabber:client status"`
       
   115 	Priority *Generic `xml:"jabber:client priority"`
       
   116 }
       
   117 
       
   118 var _ Stanza = &Presence{}
       
   119 
       
   120 // iq stanza
       
   121 type Iq struct {
       
   122 	XMLName xml.Name `xml:"iq"`
       
   123 	Header
       
   124 }
       
   125 
       
   126 var _ Stanza = &Iq{}
       
   127 
       
   128 // Describes an XMPP stanza error. See RFC 3920, Section 9.3.
       
   129 type Error struct {
       
   130 	XMLName xml.Name `xml:"error"`
       
   131 	// The error type attribute.
       
   132 	Type string `xml:"type,attr"`
       
   133 	// Any nested element, if present.
       
   134 	Any *Generic
       
   135 }
       
   136 
       
   137 var _ error = &Error{}
       
   138 
       
   139 // Used for resource binding as a nested element inside <iq/>.
       
   140 type bindIq struct {
       
   141 	XMLName  xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-bind bind"`
       
   142 	Resource *string  `xml:"resource"`
       
   143 	Jid      *string  `xml:"jid"`
       
   144 }
       
   145 
       
   146 // Holds an XML element not described by the more specific types.
       
   147 type Generic struct {
       
   148 	XMLName  xml.Name
       
   149 	Any      *Generic `xml:",any"`
       
   150 	Chardata string   `xml:",chardata"`
       
   151 }
       
   152 
       
   153 var _ fmt.Stringer = &Generic{}
       
   154 
       
   155 func (jid *JID) String() string {
       
   156 	result := jid.Domain
       
   157 	if jid.Node != "" {
       
   158 		result = jid.Node + "@" + result
       
   159 	}
       
   160 	if jid.Resource != "" {
       
   161 		result = result + "/" + jid.Resource
       
   162 	}
       
   163 	return result
       
   164 }
       
   165 
       
   166 // Set implements flag.Value. It returns true if it successfully
       
   167 // parses the string.
       
   168 func (jid *JID) Set(val string) error {
       
   169 	r := regexp.MustCompile("^(([^@/]+)@)?([^@/]+)(/([^@/]+))?$")
       
   170 	parts := r.FindStringSubmatch(val)
       
   171 	if parts == nil {
       
   172 		return fmt.Errorf("%s doesn't match user@domain/resource", val)
       
   173 	}
       
   174 	// jid.Node = stringprep.Nodeprep(parts[2])
       
   175 	// jid.Domain = stringprep.Nodeprep(parts[3])
       
   176 	// jid.Resource = stringprep.Resourceprep(parts[5])
       
   177 	jid.Node = parts[2]
       
   178 	jid.Domain = parts[3]
       
   179 	jid.Resource = parts[5]
       
   180 	return nil
       
   181 }
       
   182 
       
   183 func (s *stream) String() string {
       
   184 	var buf bytes.Buffer
       
   185 	buf.WriteString(`<stream:stream xmlns="`)
       
   186 	buf.WriteString(NsClient)
       
   187 	buf.WriteString(`" xmlns:stream="`)
       
   188 	buf.WriteString(NsStream)
       
   189 	buf.WriteString(`"`)
       
   190 	if s.To != "" {
       
   191 		buf.WriteString(` to="`)
       
   192 		xml.Escape(&buf, []byte(s.To))
       
   193 		buf.WriteString(`"`)
       
   194 	}
       
   195 	if s.From != "" {
       
   196 		buf.WriteString(` from="`)
       
   197 		xml.Escape(&buf, []byte(s.From))
       
   198 		buf.WriteString(`"`)
       
   199 	}
       
   200 	if s.Id != "" {
       
   201 		buf.WriteString(` id="`)
       
   202 		xml.Escape(&buf, []byte(s.Id))
       
   203 		buf.WriteString(`"`)
       
   204 	}
       
   205 	if s.Lang != "" {
       
   206 		buf.WriteString(` xml:lang="`)
       
   207 		xml.Escape(&buf, []byte(s.Lang))
       
   208 		buf.WriteString(`"`)
       
   209 	}
       
   210 	if s.Version != "" {
       
   211 		buf.WriteString(` version="`)
       
   212 		xml.Escape(&buf, []byte(s.Version))
       
   213 		buf.WriteString(`"`)
       
   214 	}
       
   215 	buf.WriteString(">")
       
   216 	return buf.String()
       
   217 }
       
   218 
       
   219 func parseStream(se xml.StartElement) (*stream, error) {
       
   220 	s := &stream{}
       
   221 	for _, attr := range se.Attr {
       
   222 		switch strings.ToLower(attr.Name.Local) {
       
   223 		case "to":
       
   224 			s.To = attr.Value
       
   225 		case "from":
       
   226 			s.From = attr.Value
       
   227 		case "id":
       
   228 			s.Id = attr.Value
       
   229 		case "lang":
       
   230 			s.Lang = attr.Value
       
   231 		case "version":
       
   232 			s.Version = attr.Value
       
   233 		}
       
   234 	}
       
   235 	return s, nil
       
   236 }
       
   237 
       
   238 func (iq *Iq) GetHeader() *Header {
       
   239 	return &iq.Header
       
   240 }
       
   241 
       
   242 func (m *Message) GetHeader() *Header {
       
   243 	return &m.Header
       
   244 }
       
   245 
       
   246 func (p *Presence) GetHeader() *Header {
       
   247 	return &p.Header
       
   248 }
       
   249 
       
   250 func (u *Generic) String() string {
       
   251 	if u == nil {
       
   252 		return "nil"
       
   253 	}
       
   254 	var sub string
       
   255 	if u.Any != nil {
       
   256 		sub = u.Any.String()
       
   257 	}
       
   258 	return fmt.Sprintf("<%s %s>%s%s</%s %s>", u.XMLName.Space,
       
   259 		u.XMLName.Local, sub, u.Chardata, u.XMLName.Space,
       
   260 		u.XMLName.Local)
       
   261 }
       
   262 
       
   263 func (er *Error) Error() string {
       
   264 	buf, err := xml.Marshal(er)
       
   265 	if err != nil {
       
   266 		Warn.Log("double bad error: couldn't marshal error")
       
   267 		return "unreadable error"
       
   268 	}
       
   269 	return string(buf)
       
   270 }
       
   271 
       
   272 var bindExt Extension = Extension{StanzaHandlers: map[string]func(*xml.Name) interface{}{NsBind: newBind}}
       
   273 
       
   274 func newBind(name *xml.Name) interface{} {
       
   275 	return &bindIq{}
       
   276 }