Guido van Rossum | f194546 | 1995-06-14 23:43:44 +0000 | [diff] [blame] | 1 | # Conversions to/from base64 transport encoding as per RFC-MIME (Dec 1991 |
| 2 | # version). |
| 3 | |
| 4 | # Parameters set by RFX-XXXX. |
| 5 | MAXLINESIZE = 76 # Excluding the CRLF |
| 6 | INVAR = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' |
| 7 | PAD = '=' |
| 8 | |
| 9 | # Check that I typed that string correctly... |
| 10 | if len(INVAR) <> 64: raise RuntimeError, 'wrong INVAR string!?!?' |
| 11 | |
| 12 | # Compute the inverse table, for decode(). |
| 13 | inverse = {} |
| 14 | for i in range(64): inverse[INVAR[i]] = i |
| 15 | del i |
| 16 | inverse[PAD] = 0 |
| 17 | |
| 18 | # Encode a file. |
| 19 | def 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 | |
| 45 | def 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 | |
| 52 | def 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. |
| 64 | def 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 | |
| 90 | def 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 | |
| 95 | def encodestring(s): |
| 96 | import StringIO |
| 97 | f = StringIO.StringIO(s) |
| 98 | g = StringIO.StringIO() |
| 99 | encode(f, g) |
| 100 | return g.getvalue() |
| 101 | |
| 102 | def 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. |
| 111 | def test(): |
Guido van Rossum | 3b8e160 | 1995-08-10 19:26:37 +0000 | [diff] [blame^] | 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) |
Guido van Rossum | f194546 | 1995-06-14 23:43:44 +0000 | [diff] [blame] | 129 | else: |
Guido van Rossum | 3b8e160 | 1995-08-10 19:26:37 +0000 | [diff] [blame^] | 130 | func(sys.stdin, sys.stdout) |
Guido van Rossum | f194546 | 1995-06-14 23:43:44 +0000 | [diff] [blame] | 131 | |
| 132 | def test1(): |
| 133 | s0 = "Aladdin:open sesame" |
| 134 | s1 = encodestring(s0) |
| 135 | s2 = decodestring(s1) |
| 136 | print s0, `s1`, s2 |
| 137 | |
| 138 | if __name__ == '__main__': |
| 139 | test() |