initial load of upstream version 1.06.32
[xmlrpc-c] / src / xmlrpc_base64.c
1 /* Copyright (C) 2001 by First Peer, Inc. All rights reserved.
2 **
3 ** Redistribution and use in source and binary forms, with or without
4 ** modification, are permitted provided that the following conditions
5 ** are met:
6 ** 1. Redistributions of source code must retain the above copyright
7 **    notice, this list of conditions and the following disclaimer.
8 ** 2. Redistributions in binary form must reproduce the above copyright
9 **    notice, this list of conditions and the following disclaimer in the
10 **    documentation and/or other materials provided with the distribution.
11 ** 3. The name of the author may not be used to endorse or promote products
12 **    derived from this software without specific prior written permission. 
13 **  
14 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 ** ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 ** SUCH DAMAGE.
25 **
26 ** There is more copyright information in the bottom half of this file. 
27 ** Please see it for more details. */
28
29
30 /*=========================================================================
31 **  XML-RPC Base64 Utilities
32 **=========================================================================
33 **  This code was swiped from Jack Jansen's code in Python 1.5.2 and
34 **  modified to work with our data types.
35 */
36
37 #include "xmlrpc_config.h"
38
39 #include "xmlrpc-c/base.h"
40
41 #define CRLF    "\015\012"
42 #define CR      '\015'
43 #define LF      '\012'
44
45
46 /***********************************************************
47 Copyright 1991, 1992, 1993, 1994 by Stichting Mathematisch Centrum,
48 Amsterdam, The Netherlands.
49
50                         All Rights Reserved
51
52 Permission to use, copy, modify, and distribute this software and its
53 documentation for any purpose and without fee is hereby granted,
54 provided that the above copyright notice appear in all copies and that
55 both that copyright notice and this permission notice appear in
56 supporting documentation, and that the names of Stichting Mathematisch
57 Centrum or CWI or Corporation for National Research Initiatives or
58 CNRI not be used in advertising or publicity pertaining to
59 distribution of the software without specific, written prior
60 permission.
61
62 While CWI is the initial source for this software, a modified version
63 is made available by the Corporation for National Research Initiatives
64 (CNRI) at the Internet address ftp://ftp.python.org.
65
66 STICHTING MATHEMATISCH CENTRUM AND CNRI DISCLAIM ALL WARRANTIES WITH
67 REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
68 MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH
69 CENTRUM OR CNRI BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
70 DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
71 PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
72 TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
73 PERFORMANCE OF THIS SOFTWARE.
74
75 ******************************************************************/
76
77 static char table_a2b_base64[] = {
78     -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
79     -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
80     -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
81     52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1, 0,-1,-1, /* Note PAD->0 */
82     -1, 0, 1, 2,  3, 4, 5, 6,  7, 8, 9,10, 11,12,13,14,
83     15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,
84     -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
85     41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
86 };
87
88 #define BASE64_PAD '='
89 #define BASE64_MAXBIN 57    /* Max binary chunk size (76 char line) */
90 #define BASE64_LINE_SZ 128      /* Buffer size for a single line. */    
91
92 static unsigned char table_b2a_base64[] =
93 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
94
95 static xmlrpc_mem_block *
96 xmlrpc_base64_encode_internal (xmlrpc_env *env,
97                                unsigned char *bin_data,
98                                size_t bin_len,
99                                int want_newlines)
100 {
101     size_t chunk_start, chunk_left;
102     unsigned char *ascii_data;
103     int leftbits;
104     unsigned char this_ch;
105     unsigned int leftchar;
106     xmlrpc_mem_block *output;
107     unsigned char line_buffer[BASE64_LINE_SZ];
108
109     /* Create a block to hold our lines when we finish them. */
110     output = xmlrpc_mem_block_new(env, 0);
111     XMLRPC_FAIL_IF_FAULT(env);
112
113     /* Deal with empty data blocks gracefully. Yuck. */
114     if (bin_len == 0) {
115         if (want_newlines)
116             XMLRPC_TYPED_MEM_BLOCK_APPEND(char, env, output, CRLF, 2);
117         goto cleanup;
118     }
119
120     /* Process our binary data in line-sized chunks. */
121     for (chunk_start=0; chunk_start < bin_len; chunk_start += BASE64_MAXBIN) {
122
123         /* Set up our per-line state. */
124         ascii_data = &line_buffer[0];
125         chunk_left = bin_len - chunk_start;
126         if (chunk_left > BASE64_MAXBIN)
127             chunk_left = BASE64_MAXBIN;
128         leftbits = 0;
129         leftchar = 0;
130
131         for(; chunk_left > 0; chunk_left--, bin_data++) {
132             /* Shift the data into our buffer */
133             leftchar = (leftchar << 8) | *bin_data;
134             leftbits += 8;
135
136             /* See if there are 6-bit groups ready */
137             while (leftbits >= 6) {
138                 this_ch = (leftchar >> (leftbits-6)) & 0x3f;
139                 leftbits -= 6;
140                 *ascii_data++ = table_b2a_base64[this_ch];
141             }
142         }
143         if (leftbits == 2) {
144             *ascii_data++ = table_b2a_base64[(leftchar&3) << 4];
145             *ascii_data++ = BASE64_PAD;
146             *ascii_data++ = BASE64_PAD;
147         } else if (leftbits == 4) {
148             *ascii_data++ = table_b2a_base64[(leftchar&0xf) << 2];
149             *ascii_data++ = BASE64_PAD;
150         } 
151
152         /* Append a courtesy CRLF. */
153         if (want_newlines) {
154             *ascii_data++ = CR;
155             *ascii_data++ = LF;
156         }
157     
158         /* Save our line. */
159         XMLRPC_TYPED_MEM_BLOCK_APPEND(char, env, output, line_buffer,
160                                       ascii_data - &line_buffer[0]);
161         XMLRPC_FAIL_IF_FAULT(env);
162     }
163
164  cleanup:
165     if (env->fault_occurred) {
166         if (output)
167             xmlrpc_mem_block_free(output);
168         return NULL;
169     }
170     return output;
171 }
172
173
174 xmlrpc_mem_block *
175 xmlrpc_base64_encode (xmlrpc_env *env, unsigned char *bin_data, size_t bin_len)
176 {
177     return xmlrpc_base64_encode_internal(env, bin_data, bin_len, 1);
178 }
179
180
181 xmlrpc_mem_block *
182 xmlrpc_base64_encode_without_newlines (xmlrpc_env *env,
183                                        unsigned char *bin_data,
184                                        size_t bin_len)
185 {
186     return xmlrpc_base64_encode_internal(env, bin_data, bin_len, 0);
187 }
188
189
190 xmlrpc_mem_block *
191 xmlrpc_base64_decode (xmlrpc_env * const env,
192                       const char * const ascii_data,
193                       size_t       const ascii_len) {
194
195     unsigned char *bin_data;
196     int leftbits;
197     unsigned char this_ch;
198     unsigned int leftchar;
199     size_t npad;
200     size_t bin_len, buffer_size;
201     xmlrpc_mem_block *output;
202     const char * next_char;
203     size_t remaining_len;
204
205     /* Create a block to hold our chunks when we finish them.
206     ** We overestimate the size now, and fix it later. */
207     buffer_size = ((ascii_len+3)/4)*3;
208     output = xmlrpc_mem_block_new(env, buffer_size);
209     XMLRPC_FAIL_IF_FAULT(env);
210
211     /* Set up our decoder state. */
212     leftbits = 0;
213     leftchar = 0;
214     npad = 0;
215     bin_data = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(unsigned char, output);
216     bin_len = 0;
217
218     for (remaining_len = ascii_len, next_char = ascii_data;
219          remaining_len > 0; 
220          --remaining_len, ++next_char) {
221
222         /* Skip some punctuation. */
223         this_ch = (*next_char & 0x7f);
224         if ( this_ch == '\r' || this_ch == '\n' || this_ch == ' ' )
225             continue;
226         if ( this_ch == BASE64_PAD )
227             npad++;
228         this_ch = table_a2b_base64[(*next_char) & 0x7f];
229
230         /* XXX - We just throw away invalid characters. Is this right? */
231         if ( this_ch == (unsigned char) -1 ) continue;
232
233         /* Shift it in on the low end, and see if there's
234         ** a byte ready for output. */
235         leftchar = (leftchar << 6) | (this_ch);
236         leftbits += 6;
237         if ( leftbits >= 8 ) {
238             leftbits -= 8;
239             XMLRPC_ASSERT(bin_len < buffer_size);
240             *bin_data++ = (leftchar >> leftbits) & 0xFF;
241             leftchar &= ((1 << leftbits) - 1);
242             bin_len++;
243         }
244     }
245
246     /* Check that no bits are left. */
247     if ( leftbits )
248         XMLRPC_FAIL(env, XMLRPC_PARSE_ERROR, "Incorrect Base64 padding");
249
250     /* Check to make sure we have a sane amount of padding. */
251     if (npad > bin_len || npad > 2)
252         XMLRPC_FAIL(env, XMLRPC_PARSE_ERROR, "Malformed Base64 data");
253
254     /* Remove any padding and set the correct size. */
255     bin_len -= npad;
256     XMLRPC_TYPED_MEM_BLOCK_RESIZE(char, env, output, bin_len);
257     XMLRPC_ASSERT(!env->fault_occurred);
258
259                       cleanup:
260     if (env->fault_occurred) {
261         if (output)
262             xmlrpc_mem_block_free(output);
263         return NULL;
264     }
265     return output;
266 }