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