1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2008, Daniel Stenberg, <daniel@haxx.se>, et al.
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.
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.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 * $Id: base64.c,v 1.1.1.1 2008-09-23 16:32:05 hoffman Exp $
22 ***************************************************************************/
24 /* Base64 encoding/decoding
26 * Test harnesses down the bottom - compile with -DTEST_ENCODE for
27 * a program that will read in raw data from stdin and write out
28 * a base64-encoded version to stdout, and the length returned by the
29 * encoding function to stderr. Compile with -DTEST_DECODE for a program that
30 * will go the other way.
32 * This code will break if int is smaller than 32 bits
40 #define _MPRINTF_REPLACE /* use our functions only */
41 #include <curl/mprintf.h>
43 #include "urldata.h" /* for the SessionHandle definition */
44 #include "easyif.h" /* for Curl_convert_... prototypes */
45 #include "curl_base64.h"
48 /* include memdebug.h last */
51 /* ---- Base64 Encoding/Decoding Table --- */
52 static const char table64
[]=
53 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
55 static void decodeQuantum(unsigned char *dest
, const char *src
)
61 for(i
= 0; i
< 4; i
++) {
62 if((found
= strchr(table64
, src
[i
])) != NULL
)
63 x
= (x
<< 6) + (unsigned int)(found
- table64
);
64 else if(src
[i
] == '=')
68 dest
[2] = (unsigned char)(x
& 255);
70 dest
[1] = (unsigned char)(x
& 255);
72 dest
[0] = (unsigned char)(x
& 255);
76 * Curl_base64_decode()
78 * Given a base64 string at src, decode it and return an allocated memory in
79 * the *outptr. Returns the length of the decoded data.
81 size_t Curl_base64_decode(const char *src
, unsigned char **outptr
)
87 unsigned char lastQuantum
[3];
89 unsigned char *newstr
;
93 while((src
[length
] != '=') && src
[length
])
95 /* A maximum of two = padding characters is allowed */
96 if(src
[length
] == '=') {
98 if(src
[length
+equalsTerm
] == '=')
101 numQuantums
= (length
+ equalsTerm
) / 4;
103 /* Don't allocate a buffer if the decoded length is 0 */
107 rawlen
= (numQuantums
* 3) - equalsTerm
;
109 /* The buffer must be large enough to make room for the last quantum
110 (which may be partially thrown out) and the zero terminator. */
111 newstr
= malloc(rawlen
+4);
117 /* Decode all but the last quantum (which may not decode to a
118 multiple of 3 bytes) */
119 for(i
= 0; i
< numQuantums
- 1; i
++) {
120 decodeQuantum((unsigned char *)newstr
, src
);
121 newstr
+= 3; src
+= 4;
124 /* This final decode may actually read slightly past the end of the buffer
125 if the input string is missing pad bytes. This will almost always be
127 decodeQuantum(lastQuantum
, src
);
128 for(i
= 0; i
< 3 - equalsTerm
; i
++)
129 newstr
[i
] = lastQuantum
[i
];
131 newstr
[i
] = 0; /* zero terminate */
136 * Curl_base64_encode()
138 * Returns the length of the newly created base64 string. The third argument
139 * is a pointer to an allocated area holding the base64 data. If something
140 * went wrong, 0 is returned.
143 size_t Curl_base64_encode(struct SessionHandle
*data
,
144 const char *inp
, size_t insize
, char **outptr
)
146 unsigned char ibuf
[3];
147 unsigned char obuf
[4];
152 #ifdef CURL_DOES_CONVERSIONS
153 char *convbuf
= NULL
;
156 char *indata
= (char *)inp
;
158 *outptr
= NULL
; /* set to NULL in case of failure before we reach the end */
161 insize
= strlen(indata
);
163 base64data
= output
= (char*)malloc(insize
*4/3+4);
167 #ifdef CURL_DOES_CONVERSIONS
169 * The base64 data needs to be created using the network encoding
170 * not the host encoding. And we can't change the actual input
171 * so we copy it to a buffer, translate it, and use that instead.
174 convbuf
= (char*)malloc(insize
);
179 memcpy(convbuf
, indata
, insize
);
180 if(CURLE_OK
!= Curl_convert_to_network(data
, convbuf
, insize
)) {
185 indata
= convbuf
; /* switch to the converted buffer */
192 for (i
= inputparts
= 0; i
< 3; i
++) {
203 obuf
[0] = (unsigned char) ((ibuf
[0] & 0xFC) >> 2);
204 obuf
[1] = (unsigned char) (((ibuf
[0] & 0x03) << 4) | \
205 ((ibuf
[1] & 0xF0) >> 4));
206 obuf
[2] = (unsigned char) (((ibuf
[1] & 0x0F) << 2) | \
207 ((ibuf
[2] & 0xC0) >> 6));
208 obuf
[3] = (unsigned char) (ibuf
[2] & 0x3F);
211 case 1: /* only one byte read */
212 snprintf(output
, 5, "%c%c==",
216 case 2: /* two bytes read */
217 snprintf(output
, 5, "%c%c%c=",
223 snprintf(output
, 5, "%c%c%c%c",
233 *outptr
= base64data
; /* make it return the actual data memory */
235 #ifdef CURL_DOES_CONVERSIONS
239 return strlen(base64data
); /* return the length of the new data */
241 /* ---- End of Base64 Encoding ---- */
243 /************* TEST HARNESS STUFF ****************/
247 /* encoding test harness. Read in standard input and write out the length
248 * returned by Curl_base64_encode, followed by the base64'd data itself
252 #define TEST_NEED_SUCK
255 int main(int argc
, argv_item_t argv
[], char **envp
)
261 struct SessionHandle
*handle
= NULL
;
263 #ifdef CURL_DOES_CONVERSIONS
264 /* get a Curl handle so Curl_base64_encode can translate properly */
265 handle
= curl_easy_init();
267 fprintf(stderr
, "Error: curl_easy_init failed\n");
271 data
= (unsigned char *)suck(&dataLen
);
272 base64Len
= Curl_base64_encode(handle
, data
, dataLen
, &base64
);
274 fprintf(stderr
, "%d\n", base64Len
);
275 fprintf(stdout
, "%s\n", base64
);
277 free(base64
); free(data
);
278 #ifdef CURL_DOES_CONVERSIONS
279 curl_easy_cleanup(handle
);
286 /* decoding test harness. Read in a base64 string from stdin and write out the
287 * length returned by Curl_base64_decode, followed by the decoded data itself
289 * gcc -DTEST_DECODE base64.c -o base64 mprintf.o memdebug.o
293 #define TEST_NEED_SUCK
296 int main(int argc
, argv_item_t argv
[], char **envp
)
303 #ifdef CURL_DOES_CONVERSIONS
304 /* get a Curl handle so main can translate properly */
305 struct SessionHandle
*handle
= curl_easy_init();
307 fprintf(stderr
, "Error: curl_easy_init failed\n");
312 base64
= (char *)suck(&base64Len
);
313 dataLen
= Curl_base64_decode(base64
, &data
);
315 fprintf(stderr
, "%d\n", dataLen
);
317 for(i
=0; i
< dataLen
; i
+=0x10) {
318 printf("0x%02x: ", i
);
319 for(j
=0; j
< 0x10; j
++)
321 printf("%02x ", data
[i
+j
]);
327 for(j
=0; j
< 0x10; j
++)
328 if((j
+i
) < dataLen
) {
329 #ifdef CURL_DOES_CONVERSIONS
331 Curl_convert_from_network(handle
, &data
[i
+j
], (size_t)1))
333 #endif /* CURL_DOES_CONVERSIONS */
334 printf("%c", ISGRAPH(data
[i
+j
])?data
[i
+j
]:'.');
340 #ifdef CURL_DOES_CONVERSIONS
341 curl_easy_cleanup(handle
);
343 free(base64
); free(data
);
348 #ifdef TEST_NEED_SUCK
349 /* this function 'sucks' in as much as possible from stdin */
350 void *suck(int *lenptr
)
353 unsigned char *buf
= NULL
;
359 buf
= (unsigned char *)realloc(buf
, cursize
);
360 memset(buf
+ len
, 0, cursize
- len
);
361 lastread
= fread(buf
+ len
, 1, cursize
- len
, stdin
);
363 } while(!feof(stdin
));