11590Srgrimes/* 21590Srgrimes * base64.c: base64 encoding and decoding functions 31590Srgrimes * 41590Srgrimes * ==================================================================== 51590Srgrimes * Licensed to the Apache Software Foundation (ASF) under one 61590Srgrimes * or more contributor license agreements. See the NOTICE file 71590Srgrimes * distributed with this work for additional information 81590Srgrimes * regarding copyright ownership. The ASF licenses this file 91590Srgrimes * to you under the Apache License, Version 2.0 (the 101590Srgrimes * "License"); you may not use this file except in compliance 111590Srgrimes * with the License. You may obtain a copy of the License at 121590Srgrimes * 131590Srgrimes * http://www.apache.org/licenses/LICENSE-2.0 141590Srgrimes * 151590Srgrimes * Unless required by applicable law or agreed to in writing, 161590Srgrimes * software distributed under the License is distributed on an 171590Srgrimes * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 181590Srgrimes * KIND, either express or implied. See the License for the 191590Srgrimes * specific language governing permissions and limitations 201590Srgrimes * under the License. 211590Srgrimes * ==================================================================== 221590Srgrimes */ 231590Srgrimes 241590Srgrimes 251590Srgrimes 261590Srgrimes#include <string.h> 271590Srgrimes 281590Srgrimes#include <apr.h> 291590Srgrimes#include <apr_pools.h> 301590Srgrimes#include <apr_general.h> /* for APR_INLINE */ 311590Srgrimes 3260833Sjake#include "svn_pools.h" 3360833Sjake#include "svn_io.h" 341590Srgrimes#include "svn_error.h" 351590Srgrimes#include "svn_base64.h" 361590Srgrimes#include "private/svn_string_private.h" 371590Srgrimes#include "private/svn_subr_private.h" 381590Srgrimes 391590Srgrimes/* When asked to format the base64-encoded output as multiple lines, 401590Srgrimes we put this many chars in each line (plus one new line char) unless 411590Srgrimes we run out of data. 421590Srgrimes It is vital for some of the optimizations below that this value is 431590Srgrimes a multiple of 4. */ 441590Srgrimes#define BASE64_LINELEN 76 451590Srgrimes 461590Srgrimes/* This number of bytes is encoded in a line of base64 chars. */ 471590Srgrimes#define BYTES_PER_LINE (BASE64_LINELEN / 4 * 3) 481590Srgrimes 491590Srgrimes/* Value -> base64 char mapping table (2^6 entries) */ 501590Srgrimesstatic const char base64tab[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \ 5116438Sache "abcdefghijklmnopqrstuvwxyz0123456789+/"; 521590Srgrimes 531590Srgrimes 541590Srgrimes/* Binary input --> base64-encoded output */ 551590Srgrimes 561590Srgrimesstruct encode_baton { 571590Srgrimes svn_stream_t *output; 581590Srgrimes unsigned char buf[3]; /* Bytes waiting to be encoded */ 591590Srgrimes size_t buflen; /* Number of bytes waiting */ 6011547Sdg size_t linelen; /* Bytes output so far on this line */ 611590Srgrimes apr_pool_t *scratch_pool; 621590Srgrimes}; 631590Srgrimes 641590Srgrimes 651590Srgrimes/* Base64-encode a group. IN needs to have three bytes and OUT needs 661590Srgrimes to have room for four bytes. The input group is treated as four 671590Srgrimes six-bit units which are treated as lookups into base64tab for the 681590Srgrimes bytes of the output group. */ 691590Srgrimesstatic APR_INLINE void 701590Srgrimesencode_group(const unsigned char *in, char *out) 711590Srgrimes{ 721590Srgrimes /* Expand input bytes to machine word length (with zero extra cost 731590Srgrimes on x86/x64) ... */ 741590Srgrimes apr_size_t part0 = in[0]; 751590Srgrimes apr_size_t part1 = in[1]; 761590Srgrimes apr_size_t part2 = in[2]; 7760833Sjake 7811547Sdg /* ... to prevent these arithmetic operations from being limited to 7911547Sdg byte size. This saves non-zero cost conversions of the result when 8036062Sjb calculating the addresses within base64tab. */ 811590Srgrimes out[0] = base64tab[part0 >> 2]; 8260833Sjake out[1] = base64tab[((part0 & 3) << 4) | (part1 >> 4)]; 8311547Sdg out[2] = base64tab[((part1 & 0xf) << 2) | (part2 >> 6)]; 841590Srgrimes out[3] = base64tab[part2 & 0x3f]; 851590Srgrimes} 861590Srgrimes 871590Srgrimes/* Base64-encode a line, i.e. BYTES_PER_LINE bytes from DATA into 8836434Sdanny BASE64_LINELEN chars and append it to STR. It does not assume that 8936434Sdanny a new line char will be appended, though. 901590Srgrimes The code in this function will simply transform the data without 911590Srgrimes performing any boundary checks. Therefore, DATA must have at least 921590Srgrimes BYTES_PER_LINE left and space for at least another BASE64_LINELEN 931590Srgrimes chars must have been pre-allocated in STR before calling this 941590Srgrimes function. */ 9511547Sdgstatic void 961590Srgrimesencode_line(svn_stringbuf_t *str, const char *data) 971590Srgrimes{ 9836434Sdanny /* Translate directly from DATA to STR->DATA. */ 9936434Sdanny const unsigned char *in = (const unsigned char *)data; 10036434Sdanny char *out = str->data + str->len; 10136434Sdanny char *end = out + BASE64_LINELEN; 10236434Sdanny 10336434Sdanny /* We assume that BYTES_PER_LINE is a multiple of 3 and BASE64_LINELEN 10436434Sdanny a multiple of 4. */ 10536434Sdanny for ( ; out != end; in += 3, out += 4) 1061590Srgrimes encode_group(in, out); 1071590Srgrimes 1081590Srgrimes /* Expand and terminate the string. */ 1091590Srgrimes *out = '\0'; 1101590Srgrimes str->len += BASE64_LINELEN; 1111590Srgrimes} 1121590Srgrimes 1131590Srgrimes/* (Continue to) Base64-encode the byte string DATA (of length LEN) 1141590Srgrimes into STR. Include newlines every so often if BREAK_LINES is true. 1151590Srgrimes INBUF, INBUFLEN, and LINELEN are used internally; the caller shall 11616438Sache make INBUF have room for three characters and initialize *INBUFLEN 11716438Sache and *LINELEN to 0. 1181590Srgrimes 11936434Sdanny INBUF and *INBUFLEN carry the leftover data from call to call, and 1201590Srgrimes *LINELEN carries the length of the current output line. */ 1211590Srgrimesstatic void 1221590Srgrimesencode_bytes(svn_stringbuf_t *str, const void *data, apr_size_t len, 1231590Srgrimes unsigned char *inbuf, size_t *inbuflen, size_t *linelen, 1241590Srgrimes svn_boolean_t break_lines) 1251590Srgrimes{ 1261590Srgrimes char group[4]; 1271590Srgrimes const char *p = data, *end = p + len; 1281590Srgrimes apr_size_t buflen; 1291590Srgrimes 1301590Srgrimes /* Resize the stringbuf to make room for the (approximate) size of 1311590Srgrimes output, to avoid repeated resizes later. 1321590Srgrimes Please note that our optimized code relies on the fact that STR 1331590Srgrimes never needs to be resized until we leave this function. */ 1341590Srgrimes buflen = len * 4 / 3 + 4; 1351590Srgrimes if (break_lines) 1361590Srgrimes { 1371590Srgrimes /* Add an extra space for line breaks. */ 1381590Srgrimes buflen += buflen / BASE64_LINELEN; 1391590Srgrimes } 1401590Srgrimes svn_stringbuf_ensure(str, str->len + buflen); 1411590Srgrimes 1421590Srgrimes /* Keep encoding three-byte groups until we run out. */ 1431590Srgrimes while (*inbuflen + (end - p) >= 3) 14436434Sdanny { 14536434Sdanny /* May we encode BYTES_PER_LINE bytes without caring about 14636434Sdanny line breaks, data in the temporary INBUF or running out 1471590Srgrimes of data? */ 1481590Srgrimes if ( *inbuflen == 0 1491590Srgrimes && (*linelen == 0 || !break_lines) 15036434Sdanny && (end - p >= BYTES_PER_LINE)) 15136434Sdanny { 15236434Sdanny /* Yes, we can encode a whole chunk of data at once. */ 1531590Srgrimes encode_line(str, p); 1541590Srgrimes p += BYTES_PER_LINE; 15536434Sdanny *linelen += BASE64_LINELEN; 1561590Srgrimes } 1571590Srgrimes else 15836434Sdanny { 15936434Sdanny /* No, this is one of a number of special cases. 1601590Srgrimes Encode the data byte by byte. */ 1611590Srgrimes memcpy(inbuf + *inbuflen, p, 3 - *inbuflen); 1621590Srgrimes p += (3 - *inbuflen); 1631590Srgrimes encode_group(inbuf, group); 1641590Srgrimes svn_stringbuf_appendbytes(str, group, 4); 1651590Srgrimes *inbuflen = 0; 1661590Srgrimes *linelen += 4; 1671590Srgrimes } 1681590Srgrimes 1691590Srgrimes /* Add line breaks as necessary. */ 1701590Srgrimes if (break_lines && *linelen == BASE64_LINELEN) 1711590Srgrimes { 1721590Srgrimes svn_stringbuf_appendbyte(str, '\n'); 1731590Srgrimes *linelen = 0; 1741590Srgrimes } 1751590Srgrimes } 1761590Srgrimes 1771590Srgrimes /* Tack any extra input onto *INBUF. */ 1781590Srgrimes memcpy(inbuf + *inbuflen, p, end - p); 1791590Srgrimes *inbuflen += (end - p); 1801590Srgrimes} 1811590Srgrimes 1821590Srgrimes 18319223Sjoerg/* Encode leftover data, if any, and possibly a final newline (if 1841590Srgrimes there has been any data and BREAK_LINES is set), appending to STR. 18536062Sjb LEN must be in the range 0..2. */ 18636062Sjbstatic void 1871590Srgrimesencode_partial_group(svn_stringbuf_t *str, const unsigned char *extra, 18816438Sache size_t len, size_t linelen, svn_boolean_t break_lines) 18916438Sache{ 19016438Sache unsigned char ingroup[3]; 1911590Srgrimes char outgroup[4]; 19211547Sdg 19311547Sdg if (len > 0) 1941590Srgrimes { 1951590Srgrimes memcpy(ingroup, extra, len); 1961590Srgrimes memset(ingroup + len, 0, 3 - len); 1971590Srgrimes encode_group(ingroup, outgroup); 1981590Srgrimes memset(outgroup + (len + 1), '=', 4 - (len + 1)); 1991590Srgrimes svn_stringbuf_appendbytes(str, outgroup, 4); 2001590Srgrimes linelen += 4; 2011590Srgrimes } 2021590Srgrimes if (break_lines && linelen > 0) 2031590Srgrimes svn_stringbuf_appendbyte(str, '\n'); 2041590Srgrimes} 2051590Srgrimes 2061590Srgrimes 2071590Srgrimes/* Write handler for svn_base64_encode. */ 2081590Srgrimesstatic svn_error_t * 2091590Srgrimesencode_data(void *baton, const char *data, apr_size_t *len) 2101590Srgrimes{ 2111590Srgrimes struct encode_baton *eb = baton; 2121590Srgrimes svn_stringbuf_t *encoded = svn_stringbuf_create_empty(eb->scratch_pool); 21319223Sjoerg apr_size_t enclen; 21411547Sdg svn_error_t *err = SVN_NO_ERROR; 21519223Sjoerg 21619223Sjoerg /* Encode this block of data and write it out. */ 21719223Sjoerg encode_bytes(encoded, data, *len, eb->buf, &eb->buflen, &eb->linelen, TRUE); 21811547Sdg enclen = encoded->len; 2191590Srgrimes if (enclen != 0) 2201590Srgrimes err = svn_stream_write(eb->output, encoded->data, &enclen); 2211590Srgrimes svn_pool_clear(eb->scratch_pool); 22211547Sdg return err; 22316438Sache} 22416438Sache 22520158Sache 2261590Srgrimes/* Close handler for svn_base64_encode(). */ 2271590Srgrimesstatic svn_error_t * 2281590Srgrimesfinish_encoding_data(void *baton) 2291590Srgrimes{ 2301590Srgrimes struct encode_baton *eb = baton; 2311590Srgrimes svn_stringbuf_t *encoded = svn_stringbuf_create_empty(eb->scratch_pool); 2321590Srgrimes apr_size_t enclen; 2331590Srgrimes svn_error_t *err = SVN_NO_ERROR; 2341590Srgrimes 2351590Srgrimes /* Encode a partial group at the end if necessary, and write it out. */ 2361590Srgrimes encode_partial_group(encoded, eb->buf, eb->buflen, eb->linelen, TRUE); 2371590Srgrimes enclen = encoded->len; 2381590Srgrimes if (enclen != 0) 2391590Srgrimes err = svn_stream_write(eb->output, encoded->data, &enclen); 2401590Srgrimes 2411590Srgrimes /* Pass on the close request and clean up the baton. */ 24211547Sdg if (err == SVN_NO_ERROR) 24316438Sache err = svn_stream_close(eb->output); 24416438Sache svn_pool_destroy(eb->scratch_pool); 24520158Sache return err; 24611547Sdg} 24711547Sdg 24811547Sdg 24911547Sdgsvn_stream_t * 2501590Srgrimessvn_base64_encode(svn_stream_t *output, apr_pool_t *pool) 2511590Srgrimes{ 2521590Srgrimes struct encode_baton *eb = apr_palloc(pool, sizeof(*eb)); 2531590Srgrimes svn_stream_t *stream; 2541590Srgrimes 25511547Sdg eb->output = output; 25611547Sdg eb->buflen = 0; 25711547Sdg eb->linelen = 0; 25811547Sdg eb->scratch_pool = svn_pool_create(pool); 25911547Sdg stream = svn_stream_create(eb, pool); 26011547Sdg svn_stream_set_write(stream, encode_data); 26111547Sdg svn_stream_set_close(stream, finish_encoding_data); 26211547Sdg return stream; 26311547Sdg} 26411547Sdg 26511547Sdg 26611547Sdgconst svn_string_t * 26711547Sdgsvn_base64_encode_string2(const svn_string_t *str, 26811547Sdg svn_boolean_t break_lines, 26911547Sdg apr_pool_t *pool) 2701590Srgrimes{ 27111547Sdg svn_stringbuf_t *encoded = svn_stringbuf_create_empty(pool); 27211547Sdg unsigned char ingroup[3]; 27311547Sdg size_t ingrouplen = 0; 27411547Sdg size_t linelen = 0; 27511547Sdg 27611547Sdg encode_bytes(encoded, str->data, str->len, ingroup, &ingrouplen, &linelen, 27711547Sdg break_lines); 27811547Sdg encode_partial_group(encoded, ingroup, ingrouplen, linelen, 27911547Sdg break_lines); 28011547Sdg return svn_stringbuf__morph_into_string(encoded); 28116438Sache} 28216438Sache 28320158Sacheconst svn_string_t * 28411547Sdgsvn_base64_encode_string(const svn_string_t *str, apr_pool_t *pool) 28511547Sdg{ 28611547Sdg return svn_base64_encode_string2(str, TRUE, pool); 28711547Sdg} 28811547Sdg 28911547Sdg 29011547Sdg 29111547Sdg/* Base64-encoded input --> binary output */ 29211547Sdg 29311547Sdgstruct decode_baton { 29411547Sdg svn_stream_t *output; 29516438Sache unsigned char buf[4]; /* Bytes waiting to be decoded */ 29616438Sache int buflen; /* Number of bytes waiting */ 29716438Sache svn_boolean_t done; /* True if we already saw an '=' */ 29816438Sache apr_pool_t *scratch_pool; 29916438Sache}; 30011547Sdg 30136434Sdanny 30236434Sdanny/* Base64-decode a group. IN needs to have four bytes and OUT needs 30336434Sdanny to have room for three bytes. The input bytes must already have 30436434Sdanny been decoded from base64tab into the range 0..63. The four 30536434Sdanny six-bit values are pasted together to form three eight-bit bytes. */ 30636434Sdannystatic APR_INLINE void 30736434Sdannydecode_group(const unsigned char *in, char *out) 30836434Sdanny{ 30936434Sdanny out[0] = (char)((in[0] << 2) | (in[1] >> 4)); 31036434Sdanny out[1] = (char)(((in[1] & 0xf) << 4) | (in[2] >> 2)); 31136434Sdanny out[2] = (char)(((in[2] & 0x3) << 6) | in[3]); 31236434Sdanny} 3131590Srgrimes 31411547Sdg/* Lookup table for base64 characters; reverse_base64[ch] gives a 31511547Sdg negative value if ch is not a valid base64 character, or otherwise 31611547Sdg the value of the byte represented; 'A' => 0 etc. */ 31711547Sdgstatic const signed char reverse_base64[256] = { 31811547Sdg-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 31911547Sdg-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 3201590Srgrimes-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 3211590Srgrimes52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, 3221590Srgrimes-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 3231590Srgrimes15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, 32416438Sache-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 32535658Ssteve41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, 32635658Ssteve-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 3271590Srgrimes-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 3281590Srgrimes-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 3291590Srgrimes-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 3301590Srgrimes-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 3311590Srgrimes-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 3321590Srgrimes-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 3331590Srgrimes-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 33411547Sdg}; 3351590Srgrimes 3361590Srgrimes/* Similar to decode_group but this function also translates the 3371590Srgrimes 6-bit values from the IN buffer before translating them. 3381590Srgrimes Return FALSE if a non-base64 char (e.g. '=' or new line) 3391590Srgrimes has been encountered. */ 3401590Srgrimesstatic APR_INLINE svn_boolean_t 3411590Srgrimesdecode_group_directly(const unsigned char *in, char *out) 3421590Srgrimes{ 3431590Srgrimes /* Translate the base64 chars in values [0..63, 0xff] */ 3441590Srgrimes apr_size_t part0 = (unsigned char)reverse_base64[(unsigned char)in[0]]; 3451590Srgrimes apr_size_t part1 = (unsigned char)reverse_base64[(unsigned char)in[1]]; 3461590Srgrimes apr_size_t part2 = (unsigned char)reverse_base64[(unsigned char)in[2]]; 3471590Srgrimes apr_size_t part3 = (unsigned char)reverse_base64[(unsigned char)in[3]]; 3481590Srgrimes 3491590Srgrimes /* Pack 4x6 bits into 3x8.*/ 3501590Srgrimes out[0] = (char)((part0 << 2) | (part1 >> 4)); 3511590Srgrimes out[1] = (char)(((part1 & 0xf) << 4) | (part2 >> 2)); 3521590Srgrimes out[2] = (char)(((part2 & 0x3) << 6) | part3); 3531590Srgrimes 3541590Srgrimes /* FALSE, iff any part is 0xff. */ 3551590Srgrimes return (part0 | part1 | part2 | part3) != (unsigned char)(-1); 3561590Srgrimes} 3571590Srgrimes 3581590Srgrimes/* Base64-encode up to BASE64_LINELEN chars from *DATA and append it to 3591590Srgrimes STR. After the function returns, *DATA will point to the first char 3601590Srgrimes that has not been translated, yet. Returns TRUE if all BASE64_LINELEN 3611590Srgrimes chars could be translated, i.e. no special char has been encountered 3621590Srgrimes in between. 3631590Srgrimes The code in this function will simply transform the data without 3641590Srgrimes performing any boundary checks. Therefore, DATA must have at least 3651590Srgrimes BASE64_LINELEN left and space for at least another BYTES_PER_LINE 3661590Srgrimes chars must have been pre-allocated in STR before calling this 3671590Srgrimes function. */ 3681590Srgrimesstatic svn_boolean_t 3691590Srgrimesdecode_line(svn_stringbuf_t *str, const char **data) 3701590Srgrimes{ 3711590Srgrimes /* Decode up to BYTES_PER_LINE bytes directly from *DATA into STR->DATA. */ 3721590Srgrimes const unsigned char *p = *(const unsigned char **)data; 3731590Srgrimes char *out = str->data + str->len; 3741590Srgrimes char *end = out + BYTES_PER_LINE; 3751590Srgrimes 3761590Srgrimes /* We assume that BYTES_PER_LINE is a multiple of 3 and BASE64_LINELEN 3771590Srgrimes a multiple of 4. Stop translation as soon as we encounter a special 3781590Srgrimes char. Leave the entire group untouched in that case. */ 3791590Srgrimes for (; out < end; p += 4, out += 3) 3801590Srgrimes if (!decode_group_directly(p, out)) 3811590Srgrimes break; 3821590Srgrimes 3831590Srgrimes /* Update string sizes and positions. */ 3841590Srgrimes str->len = out - str->data; 3851590Srgrimes *out = '\0'; 3861590Srgrimes *data = (const char *)p; 3871590Srgrimes 3881590Srgrimes /* Return FALSE, if the caller should continue the decoding process 3891590Srgrimes using the slow standard method. */ 3901590Srgrimes return out == end; 3911590Srgrimes} 3921590Srgrimes 3931590Srgrimes 3941590Srgrimes/* (Continue to) Base64-decode the byte string DATA (of length LEN) 3951590Srgrimes into STR. INBUF, INBUFLEN, and DONE are used internally; the 3961590Srgrimes caller shall have room for four bytes in INBUF and initialize 3971590Srgrimes *INBUFLEN to 0 and *DONE to FALSE. 3981590Srgrimes 3991590Srgrimes INBUF and *INBUFLEN carry the leftover bytes from call to call, and 4001590Srgrimes *DONE keeps track of whether we've seen an '=' which terminates the 4011590Srgrimes encoded data. */ 4021590Srgrimesstatic void 4031590Srgrimesdecode_bytes(svn_stringbuf_t *str, const char *data, apr_size_t len, 4041590Srgrimes unsigned char *inbuf, int *inbuflen, svn_boolean_t *done) 4051590Srgrimes{ 4061590Srgrimes const char *p = data; 4071590Srgrimes char group[3]; 4081590Srgrimes signed char find; 4091590Srgrimes const char *end = data + len; 4101590Srgrimes 4111590Srgrimes /* Resize the stringbuf to make room for the maximum size of output, 4121590Srgrimes to avoid repeated resizes later. The optimizations in 4131590Srgrimes decode_line rely on no resizes being necessary! 4141590Srgrimes 4151590Srgrimes (*inbuflen+len) is encoded data length 4161590Srgrimes (*inbuflen+len)/4 is the number of complete 4-bytes sets 4171590Srgrimes (*inbuflen+len)/4*3 is the number of decoded bytes 4181590Srgrimes svn_stringbuf_ensure will add an additional byte for the terminating 0. 4191590Srgrimes */ 4201590Srgrimes svn_stringbuf_ensure(str, str->len + ((*inbuflen + len) / 4) * 3); 4211590Srgrimes 4221590Srgrimes while ( !*done && p < end ) 4231590Srgrimes { 4241590Srgrimes /* If no data is left in temporary INBUF and there is at least 4251590Srgrimes one line-sized chunk left to decode, we may use the optimized 4261590Srgrimes code path. */ 4271590Srgrimes if ((*inbuflen == 0) && (p + BASE64_LINELEN <= end)) 4281590Srgrimes if (decode_line(str, &p)) 4291590Srgrimes continue; 4301590Srgrimes 4311590Srgrimes /* A special case or decode_line encountered a special char. */ 4321590Srgrimes if (*p == '=') 4331590Srgrimes { 4341590Srgrimes /* We are at the end and have to decode a partial group. */ 4351590Srgrimes if (*inbuflen >= 2) 4361590Srgrimes { 4371590Srgrimes memset(inbuf + *inbuflen, 0, 4 - *inbuflen); 4381590Srgrimes decode_group(inbuf, group); 4391590Srgrimes svn_stringbuf_appendbytes(str, group, *inbuflen - 1); 4401590Srgrimes } 4411590Srgrimes *done = TRUE; 4421590Srgrimes } 4431590Srgrimes else 44416438Sache { 44516438Sache find = reverse_base64[(unsigned char)*p]; 4461590Srgrimes ++p; 44716438Sache 44816438Sache if (find >= 0) 4491590Srgrimes inbuf[(*inbuflen)++] = find; 4501590Srgrimes if (*inbuflen == 4) 4511590Srgrimes { 4521590Srgrimes decode_group(inbuf, group); 4531590Srgrimes svn_stringbuf_appendbytes(str, group, 3); 454 *inbuflen = 0; 455 } 456 } 457 } 458} 459 460 461/* Write handler for svn_base64_decode. */ 462static svn_error_t * 463decode_data(void *baton, const char *data, apr_size_t *len) 464{ 465 struct decode_baton *db = baton; 466 svn_stringbuf_t *decoded; 467 apr_size_t declen; 468 svn_error_t *err = SVN_NO_ERROR; 469 470 /* Decode this block of data. */ 471 decoded = svn_stringbuf_create_empty(db->scratch_pool); 472 decode_bytes(decoded, data, *len, db->buf, &db->buflen, &db->done); 473 474 /* Write the output, clean up, go home. */ 475 declen = decoded->len; 476 if (declen != 0) 477 err = svn_stream_write(db->output, decoded->data, &declen); 478 svn_pool_clear(db->scratch_pool); 479 return err; 480} 481 482 483/* Close handler for svn_base64_decode(). */ 484static svn_error_t * 485finish_decoding_data(void *baton) 486{ 487 struct decode_baton *db = baton; 488 svn_error_t *err; 489 490 /* Pass on the close request and clean up the baton. */ 491 err = svn_stream_close(db->output); 492 svn_pool_destroy(db->scratch_pool); 493 return err; 494} 495 496 497svn_stream_t * 498svn_base64_decode(svn_stream_t *output, apr_pool_t *pool) 499{ 500 struct decode_baton *db = apr_palloc(pool, sizeof(*db)); 501 svn_stream_t *stream; 502 503 db->output = output; 504 db->buflen = 0; 505 db->done = FALSE; 506 db->scratch_pool = svn_pool_create(pool); 507 stream = svn_stream_create(db, pool); 508 svn_stream_set_write(stream, decode_data); 509 svn_stream_set_close(stream, finish_decoding_data); 510 return stream; 511} 512 513 514const svn_string_t * 515svn_base64_decode_string(const svn_string_t *str, apr_pool_t *pool) 516{ 517 svn_stringbuf_t *decoded = svn_stringbuf_create_empty(pool); 518 unsigned char ingroup[4]; 519 int ingrouplen = 0; 520 svn_boolean_t done = FALSE; 521 522 decode_bytes(decoded, str->data, str->len, ingroup, &ingrouplen, &done); 523 return svn_stringbuf__morph_into_string(decoded); 524} 525 526 527/* Return a base64-encoded representation of CHECKSUM, allocated in POOL. 528 If CHECKSUM->kind is not recognized, return NULL. 529 ### That 'NULL' claim was in the header file when this was public, but 530 doesn't look true in the implementation. 531 532 ### This is now only used as a new implementation of svn_base64_from_md5(); 533 it would probably be safer to revert that to its old implementation. */ 534static svn_stringbuf_t * 535base64_from_checksum(const svn_checksum_t *checksum, apr_pool_t *pool) 536{ 537 svn_stringbuf_t *checksum_str; 538 unsigned char ingroup[3]; 539 size_t ingrouplen = 0; 540 size_t linelen = 0; 541 checksum_str = svn_stringbuf_create_empty(pool); 542 543 encode_bytes(checksum_str, checksum->digest, 544 svn_checksum_size(checksum), ingroup, &ingrouplen, 545 &linelen, TRUE); 546 encode_partial_group(checksum_str, ingroup, ingrouplen, linelen, TRUE); 547 548 /* Our base64-encoding routines append a final newline if any data 549 was created at all, so let's hack that off. */ 550 if (checksum_str->len) 551 { 552 checksum_str->len--; 553 checksum_str->data[checksum_str->len] = 0; 554 } 555 556 return checksum_str; 557} 558 559 560svn_stringbuf_t * 561svn_base64_from_md5(unsigned char digest[], apr_pool_t *pool) 562{ 563 svn_checksum_t *checksum 564 = svn_checksum__from_digest_md5(digest, pool); 565 566 return base64_from_checksum(checksum, pool); 567} 568