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