]> code.delx.au - pymsnt/blob - src/tlib/jabber/xmpp_stringprep.py
Oops. XDB fixes fixed.
[pymsnt] / src / tlib / jabber / xmpp_stringprep.py
1 # -*- test-case-name: twisted.words.test.test_jabberxmppstringprep -*-
2 #
3 # Copyright (c) 2001-2005 Twisted Matrix Laboratories.
4 # See LICENSE for details.
5
6 import sys, warnings
7
8 if sys.version_info < (2,3,2):
9 import re
10
11 class IDNA:
12 dots = re.compile(u"[\u002E\u3002\uFF0E\uFF61]")
13 def nameprep(self, label):
14 return label.lower()
15
16 idna = IDNA()
17
18 crippled = True
19
20 warnings.warn("Accented and non-Western Jabber IDs will not be properly "
21 "case-folded with this version of Python, resulting in "
22 "incorrect protocol-level behavior. It is strongly "
23 "recommended you upgrade to Python 2.3.2 or newer if you "
24 "intend to use Twisted's Jabber support.")
25
26 else:
27 import stringprep
28 import unicodedata
29 from encodings import idna
30
31 crippled = False
32
33 del sys, warnings
34
35 class ILookupTable:
36 """ Interface for character lookup classes. """
37
38 def lookup(self, c):
39 """ Return whether character is in this table. """
40
41 class IMappingTable:
42 """ Interface for character mapping classes. """
43
44 def map(self, c):
45 """ Return mapping for character. """
46
47 class LookupTableFromFunction:
48
49 __implements__ = ILookupTable
50
51 def __init__(self, in_table_function):
52 self.lookup = in_table_function
53
54 class LookupTable:
55
56 __implements__ = ILookupTable
57
58 def __init__(self, table):
59 self._table = table
60
61 def lookup(self, c):
62 return c in self._table
63
64 class MappingTableFromFunction:
65
66 __implements__ = IMappingTable
67
68 def __init__(self, map_table_function):
69 self.map = map_table_function
70
71 class EmptyMappingTable:
72
73 __implements__ = IMappingTable
74
75 def __init__(self, in_table_function):
76 self._in_table_function = in_table_function
77
78 def map(self, c):
79 if self._in_table_function(c):
80 return None
81 else:
82 return c
83
84 class Profile:
85 def __init__(self, mappings=[], normalize=True, prohibiteds=[],
86 check_unassigneds=True, check_bidi=True):
87 self.mappings = mappings
88 self.normalize = normalize
89 self.prohibiteds = prohibiteds
90 self.do_check_unassigneds = check_unassigneds
91 self.do_check_bidi = check_bidi
92
93 def prepare(self, string):
94 result = self.map(string)
95 if self.normalize:
96 result = unicodedata.normalize("NFKC", result)
97 self.check_prohibiteds(result)
98 if self.do_check_unassigneds:
99 self.check_unassigneds(result)
100 if self.do_check_bidi:
101 self.check_bidirectionals(result)
102 return result
103
104 def map(self, string):
105 result = []
106
107 for c in string:
108 result_c = c
109
110 for mapping in self.mappings:
111 result_c = mapping.map(c)
112 if result_c != c:
113 break
114
115 if result_c is not None:
116 result.append(result_c)
117
118 return u"".join(result)
119
120 def check_prohibiteds(self, string):
121 for c in string:
122 for table in self.prohibiteds:
123 if table.lookup(c):
124 raise UnicodeError, "Invalid character %s" % repr(c)
125
126 def check_unassigneds(self, string):
127 for c in string:
128 if stringprep.in_table_a1(c):
129 raise UnicodeError, "Unassigned code point %s" % repr(c)
130
131 def check_bidirectionals(self, string):
132 found_LCat = False
133 found_RandALCat = False
134
135 for c in string:
136 if stringprep.in_table_d1(c):
137 found_RandALCat = True
138 if stringprep.in_table_d2(c):
139 found_LCat = True
140
141 if found_LCat and found_RandALCat:
142 raise UnicodeError, "Violation of BIDI Requirement 2"
143
144 if found_RandALCat and not (stringprep.in_table_d1(string[0]) and
145 stringprep.in_table_d1(string[-1])):
146 raise UnicodeError, "Violation of BIDI Requirement 3"
147
148
149 class NamePrep:
150 """ Implements nameprep on international domain names.
151
152 STD3ASCIIRules is assumed true in this implementation.
153 """
154
155 # Prohibited characters.
156 prohibiteds = [unichr(n) for n in range(0x00, 0x2c + 1) +
157 range(0x2e, 0x2f + 1) +
158 range(0x3a, 0x40 + 1) +
159 range(0x5b, 0x60 + 1) +
160 range(0x7b, 0x7f + 1) ]
161
162 def prepare(self, string):
163 result = []
164
165 labels = idna.dots.split(string)
166
167 if labels and len(labels[-1]) == 0:
168 trailing_dot = '.'
169 del labels[-1]
170 else:
171 trailing_dot = ''
172
173 for label in labels:
174 result.append(self.nameprep(label))
175
176 return ".".join(result)+trailing_dot
177
178 def check_prohibiteds(self, string):
179 for c in string:
180 if c in self.prohibiteds:
181 raise UnicodeError, "Invalid character %s" % repr(c)
182
183 def nameprep(self, label):
184 label = idna.nameprep(label)
185 self.check_prohibiteds(label)
186 if label[0] == '-':
187 raise UnicodeError, "Invalid leading hyphen-minus"
188 if label[-1] == '-':
189 raise UnicodeError, "Invalid trailing hyphen-minus"
190 return label
191
192 if crippled:
193 case_map = MappingTableFromFunction(lambda c: c.lower())
194 nodeprep = Profile(mappings=[case_map],
195 normalize=False,
196 prohibiteds=[LookupTable([u' ', u'"', u'&', u"'", u'/',
197 u':', u'<', u'>', u'@'])],
198 check_unassigneds=False,
199 check_bidi=False)
200
201 resourceprep = Profile(normalize=False,
202 check_unassigneds=False,
203 check_bidi=False)
204
205 else:
206 C_11 = LookupTableFromFunction(stringprep.in_table_c11)
207 C_12 = LookupTableFromFunction(stringprep.in_table_c12)
208 C_21 = LookupTableFromFunction(stringprep.in_table_c21)
209 C_22 = LookupTableFromFunction(stringprep.in_table_c22)
210 C_3 = LookupTableFromFunction(stringprep.in_table_c3)
211 C_4 = LookupTableFromFunction(stringprep.in_table_c4)
212 C_5 = LookupTableFromFunction(stringprep.in_table_c5)
213 C_6 = LookupTableFromFunction(stringprep.in_table_c6)
214 C_7 = LookupTableFromFunction(stringprep.in_table_c7)
215 C_8 = LookupTableFromFunction(stringprep.in_table_c8)
216 C_9 = LookupTableFromFunction(stringprep.in_table_c9)
217
218 B_1 = EmptyMappingTable(stringprep.in_table_b1)
219 B_2 = MappingTableFromFunction(stringprep.map_table_b2)
220
221 nodeprep = Profile(mappings=[B_1, B_2],
222 prohibiteds=[C_11, C_12, C_21, C_22,
223 C_3, C_4, C_5, C_6, C_7, C_8, C_9,
224 LookupTable([u'"', u'&', u"'", u'/',
225 u':', u'<', u'>', u'@'])])
226
227 resourceprep = Profile(mappings=[B_1,],
228 prohibiteds=[C_12, C_21, C_22,
229 C_3, C_4, C_5, C_6, C_7, C_8, C_9])
230
231 nameprep = NamePrep()