Package pyxmpp :: Package jabber :: Module delay

[hide private]

# Source Code for Module pyxmpp.jabber.delay

      1  #
      2  # (C) Copyright 2003-2010 Jacek Konieczny <jajcus@jajcus.net>
      3  #
      4  # This program is free software; you can redistribute it and/or modify
      5  # it under the terms of the GNU Lesser General Public License Version
      6  # 2.1 as published by the Free Software Foundation.
      7  #
      8  # This program is distributed in the hope that it will be useful,
      9  # but WITHOUT ANY WARRANTY; without even the implied warranty of
     10  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     11  # GNU Lesser General Public License for more details.
     12  #
     13  # You should have received a copy of the GNU Lesser General Public
     14  # License along with this program; if not, write to the Free Software
     15  # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
     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
     82 -    def from_xml(self,xmlnode):
     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
    115 -    def complete_xml_element(self, xmlnode, _unused):
    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
    134 -    def get_datetime_local(self):
    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
    143 -    def get_datetime_utc(self):
    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
    150 -    def __str__(self):
    151          n=self.as_xml()
    152          r=n.serialize()
    153          n.freeNode()
    154          return r
    155
    156 -    def __cmp__(self,other):
    157          return cmp(timestamp, other.timestamp)
    158
    159 -def get_delays(stanza):
    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
    178 -def get_delay(stanza):
    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  # vi: sts=4 et sw=4
    197