Initial import to Tizen
[profile/ivi/python-twisted.git] / twisted / words / protocols / jabber / jid.py
1 # -*- test-case-name: twisted.words.test.test_jabberjid -*-
2 #
3 # Copyright (c) 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, resourceprep, 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()