* subversion/libsvn_subr/validate.c
[svn.git] / subversion / libsvn_subr / svn_base64.c
blob38862217e278e135718b25d42d6b031f0c642d9a
1 /*
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 * ====================================================================
21 #include <string.h>
23 #include <apr.h>
24 #include <apr_pools.h>
25 #include <apr_general.h> /* for APR_INLINE */
27 #include "svn_pools.h"
28 #include "svn_io.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 */
41 struct encode_baton {
42 svn_stream_t *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 */
46 apr_pool_t *pool;
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
69 STR. */
70 static void
71 encode_bytes(svn_stringbuf_t *str, const char *data, apr_size_t len,
72 unsigned char *inbuf, int *inbuflen, int *linelen)
74 char group[4];
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);
81 p += (3 - *inbuflen);
82 encode_group(inbuf, group);
83 svn_stringbuf_appendbytes(str, group, 4);
84 *inbuflen = 0;
85 *linelen += 4;
86 if (*linelen == BASE64_LINELEN)
88 svn_stringbuf_appendcstr(str, "\n");
89 *linelen = 0;
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. */
101 static void
102 encode_partial_group(svn_stringbuf_t *str, const unsigned char *extra,
103 int len, int linelen)
105 unsigned char ingroup[3];
106 char outgroup[4];
108 if (len > 0)
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);
115 linelen += 4;
117 if (linelen > 0)
118 svn_stringbuf_appendcstr(str, "\n");
122 /* Write handler for svn_base64_encode. */
123 static svn_error_t *
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);
129 apr_size_t enclen;
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;
135 if (enclen != 0)
136 err = svn_stream_write(eb->output, encoded->data, &enclen);
137 svn_pool_destroy(subpool);
138 return err;
142 /* Close handler for svn_base64_encode(). */
143 static svn_error_t *
144 finish_encoding_data(void *baton)
146 struct encode_baton *eb = baton;
147 svn_stringbuf_t *encoded = svn_stringbuf_create("", eb->pool);
148 apr_size_t enclen;
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;
154 if (enclen != 0)
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);
161 return err;
165 svn_stream_t *
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;
172 eb->output = output;
173 eb->buflen = 0;
174 eb->linelen = 0;
175 eb->pool = subpool;
176 stream = svn_stream_create(eb, pool);
177 svn_stream_set_write(stream, encode_data);
178 svn_stream_set_close(stream, finish_encoding_data);
179 return stream;
183 const svn_string_t *
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;
195 return retval;
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 '=' */
207 apr_pool_t *pool;
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];
223 /* Lookup table for base64 characters; reverse_base64[ch] gives a
224 negative value if ch is not a valid base64 character, or otherwise
225 the value of the byte represented; 'A' => 0 etc. */
226 static const signed char reverse_base64[256] = {
227 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
228 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
229 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
230 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
231 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
232 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
233 -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
234 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
235 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
236 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
237 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
238 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
239 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
240 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
241 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
242 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
245 /* Decode a byte string which may or may not be the total amount of
246 data being decoded. INBUF and *INBUFLEN carry the leftover bytes
247 from call to call, and *DONE keeps track of whether we've seen an
248 '=' which terminates the encoded data. Have room for four bytes in
249 INBUF and initialize *INBUFLEN to 0 and *DONE to FALSE. Output
250 will be appended to STR. */
251 static void
252 decode_bytes(svn_stringbuf_t *str, const char *data, apr_size_t len,
253 unsigned char *inbuf, int *inbuflen, svn_boolean_t *done)
255 const char *p;
256 char group[3];
257 signed char find;
259 /* Resize the stringbuf to make room for the (approximate) size of
260 output, to avoid repeated resizes later. */
261 svn_stringbuf_ensure(str, (len / 4) * 3 + 3);
263 for (p = data; !*done && p < data + len; p++)
265 if (*p == '=')
267 /* We are at the end and have to decode a partial group. */
268 if (*inbuflen >= 2)
270 memset(inbuf + *inbuflen, 0, 4 - *inbuflen);
271 decode_group(inbuf, group);
272 svn_stringbuf_appendbytes(str, group, *inbuflen - 1);
274 *done = TRUE;
276 else
278 find = reverse_base64[(unsigned char)*p];
279 if (find >= 0)
280 inbuf[(*inbuflen)++] = find;
281 if (*inbuflen == 4)
283 decode_group(inbuf, group);
284 svn_stringbuf_appendbytes(str, group, 3);
285 *inbuflen = 0;
292 /* Write handler for svn_base64_decode. */
293 static svn_error_t *
294 decode_data(void *baton, const char *data, apr_size_t *len)
296 struct decode_baton *db = baton;
297 apr_pool_t *subpool;
298 svn_stringbuf_t *decoded;
299 apr_size_t declen;
300 svn_error_t *err = SVN_NO_ERROR;
302 /* Decode this block of data. */
303 subpool = svn_pool_create(db->pool);
304 decoded = svn_stringbuf_create("", subpool);
305 decode_bytes(decoded, data, *len, db->buf, &db->buflen, &db->done);
307 /* Write the output, clean up, go home. */
308 declen = decoded->len;
309 if (declen != 0)
310 err = svn_stream_write(db->output, decoded->data, &declen);
311 svn_pool_destroy(subpool);
312 return err;
316 /* Close handler for svn_base64_decode(). */
317 static svn_error_t *
318 finish_decoding_data(void *baton)
320 struct decode_baton *db = baton;
321 svn_error_t *err;
323 /* Pass on the close request and clean up the baton. */
324 err = svn_stream_close(db->output);
325 svn_pool_destroy(db->pool);
326 return err;
330 svn_stream_t *
331 svn_base64_decode(svn_stream_t *output, apr_pool_t *pool)
333 apr_pool_t *subpool = svn_pool_create(pool);
334 struct decode_baton *db = apr_palloc(subpool, sizeof(*db));
335 svn_stream_t *stream;
337 db->output = output;
338 db->buflen = 0;
339 db->done = FALSE;
340 db->pool = subpool;
341 stream = svn_stream_create(db, pool);
342 svn_stream_set_write(stream, decode_data);
343 svn_stream_set_close(stream, finish_decoding_data);
344 return stream;
348 const svn_string_t *
349 svn_base64_decode_string(const svn_string_t *str, apr_pool_t *pool)
351 svn_stringbuf_t *decoded = svn_stringbuf_create("", pool);
352 svn_string_t *retval = apr_pcalloc(pool, sizeof(*retval));
353 unsigned char ingroup[4];
354 int ingrouplen = 0;
355 svn_boolean_t done = FALSE;
357 decode_bytes(decoded, str->data, str->len, ingroup, &ingrouplen, &done);
358 retval->data = decoded->data;
359 retval->len = decoded->len;
360 return retval;
364 svn_stringbuf_t *
365 svn_base64_from_md5(unsigned char digest[], apr_pool_t *pool)
367 svn_stringbuf_t *md5str;
368 unsigned char ingroup[3];
369 int ingrouplen = 0, linelen = 0;
370 md5str = svn_stringbuf_create("", pool);
372 /* This cast is safe because we know encode_bytes does a memcpy and
373 * does an implicit unsigned char * cast.
375 encode_bytes(md5str, (char*)digest, APR_MD5_DIGESTSIZE, ingroup,
376 &ingrouplen, &linelen);
377 encode_partial_group(md5str, ingroup, ingrouplen, linelen);
379 /* Our base64-encoding routines append a final newline if any data
380 was created at all, so let's hack that off. */
381 if ((md5str)->len)
383 (md5str)->len--;
384 (md5str)->data[(md5str)->len] = 0;
387 return md5str;