home *** CD-ROM | disk | FTP | other *** search
/ Freelog Special Freeware 31 / FreelogHS31.iso / Texte / scribus / scribus-1.3.3.9-win32-install.exe / lib / quopri.py < prev    next >
Text File  |  2002-03-23  |  7KB  |  238 lines

  1. #! /usr/bin/env python
  2.  
  3. """Conversions to/from quoted-printable transport encoding as per RFC 1521."""
  4.  
  5. # (Dec 1991 version).
  6.  
  7. __all__ = ["encode", "decode", "encodestring", "decodestring"]
  8.  
  9. ESCAPE = '='
  10. MAXLINESIZE = 76
  11. HEX = '0123456789ABCDEF'
  12. EMPTYSTRING = ''
  13.  
  14. try:
  15.     from binascii import a2b_qp, b2a_qp
  16. except ImportError:
  17.     a2b_qp = None
  18.     b2a_qp = None
  19.  
  20.  
  21. def needsquoting(c, quotetabs, header):
  22.     """Decide whether a particular character needs to be quoted.
  23.  
  24.     The 'quotetabs' flag indicates whether embedded tabs and spaces should be
  25.     quoted.  Note that line-ending tabs and spaces are always encoded, as per
  26.     RFC 1521.
  27.     """
  28.     if c in ' \t':
  29.         return quotetabs
  30.     # if header, we have to escape _ because _ is used to escape space
  31.     if c == '_':
  32.         return header
  33.     return c == ESCAPE or not (' ' <= c <= '~')
  34.  
  35. def quote(c):
  36.     """Quote a single character."""
  37.     i = ord(c)
  38.     return ESCAPE + HEX[i//16] + HEX[i%16]
  39.  
  40.  
  41.  
  42. def encode(input, output, quotetabs, header = 0):
  43.     """Read 'input', apply quoted-printable encoding, and write to 'output'.
  44.  
  45.     'input' and 'output' are files with readline() and write() methods.
  46.     The 'quotetabs' flag indicates whether embedded tabs and spaces should be
  47.     quoted.  Note that line-ending tabs and spaces are always encoded, as per
  48.     RFC 1521.
  49.     The 'header' flag indicates whether we are encoding spaces as _ as per
  50.     RFC 1522.
  51.     """
  52.  
  53.     if b2a_qp is not None:
  54.         data = input.read()
  55.         odata = b2a_qp(data, quotetabs = quotetabs, header = header)
  56.         output.write(odata)
  57.         return
  58.  
  59.     def write(s, output=output, lineEnd='\n'):
  60.         # RFC 1521 requires that the line ending in a space or tab must have
  61.         # that trailing character encoded.
  62.         if s and s[-1:] in ' \t':
  63.             output.write(s[:-1] + quote(s[-1]) + lineEnd)
  64.         elif s == '.':
  65.             output.write(quote(s) + lineEnd)
  66.         else:
  67.             output.write(s + lineEnd)
  68.  
  69.     prevline = None
  70.     while 1:
  71.         line = input.readline()
  72.         if not line:
  73.             break
  74.         outline = []
  75.         # Strip off any readline induced trailing newline
  76.         stripped = ''
  77.         if line[-1:] == '\n':
  78.             line = line[:-1]
  79.             stripped = '\n'
  80.         # Calculate the un-length-limited encoded line
  81.         for c in line:
  82.             if needsquoting(c, quotetabs, header):
  83.                 c = quote(c)
  84.             if header and c == ' ':
  85.                 outline.append('_')
  86.             else:
  87.                 outline.append(c)
  88.         # First, write out the previous line
  89.         if prevline is not None:
  90.             write(prevline)
  91.         # Now see if we need any soft line breaks because of RFC-imposed
  92.         # length limitations.  Then do the thisline->prevline dance.
  93.         thisline = EMPTYSTRING.join(outline)
  94.         while len(thisline) > MAXLINESIZE:
  95.             # Don't forget to include the soft line break `=' sign in the
  96.             # length calculation!
  97.             write(thisline[:MAXLINESIZE-1], lineEnd='=\n')
  98.             thisline = thisline[MAXLINESIZE-1:]
  99.         # Write out the current line
  100.         prevline = thisline
  101.     # Write out the last line, without a trailing newline
  102.     if prevline is not None:
  103.         write(prevline, lineEnd=stripped)
  104.  
  105. def encodestring(s, quotetabs = 0, header = 0):
  106.     if b2a_qp is not None:
  107.         return b2a_qp(s, quotetabs = quotetabs, header = header)
  108.     from cStringIO import StringIO
  109.     infp = StringIO(s)
  110.     outfp = StringIO()
  111.     encode(infp, outfp, quotetabs, header)
  112.     return outfp.getvalue()
  113.  
  114.  
  115.  
  116. def decode(input, output, header = 0):
  117.     """Read 'input', apply quoted-printable decoding, and write to 'output'.
  118.     'input' and 'output' are files with readline() and write() methods.
  119.     If 'header' is true, decode underscore as space (per RFC 1522)."""
  120.  
  121.     if a2b_qp is not None:
  122.         data = input.read()
  123.         odata = a2b_qp(data, header = header)
  124.         output.write(odata)
  125.         return
  126.  
  127.     new = ''
  128.     while 1:
  129.         line = input.readline()
  130.         if not line: break
  131.         i, n = 0, len(line)
  132.         if n > 0 and line[n-1] == '\n':
  133.             partial = 0; n = n-1
  134.             # Strip trailing whitespace
  135.             while n > 0 and line[n-1] in " \t\r":
  136.                 n = n-1
  137.         else:
  138.             partial = 1
  139.         while i < n:
  140.             c = line[i]
  141.             if c == '_' and header:
  142.                 new = new + ' '; i = i+1
  143.             elif c != ESCAPE:
  144.                 new = new + c; i = i+1
  145.             elif i+1 == n and not partial:
  146.                 partial = 1; break
  147.             elif i+1 < n and line[i+1] == ESCAPE:
  148.                 new = new + ESCAPE; i = i+2
  149.             elif i+2 < n and ishex(line[i+1]) and ishex(line[i+2]):
  150.                 new = new + chr(unhex(line[i+1:i+3])); i = i+3
  151.             else: # Bad escape sequence -- leave it in
  152.                 new = new + c; i = i+1
  153.         if not partial:
  154.             output.write(new + '\n')
  155.             new = ''
  156.     if new:
  157.         output.write(new)
  158.  
  159. def decodestring(s, header = 0):
  160.     if a2b_qp is not None:
  161.         return a2b_qp(s, header = header)
  162.     from cStringIO import StringIO
  163.     infp = StringIO(s)
  164.     outfp = StringIO()
  165.     decode(infp, outfp, header = header)
  166.     return outfp.getvalue()
  167.  
  168.  
  169.  
  170. # Other helper functions
  171. def ishex(c):
  172.     """Return true if the character 'c' is a hexadecimal digit."""
  173.     return '0' <= c <= '9' or 'a' <= c <= 'f' or 'A' <= c <= 'F'
  174.  
  175. def unhex(s):
  176.     """Get the integer value of a hexadecimal number."""
  177.     bits = 0
  178.     for c in s:
  179.         if '0' <= c <= '9':
  180.             i = ord('0')
  181.         elif 'a' <= c <= 'f':
  182.             i = ord('a')-10
  183.         elif 'A' <= c <= 'F':
  184.             i = ord('A')-10
  185.         else:
  186.             break
  187.         bits = bits*16 + (ord(c) - i)
  188.     return bits
  189.  
  190.  
  191.  
  192. def main():
  193.     import sys
  194.     import getopt
  195.     try:
  196.         opts, args = getopt.getopt(sys.argv[1:], 'td')
  197.     except getopt.error, msg:
  198.         sys.stdout = sys.stderr
  199.         print msg
  200.         print "usage: quopri [-t | -d] [file] ..."
  201.         print "-t: quote tabs"
  202.         print "-d: decode; default encode"
  203.         sys.exit(2)
  204.     deco = 0
  205.     tabs = 0
  206.     for o, a in opts:
  207.         if o == '-t': tabs = 1
  208.         if o == '-d': deco = 1
  209.     if tabs and deco:
  210.         sys.stdout = sys.stderr
  211.         print "-t and -d are mutually exclusive"
  212.         sys.exit(2)
  213.     if not args: args = ['-']
  214.     sts = 0
  215.     for file in args:
  216.         if file == '-':
  217.             fp = sys.stdin
  218.         else:
  219.             try:
  220.                 fp = open(file)
  221.             except IOError, msg:
  222.                 sys.stderr.write("%s: can't open (%s)\n" % (file, msg))
  223.                 sts = 1
  224.                 continue
  225.         if deco:
  226.             decode(fp, sys.stdout)
  227.         else:
  228.             encode(fp, sys.stdout, tabs)
  229.         if fp is not sys.stdin:
  230.             fp.close()
  231.     if sts:
  232.         sys.exit(sts)
  233.  
  234.  
  235.  
  236. if __name__ == '__main__':
  237.     main()
  238.