xmpp/structs.go
changeset 183 b4bd77d58a3e
parent 180 3010996c1928
equal deleted inserted replaced
182:626c390682fc 183:b4bd77d58a3e
     3 // This file contains data structures.
     3 // This file contains data structures.
     4 
     4 
     5 import (
     5 import (
     6 	"bytes"
     6 	"bytes"
     7 	"encoding/xml"
     7 	"encoding/xml"
     8 	"flag"
       
     9 	"fmt"
     8 	"fmt"
    10 	// BUG(cjyar): Doesn't use stringprep. Could try the implementation at
     9 	"log"
    11 	// "code.google.com/p/go-idn/src/stringprep"
       
    12 	"reflect"
    10 	"reflect"
    13 	"regexp"
       
    14 	"strings"
    11 	"strings"
    15 )
    12 )
       
    13 
       
    14 // BUG(cjyar): Doesn't use stringprep. Could try the implementation at
       
    15 // "code.google.com/p/go-idn/src/stringprep"
    16 
    16 
    17 // JID represents an entity that can communicate with other
    17 // JID represents an entity that can communicate with other
    18 // entities. It looks like node@domain/resource. Node and resource are
    18 // entities. It looks like node@domain/resource. Node and resource are
    19 // sometimes optional.
    19 // sometimes optional.
    20 type JID struct {
    20 type JID string
    21 	Node     string
       
    22 	Domain   string
       
    23 	Resource string
       
    24 }
       
    25 
       
    26 var _ fmt.Stringer = &JID{}
       
    27 var _ flag.Value = &JID{}
       
    28 
    21 
    29 // XMPP's <stream:stream> XML element
    22 // XMPP's <stream:stream> XML element
    30 type stream struct {
    23 type stream struct {
    31 	XMLName xml.Name `xml:"stream=http://etherx.jabber.org/streams stream"`
    24 	XMLName xml.Name `xml:"stream=http://etherx.jabber.org/streams stream"`
    32 	To      string   `xml:"to,attr"`
    25 	To      string   `xml:"to,attr"`
    80 }
    73 }
    81 
    74 
    82 // One of the three core XMPP stanza types: iq, message, presence. See
    75 // One of the three core XMPP stanza types: iq, message, presence. See
    83 // RFC3920, section 9.
    76 // RFC3920, section 9.
    84 type Header struct {
    77 type Header struct {
    85 	To       string `xml:"to,attr,omitempty"`
    78 	To       JID    `xml:"to,attr,omitempty"`
    86 	From     string `xml:"from,attr,omitempty"`
    79 	From     JID    `xml:"from,attr,omitempty"`
    87 	Id       string `xml:"id,attr,omitempty"`
    80 	Id       string `xml:"id,attr,omitempty"`
    88 	Type     string `xml:"type,attr,omitempty"`
    81 	Type     string `xml:"type,attr,omitempty"`
    89 	Lang     string `xml:"http://www.w3.org/XML/1998/namespace lang,attr,omitempty"`
    82 	Lang     string `xml:"http://www.w3.org/XML/1998/namespace lang,attr,omitempty"`
    90 	Innerxml string `xml:",innerxml"`
    83 	Innerxml string `xml:",innerxml"`
    91 	Error    *Error
    84 	Error    *Error
    94 
    87 
    95 // message stanza
    88 // message stanza
    96 type Message struct {
    89 type Message struct {
    97 	XMLName xml.Name `xml:"jabber:client message"`
    90 	XMLName xml.Name `xml:"jabber:client message"`
    98 	Header
    91 	Header
    99 	Subject *Generic `xml:"jabber:client subject"`
    92 	Subject []Text `xml:"jabber:client subject"`
   100 	Body    *Generic `xml:"jabber:client body"`
    93 	Body    []Text `xml:"jabber:client body"`
   101 	Thread  *Generic `xml:"jabber:client thread"`
    94 	Thread  *Data  `xml:"jabber:client thread"`
   102 }
    95 }
   103 
    96 
   104 var _ Stanza = &Message{}
    97 var _ Stanza = &Message{}
   105 
    98 
   106 // presence stanza
    99 // presence stanza
   107 type Presence struct {
   100 type Presence struct {
   108 	XMLName xml.Name `xml:"presence"`
   101 	XMLName xml.Name `xml:"presence"`
   109 	Header
   102 	Header
   110 	Show     *Generic `xml:"jabber:client show"`
   103 	Show     *Data  `xml:"jabber:client show"`
   111 	Status   *Generic `xml:"jabber:client status"`
   104 	Status   []Text `xml:"jabber:client status"`
   112 	Priority *Generic `xml:"jabber:client priority"`
   105 	Priority *Data  `xml:"jabber:client priority"`
   113 }
   106 }
   114 
   107 
   115 var _ Stanza = &Presence{}
   108 var _ Stanza = &Presence{}
   116 
   109 
   117 // iq stanza
   110 // iq stanza
   135 
   128 
   136 // Used for resource binding as a nested element inside <iq/>.
   129 // Used for resource binding as a nested element inside <iq/>.
   137 type bindIq struct {
   130 type bindIq struct {
   138 	XMLName  xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-bind bind"`
   131 	XMLName  xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-bind bind"`
   139 	Resource *string  `xml:"resource"`
   132 	Resource *string  `xml:"resource"`
   140 	Jid      *string  `xml:"jid"`
   133 	Jid      *JID     `xml:"jid"`
       
   134 }
       
   135 
       
   136 // Holds human-readable text, with an optional language
       
   137 // specification. Generally multiple instances of these can be found
       
   138 // together, allowing the software to choose which language to present
       
   139 // to the user.
       
   140 type Text struct {
       
   141 	XMLName  xml.Name
       
   142 	Lang     string `xml:"http://www.w3.org/XML/1998/namespace lang,attr,omitempty"`
       
   143 	Chardata string `xml:",chardata"`
       
   144 }
       
   145 
       
   146 // Non-human-readable content of some sort, used by the protocol.
       
   147 type Data struct {
       
   148 	XMLName  xml.Name
       
   149 	Chardata string `xml:",chardata"`
   141 }
   150 }
   142 
   151 
   143 // Holds an XML element not described by the more specific types.
   152 // Holds an XML element not described by the more specific types.
   144 type Generic struct {
   153 type Generic struct {
   145 	XMLName  xml.Name
   154 	XMLName  xml.Name
   147 	Chardata string   `xml:",chardata"`
   156 	Chardata string   `xml:",chardata"`
   148 }
   157 }
   149 
   158 
   150 var _ fmt.Stringer = &Generic{}
   159 var _ fmt.Stringer = &Generic{}
   151 
   160 
   152 func (jid *JID) String() string {
   161 func (j JID) Node() string {
   153 	result := jid.Domain
   162 	at := strings.Index(string(j), "@")
   154 	if jid.Node != "" {
   163 	if at == -1 {
   155 		result = jid.Node + "@" + result
   164 		return ""
   156 	}
   165 	}
   157 	if jid.Resource != "" {
   166 	return string(j[:at])
   158 		result = result + "/" + jid.Resource
   167 }
   159 	}
   168 
   160 	return result
   169 func (j JID) Domain() string {
   161 }
   170 	at := strings.Index(string(j), "@")
   162 
   171 	slash := strings.LastIndex(string(j), "/")
   163 // Set implements flag.Value. It returns true if it successfully
   172 	if slash == -1 {
   164 // parses the string.
   173 		slash = len(j)
   165 func (jid *JID) Set(val string) error {
   174 	}
   166 	r := regexp.MustCompile("^(([^@/]+)@)?([^@/]+)(/([^@/]+))?$")
   175 	return string(j[at+1 : slash])
   167 	parts := r.FindStringSubmatch(val)
   176 }
   168 	if parts == nil {
   177 
   169 		return fmt.Errorf("%s doesn't match user@domain/resource", val)
   178 func (j JID) Resource() string {
   170 	}
   179 	slash := strings.LastIndex(string(j), "/")
   171 	// jid.Node = stringprep.Nodeprep(parts[2])
   180 	if slash == -1 {
   172 	// jid.Domain = stringprep.Nodeprep(parts[3])
   181 		return ""
   173 	// jid.Resource = stringprep.Resourceprep(parts[5])
   182 	}
   174 	jid.Node = parts[2]
   183 	return string(j[slash+1:])
   175 	jid.Domain = parts[3]
   184 }
   176 	jid.Resource = parts[5]
   185 
   177 	return nil
   186 // Returns the bare JID, which is the JID without the resource part.
       
   187 func (j JID) Bare() JID {
       
   188 	node := j.Node()
       
   189 	if node == "" {
       
   190 		return JID(j.Domain())
       
   191 	}
       
   192 	return JID(fmt.Sprintf("%s@%s", node, j.Domain()))
   178 }
   193 }
   179 
   194 
   180 func (s *stream) String() string {
   195 func (s *stream) String() string {
   181 	var buf bytes.Buffer
   196 	var buf bytes.Buffer
   182 	buf.WriteString(`<stream:stream xmlns="`)
   197 	buf.WriteString(`<stream:stream xmlns="`)
   258 }
   273 }
   259 
   274 
   260 func (er *Error) Error() string {
   275 func (er *Error) Error() string {
   261 	buf, err := xml.Marshal(er)
   276 	buf, err := xml.Marshal(er)
   262 	if err != nil {
   277 	if err != nil {
   263 		Warn.Log("double bad error: couldn't marshal error")
   278 		log.Println("double bad error: couldn't marshal error")
   264 		return "unreadable error"
   279 		return "unreadable error"
   265 	}
   280 	}
   266 	return string(buf)
   281 	return string(buf)
   267 }
   282 }
   268 
   283 
   269 var bindExt Extension = Extension{}
   284 var bindExt Extension = Extension{}
   270 
   285 
   271 func init() {
   286 func init() {
   272 	bindExt.StanzaHandlers = make(map[xml.Name]reflect.Type)
   287 	bindExt.StanzaTypes = make(map[xml.Name]reflect.Type)
   273 	bName := xml.Name{Space: NsBind, Local: "bind"}
   288 	bName := xml.Name{Space: NsBind, Local: "bind"}
   274 	bindExt.StanzaHandlers[bName] = reflect.TypeOf(bindIq{})
   289 	bindExt.StanzaTypes[bName] = reflect.TypeOf(bindIq{})
   275 }
   290 }