diff options
author | Martin v. Löwis <martin@v.loewis.de> | 2001-09-30 20:32:11 (GMT) |
---|---|---|
committer | Martin v. Löwis <martin@v.loewis.de> | 2001-09-30 20:32:11 (GMT) |
commit | 16dc7f44b1116aab58897bc7e94cb972488206fc (patch) | |
tree | b1e90d9ca27e6dbdd0cd6b6d66fcb8a333a746a3 /Lib | |
parent | 5f12d755a82312673c35e8224b2bde7ced159c52 (diff) | |
download | cpython-16dc7f44b1116aab58897bc7e94cb972488206fc.zip cpython-16dc7f44b1116aab58897bc7e94cb972488206fc.tar.gz cpython-16dc7f44b1116aab58897bc7e94cb972488206fc.tar.bz2 |
Patch #462190, patch #464070: Support quoted printable in the binascii module.
Decode and encode underscores for header style encoding. Fixes bug #463996.
Diffstat (limited to 'Lib')
-rwxr-xr-x | Lib/quopri.py | 55 | ||||
-rw-r--r-- | Lib/test/test_quopri.py | 13 |
2 files changed, 57 insertions, 11 deletions
diff --git a/Lib/quopri.py b/Lib/quopri.py index f668abf..0425735 100755 --- a/Lib/quopri.py +++ b/Lib/quopri.py @@ -11,9 +11,14 @@ MAXLINESIZE = 76 HEX = '0123456789ABCDEF' EMPTYSTRING = '' +try: + from binascii import a2b_qp, b2a_qp +except: + a2b_qp = None + b2a_qp = None -def needsquoting(c, quotetabs): +def needsquoting(c, quotetabs, header): """Decide whether a particular character needs to be quoted. The 'quotetabs' flag indicates whether embedded tabs and spaces should be @@ -22,6 +27,9 @@ def needsquoting(c, quotetabs): """ if c in ' \t': return quotetabs + # if header, we have to escape _ because _ is used to escape space + if c == '_': + return header return c == ESCAPE or not (' ' <= c <= '~') def quote(c): @@ -31,14 +39,23 @@ def quote(c): -def encode(input, output, quotetabs): +def encode(input, output, quotetabs, header = 0): """Read 'input', apply quoted-printable encoding, and write to 'output'. 'input' and 'output' are files with readline() and write() methods. The 'quotetabs' flag indicates whether embedded tabs and spaces should be quoted. Note that line-ending tabs and spaces are always encoded, as per RFC 1521. + The 'header' flag indicates whether we are encoding spaces as _ as per + RFC 1522. """ + + if b2a_qp is not None: + data = input.read() + odata = b2a_qp(data, quotetabs = quotetabs, header = header) + output.write(odata) + return + def write(s, output=output, lineEnd='\n'): # RFC 1521 requires that the line ending in a space or tab must have # that trailing character encoded. @@ -60,9 +77,12 @@ def encode(input, output, quotetabs): stripped = '\n' # Calculate the un-length-limited encoded line for c in line: - if needsquoting(c, quotetabs): + if needsquoting(c, quotetabs, header): c = quote(c) - outline.append(c) + if header and c == ' ': + outline.append('_') + else: + outline.append(c) # First, write out the previous line if prevline is not None: write(prevline) @@ -80,19 +100,28 @@ def encode(input, output, quotetabs): if prevline is not None: write(prevline, lineEnd=stripped) -def encodestring(s, quotetabs=0): +def encodestring(s, quotetabs = 0, header = 0): + if b2a_qp is not None: + return b2a_qp(s, quotetabs = quotetabs, header = header) from cStringIO import StringIO infp = StringIO(s) outfp = StringIO() - encode(infp, outfp, quotetabs) + encode(infp, outfp, quotetabs, header) return outfp.getvalue() -def decode(input, output): +def decode(input, output, header = 0): """Read 'input', apply quoted-printable decoding, and write to 'output'. + 'input' and 'output' are files with readline() and write() methods. + If 'header' is true, decode underscore as space (per RFC 1522).""" + + if a2b_qp is not None: + data = input.read() + odata = a2b_qp(data, header = header) + output.write(odata) + return - 'input' and 'output' are files with readline() and write() methods.""" new = '' while 1: line = input.readline() @@ -107,7 +136,9 @@ def decode(input, output): partial = 1 while i < n: c = line[i] - if c != ESCAPE: + if c == '_' and header: + new = new + ' '; i = i+1 + elif c != ESCAPE: new = new + c; i = i+1 elif i+1 == n and not partial: partial = 1; break @@ -123,11 +154,13 @@ def decode(input, output): if new: output.write(new) -def decodestring(s): +def decodestring(s, header = 0): + if a2b_qp is not None: + return a2b_qp(s, header = header) from cStringIO import StringIO infp = StringIO(s) outfp = StringIO() - decode(infp, outfp) + decode(infp, outfp, header = header) return outfp.getvalue() diff --git a/Lib/test/test_quopri.py b/Lib/test/test_quopri.py index 0e99727..2497705 100644 --- a/Lib/test/test_quopri.py +++ b/Lib/test/test_quopri.py @@ -104,6 +104,12 @@ zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz''') ('hello\tworld', 'hello=09world'), ) + # These are used in the "header=1" tests. + HSTRINGS = ( + ('hello world', 'hello_world'), + ('hello_world', 'hello=5Fworld'), + ) + def test_encodestring(self): for p, e in self.STRINGS: self.assert_(encodestring(p) == e) @@ -135,6 +141,13 @@ zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz''') self.assert_(encodestring(p, quotetabs=1) == e) self.assert_(decodestring(e) == p) + def test_encode_header(self): + for p, e in self.HSTRINGS: + self.assert_(encodestring(p, header = 1) == e) + + def test_decode_header(self): + for p, e in self.HSTRINGS: + self.assert_(decodestring(e, header = 1) == p) def test_main(): test_support.run_unittest(QuopriTestCase) |