1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 """Delayed delivery mark (jabber:x:delay) handling.
19
20 Normative reference:
21 - `JEP 91 <http://www.jabber.org/jeps/jep-0091.html>`__
22 """
23
24 __docformat__="restructuredtext en"
25
26 import libxml2
27 import time
28 import datetime
29
30 from pyxmpp.jid import JID
31
32 from pyxmpp.utils import to_utf8,from_utf8
33 from pyxmpp.xmlextra import get_node_ns_uri
34 from pyxmpp.utils import datetime_utc_to_local,datetime_local_to_utc
35 from pyxmpp.objects import StanzaPayloadObject
36 from pyxmpp.exceptions import BadRequestProtocolError, JIDMalformedProtocolError, JIDError
37
38 DELAY_NS="jabber:x:delay"
39
40 -class Delay(StanzaPayloadObject):
41 """
42 Delayed delivery tag.
43
44 Represents 'jabber:x:delay' (JEP-0091) element of a Jabber stanza.
45
46 :Ivariables:
47 - `delay_from`: the "from" value of the delay element
48 - `reason`: the "reason" (content) of the delay element
49 - `timestamp`: the UTC timestamp as naive datetime object
50 """
51
52 xml_element_name = "x"
53 xml_element_namespace = DELAY_NS
54
55 - def __init__(self,node_or_datetime,delay_from=None,reason=None,utc=True):
56 """
57 Initialize the Delay object.
58
59 :Parameters:
60 - `node_or_datetime`: an XML node to parse or the timestamp.
61 - `delay_from`: JID of the entity which adds the delay mark
62 (when `node_or_datetime` is a timestamp).
63 - `reason`: reason of the delay (when `node_or_datetime` is a
64 timestamp).
65 - `utc`: if `True` then the timestamp is assumed to be UTC,
66 otherwise it is assumed to be local time.
67 :Types:
68 - `node_or_datetime`: `libxml2.xmlNode` or `datetime.datetime`
69 - `delay_from`: `pyxmpp.JID`
70 - `reason`: `unicode`
71 - `utc`: `bool`"""
72 if isinstance(node_or_datetime,libxml2.xmlNode):
73 self.from_xml(node_or_datetime)
74 else:
75 if utc:
76 self.timestamp=node_or_datetime
77 else:
78 self.timestamp=datetime_local_to_utc(node_or_datetime)
79 self.delay_from=JID(delay_from)
80 self.reason=unicode(reason)
81
83 """Initialize Delay object from an XML node.
84
85 :Parameters:
86 - `xmlnode`: the jabber:x:delay XML element.
87 :Types:
88 - `xmlnode`: `libxml2.xmlNode`"""
89 if xmlnode.type!="element":
90 raise ValueError,"XML node is not a jabber:x:delay element (not an element)"
91 ns=get_node_ns_uri(xmlnode)
92 if ns and ns!=DELAY_NS or xmlnode.name!="x":
93 raise ValueError,"XML node is not a jabber:x:delay element"
94 stamp=xmlnode.prop("stamp")
95 if stamp.endswith("Z"):
96 stamp=stamp[:-1]
97 if "-" in stamp:
98 stamp=stamp.split("-",1)[0]
99 try:
100 tm = time.strptime(stamp, "%Y%m%dT%H:%M:%S")
101 except ValueError:
102 raise BadRequestProtocolError, "Bad timestamp"
103 tm=tm[0:8]+(0,)
104 self.timestamp=datetime.datetime.fromtimestamp(time.mktime(tm))
105 delay_from=from_utf8(xmlnode.prop("from"))
106 if delay_from:
107 try:
108 self.delay_from = JID(delay_from)
109 except JIDError:
110 raise JIDMalformedProtocolError, "Bad JID in the jabber:x:delay 'from' attribute"
111 else:
112 self.delay_from = None
113 self.reason = from_utf8(xmlnode.getContent())
114
116 """Complete the XML node with `self` content.
117
118 Should be overriden in classes derived from `StanzaPayloadObject`.
119
120 :Parameters:
121 - `xmlnode`: XML node with the element being built. It has already
122 right name and namespace, but no attributes or content.
123 - `_unused`: document to which the element belongs.
124 :Types:
125 - `xmlnode`: `libxml2.xmlNode`
126 - `_unused`: `libxml2.xmlDoc`"""
127 tm=self.timestamp.strftime("%Y%m%dT%H:%M:%S")
128 xmlnode.setProp("stamp",tm)
129 if self.delay_from:
130 xmlnode.setProp("from",self.delay_from.as_utf8())
131 if self.reason:
132 xmlnode.setContent(to_utf8(self.reason))
133
135 """Get the timestamp as a local time.
136
137 :return: the timestamp of the delay element represented in the local
138 timezone.
139 :returntype: `datetime.datetime`"""
140 r=datetime_utc_to_local(self.timestamp)
141 return r
142
144 """Get the timestamp as a UTC.
145
146 :return: the timestamp of the delay element represented in UTC.
147 :returntype: `datetime.datetime`"""
148 return self.timestamp
149
155
158
160 """Get jabber:x:delay elements from the stanza.
161
162 :Parameters:
163 - `stanza`: a, probably delayed, stanza.
164 :Types:
165 - `stanza`: `pyxmpp.stanza.Stanza`
166
167 :return: list of delay tags sorted by the timestamp.
168 :returntype: `list` of `Delay`"""
169 delays=[]
170 n=stanza.xmlnode.children
171 while n:
172 if n.type=="element" and get_node_ns_uri(n)==DELAY_NS and n.name=="x":
173 delays.append(Delay(n))
174 n=n.next
175 delays.sort()
176 return delays
177
179 """Get the oldest jabber:x:delay elements from the stanza.
180
181 :Parameters:
182 - `stanza`: a, probably delayed, stanza.
183 :Types:
184 - `stanza`: `pyxmpp.stanza.Stanza`
185
186 The return value, if not `None`, contains a quite reliable
187 timestamp of a delayed (e.g. from offline storage) message.
188
189 :return: the oldest delay tag of the stanza or `None`.
190 :returntype: `Delay`"""
191 delays=get_delays(stanza)
192 if not delays:
193 return None
194 return get_delays(stanza)[0]
195
196
197