|
1 /* |
|
2 * Copyright (c) 2004-2006 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: |
|
15 * Helper class to implement attachment handling |
|
16 * |
|
17 */ |
|
18 |
|
19 |
|
20 |
|
21 // INCLUDE FILES |
|
22 |
|
23 #include <e32std.h> |
|
24 |
|
25 #include <msventry.h> |
|
26 #include <msvstd.h> |
|
27 #include <msvapi.h> //Message Server |
|
28 #include <centralrepository.h> |
|
29 #include <badesca.h> |
|
30 #include <cmsvmimeheaders.h> |
|
31 #include <mmsvattachmentmanager.h> |
|
32 #include <mmsvattachmentmanagersync.h> |
|
33 #include <utf.h> |
|
34 #include <f32file.h> |
|
35 #include <msgtextutils.h> |
|
36 |
|
37 #include "mmsconst.h" |
|
38 #include "mmsattachmenthandler.h" |
|
39 #include "mmsgenutils.h" |
|
40 |
|
41 // EXTERNAL DATA STRUCTURES |
|
42 |
|
43 // EXTERNAL FUNCTION PROTOTYPES |
|
44 |
|
45 // CONSTANTS |
|
46 |
|
47 // MACROS |
|
48 |
|
49 // LOCAL CONSTANTS AND MACROS |
|
50 const TInt KMms10kilos = 10240; |
|
51 const TInt KMmsMaxBytesPerCharacter = 4; |
|
52 const TInt KMmsTextBufferSize = 256; |
|
53 const TInt KMmsUnicodeToUtf2MaxIncrease = 2; |
|
54 const TInt KMmsLengthOfCRlf = 2; |
|
55 |
|
56 // MODULE DATA STRUCTURES |
|
57 |
|
58 // LOCAL FUNCTION PROTOTYPES |
|
59 |
|
60 // ==================== LOCAL FUNCTIONS ==================== |
|
61 |
|
62 // ================= MEMBER FUNCTIONS ======================= |
|
63 |
|
64 // C++ default constructor can NOT contain any code, that |
|
65 // might leave. |
|
66 // |
|
67 CMmsAttachmentHandler::CMmsAttachmentHandler() |
|
68 { |
|
69 } |
|
70 |
|
71 // EPOC default constructor can leave. |
|
72 void CMmsAttachmentHandler::ConstructL() |
|
73 { |
|
74 } |
|
75 |
|
76 // Two-phased constructor. |
|
77 EXPORT_C CMmsAttachmentHandler* CMmsAttachmentHandler::NewL() |
|
78 { |
|
79 CMmsAttachmentHandler* self = new (ELeave) CMmsAttachmentHandler(); |
|
80 |
|
81 CleanupStack::PushL( self ); |
|
82 self->ConstructL(); |
|
83 CleanupStack::Pop( self ); |
|
84 |
|
85 return self; |
|
86 } |
|
87 |
|
88 |
|
89 // Destructor |
|
90 CMmsAttachmentHandler::~CMmsAttachmentHandler() |
|
91 { |
|
92 } |
|
93 |
|
94 // --------------------------------------------------------- |
|
95 // CMmsAttachmentHandler::AttachmentsSizeL |
|
96 // --------------------------------------------------------- |
|
97 // |
|
98 EXPORT_C TInt CMmsAttachmentHandler::AttachmentsSizeL( CMsvStore& aStore ) |
|
99 { |
|
100 // Caller controls store |
|
101 TInt size = 0; |
|
102 |
|
103 MMsvAttachmentManager& attachMan = aStore.AttachmentManagerL(); |
|
104 TInt numAttachments = attachMan.AttachmentCount(); |
|
105 |
|
106 TInt i; |
|
107 |
|
108 for ( i = 0; i < numAttachments; i++ ) |
|
109 { |
|
110 CMsvAttachment* attachmentInfo = attachMan.GetAttachmentInfoL(i); |
|
111 CleanupStack::PushL( attachmentInfo ); |
|
112 |
|
113 CMsvMimeHeaders* mimeHeaders = CMsvMimeHeaders::NewL(); |
|
114 CleanupStack::PushL( mimeHeaders ); |
|
115 |
|
116 mimeHeaders->RestoreL( *attachmentInfo ); |
|
117 |
|
118 RFile attaFile = attachMan.GetAttachmentFileL( i ); |
|
119 CleanupClosePushL( attaFile ); |
|
120 TInt fileSize = 0; |
|
121 |
|
122 // If we cannot access the file, we are in trouble |
|
123 User::LeaveIfError( attaFile.Size( fileSize ) ); |
|
124 |
|
125 // This adds up mime header size + actual attachment binary data |
|
126 size += mimeHeaders->Size() + fileSize; |
|
127 |
|
128 CleanupStack::PopAndDestroy( &attaFile ); // close attaFile |
|
129 CleanupStack::PopAndDestroy( mimeHeaders ); |
|
130 CleanupStack::PopAndDestroy( attachmentInfo ); |
|
131 } |
|
132 |
|
133 return size; |
|
134 } |
|
135 |
|
136 // --------------------------------------------------------- |
|
137 // CMmsAttachmentHandler::IsValidFilename |
|
138 // --------------------------------------------------------- |
|
139 // |
|
140 EXPORT_C TBool CMmsAttachmentHandler::IsValidFilename( RFs& aFs, const TPtrC& aFileName ) |
|
141 { |
|
142 TBool validName = EFalse; //pessimist. |
|
143 |
|
144 if ( aFileName.Length() == 0 ) |
|
145 { |
|
146 return EFalse; |
|
147 } |
|
148 |
|
149 // filename should not start with dot |
|
150 // or contain any control characters |
|
151 TInt i; |
|
152 // First character may not be . or space |
|
153 if ( aFileName[0] == 0x2e || aFileName[0] == 0x20 ) |
|
154 { |
|
155 return EFalse; |
|
156 } |
|
157 |
|
158 for ( i = 0; i < aFileName.Length(); i++ ) |
|
159 { |
|
160 // check for control characters - RFs does not do it. |
|
161 if ( aFileName[i] < 0x20 ) |
|
162 { |
|
163 // found a control character - not allowed. |
|
164 return EFalse; |
|
165 } |
|
166 } |
|
167 validName = aFs.IsValidName( aFileName ); |
|
168 |
|
169 return validName; |
|
170 } |
|
171 |
|
172 // --------------------------------------------------------- |
|
173 // CMmsAttachmentHandler::CreateAttachmentL |
|
174 // --------------------------------------------------------- |
|
175 // |
|
176 EXPORT_C void CMmsAttachmentHandler::CreateAttachmentL( |
|
177 CMsvStore& aStore, |
|
178 RFile& aFile, |
|
179 RFs& aFs, |
|
180 TDriveUnit aMessageDrive, |
|
181 TDesC8& aMimeType, |
|
182 CMsvMimeHeaders& aMimeHeaders, |
|
183 CMsvAttachment* aAttachmentInfo, |
|
184 TMsvAttachmentId& aAttaId) |
|
185 { |
|
186 // The ownership of aAttachmentInfo will be transferred to attachment manager |
|
187 // We must keep it safe until that time |
|
188 CleanupStack::PushL( aAttachmentInfo ); |
|
189 |
|
190 // Check that sufficient disk space available |
|
191 // for attachment binary file and index entry |
|
192 |
|
193 TInt error = KErrNone; |
|
194 TInt fileSize = 0; |
|
195 |
|
196 error = aFile.Size( fileSize ); |
|
197 User::LeaveIfError( error ); |
|
198 |
|
199 aAttachmentInfo->SetSize( fileSize ); |
|
200 if ( aMimeHeaders.SuggestedFilename().Length() == 0 ) |
|
201 { |
|
202 TFileName name; |
|
203 error = aFile.Name( name ); |
|
204 if ( error == KErrNone ) |
|
205 { |
|
206 aMimeHeaders.SetSuggestedFilenameL( name ); |
|
207 } |
|
208 } |
|
209 |
|
210 if ( aMimeHeaders.SuggestedFilename().Length() > 0 ) |
|
211 { |
|
212 aAttachmentInfo->SetAttachmentNameL( aMimeHeaders.SuggestedFilename() ); |
|
213 } |
|
214 if ( aMimeType.Length() > 0 ) |
|
215 { |
|
216 aAttachmentInfo->SetMimeTypeL( aMimeType ); |
|
217 } |
|
218 |
|
219 // Check that sufficient disk space available |
|
220 // for attachment binary file and index entry |
|
221 |
|
222 // This does not include mime headers. |
|
223 // The mime headers are covered by KMmsIndexEntryExtra, |
|
224 // however the value may be too small, has to be checked. |
|
225 |
|
226 if ( TMmsGenUtils::DiskSpaceBelowCriticalLevelL( |
|
227 &aFs, |
|
228 fileSize + KMmsIndexEntryExtra, |
|
229 aMessageDrive ) ) |
|
230 { |
|
231 // we use standard error code here |
|
232 User::Leave( KErrDiskFull ); |
|
233 } |
|
234 |
|
235 if ( ( aMimeHeaders.ContentType().Length() == 0 || |
|
236 aMimeHeaders.ContentSubType().Length() == 0 ) && aMimeType.Length() > 0 ) |
|
237 { |
|
238 TInt position = aMimeType.Find( KMmsSlash8 ); |
|
239 if ( position > 0 ) |
|
240 { |
|
241 aMimeHeaders.SetContentTypeL( aMimeType.Left( position ) ); |
|
242 } |
|
243 if ( position < aMimeType.Length() - 1 ) |
|
244 { |
|
245 aMimeHeaders.SetContentSubTypeL( aMimeType.Mid( position + 1 ) ); |
|
246 } |
|
247 } |
|
248 |
|
249 MMsvAttachmentManagerSync& attaManSync = aStore.AttachmentManagerExtensionsL(); |
|
250 |
|
251 RFile attaFile; |
|
252 |
|
253 // ownership of aAttachmentInfo is transferred to attachment manager. |
|
254 attaManSync.CreateAttachmentL( aMimeHeaders.SuggestedFilename(), |
|
255 attaFile, aAttachmentInfo ); |
|
256 aAttaId = aAttachmentInfo->Id(); |
|
257 CleanupStack::Pop( aAttachmentInfo ); // attachment manager now owns aAttachmentInfo |
|
258 |
|
259 // If the previous call was successful, we can now write the data |
|
260 // We need a buffer because we read from one file and write to another |
|
261 |
|
262 CleanupClosePushL( attaFile ); |
|
263 |
|
264 if ( fileSize > 0 ) |
|
265 { |
|
266 // Greedy, but we don't try to swallow large files all in one piece |
|
267 // Small files may be handled in one piece |
|
268 HBufC8* buffer = HBufC8::NewL( Min( fileSize, KMms10kilos ) ); // Try to get at least 10 k |
|
269 CleanupStack::PushL( buffer ); |
|
270 |
|
271 TPtr8 ptr = buffer->Des(); |
|
272 ptr.SetLength( 1 ); // initialized to something larger that 0, size is adjusted later |
|
273 |
|
274 while( ptr.Length() > 0 && error == KErrNone ) |
|
275 { |
|
276 error = aFile.Read( ptr ); |
|
277 if ( ptr.Length() > 0 && error == KErrNone) |
|
278 { |
|
279 error = attaFile.Write( ptr ); |
|
280 } |
|
281 } |
|
282 if ( error == KErrNone ) |
|
283 { |
|
284 error = attaFile.Flush(); |
|
285 } |
|
286 |
|
287 CleanupStack::PopAndDestroy( buffer ); |
|
288 buffer = NULL; |
|
289 } |
|
290 |
|
291 // we must alway close |
|
292 CleanupStack::PopAndDestroy( &attaFile ); // close attaFile |
|
293 |
|
294 // Now actual datafile is ready. |
|
295 // We still have the atta info, and we must store the mimeheaders |
|
296 |
|
297 aMimeHeaders.StoreL( *aAttachmentInfo ); |
|
298 |
|
299 // Now all should be ready. |
|
300 // Caller must commit store (maybe headers still need to be changed, |
|
301 // or maybe several attachments are added before committing store) |
|
302 |
|
303 User::LeaveIfError( error ); |
|
304 } |
|
305 |
|
306 // --------------------------------------------------------- |
|
307 // CMmsAttachmentHandler::CreateTextAttachmentL |
|
308 // --------------------------------------------------------- |
|
309 EXPORT_C void CMmsAttachmentHandler::CreateTextAttachmentL( |
|
310 CMsvStore& aStore, |
|
311 TMsvAttachmentId& aAttachmentId, |
|
312 const TDesC& aText, |
|
313 const TDesC& aFile, |
|
314 RFs& aFs, |
|
315 TDriveUnit aMessageDrive, |
|
316 TBool aConvertParagraphSeparator /*= ETrue*/ ) |
|
317 { |
|
318 |
|
319 HBufC* convertedText = NULL; |
|
320 TPtrC text; |
|
321 |
|
322 if ( aConvertParagraphSeparator ) |
|
323 { |
|
324 convertedText = CMsgTextUtils::ConvertParagraphSeparatorsLC( aText ); |
|
325 text.Set( convertedText->Des() ); |
|
326 } |
|
327 else |
|
328 { |
|
329 text.Set( aText ); |
|
330 } |
|
331 |
|
332 const TInt KMmsMaxBytesPerCharacter = 4; |
|
333 HBufC8* buffer = HBufC8::NewL( text.Length() * KMmsMaxBytesPerCharacter ); // paranoid. |
|
334 CleanupStack::PushL( buffer ); |
|
335 TPtr8 buf8 = buffer->Des(); |
|
336 |
|
337 CMsvMimeHeaders* mimeHeaders = CMsvMimeHeaders::NewL(); |
|
338 CleanupStack::PushL( mimeHeaders ); |
|
339 |
|
340 // attaInfo must be on top of stack because the ownership will be transferred |
|
341 // to attacment manager. |
|
342 CMsvAttachment* attaInfo = CMsvAttachment::NewL(CMsvAttachment::EMsvFile); |
|
343 CleanupStack::PushL( attaInfo ); |
|
344 |
|
345 TPtrC8 contentType; |
|
346 contentType.Set( KMmsTextPlain ); |
|
347 |
|
348 TInt position = contentType.Find( KMmsSlash8 ); |
|
349 mimeHeaders->SetContentTypeL( contentType.Left( position ) ); |
|
350 mimeHeaders->SetContentSubTypeL( contentType.Mid( position + 1 ) ); |
|
351 attaInfo->SetMimeTypeL( contentType ); |
|
352 attaInfo->SetAttachmentNameL( aFile ); |
|
353 |
|
354 mimeHeaders->SetMimeCharset( KMmsUtf8 ); |
|
355 mimeHeaders->SetSuggestedFilenameL( aFile ); |
|
356 |
|
357 // if conversion fails, something is really seriously wrong |
|
358 TInt error = CnvUtfConverter::ConvertFromUnicodeToUtf8( buf8, text ); |
|
359 |
|
360 if ( TMmsGenUtils::DiskSpaceBelowCriticalLevelL( |
|
361 &aFs, |
|
362 buf8.Length() + mimeHeaders->Size() + KMmsIndexEntryExtra, |
|
363 aMessageDrive ) ) |
|
364 { |
|
365 // we use standard error code here |
|
366 User::Leave( KErrDiskFull ); |
|
367 } |
|
368 else |
|
369 { |
|
370 User::LeaveIfError( error ); |
|
371 } |
|
372 |
|
373 attaInfo->SetSize( buf8.Length() ); |
|
374 mimeHeaders->StoreL( *attaInfo ); // Mime headers are streamed into atta info |
|
375 |
|
376 MMsvAttachmentManagerSync& attaManSync = aStore.AttachmentManagerExtensionsL(); |
|
377 |
|
378 RFile attaFile; |
|
379 attaManSync.CreateAttachmentL( aFile, attaFile, attaInfo ); |
|
380 CleanupStack::Pop( attaInfo ); // attaInfo ownership was transferred. |
|
381 aAttachmentId = attaInfo->Id(); |
|
382 |
|
383 // Now our file handle is open for writing |
|
384 |
|
385 if ( buf8.Length() > 0 ) |
|
386 { |
|
387 attaFile.Write( buf8 ); |
|
388 error = attaFile.Flush(); |
|
389 } |
|
390 attaFile.Close(); |
|
391 |
|
392 if ( error != KErrNone ) |
|
393 { |
|
394 // Something went wrong when we tried to write our data. |
|
395 // We must delete the attachment as it does not contain the |
|
396 // intended data. |
|
397 RemoveAttachmentL( aAttachmentId, aStore ); |
|
398 aAttachmentId = 0; |
|
399 } |
|
400 |
|
401 CleanupStack::PopAndDestroy( mimeHeaders ); |
|
402 CleanupStack::PopAndDestroy( buffer ); |
|
403 |
|
404 if ( convertedText ) |
|
405 { |
|
406 CleanupStack::PopAndDestroy( convertedText ); |
|
407 convertedText = NULL; |
|
408 } |
|
409 |
|
410 User::LeaveIfError( error ); |
|
411 |
|
412 } |
|
413 |
|
414 // --------------------------------------------------------- |
|
415 // CMmsAttachmentHandler::CreateUTF8TextAttachmentFromFileL |
|
416 // --------------------------------------------------------- |
|
417 EXPORT_C void CMmsAttachmentHandler::CreateUTF8TextAttachmentFromFileL( |
|
418 CMsvStore& aStore, |
|
419 TMsvAttachmentId& aAttachmentId, |
|
420 RFile& aFile, |
|
421 RFs& aFs, |
|
422 TDriveUnit aMessageDrive ) |
|
423 { |
|
424 |
|
425 _LIT8 ( KMmsCrLf8, "\x00D\x00A" ); // 8 bit line feed |
|
426 TInt size = 0; |
|
427 TInt error = KErrNone; |
|
428 error = aFile.Size( size ); |
|
429 |
|
430 User::LeaveIfError( error ); // if can't get file size, we are in trouble |
|
431 |
|
432 TFileName* filename = new( ELeave ) TFileName; |
|
433 CleanupStack::PushL( filename ); |
|
434 |
|
435 // 256 characters for each read |
|
436 HBufC* textBuffer = HBufC::NewL( KMmsTextBufferSize ); |
|
437 CleanupStack::PushL( textBuffer ); |
|
438 TPtr textPtr = textBuffer->Des(); |
|
439 |
|
440 HBufC8* buffer = HBufC8::NewL( KMmsTextBufferSize * KMmsMaxBytesPerCharacter ); // paranoid. |
|
441 TInt fileSize = 0; // we don't know how big the file will be after conversion |
|
442 CleanupStack::PushL( buffer ); |
|
443 TPtr8 buf8 = buffer->Des(); |
|
444 |
|
445 CMsvMimeHeaders* mimeHeaders = CMsvMimeHeaders::NewL(); |
|
446 CleanupStack::PushL( mimeHeaders ); |
|
447 |
|
448 // attaInfo must be on top of stack because the ownership will be transferred |
|
449 // to attacment manager. |
|
450 CMsvAttachment* attaInfo = CMsvAttachment::NewL( CMsvAttachment::EMsvFile ); |
|
451 CleanupStack::PushL( attaInfo ); |
|
452 |
|
453 TPtrC8 contentType; |
|
454 contentType.Set( KMmsTextPlain ); |
|
455 |
|
456 TInt position = contentType.Find( KMmsSlash8 ); |
|
457 mimeHeaders->SetContentTypeL( contentType.Left( position ) ); |
|
458 mimeHeaders->SetContentSubTypeL( contentType.Mid( position + 1 ) ); |
|
459 attaInfo->SetMimeTypeL( contentType ); |
|
460 |
|
461 filename->Copy( TPtrC() ); |
|
462 aFile.Name( *filename ); // if this returns error, filename should be empty - no suggestion. |
|
463 attaInfo->SetAttachmentNameL( *filename ); |
|
464 mimeHeaders->SetSuggestedFilenameL( *filename ); |
|
465 mimeHeaders->SetMimeCharset( KMmsUtf8 ); |
|
466 |
|
467 if ( TMmsGenUtils::DiskSpaceBelowCriticalLevelL( |
|
468 &aFs, |
|
469 size * KMmsUnicodeToUtf2MaxIncrease + mimeHeaders->Size() + KMmsIndexEntryExtra, |
|
470 aMessageDrive ) ) |
|
471 { |
|
472 // we use standard error code here |
|
473 User::Leave( KErrDiskFull ); |
|
474 } |
|
475 |
|
476 mimeHeaders->StoreL( *attaInfo ); // Mime headers are streamed into atta info |
|
477 |
|
478 MMsvAttachmentManagerSync& attaManSync = aStore.AttachmentManagerExtensionsL(); |
|
479 |
|
480 RFile attaFile; |
|
481 attaManSync.CreateAttachmentL( *filename, attaFile, attaInfo ); |
|
482 CleanupStack::Pop( attaInfo ); // attaInfo ownership was transferred. |
|
483 aAttachmentId = attaInfo->Id(); |
|
484 |
|
485 // Now our file handle is open for writing |
|
486 |
|
487 error = KErrNone; |
|
488 TMmsFileText textFile; |
|
489 textFile.Set( aFile ); |
|
490 |
|
491 while ( error == KErrNone || error == KErrTooBig ) |
|
492 { |
|
493 error = textFile.Read( textPtr ); |
|
494 TBool appendCRLF = ETrue; |
|
495 if ( error == KErrTooBig ) |
|
496 { |
|
497 appendCRLF = EFalse; |
|
498 error = KErrNone; |
|
499 } |
|
500 if ( error != KErrEof ) |
|
501 { |
|
502 // if conversion fails, something is really seriously wrong |
|
503 error = CnvUtfConverter::ConvertFromUnicodeToUtf8( buf8, textPtr ); |
|
504 } |
|
505 if ( error == KErrNone ) |
|
506 { |
|
507 error = attaFile.Write( buf8 ); |
|
508 if ( error == KErrNone ) |
|
509 { |
|
510 fileSize += buf8.Length(); |
|
511 if ( appendCRLF ) |
|
512 { |
|
513 error = attaFile.Write( KMmsCrLf8 ); |
|
514 fileSize += KMmsLengthOfCRlf; // add length of carriage return/line feed |
|
515 } |
|
516 } |
|
517 } |
|
518 } |
|
519 |
|
520 if ( error == KErrEof ) |
|
521 { |
|
522 // end of file has been reached successfully |
|
523 error = KErrNone; |
|
524 } |
|
525 |
|
526 if ( error == KErrNone ) |
|
527 { |
|
528 error = attaFile.Flush(); |
|
529 } |
|
530 attaFile.Close(); |
|
531 |
|
532 if ( error != KErrNone ) |
|
533 { |
|
534 // Something went wrong when we tried to write our data. |
|
535 // We must delete the attachment as it does not contain the |
|
536 // intended data. |
|
537 RemoveAttachmentL( aAttachmentId, aStore ); |
|
538 aAttachmentId = 0; |
|
539 } |
|
540 else |
|
541 { |
|
542 // If data writing was successful, the amount of data written |
|
543 // is now stored in fileSize. |
|
544 // Attachment info structure must be updated |
|
545 MMsvAttachmentManager& attaMan = aStore.AttachmentManagerL(); |
|
546 attaInfo = attaMan.GetAttachmentInfoL( aAttachmentId ); |
|
547 CleanupStack::PushL( attaInfo ); |
|
548 attaInfo->SetSize( fileSize ); |
|
549 attaManSync.ModifyAttachmentInfoL( attaInfo ); |
|
550 // attachment manager now owns the attachment info |
|
551 CleanupStack::Pop( attaInfo ); // attaInfo |
|
552 } |
|
553 |
|
554 CleanupStack::PopAndDestroy( mimeHeaders ); |
|
555 CleanupStack::PopAndDestroy( buffer ); |
|
556 CleanupStack::PopAndDestroy( textBuffer ); |
|
557 CleanupStack::PopAndDestroy( filename ); |
|
558 |
|
559 User::LeaveIfError( error ); |
|
560 |
|
561 } |
|
562 |
|
563 // --------------------------------------------------------- |
|
564 // |
|
565 // --------------------------------------------------------- |
|
566 void CMmsAttachmentHandler::RemoveAttachmentL( TMsvAttachmentId aAttaId, CMsvStore& aStore ) |
|
567 { |
|
568 MMsvAttachmentManager& attaMan = aStore.AttachmentManagerL(); |
|
569 MMsvAttachmentManagerSync& attaManSync = aStore.AttachmentManagerExtensionsL(); |
|
570 |
|
571 // can only remove synchronously if index is known. |
|
572 TInt count = attaMan.AttachmentCount(); |
|
573 |
|
574 TInt i = count - 1; |
|
575 TBool found = EFalse; |
|
576 while ( i >= 0 && !found ) |
|
577 { |
|
578 CMsvAttachment* attachmentInfo = attaMan.GetAttachmentInfoL( i ); |
|
579 CleanupStack::PushL( attachmentInfo ); |
|
580 if ( attachmentInfo->Id() == aAttaId ) |
|
581 { |
|
582 found = ETrue; |
|
583 } |
|
584 else |
|
585 { |
|
586 i--; |
|
587 } |
|
588 CleanupStack::PopAndDestroy( attachmentInfo ); |
|
589 attachmentInfo = NULL; |
|
590 } |
|
591 if ( i >= 0 && found ) |
|
592 { |
|
593 attaManSync.RemoveAttachmentL( i ); |
|
594 } |
|
595 } |
|
596 |
|
597 |
|
598 // Helper class that is used instead of TFileText |
|
599 // because the TFileText does not behave like we want it to behave |
|
600 |
|
601 // --------------------------------------------------------- |
|
602 // Default constructor. |
|
603 // --------------------------------------------------------- |
|
604 // |
|
605 TMmsFileText::TMmsFileText() |
|
606 {} |
|
607 |
|
608 // --------------------------------------------------------- |
|
609 // Sets the file to be read from |
|
610 // --------------------------------------------------------- |
|
611 // |
|
612 void TMmsFileText::Set( RFile& aFile ) |
|
613 { |
|
614 iFile = aFile; |
|
615 iReadBuf.Zero(); |
|
616 iNext = ( TText* )iReadBuf.Ptr(); |
|
617 iEnd = iNext; |
|
618 TInt pos = 0; |
|
619 iFile.Seek( ESeekStart, pos ); |
|
620 iState = EStartOfFile; |
|
621 } |
|
622 |
|
623 // --------------------------------------------------------- |
|
624 // |
|
625 // --------------------------------------------------------- |
|
626 // |
|
627 TInt TMmsFileText::Read( TDes& aDes ) |
|
628 /** |
|
629 Reads single line text record into the specified descriptor. |
|
630 |
|
631 The read operation begins at the current file position, and ends when |
|
632 a line delimiter character is read or the caller's buffer is full or |
|
633 the file ends; |
|
634 |
|
635 If the line is longer than fits into user's buffer, of if the file does |
|
636 not end with a terminator, KErrTooBig is returned. |
|
637 The purpose is to inform the caller that a terminator should not be added |
|
638 to the line when it is written elsewhere. |
|
639 |
|
640 Next time the reading continues from the current position so that a long |
|
641 line may be read in chunks and terminator added when the end of the line |
|
642 has been reached. |
|
643 |
|
644 If Read() is called when the current position is the end of the file (that |
|
645 is, after the last line delimiter in the file), KErrEof is returned, and the |
|
646 length of the buffer is set to zero. |
|
647 |
|
648 @param aDes On return, contains the single record read from the file. Any |
|
649 previous contents are overwritten. |
|
650 |
|
651 @return KErrNone if successful, otherwise one of the other system-wide error |
|
652 codes. KErrTooBig indicates that the line does not end with a |
|
653 terminator. Buffer is too short to hold the whole line or the line |
|
654 is the last line in the file and the file does not end with a |
|
655 terminator character. |
|
656 */ |
|
657 { |
|
658 TText* pD = ( TText* )aDes.Ptr(); |
|
659 TInt len = aDes.MaxLength(); |
|
660 TInt newLen = 0; |
|
661 TInt r = KErrNone; |
|
662 TBool terminate = EFalse; |
|
663 while ( newLen < len ) |
|
664 { |
|
665 if ( iNext >= iEnd ) |
|
666 { |
|
667 r = FillBuffer(); |
|
668 if ( r != KErrNone && r != KErrEof ) |
|
669 { |
|
670 return r; |
|
671 } |
|
672 if ( r == KErrEof ) |
|
673 { |
|
674 aDes.SetLength( newLen ); |
|
675 return ( newLen ? KErrTooBig : KErrEof ); |
|
676 } |
|
677 continue; |
|
678 } |
|
679 terminate = newLen; |
|
680 r = CheckForTerminator( terminate ); |
|
681 if ( r != KErrNone || terminate) |
|
682 { |
|
683 aDes.SetLength( newLen ); |
|
684 return r; |
|
685 } |
|
686 *pD++ = ( *iNext++ ); |
|
687 newLen++; |
|
688 } |
|
689 aDes.SetLength( newLen ); |
|
690 terminate = newLen; |
|
691 r=CheckForTerminator( terminate ); |
|
692 if ( r != KErrNone || terminate ) |
|
693 { |
|
694 return r; |
|
695 } |
|
696 // don't skip the rest of the line - return the rest the next time. |
|
697 return KErrTooBig; |
|
698 } |
|
699 |
|
700 // --------------------------------------------------------- |
|
701 // |
|
702 // --------------------------------------------------------- |
|
703 // |
|
704 static void SwapWords( TText* aStart, TInt aCount ) |
|
705 { |
|
706 TUint8* p = ( TUint8* )aStart; |
|
707 while ( aCount-- > 0 ) |
|
708 { |
|
709 TUint8 temp = *p; |
|
710 *p = p[1]; |
|
711 p[1] = temp; |
|
712 p += 2; |
|
713 } |
|
714 } |
|
715 |
|
716 // --------------------------------------------------------- |
|
717 // Read the new data from the file |
|
718 // --------------------------------------------------------- |
|
719 // |
|
720 TInt TMmsFileText::FillBuffer() |
|
721 { |
|
722 TInt r = iFile.Read( iReadBuf ); |
|
723 if ( r !=KErrNone ) |
|
724 { |
|
725 return r; |
|
726 } |
|
727 if ( iReadBuf.Length() == 0 ) |
|
728 { |
|
729 return KErrEof; |
|
730 } |
|
731 iNext = ( const TText* )iReadBuf.Ptr(); |
|
732 iEnd = iNext + iReadBuf.Length() / sizeof( TText ); |
|
733 |
|
734 // Use any leading byte order marker to determine endianness. |
|
735 if ( iState == EStartOfFile ) |
|
736 { |
|
737 iState = ENormal; |
|
738 |
|
739 // Ignore an ordinary byte order marker. |
|
740 if ( *iNext == 0xFEFF ) |
|
741 { |
|
742 iNext++; |
|
743 } |
|
744 |
|
745 // Set the endianness state to 'reverse' if a reversed byte order marker is found. |
|
746 else if ( *iNext == 0xFFFE ) |
|
747 { |
|
748 iNext++; |
|
749 iState = EReverse; |
|
750 } |
|
751 |
|
752 if ( iNext == iEnd ) |
|
753 { |
|
754 return KErrEof; |
|
755 } |
|
756 } |
|
757 |
|
758 if ( iState == EReverse ) |
|
759 { |
|
760 SwapWords( ( TText* )iNext, ( iEnd - iNext ) ); |
|
761 } |
|
762 |
|
763 return KErrNone; |
|
764 } |
|
765 |
|
766 // --------------------------------------------------------- |
|
767 // Return ETrue if the next char is a record terminator: PARAGRAPH SEPARATOR (U+2029), LINE SEPARATOR (U+2028), |
|
768 // CR-LF (U+000D, U+000A), or LF (U+000A) |
|
769 // If the file ends without terminator, return KErrTooBig |
|
770 // KErrTooBig actually only means that the line does not end with a terminator |
|
771 // --------------------------------------------------------- |
|
772 // |
|
773 TInt TMmsFileText::CheckForTerminator( TBool& anAnswer ) |
|
774 { |
|
775 TInt r = KErrNone; |
|
776 if ( iNext >= iEnd ) |
|
777 { |
|
778 r = FillBuffer(); |
|
779 if ( r != KErrNone ) |
|
780 { |
|
781 if ( r == KErrEof && anAnswer ) |
|
782 { |
|
783 return KErrTooBig; // no terminator |
|
784 } |
|
785 return r; |
|
786 } |
|
787 } |
|
788 |
|
789 anAnswer = EFalse; |
|
790 const TText* oldNext = iNext; |
|
791 TInt oldBufferLength = iReadBuf.Length(); |
|
792 TText c = ( *iNext ); |
|
793 TBool peek = EFalse; |
|
794 |
|
795 // Check for unambiguous paragraph or line separator. |
|
796 if ( c == 0x2029 || c == 0x2028 ) |
|
797 { |
|
798 iNext++; |
|
799 anAnswer = ETrue; |
|
800 return KErrNone; |
|
801 } |
|
802 |
|
803 // Check for CR-LF or LF. |
|
804 if ( c == 0x000D ) |
|
805 { |
|
806 iNext++; |
|
807 if ( iNext < iEnd ) |
|
808 { |
|
809 c = ( *iNext ); |
|
810 } |
|
811 else |
|
812 { |
|
813 peek = ETrue; |
|
814 r = FillBuffer(); |
|
815 if ( r != KErrNone && r != KErrEof ) |
|
816 { |
|
817 return r; |
|
818 } |
|
819 if ( r == KErrNone ) |
|
820 { |
|
821 c = ( *iNext ); |
|
822 } |
|
823 } |
|
824 } |
|
825 |
|
826 if ( c == 0x000A ) |
|
827 { |
|
828 iNext++; |
|
829 anAnswer = ETrue; |
|
830 return KErrNone; |
|
831 } |
|
832 |
|
833 iNext = oldNext; |
|
834 if ( !peek ) |
|
835 { |
|
836 return KErrNone; |
|
837 } |
|
838 |
|
839 TInt pos = ( -1 ) * ( oldBufferLength + iReadBuf.Length() ); |
|
840 r = iFile.Seek( ESeekCurrent, pos ); |
|
841 if ( r == KErrNone ) |
|
842 { |
|
843 r = FillBuffer(); |
|
844 } |
|
845 if ( r != KErrNone ) |
|
846 { |
|
847 return r; |
|
848 } |
|
849 iNext = oldNext; |
|
850 return KErrNone; |
|
851 } |
|
852 |
|
853 // ================= OTHER EXPORTED FUNCTIONS ============== |
|
854 |
|
855 // End of File |