2 * base64.c: base64 encoding and decoding functions
4 * ====================================================================
5 * Copyright (c) 2000-2004 CollabNet. All rights reserved.
7 * This software is licensed as described in the file COPYING, which
8 * you should have received as part of this distribution. The terms
9 * are also available at http://subversion.tigris.org/license-1.html.
10 * If newer versions of this license are posted there, you may use a
11 * newer version instead, at your option.
13 * This software consists of voluntary contributions made by many
14 * individuals. For exact contribution history, see the revision
15 * history and logs, available at http://subversion.tigris.org/.
16 * ====================================================================
24 #include <apr_pools.h>
25 #include <apr_general.h> /* for APR_INLINE */
27 #include "svn_pools.h"
29 #include "svn_error.h"
30 #include "svn_base64.h"
33 #define BASE64_LINELEN 76
34 static const char base64tab
[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
35 "abcdefghijklmnopqrstuvwxyz0123456789+/";
39 /* Binary input --> base64-encoded output */
43 unsigned char buf
[3]; /* Bytes waiting to be encoded */
44 int buflen
; /* Number of bytes waiting */
45 int linelen
; /* Bytes output so far on this line */
50 /* Base64-encode a group. IN needs to have three bytes and OUT needs
51 to have room for four bytes. The input group is treated as four
52 six-bit units which are treated as lookups into base64tab for the
53 bytes of the output group. */
54 static APR_INLINE
void
55 encode_group(const unsigned char *in
, char *out
)
57 out
[0] = base64tab
[in
[0] >> 2];
58 out
[1] = base64tab
[((in
[0] & 0x3) << 4) | (in
[1] >> 4)];
59 out
[2] = base64tab
[((in
[1] & 0xf) << 2) | (in
[2] >> 6)];
60 out
[3] = base64tab
[in
[2] & 0x3f];
64 /* Base64-encode a byte string which may or may not be the totality of
65 the data being encoded. INBUF and *INBUFLEN carry the leftover
66 data from call to call, and *LINELEN carries the length of the
67 current output line. Make INBUF have room for three characters and
68 initialize *INBUFLEN and *LINELEN to 0. Output will be appended to
71 encode_bytes(svn_stringbuf_t
*str
, const char *data
, apr_size_t len
,
72 unsigned char *inbuf
, int *inbuflen
, int *linelen
)
75 const char *p
= data
, *end
= data
+ len
;
77 /* Keep encoding three-byte groups until we run out. */
78 while (*inbuflen
+ (end
- p
) >= 3)
80 memcpy(inbuf
+ *inbuflen
, p
, 3 - *inbuflen
);
82 encode_group(inbuf
, group
);
83 svn_stringbuf_appendbytes(str
, group
, 4);
86 if (*linelen
== BASE64_LINELEN
)
88 svn_stringbuf_appendcstr(str
, "\n");
93 /* Tack any extra input onto *INBUF. */
94 memcpy(inbuf
+ *inbuflen
, p
, end
- p
);
95 *inbuflen
+= (end
- p
);
99 /* Encode leftover data, if any, and possibly a final newline,
100 appending to STR. LEN must be in the range 0..2. */
102 encode_partial_group(svn_stringbuf_t
*str
, const unsigned char *extra
,
103 int len
, int linelen
)
105 unsigned char ingroup
[3];
110 memcpy(ingroup
, extra
, len
);
111 memset(ingroup
+ len
, 0, 3 - len
);
112 encode_group(ingroup
, outgroup
);
113 memset(outgroup
+ (len
+ 1), '=', 4 - (len
+ 1));
114 svn_stringbuf_appendbytes(str
, outgroup
, 4);
118 svn_stringbuf_appendcstr(str
, "\n");
122 /* Write handler for svn_base64_encode. */
124 encode_data(void *baton
, const char *data
, apr_size_t
*len
)
126 struct encode_baton
*eb
= baton
;
127 apr_pool_t
*subpool
= svn_pool_create(eb
->pool
);
128 svn_stringbuf_t
*encoded
= svn_stringbuf_create("", subpool
);
130 svn_error_t
*err
= SVN_NO_ERROR
;
132 /* Encode this block of data and write it out. */
133 encode_bytes(encoded
, data
, *len
, eb
->buf
, &eb
->buflen
, &eb
->linelen
);
134 enclen
= encoded
->len
;
136 err
= svn_stream_write(eb
->output
, encoded
->data
, &enclen
);
137 svn_pool_destroy(subpool
);
142 /* Close handler for svn_base64_encode(). */
144 finish_encoding_data(void *baton
)
146 struct encode_baton
*eb
= baton
;
147 svn_stringbuf_t
*encoded
= svn_stringbuf_create("", eb
->pool
);
149 svn_error_t
*err
= SVN_NO_ERROR
;
151 /* Encode a partial group at the end if necessary, and write it out. */
152 encode_partial_group(encoded
, eb
->buf
, eb
->buflen
, eb
->linelen
);
153 enclen
= encoded
->len
;
155 err
= svn_stream_write(eb
->output
, encoded
->data
, &enclen
);
157 /* Pass on the close request and clean up the baton. */
158 if (err
== SVN_NO_ERROR
)
159 err
= svn_stream_close(eb
->output
);
160 svn_pool_destroy(eb
->pool
);
166 svn_base64_encode(svn_stream_t
*output
, apr_pool_t
*pool
)
168 apr_pool_t
*subpool
= svn_pool_create(pool
);
169 struct encode_baton
*eb
= apr_palloc(subpool
, sizeof(*eb
));
170 svn_stream_t
*stream
;
176 stream
= svn_stream_create(eb
, pool
);
177 svn_stream_set_write(stream
, encode_data
);
178 svn_stream_set_close(stream
, finish_encoding_data
);
184 svn_base64_encode_string(const svn_string_t
*str
, apr_pool_t
*pool
)
186 svn_stringbuf_t
*encoded
= svn_stringbuf_create("", pool
);
187 svn_string_t
*retval
= apr_pcalloc(pool
, sizeof(*retval
));
188 unsigned char ingroup
[3];
189 int ingrouplen
= 0, linelen
= 0;
191 encode_bytes(encoded
, str
->data
, str
->len
, ingroup
, &ingrouplen
, &linelen
);
192 encode_partial_group(encoded
, ingroup
, ingrouplen
, linelen
);
193 retval
->data
= encoded
->data
;
194 retval
->len
= encoded
->len
;
200 /* Base64-encoded input --> binary output */
202 struct decode_baton
{
203 svn_stream_t
*output
;
204 unsigned char buf
[4]; /* Bytes waiting to be decoded */
205 int buflen
; /* Number of bytes waiting */
206 svn_boolean_t done
; /* True if we already saw an '=' */
211 /* Base64-decode a group. IN needs to have four bytes and OUT needs
212 to have room for three bytes. The input bytes must already have
213 been decoded from base64tab into the range 0..63. The four
214 six-byte values are pasted together to form three eight-bit bytes. */
215 static APR_INLINE
void
216 decode_group(const unsigned char *in
, char *out
)
218 out
[0] = (in
[0] << 2) | (in
[1] >> 4);
219 out
[1] = ((in
[1] & 0xf) << 4) | (in
[2] >> 2);
220 out
[2] = ((in
[2] & 0x3) << 6) | in
[3];
224 /* Decode a byte string which may or may not be the total amount of
225 data being decoded. INBUF and *INBUFLEN carry the leftover bytes
226 from call to call, and *DONE keeps track of whether we've seen an
227 '=' which terminates the encoded data. Have room for four bytes in
228 INBUF and initialize *INBUFLEN to 0 and *DONE to FALSE. Output
229 will be appended to STR. */
231 decode_bytes(svn_stringbuf_t
*str
, const char *data
, apr_size_t len
,
232 unsigned char *inbuf
, int *inbuflen
, svn_boolean_t
*done
)
234 const char *p
, *find
;
237 for (p
= data
; !*done
&& p
< data
+ len
; p
++)
241 /* We are at the end and have to decode a partial group. */
244 memset(inbuf
+ *inbuflen
, 0, 4 - *inbuflen
);
245 decode_group(inbuf
, group
);
246 svn_stringbuf_appendbytes(str
, group
, *inbuflen
- 1);
252 find
= strchr(base64tab
, *p
);
254 inbuf
[(*inbuflen
)++] = find
- base64tab
;
257 decode_group(inbuf
, group
);
258 svn_stringbuf_appendbytes(str
, group
, 3);
266 /* Write handler for svn_base64_decode. */
268 decode_data(void *baton
, const char *data
, apr_size_t
*len
)
270 struct decode_baton
*db
= baton
;
272 svn_stringbuf_t
*decoded
;
274 svn_error_t
*err
= SVN_NO_ERROR
;
276 /* Decode this block of data. */
277 subpool
= svn_pool_create(db
->pool
);
278 decoded
= svn_stringbuf_create("", subpool
);
279 decode_bytes(decoded
, data
, *len
, db
->buf
, &db
->buflen
, &db
->done
);
281 /* Write the output, clean up, go home. */
282 declen
= decoded
->len
;
284 err
= svn_stream_write(db
->output
, decoded
->data
, &declen
);
285 svn_pool_destroy(subpool
);
290 /* Close handler for svn_base64_decode(). */
292 finish_decoding_data(void *baton
)
294 struct decode_baton
*db
= baton
;
297 /* Pass on the close request and clean up the baton. */
298 err
= svn_stream_close(db
->output
);
299 svn_pool_destroy(db
->pool
);
305 svn_base64_decode(svn_stream_t
*output
, apr_pool_t
*pool
)
307 apr_pool_t
*subpool
= svn_pool_create(pool
);
308 struct decode_baton
*db
= apr_palloc(subpool
, sizeof(*db
));
309 svn_stream_t
*stream
;
315 stream
= svn_stream_create(db
, pool
);
316 svn_stream_set_write(stream
, decode_data
);
317 svn_stream_set_close(stream
, finish_decoding_data
);
323 svn_base64_decode_string(const svn_string_t
*str
, apr_pool_t
*pool
)
325 svn_stringbuf_t
*decoded
= svn_stringbuf_create("", pool
);
326 svn_string_t
*retval
= apr_pcalloc(pool
, sizeof(*retval
));
327 unsigned char ingroup
[4];
329 svn_boolean_t done
= FALSE
;
331 decode_bytes(decoded
, str
->data
, str
->len
, ingroup
, &ingrouplen
, &done
);
332 retval
->data
= decoded
->data
;
333 retval
->len
= decoded
->len
;
339 svn_base64_from_md5(unsigned char digest
[], apr_pool_t
*pool
)
341 svn_stringbuf_t
*md5str
;
342 unsigned char ingroup
[3];
343 int ingrouplen
= 0, linelen
= 0;
344 md5str
= svn_stringbuf_create("", pool
);
346 /* This cast is safe because we know encode_bytes does a memcpy and
347 * does an implicit unsigned char * cast.
349 encode_bytes(md5str
, (char*)digest
, APR_MD5_DIGESTSIZE
, ingroup
,
350 &ingrouplen
, &linelen
);
351 encode_partial_group(md5str
, ingroup
, ingrouplen
, linelen
);
353 /* Our base64-encoding routines append a final newline if any data
354 was created at all, so let's hack that off. */
358 (md5str
)->data
[(md5str
)->len
] = 0;