You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

quopri.py 4.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. #! /usr/bin/env python
  2. """Conversions to/from quoted-printable transport encoding as per RFC-1521."""
  3. # (Dec 1991 version).
  4. __all__ = ["encode","decode"]
  5. ESCAPE = '='
  6. MAXLINESIZE = 76
  7. HEX = '0123456789ABCDEF'
  8. def needsquoting(c, quotetabs):
  9. """Decide whether a particular character needs to be quoted.
  10. The 'quotetabs' flag indicates whether tabs should be quoted."""
  11. if c == '\t':
  12. return not quotetabs
  13. return c == ESCAPE or not(' ' <= c <= '~')
  14. def quote(c):
  15. """Quote a single character."""
  16. i = ord(c)
  17. return ESCAPE + HEX[i/16] + HEX[i%16]
  18. def encode(input, output, quotetabs):
  19. """Read 'input', apply quoted-printable encoding, and write to 'output'.
  20. 'input' and 'output' are files with readline() and write() methods.
  21. The 'quotetabs' flag indicates whether tabs should be quoted.
  22. """
  23. while 1:
  24. line = input.readline()
  25. if not line:
  26. break
  27. new = ''
  28. last = line[-1:]
  29. if last == '\n':
  30. line = line[:-1]
  31. else:
  32. last = ''
  33. prev = ''
  34. for c in line:
  35. if needsquoting(c, quotetabs):
  36. c = quote(c)
  37. if len(new) + len(c) >= MAXLINESIZE:
  38. output.write(new + ESCAPE + '\n')
  39. new = ''
  40. new = new + c
  41. prev = c
  42. if prev in (' ', '\t'):
  43. output.write(new + ESCAPE + '\n\n')
  44. else:
  45. output.write(new + '\n')
  46. def decode(input, output):
  47. """Read 'input', apply quoted-printable decoding, and write to 'output'.
  48. 'input' and 'output' are files with readline() and write() methods."""
  49. new = ''
  50. while 1:
  51. line = input.readline()
  52. if not line: break
  53. i, n = 0, len(line)
  54. if n > 0 and line[n-1] == '\n':
  55. partial = 0; n = n-1
  56. # Strip trailing whitespace
  57. while n > 0 and line[n-1] in " \t\r":
  58. n = n-1
  59. else:
  60. partial = 1
  61. while i < n:
  62. c = line[i]
  63. if c != ESCAPE:
  64. new = new + c; i = i+1
  65. elif i+1 == n and not partial:
  66. partial = 1; break
  67. elif i+1 < n and line[i+1] == ESCAPE:
  68. new = new + ESCAPE; i = i+2
  69. elif i+2 < n and ishex(line[i+1]) and ishex(line[i+2]):
  70. new = new + chr(unhex(line[i+1:i+3])); i = i+3
  71. else: # Bad escape sequence -- leave it in
  72. new = new + c; i = i+1
  73. if not partial:
  74. output.write(new + '\n')
  75. new = ''
  76. if new:
  77. output.write(new)
  78. def ishex(c):
  79. """Return true if the character 'c' is a hexadecimal digit."""
  80. return '0' <= c <= '9' or 'a' <= c <= 'f' or 'A' <= c <= 'F'
  81. def unhex(s):
  82. """Get the integer value of a hexadecimal number."""
  83. bits = 0
  84. for c in s:
  85. if '0' <= c <= '9':
  86. i = ord('0')
  87. elif 'a' <= c <= 'f':
  88. i = ord('a')-10
  89. elif 'A' <= c <= 'F':
  90. i = ord('A')-10
  91. else:
  92. break
  93. bits = bits*16 + (ord(c) - i)
  94. return bits
  95. def test():
  96. import sys
  97. import getopt
  98. try:
  99. opts, args = getopt.getopt(sys.argv[1:], 'td')
  100. except getopt.error, msg:
  101. sys.stdout = sys.stderr
  102. print msg
  103. print "usage: quopri [-t | -d] [file] ..."
  104. print "-t: quote tabs"
  105. print "-d: decode; default encode"
  106. sys.exit(2)
  107. deco = 0
  108. tabs = 0
  109. for o, a in opts:
  110. if o == '-t': tabs = 1
  111. if o == '-d': deco = 1
  112. if tabs and deco:
  113. sys.stdout = sys.stderr
  114. print "-t and -d are mutually exclusive"
  115. sys.exit(2)
  116. if not args: args = ['-']
  117. sts = 0
  118. for file in args:
  119. if file == '-':
  120. fp = sys.stdin
  121. else:
  122. try:
  123. fp = open(file)
  124. except IOError, msg:
  125. sys.stderr.write("%s: can't open (%s)\n" % (file, msg))
  126. sts = 1
  127. continue
  128. if deco:
  129. decode(fp, sys.stdout)
  130. else:
  131. encode(fp, sys.stdout, tabs)
  132. if fp is not sys.stdin:
  133. fp.close()
  134. if sts:
  135. sys.exit(sts)
  136. if __name__ == '__main__':
  137. test()