quoprimime.py (4684B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\carbon\common\stdlib\email\quoprimime.py 2 __all__ = ['body_decode', 3 'body_encode', 4 'body_quopri_check', 5 'body_quopri_len', 6 'decode', 7 'decodestring', 8 'encode', 9 'encodestring', 10 'header_decode', 11 'header_encode', 12 'header_quopri_check', 13 'header_quopri_len', 14 'quote', 15 'unquote'] 16 import re 17 from string import hexdigits 18 from email.utils import fix_eols 19 CRLF = '\r\n' 20 NL = '\n' 21 MISC_LEN = 7 22 hqre = re.compile('[^-a-zA-Z0-9!*+/ ]') 23 bqre = re.compile('[^ !-<>-~\\t]') 24 25 def header_quopri_check(c): 26 return bool(hqre.match(c)) 27 28 29 def body_quopri_check(c): 30 return bool(bqre.match(c)) 31 32 33 def header_quopri_len(s): 34 count = 0 35 for c in s: 36 if hqre.match(c): 37 count += 3 38 else: 39 count += 1 40 41 return count 42 43 44 def body_quopri_len(str): 45 count = 0 46 for c in str: 47 if bqre.match(c): 48 count += 3 49 else: 50 count += 1 51 52 return count 53 54 55 def _max_append(L, s, maxlen, extra = ''): 56 if not L: 57 L.append(s.lstrip()) 58 elif len(L[-1]) + len(s) <= maxlen: 59 L[-1] += extra + s 60 else: 61 L.append(s.lstrip()) 62 63 64 def unquote(s): 65 return chr(int(s[1:3], 16)) 66 67 68 def quote(c): 69 return '=%02X' % ord(c) 70 71 72 def header_encode(header, charset = 'iso-8859-1', keep_eols = False, maxlinelen = 76, eol = NL): 73 if not header: 74 return header 75 if not keep_eols: 76 header = fix_eols(header) 77 quoted = [] 78 if maxlinelen is None: 79 max_encoded = 100000 80 else: 81 max_encoded = maxlinelen - len(charset) - MISC_LEN - 1 82 for c in header: 83 if c == ' ': 84 _max_append(quoted, '_', max_encoded) 85 elif not hqre.match(c): 86 _max_append(quoted, c, max_encoded) 87 else: 88 _max_append(quoted, '=%02X' % ord(c), max_encoded) 89 90 joiner = eol + ' ' 91 return joiner.join([ '=?%s?q?%s?=' % (charset, line) for line in quoted ]) 92 93 94 def encode(body, binary = False, maxlinelen = 76, eol = NL): 95 if not body: 96 return body 97 if not binary: 98 body = fix_eols(body) 99 encoded_body = '' 100 lineno = -1 101 lines = body.splitlines(1) 102 for line in lines: 103 if line.endswith(CRLF): 104 line = line[:-2] 105 elif line[-1] in CRLF: 106 line = line[:-1] 107 lineno += 1 108 encoded_line = '' 109 prev = None 110 linelen = len(line) 111 for j in range(linelen): 112 c = line[j] 113 prev = c 114 if bqre.match(c): 115 c = quote(c) 116 elif j + 1 == linelen: 117 if c not in ' \t': 118 encoded_line += c 119 prev = c 120 continue 121 if len(encoded_line) + len(c) >= maxlinelen: 122 encoded_body += encoded_line + '=' + eol 123 encoded_line = '' 124 encoded_line += c 125 126 if prev and prev in ' \t': 127 if lineno + 1 == len(lines): 128 prev = quote(prev) 129 if len(encoded_line) + len(prev) > maxlinelen: 130 encoded_body += encoded_line + '=' + eol + prev 131 else: 132 encoded_body += encoded_line + prev 133 else: 134 encoded_body += encoded_line + prev + '=' + eol 135 encoded_line = '' 136 if lines[lineno].endswith(CRLF) or lines[lineno][-1] in CRLF: 137 encoded_body += encoded_line + eol 138 else: 139 encoded_body += encoded_line 140 encoded_line = '' 141 142 return encoded_body 143 144 145 body_encode = encode 146 encodestring = encode 147 148 def decode(encoded, eol = NL): 149 if not encoded: 150 return encoded 151 decoded = '' 152 for line in encoded.splitlines(): 153 line = line.rstrip() 154 if not line: 155 decoded += eol 156 continue 157 i = 0 158 n = len(line) 159 while i < n: 160 c = line[i] 161 if c != '=': 162 decoded += c 163 i += 1 164 elif i + 1 == n: 165 i += 1 166 continue 167 elif i + 2 < n and line[i + 1] in hexdigits and line[i + 2] in hexdigits: 168 decoded += unquote(line[i:i + 3]) 169 i += 3 170 else: 171 decoded += c 172 i += 1 173 if i == n: 174 decoded += eol 175 176 if not encoded.endswith(eol) and decoded.endswith(eol): 177 decoded = decoded[:-1] 178 return decoded 179 180 181 body_decode = decode 182 decodestring = decode 183 184 def _unquote_match(match): 185 s = match.group(0) 186 return unquote(s) 187 188 189 def header_decode(s): 190 s = s.replace('_', ' ') 191 return re.sub('=[a-fA-F0-9]{2}', _unquote_match, s)