home *** CD-ROM | disk | FTP | other *** search
/ PC World 2005 June / PCWorld_2005-06_cd.bin / software / vyzkuste / firewally / firewally.exe / framework-2.3.exe / idna.py < prev    next >
Text File  |  2003-12-30  |  6KB  |  200 lines

  1. # This module implements the RFCs 3490 (IDNA) and 3491 (Nameprep)
  2.  
  3. import stringprep, unicodedata, re, codecs
  4.  
  5. # IDNA section 3.1
  6. dots = re.compile(u"[\u002E\u3002\uFF0E\uFF61]")
  7.  
  8. # IDNA section 5
  9. ace_prefix = "xn--"
  10. uace_prefix = unicode(ace_prefix, "ascii")
  11.  
  12. # This assumes query strings, so AllowUnassigned is true
  13. def nameprep(label):
  14.     # Map
  15.     newlabel = []
  16.     for c in label:
  17.         if stringprep.in_table_b1(c):
  18.             # Map to nothing
  19.             continue
  20.         newlabel.append(stringprep.map_table_b2(c))
  21.     label = u"".join(newlabel)
  22.  
  23.     # Normalize
  24.     label = unicodedata.normalize("NFKC", label)
  25.  
  26.     # Prohibit
  27.     for c in label:
  28.         if stringprep.in_table_c12(c) or \
  29.            stringprep.in_table_c22(c) or \
  30.            stringprep.in_table_c3(c) or \
  31.            stringprep.in_table_c4(c) or \
  32.            stringprep.in_table_c5(c) or \
  33.            stringprep.in_table_c6(c) or \
  34.            stringprep.in_table_c7(c) or \
  35.            stringprep.in_table_c8(c) or \
  36.            stringprep.in_table_c9(c):
  37.             raise UnicodeError, "Invalid character %s" % repr(c)
  38.  
  39.     # Check bidi
  40.     RandAL = map(stringprep.in_table_d1, label)
  41.     for c in RandAL:
  42.         if c:
  43.             # There is a RandAL char in the string. Must perform further
  44.             # tests:
  45.             # 1) The characters in section 5.8 MUST be prohibited.
  46.             # This is table C.8, which was already checked
  47.             # 2) If a string contains any RandALCat character, the string
  48.             # MUST NOT contain any LCat character.
  49.             if filter(stringprep.in_table_d2, label):
  50.                 raise UnicodeError, "Violation of BIDI requirement 2"
  51.  
  52.             # 3) If a string contains any RandALCat character, a
  53.             # RandALCat character MUST be the first character of the
  54.             # string, and a RandALCat character MUST be the last
  55.             # character of the string.
  56.             if not RandAL[0] or not RandAL[-1]:
  57.                 raise UnicodeError, "Violation of BIDI requirement 3"
  58.  
  59.     return label
  60.  
  61. def ToASCII(label):
  62.     try:
  63.         # Step 1: try ASCII
  64.         label = label.encode("ascii")
  65.     except UnicodeError:
  66.         pass
  67.     else:
  68.         # Skip to step 3: UseSTD3ASCIIRules is false, so
  69.         # Skip to step 8.
  70.         if 0 < len(label) < 64:
  71.             return label
  72.         raise UnicodeError, "label too long"
  73.  
  74.     # Step 2: nameprep
  75.     label = nameprep(label)
  76.  
  77.     # Step 3: UseSTD3ASCIIRules is false
  78.     # Step 4: try ASCII
  79.     try:
  80.         label = label.encode("ascii")
  81.     except UnicodeError:
  82.         pass
  83.     else:
  84.         # Skip to step 8.
  85.         if 0 < len(label) < 64:
  86.             return label
  87.         raise UnicodeError, "label too long"
  88.  
  89.     # Step 5: Check ACE prefix
  90.     if label.startswith(uace_prefix):
  91.         raise UnicodeError, "Label starts with ACE prefix"
  92.  
  93.     # Step 6: Encode with PUNYCODE
  94.     label = label.encode("punycode")
  95.  
  96.     # Step 7: Prepend ACE prefix
  97.     label = ace_prefix + label
  98.  
  99.     # Step 8: Check size
  100.     if 0 < len(label) < 64:
  101.         return label
  102.     raise UnicodeError, "label too long"
  103.  
  104. def ToUnicode(label):
  105.     # Step 1: Check for ASCII
  106.     if isinstance(label, str):
  107.         pure_ascii = True
  108.     else:
  109.         try:
  110.             label = label.encode("ascii")
  111.             pure_ascii = True
  112.         except UnicodeError:
  113.             pure_ascii = False
  114.     if not pure_ascii:
  115.         # Step 2: Perform nameprep
  116.         label = nameprep(label)
  117.         # It doesn't say this, but apparently, it should be ASCII now
  118.         try:
  119.             label = label.encode("ascii")
  120.         except UnicodeError:
  121.             raise UnicodeError, "Invalid character in IDN label"
  122.     # Step 3: Check for ACE prefix
  123.     if not label.startswith(ace_prefix):
  124.         return unicode(label, "ascii")
  125.  
  126.     # Step 4: Remove ACE prefix
  127.     label1 = label[len(ace_prefix):]
  128.  
  129.     # Step 5: Decode using PUNYCODE
  130.     result = label1.decode("punycode")
  131.  
  132.     # Step 6: Apply ToASCII
  133.     label2 = ToASCII(result)
  134.  
  135.     # Step 7: Compare the result of step 6 with the one of step 3
  136.     # label2 will already be in lower case.
  137.     if label.lower() != label2:
  138.         raise UnicodeError, ("IDNA does not round-trip", label, label2)
  139.  
  140.     # Step 8: return the result of step 5
  141.     return result
  142.  
  143. ### Codec APIs
  144.  
  145. class Codec(codecs.Codec):
  146.     def encode(self,input,errors='strict'):
  147.  
  148.         if errors != 'strict':
  149.             # IDNA is quite clear that implementations must be strict
  150.             raise UnicodeError, "unsupported error handling "+errors
  151.  
  152.         result = []
  153.         labels = dots.split(input)
  154.         if labels and len(labels[-1])==0:
  155.             trailing_dot = '.'
  156.             del labels[-1]
  157.         else:
  158.             trailing_dot = ''
  159.         for label in labels:
  160.             result.append(ToASCII(label))
  161.         # Join with U+002E
  162.         return ".".join(result)+trailing_dot, len(input)
  163.  
  164.     def decode(self,input,errors='strict'):
  165.  
  166.         if errors != 'strict':
  167.             raise UnicodeError, "Unsupported error handling "+errors
  168.  
  169.         # IDNA allows decoding to operate on Unicode strings, too.
  170.         if isinstance(input, unicode):
  171.             labels = dots.split(input)
  172.         else:
  173.             # Must be ASCII string
  174.             unicode(input, "ascii")
  175.             labels = input.split(".")
  176.  
  177.         if labels and len(labels[-1]) == 0:
  178.             trailing_dot = u'.'
  179.             del labels[-1]
  180.         else:
  181.             trailing_dot = u''
  182.  
  183.         result = []
  184.         for label in labels:
  185.             result.append(ToUnicode(label))
  186.  
  187.         return u".".join(result)+trailing_dot, len(input)
  188.  
  189. class StreamWriter(Codec,codecs.StreamWriter):
  190.     pass
  191.  
  192. class StreamReader(Codec,codecs.StreamReader):
  193.     pass
  194.  
  195. ### encodings module API
  196.  
  197. def getregentry():
  198.  
  199.     return (Codec().encode,Codec().decode,StreamReader,StreamWriter)
  200.