|
1 /* |
|
2 * Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * This component and the accompanying materials are made available |
|
5 * under the terms of "Eclipse Public License v1.0" |
|
6 * which accompanies this distribution, and is available |
|
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description: Base64 coder and decoder. |
|
15 * |
|
16 */ |
|
17 |
|
18 |
|
19 // INCLUDE FILES |
|
20 #include "pimbase64converter.h" |
|
21 #include "pimcommon.h" |
|
22 #include "pimpanics.h" |
|
23 #include "logger.h" |
|
24 |
|
25 // CONSTANTS |
|
26 |
|
27 /** |
|
28 * Six least significant bits. |
|
29 */ |
|
30 const TUint8 KSixLSBits = 63; // 00111111 |
|
31 |
|
32 /** |
|
33 * Eight least significant bits. |
|
34 */ |
|
35 const TUint8 KEightLSBits = 255; // 11111111 |
|
36 |
|
37 /** |
|
38 * Pad byte. |
|
39 */ |
|
40 const TUint8 KPadLetter = '='; |
|
41 |
|
42 /** |
|
43 * A non-base64 letter. |
|
44 * Can be used to denote an erroneous situation. |
|
45 */ |
|
46 const TUint8 KNonBase64Letter = 255; |
|
47 |
|
48 /** |
|
49 * Byte array granularity. Byte arrays are usually quite large. |
|
50 */ |
|
51 const TInt KByteArrayGranularity = 24; |
|
52 |
|
53 #if 0 // Disabled to satisfy TCK, which expects no newlines |
|
54 /** |
|
55 * Recommended number of encoded characters per line. |
|
56 */ |
|
57 const TInt KNumEncodedCharsPerLine = 76; |
|
58 #endif |
|
59 |
|
60 /** |
|
61 * Base64 alphabet from RFC 2045. |
|
62 * 64 characters, indexed from 0 to 63. The pad byte is not included |
|
63 * in the array. |
|
64 * |
|
65 * <pre> |
|
66 * Table 1: The Base64 Alphabet |
|
67 * |
|
68 * Value Encoding Value Encoding Value Encoding Value Encoding |
|
69 * 0 A 17 R 34 i 51 z |
|
70 * 1 B 18 S 35 j 52 0 |
|
71 * 2 C 19 T 36 k 53 1 |
|
72 * 3 D 20 U 37 l 54 2 |
|
73 * 4 E 21 V 38 m 55 3 |
|
74 * 5 F 22 W 39 n 56 4 |
|
75 * 6 G 23 X 40 o 57 5 |
|
76 * 7 H 24 Y 41 p 58 6 |
|
77 * 8 I 25 Z 42 q 59 7 |
|
78 * 9 J 26 a 43 r 60 8 |
|
79 * 10 K 27 b 44 s 61 9 |
|
80 * 11 L 28 c 45 t 62 + |
|
81 * 12 M 29 d 46 u 63 / |
|
82 * 13 N 30 e 47 v |
|
83 * 14 O 31 f 48 w (pad) = |
|
84 * 15 P 32 g 49 x |
|
85 * 16 Q 33 h 50 y |
|
86 * |
|
87 * </pre> |
|
88 */ |
|
89 const TUint8 KBase64Alphabet[64] = |
|
90 { |
|
91 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', |
|
92 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', |
|
93 |
|
94 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', |
|
95 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', |
|
96 |
|
97 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' |
|
98 }; |
|
99 |
|
100 CPIMByteArray* PIMBase64Converter::EncodeBase64L(const CPIMByteArray& aSource) |
|
101 { |
|
102 JELOG2(EPim); |
|
103 CPIMByteArray* targetBytes = |
|
104 new(ELeave) CPIMByteArray(KByteArrayGranularity); |
|
105 CleanupStack::PushL(targetBytes); |
|
106 |
|
107 // Loop through the source array. |
|
108 // |
|
109 // Read an 8 bit byte on each iteration. Insert the byte to current |
|
110 // quantum, shifting it's contents 8 bits left before insertion. |
|
111 // When three bytes are read, append the bytes as four base64-encoded |
|
112 // bytes to the target array. Then reset the quantum and start reading |
|
113 // another three bytes. |
|
114 // |
|
115 // If less than three bytes could be read from the source array, |
|
116 // Convert a partial quantum. If zero bytes were read, the encoding |
|
117 // stops. If one or two bytes were read, pad bytes ('=') are appended |
|
118 // after conversion to make it full four base64 bytes and encoding stops. |
|
119 |
|
120 TUint32 quantum = 0; |
|
121 TInt numBytesInQuantum = 0; |
|
122 const TInt numTotalSourceBytes = aSource.Count(); |
|
123 TInt targetByteCount = 0; |
|
124 |
|
125 for (TInt i = 0; i < numTotalSourceBytes; i++) |
|
126 { |
|
127 TUint8 sourceByte = aSource[i]; // 8 raw, non-encoded bits |
|
128 quantum <<= 8; |
|
129 quantum |= sourceByte; |
|
130 numBytesInQuantum++; |
|
131 |
|
132 if (numBytesInQuantum == 3) |
|
133 { |
|
134 // 24 bits in the quantum |
|
135 EncodeQuantumL(quantum, *targetBytes, 3); |
|
136 quantum = 0; |
|
137 numBytesInQuantum = 0; |
|
138 targetByteCount += 4; |
|
139 } |
|
140 |
|
141 #if 0 // Disabled to satisfy TCK, which expects no newlines |
|
142 // PIM API TCK does not accept newlines in output. |
|
143 if ((targetByteCount> 0) && |
|
144 ((targetByteCount % KNumEncodedCharsPerLine) == 0)) |
|
145 { |
|
146 targetBytes->AppendL('\n'); |
|
147 } |
|
148 #endif |
|
149 } |
|
150 |
|
151 // 0, 8 or 16 bits in the quantum |
|
152 EncodeQuantumL(quantum, *targetBytes, numBytesInQuantum); |
|
153 |
|
154 CleanupStack::Pop(targetBytes); |
|
155 return targetBytes; |
|
156 |
|
157 } |
|
158 |
|
159 CPIMByteArray* PIMBase64Converter::DecodeBase64L(const CPIMByteArray& aSource) |
|
160 { |
|
161 JELOG2(EPim); |
|
162 CPIMByteArray* targetBytes = |
|
163 new(ELeave) CPIMByteArray(KByteArrayGranularity); |
|
164 CleanupStack::PushL(targetBytes); |
|
165 |
|
166 // Loop through the source array. |
|
167 // |
|
168 // Read an 8 bit byte on each iteration. If the byte is a pad byte, stop |
|
169 // reading. If the byte is not a base64 letter, skip it. Otherwise, |
|
170 // consider it a base64 letter and convert it to the corresponding 6 bit |
|
171 // value (in an 8 bit byte, of course). Insert the 6 bits to a quantum, |
|
172 // shifting its contents 6 bits left before insertion. When four bytes |
|
173 // are read, append the bytes as three base64-decoded bytes to the target |
|
174 // array. Then reset the quantum and start reading another four bytes. |
|
175 // |
|
176 // If a pad byte was read before full quantum was constructed, a partial |
|
177 // quantum is constructed. Quantum decoding method can also parse an empty |
|
178 // quantum, in which case it does nothing. |
|
179 |
|
180 TUint32 quantum = 0; |
|
181 TInt numBytesInQuantum = 0; |
|
182 const TInt numTotalSourceBytes = aSource.Count(); |
|
183 |
|
184 for (TInt i = 0; i < numTotalSourceBytes; i++) |
|
185 { |
|
186 TUint8 sourceBase64Letter = aSource[i]; |
|
187 |
|
188 if (sourceBase64Letter == KPadLetter) |
|
189 { |
|
190 break; // End of input |
|
191 } |
|
192 |
|
193 TUint8 sourceByte = Base64LetterToByte(sourceBase64Letter); |
|
194 |
|
195 if (sourceByte == KNonBase64Letter) |
|
196 { |
|
197 continue; // Skip |
|
198 } |
|
199 |
|
200 quantum <<= 6; |
|
201 quantum |= sourceByte; |
|
202 numBytesInQuantum++; |
|
203 |
|
204 if (numBytesInQuantum == 4) |
|
205 { |
|
206 // 24 bits in quantum |
|
207 DecodeQuantumL(quantum, *targetBytes, 4); |
|
208 quantum = 0; |
|
209 numBytesInQuantum = 0; |
|
210 } |
|
211 } |
|
212 |
|
213 // 0, 6, 12 or 18 bits in quantum |
|
214 DecodeQuantumL(quantum, *targetBytes, numBytesInQuantum); |
|
215 |
|
216 CleanupStack::Pop(targetBytes); |
|
217 return targetBytes; |
|
218 } |
|
219 |
|
220 void PIMBase64Converter::EncodeQuantumL(const TUint32& aQuantum, |
|
221 CPIMByteArray& aTargetArray, const TInt& aNumSourceBytes) |
|
222 { |
|
223 JELOG2(EPim); |
|
224 TInt numTargetBytes = 0; |
|
225 TInt padShift = 0; |
|
226 |
|
227 switch (aNumSourceBytes) |
|
228 { |
|
229 case 3: // 24 bits |
|
230 { |
|
231 numTargetBytes = 4; // 6 + 6 + 6 + 6 |
|
232 break; |
|
233 } |
|
234 case 2: // 16 bits |
|
235 { |
|
236 numTargetBytes = 3; // 6 + 6 + 4 |
|
237 padShift = 2; |
|
238 break; |
|
239 } |
|
240 case 1: // 8 bits |
|
241 { |
|
242 numTargetBytes = 2; // 6 + 2 |
|
243 padShift = 4; |
|
244 break; |
|
245 } |
|
246 case 0: // 0 bits |
|
247 { |
|
248 return; // Done. |
|
249 } |
|
250 default: |
|
251 { |
|
252 User::Leave(KErrArgument); |
|
253 } |
|
254 } |
|
255 |
|
256 TInt numPadBytes = 4 - numTargetBytes; |
|
257 |
|
258 while (numTargetBytes > 0) |
|
259 { |
|
260 numTargetBytes--; |
|
261 TUint32 tempQuantum = aQuantum; |
|
262 TInt shift = 6 * numTargetBytes; |
|
263 tempQuantum <<= padShift; |
|
264 tempQuantum >>= shift; |
|
265 tempQuantum &= KSixLSBits; |
|
266 |
|
267 TUint8 byte = static_cast<TUint8>(tempQuantum); |
|
268 TUint8 base64Letter = ByteToBase64Letter(byte); |
|
269 aTargetArray.AppendL(base64Letter); |
|
270 } |
|
271 |
|
272 while (numPadBytes > 0) |
|
273 { |
|
274 aTargetArray.AppendL(KPadLetter); |
|
275 numPadBytes--; |
|
276 } |
|
277 } |
|
278 |
|
279 void PIMBase64Converter::DecodeQuantumL(const TUint32& aQuantum, |
|
280 CPIMByteArray& aTargetArray, const TInt& aNumSourceBytes) |
|
281 { |
|
282 JELOG2(EPim); |
|
283 TInt numTargetBytes = 0; |
|
284 TInt padShift = 0; |
|
285 |
|
286 switch (aNumSourceBytes) |
|
287 { |
|
288 case 4: // 24 bits |
|
289 { |
|
290 numTargetBytes = 3; // 8 + 8 + 8 bits |
|
291 break; |
|
292 } |
|
293 case 3: // 18 bits |
|
294 { |
|
295 numTargetBytes = 2; // 8 + 8 (+ 2) bits |
|
296 padShift = 2; |
|
297 break; |
|
298 } |
|
299 case 2: // 12 bits |
|
300 { |
|
301 numTargetBytes = 1; // 8 (+ 4) bits |
|
302 padShift = 4; |
|
303 break; |
|
304 } |
|
305 case 1: // 6 bits |
|
306 { |
|
307 // This should never happen |
|
308 __ASSERT_DEBUG(EFalse, User::Panic(KPIMPanicCategory, |
|
309 EPIMPanicBase64Error)); |
|
310 break; |
|
311 } |
|
312 case 0: // 0 bits |
|
313 { |
|
314 return; // Done. |
|
315 } |
|
316 default: |
|
317 { |
|
318 User::Leave(KErrArgument); |
|
319 } |
|
320 } |
|
321 |
|
322 while (numTargetBytes > 0) |
|
323 { |
|
324 numTargetBytes--; |
|
325 TUint32 tempQuantum = aQuantum; |
|
326 tempQuantum >>= (8 * numTargetBytes + padShift); |
|
327 tempQuantum &= KEightLSBits; |
|
328 TUint8 byte = static_cast<TUint8>(tempQuantum); |
|
329 aTargetArray.AppendL(byte); |
|
330 } |
|
331 } |
|
332 |
|
333 inline TUint8 PIMBase64Converter::ByteToBase64Letter(const TUint8& aByte) |
|
334 { |
|
335 JELOG2(EPim); |
|
336 __ASSERT_DEBUG(aByte < 64, User::Panic(KPIMPanicCategory, |
|
337 EPIMPanicBase64Error)); |
|
338 |
|
339 return KBase64Alphabet[aByte]; |
|
340 } |
|
341 |
|
342 TUint8 PIMBase64Converter::Base64LetterToByte(const TUint8& aLetter) |
|
343 { |
|
344 JELOG2(EPim); |
|
345 const TUint8 KUCAlphaBase = 0; // first upper case base64 letter |
|
346 const TUint8 KLCAlphaBase = 26; // first lower case base64 letter |
|
347 const TUint8 KNumBase = 52; // first numerical letter |
|
348 const TUint8 KPlus = 62; // '+' character |
|
349 const TUint8 KSlash = 63; // '/' character |
|
350 |
|
351 TUint8 retVal = 0; |
|
352 |
|
353 if ((aLetter >= 'A') && (aLetter <= 'Z')) |
|
354 { |
|
355 retVal = static_cast<TUint8>(aLetter - 'A' + KUCAlphaBase); |
|
356 } |
|
357 else if ((aLetter >= 'a') && (aLetter <= 'z')) |
|
358 { |
|
359 retVal = static_cast<TUint8>(aLetter - 'a' + KLCAlphaBase); |
|
360 } |
|
361 else if ((aLetter >= '0') && (aLetter <= '9')) |
|
362 { |
|
363 retVal = static_cast<TUint8>(aLetter - '0' + KNumBase); |
|
364 } |
|
365 else if (aLetter == '+') |
|
366 { |
|
367 retVal = KPlus; |
|
368 } |
|
369 else if (aLetter == '/') |
|
370 { |
|
371 retVal = KSlash; |
|
372 } |
|
373 else |
|
374 { |
|
375 retVal = KNonBase64Letter; |
|
376 } |
|
377 |
|
378 __ASSERT_DEBUG((retVal < 64) || (retVal == KNonBase64Letter), User::Panic( |
|
379 KPIMPanicCategory, EPIMPanicBase64Error)); |
|
380 |
|
381 return retVal; |
|
382 } |
|
383 |
|
384 // End of File |