1#!/usr/bin/env python
2#
3# This file is part of pyasn1 software.
4#
5# Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com>
6# License: http://snmplabs.com/pyasn1/license.html
7#
8import sys
9
10from pyasn1 import error
11from pyasn1.type import tag
12from pyasn1.type import univ
13
14__all__ = ['NumericString', 'PrintableString', 'TeletexString', 'T61String', 'VideotexString',
15           'IA5String', 'GraphicString', 'VisibleString', 'ISO646String',
16           'GeneralString', 'UniversalString', 'BMPString', 'UTF8String']
17
18NoValue = univ.NoValue
19noValue = univ.noValue
20
21
22class AbstractCharacterString(univ.OctetString):
23    """Creates |ASN.1| schema or value object.
24
25    |ASN.1| objects are immutable and duck-type Python 2 :class:`unicode` or Python 3 :class:`str`.
26    When used in octet-stream context, |ASN.1| type assumes "|encoding|" encoding.
27
28    Keyword Args
29    ------------
30    value: :class:`unicode`, :class:`str`, :class:`bytes` or |ASN.1| object
31        unicode object (Python 2) or string (Python 3), alternatively string
32        (Python 2) or bytes (Python 3) representing octet-stream of serialised
33        unicode string (note `encoding` parameter) or |ASN.1| class instance.
34
35    tagSet: :py:class:`~pyasn1.type.tag.TagSet`
36        Object representing non-default ASN.1 tag(s)
37
38    subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
39        Object representing non-default ASN.1 subtype constraint(s)
40
41    encoding: :py:class:`str`
42        Unicode codec ID to encode/decode :class:`unicode` (Python 2) or
43        :class:`str` (Python 3) the payload when |ASN.1| object is used
44        in octet-stream context.
45
46    Raises
47    ------
48    :py:class:`~pyasn1.error.PyAsn1Error`
49        On constraint violation or bad initializer.
50    """
51
52    if sys.version_info[0] <= 2:
53        def __str__(self):
54            try:
55                # `str` is Py2 text representation
56                return self._value.encode(self.encoding)
57
58            except UnicodeEncodeError:
59                raise error.PyAsn1Error(
60                    "Can't encode string '%s' with codec %s" % (self._value, self.encoding)
61                )
62
63        def __unicode__(self):
64            return unicode(self._value)
65
66        def prettyIn(self, value):
67            try:
68                if isinstance(value, unicode):
69                    return value
70                elif isinstance(value, str):
71                    return value.decode(self.encoding)
72                elif isinstance(value, (tuple, list)):
73                    return self.prettyIn(''.join([chr(x) for x in value]))
74                elif isinstance(value, univ.OctetString):
75                    return value.asOctets().decode(self.encoding)
76                else:
77                    return unicode(value)
78
79            except (UnicodeDecodeError, LookupError):
80                raise error.PyAsn1Error(
81                    "Can't decode string '%s' with codec %s" % (value, self.encoding)
82                )
83
84        def asOctets(self, padding=True):
85            return str(self)
86
87        def asNumbers(self, padding=True):
88            return tuple([ord(x) for x in str(self)])
89
90    else:
91        def __str__(self):
92            # `unicode` is Py3 text representation
93            return str(self._value)
94
95        def __bytes__(self):
96            try:
97                return self._value.encode(self.encoding)
98            except UnicodeEncodeError:
99                raise error.PyAsn1Error(
100                    "Can't encode string '%s' with codec %s" % (self._value, self.encoding)
101                )
102
103        def prettyIn(self, value):
104            try:
105                if isinstance(value, str):
106                    return value
107                elif isinstance(value, bytes):
108                    return value.decode(self.encoding)
109                elif isinstance(value, (tuple, list)):
110                    return self.prettyIn(bytes(value))
111                elif isinstance(value, univ.OctetString):
112                    return value.asOctets().decode(self.encoding)
113                else:
114                    return str(value)
115
116            except (UnicodeDecodeError, LookupError):
117                raise error.PyAsn1Error(
118                    "Can't decode string '%s' with codec %s" % (value, self.encoding)
119                )
120
121        def asOctets(self, padding=True):
122            return bytes(self)
123
124        def asNumbers(self, padding=True):
125            return tuple(bytes(self))
126
127    #
128    # See OctetString.prettyPrint() for the explanation
129    #
130
131    def prettyOut(self, value):
132        return value
133
134    def prettyPrint(self, scope=0):
135        # first see if subclass has its own .prettyOut()
136        value = self.prettyOut(self._value)
137
138        if value is not self._value:
139            return value
140
141        return AbstractCharacterString.__str__(self)
142
143    def __reversed__(self):
144        return reversed(self._value)
145
146
147class NumericString(AbstractCharacterString):
148    __doc__ = AbstractCharacterString.__doc__
149
150    #: Set (on class, not on instance) or return a
151    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
152    #: associated with |ASN.1| type.
153    tagSet = AbstractCharacterString.tagSet.tagImplicitly(
154        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 18)
155    )
156    encoding = 'us-ascii'
157
158    # Optimization for faster codec lookup
159    typeId = AbstractCharacterString.getTypeId()
160
161
162class PrintableString(AbstractCharacterString):
163    __doc__ = AbstractCharacterString.__doc__
164
165    #: Set (on class, not on instance) or return a
166    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
167    #: associated with |ASN.1| type.
168    tagSet = AbstractCharacterString.tagSet.tagImplicitly(
169        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 19)
170    )
171    encoding = 'us-ascii'
172
173    # Optimization for faster codec lookup
174    typeId = AbstractCharacterString.getTypeId()
175
176
177class TeletexString(AbstractCharacterString):
178    __doc__ = AbstractCharacterString.__doc__
179
180    #: Set (on class, not on instance) or return a
181    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
182    #: associated with |ASN.1| type.
183    tagSet = AbstractCharacterString.tagSet.tagImplicitly(
184        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 20)
185    )
186    encoding = 'iso-8859-1'
187
188    # Optimization for faster codec lookup
189    typeId = AbstractCharacterString.getTypeId()
190
191
192class T61String(TeletexString):
193    __doc__ = TeletexString.__doc__
194
195    # Optimization for faster codec lookup
196    typeId = AbstractCharacterString.getTypeId()
197
198
199class VideotexString(AbstractCharacterString):
200    __doc__ = AbstractCharacterString.__doc__
201
202    #: Set (on class, not on instance) or return a
203    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
204    #: associated with |ASN.1| type.
205    tagSet = AbstractCharacterString.tagSet.tagImplicitly(
206        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 21)
207    )
208    encoding = 'iso-8859-1'
209
210    # Optimization for faster codec lookup
211    typeId = AbstractCharacterString.getTypeId()
212
213
214class IA5String(AbstractCharacterString):
215    __doc__ = AbstractCharacterString.__doc__
216
217    #: Set (on class, not on instance) or return a
218    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
219    #: associated with |ASN.1| type.
220    tagSet = AbstractCharacterString.tagSet.tagImplicitly(
221        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 22)
222    )
223    encoding = 'us-ascii'
224
225    # Optimization for faster codec lookup
226    typeId = AbstractCharacterString.getTypeId()
227
228
229class GraphicString(AbstractCharacterString):
230    __doc__ = AbstractCharacterString.__doc__
231
232    #: Set (on class, not on instance) or return a
233    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
234    #: associated with |ASN.1| type.
235    tagSet = AbstractCharacterString.tagSet.tagImplicitly(
236        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 25)
237    )
238    encoding = 'iso-8859-1'
239
240    # Optimization for faster codec lookup
241    typeId = AbstractCharacterString.getTypeId()
242
243
244class VisibleString(AbstractCharacterString):
245    __doc__ = AbstractCharacterString.__doc__
246
247    #: Set (on class, not on instance) or return a
248    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
249    #: associated with |ASN.1| type.
250    tagSet = AbstractCharacterString.tagSet.tagImplicitly(
251        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 26)
252    )
253    encoding = 'us-ascii'
254
255    # Optimization for faster codec lookup
256    typeId = AbstractCharacterString.getTypeId()
257
258
259class ISO646String(VisibleString):
260    __doc__ = VisibleString.__doc__
261
262    # Optimization for faster codec lookup
263    typeId = AbstractCharacterString.getTypeId()
264
265class GeneralString(AbstractCharacterString):
266    __doc__ = AbstractCharacterString.__doc__
267
268    #: Set (on class, not on instance) or return a
269    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
270    #: associated with |ASN.1| type.
271    tagSet = AbstractCharacterString.tagSet.tagImplicitly(
272        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 27)
273    )
274    encoding = 'iso-8859-1'
275
276    # Optimization for faster codec lookup
277    typeId = AbstractCharacterString.getTypeId()
278
279
280class UniversalString(AbstractCharacterString):
281    __doc__ = AbstractCharacterString.__doc__
282
283    #: Set (on class, not on instance) or return a
284    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
285    #: associated with |ASN.1| type.
286    tagSet = AbstractCharacterString.tagSet.tagImplicitly(
287        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 28)
288    )
289    encoding = "utf-32-be"
290
291    # Optimization for faster codec lookup
292    typeId = AbstractCharacterString.getTypeId()
293
294
295class BMPString(AbstractCharacterString):
296    __doc__ = AbstractCharacterString.__doc__
297
298    #: Set (on class, not on instance) or return a
299    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
300    #: associated with |ASN.1| type.
301    tagSet = AbstractCharacterString.tagSet.tagImplicitly(
302        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 30)
303    )
304    encoding = "utf-16-be"
305
306    # Optimization for faster codec lookup
307    typeId = AbstractCharacterString.getTypeId()
308
309
310class UTF8String(AbstractCharacterString):
311    __doc__ = AbstractCharacterString.__doc__
312
313    #: Set (on class, not on instance) or return a
314    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
315    #: associated with |ASN.1| type.
316    tagSet = AbstractCharacterString.tagSet.tagImplicitly(
317        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 12)
318    )
319    encoding = "utf-8"
320
321    # Optimization for faster codec lookup
322    typeId = AbstractCharacterString.getTypeId()
323