2
## src/common/xmpp/stringprepare.py
4
## Copyright (C) 2001-2005 Twisted Matrix Laboratories
5
## Copyright (C) 2005-2012 Yann Leboulanger <asterix AT lagaule.org>
6
## Copyright (C) 2006 Stefan Bethge <stefan AT lanpartei.de>
7
## Copyright (C) 2007 Jean-Marie Traissard <jim AT lapin.org>
9
## This file is part of Gajim.
11
## Gajim is free software; you can redistribute it and/or modify
12
## it under the terms of the GNU General Public License as published
13
## by the Free Software Foundation; version 3 only.
15
## Gajim is distributed in the hope that it will be useful,
16
## but WITHOUT ANY WARRANTY; without even the implied warranty of
17
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
## GNU General Public License for more details.
20
## You should have received a copy of the GNU General Public License
21
## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
26
from encodings import idna
30
Interface for character lookup classes
35
Return whether character is in this table
41
Interface for character mapping classes
46
Return mapping for character
50
class LookupTableFromFunction:
52
__implements__ = ILookupTable
54
def __init__(self, in_table_function):
55
self.lookup = in_table_function
59
__implements__ = ILookupTable
61
def __init__(self, table):
65
return c in self._table
67
class MappingTableFromFunction:
69
__implements__ = IMappingTable
71
def __init__(self, map_table_function):
72
self.map = map_table_function
74
class EmptyMappingTable:
76
__implements__ = IMappingTable
78
def __init__(self, in_table_function):
79
self._in_table_function = in_table_function
82
if self._in_table_function(c):
88
def __init__(self, mappings=[], normalize=True, prohibiteds=[],
89
check_unassigneds=True, check_bidi=True):
90
self.mappings = mappings
91
self.normalize = normalize
92
self.prohibiteds = prohibiteds
93
self.do_check_unassigneds = check_unassigneds
94
self.do_check_bidi = check_bidi
96
def prepare(self, string):
97
result = self.map(string)
99
result = unicodedata.normalize("NFKC", result)
100
self.check_prohibiteds(result)
101
if self.do_check_unassigneds:
102
self.check_unassigneds(result)
103
if self.do_check_bidi:
104
self.check_bidirectionals(result)
107
def map(self, string):
113
for mapping in self.mappings:
114
result_c = mapping.map(c)
118
if result_c is not None:
119
result.append(result_c)
121
return "".join(result)
123
def check_prohibiteds(self, string):
125
for table in self.prohibiteds:
127
raise UnicodeError("Invalid character %s" % repr(c))
129
def check_unassigneds(self, string):
131
if stringprep.in_table_a1(c):
132
raise UnicodeError("Unassigned code point %s" % repr(c))
134
def check_bidirectionals(self, string):
136
found_RandALCat = False
139
if stringprep.in_table_d1(c):
140
found_RandALCat = True
141
if stringprep.in_table_d2(c):
144
if found_LCat and found_RandALCat:
145
raise UnicodeError("Violation of BIDI Requirement 2")
147
if found_RandALCat and not (stringprep.in_table_d1(string[0]) and
148
stringprep.in_table_d1(string[-1])):
149
raise UnicodeError("Violation of BIDI Requirement 3")
154
Implements preparation of internationalized domain names
156
This class implements preparing internationalized domain names using the
157
rules defined in RFC 3491, section 4 (Conversion operations).
159
We do not perform step 4 since we deal with unicode representations of
160
domain names and do not convert from or to ASCII representations using
161
punycode encoding. When such a conversion is needed, the L{idna} standard
162
library provides the C{ToUnicode()} and C{ToASCII()} functions. Note that
163
L{idna} itself assumes UseSTD3ASCIIRules to be false.
165
The following steps are performed by C{prepare()}:
167
* Split the domain name in labels at the dots (RFC 3490, 3.1)
168
* Apply nameprep proper on each label (RFC 3491)
169
* Enforce the restrictions on ASCII characters in host names by
170
assuming STD3ASCIIRules to be true. (STD 3)
171
* Rejoin the labels using the label separator U+002E (full stop).
174
# Prohibited characters.
175
prohibiteds = [chr(n) for n in list(range(0x00, 0x2c + 1)) +
176
list(range(0x2e, 0x2f + 1)) +
177
list(range(0x3a, 0x40 + 1)) +
178
list(range(0x5b, 0x60 + 1)) +
179
list(range(0x7b, 0x7f + 1)) ]
181
def prepare(self, string):
184
labels = idna.dots.split(string)
186
if labels and len(labels[-1]) == 0:
193
result.append(self.nameprep(label))
195
return ".".join(result)+trailing_dot
197
def check_prohibiteds(self, string):
199
if c in self.prohibiteds:
200
raise UnicodeError("Invalid character %s" % repr(c))
202
def nameprep(self, label):
203
label = idna.nameprep(label)
204
self.check_prohibiteds(label)
206
raise UnicodeError("Invalid empty name")
208
raise UnicodeError("Invalid leading hyphen-minus")
210
raise UnicodeError("Invalid trailing hyphen-minus")
213
C_11 = LookupTableFromFunction(stringprep.in_table_c11)
214
C_12 = LookupTableFromFunction(stringprep.in_table_c12)
215
C_21 = LookupTableFromFunction(stringprep.in_table_c21)
216
C_22 = LookupTableFromFunction(stringprep.in_table_c22)
217
C_3 = LookupTableFromFunction(stringprep.in_table_c3)
218
C_4 = LookupTableFromFunction(stringprep.in_table_c4)
219
C_5 = LookupTableFromFunction(stringprep.in_table_c5)
220
C_6 = LookupTableFromFunction(stringprep.in_table_c6)
221
C_7 = LookupTableFromFunction(stringprep.in_table_c7)
222
C_8 = LookupTableFromFunction(stringprep.in_table_c8)
223
C_9 = LookupTableFromFunction(stringprep.in_table_c9)
225
B_1 = EmptyMappingTable(stringprep.in_table_b1)
226
B_2 = MappingTableFromFunction(stringprep.map_table_b2)
228
nodeprep = Profile(mappings=[B_1, B_2],
229
prohibiteds=[C_11, C_12, C_21, C_22,
230
C_3, C_4, C_5, C_6, C_7, C_8, C_9,
231
LookupTable(['"', '&', "'", '/',
232
':', '<', '>', '@'])])
234
resourceprep = Profile(mappings=[B_1,],
235
prohibiteds=[C_12, C_21, C_22,
236
C_3, C_4, C_5, C_6, C_7, C_8, C_9])
238
nameprep = NamePrep()