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