base64.py revision 3b8e1604e8dd4c871e5e8a22d6c738c7f2d39f3a
1# Conversions to/from base64 transport encoding as per RFC-MIME (Dec 1991 2# version). 3 4# Parameters set by RFX-XXXX. 5MAXLINESIZE = 76 # Excluding the CRLF 6INVAR = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' 7PAD = '=' 8 9# Check that I typed that string correctly... 10if len(INVAR) <> 64: raise RuntimeError, 'wrong INVAR string!?!?' 11 12# Compute the inverse table, for decode(). 13inverse = {} 14for i in range(64): inverse[INVAR[i]] = i 15del i 16inverse[PAD] = 0 17 18# Encode a file. 19def encode(input, output): 20 line = '' 21 BUFSIZE = 8192 22 leftover = '' 23 while 1: 24 s = input.read(BUFSIZE) 25 if not s: break 26 s = leftover + s 27 i = 0 28 while i+3 <= len(s): 29 quad = makequad(s[i:i+3]) 30 i = i+3 31 if len(line) + 4 > MAXLINESIZE: 32 output.write(line + '\n') 33 line = '' 34 line = line + quad 35 leftover = s[i:] 36 if leftover: 37 quad = makeshortquad(leftover) 38 if len(line) + 4 > MAXLINESIZE: 39 output.write(line + '\n') 40 line = '' 41 line = line + quad 42 if line: 43 output.write(line + '\n') 44 45def makequad(s): # Return the quad for a 3 character string 46 x = ord(s[0])*0x10000 + ord(s[1])*0x100 + ord(s[2]) 47 x, c4 = divmod(x, 64) 48 x, c3 = divmod(x, 64) 49 c1, c2 = divmod(x, 64) 50 return INVAR[c1] + INVAR[c2] +INVAR[c3] + INVAR[c4] 51 52def makeshortquad(s): # Return the quad value for a 1 or 2 character string 53 n = len(s) 54 while len(s) < 3: 55 s = s + '\0' 56 quad = makequad(s) 57 if n == 2: 58 quad = quad[:3] + PAD 59 elif n == 1: 60 quad = quad[:2] + 2*PAD 61 return quad 62 63# Decode a file. 64def decode(input, output): 65 BUFSIZE = 8192 66 bits, n, bytes, prev = 0, 0, '', '' 67 while 1: 68 line = input.readline() 69 if not line: break 70 for c in line: 71 if inverse.has_key(c): 72 bits = bits*64 + inverse[c] 73 n = n+6 74 if n == 24: 75 triplet = decodequad(bits) 76 if c == PAD: 77 if prev == PAD: 78 triplet = triplet[:1] 79 else: 80 triplet = triplet[:2] 81 bits, n = 0, 0 82 bytes = bytes + triplet 83 if len(bytes) > BUFSIZE: 84 output.write(bytes[:BUFSIZE]) 85 bytes = bytes[BUFSIZE:] 86 prev = c 87 if bytes: 88 output.write(bytes) 89 90def decodequad(bits): # Turn 24 bits into 3 characters 91 bits, c3 = divmod(bits, 256) 92 c1, c2 = divmod(bits, 256) 93 return chr(c1) + chr(c2) + chr(c3) 94 95def encodestring(s): 96 import StringIO 97 f = StringIO.StringIO(s) 98 g = StringIO.StringIO() 99 encode(f, g) 100 return g.getvalue() 101 102def decodestring(s): 103 import StringIO 104 f = StringIO.StringIO(s) 105 g = StringIO.StringIO() 106 decode(f, g) 107 return g.getvalue() 108 109# Small test program, reads stdin, writes stdout. 110# no arg: encode, any arg: decode. 111def test(): 112 import sys, getopt 113 try: 114 opts, args = getopt.getopt(sys.argv[1:], 'deut') 115 except getopt.error, msg: 116 print msg 117 print """usage: basd64 [-d] [-e] [-u] [-t] [file|-] 118 -d, -u: decode 119 -e: encode (default) 120 -t: decode string 'Aladdin:open sesame'""" 121 func = encode 122 for o, a in opts: 123 if o == '-e': func = encode 124 if o == '-d': func = decode 125 if o == '-u': func = decode 126 if o == '-t': test1(); return 127 if args and args[0] != '-': 128 func(open(args[0]), sys.stdout) 129 else: 130 func(sys.stdin, sys.stdout) 131 132def test1(): 133 s0 = "Aladdin:open sesame" 134 s1 = encodestring(s0) 135 s2 = decodestring(s1) 136 print s0, `s1`, s2 137 138if __name__ == '__main__': 139 test() 140