ยปCore Development>Code coverage>Lib/quopri.py

Python code coverage for Lib/quopri.py

#countcontent
1n/a#! /usr/bin/env python3
2n/a
3n/a"""Conversions to/from quoted-printable transport encoding as per RFC 1521."""
4n/a
5n/a# (Dec 1991 version).
6n/a
7n/a__all__ = ["encode", "decode", "encodestring", "decodestring"]
8n/a
9n/aESCAPE = b'='
10n/aMAXLINESIZE = 76
11n/aHEX = b'0123456789ABCDEF'
12n/aEMPTYSTRING = b''
13n/a
14n/atry:
15n/a from binascii import a2b_qp, b2a_qp
16n/aexcept ImportError:
17n/a a2b_qp = None
18n/a b2a_qp = None
19n/a
20n/a
21n/adef needsquoting(c, quotetabs, header):
22n/a """Decide whether a particular byte ordinal needs to be quoted.
23n/a
24n/a The 'quotetabs' flag indicates whether embedded tabs and spaces should be
25n/a quoted. Note that line-ending tabs and spaces are always encoded, as per
26n/a RFC 1521.
27n/a """
28n/a assert isinstance(c, bytes)
29n/a if c in b' \t':
30n/a return quotetabs
31n/a # if header, we have to escape _ because _ is used to escape space
32n/a if c == b'_':
33n/a return header
34n/a return c == ESCAPE or not (b' ' <= c <= b'~')
35n/a
36n/adef quote(c):
37n/a """Quote a single character."""
38n/a assert isinstance(c, bytes) and len(c)==1
39n/a c = ord(c)
40n/a return ESCAPE + bytes((HEX[c//16], HEX[c%16]))
41n/a
42n/a
43n/a
44n/adef encode(input, output, quotetabs, header=False):
45n/a """Read 'input', apply quoted-printable encoding, and write to 'output'.
46n/a
47n/a 'input' and 'output' are binary file objects. The 'quotetabs' flag
48n/a indicates whether embedded tabs and spaces should be quoted. Note that
49n/a line-ending tabs and spaces are always encoded, as per RFC 1521.
50n/a The 'header' flag indicates whether we are encoding spaces as _ as per RFC
51n/a 1522."""
52n/a
53n/a if b2a_qp is not None:
54n/a data = input.read()
55n/a odata = b2a_qp(data, quotetabs=quotetabs, header=header)
56n/a output.write(odata)
57n/a return
58n/a
59n/a def write(s, output=output, lineEnd=b'\n'):
60n/a # RFC 1521 requires that the line ending in a space or tab must have
61n/a # that trailing character encoded.
62n/a if s and s[-1:] in b' \t':
63n/a output.write(s[:-1] + quote(s[-1:]) + lineEnd)
64n/a elif s == b'.':
65n/a output.write(quote(s) + lineEnd)
66n/a else:
67n/a output.write(s + lineEnd)
68n/a
69n/a prevline = None
70n/a while 1:
71n/a line = input.readline()
72n/a if not line:
73n/a break
74n/a outline = []
75n/a # Strip off any readline induced trailing newline
76n/a stripped = b''
77n/a if line[-1:] == b'\n':
78n/a line = line[:-1]
79n/a stripped = b'\n'
80n/a # Calculate the un-length-limited encoded line
81n/a for c in line:
82n/a c = bytes((c,))
83n/a if needsquoting(c, quotetabs, header):
84n/a c = quote(c)
85n/a if header and c == b' ':
86n/a outline.append(b'_')
87n/a else:
88n/a outline.append(c)
89n/a # First, write out the previous line
90n/a if prevline is not None:
91n/a write(prevline)
92n/a # Now see if we need any soft line breaks because of RFC-imposed
93n/a # length limitations. Then do the thisline->prevline dance.
94n/a thisline = EMPTYSTRING.join(outline)
95n/a while len(thisline) > MAXLINESIZE:
96n/a # Don't forget to include the soft line break `=' sign in the
97n/a # length calculation!
98n/a write(thisline[:MAXLINESIZE-1], lineEnd=b'=\n')
99n/a thisline = thisline[MAXLINESIZE-1:]
100n/a # Write out the current line
101n/a prevline = thisline
102n/a # Write out the last line, without a trailing newline
103n/a if prevline is not None:
104n/a write(prevline, lineEnd=stripped)
105n/a
106n/adef encodestring(s, quotetabs=False, header=False):
107n/a if b2a_qp is not None:
108n/a return b2a_qp(s, quotetabs=quotetabs, header=header)
109n/a from io import BytesIO
110n/a infp = BytesIO(s)
111n/a outfp = BytesIO()
112n/a encode(infp, outfp, quotetabs, header)
113n/a return outfp.getvalue()
114n/a
115n/a
116n/a
117n/adef decode(input, output, header=False):
118n/a """Read 'input', apply quoted-printable decoding, and write to 'output'.
119n/a 'input' and 'output' are binary file objects.
120n/a If 'header' is true, decode underscore as space (per RFC 1522)."""
121n/a
122n/a if a2b_qp is not None:
123n/a data = input.read()
124n/a odata = a2b_qp(data, header=header)
125n/a output.write(odata)
126n/a return
127n/a
128n/a new = b''
129n/a while 1:
130n/a line = input.readline()
131n/a if not line: break
132n/a i, n = 0, len(line)
133n/a if n > 0 and line[n-1:n] == b'\n':
134n/a partial = 0; n = n-1
135n/a # Strip trailing whitespace
136n/a while n > 0 and line[n-1:n] in b" \t\r":
137n/a n = n-1
138n/a else:
139n/a partial = 1
140n/a while i < n:
141n/a c = line[i:i+1]
142n/a if c == b'_' and header:
143n/a new = new + b' '; i = i+1
144n/a elif c != ESCAPE:
145n/a new = new + c; i = i+1
146n/a elif i+1 == n and not partial:
147n/a partial = 1; break
148n/a elif i+1 < n and line[i+1:i+2] == ESCAPE:
149n/a new = new + ESCAPE; i = i+2
150n/a elif i+2 < n and ishex(line[i+1:i+2]) and ishex(line[i+2:i+3]):
151n/a new = new + bytes((unhex(line[i+1:i+3]),)); i = i+3
152n/a else: # Bad escape sequence -- leave it in
153n/a new = new + c; i = i+1
154n/a if not partial:
155n/a output.write(new + b'\n')
156n/a new = b''
157n/a if new:
158n/a output.write(new)
159n/a
160n/adef decodestring(s, header=False):
161n/a if a2b_qp is not None:
162n/a return a2b_qp(s, header=header)
163n/a from io import BytesIO
164n/a infp = BytesIO(s)
165n/a outfp = BytesIO()
166n/a decode(infp, outfp, header=header)
167n/a return outfp.getvalue()
168n/a
169n/a
170n/a
171n/a# Other helper functions
172n/adef ishex(c):
173n/a """Return true if the byte ordinal 'c' is a hexadecimal digit in ASCII."""
174n/a assert isinstance(c, bytes)
175n/a return b'0' <= c <= b'9' or b'a' <= c <= b'f' or b'A' <= c <= b'F'
176n/a
177n/adef unhex(s):
178n/a """Get the integer value of a hexadecimal number."""
179n/a bits = 0
180n/a for c in s:
181n/a c = bytes((c,))
182n/a if b'0' <= c <= b'9':
183n/a i = ord('0')
184n/a elif b'a' <= c <= b'f':
185n/a i = ord('a')-10
186n/a elif b'A' <= c <= b'F':
187n/a i = ord(b'A')-10
188n/a else:
189n/a assert False, "non-hex digit "+repr(c)
190n/a bits = bits*16 + (ord(c) - i)
191n/a return bits
192n/a
193n/a
194n/a
195n/adef main():
196n/a import sys
197n/a import getopt
198n/a try:
199n/a opts, args = getopt.getopt(sys.argv[1:], 'td')
200n/a except getopt.error as msg:
201n/a sys.stdout = sys.stderr
202n/a print(msg)
203n/a print("usage: quopri [-t | -d] [file] ...")
204n/a print("-t: quote tabs")
205n/a print("-d: decode; default encode")
206n/a sys.exit(2)
207n/a deco = 0
208n/a tabs = 0
209n/a for o, a in opts:
210n/a if o == '-t': tabs = 1
211n/a if o == '-d': deco = 1
212n/a if tabs and deco:
213n/a sys.stdout = sys.stderr
214n/a print("-t and -d are mutually exclusive")
215n/a sys.exit(2)
216n/a if not args: args = ['-']
217n/a sts = 0
218n/a for file in args:
219n/a if file == '-':
220n/a fp = sys.stdin.buffer
221n/a else:
222n/a try:
223n/a fp = open(file, "rb")
224n/a except OSError as msg:
225n/a sys.stderr.write("%s: can't open (%s)\n" % (file, msg))
226n/a sts = 1
227n/a continue
228n/a try:
229n/a if deco:
230n/a decode(fp, sys.stdout.buffer)
231n/a else:
232n/a encode(fp, sys.stdout.buffer, tabs)
233n/a finally:
234n/a if file != '-':
235n/a fp.close()
236n/a if sts:
237n/a sys.exit(sts)
238n/a
239n/a
240n/a
241n/aif __name__ == '__main__':
242n/a main()