pyxmpp.jid

Package pyxmpp :: Module jid |
[hide private]

# Source Code for Module pyxmpp.jid

      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  # pylint: disable-msg=W0232, E0201
     18
     19  """jid -- Jabber ID handling
     20
     21  Normative reference:
     22    - `RFC 3920 <http://www.ietf.org/rfc/rfc3920.txt>`__
     23  """
     24
     25  __revision__="$Id: jid.py 714 2010-04-05 10:20:10Z jajcus $"
     26  __docformat__="restructuredtext en"
     27
     28  import re
     29  import weakref
     30  import warnings
     31
     32  from encodings import idna
     33
     34  from pyxmpp.xmppstringprep import nodeprep,resourceprep
     35  from pyxmpp.exceptions import JIDError
     36
     37  node_invalid_re=re.compile(ur"[" u'"' ur"&'/:<>@\s\x00-\x19]",re.UNICODE)
     38  resource_invalid_re=re.compile(ur"[\s\x00-\x19]",re.UNICODE)
     39
     40 -def are_domains_equal(a,b):
     41      """Compare two International Domain Names.
     42
     43      :Parameters:
     44          - `a`,`b`: domains names to compare
     45
     46      :return: True `a` and `b` are equal as domain names."""
     47
     48      a=idna.ToASCII(a)
     49      b=idna.ToASCII(b)
     50      return a.lower()==b.lower()
     51
     52 -class JID(object):
     53      """JID.
     54
     55      :Ivariables:
     56          - `node`: node part of the JID
     57          - `domain`: domain part of the JID
     58          - `resource`: resource part of the JID
     59
     60      JID objects are immutable. They are also cached for better performance.
     61      """
     62      cache=weakref.WeakValueDictionary()
     63      __slots__=["node","domain","resource","__weakref__"]
     64 -    def __new__(cls,node_or_jid=None,domain=None,resource=None,check=True):
     65          """Create a new JID object or take one from the cache.
     66
     67          :Parameters:
     68              - `node_or_jid`: node part of the JID, JID object to copy or
     69                Unicode representation of the JID.
     70              - `domain`: domain part of the JID
     71              - `resource`: resource part of the JID
     72              - `check`: if `False` then JID is not checked for specifiaction
     73                compliance.
     74          """
     75
     76          if isinstance(node_or_jid,JID):
     77              return node_or_jid
     78
     79          if domain is None and resource is None:
     80              obj=cls.cache.get(node_or_jid)
     81              if obj:
     82                  return obj
     83          else:
     84              obj=None
     85          if obj is None:
     86              obj=object.__new__(cls)
     87
     88          if node_or_jid:
     89              node_or_jid = unicode(node_or_jid)
     90          if (node_or_jid and
     91                  ((u"@" in node_or_jid) or (u"/" in node_or_jid))):
     92              obj.__from_unicode(node_or_jid)
     93              cls.cache[node_or_jid]=obj
     94          else:
     95              if domain is None and resource is None:
     96                  if node_or_jid is None:
     97                      raise JIDError,"At least domain must be given"
     98                  domain=node_or_jid
     99                  node_or_jid=None
    100              if check:
    101                  obj.__set_node(node_or_jid)
    102                  obj.__set_domain(domain)
    103                  obj.__set_resource(resource)
    104              else:
    105                  object.__setattr__(obj,"node",node_or_jid)
    106                  object.__setattr__(obj,"domain",domain)
    107                  object.__setattr__(obj,"resource",resource)
    108          return obj
    109
    110 -    def __setattr__(self,name,value):
    111          raise RuntimeError,"JID objects are immutable!"
    112
    113 -    def __from_unicode(self,s,check=True):
    114          """Initialize JID object from Unicode string.
    115
    116          :Parameters:
    117              - `s`: the JID string
    118              - `check`: when `False` then the JID is not checked for
    119                specification compliance."""
    120          s1=s.split(u"/",1)
    121          s2=s1[0].split(u"@",1)
    122          if len(s2)==2:
    123              if check:
    124                  self.__set_node(s2[0])
    125                  self.__set_domain(s2[1])
    126              else:
    127                  object.__setattr__(self,"node",s2[0])
    128                  object.__setattr__(self,"domain",s2[1])
    129          else:
    130              if check:
    131                  self.__set_domain(s2[0])
    132              else:
    133                  object.__setattr__(self,"domain",s2[0])
    134              object.__setattr__(self,"node",None)
    135          if len(s1)==2:
    136              if check:
    137                  self.__set_resource(s1[1])
    138              else:
    139                  object.__setattr__(self,"resource",s1[1])
    140          else:
    141              object.__setattr__(self,"resource",None)
    142          if not self.domain:
    143              raise JIDError,"Domain is required in JID."
    144
    145 -    def __set_node(self,s):
    146          """Initialize `self.node`
    147
    148          :Parameters:
    149              - `s`: Node part of the JID
    150          :Types:
    151              - `s`: unicode
    152
    153          :raise JIDError: if the node name is too long.
    154          :raise pyxmpp.xmppstringprep.StringprepError: if the
    155              node name fails Nodeprep preparation."""
    156          if s:
    157              s = unicode(s)
    158              s=nodeprep.prepare(s)
    159              if len(s.encode("utf-8"))>1023:
    160                  raise JIDError,"Node name too long"
    161          else:
    162              s=None
    163          object.__setattr__(self,"node",s)
    164
    165 -    def __set_domain(self,s):
    166          """Initialize `self.domain`
    167
    168          :Parameters:
    169              - `s`: Unicode or UTF-8 domain part of the JID
    170
    171          :raise JIDError: if the domain name is too long."""
    172
    173          if s is None:
    174              raise JIDError,"Domain must be given"
    175          if s:
    176              s = unicode(s)
    177          s=idna.nameprep(s)
    178          if len(s.encode("utf-8"))>1023:
    179              raise JIDError,"Domain name too long"
    180          object.__setattr__(self,"domain",s)
    181
    182 -    def __set_resource(self,s):
    183          """Initialize `self.resource`
    184
    185          :Parameters:
    186              - `s`: Unicode or UTF-8 resource part of the JID
    187
    188          :raise JIDError: if the resource name is too long.
    189          :raise pyxmpp.xmppstringprep.StringprepError: if the
    190              node name fails Resourceprep preparation."""
    191          if s:
    192              s = unicode(s)
    193              s=resourceprep.prepare(s)
    194              if len(s.encode("utf-8"))>1023:
    195                  raise JIDError,"Resource name too long"
    196          else:
    197              s=None
    198          object.__setattr__(self,"resource",s)
    199
    200 -    def __str__(self):
    201          warnings.warn("JIDs should not be used as strings", DeprecationWarning, stacklevel=2)
    202          return self.as_utf8()
    203
    204 -    def __unicode__(self):
    205          return self.as_unicode()
    206
    207 -    def __repr__(self):
    208          return "<JID: %r>" % (self.as_unicode())
    209
    210 -    def as_utf8(self):
    211          """UTF-8 encoded JID representation.
    212
    213          :return: UTF-8 encoded JID."""
    214          return self.as_unicode().encode("utf-8")
    215
    216 -    def as_string(self):
    217          """UTF-8 encoded JID representation.
    218
    219          *Deprecated* Always use Unicode objects, or `as_utf8` if you really want.
    220
    221          :return: UTF-8 encoded JID."""
    222          warnings.warn("JID.as_string() is deprecated. Use unicode() or `as_utf8` instead.",
    223                   DeprecationWarning, stacklevel=1)
    224          return self.as_utf8()
    225
    226 -    def as_unicode(self):
    227          """Unicode string JID representation.
    228
    229          :return: JID as Unicode string."""
    230          r=self.domain
    231          if self.node:
    232              r=self.node+u'@'+r
    233          if self.resource:
    234              r=r+u'/'+self.resource
    235          if not JID.cache.has_key(r):
    236              JID.cache[r]=self
    237          return r
    238
    239 -    def bare(self):
    240          """Make bare JID made by removing resource from current `self`.
    241
    242          :return: new JID object without resource part."""
    243          return JID(self.node,self.domain,check=False)
    244
    245 -    def __eq__(self,other):
    246          if other is None:
    247              return False
    248          elif type(other) in (str, unicode):
    249              try:
    250                  other=JID(other)
    251              except:
    252                  return False
    253          elif not isinstance(other,JID):
    254              return False
    255
    256          return (self.node==other.node
    257              and are_domains_equal(self.domain,other.domain)
    258              and self.resource==other.resource)
    259
    260 -    def __ne__(self,other):
    261          return not self.__eq__(other)
    262
    263 -    def __cmp__(self,other):
    264          a=self.as_unicode()
    265          return cmp(a,other)
    266
    267 -    def __hash__(self):
    268          return hash(self.node)^hash(self.domain)^hash(self.resource)
    269
    270  # vi: sts=4 et sw=4
    271