OLD | NEW |
| (Empty) |
1 # -*- test-case-name: twisted.words.test.test_jabberjid -*- | |
2 # | |
3 # Copyright (c) 2001-2008 Twisted Matrix Laboratories. | |
4 # See LICENSE for details. | |
5 | |
6 """ | |
7 Jabber Identifier support. | |
8 | |
9 This module provides an object to represent Jabber Identifiers (JIDs) and | |
10 parse string representations into them with proper checking for illegal | |
11 characters, case folding and canonicalisation through L{stringprep<twisted.words
.protocols.jabber.xmpp_stringprep>}. | |
12 """ | |
13 | |
14 from twisted.words.protocols.jabber.xmpp_stringprep import nodeprep, resourcepre
p, nameprep | |
15 | |
16 class InvalidFormat(Exception): | |
17 """ | |
18 The given string could not be parsed into a valid Jabber Identifier (JID). | |
19 """ | |
20 | |
21 def parse(jidstring): | |
22 """ | |
23 Parse given JID string into its respective parts and apply stringprep. | |
24 | |
25 @param jidstring: string representation of a JID. | |
26 @type jidstring: C{unicode} | |
27 @return: tuple of (user, host, resource), each of type C{unicode} as | |
28 the parsed and stringprep'd parts of the given JID. If the | |
29 given string did not have a user or resource part, the respective | |
30 field in the tuple will hold C{None}. | |
31 @rtype: C{tuple} | |
32 """ | |
33 user = None | |
34 host = None | |
35 resource = None | |
36 | |
37 # Search for delimiters | |
38 user_sep = jidstring.find("@") | |
39 res_sep = jidstring.find("/") | |
40 | |
41 if user_sep == -1: | |
42 if res_sep == -1: | |
43 # host | |
44 host = jidstring | |
45 else: | |
46 # host/resource | |
47 host = jidstring[0:res_sep] | |
48 resource = jidstring[res_sep + 1:] or None | |
49 else: | |
50 if res_sep == -1: | |
51 # user@host | |
52 user = jidstring[0:user_sep] or None | |
53 host = jidstring[user_sep + 1:] | |
54 else: | |
55 if user_sep < res_sep: | |
56 # user@host/resource | |
57 user = jidstring[0:user_sep] or None | |
58 host = jidstring[user_sep + 1:user_sep + (res_sep - user_sep)] | |
59 resource = jidstring[res_sep + 1:] or None | |
60 else: | |
61 # host/resource (with an @ in resource) | |
62 host = jidstring[0:res_sep] | |
63 resource = jidstring[res_sep + 1:] or None | |
64 | |
65 return prep(user, host, resource) | |
66 | |
67 def prep(user, host, resource): | |
68 """ | |
69 Perform stringprep on all JID fragments. | |
70 | |
71 @param user: The user part of the JID. | |
72 @type user: C{unicode} | |
73 @param host: The host part of the JID. | |
74 @type host: C{unicode} | |
75 @param resource: The resource part of the JID. | |
76 @type resource: C{unicode} | |
77 @return: The given parts with stringprep applied. | |
78 @rtype: C{tuple} | |
79 """ | |
80 | |
81 if user: | |
82 try: | |
83 user = nodeprep.prepare(unicode(user)) | |
84 except UnicodeError: | |
85 raise InvalidFormat, "Invalid character in username" | |
86 else: | |
87 user = None | |
88 | |
89 if not host: | |
90 raise InvalidFormat, "Server address required." | |
91 else: | |
92 try: | |
93 host = nameprep.prepare(unicode(host)) | |
94 except UnicodeError: | |
95 raise InvalidFormat, "Invalid character in hostname" | |
96 | |
97 if resource: | |
98 try: | |
99 resource = resourceprep.prepare(unicode(resource)) | |
100 except UnicodeError: | |
101 raise InvalidFormat, "Invalid character in resource" | |
102 else: | |
103 resource = None | |
104 | |
105 return (user, host, resource) | |
106 | |
107 __internJIDs = {} | |
108 | |
109 def internJID(jidstring): | |
110 """ | |
111 Return interned JID. | |
112 | |
113 @rtype: L{JID} | |
114 """ | |
115 | |
116 if jidstring in __internJIDs: | |
117 return __internJIDs[jidstring] | |
118 else: | |
119 j = JID(jidstring) | |
120 __internJIDs[jidstring] = j | |
121 return j | |
122 | |
123 class JID(object): | |
124 """ | |
125 Represents a stringprep'd Jabber ID. | |
126 | |
127 JID objects are hashable so they can be used in sets and as keys in | |
128 dictionaries. | |
129 """ | |
130 | |
131 def __init__(self, str=None, tuple=None): | |
132 if not (str or tuple): | |
133 raise RuntimeError("You must provide a value for either 'str' or " | |
134 "'tuple' arguments.") | |
135 | |
136 if str: | |
137 user, host, res = parse(str) | |
138 else: | |
139 user, host, res = prep(*tuple) | |
140 | |
141 self.user = user | |
142 self.host = host | |
143 self.resource = res | |
144 | |
145 def userhost(self): | |
146 """ | |
147 Extract the bare JID as a unicode string. | |
148 | |
149 A bare JID does not have a resource part, so this returns either | |
150 C{user@host} or just C{host}. | |
151 | |
152 @rtype: C{unicode} | |
153 """ | |
154 if self.user: | |
155 return u"%s@%s" % (self.user, self.host) | |
156 else: | |
157 return self.host | |
158 | |
159 def userhostJID(self): | |
160 """ | |
161 Extract the bare JID. | |
162 | |
163 A bare JID does not have a resource part, so this returns a | |
164 L{JID} object representing either C{user@host} or just C{host}. | |
165 | |
166 If the object this method is called upon doesn't have a resource | |
167 set, it will return itself. Otherwise, the bare JID object will | |
168 be created, interned using L{internJID}. | |
169 | |
170 @rtype: L{JID} | |
171 """ | |
172 if self.resource: | |
173 return internJID(self.userhost()) | |
174 else: | |
175 return self | |
176 | |
177 def full(self): | |
178 """ | |
179 Return the string representation of this JID. | |
180 | |
181 @rtype: C{unicode} | |
182 """ | |
183 if self.user: | |
184 if self.resource: | |
185 return u"%s@%s/%s" % (self.user, self.host, self.resource) | |
186 else: | |
187 return u"%s@%s" % (self.user, self.host) | |
188 else: | |
189 if self.resource: | |
190 return u"%s/%s" % (self.host, self.resource) | |
191 else: | |
192 return self.host | |
193 | |
194 def __eq__(self, other): | |
195 """ | |
196 Equality comparison. | |
197 | |
198 L{JID}s compare equal if their user, host and resource parts all | |
199 compare equal. When comparing against instances of other types, it | |
200 uses the default comparison. | |
201 """ | |
202 if isinstance(other, JID): | |
203 return (self.user == other.user and | |
204 self.host == other.host and | |
205 self.resource == other.resource) | |
206 else: | |
207 return NotImplemented | |
208 | |
209 def __ne__(self, other): | |
210 """ | |
211 Inequality comparison. | |
212 | |
213 This negates L{__eq__} for comparison with JIDs and uses the default | |
214 comparison for other types. | |
215 """ | |
216 result = self.__eq__(other) | |
217 if result is NotImplemented: | |
218 return result | |
219 else: | |
220 return not result | |
221 | |
222 def __hash__(self): | |
223 """ | |
224 Calculate hash. | |
225 | |
226 L{JID}s with identical constituent user, host and resource parts have | |
227 equal hash values. In combination with the comparison defined on JIDs, | |
228 this allows for using L{JID}s in sets and as dictionary keys. | |
229 """ | |
230 return hash((self.user, self.host, self.resource)) | |
231 | |
232 def __unicode__(self): | |
233 """ | |
234 Get unicode representation. | |
235 | |
236 Return the string representation of this JID as a unicode string. | |
237 @see: L{full} | |
238 """ | |
239 | |
240 return self.full() | |
241 | |
242 def __repr__(self): | |
243 """ | |
244 Get object representation. | |
245 | |
246 Returns a string that would create a new JID object that compares equal | |
247 to this one. | |
248 """ | |
249 return 'JID(%r)' % self.full() | |
OLD | NEW |