|
2 | 2 |
|
3 | 3 | # (Dec 1991 version). |
4 | 4 |
|
| 5 | +from binascii import a2b_qp, b2a_qp |
| 6 | + |
5 | 7 | __all__ = ["encode", "decode", "encodestring", "decodestring"] |
6 | 8 |
|
7 | 9 | ESCAPE = b'=' |
8 | 10 | MAXLINESIZE = 76 |
9 | 11 | HEX = b'0123456789ABCDEF' |
10 | 12 | EMPTYSTRING = b'' |
11 | 13 |
|
12 | | -try: |
13 | | - from binascii import a2b_qp, b2a_qp |
14 | | -except ImportError: |
15 | | - a2b_qp = None |
16 | | - b2a_qp = None |
17 | | - |
18 | 14 |
|
19 | 15 | def needsquoting(c, quotetabs, header): |
20 | 16 | """Decide whether a particular byte ordinal needs to be quoted. |
@@ -47,118 +43,26 @@ def encode(input, output, quotetabs, header=False): |
47 | 43 | line-ending tabs and spaces are always encoded, as per RFC 1521. |
48 | 44 | The 'header' flag indicates whether we are encoding spaces as _ as per RFC |
49 | 45 | 1522.""" |
| 46 | + data = input.read() |
| 47 | + odata = b2a_qp(data, quotetabs=quotetabs, header=header) |
| 48 | + output.write(odata) |
50 | 49 |
|
51 | | - if b2a_qp is not None: |
52 | | - data = input.read() |
53 | | - odata = b2a_qp(data, quotetabs=quotetabs, header=header) |
54 | | - output.write(odata) |
55 | | - return |
56 | | - |
57 | | - def write(s, output=output, lineEnd=b'\n'): |
58 | | - # RFC 1521 requires that the line ending in a space or tab must have |
59 | | - # that trailing character encoded. |
60 | | - if s and s[-1:] in b' \t': |
61 | | - output.write(s[:-1] + quote(s[-1:]) + lineEnd) |
62 | | - elif s == b'.': |
63 | | - output.write(quote(s) + lineEnd) |
64 | | - else: |
65 | | - output.write(s + lineEnd) |
66 | | - |
67 | | - prevline = None |
68 | | - while line := input.readline(): |
69 | | - outline = [] |
70 | | - # Strip off any readline induced trailing newline |
71 | | - stripped = b'' |
72 | | - if line[-1:] == b'\n': |
73 | | - line = line[:-1] |
74 | | - stripped = b'\n' |
75 | | - # Calculate the un-length-limited encoded line |
76 | | - for c in line: |
77 | | - c = bytes((c,)) |
78 | | - if needsquoting(c, quotetabs, header): |
79 | | - c = quote(c) |
80 | | - if header and c == b' ': |
81 | | - outline.append(b'_') |
82 | | - else: |
83 | | - outline.append(c) |
84 | | - # First, write out the previous line |
85 | | - if prevline is not None: |
86 | | - write(prevline) |
87 | | - # Now see if we need any soft line breaks because of RFC-imposed |
88 | | - # length limitations. Then do the thisline->prevline dance. |
89 | | - thisline = EMPTYSTRING.join(outline) |
90 | | - while len(thisline) > MAXLINESIZE: |
91 | | - # Don't forget to include the soft line break `=' sign in the |
92 | | - # length calculation! |
93 | | - write(thisline[:MAXLINESIZE-1], lineEnd=b'=\n') |
94 | | - thisline = thisline[MAXLINESIZE-1:] |
95 | | - # Write out the current line |
96 | | - prevline = thisline |
97 | | - # Write out the last line, without a trailing newline |
98 | | - if prevline is not None: |
99 | | - write(prevline, lineEnd=stripped) |
100 | 50 |
|
101 | 51 | def encodestring(s, quotetabs=False, header=False): |
102 | | - if b2a_qp is not None: |
103 | | - return b2a_qp(s, quotetabs=quotetabs, header=header) |
104 | | - from io import BytesIO |
105 | | - infp = BytesIO(s) |
106 | | - outfp = BytesIO() |
107 | | - encode(infp, outfp, quotetabs, header) |
108 | | - return outfp.getvalue() |
109 | | - |
| 52 | + return b2a_qp(s, quotetabs=quotetabs, header=header) |
110 | 53 |
|
111 | 54 |
|
112 | 55 | def decode(input, output, header=False): |
113 | 56 | """Read 'input', apply quoted-printable decoding, and write to 'output'. |
114 | 57 | 'input' and 'output' are binary file objects. |
115 | 58 | If 'header' is true, decode underscore as space (per RFC 1522).""" |
| 59 | + data = input.read() |
| 60 | + odata = a2b_qp(data, header=header) |
| 61 | + output.write(odata) |
116 | 62 |
|
117 | | - if a2b_qp is not None: |
118 | | - data = input.read() |
119 | | - odata = a2b_qp(data, header=header) |
120 | | - output.write(odata) |
121 | | - return |
122 | | - |
123 | | - new = b'' |
124 | | - while line := input.readline(): |
125 | | - i, n = 0, len(line) |
126 | | - if n > 0 and line[n-1:n] == b'\n': |
127 | | - partial = 0; n = n-1 |
128 | | - # Strip trailing whitespace |
129 | | - while n > 0 and line[n-1:n] in b" \t\r": |
130 | | - n = n-1 |
131 | | - else: |
132 | | - partial = 1 |
133 | | - while i < n: |
134 | | - c = line[i:i+1] |
135 | | - if c == b'_' and header: |
136 | | - new = new + b' '; i = i+1 |
137 | | - elif c != ESCAPE: |
138 | | - new = new + c; i = i+1 |
139 | | - elif i+1 == n and not partial: |
140 | | - partial = 1; break |
141 | | - elif i+1 < n and line[i+1:i+2] == ESCAPE: |
142 | | - new = new + ESCAPE; i = i+2 |
143 | | - elif i+2 < n and ishex(line[i+1:i+2]) and ishex(line[i+2:i+3]): |
144 | | - new = new + bytes((unhex(line[i+1:i+3]),)); i = i+3 |
145 | | - else: # Bad escape sequence -- leave it in |
146 | | - new = new + c; i = i+1 |
147 | | - if not partial: |
148 | | - output.write(new + b'\n') |
149 | | - new = b'' |
150 | | - if new: |
151 | | - output.write(new) |
152 | 63 |
|
153 | 64 | def decodestring(s, header=False): |
154 | | - if a2b_qp is not None: |
155 | | - return a2b_qp(s, header=header) |
156 | | - from io import BytesIO |
157 | | - infp = BytesIO(s) |
158 | | - outfp = BytesIO() |
159 | | - decode(infp, outfp, header=header) |
160 | | - return outfp.getvalue() |
161 | | - |
| 65 | + return a2b_qp(s, header=header) |
162 | 66 |
|
163 | 67 |
|
164 | 68 | # Other helper functions |
|
0 commit comments