| 
									
										
										
										
											2010-03-11 22:53:45 +00:00
										 |  |  | #! /usr/bin/env python3 | 
					
						
							| 
									
										
										
										
											1997-07-11 18:39:03 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
											
												Better support for RFC 1521 quoted-printable specification, along with
addition of interface for consistency with base64 module.  Namely,
encodestring(), decodestring(): New functions which accept a string
object and return a string object.  They just wrap the string in
StringIOs and pass them to the encode() and decode() methods
respectively.  encodestring() accepts a default argument of quotetabs,
defaulting to zero, which is passed on straight through to encode().
encode(): Fix the bug where an extra newline would always be added to
the output, which prevented an idempotent roundtrip through
encode->decode.  Now, if the source string doesn't end in a newline,
then the result string won't end in a newline.
Also, extend the quotetabs argument semantics to include quoting
embedded strings, which is also optional according to the RFC.
test() -> main()
"from quopri import *" also imports encodestring() and decodestring().
											
										 
											2001-06-19 19:07:46 +00:00
										 |  |  | """Conversions to/from quoted-printable transport encoding as per RFC 1521.""" | 
					
						
							| 
									
										
										
										
											2000-02-04 15:28:42 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											1995-06-14 23:43:44 +00:00
										 |  |  | # (Dec 1991 version). | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
											
												Better support for RFC 1521 quoted-printable specification, along with
addition of interface for consistency with base64 module.  Namely,
encodestring(), decodestring(): New functions which accept a string
object and return a string object.  They just wrap the string in
StringIOs and pass them to the encode() and decode() methods
respectively.  encodestring() accepts a default argument of quotetabs,
defaulting to zero, which is passed on straight through to encode().
encode(): Fix the bug where an extra newline would always be added to
the output, which prevented an idempotent roundtrip through
encode->decode.  Now, if the source string doesn't end in a newline,
then the result string won't end in a newline.
Also, extend the quotetabs argument semantics to include quoting
embedded strings, which is also optional according to the RFC.
test() -> main()
"from quopri import *" also imports encodestring() and decodestring().
											
										 
											2001-06-19 19:07:46 +00:00
										 |  |  | __all__ = ["encode", "decode", "encodestring", "decodestring"] | 
					
						
							| 
									
										
										
										
											2001-02-12 02:00:42 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-07-28 17:52:25 +00:00
										 |  |  | ESCAPE = b'=' | 
					
						
							| 
									
										
										
										
											1995-06-14 23:43:44 +00:00
										 |  |  | MAXLINESIZE = 76 | 
					
						
							| 
									
										
										
										
											2007-07-28 17:52:25 +00:00
										 |  |  | HEX = b'0123456789ABCDEF' | 
					
						
							|  |  |  | EMPTYSTRING = b'' | 
					
						
							| 
									
										
										
										
											1995-06-14 23:43:44 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-09-30 20:32:11 +00:00
										 |  |  | try: | 
					
						
							| 
									
										
										
										
											2001-10-04 05:36:56 +00:00
										 |  |  |     from binascii import a2b_qp, b2a_qp | 
					
						
							| 
									
										
										
										
											2002-03-23 05:55:18 +00:00
										 |  |  | except ImportError: | 
					
						
							| 
									
										
										
										
											2001-10-04 05:36:56 +00:00
										 |  |  |     a2b_qp = None | 
					
						
							|  |  |  |     b2a_qp = None | 
					
						
							| 
									
										
										
											
												Better support for RFC 1521 quoted-printable specification, along with
addition of interface for consistency with base64 module.  Namely,
encodestring(), decodestring(): New functions which accept a string
object and return a string object.  They just wrap the string in
StringIOs and pass them to the encode() and decode() methods
respectively.  encodestring() accepts a default argument of quotetabs,
defaulting to zero, which is passed on straight through to encode().
encode(): Fix the bug where an extra newline would always be added to
the output, which prevented an idempotent roundtrip through
encode->decode.  Now, if the source string doesn't end in a newline,
then the result string won't end in a newline.
Also, extend the quotetabs argument semantics to include quoting
embedded strings, which is also optional according to the RFC.
test() -> main()
"from quopri import *" also imports encodestring() and decodestring().
											
										 
											2001-06-19 19:07:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-07-02 04:57:30 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-09-30 20:32:11 +00:00
										 |  |  | def needsquoting(c, quotetabs, header): | 
					
						
							| 
									
										
										
										
											2007-07-28 17:52:25 +00:00
										 |  |  |     """Decide whether a particular byte ordinal needs to be quoted.
 | 
					
						
							| 
									
										
										
										
											2000-02-04 15:28:42 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
											
												Better support for RFC 1521 quoted-printable specification, along with
addition of interface for consistency with base64 module.  Namely,
encodestring(), decodestring(): New functions which accept a string
object and return a string object.  They just wrap the string in
StringIOs and pass them to the encode() and decode() methods
respectively.  encodestring() accepts a default argument of quotetabs,
defaulting to zero, which is passed on straight through to encode().
encode(): Fix the bug where an extra newline would always be added to
the output, which prevented an idempotent roundtrip through
encode->decode.  Now, if the source string doesn't end in a newline,
then the result string won't end in a newline.
Also, extend the quotetabs argument semantics to include quoting
embedded strings, which is also optional according to the RFC.
test() -> main()
"from quopri import *" also imports encodestring() and decodestring().
											
										 
											2001-06-19 19:07:46 +00:00
										 |  |  |     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. | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2007-07-28 17:52:25 +00:00
										 |  |  |     assert isinstance(c, bytes) | 
					
						
							|  |  |  |     if c in b' \t': | 
					
						
							| 
									
										
										
											
												Better support for RFC 1521 quoted-printable specification, along with
addition of interface for consistency with base64 module.  Namely,
encodestring(), decodestring(): New functions which accept a string
object and return a string object.  They just wrap the string in
StringIOs and pass them to the encode() and decode() methods
respectively.  encodestring() accepts a default argument of quotetabs,
defaulting to zero, which is passed on straight through to encode().
encode(): Fix the bug where an extra newline would always be added to
the output, which prevented an idempotent roundtrip through
encode->decode.  Now, if the source string doesn't end in a newline,
then the result string won't end in a newline.
Also, extend the quotetabs argument semantics to include quoting
embedded strings, which is also optional according to the RFC.
test() -> main()
"from quopri import *" also imports encodestring() and decodestring().
											
										 
											2001-06-19 19:07:46 +00:00
										 |  |  |         return quotetabs | 
					
						
							| 
									
										
										
										
											2001-09-30 20:32:11 +00:00
										 |  |  |     # if header, we have to escape _ because _ is used to escape space | 
					
						
							| 
									
										
										
										
											2007-07-28 17:52:25 +00:00
										 |  |  |     if c == b'_': | 
					
						
							| 
									
										
										
										
											2001-09-30 20:32:11 +00:00
										 |  |  |         return header | 
					
						
							| 
									
										
										
										
											2007-07-28 17:52:25 +00:00
										 |  |  |     return c == ESCAPE or not (b' ' <= c <= b'~') | 
					
						
							| 
									
										
										
										
											1995-06-14 23:43:44 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | def quote(c): | 
					
						
							| 
									
										
										
										
											2000-10-05 17:24:33 +00:00
										 |  |  |     """Quote a single character.""" | 
					
						
							| 
									
										
										
										
											2007-07-28 17:52:25 +00:00
										 |  |  |     assert isinstance(c, bytes) and len(c)==1 | 
					
						
							|  |  |  |     c = ord(c) | 
					
						
							|  |  |  |     return ESCAPE + bytes((HEX[c//16], HEX[c%16])) | 
					
						
							| 
									
										
										
										
											1995-06-14 23:43:44 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
											
												Better support for RFC 1521 quoted-printable specification, along with
addition of interface for consistency with base64 module.  Namely,
encodestring(), decodestring(): New functions which accept a string
object and return a string object.  They just wrap the string in
StringIOs and pass them to the encode() and decode() methods
respectively.  encodestring() accepts a default argument of quotetabs,
defaulting to zero, which is passed on straight through to encode().
encode(): Fix the bug where an extra newline would always be added to
the output, which prevented an idempotent roundtrip through
encode->decode.  Now, if the source string doesn't end in a newline,
then the result string won't end in a newline.
Also, extend the quotetabs argument semantics to include quoting
embedded strings, which is also optional according to the RFC.
test() -> main()
"from quopri import *" also imports encodestring() and decodestring().
											
										 
											2001-06-19 19:07:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-07-02 04:57:30 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-09-02 20:34:14 +00:00
										 |  |  | def encode(input, output, quotetabs, header=False): | 
					
						
							| 
									
										
										
										
											2000-10-05 17:24:33 +00:00
										 |  |  |     """Read 'input', apply quoted-printable encoding, and write to 'output'.
 | 
					
						
							| 
									
										
										
										
											2000-02-04 15:28:42 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-10-05 17:24:33 +00:00
										 |  |  |     'input' and 'output' are files with readline() and write() methods. | 
					
						
							| 
									
										
										
											
												Better support for RFC 1521 quoted-printable specification, along with
addition of interface for consistency with base64 module.  Namely,
encodestring(), decodestring(): New functions which accept a string
object and return a string object.  They just wrap the string in
StringIOs and pass them to the encode() and decode() methods
respectively.  encodestring() accepts a default argument of quotetabs,
defaulting to zero, which is passed on straight through to encode().
encode(): Fix the bug where an extra newline would always be added to
the output, which prevented an idempotent roundtrip through
encode->decode.  Now, if the source string doesn't end in a newline,
then the result string won't end in a newline.
Also, extend the quotetabs argument semantics to include quoting
embedded strings, which is also optional according to the RFC.
test() -> main()
"from quopri import *" also imports encodestring() and decodestring().
											
										 
											2001-06-19 19:07:46 +00:00
										 |  |  |     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. | 
					
						
							| 
									
										
										
										
											2001-09-30 20:32:11 +00:00
										 |  |  |     The 'header' flag indicates whether we are encoding spaces as _ as per | 
					
						
							|  |  |  |     RFC 1522. | 
					
						
							| 
									
										
										
											
												Better support for RFC 1521 quoted-printable specification, along with
addition of interface for consistency with base64 module.  Namely,
encodestring(), decodestring(): New functions which accept a string
object and return a string object.  They just wrap the string in
StringIOs and pass them to the encode() and decode() methods
respectively.  encodestring() accepts a default argument of quotetabs,
defaulting to zero, which is passed on straight through to encode().
encode(): Fix the bug where an extra newline would always be added to
the output, which prevented an idempotent roundtrip through
encode->decode.  Now, if the source string doesn't end in a newline,
then the result string won't end in a newline.
Also, extend the quotetabs argument semantics to include quoting
embedded strings, which is also optional according to the RFC.
test() -> main()
"from quopri import *" also imports encodestring() and decodestring().
											
										 
											2001-06-19 19:07:46 +00:00
										 |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2001-09-30 20:32:11 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if b2a_qp is not None: | 
					
						
							|  |  |  |         data = input.read() | 
					
						
							| 
									
										
										
										
											2009-09-02 20:34:14 +00:00
										 |  |  |         odata = b2a_qp(data, quotetabs=quotetabs, header=header) | 
					
						
							| 
									
										
										
										
											2001-09-30 20:32:11 +00:00
										 |  |  |         output.write(odata) | 
					
						
							|  |  |  |         return | 
					
						
							| 
									
										
										
										
											2001-10-04 05:36:56 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-07-28 17:52:25 +00:00
										 |  |  |     def write(s, output=output, lineEnd=b'\n'): | 
					
						
							| 
									
										
										
											
												Better support for RFC 1521 quoted-printable specification, along with
addition of interface for consistency with base64 module.  Namely,
encodestring(), decodestring(): New functions which accept a string
object and return a string object.  They just wrap the string in
StringIOs and pass them to the encode() and decode() methods
respectively.  encodestring() accepts a default argument of quotetabs,
defaulting to zero, which is passed on straight through to encode().
encode(): Fix the bug where an extra newline would always be added to
the output, which prevented an idempotent roundtrip through
encode->decode.  Now, if the source string doesn't end in a newline,
then the result string won't end in a newline.
Also, extend the quotetabs argument semantics to include quoting
embedded strings, which is also optional according to the RFC.
test() -> main()
"from quopri import *" also imports encodestring() and decodestring().
											
										 
											2001-06-19 19:07:46 +00:00
										 |  |  |         # RFC 1521 requires that the line ending in a space or tab must have | 
					
						
							|  |  |  |         # that trailing character encoded. | 
					
						
							| 
									
										
										
										
											2007-07-28 17:52:25 +00:00
										 |  |  |         if s and s[-1:] in b' \t': | 
					
						
							|  |  |  |             output.write(s[:-1] + quote(s[-1:]) + lineEnd) | 
					
						
							|  |  |  |         elif s == b'.': | 
					
						
							| 
									
										
										
										
											2001-10-15 18:44:26 +00:00
										 |  |  |             output.write(quote(s) + lineEnd) | 
					
						
							| 
									
										
										
											
												Better support for RFC 1521 quoted-printable specification, along with
addition of interface for consistency with base64 module.  Namely,
encodestring(), decodestring(): New functions which accept a string
object and return a string object.  They just wrap the string in
StringIOs and pass them to the encode() and decode() methods
respectively.  encodestring() accepts a default argument of quotetabs,
defaulting to zero, which is passed on straight through to encode().
encode(): Fix the bug where an extra newline would always be added to
the output, which prevented an idempotent roundtrip through
encode->decode.  Now, if the source string doesn't end in a newline,
then the result string won't end in a newline.
Also, extend the quotetabs argument semantics to include quoting
embedded strings, which is also optional according to the RFC.
test() -> main()
"from quopri import *" also imports encodestring() and decodestring().
											
										 
											2001-06-19 19:07:46 +00:00
										 |  |  |         else: | 
					
						
							|  |  |  |             output.write(s + lineEnd) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     prevline = None | 
					
						
							| 
									
										
										
										
											2000-10-05 17:24:33 +00:00
										 |  |  |     while 1: | 
					
						
							|  |  |  |         line = input.readline() | 
					
						
							|  |  |  |         if not line: | 
					
						
							|  |  |  |             break | 
					
						
							| 
									
										
										
											
												Better support for RFC 1521 quoted-printable specification, along with
addition of interface for consistency with base64 module.  Namely,
encodestring(), decodestring(): New functions which accept a string
object and return a string object.  They just wrap the string in
StringIOs and pass them to the encode() and decode() methods
respectively.  encodestring() accepts a default argument of quotetabs,
defaulting to zero, which is passed on straight through to encode().
encode(): Fix the bug where an extra newline would always be added to
the output, which prevented an idempotent roundtrip through
encode->decode.  Now, if the source string doesn't end in a newline,
then the result string won't end in a newline.
Also, extend the quotetabs argument semantics to include quoting
embedded strings, which is also optional according to the RFC.
test() -> main()
"from quopri import *" also imports encodestring() and decodestring().
											
										 
											2001-06-19 19:07:46 +00:00
										 |  |  |         outline = [] | 
					
						
							|  |  |  |         # Strip off any readline induced trailing newline | 
					
						
							| 
									
										
										
										
											2007-07-28 17:52:25 +00:00
										 |  |  |         stripped = b'' | 
					
						
							|  |  |  |         if line[-1:] == b'\n': | 
					
						
							| 
									
										
										
										
											2000-10-05 17:24:33 +00:00
										 |  |  |             line = line[:-1] | 
					
						
							| 
									
										
										
										
											2007-07-28 17:52:25 +00:00
										 |  |  |             stripped = b'\n' | 
					
						
							| 
									
										
										
										
											2001-06-19 22:48:10 +00:00
										 |  |  |         # Calculate the un-length-limited encoded line | 
					
						
							| 
									
										
										
										
											2000-10-05 17:24:33 +00:00
										 |  |  |         for c in line: | 
					
						
							| 
									
										
										
										
											2007-07-28 17:52:25 +00:00
										 |  |  |             c = bytes((c,)) | 
					
						
							| 
									
										
										
										
											2001-09-30 20:32:11 +00:00
										 |  |  |             if needsquoting(c, quotetabs, header): | 
					
						
							| 
									
										
										
										
											2000-10-05 17:24:33 +00:00
										 |  |  |                 c = quote(c) | 
					
						
							| 
									
										
										
										
											2007-07-28 17:52:25 +00:00
										 |  |  |             if header and c == b' ': | 
					
						
							|  |  |  |                 outline.append(b'_') | 
					
						
							| 
									
										
										
										
											2001-09-30 20:32:11 +00:00
										 |  |  |             else: | 
					
						
							|  |  |  |                 outline.append(c) | 
					
						
							| 
									
										
										
										
											2001-06-19 22:48:10 +00:00
										 |  |  |         # First, write out the previous line | 
					
						
							| 
									
										
										
											
												Better support for RFC 1521 quoted-printable specification, along with
addition of interface for consistency with base64 module.  Namely,
encodestring(), decodestring(): New functions which accept a string
object and return a string object.  They just wrap the string in
StringIOs and pass them to the encode() and decode() methods
respectively.  encodestring() accepts a default argument of quotetabs,
defaulting to zero, which is passed on straight through to encode().
encode(): Fix the bug where an extra newline would always be added to
the output, which prevented an idempotent roundtrip through
encode->decode.  Now, if the source string doesn't end in a newline,
then the result string won't end in a newline.
Also, extend the quotetabs argument semantics to include quoting
embedded strings, which is also optional according to the RFC.
test() -> main()
"from quopri import *" also imports encodestring() and decodestring().
											
										 
											2001-06-19 19:07:46 +00:00
										 |  |  |         if prevline is not None: | 
					
						
							|  |  |  |             write(prevline) | 
					
						
							| 
									
										
										
										
											2001-06-19 22:48:10 +00:00
										 |  |  |         # Now see if we need any soft line breaks because of RFC-imposed | 
					
						
							|  |  |  |         # length limitations.  Then do the thisline->prevline dance. | 
					
						
							|  |  |  |         thisline = EMPTYSTRING.join(outline) | 
					
						
							|  |  |  |         while len(thisline) > MAXLINESIZE: | 
					
						
							|  |  |  |             # Don't forget to include the soft line break `=' sign in the | 
					
						
							|  |  |  |             # length calculation! | 
					
						
							| 
									
										
										
										
											2007-07-28 17:52:25 +00:00
										 |  |  |             write(thisline[:MAXLINESIZE-1], lineEnd=b'=\n') | 
					
						
							| 
									
										
										
										
											2001-06-19 22:48:10 +00:00
										 |  |  |             thisline = thisline[MAXLINESIZE-1:] | 
					
						
							|  |  |  |         # Write out the current line | 
					
						
							|  |  |  |         prevline = thisline | 
					
						
							| 
									
										
										
											
												Better support for RFC 1521 quoted-printable specification, along with
addition of interface for consistency with base64 module.  Namely,
encodestring(), decodestring(): New functions which accept a string
object and return a string object.  They just wrap the string in
StringIOs and pass them to the encode() and decode() methods
respectively.  encodestring() accepts a default argument of quotetabs,
defaulting to zero, which is passed on straight through to encode().
encode(): Fix the bug where an extra newline would always be added to
the output, which prevented an idempotent roundtrip through
encode->decode.  Now, if the source string doesn't end in a newline,
then the result string won't end in a newline.
Also, extend the quotetabs argument semantics to include quoting
embedded strings, which is also optional according to the RFC.
test() -> main()
"from quopri import *" also imports encodestring() and decodestring().
											
										 
											2001-06-19 19:07:46 +00:00
										 |  |  |     # Write out the last line, without a trailing newline | 
					
						
							|  |  |  |     if prevline is not None: | 
					
						
							|  |  |  |         write(prevline, lineEnd=stripped) | 
					
						
							| 
									
										
										
										
											1995-06-14 23:43:44 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-09-02 20:34:14 +00:00
										 |  |  | def encodestring(s, quotetabs=False, header=False): | 
					
						
							| 
									
										
										
										
											2001-09-30 20:32:11 +00:00
										 |  |  |     if b2a_qp is not None: | 
					
						
							| 
									
										
										
										
											2009-09-02 20:34:14 +00:00
										 |  |  |         return b2a_qp(s, quotetabs=quotetabs, header=header) | 
					
						
							| 
									
										
										
										
											2007-07-28 17:52:25 +00:00
										 |  |  |     from io import BytesIO | 
					
						
							|  |  |  |     infp = BytesIO(s) | 
					
						
							|  |  |  |     outfp = BytesIO() | 
					
						
							| 
									
										
										
										
											2001-09-30 20:32:11 +00:00
										 |  |  |     encode(infp, outfp, quotetabs, header) | 
					
						
							| 
									
										
										
											
												Better support for RFC 1521 quoted-printable specification, along with
addition of interface for consistency with base64 module.  Namely,
encodestring(), decodestring(): New functions which accept a string
object and return a string object.  They just wrap the string in
StringIOs and pass them to the encode() and decode() methods
respectively.  encodestring() accepts a default argument of quotetabs,
defaulting to zero, which is passed on straight through to encode().
encode(): Fix the bug where an extra newline would always be added to
the output, which prevented an idempotent roundtrip through
encode->decode.  Now, if the source string doesn't end in a newline,
then the result string won't end in a newline.
Also, extend the quotetabs argument semantics to include quoting
embedded strings, which is also optional according to the RFC.
test() -> main()
"from quopri import *" also imports encodestring() and decodestring().
											
										 
											2001-06-19 19:07:46 +00:00
										 |  |  |     return outfp.getvalue() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-07-02 04:57:30 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-09-02 20:34:14 +00:00
										 |  |  | def decode(input, output, header=False): | 
					
						
							| 
									
										
										
										
											2000-10-05 17:24:33 +00:00
										 |  |  |     """Read 'input', apply quoted-printable decoding, and write to 'output'.
 | 
					
						
							| 
									
										
										
										
											2001-09-30 20:32:11 +00:00
										 |  |  |     '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() | 
					
						
							| 
									
										
										
										
											2009-09-02 20:34:14 +00:00
										 |  |  |         odata = a2b_qp(data, header=header) | 
					
						
							| 
									
										
										
										
											2001-09-30 20:32:11 +00:00
										 |  |  |         output.write(odata) | 
					
						
							|  |  |  |         return | 
					
						
							| 
									
										
										
										
											2000-02-04 15:28:42 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-07-28 17:52:25 +00:00
										 |  |  |     new = b'' | 
					
						
							| 
									
										
										
										
											2000-10-05 17:24:33 +00:00
										 |  |  |     while 1: | 
					
						
							|  |  |  |         line = input.readline() | 
					
						
							|  |  |  |         if not line: break | 
					
						
							|  |  |  |         i, n = 0, len(line) | 
					
						
							| 
									
										
										
										
											2007-07-28 17:52:25 +00:00
										 |  |  |         if n > 0 and line[n-1:n] == b'\n': | 
					
						
							| 
									
										
										
										
											2000-10-05 17:24:33 +00:00
										 |  |  |             partial = 0; n = n-1 | 
					
						
							|  |  |  |             # Strip trailing whitespace | 
					
						
							| 
									
										
										
										
											2007-07-28 17:52:25 +00:00
										 |  |  |             while n > 0 and line[n-1:n] in b" \t\r": | 
					
						
							| 
									
										
										
										
											2000-10-05 17:24:33 +00:00
										 |  |  |                 n = n-1 | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             partial = 1 | 
					
						
							|  |  |  |         while i < n: | 
					
						
							| 
									
										
										
										
											2007-07-28 17:52:25 +00:00
										 |  |  |             c = line[i:i+1] | 
					
						
							|  |  |  |             if c == b'_' and header: | 
					
						
							|  |  |  |                 new = new + b' '; i = i+1 | 
					
						
							| 
									
										
										
										
											2001-09-30 20:32:11 +00:00
										 |  |  |             elif c != ESCAPE: | 
					
						
							| 
									
										
										
										
											2000-10-05 17:24:33 +00:00
										 |  |  |                 new = new + c; i = i+1 | 
					
						
							|  |  |  |             elif i+1 == n and not partial: | 
					
						
							|  |  |  |                 partial = 1; break | 
					
						
							|  |  |  |             elif i+1 < n and line[i+1] == ESCAPE: | 
					
						
							|  |  |  |                 new = new + ESCAPE; i = i+2 | 
					
						
							| 
									
										
										
										
											2007-07-28 17:52:25 +00:00
										 |  |  |             elif i+2 < n and ishex(line[i+1:i+2]) and ishex(line[i+2:i+3]): | 
					
						
							|  |  |  |                 new = new + bytes((unhex(line[i+1:i+3]),)); i = i+3 | 
					
						
							| 
									
										
										
										
											2000-10-05 17:24:33 +00:00
										 |  |  |             else: # Bad escape sequence -- leave it in | 
					
						
							|  |  |  |                 new = new + c; i = i+1 | 
					
						
							|  |  |  |         if not partial: | 
					
						
							| 
									
										
										
										
											2007-07-28 17:52:25 +00:00
										 |  |  |             output.write(new + b'\n') | 
					
						
							|  |  |  |             new = b'' | 
					
						
							| 
									
										
										
										
											2000-10-05 17:24:33 +00:00
										 |  |  |     if new: | 
					
						
							|  |  |  |         output.write(new) | 
					
						
							| 
									
										
										
										
											1995-06-14 23:43:44 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-09-02 20:34:14 +00:00
										 |  |  | def decodestring(s, header=False): | 
					
						
							| 
									
										
										
										
											2001-09-30 20:32:11 +00:00
										 |  |  |     if a2b_qp is not None: | 
					
						
							| 
									
										
										
										
											2009-09-02 20:34:14 +00:00
										 |  |  |         return a2b_qp(s, header=header) | 
					
						
							| 
									
										
										
										
											2007-07-28 17:52:25 +00:00
										 |  |  |     from io import BytesIO | 
					
						
							|  |  |  |     infp = BytesIO(s) | 
					
						
							|  |  |  |     outfp = BytesIO() | 
					
						
							| 
									
										
										
										
											2009-09-02 20:34:14 +00:00
										 |  |  |     decode(infp, outfp, header=header) | 
					
						
							| 
									
										
										
											
												Better support for RFC 1521 quoted-printable specification, along with
addition of interface for consistency with base64 module.  Namely,
encodestring(), decodestring(): New functions which accept a string
object and return a string object.  They just wrap the string in
StringIOs and pass them to the encode() and decode() methods
respectively.  encodestring() accepts a default argument of quotetabs,
defaulting to zero, which is passed on straight through to encode().
encode(): Fix the bug where an extra newline would always be added to
the output, which prevented an idempotent roundtrip through
encode->decode.  Now, if the source string doesn't end in a newline,
then the result string won't end in a newline.
Also, extend the quotetabs argument semantics to include quoting
embedded strings, which is also optional according to the RFC.
test() -> main()
"from quopri import *" also imports encodestring() and decodestring().
											
										 
											2001-06-19 19:07:46 +00:00
										 |  |  |     return outfp.getvalue() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-07-02 04:57:30 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
											
												Better support for RFC 1521 quoted-printable specification, along with
addition of interface for consistency with base64 module.  Namely,
encodestring(), decodestring(): New functions which accept a string
object and return a string object.  They just wrap the string in
StringIOs and pass them to the encode() and decode() methods
respectively.  encodestring() accepts a default argument of quotetabs,
defaulting to zero, which is passed on straight through to encode().
encode(): Fix the bug where an extra newline would always be added to
the output, which prevented an idempotent roundtrip through
encode->decode.  Now, if the source string doesn't end in a newline,
then the result string won't end in a newline.
Also, extend the quotetabs argument semantics to include quoting
embedded strings, which is also optional according to the RFC.
test() -> main()
"from quopri import *" also imports encodestring() and decodestring().
											
										 
											2001-06-19 19:07:46 +00:00
										 |  |  | # Other helper functions | 
					
						
							| 
									
										
										
										
											1995-06-14 23:43:44 +00:00
										 |  |  | def ishex(c): | 
					
						
							| 
									
										
										
										
											2007-07-28 17:52:25 +00:00
										 |  |  |     """Return true if the byte ordinal 'c' is a hexadecimal digit in ASCII.""" | 
					
						
							|  |  |  |     assert isinstance(c, bytes) | 
					
						
							|  |  |  |     return b'0' <= c <= b'9' or b'a' <= c <= b'f' or b'A' <= c <= b'F' | 
					
						
							| 
									
										
										
										
											1995-06-14 23:43:44 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | def unhex(s): | 
					
						
							| 
									
										
										
										
											2000-10-05 17:24:33 +00:00
										 |  |  |     """Get the integer value of a hexadecimal number.""" | 
					
						
							|  |  |  |     bits = 0 | 
					
						
							|  |  |  |     for c in s: | 
					
						
							| 
									
										
										
										
											2007-07-28 17:52:25 +00:00
										 |  |  |         c = bytes((c,)) | 
					
						
							|  |  |  |         if b'0' <= c <= b'9': | 
					
						
							| 
									
										
										
										
											2000-10-05 17:24:33 +00:00
										 |  |  |             i = ord('0') | 
					
						
							| 
									
										
										
										
											2007-07-28 17:52:25 +00:00
										 |  |  |         elif b'a' <= c <= b'f': | 
					
						
							| 
									
										
										
										
											2000-10-05 17:24:33 +00:00
										 |  |  |             i = ord('a')-10 | 
					
						
							| 
									
										
										
										
											2007-07-28 17:52:25 +00:00
										 |  |  |         elif b'A' <= c <= b'F': | 
					
						
							|  |  |  |             i = ord(b'A')-10 | 
					
						
							| 
									
										
										
										
											2000-10-05 17:24:33 +00:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2007-07-28 17:52:25 +00:00
										 |  |  |             assert False, "non-hex digit "+repr(c) | 
					
						
							| 
									
										
										
										
											2000-10-05 17:24:33 +00:00
										 |  |  |         bits = bits*16 + (ord(c) - i) | 
					
						
							|  |  |  |     return bits | 
					
						
							| 
									
										
										
										
											1995-06-14 23:43:44 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
											
												Better support for RFC 1521 quoted-printable specification, along with
addition of interface for consistency with base64 module.  Namely,
encodestring(), decodestring(): New functions which accept a string
object and return a string object.  They just wrap the string in
StringIOs and pass them to the encode() and decode() methods
respectively.  encodestring() accepts a default argument of quotetabs,
defaulting to zero, which is passed on straight through to encode().
encode(): Fix the bug where an extra newline would always be added to
the output, which prevented an idempotent roundtrip through
encode->decode.  Now, if the source string doesn't end in a newline,
then the result string won't end in a newline.
Also, extend the quotetabs argument semantics to include quoting
embedded strings, which is also optional according to the RFC.
test() -> main()
"from quopri import *" also imports encodestring() and decodestring().
											
										 
											2001-06-19 19:07:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-07-02 04:57:30 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
											
												Better support for RFC 1521 quoted-printable specification, along with
addition of interface for consistency with base64 module.  Namely,
encodestring(), decodestring(): New functions which accept a string
object and return a string object.  They just wrap the string in
StringIOs and pass them to the encode() and decode() methods
respectively.  encodestring() accepts a default argument of quotetabs,
defaulting to zero, which is passed on straight through to encode().
encode(): Fix the bug where an extra newline would always be added to
the output, which prevented an idempotent roundtrip through
encode->decode.  Now, if the source string doesn't end in a newline,
then the result string won't end in a newline.
Also, extend the quotetabs argument semantics to include quoting
embedded strings, which is also optional according to the RFC.
test() -> main()
"from quopri import *" also imports encodestring() and decodestring().
											
										 
											2001-06-19 19:07:46 +00:00
										 |  |  | def main(): | 
					
						
							| 
									
										
										
										
											2000-10-05 17:24:33 +00:00
										 |  |  |     import sys | 
					
						
							|  |  |  |     import getopt | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         opts, args = getopt.getopt(sys.argv[1:], 'td') | 
					
						
							| 
									
										
										
										
											2007-01-10 16:19:56 +00:00
										 |  |  |     except getopt.error as msg: | 
					
						
							| 
									
										
										
										
											2000-10-05 17:24:33 +00:00
										 |  |  |         sys.stdout = sys.stderr | 
					
						
							| 
									
										
										
										
											2007-02-09 05:37:30 +00:00
										 |  |  |         print(msg) | 
					
						
							|  |  |  |         print("usage: quopri [-t | -d] [file] ...") | 
					
						
							|  |  |  |         print("-t: quote tabs") | 
					
						
							|  |  |  |         print("-d: decode; default encode") | 
					
						
							| 
									
										
										
										
											2000-10-05 17:24:33 +00:00
										 |  |  |         sys.exit(2) | 
					
						
							|  |  |  |     deco = 0 | 
					
						
							|  |  |  |     tabs = 0 | 
					
						
							|  |  |  |     for o, a in opts: | 
					
						
							|  |  |  |         if o == '-t': tabs = 1 | 
					
						
							|  |  |  |         if o == '-d': deco = 1 | 
					
						
							|  |  |  |     if tabs and deco: | 
					
						
							|  |  |  |         sys.stdout = sys.stderr | 
					
						
							| 
									
										
										
										
											2007-02-09 05:37:30 +00:00
										 |  |  |         print("-t and -d are mutually exclusive") | 
					
						
							| 
									
										
										
										
											2000-10-05 17:24:33 +00:00
										 |  |  |         sys.exit(2) | 
					
						
							|  |  |  |     if not args: args = ['-'] | 
					
						
							|  |  |  |     sts = 0 | 
					
						
							|  |  |  |     for file in args: | 
					
						
							|  |  |  |         if file == '-': | 
					
						
							| 
									
										
										
										
											2007-07-28 17:52:25 +00:00
										 |  |  |             fp = sys.stdin.buffer | 
					
						
							| 
									
										
										
										
											2000-10-05 17:24:33 +00:00
										 |  |  |         else: | 
					
						
							|  |  |  |             try: | 
					
						
							| 
									
										
										
										
											2007-07-28 17:52:25 +00:00
										 |  |  |                 fp = open(file, "rb") | 
					
						
							| 
									
										
										
										
											2007-01-10 16:19:56 +00:00
										 |  |  |             except IOError as msg: | 
					
						
							| 
									
										
										
										
											2000-10-05 17:24:33 +00:00
										 |  |  |                 sys.stderr.write("%s: can't open (%s)\n" % (file, msg)) | 
					
						
							|  |  |  |                 sts = 1 | 
					
						
							|  |  |  |                 continue | 
					
						
							| 
									
										
										
										
											2007-10-30 17:27:30 +00:00
										 |  |  |         try: | 
					
						
							|  |  |  |             if deco: | 
					
						
							|  |  |  |                 decode(fp, sys.stdout.buffer) | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 encode(fp, sys.stdout.buffer, tabs) | 
					
						
							|  |  |  |         finally: | 
					
						
							|  |  |  |             if file != '-': | 
					
						
							|  |  |  |                 fp.close() | 
					
						
							| 
									
										
										
										
											2000-10-05 17:24:33 +00:00
										 |  |  |     if sts: | 
					
						
							|  |  |  |         sys.exit(sts) | 
					
						
							| 
									
										
										
										
											1995-06-14 23:43:44 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
											
												Better support for RFC 1521 quoted-printable specification, along with
addition of interface for consistency with base64 module.  Namely,
encodestring(), decodestring(): New functions which accept a string
object and return a string object.  They just wrap the string in
StringIOs and pass them to the encode() and decode() methods
respectively.  encodestring() accepts a default argument of quotetabs,
defaulting to zero, which is passed on straight through to encode().
encode(): Fix the bug where an extra newline would always be added to
the output, which prevented an idempotent roundtrip through
encode->decode.  Now, if the source string doesn't end in a newline,
then the result string won't end in a newline.
Also, extend the quotetabs argument semantics to include quoting
embedded strings, which is also optional according to the RFC.
test() -> main()
"from quopri import *" also imports encodestring() and decodestring().
											
										 
											2001-06-19 19:07:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-07-02 04:57:30 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											1995-06-14 23:43:44 +00:00
										 |  |  | if __name__ == '__main__': | 
					
						
							| 
									
										
										
											
												Better support for RFC 1521 quoted-printable specification, along with
addition of interface for consistency with base64 module.  Namely,
encodestring(), decodestring(): New functions which accept a string
object and return a string object.  They just wrap the string in
StringIOs and pass them to the encode() and decode() methods
respectively.  encodestring() accepts a default argument of quotetabs,
defaulting to zero, which is passed on straight through to encode().
encode(): Fix the bug where an extra newline would always be added to
the output, which prevented an idempotent roundtrip through
encode->decode.  Now, if the source string doesn't end in a newline,
then the result string won't end in a newline.
Also, extend the quotetabs argument semantics to include quoting
embedded strings, which is also optional according to the RFC.
test() -> main()
"from quopri import *" also imports encodestring() and decodestring().
											
										 
											2001-06-19 19:07:46 +00:00
										 |  |  |     main() |