3 """Conversions to/from quoted-printable transport encoding as per RFC 1521."""
7 __all__
= ["encode", "decode", "encodestring", "decodestring"]
11 HEX
= '0123456789ABCDEF'
15 from binascii
import a2b_qp
, b2a_qp
21 def needsquoting(c
, quotetabs
, header
):
22 """Decide whether a particular character needs to be quoted.
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
30 # if header, we have to escape _ because _ is used to escape space
33 return c
== ESCAPE
or not (' ' <= c
<= '~')
36 """Quote a single character."""
38 return ESCAPE
+ HEX
[i
//16] + HEX
[i
%16]
42 def encode(input, output
, quotetabs
, header
= 0):
43 """Read 'input', apply quoted-printable encoding, and write to 'output'.
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
49 The 'header' flag indicates whether we are encoding spaces as _ as per
53 if b2a_qp
is not None:
55 odata
= b2a_qp(data
, quotetabs
= quotetabs
, header
= header
)
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
)
65 output
.write(quote(s
) + lineEnd
)
67 output
.write(s
+ lineEnd
)
71 line
= input.readline()
75 # Strip off any readline induced trailing newline
80 # Calculate the un-length-limited encoded line
82 if needsquoting(c
, quotetabs
, header
):
84 if header
and c
== ' ':
88 # First, write out the previous line
89 if prevline
is not None:
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
97 write(thisline
[:MAXLINESIZE
-1], lineEnd
='=\n')
98 thisline
= thisline
[MAXLINESIZE
-1:]
99 # Write out the current line
101 # Write out the last line, without a trailing newline
102 if prevline
is not None:
103 write(prevline
, lineEnd
=stripped
)
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
111 encode(infp
, outfp
, quotetabs
, header
)
112 return outfp
.getvalue()
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)."""
121 if a2b_qp
is not None:
123 odata
= a2b_qp(data
, header
= header
)
129 line
= input.readline()
132 if n
> 0 and line
[n
-1] == '\n':
134 # Strip trailing whitespace
135 while n
> 0 and line
[n
-1] in " \t\r":
141 if c
== '_' and header
:
142 new
= new
+ ' '; i
= i
+1
144 new
= new
+ c
; i
= i
+1
145 elif i
+1 == n
and not partial
:
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
154 output
.write(new
+ '\n')
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
165 decode(infp
, outfp
, header
= header
)
166 return outfp
.getvalue()
170 # Other helper functions
172 """Return true if the character 'c' is a hexadecimal digit."""
173 return '0' <= c
<= '9' or 'a' <= c
<= 'f' or 'A' <= c
<= 'F'
176 """Get the integer value of a hexadecimal number."""
181 elif 'a' <= c
<= 'f':
183 elif 'A' <= c
<= 'F':
187 bits
= bits
*16 + (ord(c
) - i
)
196 opts
, args
= getopt
.getopt(sys
.argv
[1:], 'td')
197 except getopt
.error
, msg
:
198 sys
.stdout
= sys
.stderr
200 print "usage: quopri [-t | -d] [file] ..."
201 print "-t: quote tabs"
202 print "-d: decode; default encode"
207 if o
== '-t': tabs
= 1
208 if o
== '-d': deco
= 1
210 sys
.stdout
= sys
.stderr
211 print "-t and -d are mutually exclusive"
213 if not args
: args
= ['-']
222 sys
.stderr
.write("%s: can't open (%s)\n" % (file, msg
))
226 decode(fp
, sys
.stdout
)
228 encode(fp
, sys
.stdout
, tabs
)
229 if fp
is not sys
.stdin
:
236 if __name__
== '__main__':