|
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 // This package implements a simple XMPP client according to RFCs 3920 |
|
6 // and 3921, plus the various XEPs at http://xmpp.org/protocols/. |
|
7 package xmpp |
|
8 |
|
9 import ( |
|
10 "bytes" |
|
11 "fmt" |
|
12 "io" |
|
13 "os" |
|
14 "xml" |
|
15 ) |
|
16 |
|
17 const ( |
|
18 // Version of RFC 3920 that we implement. |
|
19 Version = "1.0" |
|
20 nsStreams = "urn:ietf:params:xml:ns:xmpp-streams" |
|
21 ) |
|
22 |
|
23 // JID represents an entity that can communicate with other |
|
24 // entities. It looks like node@domain/resource. Node and resource are |
|
25 // sometimes optional. |
|
26 type JID struct { |
|
27 Node *string |
|
28 Domain string |
|
29 Resource *string |
|
30 } |
|
31 var _ fmt.Stringer = &JID{} |
|
32 |
|
33 // XMPP's <stream:stream> XML element |
|
34 type Stream struct { |
|
35 to string `xml:"attr"` |
|
36 from string `xml:"attr"` |
|
37 id string `xml:"attr"` |
|
38 lang string `xml:"attr"` |
|
39 version string `xml:"attr"` |
|
40 } |
|
41 var _ xml.Marshaler = &Stream{} |
|
42 |
|
43 type StreamError struct { |
|
44 cond definedCondition |
|
45 text errText |
|
46 } |
|
47 var _ xml.Marshaler = &StreamError{} |
|
48 |
|
49 type definedCondition struct { |
|
50 // Must always be in namespace nsStreams |
|
51 XMLName xml.Name |
|
52 } |
|
53 |
|
54 type errText struct { |
|
55 XMLName xml.Name |
|
56 Lang string |
|
57 text string `xml:"chardata"` |
|
58 } |
|
59 var _ xml.Marshaler = &errText{} |
|
60 |
|
61 func (jid *JID) String() string { |
|
62 result := jid.Domain |
|
63 if jid.Node != nil { |
|
64 result = *jid.Node + "@" + result |
|
65 } |
|
66 if jid.Resource != nil { |
|
67 result = result + "/" + *jid.Resource |
|
68 } |
|
69 return result |
|
70 } |
|
71 |
|
72 func (s *Stream) MarshalXML() ([]byte, os.Error) { |
|
73 buf := bytes.NewBuffer(nil) |
|
74 buf.WriteString("<stream:stream") |
|
75 writeField(buf, "to", s.to) |
|
76 writeField(buf, "from", s.from) |
|
77 writeField(buf, "id", s.id) |
|
78 writeField(buf, "xml:lang", s.lang) |
|
79 writeField(buf, "version", s.version) |
|
80 buf.WriteString(">") |
|
81 // We never write </stream:stream> |
|
82 return buf.Bytes(), nil |
|
83 } |
|
84 |
|
85 func (s *StreamError) MarshalXML() ([]byte, os.Error) { |
|
86 buf := bytes.NewBuffer(nil) |
|
87 buf.WriteString("<stream:error>") |
|
88 xml.Marshal(buf, s.cond) |
|
89 xml.Marshal(buf, s.text) |
|
90 buf.WriteString("</stream:error>") |
|
91 return buf.Bytes(), nil |
|
92 } |
|
93 |
|
94 func (e *errText) MarshalXML() ([]byte, os.Error) { |
|
95 buf := bytes.NewBuffer(nil) |
|
96 buf.WriteString("<text") |
|
97 writeField(buf, "xmlns", nsStreams) |
|
98 writeField(buf, "xml:lang", e.Lang) |
|
99 buf.WriteString(">") |
|
100 xml.Escape(buf, []byte(e.text)) |
|
101 buf.WriteString("</text>") |
|
102 return buf.Bytes(), nil |
|
103 } |
|
104 |
|
105 func writeField(w io.Writer, field, value string) { |
|
106 if value != "" { |
|
107 io.WriteString(w, " ") |
|
108 io.WriteString(w, field) |
|
109 io.WriteString(w, `="`) |
|
110 xml.Escape(w, []byte(value)) |
|
111 io.WriteString(w, `"`) |
|
112 } |
|
113 } |