1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2007, 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.2 2007/03/15 19:22:13 andy 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 */
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
])))
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 */
104 if (numQuantums
<= 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, -1 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
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
);
178 memcpy(convbuf
, indata
, insize
);
179 if(CURLE_OK
!= Curl_convert_to_network(data
, convbuf
, insize
)) {
183 indata
= convbuf
; /* switch to the converted buffer */
190 for (i
= inputparts
= 0; i
< 3; i
++) {
201 obuf
[0] = (unsigned char) ((ibuf
[0] & 0xFC) >> 2);
202 obuf
[1] = (unsigned char) (((ibuf
[0] & 0x03) << 4) | \
203 ((ibuf
[1] & 0xF0) >> 4));
204 obuf
[2] = (unsigned char) (((ibuf
[1] & 0x0F) << 2) | \
205 ((ibuf
[2] & 0xC0) >> 6));
206 obuf
[3] = (unsigned char) (ibuf
[2] & 0x3F);
209 case 1: /* only one byte read */
210 snprintf(output
, 5, "%c%c==",
214 case 2: /* two bytes read */
215 snprintf(output
, 5, "%c%c%c=",
221 snprintf(output
, 5, "%c%c%c%c",
231 *outptr
= base64data
; /* make it return the actual data memory */
233 #ifdef CURL_DOES_CONVERSIONS
237 return strlen(base64data
); /* return the length of the new data */
239 /* ---- End of Base64 Encoding ---- */
241 /************* TEST HARNESS STUFF ****************/
245 /* encoding test harness. Read in standard input and write out the length
246 * returned by Curl_base64_encode, followed by the base64'd data itself
250 #define TEST_NEED_SUCK
253 int main(int argc
, char **argv
, char **envp
)
259 struct SessionHandle
*handle
= NULL
;
261 #ifdef CURL_DOES_CONVERSIONS
262 /* get a Curl handle so Curl_base64_encode can translate properly */
263 handle
= curl_easy_init();
265 fprintf(stderr
, "Error: curl_easy_init failed\n");
269 data
= (unsigned char *)suck(&dataLen
);
270 base64Len
= Curl_base64_encode(handle
, data
, dataLen
, &base64
);
272 fprintf(stderr
, "%d\n", base64Len
);
273 fprintf(stdout
, "%s\n", base64
);
275 free(base64
); free(data
);
276 #ifdef CURL_DOES_CONVERSIONS
277 curl_easy_cleanup(handle
);
284 /* decoding test harness. Read in a base64 string from stdin and write out the
285 * length returned by Curl_base64_decode, followed by the decoded data itself
287 * gcc -DTEST_DECODE base64.c -o base64 mprintf.o memdebug.o
291 #define TEST_NEED_SUCK
294 int main(int argc
, char **argv
, char **envp
)
301 #ifdef CURL_DOES_CONVERSIONS
302 /* get a Curl handle so main can translate properly */
303 struct SessionHandle
*handle
= curl_easy_init();
305 fprintf(stderr
, "Error: curl_easy_init failed\n");
310 base64
= (char *)suck(&base64Len
);
311 dataLen
= Curl_base64_decode(base64
, &data
);
313 fprintf(stderr
, "%d\n", dataLen
);
315 for(i
=0; i
< dataLen
; i
+=0x10) {
316 printf("0x%02x: ", i
);
317 for(j
=0; j
< 0x10; j
++)
319 printf("%02x ", data
[i
+j
]);
325 for(j
=0; j
< 0x10; j
++)
326 if((j
+i
) < dataLen
) {
327 #ifdef CURL_DOES_CONVERSIONS
329 Curl_convert_from_network(handle
, &data
[i
+j
], (size_t)1))
331 #endif /* CURL_DOES_CONVERSIONS */
332 printf("%c", ISGRAPH(data
[i
+j
])?data
[i
+j
]:'.');
338 #ifdef CURL_DOES_CONVERSIONS
339 curl_easy_cleanup(handle
);
341 free(base64
); free(data
);
346 #ifdef TEST_NEED_SUCK
347 /* this function 'sucks' in as much as possible from stdin */
348 void *suck(int *lenptr
)
351 unsigned char *buf
= NULL
;
357 buf
= (unsigned char *)realloc(buf
, cursize
);
358 memset(buf
+ len
, 0, cursize
- len
);
359 lastread
= fread(buf
+ len
, 1, cursize
- len
, stdin
);
361 } while(!feof(stdin
));