Xorn
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Pages
base64.py
Go to the documentation of this file.
1 # xorn.geda - Python library for manipulating gEDA files
2 #**********************************************************************
3 # _ _ __ _ _
4 # __ _ _ __ ___| |_ | |__ __ _ ___ ___ / /_ | or |
5 # / _` | '_ \ / _ \ __| | '_ \ / _` / __|/ _ \ '_ \| or |_
6 # | (_| | | | | __/ |_ | |_) | (_| \__ \ __/ (_) |__ _|
7 # \__, |_| |_|\___|\__| |_.__/ \__,_|___/\___|\___/ |_|
8 # |___/
9 #
10 # created by Alfred Reibenschuh <alfredreibenschuh@gmx.net>,
11 # under the "GNU Library General Public License" (see below).
12 #
13 #**********************************************************************
14 # Copyright (C) 2003 Free Software Foundation
15 # Copyright (C) 2013-2016 Roland Lutz
16 #
17 # This program is free software; you can redistribute it and/or modify
18 # it under the terms of the GNU General Public License as published by
19 # the Free Software Foundation; either version 2 of the License, or
20 # (at your option) any later version.
21 #
22 # This program is distributed in the hope that it will be useful,
23 # but WITHOUT ANY WARRANTY; without even the implied warranty of
24 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 # GNU General Public License for more details.
26 #
27 # You should have received a copy of the GNU General Public License
28 # along with this program; if not, write to the Free Software Foundation,
29 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
30 
31 ## \namespace xorn.base64
32 ## Reading and writing base64-encoded data
33 
34 from gettext import gettext as _
35 
36 BASE64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
37 PAD64 = '='
38 RANK = [
39  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 0x00-0x0f
40  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 0x10-0x1f
41  255,255,255,255,255,255,255,255,255,255,255, 62,255,255,255, 63, # 0x20-0x2f
42  52, 53, 54, 55, 56, 57, 58, 59, 60, 61,255,255,255,255,255,255, # 0x30-0x3f
43  255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, # 0x40-0x4f
44  15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,255,255,255,255,255, # 0x50-0x5f
45  255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, # 0x60-0x6f
46  41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,255,255,255,255,255, # 0x70-0x7f
47  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 0x80-0x8f
48  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 0x90-0x9f
49  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 0xa0-0xaf
50  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 0xb0-0xbf
51  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 0xc0-0xcf
52  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 0xd0-0xdf
53  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 0xe0-0xef
54  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 0xf0-0xff
55 ]
56 
57 ## Write a binary string to a file in %base64 representation.
58 #
59 # If \a columns is not \c None, insert a newline every \a columns
60 # characters. This is required by RFC 2045, but some applications
61 # don't require it. \a columns must positive and a multiple of \c 4.
62 #
63 # If \a delim is not \c None, it is written on a separate line after
64 # the data. This argument is provided for symmetry with \ref decode.
65 #
66 # \return \c None.
67 
68 def encode(f, src, columns = 72, delim = None):
69  # bulk encoding
70  blen = len(src) - len(src) % 3
71  ocnt = 0
72 
73  for pos in xrange(0, blen, 3):
74  # Convert 3 bytes of src to 4 bytes of output
75  #
76  # output[0] = input[0] 7:2
77  # output[1] = input[0] 1:0 input[1] 7:4
78  # output[2] = input[1] 3:0 input[2] 7:6
79  # output[3] = input[1] 5:0
80 
81  i0, i1, i2 = [ord(ch) for ch in src[pos:pos + 3]]
82 
83  # Map output to the Base64 alphabet
84  f.write(BASE64[i0 >> 2] +
85  BASE64[((i0 & 0x03) << 4) + (i1 >> 4)] +
86  BASE64[((i1 & 0x0f) << 2) + (i2 >> 6)] +
87  BASE64[i2 & 0x3f])
88 
89  if columns is not None:
90  ocnt += 1
91  if ocnt % (columns / 4) == 0 and pos != len(src) - 3:
92  f.write('\n')
93 
94  # Now worry about padding with remaining 1 or 2 bytes
95  if blen != len(src):
96  i0 = ord(src[blen])
97  if blen == len(src) - 1:
98  i1 = 0
99  else:
100  i1 = ord(src[blen + 1])
101  i2 = 0
102 
103  f.write(BASE64[i0 >> 2] +
104  BASE64[((i0 & 0x03) << 4) + (i1 >> 4)])
105  if blen == len(src) - 1:
106  f.write(PAD64)
107  else:
108  f.write(BASE64[((i1 & 0x0f) << 2) + (i2 >> 6)])
109  f.write(PAD64)
110 
111  if src:
112  f.write('\n')
113 
114  if delim is not None:
115  f.write(delim + '\n')
116 
117 ## Raised when reading invalid or unterminated base64-encoded data.
118 
119 class DecodingError(Exception):
120  pass
121 
122 ## Read a string in %base64 representation from a file.
123 #
124 # This function is liberal in what it will accept. It ignores
125 # non-base64 symbols.
126 #
127 # If \a delim is \c None, read until the end of the file. If \a delim
128 # is not \c None, read until a line containing exactly \a delim is
129 # found.
130 #
131 # \return A string containing the decoded data.
132 #
133 # \throw DecodingError if reading something that is not valid
134 # base64-encoded data
135 # \throw DecodingError if the end of the file is hit and \a delim is
136 # not \c None
137 
138 def decode(f, delim = None):
139  ch = 0
140  state = 0
141  res = 0
142  dst = []
143  pad = 0
144 
145  while True:
146  try:
147  line = f.next()
148  except StopIteration:
149  if delim is not None:
150  raise DecodingError, _("Unexpected end-of-file")
151  break
152 
153  if delim is not None and line == delim + '\n':
154  break
155 
156  for ch in line:
157  if ch == PAD64:
158  pad += 1
159  continue
160  pos = RANK[ord(ch)]
161  if pos == 255:
162  # Skip any non-base64 anywhere
163  continue
164  if pad != 0:
165  raise DecodingError
166 
167  if state == 0:
168  dst += [pos << 2]
169  state = 1
170  elif state == 1:
171  dst[-1] |= pos >> 4
172  res = (pos & 0x0f) << 4
173  state = 2
174  elif state == 2:
175  dst += [res | (pos >> 2)]
176  res = (pos & 0x03) << 6
177  state = 3
178  elif state == 3:
179  dst += [res | pos]
180  state = 0
181 
182  # We are done decoding Base-64 chars. Let's see if we ended
183  # on a byte boundary, and/or with erroneous trailing characters.
184  if pad != 0:
185  # We got a pad char.
186  if state == 0:
187  # Invalid = in first position
188  raise DecodingError
189  elif state == 1:
190  # Invalid = in second position
191  raise DecodingError
192  elif state == 2:
193  # Valid, means one byte of info
194  # Make sure there is another trailing = sign.
195  if pad != 2:
196  raise DecodingError
197  elif state == 3:
198  # Valid, means two bytes of info
199  # We know this char is an =. Is there anything but
200  # whitespace after it?
201  if pad != 1:
202  raise DecodingError
203  if state == 2 or state == 3:
204  # Now make sure for cases 2 and 3 that the "extra"
205  # bits that slopped past the last full byte were
206  # zeros. If we don't check them, they become a
207  # subliminal channel.
208  if res != 0:
209  raise DecodingError
210  else:
211  # We ended by seeing the end of the string. Make sure we
212  # have no partial bytes lying around.
213  if state != 0:
214  raise DecodingError
215  return ''.join(chr(b) for b in dst)
def encode
Write a binary string to a file in base64 representation.
Definition: base64.py:68
Raised when reading invalid or unterminated base64-encoded data.
Definition: base64.py:119
def decode
Read a string in base64 representation from a file.
Definition: base64.py:138