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