Change the format of the revprops block sent in svnserve for
[svn.git] / subversion / libsvn_subr / svn_base64.c
blobf329128c04f11b20c8ff55cbe404be5489a10963
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];
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. */
230 static void
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;
235 char group[3];
237 for (p = data; !*done && p < data + len; p++)
239 if (*p == '=')
241 /* We are at the end and have to decode a partial group. */
242 if (*inbuflen >= 2)
244 memset(inbuf + *inbuflen, 0, 4 - *inbuflen);
245 decode_group(inbuf, group);
246 svn_stringbuf_appendbytes(str, group, *inbuflen - 1);
248 *done = TRUE;
250 else
252 find = strchr(base64tab, *p);
253 if (find != NULL)
254 inbuf[(*inbuflen)++] = find - base64tab;
255 if (*inbuflen == 4)
257 decode_group(inbuf, group);
258 svn_stringbuf_appendbytes(str, group, 3);
259 *inbuflen = 0;
266 /* Write handler for svn_base64_decode. */
267 static svn_error_t *
268 decode_data(void *baton, const char *data, apr_size_t *len)
270 struct decode_baton *db = baton;
271 apr_pool_t *subpool;
272 svn_stringbuf_t *decoded;
273 apr_size_t declen;
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;
283 if (declen != 0)
284 err = svn_stream_write(db->output, decoded->data, &declen);
285 svn_pool_destroy(subpool);
286 return err;
290 /* Close handler for svn_base64_decode(). */
291 static svn_error_t *
292 finish_decoding_data(void *baton)
294 struct decode_baton *db = baton;
295 svn_error_t *err;
297 /* Pass on the close request and clean up the baton. */
298 err = svn_stream_close(db->output);
299 svn_pool_destroy(db->pool);
300 return err;
304 svn_stream_t *
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;
311 db->output = output;
312 db->buflen = 0;
313 db->done = FALSE;
314 db->pool = subpool;
315 stream = svn_stream_create(db, pool);
316 svn_stream_set_write(stream, decode_data);
317 svn_stream_set_close(stream, finish_decoding_data);
318 return stream;
322 const svn_string_t *
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];
328 int ingrouplen = 0;
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;
334 return retval;
338 svn_stringbuf_t *
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. */
355 if ((md5str)->len)
357 (md5str)->len--;
358 (md5str)->data[(md5str)->len] = 0;
361 return md5str;