1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is the Netscape Portable Runtime (NSPR).
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 1998-2000
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Kyle Huey <me@kylehuey.com>
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either the GNU General Public License Version 2 or later (the "GPL"), or
27 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #include "Base64.h"
40 :
41 : #include "nsIInputStream.h"
42 : #include "nsStringGlue.h"
43 :
44 : #include "plbase64.h"
45 :
46 : namespace {
47 :
48 : // BEGIN base64 encode code copied and modified from NSPR
49 : const unsigned char *base = (unsigned char *)"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
50 :
51 : template <typename T>
52 : static void
53 0 : Encode3to4(const unsigned char *src, T *dest)
54 : {
55 0 : PRUint32 b32 = (PRUint32)0;
56 0 : PRIntn i, j = 18;
57 :
58 0 : for( i = 0; i < 3; i++ )
59 : {
60 0 : b32 <<= 8;
61 0 : b32 |= (PRUint32)src[i];
62 : }
63 :
64 0 : for( i = 0; i < 4; i++ )
65 : {
66 0 : dest[i] = base[ (PRUint32)((b32>>j) & 0x3F) ];
67 0 : j -= 6;
68 : }
69 0 : }
70 :
71 : template <typename T>
72 : static void
73 0 : Encode2to4(const unsigned char *src, T *dest)
74 : {
75 0 : dest[0] = base[ (PRUint32)((src[0]>>2) & 0x3F) ];
76 0 : dest[1] = base[ (PRUint32)(((src[0] & 0x03) << 4) | ((src[1] >> 4) & 0x0F)) ];
77 0 : dest[2] = base[ (PRUint32)((src[1] & 0x0F) << 2) ];
78 0 : dest[3] = (unsigned char)'=';
79 0 : }
80 :
81 : template <typename T>
82 : static void
83 0 : Encode1to4(const unsigned char *src, T *dest)
84 : {
85 0 : dest[0] = base[ (PRUint32)((src[0]>>2) & 0x3F) ];
86 0 : dest[1] = base[ (PRUint32)((src[0] & 0x03) << 4) ];
87 0 : dest[2] = (unsigned char)'=';
88 0 : dest[3] = (unsigned char)'=';
89 0 : }
90 :
91 : template <typename T>
92 : static void
93 0 : Encode(const unsigned char *src, PRUint32 srclen, T *dest)
94 : {
95 0 : while( srclen >= 3 )
96 : {
97 0 : Encode3to4(src, dest);
98 0 : src += 3;
99 0 : dest += 4;
100 0 : srclen -= 3;
101 : }
102 :
103 0 : switch( srclen )
104 : {
105 : case 2:
106 0 : Encode2to4(src, dest);
107 0 : break;
108 : case 1:
109 0 : Encode1to4(src, dest);
110 0 : break;
111 : case 0:
112 0 : break;
113 : default:
114 0 : NS_NOTREACHED("coding error");
115 : }
116 0 : }
117 :
118 : // END base64 encode code copied and modified from NSPR.
119 :
120 : template <typename T>
121 : struct EncodeInputStream_State {
122 : unsigned char c[3];
123 : PRUint8 charsOnStack;
124 : typename T::char_type* buffer;
125 : };
126 :
127 : template <typename T>
128 : NS_METHOD
129 0 : EncodeInputStream_Encoder(nsIInputStream *aStream,
130 : void *aClosure,
131 : const char *aFromSegment,
132 : PRUint32 aToOffset,
133 : PRUint32 aCount,
134 : PRUint32 *aWriteCount)
135 : {
136 0 : NS_ASSERTION(aCount > 0, "Er, what?");
137 :
138 : EncodeInputStream_State<T>* state =
139 0 : static_cast<EncodeInputStream_State<T>*>(aClosure);
140 :
141 : // If we have any data left from last time, encode it now.
142 0 : PRUint32 countRemaining = aCount;
143 0 : const unsigned char *src = (const unsigned char*)aFromSegment;
144 0 : if (state->charsOnStack) {
145 : unsigned char firstSet[4];
146 0 : if (state->charsOnStack == 1) {
147 0 : firstSet[0] = state->c[0];
148 0 : firstSet[1] = src[0];
149 0 : firstSet[2] = (countRemaining > 1) ? src[1] : '\0';
150 0 : firstSet[3] = '\0';
151 : } else /* state->charsOnStack == 2 */ {
152 0 : firstSet[0] = state->c[0];
153 0 : firstSet[1] = state->c[1];
154 0 : firstSet[2] = src[0];
155 0 : firstSet[3] = '\0';
156 : }
157 0 : Encode(firstSet, 3, state->buffer);
158 0 : state->buffer += 4;
159 0 : countRemaining -= (3 - state->charsOnStack);
160 0 : src += (3 - state->charsOnStack);
161 0 : state->charsOnStack = 0;
162 : }
163 :
164 : // Encode the bulk of the
165 0 : PRUint32 encodeLength = countRemaining - countRemaining % 3;
166 0 : NS_ABORT_IF_FALSE(encodeLength % 3 == 0,
167 : "Should have an exact number of triplets!");
168 0 : Encode(src, encodeLength, state->buffer);
169 0 : state->buffer += (encodeLength / 3) * 4;
170 0 : src += encodeLength;
171 0 : countRemaining -= encodeLength;
172 :
173 : // We must consume all data, so if there's some data left stash it
174 0 : *aWriteCount = aCount;
175 :
176 0 : if (countRemaining) {
177 : // We should never have a full triplet left at this point.
178 0 : NS_ABORT_IF_FALSE(countRemaining < 3, "We should have encoded more!");
179 0 : state->c[0] = src[0];
180 0 : state->c[1] = (countRemaining == 2) ? src[1] : '\0';
181 0 : state->charsOnStack = countRemaining;
182 : }
183 :
184 0 : return NS_OK;
185 : }
186 :
187 : template <typename T>
188 : nsresult
189 0 : EncodeInputStream(nsIInputStream *aInputStream,
190 : T &aDest,
191 : PRUint32 aCount,
192 : PRUint32 aOffset)
193 : {
194 : nsresult rv;
195 :
196 0 : if (!aCount) {
197 0 : rv = aInputStream->Available(&aCount);
198 0 : NS_ENSURE_SUCCESS(rv, rv);
199 : }
200 :
201 : PRUint64 countlong =
202 0 : (PRUint64(aCount) + 2) / 3 * 4; // +2 due to integer math.
203 0 : if (countlong + aOffset > PR_UINT32_MAX)
204 0 : return NS_ERROR_OUT_OF_MEMORY;
205 :
206 0 : PRUint32 count = PRUint32(countlong);
207 :
208 0 : aDest.SetLength(count + aOffset);
209 0 : if (aDest.Length() != count + aOffset)
210 0 : return NS_ERROR_OUT_OF_MEMORY;
211 :
212 : EncodeInputStream_State<T> state;
213 0 : state.charsOnStack = 0;
214 0 : state.c[2] = '\0';
215 0 : state.buffer = aOffset + aDest.BeginWriting();
216 :
217 0 : while (1) {
218 0 : PRUint32 read = 0;
219 :
220 0 : rv = aInputStream->ReadSegments(&EncodeInputStream_Encoder<T>,
221 : (void*)&state,
222 : aCount,
223 : &read);
224 0 : if (NS_FAILED(rv)) {
225 0 : if (rv == NS_BASE_STREAM_WOULD_BLOCK)
226 0 : NS_RUNTIMEABORT("Not implemented for async streams!");
227 0 : if (rv == NS_ERROR_NOT_IMPLEMENTED)
228 0 : NS_RUNTIMEABORT("Requires a stream that implements ReadSegments!");
229 0 : return rv;
230 : }
231 :
232 0 : if (!read)
233 : break;
234 : }
235 :
236 : // Finish encoding if anything is left
237 0 : if (state.charsOnStack)
238 0 : Encode(state.c, state.charsOnStack, state.buffer);
239 :
240 0 : *aDest.EndWriting() = '\0';
241 :
242 0 : return NS_OK;
243 : }
244 :
245 : } // namespace (anonymous)
246 :
247 : namespace mozilla {
248 :
249 : nsresult
250 0 : Base64EncodeInputStream(nsIInputStream *aInputStream,
251 : nsACString &aDest,
252 : PRUint32 aCount,
253 : PRUint32 aOffset)
254 : {
255 0 : return EncodeInputStream<nsACString>(aInputStream, aDest, aCount, aOffset);
256 : }
257 :
258 : nsresult
259 0 : Base64EncodeInputStream(nsIInputStream *aInputStream,
260 : nsAString &aDest,
261 : PRUint32 aCount,
262 : PRUint32 aOffset)
263 : {
264 0 : return EncodeInputStream<nsAString>(aInputStream, aDest, aCount, aOffset);
265 : }
266 :
267 : nsresult
268 6763 : Base64Encode(const nsACString &aBinaryData, nsACString &aString)
269 : {
270 : // Check for overflow.
271 6763 : if (aBinaryData.Length() > (PR_UINT32_MAX / 4) * 3) {
272 0 : return NS_ERROR_FAILURE;
273 : }
274 :
275 6763 : PRUint32 stringLen = ((aBinaryData.Length() + 2) / 3) * 4;
276 :
277 : char *buffer;
278 :
279 : // Add one byte for null termination.
280 13526 : if (aString.SetCapacity(stringLen + 1) &&
281 : (buffer = aString.BeginWriting()) &&
282 6763 : PL_Base64Encode(aBinaryData.BeginReading(), aBinaryData.Length(), buffer)) {
283 : // PL_Base64Encode doesn't null terminate the buffer for us when we pass
284 : // the buffer in. Do that manually.
285 6763 : buffer[stringLen] = '\0';
286 :
287 6763 : aString.SetLength(stringLen);
288 6763 : return NS_OK;
289 : }
290 :
291 0 : aString.Truncate();
292 0 : return NS_ERROR_INVALID_ARG;
293 : }
294 :
295 : nsresult
296 0 : Base64Encode(const nsAString &aString, nsAString &aBinaryData)
297 : {
298 0 : NS_LossyConvertUTF16toASCII string(aString);
299 0 : nsCAutoString binaryData;
300 :
301 0 : nsresult rv = Base64Encode(string, binaryData);
302 0 : if (NS_SUCCEEDED(rv)) {
303 0 : CopyASCIItoUTF16(binaryData, aBinaryData);
304 : } else {
305 0 : aBinaryData.Truncate();
306 : }
307 :
308 0 : return rv;
309 : }
310 :
311 : nsresult
312 1070 : Base64Decode(const nsACString &aString, nsACString &aBinaryData)
313 : {
314 : // Check for overflow.
315 1070 : if (aString.Length() > PR_UINT32_MAX / 3) {
316 0 : return NS_ERROR_FAILURE;
317 : }
318 :
319 1070 : PRUint32 binaryDataLen = ((aString.Length() * 3) / 4);
320 :
321 : char *buffer;
322 :
323 : // Add one byte for null termination.
324 2140 : if (aBinaryData.SetCapacity(binaryDataLen + 1) &&
325 : (buffer = aBinaryData.BeginWriting()) &&
326 1070 : PL_Base64Decode(aString.BeginReading(), aString.Length(), buffer)) {
327 : // PL_Base64Decode doesn't null terminate the buffer for us when we pass
328 : // the buffer in. Do that manually, taking into account the number of '='
329 : // characters we were passed.
330 1070 : if (!aString.IsEmpty() && aString[aString.Length() - 1] == '=') {
331 959 : if (aString.Length() > 1 && aString[aString.Length() - 2] == '=') {
332 482 : binaryDataLen -= 2;
333 : } else {
334 477 : binaryDataLen -= 1;
335 : }
336 : }
337 1070 : buffer[binaryDataLen] = '\0';
338 :
339 1070 : aBinaryData.SetLength(binaryDataLen);
340 1070 : return NS_OK;
341 : }
342 :
343 0 : aBinaryData.Truncate();
344 0 : return NS_ERROR_INVALID_ARG;
345 : }
346 :
347 : nsresult
348 0 : Base64Decode(const nsAString &aBinaryData, nsAString &aString)
349 : {
350 0 : NS_LossyConvertUTF16toASCII binaryData(aBinaryData);
351 0 : nsCAutoString string;
352 :
353 0 : nsresult rv = Base64Decode(binaryData, string);
354 0 : if (NS_SUCCEEDED(rv)) {
355 0 : CopyASCIItoUTF16(string, aString);
356 : } else {
357 0 : aString.Truncate();
358 : }
359 :
360 0 : return rv;
361 : }
362 :
363 : } // namespace mozilla
|