|
1 // Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
2 // All rights reserved. |
|
3 // This component and the accompanying materials are made available |
|
4 // under the terms of "Eclipse Public License v1.0" |
|
5 // which accompanies this distribution, and is available |
|
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
7 // |
|
8 // Initial Contributors: |
|
9 // Nokia Corporation - initial contribution. |
|
10 // |
|
11 // Contributors: |
|
12 // |
|
13 // Description: |
|
14 // |
|
15 |
|
16 #include "cstoreutilities.h" |
|
17 #include <miuthdr.h> |
|
18 #include <barsread.h> |
|
19 |
|
20 // The maximum number of octets to be uuencoded on each line is 45 |
|
21 const TInt KUuDecodedLineLength = 45; |
|
22 // Initial size of buffer in which the "Real name <user@host>" strings |
|
23 // are built before they're put into the CImHeader and the size by |
|
24 // which to increment the buffer when necessary |
|
25 const TInt KImapAddressSizeInc=256; |
|
26 const TInt KKiloByteSize = 1024; |
|
27 const TInt KBufferExtension = 4; |
|
28 /** |
|
29 Constructor. |
|
30 */ |
|
31 CStoreUtilities::CStoreUtilities(TImEncodingType aEncodingType,TUint aCharsetId,RFs& aFs) |
|
32 :iEncodingType(aEncodingType),iCharsetId(aCharsetId),iFs(aFs) |
|
33 { |
|
34 } |
|
35 |
|
36 /** |
|
37 Factory constructors. |
|
38 Ownership aHeader is taken. |
|
39 */ |
|
40 CStoreUtilities* CStoreUtilities::NewL(TImEncodingType aEncodingType,TUint aCharsetId,RFs& aFs) |
|
41 { |
|
42 CStoreUtilities* self=new(ELeave) CStoreUtilities(aEncodingType,aCharsetId,aFs); |
|
43 CleanupStack::PushL(self); |
|
44 self->ConstructL(); |
|
45 CleanupStack::Pop(); |
|
46 return self; |
|
47 } |
|
48 |
|
49 void CStoreUtilities::ConstructL() |
|
50 { |
|
51 // Create converter objects |
|
52 iCharacterConverter=CCnvCharacterSetConverter::NewL(); |
|
53 iCharConv=CImConvertCharconv::NewL(*iCharacterConverter, iFs); |
|
54 } |
|
55 |
|
56 /** |
|
57 Destructor. |
|
58 */ |
|
59 CStoreUtilities::~CStoreUtilities() |
|
60 { |
|
61 delete iCharConv; |
|
62 delete iCharacterConverter; |
|
63 delete iFooterString; |
|
64 delete iPartialLine; |
|
65 } |
|
66 |
|
67 /** |
|
68 Checks if the descriptor contains the UUE begin header |
|
69 Extracts the file name if it is |
|
70 */ |
|
71 TBool CStoreUtilities::CheckUUEStartL(const TDesC8& aSourceLine) |
|
72 { |
|
73 |
|
74 TInt sourceLength = aSourceLine.Length(); |
|
75 if(sourceLength < KImcvUueStart().Length()+3) // can't be "begin ###", it's not long enough; 3=length of ### |
|
76 return EFalse; |
|
77 |
|
78 if(!aSourceLine.Left(KImcvUueStart().Length()).CompareF(KImcvUueStart)) // start of line might be UUE boundary |
|
79 { |
|
80 // we also need to check that the next three chars are numbers - Unix file access code |
|
81 const TUint8* sourceLinePtr = aSourceLine.Ptr(); |
|
82 TInt length=KImcvUueStart().Length();// this defines length as 6 ie. "b e g i n " |
|
83 if( TChar(sourceLinePtr[length]).IsDigit() && |
|
84 TChar(sourceLinePtr[length+1]).IsDigit() && |
|
85 TChar(sourceLinePtr[length+2]).IsDigit() ) |
|
86 { |
|
87 // Found 'begin ###' at the start of a line - assume this is a UUencode header |
|
88 // The attachment name in this header is ignored. We use the value from the MIME header |
|
89 return ETrue; |
|
90 } |
|
91 } |
|
92 |
|
93 return EFalse; |
|
94 } |
|
95 |
|
96 /** |
|
97 Appends data to partial line object extening the destination buffer as appropirate |
|
98 @param aBufferPtr the destination bufer |
|
99 @param aText the data to be appended |
|
100 */ |
|
101 |
|
102 void CStoreUtilities::AppendExtendL(HBufC8** aBufferPtr, const TDesC8& aText) |
|
103 { |
|
104 HBufC8 *buffer = *aBufferPtr; |
|
105 TInt32 space = buffer->Des().MaxLength() - (buffer->Des().Length() + aText.Length()); |
|
106 if (space < 0) |
|
107 { |
|
108 TInt32 inc = (-space) + KImapAddressSizeInc - ((-space) % KImapAddressSizeInc); |
|
109 TInt32 newSize = buffer->Des().MaxLength() + inc; |
|
110 HBufC8 *newBuf = buffer->ReAllocL(newSize); |
|
111 *aBufferPtr = buffer = newBuf; |
|
112 } |
|
113 buffer->Des().Append(aText); |
|
114 } |
|
115 |
|
116 |
|
117 void CStoreUtilities::AttachFooterInfoL(TInt32 aBodyPartRemainingSize,CImapSettings& aImapSettings,HBufC8*& aDecodedData) |
|
118 { |
|
119 const HBufC8* buf= aImapSettings.DefaultPartialMessageFooter(); |
|
120 |
|
121 _LIT(KIntegerDirective,"%d"); |
|
122 const TInt KNoOfDigitsInMailSize = 10; // maximum length for no of digits for remaining body parts size |
|
123 TResourceReader reader; |
|
124 reader.SetBuffer(buf); |
|
125 // Check if %d is not found in the resource string (To avoid problems due to localisation) |
|
126 if(buf->Find((TDesC8&)KIntegerDirective) == KErrNotFound) |
|
127 { |
|
128 iFooterString = (reader.ReadTPtrC()).AllocL(); |
|
129 } |
|
130 else |
|
131 { |
|
132 HBufC* resourceBuf = (reader.ReadTPtrC()).AllocL(); |
|
133 CleanupStack::PushL(resourceBuf); |
|
134 iFooterString = HBufC::NewL(resourceBuf->Length()+KNoOfDigitsInMailSize); |
|
135 iFooterString->Des().Format(*resourceBuf,(aBodyPartRemainingSize / KKiloByteSize)); |
|
136 CleanupStack::PopAndDestroy(resourceBuf); |
|
137 } |
|
138 //caller will have left aDecodedData on the cleanupstack |
|
139 CleanupStack::Pop(aDecodedData); |
|
140 TInt newSize = aDecodedData->Size() + iFooterString->Size(); |
|
141 aDecodedData = aDecodedData->ReAlloc(newSize); |
|
142 CleanupStack::PushL(aDecodedData); |
|
143 aDecodedData->Des().Append(*iFooterString); |
|
144 delete iFooterString; |
|
145 iFooterString = NULL; |
|
146 |
|
147 } |
|
148 |
|
149 |
|
150 // convert text from its charset and write to richtext store. aText |
|
151 // can span multiple and partial lines |
|
152 void CStoreUtilities::WriteToBodyL(const TDesC8& aText,CRichText* aMessageBody) |
|
153 { |
|
154 TInt pos = aMessageBody->DocumentLength(); |
|
155 |
|
156 // Add bits of body text, converting along the way, till no characters left |
|
157 // .. to convert. |
|
158 |
|
159 // Convert text before writing to body. |
|
160 TInt rem = 0; |
|
161 |
|
162 // there will be a max of one output char per input byte |
|
163 HBufC16* text16=HBufC16::NewLC(aText.Length()); |
|
164 TPtr16 ptr16=text16->Des(); |
|
165 |
|
166 TBool preparedToConvert = iCharConv->PrepareToConvertToFromOurCharsetL(iCharsetId); |
|
167 |
|
168 if (!preparedToConvert) |
|
169 { |
|
170 ptr16.Copy(aText); |
|
171 aMessageBody->InsertL(pos, ptr16); |
|
172 } |
|
173 else |
|
174 { |
|
175 TInt unconvertedChars, firstPos; // not used |
|
176 rem = iCharConv->ConvertToOurCharsetL(aText, ptr16, |
|
177 unconvertedChars, firstPos); |
|
178 if (rem < 0) // error |
|
179 { |
|
180 // Copy unconverted characters. |
|
181 ptr16.Copy(aText); |
|
182 aMessageBody->InsertL(pos, ptr16); |
|
183 } |
|
184 else if (rem && rem < iLeftOver.MaxLength()) |
|
185 { |
|
186 iLeftOver.Copy(aText.Right(rem)); |
|
187 } |
|
188 |
|
189 // convert CRLF to ELineBreak |
|
190 TInt start = 0; |
|
191 TInt length = ptr16.Length(); |
|
192 |
|
193 if (length>0) |
|
194 { |
|
195 TInt i; |
|
196 for (i=1; i<length; i++) |
|
197 { |
|
198 if (ptr16[i-1] == KImcvCR && ptr16[i] == KImcvLF) |
|
199 { |
|
200 ptr16[i-1] = CEditableText::ELineBreak; |
|
201 |
|
202 // write this line to body |
|
203 TPtrC ptr = ptr16.Mid(start, i-start); |
|
204 aMessageBody->InsertL(pos, ptr); |
|
205 pos += ptr.Length(); |
|
206 start = i+1; |
|
207 } |
|
208 } |
|
209 |
|
210 if (start != i) |
|
211 { |
|
212 TPtrC ptr = ptr16.Mid(start, i-start); |
|
213 aMessageBody->InsertL(pos, ptr); |
|
214 } |
|
215 } |
|
216 } |
|
217 |
|
218 CleanupStack::PopAndDestroy(text16); |
|
219 } |
|
220 |
|
221 // Decode and store received data |
|
222 HBufC8* CStoreUtilities::DecodeL(const TPtrC8& aBodyData, const TBool aEndOfStream) |
|
223 { |
|
224 // Somewhere to store decoded data, at least as long as source (plus anything we have left |
|
225 // in the partial line buffer which may now get consumed) |
|
226 TInt outputbuffersize=aBodyData.Length()+KBufferExtension; |
|
227 if (iPartialLine) |
|
228 outputbuffersize+=iPartialLine->Des().Length(); |
|
229 |
|
230 HBufC8* decoded=HBufC8::NewL(outputbuffersize); |
|
231 TPtr8 decoded_ptr=decoded->Des(); |
|
232 |
|
233 // Which decoder are we using? |
|
234 switch(iEncodingType) |
|
235 { |
|
236 case EEncodingTypeNone: |
|
237 case EEncodingType7Bit: |
|
238 case EEncodingType8Bit: |
|
239 case EEncodingTypeBinary: |
|
240 case EEncodingTypeUnknown: |
|
241 // Nothing to do, just copy data |
|
242 decoded->Des().Append(aBodyData); |
|
243 break; |
|
244 |
|
245 case EEncodingTypeBASE64: |
|
246 // Decode Base64 data: just filter it through decoder, it |
|
247 // ignores line breaks anyway. |
|
248 iB64Decoder.Decode(aBodyData,decoded_ptr); |
|
249 break; |
|
250 |
|
251 case EEncodingTypeUU: |
|
252 { |
|
253 TPtrC8 bodydata=aBodyData; |
|
254 |
|
255 // Got a partial buffer? |
|
256 if (!iPartialLine) |
|
257 { |
|
258 // Allocate buffer |
|
259 iPartialLine=HBufC8::NewL(KUuDecodedLineLength); |
|
260 iUUDecoding = EFalse; |
|
261 } |
|
262 |
|
263 // Decode UUEncoded data: line by line |
|
264 TBool decodeEnded = EFalse; |
|
265 TInt position=0; |
|
266 while ( bodydata.Length() && !decodeEnded ) |
|
267 { |
|
268 // Find() returns the start of "\r\n". The decoding algorithm |
|
269 // requires that the encoded line contains the "\r\n". |
|
270 TInt lineEnd = bodydata.Find( _L8("\r\n") ); |
|
271 if (lineEnd != KErrNotFound) |
|
272 { |
|
273 lineEnd = lineEnd + 2; |
|
274 AppendExtendL( &iPartialLine, bodydata.Left( lineEnd )); |
|
275 |
|
276 bodydata.Set( bodydata.Mid( lineEnd ) ); |
|
277 |
|
278 // Check for a well-formated begin-tag |
|
279 if ( CheckUUEStartL( iPartialLine->Des() ) ) |
|
280 { |
|
281 iUUDecoding = ETrue; |
|
282 } |
|
283 else if ( iPartialLine->Compare( KImcvUueEnd ) != 0 && iUUDecoding ) |
|
284 { |
|
285 // Every malformatted string is decoded as an empty string |
|
286 // with length 0. Appending such a string is harmless. |
|
287 TPtr8 destination((unsigned char*)decoded_ptr.Ptr()+position,0,outputbuffersize-position); |
|
288 iUUDecoder.Decode(*iPartialLine,destination); |
|
289 position+=destination.Length(); |
|
290 } |
|
291 else if ( iUUDecoding ) |
|
292 { |
|
293 decodeEnded = ETrue; |
|
294 iUUDecoding = EFalse; |
|
295 } |
|
296 |
|
297 iPartialLine->Des().Zero(); |
|
298 } |
|
299 else |
|
300 { |
|
301 AppendExtendL( &iPartialLine, bodydata); |
|
302 |
|
303 // advance to end of bodydata |
|
304 bodydata.Set(bodydata.Ptr()+bodydata.Length(), 0); |
|
305 } |
|
306 } |
|
307 decoded->Des().SetLength(position); |
|
308 break; |
|
309 } |
|
310 |
|
311 case EEncodingTypeQP: |
|
312 { |
|
313 TPtrC8 bodydata=aBodyData; |
|
314 |
|
315 // Got a partial buffer? |
|
316 if (!iPartialLine) |
|
317 { |
|
318 // Allocate buffer |
|
319 iPartialLine=HBufC8::NewL(256); |
|
320 } |
|
321 |
|
322 // Build buffer to decode: basically, QP decoder wants CRLF terminated |
|
323 // lines, so we build them in the iPartialLine buffer. There may be |
|
324 // stuff already there from previous data packet - so we just append. |
|
325 TInt position=0; |
|
326 while(bodydata.Length()) |
|
327 { |
|
328 // Find a line break |
|
329 TInt lineend=bodydata.Find(_L8("\r\n")); |
|
330 |
|
331 // No break? |
|
332 if (lineend==KErrNotFound && !aEndOfStream) |
|
333 { |
|
334 // Stick it all in the partialline buffer, we should get a CRLF |
|
335 // soon... |
|
336 AppendExtendL( &iPartialLine,bodydata); |
|
337 break; |
|
338 } |
|
339 else |
|
340 { |
|
341 if (lineend==KErrNotFound) |
|
342 { |
|
343 // Append whole thing left to buffer |
|
344 AppendExtendL( &iPartialLine,bodydata); |
|
345 |
|
346 // advance to end of bodydata |
|
347 bodydata.Set(bodydata.Ptr()+bodydata.Length(), 0); |
|
348 } |
|
349 else |
|
350 { |
|
351 // Append to buffer up to that point (including the \r\n) |
|
352 AppendExtendL( &iPartialLine,bodydata.Left(lineend+2)); |
|
353 |
|
354 // Remove from the buffer we're working on (including the \r\n) |
|
355 bodydata.Set(bodydata.Ptr()+lineend+2,bodydata.Length()-lineend-2); |
|
356 } |
|
357 |
|
358 // Decode & skip on in buffer |
|
359 TPtr8 destination((unsigned char*)decoded_ptr.Ptr()+position,0,outputbuffersize-position); |
|
360 iQPDecoder.Decode(*iPartialLine,destination); |
|
361 position+=destination.Length(); |
|
362 iPartialLine->Des().Zero(); |
|
363 } |
|
364 } |
|
365 |
|
366 // Update decoded |
|
367 decoded->Des().SetLength(position); |
|
368 break; |
|
369 } |
|
370 } |
|
371 |
|
372 // put back any partially converted data |
|
373 if (iLeftOver.Length()) |
|
374 { |
|
375 decoded->Des().Insert(0, iLeftOver); |
|
376 iLeftOver.SetLength(0); |
|
377 } |
|
378 |
|
379 return decoded; |
|
380 } |
|
381 |