1/***************************************************************************
2 *                                  _   _ ____  _
3 *  Project                     ___| | | |  _ \| |
4 *                             / __| | | | |_) | |
5 *                            | (__| |_| |  _ <| |___
6 *                             \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
9 *
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at http://curl.haxx.se/docs/copyright.html.
13 *
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 ***************************************************************************/
22
23/* Base64 encoding/decoding */
24
25#include "setup.h"
26
27#define _MPRINTF_REPLACE /* use our functions only */
28#include <curl/mprintf.h>
29
30#include "urldata.h" /* for the SessionHandle definition */
31#include "warnless.h"
32#include "curl_base64.h"
33#include "curl_memory.h"
34#include "non-ascii.h"
35
36/* include memdebug.h last */
37#include "memdebug.h"
38
39/* ---- Base64 Encoding/Decoding Table --- */
40static const char table64[]=
41  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
42
43static void decodeQuantum(unsigned char *dest, const char *src)
44{
45  const char *s, *p;
46  unsigned long i, v, x = 0;
47
48  for(i = 0, s = src; i < 4; i++, s++) {
49    v = 0;
50    p = table64;
51    while(*p && (*p != *s)) {
52      v++;
53      p++;
54    }
55    if(*p == *s)
56      x = (x << 6) + v;
57    else if(*s == '=')
58      x = (x << 6);
59  }
60
61  dest[2] = curlx_ultouc(x);
62  x >>= 8;
63  dest[1] = curlx_ultouc(x);
64  x >>= 8;
65  dest[0] = curlx_ultouc(x);
66}
67
68/*
69 * Curl_base64_decode()
70 *
71 * Given a base64 NUL-terminated string at src, decode it and return a
72 * pointer in *outptr to a newly allocated memory area holding decoded
73 * data. Size of decoded data is returned in variable pointed by outlen.
74 *
75 * Returns CURLE_OK on success, otherwise specific error code. Function
76 * output shall not be considered valid unless CURLE_OK is returned.
77 *
78 * When decoded data length is 0, returns NULL in *outptr.
79 *
80 * @unittest: 1302
81 */
82CURLcode Curl_base64_decode(const char *src,
83                            unsigned char **outptr, size_t *outlen)
84{
85  size_t length = 0;
86  size_t equalsTerm = 0;
87  size_t i;
88  size_t numQuantums;
89  unsigned char lastQuantum[3];
90  size_t rawlen = 0;
91  unsigned char *newstr;
92
93  *outptr = NULL;
94  *outlen = 0;
95
96  while((src[length] != '=') && src[length])
97    length++;
98  /* A maximum of two = padding characters is allowed */
99  if(src[length] == '=') {
100    equalsTerm++;
101    if(src[length+equalsTerm] == '=')
102      equalsTerm++;
103  }
104  numQuantums = (length + equalsTerm) / 4;
105
106  /* Don't allocate a buffer if the decoded length is 0 */
107  if(numQuantums == 0)
108    return CURLE_OK;
109
110  rawlen = (numQuantums * 3) - equalsTerm;
111
112  /* The buffer must be large enough to make room for the last quantum
113  (which may be partially thrown out) and the zero terminator. */
114  newstr = malloc(rawlen+4);
115  if(!newstr)
116    return CURLE_OUT_OF_MEMORY;
117
118  *outptr = newstr;
119
120  /* Decode all but the last quantum (which may not decode to a
121  multiple of 3 bytes) */
122  for(i = 0; i < numQuantums - 1; i++) {
123    decodeQuantum(newstr, src);
124    newstr += 3; src += 4;
125  }
126
127  /* This final decode may actually read slightly past the end of the buffer
128  if the input string is missing pad bytes.  This will almost always be
129  harmless. */
130  decodeQuantum(lastQuantum, src);
131  for(i = 0; i < 3 - equalsTerm; i++)
132    newstr[i] = lastQuantum[i];
133
134  newstr[i] = '\0'; /* zero terminate */
135
136  *outlen = rawlen; /* return size of decoded data */
137
138  return CURLE_OK;
139}
140
141/*
142 * Curl_base64_encode()
143 *
144 * Given a pointer to an input buffer and an input size, encode it and
145 * return a pointer in *outptr to a newly allocated memory area holding
146 * encoded data. Size of encoded data is returned in variable pointed by
147 * outlen.
148 *
149 * Input length of 0 indicates input buffer holds a NUL-terminated string.
150 *
151 * Returns CURLE_OK on success, otherwise specific error code. Function
152 * output shall not be considered valid unless CURLE_OK is returned.
153 *
154 * When encoded data length is 0, returns NULL in *outptr.
155 *
156 * @unittest: 1302
157 */
158CURLcode Curl_base64_encode(struct SessionHandle *data,
159                            const char *inputbuff, size_t insize,
160                            char **outptr, size_t *outlen)
161{
162  CURLcode error;
163  unsigned char ibuf[3];
164  unsigned char obuf[4];
165  int i;
166  int inputparts;
167  char *output;
168  char *base64data;
169  char *convbuf = NULL;
170
171  const char *indata = inputbuff;
172
173  *outptr = NULL;
174  *outlen = 0;
175
176  if(0 == insize)
177    insize = strlen(indata);
178
179  base64data = output = malloc(insize*4/3+4);
180  if(NULL == output)
181    return CURLE_OUT_OF_MEMORY;
182
183  /*
184   * The base64 data needs to be created using the network encoding
185   * not the host encoding.  And we can't change the actual input
186   * so we copy it to a buffer, translate it, and use that instead.
187   */
188  error = Curl_convert_clone(data, indata, insize, &convbuf);
189  if(error) {
190    free(output);
191    return error;
192  }
193
194  if(convbuf)
195    indata = (char *)convbuf;
196
197  while(insize > 0) {
198    for(i = inputparts = 0; i < 3; i++) {
199      if(insize > 0) {
200        inputparts++;
201        ibuf[i] = (unsigned char) *indata;
202        indata++;
203        insize--;
204      }
205      else
206        ibuf[i] = 0;
207    }
208
209    obuf[0] = (unsigned char)  ((ibuf[0] & 0xFC) >> 2);
210    obuf[1] = (unsigned char) (((ibuf[0] & 0x03) << 4) | \
211                               ((ibuf[1] & 0xF0) >> 4));
212    obuf[2] = (unsigned char) (((ibuf[1] & 0x0F) << 2) | \
213                               ((ibuf[2] & 0xC0) >> 6));
214    obuf[3] = (unsigned char)   (ibuf[2] & 0x3F);
215
216    switch(inputparts) {
217    case 1: /* only one byte read */
218      snprintf(output, 5, "%c%c==",
219               table64[obuf[0]],
220               table64[obuf[1]]);
221      break;
222    case 2: /* two bytes read */
223      snprintf(output, 5, "%c%c%c=",
224               table64[obuf[0]],
225               table64[obuf[1]],
226               table64[obuf[2]]);
227      break;
228    default:
229      snprintf(output, 5, "%c%c%c%c",
230               table64[obuf[0]],
231               table64[obuf[1]],
232               table64[obuf[2]],
233               table64[obuf[3]] );
234      break;
235    }
236    output += 4;
237  }
238  *output = '\0';
239  *outptr = base64data; /* return pointer to new data, allocated memory */
240
241  if(convbuf)
242    free(convbuf);
243
244  *outlen = strlen(base64data); /* return the length of the new data */
245
246  return CURLE_OK;
247}
248/* ---- End of Base64 Encoding ---- */
249