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