|
1 // Copyright (c) 2003-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 // CMsvBodyText.cpp |
|
15 // |
|
16 |
|
17 #include "MSVSTORE.H" // CMsvStore |
|
18 #include "cmsvbodytext.h" |
|
19 #include <charconv.h> // CCnvCharacterSetConverter |
|
20 #include <txtrich.h> // CRichText |
|
21 #include <s32mem.h> |
|
22 #ifdef SYMBIAN_ENABLE_SPLIT_HEADERS |
|
23 #include "msvconsts.h" |
|
24 #endif |
|
25 |
|
26 /** |
|
27 The unique stream identifier of the character set data stored in the message store. |
|
28 */ |
|
29 const TUid KMsvCharData = {0x101FD0E1}; |
|
30 |
|
31 |
|
32 /** |
|
33 The version of KMsvEntryCharStream. Used to maintain data compatibility with older versions. |
|
34 */ |
|
35 const TUint8 KMsvCharDataVersion = 1; |
|
36 |
|
37 |
|
38 /** |
|
39 The version of KMsvEntry8BitASCIIStream. Used to maintain data compatibility with older versions. |
|
40 */ |
|
41 const TUint8 KMsv8BitEncodedBodyDataVersion = 1; |
|
42 |
|
43 |
|
44 |
|
45 |
|
46 EXPORT_C CMsvBodyText* CMsvBodyText::NewL() |
|
47 /** |
|
48 Allocates and constructs an empty body text object. |
|
49 */ |
|
50 { |
|
51 return new (ELeave) CMsvBodyText(); |
|
52 } |
|
53 |
|
54 |
|
55 EXPORT_C CMsvBodyText* CMsvBodyText::NewLC() |
|
56 /** |
|
57 Allocates and constructs an empty body text object. The object is put on the cleanup stack. |
|
58 */ |
|
59 { |
|
60 CMsvBodyText* self = new (ELeave) CMsvBodyText(); |
|
61 CleanupStack::PushL(self); |
|
62 return self; |
|
63 } |
|
64 |
|
65 |
|
66 EXPORT_C CMsvBodyText::~CMsvBodyText() |
|
67 /** |
|
68 Deallocates and destroys the body text object. |
|
69 */ |
|
70 { |
|
71 } |
|
72 |
|
73 |
|
74 EXPORT_C void CMsvBodyText::SetCharacterSet(const TUint aCharacterSetIdentifier) |
|
75 /** |
|
76 Sets the character set unique id to be used when decoding the 8 bit data stream. |
|
77 */ |
|
78 { |
|
79 iCharSet = aCharacterSetIdentifier; |
|
80 } |
|
81 |
|
82 |
|
83 EXPORT_C TUint CMsvBodyText::CharacterSet() const |
|
84 /** |
|
85 Retrieves the character set unique id that will be used when decoding the 8 bit |
|
86 data stream. |
|
87 */ |
|
88 { |
|
89 return iCharSet; |
|
90 } |
|
91 |
|
92 |
|
93 EXPORT_C void CMsvBodyText::SetDefaultCharacterSet(const TUint aCharacterSetIdentifier) |
|
94 /** |
|
95 Sets the default character set to use when decoding the 8 bit data if |
|
96 the character set unique id has not been specified. |
|
97 */ |
|
98 { |
|
99 iDefaultCharSet = aCharacterSetIdentifier; |
|
100 } |
|
101 |
|
102 |
|
103 EXPORT_C TUint CMsvBodyText::DefaultCharacterSet() const |
|
104 /** |
|
105 Retrieves the default character set unique id that will be used when decoding the 8 bit |
|
106 data stream if the character set unique id has not been specified. |
|
107 */ |
|
108 { |
|
109 return iDefaultCharSet; |
|
110 } |
|
111 |
|
112 |
|
113 EXPORT_C void CMsvBodyText::RestoreL(CMsvStore& aStore) |
|
114 /** |
|
115 Retrieves the character set unique ids and 8 bit data stream from the message store. |
|
116 |
|
117 @param aStore |
|
118 A store in read-only or read-write (edit) mode. |
|
119 |
|
120 @leave KErrNotFound |
|
121 The store does not contain 8 bit encoded body text data. |
|
122 */ |
|
123 { |
|
124 RMsvReadStream in; |
|
125 in.OpenLC(aStore, KMsvCharData); |
|
126 in.ReadUint8L(); // Ignore the version number. Future versions will have to check this value for data compatibility. |
|
127 iCharSet = in.ReadUint32L(); |
|
128 iDefaultCharSet = in.ReadUint32L(); |
|
129 CleanupStack::PopAndDestroy(&in); |
|
130 } |
|
131 |
|
132 |
|
133 EXPORT_C void CMsvBodyText::StoreL(CMsvStore& aStore) |
|
134 /** |
|
135 Just adds the character set identifiers to the store cache. |
|
136 |
|
137 @pre |
|
138 The store must be in read-write (edit) mode. |
|
139 |
|
140 @param aStore |
|
141 The store must be open in read-write (edit) mode. |
|
142 |
|
143 @leave KErrAccessedDenied |
|
144 The message store is not in read-write (edit) mode. |
|
145 */ |
|
146 { |
|
147 RMsvWriteStream out; |
|
148 out.AssignLC(aStore, KMsvCharData); |
|
149 out.WriteUint8L(KMsvCharDataVersion); |
|
150 out.WriteUint32L(iCharSet); |
|
151 out.WriteUint32L(iDefaultCharSet); |
|
152 out.CommitL(); |
|
153 CleanupStack::PopAndDestroy(&out); |
|
154 } |
|
155 |
|
156 |
|
157 EXPORT_C void CMsvBodyText::StoreL(CMsvStore& aStore, const CBufBase& aData) |
|
158 /** |
|
159 Adds the character identifiers and 8 bit encoded body text data to the store cache. |
|
160 |
|
161 @pre |
|
162 The store must be in read-write (edit) mode. |
|
163 |
|
164 @param aStore |
|
165 The store must be open in read-write (edit) mode. |
|
166 |
|
167 @param aData |
|
168 8 bit encoded body text data to be added to the store. |
|
169 |
|
170 @leave KErrAccessedDenied |
|
171 The message store is not in read-write (edit) mode. |
|
172 */ |
|
173 { |
|
174 // Add the character set information. |
|
175 StoreL(aStore); |
|
176 |
|
177 // Add the 8 bit data. |
|
178 RMsvWriteStream out; |
|
179 out.AssignLC(aStore, KMsv8BitEncodedBodyData); |
|
180 out.WriteUint8L(KMsv8BitEncodedBodyDataVersion); |
|
181 |
|
182 RBufReadStream reader(aData); |
|
183 out.WriteL(reader,aData.Size()); |
|
184 |
|
185 out.CommitL(); |
|
186 CleanupStack::PopAndDestroy(&out); |
|
187 } |
|
188 |
|
189 |
|
190 EXPORT_C void CMsvBodyText::GetBodyTextL(RFs& aFs, CMsvStore& aStore, CRichText& aBodyText) |
|
191 /** |
|
192 Decodes the encoded 8 bit body text data into the correct character set and adds the decoded output |
|
193 to the richtext object owned by the client. If the character set is not specified, then |
|
194 the default character set is used. If the character set is specified but not installed on the |
|
195 system then the default character set is used instead. |
|
196 |
|
197 @pre |
|
198 The store must contain encoded 8 bit body text data. |
|
199 |
|
200 @param aFs |
|
201 A connected file system handle. It is used to search for installed character set decoders. |
|
202 |
|
203 @param aStore |
|
204 The store can be opened in read-only or read-write (edit) mode. |
|
205 |
|
206 @param aBodyText |
|
207 The rich text object that will be appended with the decoded text. |
|
208 |
|
209 @leave KErrNotSupported |
|
210 The character set used to decode the 8 bit data cannot be found on the system. |
|
211 |
|
212 @leave KErrNotFound |
|
213 The store does not contain encoded 8 bit body text data. |
|
214 */ |
|
215 { |
|
216 // Create and initialise decoder. Leave if the character set cannot be located. |
|
217 CCnvCharacterSetConverter* converter = CCnvCharacterSetConverter::NewL(); |
|
218 CleanupStack::PushL(converter); |
|
219 CCnvCharacterSetConverter::TAvailability available = CCnvCharacterSetConverter::ENotAvailable; |
|
220 if (iCharSet != 0) |
|
221 available = converter->PrepareToConvertToOrFromL(iCharSet, aFs); |
|
222 |
|
223 if (available == CCnvCharacterSetConverter::ENotAvailable) |
|
224 available = converter->PrepareToConvertToOrFromL(iDefaultCharSet, aFs); |
|
225 |
|
226 if (available != CCnvCharacterSetConverter::EAvailable) |
|
227 User::Leave(KErrNotSupported); |
|
228 |
|
229 // Open the 8 bit data stream and calculate the number of bytes of data to be read. |
|
230 RMsvReadStream in; |
|
231 in.OpenLC(aStore, KMsv8BitEncodedBodyData); |
|
232 in.ReadUint8L(); // Ignore the version number since this is the 1st version. |
|
233 MStreamBuf* sourceStream = in.Source(); |
|
234 TInt bytesRemaining = sourceStream->SizeL() - 1; // Less one to account for the TUint8 version byte. |
|
235 |
|
236 // Create temporary buffers to hold the 8 bit data chunk and decoded chunk of unicode characters. |
|
237 // 2 kB unicode output buffer + 1 kB 8 bit input data. |
|
238 HBufC8* inBuf = HBufC8::NewLC(KMsvDecodeChunkLength); |
|
239 HBufC16* outBuf = HBufC16::NewLC(KMsvDecodeChunkLength); |
|
240 TPtr8 inPtr = inBuf->Des(); |
|
241 TPtr16 outPtr = outBuf->Des(); |
|
242 |
|
243 TInt state = CCnvCharacterSetConverter::KStateDefault; // Used to preserve state across multiple calls to ConvertToUnicode. |
|
244 TBool newLine = EFalse; |
|
245 TBool newPara = EFalse; |
|
246 |
|
247 // Decode chunk by chunk to limit memory consumption. |
|
248 while(bytesRemaining > 0) |
|
249 { |
|
250 // Set the buffer length to the smaller of the number of bytes remaining, |
|
251 // or the chunk length. |
|
252 inPtr.SetLength(bytesRemaining < KMsvDecodeChunkLength ? bytesRemaining : KMsvDecodeChunkLength); |
|
253 |
|
254 // Read data from stream up to and including the 1st LF encountered. |
|
255 TRAPD(err, in.ReadL(inPtr, TChar(0x0A))); |
|
256 if (err != KErrEof) |
|
257 User::LeaveIfError(err); |
|
258 TInt len = inPtr.Length(); |
|
259 __ASSERT_DEBUG(len > 0, User::Invariant()); |
|
260 bytesRemaining -= len; |
|
261 |
|
262 // If the data read is terminated with an LF or CRLF, remove it and mark it for |
|
263 // appending a rich text line end or paragraph delimiter. |
|
264 TInt bytesTruncated = 0; |
|
265 if (inPtr[len - 1] != 0x0A) |
|
266 { |
|
267 // The data read does not contain any new line or paragraph delimters. |
|
268 newLine = EFalse; |
|
269 newPara = EFalse; |
|
270 } |
|
271 else |
|
272 { |
|
273 // Data read is terminated with LF. Truncate length of data to ignore LF or CRLF bytes. |
|
274 if (len > 1 && inPtr[len - 2] == 0x0D) |
|
275 { |
|
276 bytesTruncated = 2; |
|
277 len -= bytesTruncated; |
|
278 inPtr.SetLength(len); // Remove CRLF. |
|
279 } |
|
280 else |
|
281 { |
|
282 bytesTruncated = 1; |
|
283 len -= bytesTruncated; |
|
284 inPtr.SetLength(len); // Remove LF. |
|
285 } |
|
286 |
|
287 // Work out if it should be a new line or new paragraph. |
|
288 if (len == 0) |
|
289 { |
|
290 if (newLine) |
|
291 { |
|
292 // There are more than two new line delimiters in a row with no data |
|
293 // between them - new paragraph. |
|
294 newLine = EFalse; |
|
295 newPara = ETrue; |
|
296 } |
|
297 else |
|
298 { |
|
299 newLine = ETrue; |
|
300 newPara = EFalse; |
|
301 } |
|
302 } |
|
303 else |
|
304 { |
|
305 // It's a new line. |
|
306 newLine = ETrue; |
|
307 newPara = EFalse; |
|
308 } |
|
309 } |
|
310 |
|
311 // Convert the 8 bit data to unicode. |
|
312 TInt bytesUnconverted = converter->ConvertToUnicode(outPtr, inPtr, state); |
|
313 if (bytesUnconverted > 0) |
|
314 { |
|
315 // Some bytes could not be converted because the output buffer was too small. |
|
316 // Seek back to the position of the 1st unconverted byte and recalculate the bytes remaining. |
|
317 TInt bytesSeekBack = bytesUnconverted + bytesTruncated; |
|
318 sourceStream->SeekL(MStreamBuf::ERead, EStreamMark, -bytesSeekBack); |
|
319 bytesRemaining += bytesSeekBack; |
|
320 newLine = EFalse; |
|
321 newPara = EFalse; |
|
322 } |
|
323 |
|
324 // There is no AppendL method for CRichText. Insert decoded text at end of document instead. |
|
325 aBodyText.InsertL(aBodyText.DocumentLength(), outPtr); |
|
326 |
|
327 // Insert a new line or paragraph delimiter if necessary. |
|
328 if (newLine) |
|
329 aBodyText.InsertL(aBodyText.DocumentLength(), CEditableText::ELineBreak); |
|
330 if (newPara) |
|
331 aBodyText.InsertL(aBodyText.DocumentLength(), CEditableText::EParagraphDelimiter); |
|
332 } |
|
333 |
|
334 CleanupStack::PopAndDestroy(4, converter); // outBuf, inBuf, in, converter |
|
335 } |
|
336 |
|
337 EXPORT_C void CMsvBodyText::GetBodyTextL(CMsvStore& aStore, TDes8& aBufer) |
|
338 { |
|
339 // Open the 8 bit data stream and calculate the number of bytes of data to be read. |
|
340 if(aStore.IsPresentL(KMsv8BitEncodedBodyData)) |
|
341 { |
|
342 RMsvReadStream in; |
|
343 in.OpenLC(aStore, KMsv8BitEncodedBodyData); |
|
344 in.ReadUint8L(); // Ignore the version number since this is the 1st version. |
|
345 MStreamBuf* sourceStream = in.Source(); |
|
346 TInt bytesRemaining = sourceStream->SizeL() - 1; // Less one to account for the TUint8 version byte. |
|
347 in.ReadL(aBufer, bytesRemaining); |
|
348 CleanupStack::PopAndDestroy(); // in |
|
349 } |
|
350 else if(aStore.IsPresentL(KMsvPlainBodyText8)) |
|
351 { |
|
352 RFileReadStream inputStream; |
|
353 aStore.Restore8BitBodyTextL(inputStream); |
|
354 inputStream.PushL(); |
|
355 MStreamBuf* sourceStream = inputStream.Source(); |
|
356 inputStream.ReadL(aBufer, sourceStream->SizeL()); |
|
357 CleanupStack::PopAndDestroy(); // inputStream |
|
358 } |
|
359 else |
|
360 { |
|
361 User::Leave(KErrNotSupported); |
|
362 } |
|
363 } |
|
364 |
|
365 EXPORT_C TInt CMsvBodyText::GetBodyLengthL(CMsvStore& aStore) |
|
366 { |
|
367 if(aStore.IsPresentL(KMsv8BitEncodedBodyData)) |
|
368 { |
|
369 RMsvReadStream in; |
|
370 in.OpenLC(aStore, KMsv8BitEncodedBodyData); |
|
371 in.ReadUint8L(); // Ignore the version number since this is the 1st version. |
|
372 MStreamBuf* sourceStream = in.Source(); |
|
373 TInt length = sourceStream->SizeL() - 1; // Less one to account for the TUint8 version byte. |
|
374 CleanupStack::PopAndDestroy(); // in |
|
375 return length; |
|
376 } |
|
377 else if(aStore.IsPresentL(KMsvPlainBodyText8)) |
|
378 { |
|
379 RFileReadStream inputStream; |
|
380 aStore.Restore8BitBodyTextL(inputStream); |
|
381 inputStream.PushL(); |
|
382 MStreamBuf* sourceStream = inputStream.Source(); |
|
383 TInt bodyLength = sourceStream->SizeL(); |
|
384 CleanupStack::PopAndDestroy(); // inputStream |
|
385 return bodyLength; |
|
386 } |
|
387 else |
|
388 { |
|
389 return KErrNotSupported; |
|
390 } |
|
391 } |
|
392 |
|
393 EXPORT_C void CMsvBodyText::RemoveL(CMsvStore& aStore) |
|
394 /** |
|
395 Removes the 8 bit data body text data from the store cache. |
|
396 |
|
397 @pre |
|
398 The store must be in read-write (edit) mode. |
|
399 |
|
400 @leave KErrAccessedDenied |
|
401 The message store is not in read-write (edit) mode. |
|
402 */ |
|
403 { |
|
404 if (aStore.IsPresentL(KMsvCharData)) |
|
405 aStore.RemoveL(KMsvCharData); |
|
406 if (aStore.IsPresentL(KMsv8BitEncodedBodyData)) |
|
407 aStore.RemoveL(KMsv8BitEncodedBodyData); |
|
408 } |
|
409 |
|
410 |
|
411 EXPORT_C TBool CMsvBodyText::IsPresentL(const CMsvStore& aStore) const |
|
412 /** |
|
413 Tests to see if the store contains 8 bit body text data. |
|
414 |
|
415 @param aStore |
|
416 The store can be open in read-only or read-write (edit) mode. |
|
417 |
|
418 @return |
|
419 ETrue if the store contains 8 bit body text data. |
|
420 */ |
|
421 { |
|
422 return aStore.IsPresentL(KMsvCharData); |
|
423 } |
|
424 |
|
425 |
|
426 CMsvBodyText::CMsvBodyText() |
|
427 /** |
|
428 Private constructor. |
|
429 */ |
|
430 { |
|
431 } |