1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 """Presence XMPP stanza handling
19
20 Normative reference:
21 - `RFC 3920 <http://www.ietf.org/rfc/rfc3920.txt>`__
22 """
23
24 __revision__="$Id: presence.py 714 2010-04-05 10:20:10Z jajcus $"
25 __docformat__="restructuredtext en"
26
27 import libxml2
28
29 from pyxmpp.utils import to_utf8,from_utf8
30 from pyxmpp.stanza import Stanza
31 from pyxmpp.xmlextra import common_ns
32
33 presence_types=("available","unavailable","probe","subscribe","unsubscribe","subscribed",
34 "unsubscribed","invisible","error")
35
36 accept_responses={
37 "subscribe": "subscribed",
38 "subscribed": "subscribe",
39 "unsubscribe": "unsubscribed",
40 "unsubscribed": "unsubscribe",
41 }
42
43 deny_responses={
44 "subscribe": "unsubscribed",
45 "subscribed": "unsubscribe",
46 "unsubscribe": "subscribed",
47 "unsubscribed": "subscribe",
48 }
49
51 """Wraper object for <presence /> stanzas."""
52 stanza_type="presence"
53 - def __init__(self, xmlnode = None, from_jid = None, to_jid = None, stanza_type = None,
54 stanza_id = None, show = None, status = None, priority = 0,
55 error = None, error_cond = None, stream = None):
56 """Initialize a `Presence` object.
57
58 :Parameters:
59 - `xmlnode`: XML node to_jid be wrapped into the `Presence` object
60 or other Presence object to be copied. If not given then new
61 presence stanza is created using following parameters.
62 - `from_jid`: sender JID.
63 - `to_jid`: recipient JID.
64 - `stanza_type`: staza type: one of: None, "available", "unavailable",
65 "subscribe", "subscribed", "unsubscribe", "unsubscribed" or
66 "error". "available" is automaticaly changed to_jid None.
67 - `stanza_id`: stanza id -- value of stanza's "id" attribute
68 - `show`: "show" field of presence stanza. One of: None, "away",
69 "xa", "dnd", "chat".
70 - `status`: descriptive text for the presence stanza.
71 - `priority`: presence priority.
72 - `error_cond`: error condition name. Ignored if `stanza_type` is not "error"
73 :Types:
74 - `xmlnode`: `unicode` or `libxml2.xmlNode` or `Stanza`
75 - `from_jid`: `JID`
76 - `to_jid`: `JID`
77 - `stanza_type`: `unicode`
78 - `stanza_id`: `unicode`
79 - `show`: `unicode`
80 - `status`: `unicode`
81 - `priority`: `unicode`
82 - `error_cond`: `unicode`"""
83 self.xmlnode=None
84 if isinstance(xmlnode,Presence):
85 pass
86 elif isinstance(xmlnode,Stanza):
87 raise TypeError,"Couldn't make Presence from other Stanza"
88 elif isinstance(xmlnode,libxml2.xmlNode):
89 pass
90 elif xmlnode is not None:
91 raise TypeError,"Couldn't make Presence from %r" % (type(xmlnode),)
92
93 if stanza_type and stanza_type not in presence_types:
94 raise ValueError, "Invalid presence type: %r" % (type,)
95
96 if stanza_type=="available":
97 stanza_type=None
98
99 if xmlnode is None:
100 xmlnode="presence"
101
102 Stanza.__init__(self, xmlnode, from_jid = from_jid, to_jid = to_jid, stanza_type = stanza_type,
103 stanza_id = stanza_id, error = error, error_cond = error_cond, stream = stream)
104
105 if show:
106 self.xmlnode.newTextChild(common_ns,"show",to_utf8(show))
107 if status:
108 self.xmlnode.newTextChild(common_ns,"status",to_utf8(status))
109 if priority and priority!=0:
110 self.xmlnode.newTextChild(common_ns,"priority",to_utf8(unicode(priority)))
111
113 """Create a deep copy of the presence stanza.
114
115 :returntype: `Presence`"""
116 return Presence(self)
117
119 """Change presence status description.
120
121 :Parameters:
122 - `status`: descriptive text for the presence stanza.
123 :Types:
124 - `status`: `unicode`"""
125 n=self.xpath_eval("ns:status")
126 if not status:
127 if n:
128 n[0].unlinkNode()
129 n[0].freeNode()
130 else:
131 return
132 if n:
133 n[0].setContent(to_utf8(status))
134 else:
135 self.xmlnode.newTextChild(common_ns,"status",to_utf8(status))
136
138 """Get presence status description.
139
140 :return: value of stanza's <status/> field.
141 :returntype: `unicode`"""
142 n=self.xpath_eval("ns:status")
143 if n:
144 return from_utf8(n[0].getContent())
145 else:
146 return None
147
149 """Get presence "show" field.
150
151 :return: value of stanza's <show/> field.
152 :returntype: `unicode`"""
153 n=self.xpath_eval("ns:show")
154 if n:
155 return from_utf8(n[0].getContent())
156 else:
157 return None
158
160 """Change presence "show" field.
161
162 :Parameters:
163 - `show`: new value for the "show" field of presence stanza. One
164 of: None, "away", "xa", "dnd", "chat".
165 :Types:
166 - `show`: `unicode`"""
167 n=self.xpath_eval("ns:show")
168 if not show:
169 if n:
170 n[0].unlinkNode()
171 n[0].freeNode()
172 else:
173 return
174 if n:
175 n[0].setContent(to_utf8(show))
176 else:
177 self.xmlnode.newTextChild(common_ns,"show",to_utf8(show))
178
180 """Get presence priority.
181
182 :return: value of stanza's priority. 0 if the stanza doesn't contain
183 <priority/> element.
184 :returntype: `int`"""
185 n=self.xpath_eval("ns:priority")
186 if not n:
187 return 0
188 try:
189 prio=int(n[0].getContent())
190 except ValueError:
191 return 0
192 return prio
193
195 """Change presence priority.
196
197 :Parameters:
198 - `priority`: new presence priority.
199 :Types:
200 - `priority`: `int`"""
201 n=self.xpath_eval("ns:priority")
202 if not priority:
203 if n:
204 n[0].unlinkNode()
205 n[0].freeNode()
206 else:
207 return
208 priority=int(priority)
209 if priority<-128 or priority>127:
210 raise ValueError, "Bad priority value"
211 priority=str(priority)
212 if n:
213 n[0].setContent(priority)
214 else:
215 self.xmlnode.newTextChild(common_ns,"priority",priority)
216
218 """Create "accept" response for the "subscribe"/"subscribed"/"unsubscribe"/"unsubscribed"
219 presence stanza.
220
221 :return: new stanza.
222 :returntype: `Presence`"""
223
224 if self.get_type() not in ("subscribe","subscribed","unsubscribe","unsubscribed"):
225 raise ValueError, ("Results may only be generated for 'subscribe',"
226 "'subscribed','unsubscribe' or 'unsubscribed' presence")
227
228 pr=Presence(stanza_type=accept_responses[self.get_type()],
229 from_jid=self.get_to(),to_jid=self.get_from(),stanza_id=self.get_id())
230 return pr
231
233 """Create "deny" response for the "subscribe"/"subscribed"/"unsubscribe"/"unsubscribed"
234 presence stanza.
235
236 :return: new presence stanza.
237 :returntype: `Presence`"""
238 if self.get_type() not in ("subscribe","subscribed","unsubscribe","unsubscribed"):
239 raise ValueError, ("Results may only be generated for 'subscribe',"
240 "'subscribed','unsubscribe' or 'unsubscribed' presence")
241
242 pr=Presence(stanza_type=deny_responses[self.get_type()],
243 from_jid=self.get_to(),to_jid=self.get_from(),stanza_id=self.get_id())
244 return pr
245
247 """Create error response for the any non-error presence stanza.
248
249 :Parameters:
250 - `cond`: error condition name, as defined in XMPP specification.
251 :Types:
252 - `cond`: `unicode`
253
254 :return: new presence stanza.
255 :returntype: `Presence`"""
256
257 if self.get_type() == "error":
258 raise ValueError, "Errors may not be generated in response to errors"
259
260 p=Presence(stanza_type="error",from_jid=self.get_to(),to_jid=self.get_from(),
261 stanza_id=self.get_id(),error_cond=cond)
262
263 if self.xmlnode.children:
264 n=self.xmlnode.children
265 while n:
266 p.xmlnode.children.addPrevSibling(n.copyNode(1))
267 n=n.next
268 return p
269
270
271