Package pyxmpp :: Module xmppstringprep |
[hide private]

# Source Code for Module pyxmpp.xmppstringprep

      1  #
      2  # (C) Copyright 2003-2006 Jacek Konieczny <jajcus@jajcus.net>
      3  #
      4  # This program is free software; you can redistribute it and/or modify
      5  # it under the terms of the GNU Lesser General Public License Version
      6  # 2.1 as published by the Free Software Foundation.
      7  #
      8  # This program is distributed in the hope that it will be useful,
      9  # but WITHOUT ANY WARRANTY; without even the implied warranty of
     10  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     11  # GNU Lesser General Public License for more details.
     12  #
     13  # You should have received a copy of the GNU Lesser General Public
     14  # License along with this program; if not, write to the Free Software
     15  # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
     16  #
     17  # pylint treats "import stringprep" like depreciated "import string"
     18  # pylint: disable-msg=W0402
     19
     20  """Nodeprep and resourceprep stringprep profiles.
     21
     22  Normative reference:
     23    - `RFC 3920 <http://www.ietf.org/rfc/rfc3920.txt>`__
     24  """
     25
     26  __revision__="$Id: xmppstringprep.py,v 1.16 2004/10/07 22:28:04 jajcus Exp $"
     27  __docformat__="restructuredtext en"
     28
     29  import stringprep
     30  import unicodedata
     31  from pyxmpp.exceptions import StringprepError
     32
     33 -class LookupFunction:
     34      """Class for looking up RFC 3454 tables using function.
     35
     36      :Ivariables:
     37          - `lookup`: the lookup function."""
     38 -    def __init__(self,function):
     39          """Initialize `LookupFunction` object.
     40
     41          :Parameters:
     42              - `function`: function taking character code as input and returning
     43                `bool` value or the mapped for `code`."""
     44          self.lookup=function
     45
     46 -class LookupTable:
     47      """Class for looking up RFC 3454 tables using a dictionary and/or list of ranges."""
     48 -    def __init__(self,singles,ranges):
     49          """Initialize `LookupTable` object.
     50
     51          :Parameters:
     52              - `singles`: dictionary mapping Unicode characters into other Unicode characters.
     53              - `ranges`: list of ``((start,end),value)`` tuples mapping codes in range (start,end)
     54                to the value."""
     55          self.singles=singles
     56          self.ranges=ranges
     57
     58 -    def lookup(self,c):
     59          """Do Unicode character lookup.
     60
     61          :Parameters:
     62              - `c`: Unicode character to look up.
     63
     64          :return: the mapped value."""
     65          if self.singles.has_key(c):
     66              return self.singles[c]
     67          c=ord(c)
     68          for (start,end),value in self.ranges:
     69              if c<start:
     70                  return None
     71              if c<=end:
     72                  return value
     73          return None
     74
     75  A_1=LookupFunction(stringprep.in_table_a1)
     76
     77 -def b1_mapping(uc):
     78      """Do RFC 3454 B.1 table mapping.
     79
     80      :Parameters:
     81          - `uc`: Unicode character to map.
     82
     83      :returns: u"" if there is `uc` code in the table, `None` otherwise."""
     84      if stringprep.in_table_b1(uc):
     85          return u""
     86      else:
     87          return None
     88
     89  B_1=LookupFunction(b1_mapping)
     90  B_2=LookupFunction(stringprep.map_table_b2)
     91  B_3=LookupFunction(stringprep.map_table_b3)
     92  C_1_1=LookupFunction(stringprep.in_table_c11)
     93  C_1_2=LookupFunction(stringprep.in_table_c12)
     94  C_2_1=LookupFunction(stringprep.in_table_c21)
     95  C_2_2=LookupFunction(stringprep.in_table_c22)
     96  C_3=LookupFunction(stringprep.in_table_c3)
     97  C_4=LookupFunction(stringprep.in_table_c4)
     98  C_5=LookupFunction(stringprep.in_table_c5)
     99  C_6=LookupFunction(stringprep.in_table_c6)
    100  C_7=LookupFunction(stringprep.in_table_c7)
    101  C_8=LookupFunction(stringprep.in_table_c8)
    102  C_9=LookupFunction(stringprep.in_table_c9)
    103  D_1=LookupFunction(stringprep.in_table_d1)
    104  D_2=LookupFunction(stringprep.in_table_d2)
    105
    106 -def nfkc(data):
    107      """Do NFKC normalization of Unicode data.
    108
    109      :Parameters:
    110          - `data`: list of Unicode characters or Unicode string.
    111
    112      :return: normalized Unicode string."""
    113      if type(data) is list:
    114          data=u"".join(data)
    115      return unicodedata.normalize("NFKC",data)
    116
    117 -class Profile:
    118      """Base class for stringprep profiles."""
    119      cache_items=[]
    120 -    def __init__(self,unassigned,mapping,normalization,prohibited,bidi=1):
    121          """Initialize Profile object.
    122
    123          :Parameters:
    124              - `unassigned`: the lookup table with unassigned codes
    125              - `mapping`: the lookup table with character mappings
    126              - `normalization`: the normalization function
    127              - `prohibited`: the lookup table with prohibited characters
    128              - `bidi`: if True then bidirectional checks should be done
    129          """
    130          self.unassigned=unassigned
    131          self.mapping=mapping
    132          self.normalization=normalization
    133          self.prohibited=prohibited
    134          self.bidi=bidi
    135          self.cache={}
    136
    137 -    def prepare(self,data):
    138          """Complete string preparation procedure for 'stored' strings.
    139          (includes checks for unassigned codes)
    140
    141          :Parameters:
    142              - `data`: Unicode string to prepare.
    143
    144          :return: prepared string
    145
    146          :raise StringprepError: if the preparation fails
    147          """
    148          r=self.cache.get(data)
    149          if r is not None:
    150              return r
    151          s=self.map(data)
    152          if self.normalization:
    153              s=self.normalization(s)
    154          s=self.prohibit(s)
    155          s=self.check_unassigned(s)
    156          if self.bidi:
    157              s=self.check_bidi(s)
    158          if type(s) is list:
    159              s=u"".string.join()
    160          if len(self.cache_items)>=stringprep_cache_size:
    161              remove=self.cache_items[:-stringprep_cache_size/2]
    162              for profile,key in remove:
    163                  try:
    164                      del profile.cache[key]
    165                  except KeyError:
    166                      pass
    167              self.cache_items[:]=self.cache_items[-stringprep_cache_size/2:]
    168          self.cache_items.append((self,data))
    169          self.cache[data]=s
    170          return s
    171
    172 -    def prepare_query(self,s):
    173          """Complete string preparation procedure for 'query' strings.
    174          (without checks for unassigned codes)
    175
    176          :Parameters:
    177              - `s`: Unicode string to prepare.
    178
    179          :return: prepared string
    180
    181          :raise StringprepError: if the preparation fails
    182          """
    183
    184          s=self.map(s)
    185          if self.normalization:
    186              s=self.normalization(s)
    187          s=self.prohibit(s)
    188          if self.bidi:
    189              s=self.check_bidi(s)
    190          if type(s) is list:
    191              s=u"".string.join(s)
    192          return s
    193
    194 -    def map(self,s):
    195          """Mapping part of string preparation."""
    196          r=[]
    197          for c in s:
    198              rc=None
    199              for t in self.mapping:
    200                  rc=t.lookup(c)
    201                  if rc is not None:
    202                      break
    203              if rc is not None:
    204                  r.append(rc)
    205              else:
    206                  r.append(c)
    207          return r
    208
    209 -    def prohibit(self,s):
    210          """Checks for prohibited characters."""
    211          for c in s:
    212              for t in self.prohibited:
    213                  if t.lookup(c):
    214                      raise StringprepError,"Prohibited character: %r" % (c,)
    215          return s
    216
    217 -    def check_unassigned(self,s):
    218          """Checks for unassigned character codes."""
    219          for c in s:
    220              for t in self.unassigned:
    221                  if t.lookup(c):
    222                      raise StringprepError,"Unassigned character: %r" % (c,)
    223          return s
    224
    225 -    def check_bidi(self,s):
    226          """Checks if sting is valid for bidirectional printing."""
    227          has_l=0
    228          has_ral=0
    229          for c in s:
    230              if D_1.lookup(c):
    231                  has_l=1
    232              elif D_2.lookup(c):
    233                  has_l=1
    234          if has_l and has_ral:
    235              raise StringprepError,"Both RandALCat and LCat characters present"
    236          if has_l and (D_1.lookup(s[0]) is None or D_1.lookup(s[-1]) is None):
    237              raise StringprepError,"The first and the last character must be RandALCat"
    238          return s
    239
    240  nodeprep=Profile(
    241      unassigned=(A_1,),
    242      mapping=(B_1,B_2),
    243      normalization=nfkc,
    244      prohibited=(C_1_1,C_1_2,C_2_1,C_2_2,C_3,C_4,C_5,C_6,C_7,C_8,C_9,
    245              LookupTable({u'"':True,u'&':True,u"'":True,u"/":True,
    246                      u":":True,u"<":True,u">":True,u"@":True},()) ),
    247      bidi=1)
    248
    249  resourceprep=Profile(
    250      unassigned=(A_1,),
    251      mapping=(B_1,),
    252      normalization=nfkc,
    253      prohibited=(C_1_2,C_2_1,C_2_2,C_3,C_4,C_5,C_6,C_7,C_8,C_9),
    254      bidi=1)
    255
    256  stringprep_cache_size=1000
    257 -def set_stringprep_cache_size(size):
    258      """Modify stringprep cache size.
    259
    260      :Parameters:
    261          - `size`: new cache size"""
    262      global stringprep_cache_size
    263      stringprep_cache_size=size
    264      if len(Profile.cache_items)>size:
    265          remove=Profile.cache_items[:-size]
    266          for profile,key in remove:
    267              try:
    268                  del profile.cache[key]
    269              except KeyError:
    270                  pass
    271          Profile.cache_items=Profile.cache_items[-size:]
    272
    273  # vi: sts=4 et sw=4
    274