summaryrefslogtreecommitdiffstats
path: root/Lib/base64.py
blob: 55f01ced092cde691ad6fa5bf712b59772361fc2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# Conversions to/from base64 transport encoding as per RFC-MIME (Dec 1991
# version).

# Parameters set by RFX-XXXX.
MAXLINESIZE = 76 # Excluding the CRLF
INVAR = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
PAD = '='

# Check that I typed that string correctly...
if len(INVAR) <> 64: raise RuntimeError, 'wrong INVAR string!?!?'

# Compute the inverse table, for decode().
inverse = {}
for i in range(64): inverse[INVAR[i]] = i
del i
inverse[PAD] = 0

# Encode a file.
def encode(input, output):
	line = ''
	BUFSIZE = 8192
	leftover = ''
	while 1:
		s = input.read(BUFSIZE)
		if not s: break
		s = leftover + s
		i = 0
		while i+3 <= len(s):
			quad = makequad(s[i:i+3])
			i = i+3
			if len(line) + 4 > MAXLINESIZE:
				output.write(line + '\n')
				line = ''
			line = line + quad
		leftover = s[i:]
	if leftover:
		quad = makeshortquad(leftover)
		if len(line) + 4 > MAXLINESIZE:
			output.write(line + '\n')
			line = ''
		line = line + quad
	if line:
		output.write(line + '\n')

def makequad(s): # Return the quad for a 3 character string
	x = ord(s[0])*0x10000 + ord(s[1])*0x100 + ord(s[2])
	x, c4 = divmod(x, 64)
	x, c3 = divmod(x, 64)
	c1, c2 = divmod(x, 64)
	return INVAR[c1] + INVAR[c2] +INVAR[c3] + INVAR[c4]

def makeshortquad(s): # Return the quad value for a 1 or 2 character string
	n = len(s)
	while len(s) < 3:
		s = s + '\0'
	quad = makequad(s)
	if n == 2:
		quad = quad[:3] + PAD
	elif n == 1:
		quad = quad[:2] + 2*PAD
	return quad

# Decode a file.
def decode(input, output):
	BUFSIZE = 8192
	bits, n, bytes, prev = 0, 0, '', ''
	while 1:
		line = input.readline()
		if not line: break
		for c in line:
			if inverse.has_key(c):
				bits = bits*64 + inverse[c]
				n = n+6
				if n == 24:
					triplet = decodequad(bits)
					if c == PAD:
						if prev == PAD:
							triplet = triplet[:1]
						else:
							triplet = triplet[:2]
					bits, n = 0, 0
					bytes = bytes + triplet
					if len(bytes) > BUFSIZE:
						output.write(bytes[:BUFSIZE])
						bytes = bytes[BUFSIZE:]
				prev = c
	if bytes:
		output.write(bytes)

def decodequad(bits): # Turn 24 bits into 3 characters
	bits, c3 = divmod(bits, 256)
	c1, c2 = divmod(bits, 256)
	return chr(c1) + chr(c2) + chr(c3)

def encodestring(s):
	import StringIO
	f = StringIO.StringIO(s)
	g = StringIO.StringIO()
	encode(f, g)
	return g.getvalue()

def decodestring(s):
	import StringIO
	f = StringIO.StringIO(s)
	g = StringIO.StringIO()
	decode(f, g)
	return g.getvalue()

# Small test program, reads stdin, writes stdout.
# no arg: encode, any arg: decode.
def test():
	import sys, getopt
	try:
		opts, args = getopt.getopt(sys.argv[1:], 'deut')
	except getopt.error, msg:
		print msg
		print """usage: basd64 [-d] [-e] [-u] [-t] [file|-]
		-d, -u: decode
		-e: encode (default)
		-t: decode string 'Aladdin:open sesame'"""
	func = encode
	for o, a in opts:
		if o == '-e': func = encode
		if o == '-d': func = decode
		if o == '-u': func = decode
		if o == '-t': test1(); return
	if args and args[0] != '-':
		func(open(args[0]), sys.stdout)
	else:
		func(sys.stdin, sys.stdout)

def test1():
	s0 = "Aladdin:open sesame"
	s1 = encodestring(s0)
	s2 = decodestring(s1)
	print s0, `s1`, s2

if __name__ == '__main__':
	test()