1251881Speter/*
2251881Speter * base64.c:  base64 encoding and decoding functions
3251881Speter *
4251881Speter * ====================================================================
5251881Speter *    Licensed to the Apache Software Foundation (ASF) under one
6251881Speter *    or more contributor license agreements.  See the NOTICE file
7251881Speter *    distributed with this work for additional information
8251881Speter *    regarding copyright ownership.  The ASF licenses this file
9251881Speter *    to you under the Apache License, Version 2.0 (the
10251881Speter *    "License"); you may not use this file except in compliance
11251881Speter *    with the License.  You may obtain a copy of the License at
12251881Speter *
13251881Speter *      http://www.apache.org/licenses/LICENSE-2.0
14251881Speter *
15251881Speter *    Unless required by applicable law or agreed to in writing,
16251881Speter *    software distributed under the License is distributed on an
17251881Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18251881Speter *    KIND, either express or implied.  See the License for the
19251881Speter *    specific language governing permissions and limitations
20251881Speter *    under the License.
21251881Speter * ====================================================================
22251881Speter */
23251881Speter
24251881Speter
25251881Speter
26251881Speter#include <string.h>
27251881Speter
28251881Speter#include <apr.h>
29251881Speter#include <apr_pools.h>
30251881Speter#include <apr_general.h>        /* for APR_INLINE */
31251881Speter
32251881Speter#include "svn_pools.h"
33251881Speter#include "svn_io.h"
34251881Speter#include "svn_error.h"
35251881Speter#include "svn_base64.h"
36251881Speter#include "private/svn_string_private.h"
37251881Speter#include "private/svn_subr_private.h"
38251881Speter
39251881Speter/* When asked to format the base64-encoded output as multiple lines,
40251881Speter   we put this many chars in each line (plus one new line char) unless
41251881Speter   we run out of data.
42251881Speter   It is vital for some of the optimizations below that this value is
43251881Speter   a multiple of 4. */
44251881Speter#define BASE64_LINELEN 76
45251881Speter
46251881Speter/* This number of bytes is encoded in a line of base64 chars. */
47251881Speter#define BYTES_PER_LINE (BASE64_LINELEN / 4 * 3)
48251881Speter
49251881Speter/* Value -> base64 char mapping table (2^6 entries) */
50251881Speterstatic const char base64tab[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
51251881Speter                                "abcdefghijklmnopqrstuvwxyz0123456789+/";
52251881Speter
53251881Speter
54251881Speter/* Binary input --> base64-encoded output */
55251881Speter
56251881Speterstruct encode_baton {
57251881Speter  svn_stream_t *output;
58251881Speter  unsigned char buf[3];         /* Bytes waiting to be encoded */
59251881Speter  size_t buflen;                /* Number of bytes waiting */
60251881Speter  size_t linelen;               /* Bytes output so far on this line */
61251881Speter  apr_pool_t *scratch_pool;
62251881Speter};
63251881Speter
64251881Speter
65251881Speter/* Base64-encode a group.  IN needs to have three bytes and OUT needs
66251881Speter   to have room for four bytes.  The input group is treated as four
67251881Speter   six-bit units which are treated as lookups into base64tab for the
68251881Speter   bytes of the output group.  */
69251881Speterstatic APR_INLINE void
70251881Speterencode_group(const unsigned char *in, char *out)
71251881Speter{
72251881Speter  /* Expand input bytes to machine word length (with zero extra cost
73251881Speter     on x86/x64) ... */
74251881Speter  apr_size_t part0 = in[0];
75251881Speter  apr_size_t part1 = in[1];
76251881Speter  apr_size_t part2 = in[2];
77251881Speter
78251881Speter  /* ... to prevent these arithmetic operations from being limited to
79251881Speter     byte size.  This saves non-zero cost conversions of the result when
80251881Speter     calculating the addresses within base64tab. */
81251881Speter  out[0] = base64tab[part0 >> 2];
82251881Speter  out[1] = base64tab[((part0 & 3) << 4) | (part1 >> 4)];
83251881Speter  out[2] = base64tab[((part1 & 0xf) << 2) | (part2 >> 6)];
84251881Speter  out[3] = base64tab[part2 & 0x3f];
85251881Speter}
86251881Speter
87251881Speter/* Base64-encode a line, i.e. BYTES_PER_LINE bytes from DATA into
88251881Speter   BASE64_LINELEN chars and append it to STR.  It does not assume that
89251881Speter   a new line char will be appended, though.
90251881Speter   The code in this function will simply transform the data without
91251881Speter   performing any boundary checks.  Therefore, DATA must have at least
92251881Speter   BYTES_PER_LINE left and space for at least another BASE64_LINELEN
93251881Speter   chars must have been pre-allocated in STR before calling this
94251881Speter   function. */
95251881Speterstatic void
96251881Speterencode_line(svn_stringbuf_t *str, const char *data)
97251881Speter{
98251881Speter  /* Translate directly from DATA to STR->DATA. */
99251881Speter  const unsigned char *in = (const unsigned char *)data;
100251881Speter  char *out = str->data + str->len;
101251881Speter  char *end = out + BASE64_LINELEN;
102251881Speter
103251881Speter  /* We assume that BYTES_PER_LINE is a multiple of 3 and BASE64_LINELEN
104251881Speter     a multiple of 4. */
105251881Speter  for ( ; out != end; in += 3, out += 4)
106251881Speter    encode_group(in, out);
107251881Speter
108251881Speter  /* Expand and terminate the string. */
109251881Speter  *out = '\0';
110251881Speter  str->len += BASE64_LINELEN;
111251881Speter}
112251881Speter
113251881Speter/* (Continue to) Base64-encode the byte string DATA (of length LEN)
114251881Speter   into STR. Include newlines every so often if BREAK_LINES is true.
115251881Speter   INBUF, INBUFLEN, and LINELEN are used internally; the caller shall
116251881Speter   make INBUF have room for three characters and initialize *INBUFLEN
117251881Speter   and *LINELEN to 0.
118251881Speter
119251881Speter   INBUF and *INBUFLEN carry the leftover data from call to call, and
120251881Speter   *LINELEN carries the length of the current output line. */
121251881Speterstatic void
122251881Speterencode_bytes(svn_stringbuf_t *str, const void *data, apr_size_t len,
123251881Speter             unsigned char *inbuf, size_t *inbuflen, size_t *linelen,
124251881Speter             svn_boolean_t break_lines)
125251881Speter{
126251881Speter  char group[4];
127251881Speter  const char *p = data, *end = p + len;
128251881Speter  apr_size_t buflen;
129251881Speter
130251881Speter  /* Resize the stringbuf to make room for the (approximate) size of
131251881Speter     output, to avoid repeated resizes later.
132251881Speter     Please note that our optimized code relies on the fact that STR
133251881Speter     never needs to be resized until we leave this function. */
134251881Speter  buflen = len * 4 / 3 + 4;
135251881Speter  if (break_lines)
136251881Speter    {
137251881Speter      /* Add an extra space for line breaks. */
138251881Speter      buflen += buflen / BASE64_LINELEN;
139251881Speter    }
140251881Speter  svn_stringbuf_ensure(str, str->len + buflen);
141251881Speter
142251881Speter  /* Keep encoding three-byte groups until we run out.  */
143251881Speter  while (*inbuflen + (end - p) >= 3)
144251881Speter    {
145251881Speter      /* May we encode BYTES_PER_LINE bytes without caring about
146251881Speter         line breaks, data in the temporary INBUF or running out
147251881Speter         of data? */
148251881Speter      if (   *inbuflen == 0
149251881Speter          && (*linelen == 0 || !break_lines)
150251881Speter          && (end - p >= BYTES_PER_LINE))
151251881Speter        {
152251881Speter          /* Yes, we can encode a whole chunk of data at once. */
153251881Speter          encode_line(str, p);
154251881Speter          p += BYTES_PER_LINE;
155251881Speter          *linelen += BASE64_LINELEN;
156251881Speter        }
157251881Speter      else
158251881Speter        {
159251881Speter          /* No, this is one of a number of special cases.
160251881Speter             Encode the data byte by byte. */
161251881Speter          memcpy(inbuf + *inbuflen, p, 3 - *inbuflen);
162251881Speter          p += (3 - *inbuflen);
163251881Speter          encode_group(inbuf, group);
164251881Speter          svn_stringbuf_appendbytes(str, group, 4);
165251881Speter          *inbuflen = 0;
166251881Speter          *linelen += 4;
167251881Speter        }
168251881Speter
169251881Speter      /* Add line breaks as necessary. */
170251881Speter      if (break_lines && *linelen == BASE64_LINELEN)
171251881Speter        {
172251881Speter          svn_stringbuf_appendbyte(str, '\n');
173251881Speter          *linelen = 0;
174251881Speter        }
175251881Speter    }
176251881Speter
177251881Speter  /* Tack any extra input onto *INBUF.  */
178251881Speter  memcpy(inbuf + *inbuflen, p, end - p);
179251881Speter  *inbuflen += (end - p);
180251881Speter}
181251881Speter
182251881Speter
183251881Speter/* Encode leftover data, if any, and possibly a final newline (if
184251881Speter   there has been any data and BREAK_LINES is set), appending to STR.
185251881Speter   LEN must be in the range 0..2.  */
186251881Speterstatic void
187251881Speterencode_partial_group(svn_stringbuf_t *str, const unsigned char *extra,
188251881Speter                     size_t len, size_t linelen, svn_boolean_t break_lines)
189251881Speter{
190251881Speter  unsigned char ingroup[3];
191251881Speter  char outgroup[4];
192251881Speter
193251881Speter  if (len > 0)
194251881Speter    {
195251881Speter      memcpy(ingroup, extra, len);
196251881Speter      memset(ingroup + len, 0, 3 - len);
197251881Speter      encode_group(ingroup, outgroup);
198251881Speter      memset(outgroup + (len + 1), '=', 4 - (len + 1));
199251881Speter      svn_stringbuf_appendbytes(str, outgroup, 4);
200251881Speter      linelen += 4;
201251881Speter    }
202251881Speter  if (break_lines && linelen > 0)
203251881Speter    svn_stringbuf_appendbyte(str, '\n');
204251881Speter}
205251881Speter
206251881Speter
207251881Speter/* Write handler for svn_base64_encode.  */
208251881Speterstatic svn_error_t *
209251881Speterencode_data(void *baton, const char *data, apr_size_t *len)
210251881Speter{
211251881Speter  struct encode_baton *eb = baton;
212251881Speter  svn_stringbuf_t *encoded = svn_stringbuf_create_empty(eb->scratch_pool);
213251881Speter  apr_size_t enclen;
214251881Speter  svn_error_t *err = SVN_NO_ERROR;
215251881Speter
216251881Speter  /* Encode this block of data and write it out.  */
217251881Speter  encode_bytes(encoded, data, *len, eb->buf, &eb->buflen, &eb->linelen, TRUE);
218251881Speter  enclen = encoded->len;
219251881Speter  if (enclen != 0)
220251881Speter    err = svn_stream_write(eb->output, encoded->data, &enclen);
221251881Speter  svn_pool_clear(eb->scratch_pool);
222251881Speter  return err;
223251881Speter}
224251881Speter
225251881Speter
226251881Speter/* Close handler for svn_base64_encode().  */
227251881Speterstatic svn_error_t *
228251881Speterfinish_encoding_data(void *baton)
229251881Speter{
230251881Speter  struct encode_baton *eb = baton;
231251881Speter  svn_stringbuf_t *encoded = svn_stringbuf_create_empty(eb->scratch_pool);
232251881Speter  apr_size_t enclen;
233251881Speter  svn_error_t *err = SVN_NO_ERROR;
234251881Speter
235251881Speter  /* Encode a partial group at the end if necessary, and write it out.  */
236251881Speter  encode_partial_group(encoded, eb->buf, eb->buflen, eb->linelen, TRUE);
237251881Speter  enclen = encoded->len;
238251881Speter  if (enclen != 0)
239251881Speter    err = svn_stream_write(eb->output, encoded->data, &enclen);
240251881Speter
241251881Speter  /* Pass on the close request and clean up the baton.  */
242251881Speter  if (err == SVN_NO_ERROR)
243251881Speter    err = svn_stream_close(eb->output);
244251881Speter  svn_pool_destroy(eb->scratch_pool);
245251881Speter  return err;
246251881Speter}
247251881Speter
248251881Speter
249251881Spetersvn_stream_t *
250251881Spetersvn_base64_encode(svn_stream_t *output, apr_pool_t *pool)
251251881Speter{
252251881Speter  struct encode_baton *eb = apr_palloc(pool, sizeof(*eb));
253251881Speter  svn_stream_t *stream;
254251881Speter
255251881Speter  eb->output = output;
256251881Speter  eb->buflen = 0;
257251881Speter  eb->linelen = 0;
258251881Speter  eb->scratch_pool = svn_pool_create(pool);
259251881Speter  stream = svn_stream_create(eb, pool);
260251881Speter  svn_stream_set_write(stream, encode_data);
261251881Speter  svn_stream_set_close(stream, finish_encoding_data);
262251881Speter  return stream;
263251881Speter}
264251881Speter
265251881Speter
266251881Speterconst svn_string_t *
267251881Spetersvn_base64_encode_string2(const svn_string_t *str,
268251881Speter                          svn_boolean_t break_lines,
269251881Speter                          apr_pool_t *pool)
270251881Speter{
271251881Speter  svn_stringbuf_t *encoded = svn_stringbuf_create_empty(pool);
272251881Speter  unsigned char ingroup[3];
273251881Speter  size_t ingrouplen = 0;
274251881Speter  size_t linelen = 0;
275251881Speter
276251881Speter  encode_bytes(encoded, str->data, str->len, ingroup, &ingrouplen, &linelen,
277251881Speter               break_lines);
278251881Speter  encode_partial_group(encoded, ingroup, ingrouplen, linelen,
279251881Speter                       break_lines);
280251881Speter  return svn_stringbuf__morph_into_string(encoded);
281251881Speter}
282251881Speter
283251881Speterconst svn_string_t *
284251881Spetersvn_base64_encode_string(const svn_string_t *str, apr_pool_t *pool)
285251881Speter{
286251881Speter  return svn_base64_encode_string2(str, TRUE, pool);
287251881Speter}
288251881Speter
289251881Speter
290251881Speter
291251881Speter/* Base64-encoded input --> binary output */
292251881Speter
293251881Speterstruct decode_baton {
294251881Speter  svn_stream_t *output;
295251881Speter  unsigned char buf[4];         /* Bytes waiting to be decoded */
296251881Speter  int buflen;                   /* Number of bytes waiting */
297251881Speter  svn_boolean_t done;           /* True if we already saw an '=' */
298251881Speter  apr_pool_t *scratch_pool;
299251881Speter};
300251881Speter
301251881Speter
302251881Speter/* Base64-decode a group.  IN needs to have four bytes and OUT needs
303251881Speter   to have room for three bytes.  The input bytes must already have
304251881Speter   been decoded from base64tab into the range 0..63.  The four
305251881Speter   six-bit values are pasted together to form three eight-bit bytes.  */
306251881Speterstatic APR_INLINE void
307251881Speterdecode_group(const unsigned char *in, char *out)
308251881Speter{
309251881Speter  out[0] = (char)((in[0] << 2) | (in[1] >> 4));
310251881Speter  out[1] = (char)(((in[1] & 0xf) << 4) | (in[2] >> 2));
311251881Speter  out[2] = (char)(((in[2] & 0x3) << 6) | in[3]);
312251881Speter}
313251881Speter
314251881Speter/* Lookup table for base64 characters; reverse_base64[ch] gives a
315251881Speter   negative value if ch is not a valid base64 character, or otherwise
316251881Speter   the value of the byte represented; 'A' => 0 etc. */
317251881Speterstatic const signed char reverse_base64[256] = {
318251881Speter-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
319251881Speter-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
320251881Speter-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
321251881Speter52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
322251881Speter-1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
323251881Speter15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
324251881Speter-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
325251881Speter41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
326251881Speter-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
327251881Speter-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
328251881Speter-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
329251881Speter-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
330251881Speter-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
331251881Speter-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
332251881Speter-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
333251881Speter-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
334251881Speter};
335251881Speter
336251881Speter/* Similar to decode_group but this function also translates the
337251881Speter   6-bit values from the IN buffer before translating them.
338251881Speter   Return FALSE if a non-base64 char (e.g. '=' or new line)
339251881Speter   has been encountered. */
340251881Speterstatic APR_INLINE svn_boolean_t
341251881Speterdecode_group_directly(const unsigned char *in, char *out)
342251881Speter{
343251881Speter  /* Translate the base64 chars in values [0..63, 0xff] */
344251881Speter  apr_size_t part0 = (unsigned char)reverse_base64[(unsigned char)in[0]];
345251881Speter  apr_size_t part1 = (unsigned char)reverse_base64[(unsigned char)in[1]];
346251881Speter  apr_size_t part2 = (unsigned char)reverse_base64[(unsigned char)in[2]];
347251881Speter  apr_size_t part3 = (unsigned char)reverse_base64[(unsigned char)in[3]];
348251881Speter
349251881Speter  /* Pack 4x6 bits into 3x8.*/
350251881Speter  out[0] = (char)((part0 << 2) | (part1 >> 4));
351251881Speter  out[1] = (char)(((part1 & 0xf) << 4) | (part2 >> 2));
352251881Speter  out[2] = (char)(((part2 & 0x3) << 6) | part3);
353251881Speter
354251881Speter  /* FALSE, iff any part is 0xff. */
355251881Speter  return (part0 | part1 | part2 | part3) != (unsigned char)(-1);
356251881Speter}
357251881Speter
358251881Speter/* Base64-encode up to BASE64_LINELEN chars from *DATA and append it to
359251881Speter   STR.  After the function returns, *DATA will point to the first char
360251881Speter   that has not been translated, yet.  Returns TRUE if all BASE64_LINELEN
361251881Speter   chars could be translated, i.e. no special char has been encountered
362251881Speter   in between.
363251881Speter   The code in this function will simply transform the data without
364251881Speter   performing any boundary checks.  Therefore, DATA must have at least
365251881Speter   BASE64_LINELEN left and space for at least another BYTES_PER_LINE
366251881Speter   chars must have been pre-allocated in STR before calling this
367251881Speter   function. */
368251881Speterstatic svn_boolean_t
369251881Speterdecode_line(svn_stringbuf_t *str, const char **data)
370251881Speter{
371251881Speter  /* Decode up to BYTES_PER_LINE bytes directly from *DATA into STR->DATA. */
372251881Speter  const unsigned char *p = *(const unsigned char **)data;
373251881Speter  char *out = str->data + str->len;
374251881Speter  char *end = out + BYTES_PER_LINE;
375251881Speter
376251881Speter  /* We assume that BYTES_PER_LINE is a multiple of 3 and BASE64_LINELEN
377251881Speter     a multiple of 4.  Stop translation as soon as we encounter a special
378251881Speter     char.  Leave the entire group untouched in that case. */
379251881Speter  for (; out < end; p += 4, out += 3)
380251881Speter    if (!decode_group_directly(p, out))
381251881Speter      break;
382251881Speter
383251881Speter  /* Update string sizes and positions. */
384251881Speter  str->len = out - str->data;
385251881Speter  *out = '\0';
386251881Speter  *data = (const char *)p;
387251881Speter
388251881Speter  /* Return FALSE, if the caller should continue the decoding process
389251881Speter     using the slow standard method. */
390251881Speter  return out == end;
391251881Speter}
392251881Speter
393251881Speter
394251881Speter/* (Continue to) Base64-decode the byte string DATA (of length LEN)
395251881Speter   into STR. INBUF, INBUFLEN, and DONE are used internally; the
396251881Speter   caller shall have room for four bytes in INBUF and initialize
397251881Speter   *INBUFLEN to 0 and *DONE to FALSE.
398251881Speter
399251881Speter   INBUF and *INBUFLEN carry the leftover bytes from call to call, and
400251881Speter   *DONE keeps track of whether we've seen an '=' which terminates the
401251881Speter   encoded data. */
402251881Speterstatic void
403251881Speterdecode_bytes(svn_stringbuf_t *str, const char *data, apr_size_t len,
404251881Speter             unsigned char *inbuf, int *inbuflen, svn_boolean_t *done)
405251881Speter{
406251881Speter  const char *p = data;
407251881Speter  char group[3];
408251881Speter  signed char find;
409251881Speter  const char *end = data + len;
410251881Speter
411251881Speter  /* Resize the stringbuf to make room for the maximum size of output,
412251881Speter     to avoid repeated resizes later.  The optimizations in
413251881Speter     decode_line rely on no resizes being necessary!
414251881Speter
415251881Speter     (*inbuflen+len) is encoded data length
416251881Speter     (*inbuflen+len)/4 is the number of complete 4-bytes sets
417251881Speter     (*inbuflen+len)/4*3 is the number of decoded bytes
418251881Speter     svn_stringbuf_ensure will add an additional byte for the terminating 0.
419251881Speter  */
420251881Speter  svn_stringbuf_ensure(str, str->len + ((*inbuflen + len) / 4) * 3);
421251881Speter
422251881Speter  while ( !*done && p < end )
423251881Speter    {
424251881Speter      /* If no data is left in temporary INBUF and there is at least
425251881Speter         one line-sized chunk left to decode, we may use the optimized
426251881Speter         code path. */
427251881Speter      if ((*inbuflen == 0) && (p + BASE64_LINELEN <= end))
428251881Speter        if (decode_line(str, &p))
429251881Speter          continue;
430251881Speter
431251881Speter      /* A special case or decode_line encountered a special char. */
432251881Speter      if (*p == '=')
433251881Speter        {
434251881Speter          /* We are at the end and have to decode a partial group.  */
435251881Speter          if (*inbuflen >= 2)
436251881Speter            {
437251881Speter              memset(inbuf + *inbuflen, 0, 4 - *inbuflen);
438251881Speter              decode_group(inbuf, group);
439251881Speter              svn_stringbuf_appendbytes(str, group, *inbuflen - 1);
440251881Speter            }
441251881Speter          *done = TRUE;
442251881Speter        }
443251881Speter      else
444251881Speter        {
445251881Speter          find = reverse_base64[(unsigned char)*p];
446251881Speter          ++p;
447251881Speter
448251881Speter          if (find >= 0)
449251881Speter            inbuf[(*inbuflen)++] = find;
450251881Speter          if (*inbuflen == 4)
451251881Speter            {
452251881Speter              decode_group(inbuf, group);
453251881Speter              svn_stringbuf_appendbytes(str, group, 3);
454251881Speter              *inbuflen = 0;
455251881Speter            }
456251881Speter        }
457251881Speter    }
458251881Speter}
459251881Speter
460251881Speter
461251881Speter/* Write handler for svn_base64_decode.  */
462251881Speterstatic svn_error_t *
463251881Speterdecode_data(void *baton, const char *data, apr_size_t *len)
464251881Speter{
465251881Speter  struct decode_baton *db = baton;
466251881Speter  svn_stringbuf_t *decoded;
467251881Speter  apr_size_t declen;
468251881Speter  svn_error_t *err = SVN_NO_ERROR;
469251881Speter
470251881Speter  /* Decode this block of data.  */
471251881Speter  decoded = svn_stringbuf_create_empty(db->scratch_pool);
472251881Speter  decode_bytes(decoded, data, *len, db->buf, &db->buflen, &db->done);
473251881Speter
474251881Speter  /* Write the output, clean up, go home.  */
475251881Speter  declen = decoded->len;
476251881Speter  if (declen != 0)
477251881Speter    err = svn_stream_write(db->output, decoded->data, &declen);
478251881Speter  svn_pool_clear(db->scratch_pool);
479251881Speter  return err;
480251881Speter}
481251881Speter
482251881Speter
483251881Speter/* Close handler for svn_base64_decode().  */
484251881Speterstatic svn_error_t *
485251881Speterfinish_decoding_data(void *baton)
486251881Speter{
487251881Speter  struct decode_baton *db = baton;
488251881Speter  svn_error_t *err;
489251881Speter
490251881Speter  /* Pass on the close request and clean up the baton.  */
491251881Speter  err = svn_stream_close(db->output);
492251881Speter  svn_pool_destroy(db->scratch_pool);
493251881Speter  return err;
494251881Speter}
495251881Speter
496251881Speter
497251881Spetersvn_stream_t *
498251881Spetersvn_base64_decode(svn_stream_t *output, apr_pool_t *pool)
499251881Speter{
500251881Speter  struct decode_baton *db = apr_palloc(pool, sizeof(*db));
501251881Speter  svn_stream_t *stream;
502251881Speter
503251881Speter  db->output = output;
504251881Speter  db->buflen = 0;
505251881Speter  db->done = FALSE;
506251881Speter  db->scratch_pool = svn_pool_create(pool);
507251881Speter  stream = svn_stream_create(db, pool);
508251881Speter  svn_stream_set_write(stream, decode_data);
509251881Speter  svn_stream_set_close(stream, finish_decoding_data);
510251881Speter  return stream;
511251881Speter}
512251881Speter
513251881Speter
514251881Speterconst svn_string_t *
515251881Spetersvn_base64_decode_string(const svn_string_t *str, apr_pool_t *pool)
516251881Speter{
517251881Speter  svn_stringbuf_t *decoded = svn_stringbuf_create_empty(pool);
518251881Speter  unsigned char ingroup[4];
519251881Speter  int ingrouplen = 0;
520251881Speter  svn_boolean_t done = FALSE;
521251881Speter
522251881Speter  decode_bytes(decoded, str->data, str->len, ingroup, &ingrouplen, &done);
523251881Speter  return svn_stringbuf__morph_into_string(decoded);
524251881Speter}
525251881Speter
526251881Speter
527251881Speter/* Return a base64-encoded representation of CHECKSUM, allocated in POOL.
528251881Speter   If CHECKSUM->kind is not recognized, return NULL.
529251881Speter   ### That 'NULL' claim was in the header file when this was public, but
530251881Speter   doesn't look true in the implementation.
531251881Speter
532251881Speter   ### This is now only used as a new implementation of svn_base64_from_md5();
533251881Speter   it would probably be safer to revert that to its old implementation. */
534251881Speterstatic svn_stringbuf_t *
535251881Speterbase64_from_checksum(const svn_checksum_t *checksum, apr_pool_t *pool)
536251881Speter{
537251881Speter  svn_stringbuf_t *checksum_str;
538251881Speter  unsigned char ingroup[3];
539251881Speter  size_t ingrouplen = 0;
540251881Speter  size_t linelen = 0;
541251881Speter  checksum_str = svn_stringbuf_create_empty(pool);
542251881Speter
543251881Speter  encode_bytes(checksum_str, checksum->digest,
544251881Speter               svn_checksum_size(checksum), ingroup, &ingrouplen,
545251881Speter               &linelen, TRUE);
546251881Speter  encode_partial_group(checksum_str, ingroup, ingrouplen, linelen, TRUE);
547251881Speter
548251881Speter  /* Our base64-encoding routines append a final newline if any data
549251881Speter     was created at all, so let's hack that off. */
550251881Speter  if (checksum_str->len)
551251881Speter    {
552251881Speter      checksum_str->len--;
553251881Speter      checksum_str->data[checksum_str->len] = 0;
554251881Speter    }
555251881Speter
556251881Speter  return checksum_str;
557251881Speter}
558251881Speter
559251881Speter
560251881Spetersvn_stringbuf_t *
561251881Spetersvn_base64_from_md5(unsigned char digest[], apr_pool_t *pool)
562251881Speter{
563251881Speter  svn_checksum_t *checksum
564251881Speter    = svn_checksum__from_digest_md5(digest, pool);
565251881Speter
566251881Speter  return base64_from_checksum(checksum, pool);
567251881Speter}
568