forked from ortuman/jackal
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpresence.go
270 lines (230 loc) · 6.47 KB
/
presence.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
/*
* Copyright (c) 2018 Miguel Ángel Ortuño.
* See the LICENSE file for more information.
*/
package xmpp
import (
"bytes"
"errors"
"fmt"
"strconv"
"github.com/ortuman/jackal/xmpp/jid"
)
const capabilitiesNamespace = "http://jabber.org/protocol/caps"
const (
// AvailableType represents an 'available' Presence type.
AvailableType = ""
// UnavailableType represents a 'unavailable' Presence type.
UnavailableType = "unavailable"
// SubscribeType represents a 'subscribe' Presence type.
SubscribeType = "subscribe"
// UnsubscribeType represents a 'unsubscribe' Presence type.
UnsubscribeType = "unsubscribe"
// SubscribedType represents a 'subscribed' Presence type.
SubscribedType = "subscribed"
// UnsubscribedType represents a 'unsubscribed' Presence type.
UnsubscribedType = "unsubscribed"
// ProbeType represents a 'probe' Presence type.
ProbeType = "probe"
)
// Capabilities represents presence entity capabilities
type Capabilities struct {
Node string
Hash string
Ver string
}
// ShowState represents Presence show state.
type ShowState int
const (
// AvailableShowState represents 'available' Presence show state.
AvailableShowState ShowState = iota
// AwayShowState represents 'away' Presence show state.
AwayShowState
// ChatShowState represents 'chat' Presence show state.
ChatShowState
// DoNotDisturbShowState represents 'dnd' Presence show state.
DoNotDisturbShowState
// ExtendedAwaysShowState represents 'xa' Presence show state.
ExtendedAwaysShowState
)
// Presence type represents an <presence> element.
// All incoming <presence> elements providing from the
// stream will automatically be converted to Presence objects.
type Presence struct {
stanzaElement
showState ShowState
priority int8
}
// NewPresenceFromElement creates a Presence object from XElement.
func NewPresenceFromElement(e XElement, from *jid.JID, to *jid.JID) (*Presence, error) {
if e.Name() != "presence" {
return nil, fmt.Errorf("wrong Presence element name: %s", e.Name())
}
presenceType := e.Type()
if !isPresenceType(presenceType) {
return nil, fmt.Errorf(`invalid Presence "type" attribute: %s`, presenceType)
}
p := &Presence{}
p.copyFrom(e)
// show
if err := p.setShow(); err != nil {
return nil, err
}
// status
if err := p.validateStatus(); err != nil {
return nil, err
}
// priority
if err := p.setPriority(); err != nil {
return nil, err
}
p.SetFromJID(from)
p.SetToJID(to)
p.SetNamespace("")
return p, nil
}
// NewPresence creates and returns a new Presence element.
func NewPresence(from *jid.JID, to *jid.JID, presenceType string) *Presence {
p := &Presence{}
p.SetName("presence")
p.SetFromJID(from)
p.SetToJID(to)
p.SetType(presenceType)
return p
}
// NewPresenceFromBytes creates and returns a new Presence element from its bytes representation.
func NewPresenceFromBytes(buf *bytes.Buffer) (*Presence, error) {
p := &Presence{}
if err := p.FromBytes(buf); err != nil {
return nil, err
}
return p, nil
}
// IsAvailable returns true if this is an 'available' type Presence.
func (p *Presence) IsAvailable() bool {
return p.Type() == AvailableType
}
// IsUnavailable returns true if this is an 'unavailable' type Presence.
func (p *Presence) IsUnavailable() bool {
return p.Type() == UnavailableType
}
// IsSubscribe returns true if this is a 'subscribe' type Presence.
func (p *Presence) IsSubscribe() bool {
return p.Type() == SubscribeType
}
// IsUnsubscribe returns true if this is an 'unsubscribe' type Presence.
func (p *Presence) IsUnsubscribe() bool {
return p.Type() == UnsubscribeType
}
// IsSubscribed returns true if this is a 'subscribed' type Presence.
func (p *Presence) IsSubscribed() bool {
return p.Type() == SubscribedType
}
// IsUnsubscribed returns true if this is an 'unsubscribed' type Presence.
func (p *Presence) IsUnsubscribed() bool {
return p.Type() == UnsubscribedType
}
// IsProbe returns true if this is an 'probe' type Presence.
func (p *Presence) IsProbe() bool {
return p.Type() == ProbeType
}
// Status returns presence stanza default status.
func (p *Presence) Status() string {
if st := p.Elements().Child("status"); st != nil {
return st.Text()
}
return ""
}
// ShowState returns presence stanza show state.
func (p *Presence) ShowState() ShowState {
return p.showState
}
// Priority returns presence stanza priority value.
func (p *Presence) Priority() int8 {
return p.priority
}
// Capabilities returns presence stanza capabilities element
func (p *Presence) Capabilities() *Capabilities {
c := p.Elements().ChildNamespace("c", capabilitiesNamespace)
if c == nil {
return nil
}
attribs := c.Attributes()
return &Capabilities{
Node: attribs.Get("node"),
Hash: attribs.Get("hash"),
Ver: attribs.Get("ver"),
}
}
func isPresenceType(presenceType string) bool {
switch presenceType {
case ErrorType, AvailableType, UnavailableType, SubscribeType,
UnsubscribeType, SubscribedType, UnsubscribedType, ProbeType:
return true
default:
return false
}
}
func (p *Presence) validateStatus() error {
sts := p.elements.Children("status")
for _, st := range sts {
switch st.Attributes().Count() {
case 0:
break
case 1:
as := st.Attributes()
if as.(attributeSet)[0].Label == "xml:lang" {
break
}
fallthrough
default:
return errors.New(" the <status/> element MUST NOT possess any attributes, with the exception of the 'xml:lang' attribute")
}
}
return nil
}
func (p *Presence) setShow() error {
shs := p.elements.Children("show")
switch len(shs) {
case 0:
p.showState = AvailableShowState
case 1:
if shs[0].Attributes().Count() > 0 {
return errors.New(" the <show/> element MUST NOT possess any attributes")
}
switch shs[0].Text() {
case "away":
p.showState = AwayShowState
case "chat":
p.showState = ChatShowState
case "dnd":
p.showState = DoNotDisturbShowState
case "xa":
p.showState = ExtendedAwaysShowState
default:
return fmt.Errorf("invalid Presence show state: %s", shs[0].Text())
}
default:
return errors.New(" Presence stanza MUST NOT contain more than one <show/> element")
}
return nil
}
func (p *Presence) setPriority() error {
ps := p.elements.Children("priority")
switch len(ps) {
case 0:
break
case 1:
pr, err := strconv.Atoi(ps[0].Text())
if err != nil {
return err
}
if pr < -128 || pr > 127 {
return errors.New("priority value MUST be an integer between -128 and +127")
}
p.priority = int8(pr)
default:
return errors.New("a Presence stanza MUST NOT contain more than one <priority/> element")
}
return nil
}