~ubuntu-branches/ubuntu/wily/python-nbxmpp/wily

« back to all changes in this revision

Viewing changes to nbxmpp/stringprepare.py

  • Committer: Package Import Robot
  • Author(s): Tanguy Ortolo
  • Date: 2014-10-15 23:27:47 UTC
  • Revision ID: package-import@ubuntu.com-20141015232747-873wvm53nzoq3tue
Tags: upstream-0.5.1
ImportĀ upstreamĀ versionĀ 0.5.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- coding:utf-8 -*-
 
2
## src/common/xmpp/stringprepare.py
 
3
##
 
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>
 
8
##
 
9
## This file is part of Gajim.
 
10
##
 
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.
 
14
##
 
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.
 
19
##
 
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/>.
 
22
##
 
23
 
 
24
import stringprep
 
25
import unicodedata
 
26
from encodings import idna
 
27
 
 
28
class ILookupTable:
 
29
    """
 
30
    Interface for character lookup classes
 
31
    """
 
32
 
 
33
    def lookup(self, c):
 
34
        """
 
35
        Return whether character is in this table
 
36
        """
 
37
        pass
 
38
 
 
39
class IMappingTable:
 
40
    """
 
41
    Interface for character mapping classes
 
42
    """
 
43
 
 
44
    def map(self, c):
 
45
        """
 
46
        Return mapping for character
 
47
        """
 
48
        pass
 
49
 
 
50
class LookupTableFromFunction:
 
51
 
 
52
    __implements__ = ILookupTable
 
53
 
 
54
    def __init__(self, in_table_function):
 
55
        self.lookup = in_table_function
 
56
 
 
57
class LookupTable:
 
58
 
 
59
    __implements__ = ILookupTable
 
60
 
 
61
    def __init__(self, table):
 
62
        self._table = table
 
63
 
 
64
    def lookup(self, c):
 
65
        return c in self._table
 
66
 
 
67
class MappingTableFromFunction:
 
68
 
 
69
    __implements__ = IMappingTable
 
70
 
 
71
    def __init__(self, map_table_function):
 
72
        self.map = map_table_function
 
73
 
 
74
class EmptyMappingTable:
 
75
 
 
76
    __implements__ = IMappingTable
 
77
 
 
78
    def __init__(self, in_table_function):
 
79
        self._in_table_function = in_table_function
 
80
 
 
81
    def map(self, c):
 
82
        if self._in_table_function(c):
 
83
            return None
 
84
        else:
 
85
            return c
 
86
 
 
87
class Profile:
 
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
 
95
 
 
96
    def prepare(self, string):
 
97
        result = self.map(string)
 
98
        if self.normalize:
 
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)
 
105
        return result
 
106
 
 
107
    def map(self, string):
 
108
        result = []
 
109
 
 
110
        for c in string:
 
111
            result_c = c
 
112
 
 
113
            for mapping in self.mappings:
 
114
                result_c = mapping.map(c)
 
115
                if result_c != c:
 
116
                    break
 
117
 
 
118
            if result_c is not None:
 
119
                result.append(result_c)
 
120
 
 
121
        return "".join(result)
 
122
 
 
123
    def check_prohibiteds(self, string):
 
124
        for c in string:
 
125
            for table in self.prohibiteds:
 
126
                if table.lookup(c):
 
127
                    raise UnicodeError("Invalid character %s" % repr(c))
 
128
 
 
129
    def check_unassigneds(self, string):
 
130
        for c in string:
 
131
            if stringprep.in_table_a1(c):
 
132
                raise UnicodeError("Unassigned code point %s" % repr(c))
 
133
 
 
134
    def check_bidirectionals(self, string):
 
135
        found_LCat = False
 
136
        found_RandALCat = False
 
137
 
 
138
        for c in string:
 
139
            if stringprep.in_table_d1(c):
 
140
                found_RandALCat = True
 
141
            if stringprep.in_table_d2(c):
 
142
                found_LCat = True
 
143
 
 
144
        if found_LCat and found_RandALCat:
 
145
            raise UnicodeError("Violation of BIDI Requirement 2")
 
146
 
 
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")
 
150
 
 
151
 
 
152
class NamePrep:
 
153
    """
 
154
    Implements preparation of internationalized domain names
 
155
 
 
156
    This class implements preparing internationalized domain names using the
 
157
    rules defined in RFC 3491, section 4 (Conversion operations).
 
158
 
 
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.
 
164
 
 
165
    The following steps are performed by C{prepare()}:
 
166
 
 
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).
 
172
    """
 
173
 
 
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)) ]
 
180
 
 
181
    def prepare(self, string):
 
182
        result = []
 
183
 
 
184
        labels = idna.dots.split(string)
 
185
 
 
186
        if labels and len(labels[-1]) == 0:
 
187
            trailing_dot = '.'
 
188
            del labels[-1]
 
189
        else:
 
190
            trailing_dot = ''
 
191
 
 
192
        for label in labels:
 
193
            result.append(self.nameprep(label))
 
194
 
 
195
        return ".".join(result)+trailing_dot
 
196
 
 
197
    def check_prohibiteds(self, string):
 
198
        for c in string:
 
199
            if c in self.prohibiteds:
 
200
                raise UnicodeError("Invalid character %s" % repr(c))
 
201
 
 
202
    def nameprep(self, label):
 
203
        label = idna.nameprep(label)
 
204
        self.check_prohibiteds(label)
 
205
        if len(label) == 0:
 
206
            raise UnicodeError("Invalid empty name")
 
207
        if label[0] == '-':
 
208
            raise UnicodeError("Invalid leading hyphen-minus")
 
209
        if label[-1] == '-':
 
210
            raise UnicodeError("Invalid trailing hyphen-minus")
 
211
        return label
 
212
 
 
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)
 
224
 
 
225
B_1 = EmptyMappingTable(stringprep.in_table_b1)
 
226
B_2 = MappingTableFromFunction(stringprep.map_table_b2)
 
227
 
 
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
                                                                                                                ':', '<', '>', '@'])])
 
233
 
 
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])
 
237
 
 
238
nameprep = NamePrep()