|
1 // Copyright (c) 1998-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 // Deal with a connection to an IMAP4rev1 server |
|
15 // This involves logging in, issuing commands and parsing responses into |
|
16 // suitable return formats as necessary, and updating the message server's |
|
17 // database. |
|
18 // |
|
19 // |
|
20 |
|
21 #include <msvstd.h> |
|
22 #include <miuthdr.h> |
|
23 #include <miut_err.h> |
|
24 #include <msventry.h> |
|
25 #include <imcvutil.h> |
|
26 |
|
27 #include <txtetext.h> |
|
28 |
|
29 #include <cmsvbodytext.h> |
|
30 #include <txtrich.h> |
|
31 #include <imcvtext.h> |
|
32 #include <imcvsend.h> |
|
33 #include <imcm.rsg> // resource definition for IMCV |
|
34 #include <barsread.h> // TResourceReader |
|
35 #include <msvapi.h> |
|
36 |
|
37 #include "impsmtm.h" |
|
38 |
|
39 #include "imapsess.h" |
|
40 #include "impspan.h" |
|
41 #include "fldindex.h" |
|
42 #include <imapset.h> |
|
43 #include <miutlog.h> |
|
44 #include <msvstore.h> |
|
45 #include <commdb.h> |
|
46 #include <commdbconnpref.h> |
|
47 #include <mmsvattachmentmanager.h> |
|
48 #include <mmsvattachmentmanagersync.h> |
|
49 #include <cemailaccounts.h> |
|
50 #ifdef __WINS__ |
|
51 #include <e32wins.h> // for maxfilename lengths |
|
52 #include <msvapi.h> |
|
53 #endif |
|
54 |
|
55 #ifdef _DEBUG |
|
56 #define LOG_COMMANDS(a) a |
|
57 #define DBG(a) a |
|
58 #define PRINTING |
|
59 #else |
|
60 #define LOG_COMMANDS(a) |
|
61 #define DBG(a) |
|
62 #undef PRINTING |
|
63 #endif |
|
64 |
|
65 #include "cimapcanceltimer.h" |
|
66 #include "mimapsessionobserver.h" |
|
67 |
|
68 // For the "UID SEARCH" command, the reply is of the form: |
|
69 // "* SEARCH 1234547890 1234567891 ......." |
|
70 // : : |
|
71 // |---9---| : |
|
72 // |---11----| |
|
73 // |
|
74 // Here the untagged headers is 9 characters and each UID part may be up to 11 characters. |
|
75 // So if we wanted to limit the number of replies recieved in a single search so that it is |
|
76 // no larger is size than a single body fetch part.... |
|
77 |
|
78 // Maximum number of UIDs acceptable in a single reply. |
|
79 const TInt KImapUidSearchSize=(5120-9)/11; |
|
80 |
|
81 // Initial size of buffer in which the "Real name <user@host>" strings |
|
82 // are built before they're put into the CImHeader and the size by |
|
83 // which to increment the buffer when necessary |
|
84 const TInt KImapAddressSizeInc=256; |
|
85 |
|
86 // Initial size of the buffer containing the UID Search list |
|
87 const TInt KUidListStringSize=256; |
|
88 |
|
89 // Illegal UID we use for marker |
|
90 const TUint KIllegalUID = 0xffffffff; |
|
91 |
|
92 // Idle time when waiting for a cancelled fetch to complete (microseconds) |
|
93 const TInt KImapFetchCancelIdleTime = 10000000; // 10 seconds |
|
94 |
|
95 // Idle time (seconds) when waiting for a DONE command to complete |
|
96 // when coming out of IMAP IDLE state. |
|
97 const TInt KImapDoneInactivityTimeSeconds = 5; |
|
98 |
|
99 // IMAP text |
|
100 _LIT8(KIMAP_UNTAGGED, "*"); |
|
101 _LIT8(KIMAP_CONTINUATION, "+"); |
|
102 _LIT8(KIMAP_BODY, "BODY"); |
|
103 _LIT8(KIMAP_BODYPEEK, "BODY.PEEK"); |
|
104 _LIT8(KIMAP_BODYSTRUCTURE, "BODYSTRUCTURE"); |
|
105 _LIT8(KIMAP_BYE, "BYE"); |
|
106 _LIT8(KIMAP_CAPABILITY, "CAPABILITY"); |
|
107 _LIT8(KIMAP_EXISTS, "EXISTS"); |
|
108 _LIT8(KIMAP_EXPUNGE, "EXPUNGE"); |
|
109 _LIT8(KIMAP_FETCH, "FETCH"); |
|
110 _LIT8(KIMAP_FLAGS, "FLAGS"); |
|
111 _LIT8(KIMAP_HEADERFIELDS, "HEADER.FIELDS"); |
|
112 |
|
113 _LIT8(KIMAP_ALERT, "ALERT"); |
|
114 _LIT8(KIMAP_LIST, "LIST"); |
|
115 _LIT8(KIMAP_LSUB, "LSUB"); |
|
116 _LIT8(KIMAP_NIL, "NIL"); |
|
117 _LIT8(KIMAP_NO, "NO"); |
|
118 _LIT8(KIMAP_PREAUTH, "PREAUTH"); |
|
119 _LIT8(KIMAP_READWRITE, "READ-WRITE"); |
|
120 _LIT8(KIMAP_READONLY, "READ-ONLY"); |
|
121 _LIT8(KIMAP_RECENT, "RECENT"); |
|
122 _LIT8(KIMAP_RFC822SIZE, "RFC822.SIZE"); |
|
123 _LIT8(KIMAP_UID, "UID"); |
|
124 _LIT8(KIMAP_UIDVALIDITY, "UIDVALIDITY"); |
|
125 _LIT8(KIMAP_UIDNEXT, "UIDNEXT"); |
|
126 _LIT8(KIMAP_MIME, "MIME"); |
|
127 _LIT8(KIMAP_SEARCH, "SEARCH"); |
|
128 |
|
129 // IMAP capabilities |
|
130 _LIT8(KIMAP_VERSION, "IMAP4rev1"); |
|
131 _LIT8(KIMAP_STARTTLS, "STARTTLS"); |
|
132 _LIT8(KIMAP_LOGINDISABLED, "LOGINDISABLED"); |
|
133 _LIT8(KIMAP_IDLE, "IDLE"); |
|
134 |
|
135 // IMAP commands (trailing spaces, except for those which take no params) |
|
136 _LIT8(KIMAPC_CLOSE, "CLOSE"); |
|
137 _LIT8(KIMAPC_LOGOUT, "LOGOUT"); |
|
138 _LIT8(KIMAPC_SUBSCRIBE, "SUBSCRIBE "); |
|
139 _LIT8(KIMAPC_UNSUBSCRIBE, "UNSUBSCRIBE "); |
|
140 _LIT8(KIMAPC_IDLE, "IDLE"); |
|
141 _LIT8(KIMAPC_DONE, "DONE"); |
|
142 |
|
143 // IMAP flags |
|
144 _LIT8(KIMAPFLAG_NOSELECT, "\\Noselect"); |
|
145 _LIT8(KIMAPFLAG_NOINFERIORS, "\\Noinferiors"); |
|
146 _LIT8(KIMAPFLAG_ANSWERED, "\\Answered"); |
|
147 _LIT8(KIMAPFLAG_DELETED, "\\Deleted"); |
|
148 _LIT8(KIMAPFLAG_DRAFT, "\\Draft"); |
|
149 _LIT8(KIMAPFLAG_FLAGGED, "\\Flagged"); |
|
150 _LIT8(KIMAPFLAG_RECENT, "\\Recent"); |
|
151 _LIT8(KIMAPFLAG_SEEN, "\\Seen"); |
|
152 _LIT8(KIMAPFLAG_UNREAD, "\\Unread"); |
|
153 |
|
154 // MIME message types |
|
155 _LIT8(KMIME_MESSAGE, "MESSAGE"); |
|
156 _LIT8(KMIME_RFC822, "RFC822"); |
|
157 _LIT8(KMIME_TEXT, "TEXT"); |
|
158 _LIT8(KMIME_HTML, "HTML"); |
|
159 _LIT8(KMIME_XVCARD, "X-VCARD"); |
|
160 _LIT8(KMIME_VCALENDAR, "X-VCALENDAR"); |
|
161 _LIT8(KMIME_ICALENDAR, "CALENDAR"); |
|
162 _LIT8(KMIME_NAME, "NAME"); |
|
163 _LIT8(KMIME_NAME_RFC2231, "NAME*"); |
|
164 _LIT8(KMIME_FILENAME, "FILENAME"); |
|
165 _LIT8(KMIME_FILENAME_RFC2231, "FILENAME*"); |
|
166 _LIT8(KMIME_ATTACHMENT, "ATTACHMENT"); |
|
167 _LIT8(KMIME_DELIVERY_STATUS, "DELIVERY-STATUS"); |
|
168 _LIT8(KMIME_ALTERNATIVE, "ALTERNATIVE"); |
|
169 _LIT8(KMIME_RELATED, "RELATED"); |
|
170 _LIT8(KMIME_IMAGE, "IMAGE"); |
|
171 _LIT8(KMIME_AUDIO, "AUDIO"); |
|
172 _LIT8(KMIME_VIDEO, "VIDEO"); |
|
173 _LIT8(KMIME_APPLICATION, "APPLICATION"); |
|
174 |
|
175 // Encoding types |
|
176 _LIT8(KMIME_BASE64, "BASE64"); |
|
177 |
|
178 // Multipart types |
|
179 _LIT8(KMIME_MIXED, "MIXED"); |
|
180 |
|
181 // for first stage download (to view in folder list) |
|
182 _LIT8(KImapFetchSmallHeaderToEnd, "%d UID FETCH %d:* (UID FLAGS BODYSTRUCTURE BODY.PEEK[HEADER.FIELDS (Received Date Subject From %S)])\r\n"); |
|
183 _LIT8(KImapFetchSmallHeaderRange,"%d UID FETCH %d:%d (UID FLAGS BODYSTRUCTURE BODY.PEEK[HEADER.FIELDS (Received Date Subject From %S)])\r\n"); |
|
184 _LIT8(KImapFetchSmallHeaderRangeRefined,"%d UID FETCH %S (UID FLAGS BODYSTRUCTURE BODY.PEEK[HEADER.FIELDS (Received Date Subject From %S)])\r\n"); |
|
185 |
|
186 // for second stage download (to view when downloading whole email) |
|
187 _LIT8(KImapFetchLargeHeader, "%d UID FETCH %d (UID FLAGS BODYSTRUCTURE BODY.PEEK[HEADER.FIELDS (Received Date Subject From Reply-to To Cc Bcc Message-ID %S)])\r\n"); |
|
188 _LIT8(KImapFetchLargeHeaderRange, "%d UID FETCH %d:* (UID FLAGS BODYSTRUCTURE BODY.PEEK[HEADER.FIELDS (Received Date Subject From Reply-to To Cc Bcc Message-ID %S)])\r\n"); |
|
189 |
|
190 // Use %S so we can specify a refined FETCH based on a previous refined SEARCH |
|
191 _LIT8(KImapFetchLargeHeaderRangeRefined, "%d UID FETCH %S (UID FLAGS BODYSTRUCTURE BODY.PEEK[HEADER.FIELDS (Received Date Subject From Reply-to To Cc Bcc Message-ID %S)])\r\n"); |
|
192 // Constants for fetching body |
|
193 _LIT8(KImapFetchBodyPeek, "%d UID FETCH %d (BODY.PEEK[%S]<%d.%d>)\r\n"); |
|
194 _LIT8(KImapFetchBody, "%d UID FETCH %d (BODY[%S]<%d.%d>)\r\n"); |
|
195 _LIT8(KImapFetchMimeBodyPeek, "%d UID FETCH %u (BODY.PEEK[%S]<0.%d> BODY.PEEK[%S.MIME])\r\n"); |
|
196 _LIT8(KImapFetchMimeBody, "%d UID FETCH %u (BODY[%S]<0.%d> BODY[%S.MIME])\r\n"); |
|
197 |
|
198 // Constants for sending commands |
|
199 _LIT8(KImapCommand, "%S\r\n"); |
|
200 |
|
201 // The maximum number of octets to be uuencoded on each line is 45 |
|
202 const TInt KUuDecodedLineLength = 45; |
|
203 |
|
204 const TInt KBodyTextChunkSizeBytes = 512; |
|
205 const TInt KKiloByteSize = 1024; |
|
206 // UIDs are 32bit integers, which takes maximum 10 decimal digits. |
|
207 // KMaxUint32Chars is used to check whether the string containg the UID |
|
208 // list has allocated enough memory when creating it. |
|
209 const TInt KMaxUint32Chars = 10; |
|
210 |
|
211 // Processing to remove illegal characters from a filename |
|
212 LOCAL_C void StripIllegalCharactersFromFileName(TDes16& aName) |
|
213 { |
|
214 TInt length=aName.Length(); |
|
215 for(TInt index=0; index < length; index++) |
|
216 { |
|
217 //parse extracted filename and replace any illegal chars with a default |
|
218 TUint charr=(TUint)aName[index]; |
|
219 if( charr == '*' || charr == '\\' || charr == '<' || charr == '>' || |
|
220 charr == ':' || charr == '"' || charr == '/' || charr == '|' || |
|
221 charr == '?' || charr < ' ') |
|
222 { |
|
223 aName[index] = KImcvDefaultChar; |
|
224 } |
|
225 } |
|
226 } |
|
227 |
|
228 |
|
229 // We don't do the async notifications yet: they make everything a bit more wobbly |
|
230 // SJM actually I guess we do do them now! |
|
231 #define ASYNC_NOTIFICATIONS |
|
232 |
|
233 // do we automatically set the iRelatedId of each object to itself? No |
|
234 // for now as it confuses the offline handling code which assumes it |
|
235 // is a shadow entry if iRelatedId is set. |
|
236 #define SET_RELATED_ID 0 |
|
237 |
|
238 // This is very nasty but necessary as the flag returning functions |
|
239 // return 0 or not-zero not 0 or 1 |
|
240 |
|
241 #define FIXBOOL(a) (a?ETrue:EFalse) |
|
242 |
|
243 // Directory structure |
|
244 CImImap4DirStruct::CImImap4DirStruct() |
|
245 { |
|
246 } |
|
247 |
|
248 CImImap4DirStruct::~CImImap4DirStruct() |
|
249 { |
|
250 // Get rid of leaf |
|
251 delete iLeafname; |
|
252 } |
|
253 |
|
254 void CImImap4DirStruct::SetLeafnameL(const TDesC& aName) |
|
255 { |
|
256 // Make buffer, set it |
|
257 iLeafname=HBufC::NewL(aName.Length()); |
|
258 *iLeafname=aName; |
|
259 } |
|
260 |
|
261 TPtrC CImImap4DirStruct::Leafname() |
|
262 { |
|
263 // Return it |
|
264 return(*iLeafname); |
|
265 } |
|
266 |
|
267 CImImap4Session::CImImap4Session(MImapSessionObserver& aObserver) // construct high-priority active object |
|
268 : CMsgActive(1), iState(EImapStateDisconnected), iCancelledTag(-1), |
|
269 iSecurityState(EUnknown), iCharset(KCharacterSetIdentifierUtf8), |
|
270 iObserver(aObserver),iPrimarySession(NULL) |
|
271 { |
|
272 __DECLARE_NAME(_S("CImImap4Session")); |
|
273 } |
|
274 |
|
275 CImImap4Session* CImImap4Session::NewLC(TInt aId, MImapSessionObserver& aObserver) |
|
276 |
|
277 { |
|
278 CImImap4Session* self=new (ELeave) CImImap4Session(aObserver); |
|
279 |
|
280 CleanupStack::PushL(self); |
|
281 self->ConstructL(aId); |
|
282 return self; |
|
283 } |
|
284 |
|
285 CImImap4Session* CImImap4Session::NewL(TInt aId, MImapSessionObserver& aObserver) |
|
286 { |
|
287 CImImap4Session* self=NewLC(aId, aObserver); |
|
288 CleanupStack::Pop(); |
|
289 return self; |
|
290 } |
|
291 |
|
292 void CImImap4Session::ConstructL(TInt aId) |
|
293 { |
|
294 // Add to active scheduler |
|
295 CActiveScheduler::Add(this); |
|
296 |
|
297 // Get FS |
|
298 User::LeaveIfError(iFs.Connect()); |
|
299 |
|
300 // Get somewhere to store service settings |
|
301 iServiceSettings=new (ELeave) CImImap4Settings; |
|
302 |
|
303 // Get an IO processor |
|
304 iImapIO=CImapIO::NewL(aId); |
|
305 |
|
306 // Message selection |
|
307 iSelection=new (ELeave) CMsvEntrySelection; |
|
308 |
|
309 // List of messages to delete on folder close |
|
310 iDeletedUids=new (ELeave) CArrayFixFlat<TMsvId>(8); |
|
311 |
|
312 // List of messages to fetch in single fetch operation |
|
313 iFetchList=new (ELeave) CArrayFixFlat<TMsvId>(8); |
|
314 |
|
315 // List of messages who's Seen status has changed |
|
316 iSetSeenList=new (ELeave) CArrayFixFlat<TMsvId>(8); |
|
317 iClearSeenList=new (ELeave) CArrayFixFlat<TMsvId>(8); |
|
318 |
|
319 RResourceFile resFile; |
|
320 OpenResourceFileL(resFile,iFs); // NB leaves if file not found |
|
321 |
|
322 // make sure the resource file will be closed if anything goes wrong |
|
323 // CloseResourceFile is declared in IMCVDLL.H and defined in IMCVDLL.CPP |
|
324 TCleanupItem close(CloseResourceFile,&resFile); |
|
325 CleanupStack::PushL(close); |
|
326 |
|
327 // Read iStore8BitData flag. |
|
328 HBufC8* buf = resFile.AllocReadLC( STORE_8BIT_BODY_TEXT ); |
|
329 TResourceReader reader; |
|
330 reader.SetBuffer(buf); |
|
331 iStore8BitData = reader.ReadInt8(); |
|
332 CleanupStack::PopAndDestroy(buf); |
|
333 |
|
334 buf=resFile.AllocReadLC(DEFAULT_ATTACHMENT_NAME); |
|
335 reader.SetBuffer(buf); |
|
336 iDefaultAttachmentName=reader.ReadTPtrC().AllocL(); |
|
337 CleanupStack::PopAndDestroy(2, &resFile); // buf, resFile (Close resfile) |
|
338 |
|
339 if (!iStore8BitData) |
|
340 { |
|
341 // CRichText bits |
|
342 iParaLayer=CParaFormatLayer::NewL(); |
|
343 iCharLayer=CCharFormatLayer::NewL(); |
|
344 } |
|
345 |
|
346 // Create converter objects |
|
347 iCharacterConverter=CCnvCharacterSetConverter::NewL(); |
|
348 iCharConv=CImConvertCharconv::NewL(*iCharacterConverter, iFs); |
|
349 iHeaderConverter=CImConvertHeader::NewL(*iCharConv); |
|
350 |
|
351 // we assume that this message is MIME as we have no way of |
|
352 // detecting otherwise (without sending a new FETCH to the |
|
353 // server to get the MIME-Version). |
|
354 iHeaderConverter->SetMessageType(ETrue); |
|
355 |
|
356 // Get timer to defeat IMSK timeout |
|
357 iDummyRead = CImImap4SessionDummyRead::NewL(*this, *iImapIO, Priority()); |
|
358 |
|
359 iIdleRead = CImImap4SessionIdleRead::NewL(*this, Priority()); |
|
360 |
|
361 // List of message UIDs in a folder |
|
362 iSearchList=new (ELeave) CArrayFixFlat<TUint32>(8); // For granularity of 8, 32 bytes are allocated each time. |
|
363 iSyncLimit = KImImapSynchroniseAll; |
|
364 // set up progress types |
|
365 iProgress.iType=EImap4GenericProgressType; |
|
366 iCurrentDrive = MessageServer::CurrentDriveL(iFs); |
|
367 |
|
368 iIdleTimerExpired = EFalse; |
|
369 |
|
370 iIdleTimer = CIdleTimeoutTimer::NewL(*this); |
|
371 iReissueIdle = EFalse; |
|
372 iDisconnectAfterIdleStopped = EFalse; |
|
373 iFetchPartialMail=EFalse; |
|
374 iCaf = new (ELeave) CImCaf(iFs); |
|
375 |
|
376 iIsICalendar = EFalse; |
|
377 iIsVCalendar = EFalse; |
|
378 |
|
379 iUidString = HBufC8::NewL(KUidListStringSize); |
|
380 |
|
381 iCancelTimer = CImapCancelTimer::NewL(*this); |
|
382 } |
|
383 |
|
384 CImImap4Session::~CImImap4Session() |
|
385 { |
|
386 Cancel(); // make sure we're cancelled |
|
387 |
|
388 delete iDummyRead; |
|
389 delete iIdleRead; |
|
390 |
|
391 // No settings |
|
392 delete iServiceSettings; |
|
393 delete iPrefs; |
|
394 |
|
395 // Get rid of connection |
|
396 delete iImapIO; |
|
397 |
|
398 // Get rid of message body bits |
|
399 delete iBodyBuf; |
|
400 delete iMessageBody; |
|
401 delete iBodyText; |
|
402 delete iParaLayer; |
|
403 delete iCharLayer; |
|
404 |
|
405 // Message selection |
|
406 delete iSelection; |
|
407 |
|
408 // List of messages to delete |
|
409 delete iDeletedUids; |
|
410 |
|
411 // List of messages to fetch |
|
412 delete iFetchList; |
|
413 |
|
414 // List of seen flags to set/clear |
|
415 delete iSetSeenList; |
|
416 delete iClearSeenList; |
|
417 |
|
418 // Partial line: used when doing Q-P decoding, as it works on a line at a time. |
|
419 delete iPartialLine; |
|
420 |
|
421 // Any attachment info (ie: there was a fetch in progress) |
|
422 delete iAttachmentFile; |
|
423 delete iAttachmentFullPath; |
|
424 delete iAttachmentMimeInfo; |
|
425 delete iDefaultAttachmentName; |
|
426 delete iFooterString; |
|
427 |
|
428 // Any message sizer left over (ie: we've been deleted in the middle of |
|
429 // an append operation) |
|
430 delete iMessageSizer; |
|
431 delete iMessageSender; |
|
432 delete iLineBuffer; |
|
433 |
|
434 // Characterset conversion |
|
435 delete iHeaderConverter; |
|
436 delete iCharConv; |
|
437 delete iCharacterConverter; |
|
438 |
|
439 // CMsvServerEntry used for moves |
|
440 delete iMoveEntry; |
|
441 |
|
442 //cached TMsvEntry data |
|
443 delete iCachedEntryData; |
|
444 |
|
445 // List of message UIDs in a folder |
|
446 delete iSearchList; |
|
447 |
|
448 // Selection passed during synchronisation |
|
449 delete iSynchronisationSelection; |
|
450 |
|
451 delete iIdleTimer; |
|
452 delete iCaf; |
|
453 delete iUidString; |
|
454 iFs.Close(); |
|
455 |
|
456 delete iCancelTimer; |
|
457 |
|
458 //Delete Username and Password |
|
459 delete iUsername; |
|
460 delete iPassword; |
|
461 |
|
462 // Note: iList is owned by a caller, not us - we don't delete it |
|
463 } |
|
464 |
|
465 // Logging calls: passed through to ImapIO |
|
466 void CImImap4Session::LogText(const TDesC8& aString) |
|
467 { |
|
468 // Log the text |
|
469 iImapIO->LogText(aString); |
|
470 } |
|
471 |
|
472 void CImImap4Session::LogText(TRefByValue<const TDesC8> aFmt,...) |
|
473 { |
|
474 VA_LIST list; |
|
475 VA_START(list,aFmt); |
|
476 TBuf8<1024> aBuf; |
|
477 //handles the data over flow panics. returns immediately without performing any action. |
|
478 TDes8OverflowHandler overFlowHandler; |
|
479 |
|
480 aBuf.AppendFormatList(aFmt,list, &overFlowHandler); |
|
481 LogText(aBuf); |
|
482 } |
|
483 |
|
484 // Do setentry, leave if there is an error |
|
485 void CImImap4Session::SetEntryL(const TMsvId aId) |
|
486 { |
|
487 #ifdef PRINTING |
|
488 TInt error=iEntry->SetEntry(aId); |
|
489 if (error) |
|
490 LogText(_L8("SetEntryL(%x) returned %d"),aId,error); |
|
491 User::LeaveIfError(error); |
|
492 #else |
|
493 User::LeaveIfError(iEntry->SetEntry(aId)); |
|
494 #endif |
|
495 } |
|
496 |
|
497 // Change entry, leave if error |
|
498 void CImImap4Session::ChangeEntryL(const TMsvEntry& aEntry) |
|
499 { |
|
500 #ifdef PRINTING |
|
501 TInt error=iEntry->ChangeEntry(aEntry); |
|
502 if (error) |
|
503 LogText(_L8("ChangeEntryL(%x) returned %d"),aEntry.Id(),error); |
|
504 User::LeaveIfError(error); |
|
505 #else |
|
506 User::LeaveIfError(iEntry->ChangeEntry(aEntry)); |
|
507 #endif |
|
508 } |
|
509 |
|
510 // Change entry in bulk mode (i.e. no index file commit), leave if error |
|
511 void CImImap4Session::ChangeEntryBulkL(const TMsvEntry& aEntry) |
|
512 { |
|
513 #ifdef PRINTING |
|
514 TInt error=iEntry->ChangeEntryBulk(aEntry); |
|
515 if (error) |
|
516 LogText(_L8("ChangeEntryL(%x) returned %d"),aEntry.Id(),error); |
|
517 User::LeaveIfError(error); |
|
518 #else |
|
519 User::LeaveIfError(iEntry->ChangeEntryBulk(aEntry)); |
|
520 #endif |
|
521 } |
|
522 // Get children, leave if error |
|
523 void CImImap4Session::GetChildrenL(CMsvEntrySelection& aSelection) |
|
524 { |
|
525 #ifdef PRINTING |
|
526 TInt error=iEntry->GetChildren(aSelection); |
|
527 if (error) |
|
528 LogText(_L8("GetChildrenL() returned %d"),error); |
|
529 User::LeaveIfError(error); |
|
530 #else |
|
531 User::LeaveIfError(iEntry->GetChildren(aSelection)); |
|
532 #endif |
|
533 } |
|
534 |
|
535 // This can be called after Select() has completed to find out if the |
|
536 // folder has changed in any way since the last sync |
|
537 TBool CImImap4Session::FolderChanged() const |
|
538 { |
|
539 return iMailboxReceivedExists || |
|
540 iMailboxReceivedExpunge || |
|
541 iMailboxReceivedFlags; |
|
542 } |
|
543 |
|
544 TBool CImImap4Session::ImapIdleSupported() const |
|
545 { |
|
546 return iUseIdleCommand && iCapabilityIdleSupport; |
|
547 } |
|
548 |
|
549 TBool CImImap4Session::IsIdling() const |
|
550 { |
|
551 return(iState==EImapStateIdling); |
|
552 } |
|
553 |
|
554 // Transfers the current selection into the iFolderIndex, and sorts it by |
|
555 // UID. |
|
556 void CImImap4Session::MakeSortedFolderIndexL(TBool aUseCachedEntryData) |
|
557 { |
|
558 |
|
559 TInt noofchildren=iSelection->Count(); |
|
560 |
|
561 // Reset folder index |
|
562 iFolderIndex.SetSizeL(noofchildren); |
|
563 TInt a=0; |
|
564 |
|
565 if(!aUseCachedEntryData) |
|
566 { //can't rely on iCachedEntryData |
|
567 TMsvEntry* entryPtr; |
|
568 TMsvId id; |
|
569 for(a=0;a<noofchildren;a++) |
|
570 { |
|
571 // Save UID/TMsvId of this entry |
|
572 id=(*iSelection)[a]; |
|
573 User::LeaveIfError(iEntry->GetEntryFromId(id,entryPtr)); |
|
574 iFolderIndex[a].iUid=((TMsvEmailEntry)(*entryPtr)).UID(); |
|
575 iFolderIndex[a].iMsvId=id; |
|
576 } |
|
577 } |
|
578 else |
|
579 { |
|
580 for(a=0;a<noofchildren;a++) |
|
581 { |
|
582 // Save UID/TMsvId of this entry |
|
583 iFolderIndex[a].iUid=(*iCachedEntryData)[a].iUid; |
|
584 iFolderIndex[a].iMsvId=(*iSelection)[a]; |
|
585 } |
|
586 } |
|
587 |
|
588 // Sort it by UID |
|
589 iFolderIndex.Sort(); |
|
590 |
|
591 // Check for any duplicate UIDs (ie, a dud netscape server) |
|
592 TMsvEntry* entryPtr; |
|
593 TMsvEntry* nextEntryPtr; |
|
594 for(a=1;a<noofchildren;a++) |
|
595 { |
|
596 if(iFolderIndex[a].iUid!=0 && iFolderIndex[a].iUid==iFolderIndex[a-1].iUid) |
|
597 { |
|
598 if(!aUseCachedEntryData) |
|
599 { |
|
600 // get the TMsvEntry for the message/folder |
|
601 User::LeaveIfError(iEntry->GetEntryFromId(iFolderIndex[a].iMsvId,entryPtr)); |
|
602 User::LeaveIfError(iEntry->GetEntryFromId(iFolderIndex[a-1].iMsvId,nextEntryPtr)); |
|
603 // check if type of TMsvEntry and type of next TMsvEntry are both Messages |
|
604 if( entryPtr->iType.iUid == nextEntryPtr->iType.iUid && entryPtr->iType.iUid == KUidMsvMessageEntryValue) |
|
605 { |
|
606 User::Leave(KErrCorrupt); |
|
607 } |
|
608 } |
|
609 else |
|
610 { |
|
611 User::Leave(KErrCorrupt); |
|
612 } |
|
613 } |
|
614 |
|
615 } |
|
616 |
|
617 #ifdef PRINTING |
|
618 LogText(_L8("MakeSortedFolderIndex done: index list follows. children=%d"),noofchildren); |
|
619 for(a=0;a<noofchildren;a++) |
|
620 LogText(_L8(" MsvId=%8x UID=%d"),iFolderIndex[a].iMsvId,iFolderIndex[a].iUid); |
|
621 #endif |
|
622 } |
|
623 |
|
624 HBufC* CImImap4Session::DoUnModUTF7LC(TDesC8& aBuffer) |
|
625 { |
|
626 iCharConv->PrepareToConvertToFromOurCharsetL(KCharacterSetIdentifierImapUtf7); |
|
627 |
|
628 // unicode version won't be longer than the original (in chars) |
|
629 HBufC* text=HBufC::NewL(aBuffer.Length()); |
|
630 CleanupStack::PushL(text); |
|
631 |
|
632 //LogText(_L8("DoUnModUTF7LC: from %S"),&aBuffer); |
|
633 |
|
634 TInt numUC, indexUC; |
|
635 TPtr des = text->Des(); |
|
636 iCharConv->ConvertToOurCharsetL(aBuffer, des, numUC, indexUC); |
|
637 |
|
638 //LogText(_L8("DoUnModUTF7LC: to %S len %d numUC %d"),&des, des.Length(), numUC); |
|
639 |
|
640 return text; |
|
641 } |
|
642 |
|
643 // Enquote a string (being sent as a string literal) if required |
|
644 void CImImap4Session::DoQuoteL(HBufC8*& aBuffer) |
|
645 { |
|
646 // Null string? Nothing to do |
|
647 if (!aBuffer->Length() || !aBuffer->Des().Length()) return; |
|
648 |
|
649 // Anything needing quoting in there? |
|
650 if (aBuffer->Des().Locate('\\')==KErrNotFound && |
|
651 aBuffer->Des().Locate('\"')==KErrNotFound) return; |
|
652 |
|
653 // Run through string, inserting quote characters as needed |
|
654 for(TInt a=0;a<aBuffer->Des().Length();a++) |
|
655 { |
|
656 if (aBuffer->Des()[a]=='\\' || aBuffer->Des()[a]=='\"') |
|
657 { |
|
658 HBufC8 *newbuf=aBuffer->ReAllocL(aBuffer->Des().Length()+1); |
|
659 |
|
660 // Been moved due to realloc? |
|
661 if (newbuf!=aBuffer) |
|
662 { |
|
663 // In all cases when DoQuoteL() is called, the buffer is on the top of |
|
664 // the cleanup stack: change this to indicate the correct entry |
|
665 CleanupStack::Pop(); |
|
666 CleanupStack::PushL(aBuffer=newbuf); |
|
667 } |
|
668 |
|
669 aBuffer->Des().Insert(a,_L8("\\")); |
|
670 a++; |
|
671 } |
|
672 } |
|
673 } |
|
674 |
|
675 TInt CImImap4Session::FindFilename(const CImMimeHeader& aMimeInfo, TPtrC8& aFilename) |
|
676 { |
|
677 // Look in content-type list |
|
678 const CDesC8Array& ctype=aMimeInfo.ContentTypeParams(); |
|
679 |
|
680 DBG((LogText(_L8("FindFilename: Checking %d entries in content-type list"), |
|
681 ctype.Count()))); |
|
682 |
|
683 TInt tuple=0; |
|
684 while(tuple<ctype.Count()) |
|
685 { |
|
686 #ifdef PRINTING |
|
687 TPtrC8 t1=ctype[tuple],t2=ctype[tuple+1]; |
|
688 LogText(_L8(" %S %S"),&t1,&t2); |
|
689 #endif |
|
690 // Look for "name xxx" |
|
691 if (ctype[tuple].CompareF(KMIME_NAME)==0) |
|
692 { |
|
693 // Got it: report that we found it |
|
694 aFilename.Set(ctype[tuple+1]); |
|
695 TBuf8<KMaxFileName>buf(aFilename); |
|
696 buf.Trim(); |
|
697 if(buf.Length()==0) |
|
698 { |
|
699 return(KErrNotFound); |
|
700 } |
|
701 |
|
702 return(KErrNone); |
|
703 } |
|
704 else if (ctype[tuple].CompareF(KMIME_NAME_RFC2231)==0) |
|
705 { |
|
706 // Got it: report that we found it |
|
707 aFilename.Set(ctype[tuple+1]); |
|
708 return(KErrRFC2231Encoded); |
|
709 } |
|
710 tuple+=2; |
|
711 } |
|
712 |
|
713 // Not found in the content type, try content disposition |
|
714 tuple=0; |
|
715 const CDesC8Array& cdisp=aMimeInfo.ContentDispositionParams(); |
|
716 while(tuple<cdisp.Count()) |
|
717 { |
|
718 #ifdef PRINTING |
|
719 TPtrC8 t1=cdisp[tuple],t2=cdisp[tuple+1]; |
|
720 LogText(_L8(" %S %S"),&t1,&t2); |
|
721 #endif |
|
722 // Look for "filename xxx" |
|
723 if (cdisp[tuple].CompareF(KMIME_FILENAME)==0) |
|
724 { |
|
725 // Got it: report that we found it |
|
726 aFilename.Set(cdisp[tuple+1]); |
|
727 return(KErrNone); |
|
728 } |
|
729 else if (cdisp[tuple].CompareF(KMIME_FILENAME_RFC2231)==0) |
|
730 { |
|
731 // Got it: report that we found it |
|
732 aFilename.Set(cdisp[tuple+1]); |
|
733 return(KErrRFC2231Encoded); |
|
734 } |
|
735 |
|
736 tuple+=2; |
|
737 } |
|
738 |
|
739 // Didn't find it |
|
740 return(KErrNotFound); |
|
741 } |
|
742 |
|
743 void CImImap4Session::FindFilenameDecodeL(const CImMimeHeader& aMimeInfo, TFileName& aFileName) |
|
744 { |
|
745 // Make an attachment name |
|
746 aFileName.Zero(); |
|
747 |
|
748 TPtrC8 origFileName; |
|
749 |
|
750 // Look for filename in Content-Type list |
|
751 TInt err = FindFilename(aMimeInfo, origFileName); |
|
752 if (KErrNotFound == err) |
|
753 { |
|
754 // Fall back to simple "attachment" (language specific) |
|
755 aFileName=iDefaultAttachmentName->Des(); |
|
756 } |
|
757 else if (KErrRFC2231Encoded == err) |
|
758 { |
|
759 // A file name has been found but it is encoded (RFC2231) |
|
760 // Use the default file name but append the file extension so that its type can be recognised |
|
761 aFileName=iDefaultAttachmentName->Des(); |
|
762 TInt dotPos = origFileName.Length() - 1; |
|
763 TBool dotFound = EFalse; |
|
764 |
|
765 // Find the extension |
|
766 while ((dotPos != 0) && (!dotFound)) |
|
767 { |
|
768 if (origFileName[dotPos] == '.') |
|
769 { |
|
770 dotFound = ETrue; |
|
771 // Extension found: append it to the filename |
|
772 TInt extensionLength = origFileName.Length() - dotPos; |
|
773 if ((aFileName.Length() + extensionLength) <= aFileName.MaxLength()) |
|
774 { |
|
775 HBufC* extension = HBufC::NewLC(extensionLength); |
|
776 extension->Des().Copy(origFileName.Right(extensionLength)); |
|
777 aFileName.Append(*extension); |
|
778 CleanupStack::PopAndDestroy(extension); |
|
779 } |
|
780 } |
|
781 |
|
782 --dotPos; |
|
783 } |
|
784 } |
|
785 else |
|
786 { |
|
787 // Run it through the QP decoder |
|
788 HBufC *decoded=HBufC::NewLC(origFileName.Length()); |
|
789 TPtr decoded_ptr(decoded->Des()); |
|
790 |
|
791 // Decode filename from the header |
|
792 iHeaderConverter->DecodeHeaderFieldL(origFileName, decoded_ptr); |
|
793 |
|
794 DBG((LogText(_L8("FindFilenameDecode: '%S' to '%S' "),&origFileName,&decoded_ptr))); |
|
795 |
|
796 // Need to do a check on the filename length here. |
|
797 // If it is too long, set to the max possible, keeping extension. |
|
798 TFileName path; |
|
799 |
|
800 TInt fileNameLength = path.Length() + decoded_ptr.Length(); |
|
801 |
|
802 if( fileNameLength > KMaxFileName) |
|
803 { |
|
804 #ifdef __WINS__ |
|
805 TFileName winsFileName; |
|
806 TFileName mailStoreDrive; |
|
807 TDriveUnit drive(MessageServer::CurrentDriveL(iFs)); |
|
808 mailStoreDrive.Append(drive.Name()); |
|
809 mailStoreDrive.Append(KPathDelimiter); |
|
810 MapEmulatedFileName(winsFileName, mailStoreDrive); |
|
811 TInt prefixLen = winsFileName.Length(); |
|
812 #else |
|
813 TInt prefixLen = 0; |
|
814 #endif |
|
815 // Crop the Old File Name |
|
816 TInt lengthToCrop = (fileNameLength - KMaxFileName) + prefixLen; |
|
817 // Use LocateReverse rather than TParsePtr as decoded_ptr may be > 256 chars |
|
818 TInt dot = decoded_ptr.LocateReverse( '.' ); |
|
819 TPtrC extension = decoded_ptr.Mid(dot != KErrNotFound ? dot : decoded_ptr.Length()); |
|
820 TInt newFileNameLength = decoded_ptr.Length() - extension.Length() - lengthToCrop; |
|
821 TPtrC newFileName=decoded_ptr.Left(newFileNameLength); |
|
822 |
|
823 // Create the New File Name (ie File Name & Extension) |
|
824 aFileName.Zero(); |
|
825 aFileName.Append(newFileName); |
|
826 aFileName.Append(extension); |
|
827 } |
|
828 else |
|
829 { |
|
830 aFileName.Copy(decoded_ptr); |
|
831 } |
|
832 CleanupStack::PopAndDestroy(); // decoded |
|
833 } |
|
834 } |
|
835 |
|
836 // Update iDate in iMailboxId to show the time now (last sync time) |
|
837 void CImImap4Session::SyncCompleteL() |
|
838 { |
|
839 DBG((LogText(_L8("CImImap4Session::SyncCompleteL()")))); |
|
840 // Find entry |
|
841 SetEntryL(iMailboxId); |
|
842 TMsvEmailEntry message=iEntry->Entry(); |
|
843 |
|
844 // Find 'now' |
|
845 TTime now; |
|
846 now.UniversalTime(); |
|
847 message.iDate=now; |
|
848 |
|
849 // Check to see if there has been a change in the number of messages in the remote folder. |
|
850 TBool folderSizeChanged=(message.RemoteFolderEntries()!=iMailboxSize); |
|
851 |
|
852 // Set 'unread' flag on folder if there are any unread messages within it |
|
853 if (FIXBOOL(message.Unread())!=iSomeUnread || !message.Visible() || folderSizeChanged) |
|
854 { |
|
855 // Update flags |
|
856 message.SetUnread(iSomeUnread); |
|
857 message.SetVisible(ETrue); |
|
858 message.SetRemoteFolderEntries(iMailboxSize); |
|
859 ChangeEntryBulkL(message); |
|
860 } |
|
861 |
|
862 // we need to ensure the hierarchy of folders containing this one |
|
863 // is now visible. Note previously this incorrectly only did this |
|
864 // when we were not in DisconncetedUserMode |
|
865 do |
|
866 { |
|
867 // Move up one |
|
868 SetEntryL(message.Parent()); |
|
869 message=iEntry->Entry(); |
|
870 |
|
871 // Ensure visibility |
|
872 if (!message.Visible()) |
|
873 { |
|
874 message.SetVisible(ETrue); |
|
875 ChangeEntryL(message); |
|
876 } |
|
877 } |
|
878 while(message.iType!=KUidMsvServiceEntry); |
|
879 |
|
880 // Before we got back to the idle state, we need to commit any |
|
881 // outstanding entries to the index file to complete the bulk |
|
882 // synchronization operation |
|
883 iEntry->CompleteBulk(); |
|
884 |
|
885 if (iReissueIdle) |
|
886 { |
|
887 iState=EImapStateSelected; |
|
888 DoStartIdleL(); |
|
889 } |
|
890 } |
|
891 |
|
892 // Reset subscription flags for all children, and recurse into folders |
|
893 void CImImap4Session::ResetSubscriptionFlagsL(const TMsvId aFolder) |
|
894 { |
|
895 // Do this one |
|
896 SetEntryL(aFolder); |
|
897 TMsvEmailEntry entry=iEntry->Entry(); |
|
898 |
|
899 // A folder or service? If not, return |
|
900 if (entry.iType!=KUidMsvServiceEntry && |
|
901 entry.iType!=KUidMsvFolderEntry) |
|
902 return; |
|
903 |
|
904 // Reset flag if needed |
|
905 if (entry.Subscribed()) |
|
906 { |
|
907 // Reset flag and save |
|
908 entry.SetSubscribed(EFalse); |
|
909 ChangeEntryL(entry); |
|
910 } |
|
911 |
|
912 // Any children? |
|
913 CMsvEntrySelection *children=new (ELeave) CMsvEntrySelection; |
|
914 CleanupStack::PushL(children); |
|
915 GetChildrenL(*children); |
|
916 if (children->Count()) |
|
917 { |
|
918 // Do each in turn |
|
919 for(TInt child=0;child<children->Count();child++) |
|
920 ResetSubscriptionFlagsL((*children)[child]); |
|
921 } |
|
922 CleanupStack::PopAndDestroy(); |
|
923 } |
|
924 |
|
925 TBool CImImap4Session::IsCancelling() const |
|
926 { |
|
927 return iCancelAndIdle; |
|
928 } |
|
929 |
|
930 void CImImap4Session::CancelAndIdleL(TBool aReissueIdle) |
|
931 { |
|
932 // Flag that a cancel and idle command has been requested. |
|
933 iCancelAndIdle = ETrue; |
|
934 |
|
935 switch( iState ) |
|
936 { |
|
937 case EImapStateFetchWait: |
|
938 { |
|
939 // Stop requesting fetches and wait for the current fetches to be completed. |
|
940 iState = EImapStateFetchCancelWait; |
|
941 iReissueIdle = aReissueIdle; |
|
942 |
|
943 // Start an idle timer - this is ensure that if the GPRS session is currently |
|
944 // suspended we don't hang forever waiting for the remaining fetch data to be |
|
945 // received. |
|
946 iCancelTimer->After(KImapFetchCancelIdleTime); |
|
947 |
|
948 // Delete any partially downloaded attachments |
|
949 if ( iAttachmentFileState == EFileIsOpen ) |
|
950 { |
|
951 iAttachmentFileState=EFileIsIncomplete; |
|
952 if(iCaf->Processing()) |
|
953 { |
|
954 iCaf->EndProcessingL(); |
|
955 } |
|
956 else |
|
957 { |
|
958 iAttachmentFile->CloseFile(); |
|
959 } |
|
960 CMsvStore* store = iEntry->EditStoreL(); |
|
961 CleanupStack::PushL(store); |
|
962 // Could be multiple attachments in the folder. |
|
963 TInt i; |
|
964 TInt attachmentCount = store->AttachmentManagerL().AttachmentCount(); |
|
965 for(i=0;i<attachmentCount;i++) |
|
966 { |
|
967 // Remove [0] as array is shuffled. Once index [n] is removed n+1 becomes n |
|
968 store->AttachmentManagerExtensionsL().RemoveAttachmentL(0); |
|
969 } |
|
970 if(attachmentCount) |
|
971 store->CommitL(); |
|
972 CleanupStack::PopAndDestroy(store); |
|
973 TMsvEmailEntry message=iEntry->Entry(); |
|
974 |
|
975 CreateAttachmentInfoL((TMsvEmailEntry&)iEntry->Entry()); |
|
976 ChangeEntryBulkL(message); |
|
977 } |
|
978 |
|
979 DBG((LogText(_L8("CImImap4Session::CancelAndIdleL() - fetch cancel; continue to process requested fetches - no more parts to be requested")))); |
|
980 } break; |
|
981 |
|
982 case EImapStateIdleWait: |
|
983 { |
|
984 // Not much to do - already issued IDLE command, just wait for the IDLE |
|
985 // command to 'start'. |
|
986 // NOTE - set the iReissueIdle flag to ensure that IssueIdleRead is |
|
987 // called when we get the server response. |
|
988 iReissueIdle = aReissueIdle; |
|
989 |
|
990 // Start an idle timer - this is ensure that if the GPRS session is currently |
|
991 // suspended we don't hang forever waiting for the remaining data to be |
|
992 // received. |
|
993 iCancelTimer->After(KImapFetchCancelIdleTime); |
|
994 |
|
995 DBG((LogText(_L8("CImImap4Session::CancelAndIdleL() - waiting for IDLE - continue")))); |
|
996 } break; |
|
997 |
|
998 case EImapStateIdling: |
|
999 { |
|
1000 // Nothing much to do here - already IDLE-ing! Unset the cancel-and-idle flag |
|
1001 iCancelAndIdle = EFalse; |
|
1002 DBG((LogText(_L8("CImImap4Session::CancelAndIdleL() - already IDLE-ing - do nothing")))); |
|
1003 } break; |
|
1004 |
|
1005 case EImapStateStopIdleWait: |
|
1006 { |
|
1007 // Need to wait for the current IDLE command to complete, then re-issue |
|
1008 // another one!! |
|
1009 iReissueIdle = aReissueIdle; |
|
1010 |
|
1011 // Start an idle timer - this is ensure that if the GPRS session is currently |
|
1012 // suspended we don't hang forever waiting for the remaining data to be |
|
1013 // received. |
|
1014 iCancelTimer->After(KImapFetchCancelIdleTime); |
|
1015 |
|
1016 DBG((LogText(_L8("CImImap4Session::CancelAndIdleL() - IDLE stop requested - wait for response and then re-issue IDLE command")))); |
|
1017 } break; |
|
1018 |
|
1019 case EImapStateSelectWait: |
|
1020 { |
|
1021 // Waiting for select to complete - issue IDLE once select completes. |
|
1022 // A check will be made to ensure that the selected mailbox is the inbox. |
|
1023 |
|
1024 iReissueIdle = aReissueIdle; |
|
1025 |
|
1026 // Start an idle timer - this is ensure that if the GPRS session is currently |
|
1027 // suspended we don't hang forever waiting for the remaining data to be |
|
1028 // received. |
|
1029 iCancelTimer->After(KImapFetchCancelIdleTime); |
|
1030 |
|
1031 DBG((LogText(_L8("CImImap4Session::CancelAndIdleL() - cancel select - wait for response and then issue IDLE command")))); |
|
1032 } break; |
|
1033 |
|
1034 case EImapStateSelected: |
|
1035 { |
|
1036 // A mailbox has been selected - does IDLE need to be re-issued? |
|
1037 iReissueIdle = aReissueIdle; |
|
1038 |
|
1039 if( iReissueIdle ) |
|
1040 { |
|
1041 // Yep, need to re-issue the IDLE (as was IDLE-ing before). |
|
1042 // For this need to be in the INBOX as a writable-select. |
|
1043 |
|
1044 // First reset counts to safe values here to avoid reporting left |
|
1045 // over values from previous fetch. Correct values will be set up |
|
1046 // once headers have been fetched and parts counted (taken from SelectL). |
|
1047 iProgress.iPartsToDo=iProgress.iBytesToDo=1; |
|
1048 iProgress.iPartsDone=iProgress.iBytesDone=0; |
|
1049 |
|
1050 // Do the select (if we really need to) or skip if possible. |
|
1051 if( iMailboxId==GetInbox() && iMailboxWritable ) |
|
1052 { |
|
1053 DBG((LogText(_L8("Need to re-issue IDLE command")))); |
|
1054 |
|
1055 // No need to do the select - so re-issue the IDLE |
|
1056 DoStartIdleL(); |
|
1057 } |
|
1058 else |
|
1059 { |
|
1060 DBG((LogText(_L8("Need to issue IDLE - first select Inbox (writable)")))); |
|
1061 |
|
1062 // Looks like we need to do the select... |
|
1063 DoSelectL(GetInbox(), ETrue); |
|
1064 } |
|
1065 } |
|
1066 else |
|
1067 { |
|
1068 DBG((LogText(_L8("Do not re-issue IDLE - cancel completed")))); |
|
1069 |
|
1070 // Cancelling completed - stay in this mailbox and do not issue idle. |
|
1071 iCancelAndIdle = EFalse; |
|
1072 } |
|
1073 } break; |
|
1074 |
|
1075 case EImapStateMoveEntryWait: |
|
1076 { |
|
1077 // We're cancelling a move entry: we need to stop it specifically |
|
1078 iMoveEntry->Cancel(); |
|
1079 iState=iSavedState; |
|
1080 |
|
1081 // Recurse (yuk!) into method with the new 'saved' state. |
|
1082 CancelAndIdleL(aReissueIdle); |
|
1083 return; |
|
1084 } |
|
1085 // No break statement required due to return statement |
|
1086 |
|
1087 default: |
|
1088 // For all other states - just do a 'normal' cancel - this will probably |
|
1089 // disconnect the session. |
|
1090 iCancelAndIdle = EFalse; |
|
1091 Cancel(); |
|
1092 return; |
|
1093 } |
|
1094 |
|
1095 // Need to complete the parent/observer. |
|
1096 CMsgActive::DoCancel(); |
|
1097 } |
|
1098 |
|
1099 // Called when parent wants to cancel current operation |
|
1100 void CImImap4Session::DoCancel() |
|
1101 { |
|
1102 DBG((LogText(_L8("CImImap4Session::DoCancel() called whilst in state %d"),iState))); |
|
1103 |
|
1104 if(IsIdling()) |
|
1105 { |
|
1106 iIdleRead->Cancel(); |
|
1107 } |
|
1108 else |
|
1109 { |
|
1110 if(iAttachmentFile && iAttachmentFileState==EFileIsOpen) |
|
1111 { |
|
1112 DBG((LogText(_L8("CImImap4Session::DoCancel() closing attachment file")))); |
|
1113 if(iCaf->Processing()) |
|
1114 { |
|
1115 TRAP_IGNORE(iCaf->EndProcessingL()); |
|
1116 } |
|
1117 else |
|
1118 { |
|
1119 iAttachmentFile->CloseFile(); |
|
1120 } |
|
1121 iAttachmentFileState=EFileNotOpen; |
|
1122 } |
|
1123 |
|
1124 // What were we about to do? |
|
1125 switch(iState) |
|
1126 { |
|
1127 case EImapStateConnectWait: |
|
1128 case EImapStateGreetingWait: |
|
1129 case EImapStateLoginSendUser: |
|
1130 case EImapStateLoginSendPassword: |
|
1131 case EImapStateLoginWait: |
|
1132 DoDisconnect(); |
|
1133 break; |
|
1134 |
|
1135 case EImapStateSelectWait: |
|
1136 // Selecting: fail back to noselect |
|
1137 iState=EImapStateNoSelect; |
|
1138 break; |
|
1139 |
|
1140 case EImapStateMoveEntryWait: |
|
1141 // We're cancelling a move entry: we need to stop it specifically |
|
1142 iMoveEntry->Cancel(); |
|
1143 iState=iSavedState; |
|
1144 break; |
|
1145 |
|
1146 |
|
1147 default: |
|
1148 // Something else: disconnect for safety |
|
1149 DoDisconnect(); |
|
1150 break; |
|
1151 } |
|
1152 |
|
1153 // Note tag which we've cancelled: anything outstanding, basically, |
|
1154 // which means anything up to (and including) the last command issued, |
|
1155 // which is iTag |
|
1156 iCancelledTag=iTag; |
|
1157 |
|
1158 iImapIO->Cancel(); |
|
1159 } |
|
1160 |
|
1161 iIdleTimer->Cancel(); |
|
1162 |
|
1163 DBG((LogText(_L8("CImImap4Session::DoCancel() finished 1")))); |
|
1164 |
|
1165 // ...ask parent to finish up |
|
1166 CMsgActive::DoCancel(); |
|
1167 |
|
1168 DBG((LogText(_L8("CImImap4Session::DoCancel() finished 2")))); |
|
1169 } |
|
1170 |
|
1171 // Disconnect and complete with an error code |
|
1172 void CImImap4Session::Fail(const TInt aError) |
|
1173 { |
|
1174 DBG((LogText(_L8("CImImap4Session::Fail(%d)"),aError))); |
|
1175 DoDisconnect(); |
|
1176 DBG((LogText(_L8("-----------------------------------------------------------")))); |
|
1177 DBG((LogText(_L8("CImap4Session::Fail(): calling Complete()")))); |
|
1178 DBG((LogText(_L8("-----------------------------------------------------------")))); |
|
1179 Complete(aError); |
|
1180 } |
|
1181 |
|
1182 void CImImap4Session::DoDisconnect() |
|
1183 { |
|
1184 DBG(LogText (_L8("CImImap4Session::DoDisconnect()"))); |
|
1185 iImapIO->Disconnect(); |
|
1186 iState = EImapStateDisconnected; |
|
1187 iSecurityState = EUnknown; |
|
1188 iCommandsOutstanding = 0; |
|
1189 iSendQueued = EFalse; |
|
1190 iReceiveQueued = EFalse; |
|
1191 iReissueIdle = EFalse; |
|
1192 iCancelAndIdle = EFalse; |
|
1193 iIdleTimer->Cancel(); |
|
1194 iIdleTimerExpired = EFalse; |
|
1195 } |
|
1196 |
|
1197 void CImImap4Session::DummyComplete(TInt aError) |
|
1198 { |
|
1199 DBG(LogText(_L8("+ CImImap4Session::DummyComplete(err=%d)"), aError)); |
|
1200 if(aError >= KErrNone) |
|
1201 { // Got a response, let's have a look at it |
|
1202 CImapAtom* p=iImapIO->RootAtom()->Child(); |
|
1203 if (!p) |
|
1204 { |
|
1205 aError = KErrNotFound; |
|
1206 return; |
|
1207 } |
|
1208 |
|
1209 if (p->Compare(KIMAP_UNTAGGED)) |
|
1210 { |
|
1211 // Process it, it could be something useful |
|
1212 TRAP( aError, ProcessUntaggedL(p->ToNextL(),EFalse) ); |
|
1213 DBG( LogText(_L8("Dummy read untagged msg processed with %d"), aError) ); |
|
1214 } |
|
1215 else |
|
1216 { |
|
1217 // Got a valid response that wasn't untagged... uhoh! |
|
1218 DBG( LogText(_L8("Dummy read received a non-untagged response!")) ); |
|
1219 aError = KErrCorrupt; |
|
1220 } |
|
1221 } |
|
1222 |
|
1223 if(aError < KErrNone) |
|
1224 { |
|
1225 // If the dummy read returned an error then the line was probably dropped, |
|
1226 // or, if the read returned something that was rubbish then the server is |
|
1227 // not playing by the rules. |
|
1228 // Either way, let's disconnect. |
|
1229 LostConnection(aError); |
|
1230 |
|
1231 // Can't report the error, no one to report to |
|
1232 } |
|
1233 DBG( LogText(_L8("- CImImap4Session::DummyComplete()")) ); |
|
1234 } |
|
1235 |
|
1236 void CImImap4Session::LostConnection(TInt /*aError*/) |
|
1237 { |
|
1238 // the line must have been dropped so call DoDisconnect to ensure state is up |
|
1239 // to date |
|
1240 DoDisconnect(); |
|
1241 |
|
1242 // mark service as offline immediately |
|
1243 // the returned error code ignored |
|
1244 if (iEntry->SetEntry(iServiceId)) |
|
1245 { |
|
1246 TMsvEntry entry=iEntry->Entry(); |
|
1247 entry.SetConnected(EFalse); |
|
1248 iEntry->ChangeEntry(entry); |
|
1249 } |
|
1250 } |
|
1251 |
|
1252 |
|
1253 void CImImap4Session::IdleReadError(TInt aError) |
|
1254 { |
|
1255 // Read completed with an error, probably lost the connection |
|
1256 DBG(LogText(_L8("IMAP Idle outstanding read completed with %d"), aError)); |
|
1257 |
|
1258 LostConnection(aError); |
|
1259 |
|
1260 // Stop the idle timer so that it does not try to restart the idle when it expires |
|
1261 iIdleTimer->Cancel(); |
|
1262 } |
|
1263 |
|
1264 |
|
1265 void CImImap4Session::IssueIdleRead() |
|
1266 { |
|
1267 __ASSERT_DEBUG(IsIdling() && !IsActive(), gPanic(EBadUseOfImap4Op)); |
|
1268 DBG((LogText(_L8("Idle read issued")))); |
|
1269 iIdleRead->Start(iStatus); |
|
1270 DBG((LogText(_L8("******************************************************************")))); |
|
1271 DBG((LogText(_L8("CImImap4Session::IssueIdleRead(): waiting for iIdleRead to wake me")))); |
|
1272 DBG((LogText(_L8("******************************************************************")))); |
|
1273 SetActive(); |
|
1274 } |
|
1275 |
|
1276 |
|
1277 void CImImap4Session::IssueDummy() |
|
1278 { |
|
1279 #ifdef ASYNC_NOTIFICATIONS |
|
1280 // Issue a dummy read from the CImapIO class so we can check the connection |
|
1281 // status. ONLY IF WE'RE CONNECTED! |
|
1282 if (iState>=EImapStateNoSelect) |
|
1283 { |
|
1284 iDummyRead->Start(); |
|
1285 } |
|
1286 #endif |
|
1287 } |
|
1288 |
|
1289 void CImImap4Session::CancelDummy() |
|
1290 { |
|
1291 #ifdef ASYNC_NOTIFICATIONS |
|
1292 // Cancel it! |
|
1293 iDummyRead->Cancel(); |
|
1294 #endif |
|
1295 } |
|
1296 |
|
1297 void CImImap4Session::ReissueIdleL() |
|
1298 { |
|
1299 DBG((LogText(_L8("CImImap4Session::Re-issueIdle(): State: %d"), iState))); |
|
1300 Cancel(); |
|
1301 |
|
1302 DBG((LogText(_L8("CImImap4Session::ReIssueIdle(): setting iReissueIdle to true")))); |
|
1303 iReissueIdle=ETrue; |
|
1304 DoStopIdleL(); |
|
1305 } |
|
1306 |
|
1307 void CImImap4Session::ReissueDummy() |
|
1308 { |
|
1309 IssueDummy(); |
|
1310 } |
|
1311 |
|
1312 |
|
1313 void CImImap4Session::DoComplete(TInt& aStatus) |
|
1314 { |
|
1315 DBG((LogText(_L8("CImImap4Session::DoComplete(iState=%d, aStatus=%d)"),iState,aStatus))); |
|
1316 |
|
1317 if (iState == EImapStateSelected) |
|
1318 { |
|
1319 iCompoundStopIdle = EFalse; |
|
1320 iStoppingIdleForSync = EFalse; |
|
1321 } |
|
1322 |
|
1323 if( iState != EImapStateFetchCancelWait && |
|
1324 iAttachmentFile && iAttachmentFileState==EFileIsOpen ) |
|
1325 { |
|
1326 // Do not close the attachment file if we're cancelling the fetch - we |
|
1327 // will still be receiving data and if this completes the attachment |
|
1328 // then we want the attachment file available for that. |
|
1329 |
|
1330 DBG((LogText(_L8("CImImap4Session::DoComplete closing attachment file")))); |
|
1331 if(iCaf->Processing()) |
|
1332 { |
|
1333 TRAP_IGNORE(iCaf->EndProcessingL()); |
|
1334 } |
|
1335 else |
|
1336 { |
|
1337 iAttachmentFile->CloseFile(); |
|
1338 } |
|
1339 iAttachmentFileState=EFileNotOpen; |
|
1340 } |
|
1341 |
|
1342 // All ok? |
|
1343 if (aStatus==KErrNone) |
|
1344 { |
|
1345 // Everything is fine. However, we need to queue a dummy read from the |
|
1346 // CImapIO layer to ensure that we get notified if the connection dies |
|
1347 // unexpectedly |
|
1348 |
|
1349 // Update the progress error code first. |
|
1350 iProgress.iErrorCode=aStatus; |
|
1351 |
|
1352 if (ImapIdleSupported()==EFalse) |
|
1353 { |
|
1354 IssueDummy(); |
|
1355 } |
|
1356 |
|
1357 if (IsIdling()) |
|
1358 { |
|
1359 IssueIdleRead(); |
|
1360 } |
|
1361 |
|
1362 return; |
|
1363 } |
|
1364 |
|
1365 if( iCancelAndIdle ) |
|
1366 { |
|
1367 // Record the error code and exit the method - ensure that we don't |
|
1368 // disconnect. |
|
1369 iProgress.iErrorCode=aStatus; |
|
1370 return; |
|
1371 } |
|
1372 |
|
1373 // Some error has ocurred. Deal with it. |
|
1374 switch(iState) |
|
1375 { |
|
1376 case EImapStateCreateWait: |
|
1377 case EImapStateRenameWait: |
|
1378 case EImapStateDeleteWait: |
|
1379 case EImapStateSubscribeWait: |
|
1380 // A 'KErrIMAPNO' error isn't fatal to the connection |
|
1381 if (aStatus==KErrCancel) |
|
1382 { |
|
1383 // Back to previous state: these commands won't have |
|
1384 // disturbed it. |
|
1385 iState=iSavedState; |
|
1386 return; |
|
1387 } |
|
1388 else if (aStatus==KErrIMAPNO) |
|
1389 { |
|
1390 // Report error |
|
1391 if (iState == EImapStateDeleteWait) |
|
1392 iProgress.iErrorCode=KErrImapCantDeleteFolder; |
|
1393 else |
|
1394 iProgress.iErrorCode=KErrNotSupported; |
|
1395 aStatus=iProgress.iErrorCode; |
|
1396 |
|
1397 // Back to previous state |
|
1398 iState=iSavedState; |
|
1399 return; |
|
1400 } |
|
1401 |
|
1402 // Otherwise, process as per normal |
|
1403 break; |
|
1404 |
|
1405 case EImapStateSelectWait: |
|
1406 case EImapStateSynchroniseWait: |
|
1407 // KErrIMAPNO isn't fatal, we just go back to the selected state |
|
1408 if (aStatus==KErrIMAPNO) |
|
1409 { |
|
1410 iState=EImapStateSelected; |
|
1411 return; |
|
1412 } |
|
1413 break; |
|
1414 |
|
1415 case EImapStateMoveEntryWait: |
|
1416 // We're done with the moveentry |
|
1417 |
|
1418 // Park the move entry again |
|
1419 iMoveEntry->SetEntry(NULL); |
|
1420 break; |
|
1421 |
|
1422 case EImapStateIdleWait: |
|
1423 case EImapStateStopIdleWait: |
|
1424 if (iIdleTimerExpired) |
|
1425 { |
|
1426 // error has occurred following re-issue of an IDLE command |
|
1427 // Notify the server MTM that the error has occurred as there |
|
1428 // is no outstanding asynchonous request on this session. |
|
1429 // (IDLE is issued autonomously by the IMAP Session). |
|
1430 iObserver.NonCompletedFailure(); |
|
1431 } |
|
1432 break; |
|
1433 |
|
1434 case EImapStateFetchCancelWait: |
|
1435 // record the error (i.e. cancel) for progress and do not disconnect. |
|
1436 iProgress.iErrorCode=aStatus; |
|
1437 // drop through to next case... (as that is returning and so not disconnecting). |
|
1438 case EImapStateNoSelect: |
|
1439 case EImapStateSelected: |
|
1440 case EImapStateIdling: |
|
1441 return; |
|
1442 |
|
1443 default: |
|
1444 break; |
|
1445 } |
|
1446 |
|
1447 // If we get here with an error, then we need to disconnect. |
|
1448 // Earlier on, if there was a time when disconnection wasn't |
|
1449 // required, we would have returned. |
|
1450 DoDisconnect(); |
|
1451 |
|
1452 // Save error code in progress |
|
1453 iProgress.iErrorCode=aStatus; |
|
1454 } |
|
1455 |
|
1456 // Copy a message: in fact, we move the entire message to the destination, but |
|
1457 // then recreate the empty shell (no parts fetched) of the source |
|
1458 void CImImap4Session::CopyMessage(TRequestStatus& aRequestStatus, const TMsvId aSourceFolder, const TMsvId aSource, const TMsvId aDestinationFolder, TMsvId* aNewSource, const TBool aRemoveOriginal) |
|
1459 { |
|
1460 TInt err=KErrNone; |
|
1461 if (!Connected()) |
|
1462 { |
|
1463 Queue(aRequestStatus); |
|
1464 err=KErrDisconnected; |
|
1465 } |
|
1466 else |
|
1467 TRAP(err,CopyMessageL(aRequestStatus, aSourceFolder, aSource, aDestinationFolder, aNewSource, aRemoveOriginal)); |
|
1468 if (err!=KErrNone) |
|
1469 { |
|
1470 // park moveentry if it fails to get going |
|
1471 if (iMoveEntry) |
|
1472 iMoveEntry->SetEntry(NULL); |
|
1473 DBG((LogText(_L8("-----------------------------------------------------------")))); |
|
1474 DBG((LogText(_L8("CImap4Session::CopyMessage(): calling Complete()")))); |
|
1475 DBG((LogText(_L8("-----------------------------------------------------------")))); |
|
1476 |
|
1477 Complete(err); |
|
1478 } |
|
1479 } |
|
1480 |
|
1481 void CImImap4Session::CopyMessageL(TRequestStatus& aRequestStatus, const TMsvId aSourceFolder, const TMsvId aSource, const TMsvId aDestinationFolder, TMsvId* aNewSource, const TBool aRemoveOriginal) |
|
1482 { |
|
1483 LOG_COMMANDS((LogText(_L8("COMMAND CopyMessage(%x (in %x) to %x)"),aSource,aSourceFolder,aDestinationFolder))); |
|
1484 |
|
1485 Queue(aRequestStatus); |
|
1486 |
|
1487 // Get a moveentry if we don't already have one |
|
1488 if (!iMoveEntry) |
|
1489 { |
|
1490 // Get a MoveEntry: we need to ask for one as a child of this entry, so |
|
1491 // move it to the root and ask for a child in the local service, which should |
|
1492 // always be there. |
|
1493 SetEntryL(KMsvRootIndexEntryId); |
|
1494 |
|
1495 // Get child |
|
1496 iMoveEntry=iEntry->NewEntryL(KMsvLocalServiceIndexEntryId); |
|
1497 } |
|
1498 |
|
1499 // Do the move, using the iMoveEntry CMsvServerEntry object, after parking our original |
|
1500 // one |
|
1501 iEntry->SetEntry(NULL); |
|
1502 User::LeaveIfError(iMoveEntry->SetEntry(aSourceFolder)); |
|
1503 |
|
1504 DBG((LogText(_L8("About to call iEntry->MoveEntry(%x,%x) when in folder %x"),aSource,aDestinationFolder,aSourceFolder))); |
|
1505 |
|
1506 // We're moving - note bits for bottom half handling |
|
1507 iMoveSource=aSource; |
|
1508 iMoveSourceFolder=aSourceFolder; |
|
1509 iNewSource=aNewSource; |
|
1510 |
|
1511 // Cancel any dummy operation that might be outstanding |
|
1512 if (ImapIdleSupported()==EFalse) |
|
1513 { |
|
1514 CancelDummy(); |
|
1515 } |
|
1516 |
|
1517 // Trap around this so we can park moveentry if it fails |
|
1518 //DS - now selectively either Copy or Move. |
|
1519 aRemoveOriginal? |
|
1520 iMoveEntry->MoveEntryL(aSource,aDestinationFolder,iStatus): |
|
1521 iMoveEntry->CopyEntryL(aSource,aDestinationFolder,iStatus); |
|
1522 |
|
1523 // Move into the new state and go active |
|
1524 iSavedState=iState; |
|
1525 iState=EImapStateMoveEntryWait; |
|
1526 if (!IsActive()) SetActive(); |
|
1527 } |
|
1528 |
|
1529 #if 0 |
|
1530 // Debug only: print out a representation of the parse tree |
|
1531 void CImImap4Session::showtree(CImapAtom *root,int indent) |
|
1532 { |
|
1533 TInt b=0; |
|
1534 |
|
1535 // Run through children |
|
1536 do |
|
1537 { |
|
1538 TPtrC8 atom=root->Atom(); |
|
1539 LogText(_L8("%03d Sibling %d: '%S'"),indent,b++,&atom); |
|
1540 |
|
1541 if (root->Child()) |
|
1542 showtree(root->Child(),indent+2); |
|
1543 |
|
1544 root=root->Next(); |
|
1545 } |
|
1546 while(root); |
|
1547 } |
|
1548 #endif |
|
1549 |
|
1550 // Parse greeting message |
|
1551 TInt CImImap4Session::ProcessGreetingL() |
|
1552 { |
|
1553 CImapAtom* p=iRootAtom->ToChildL(); |
|
1554 |
|
1555 // Should be a '*' |
|
1556 if (!p->Compare(KIMAP_UNTAGGED)) |
|
1557 User::Leave(KErrGeneral); |
|
1558 |
|
1559 // Greeting line can be: |
|
1560 // * BYE ... (server busy) |
|
1561 // * PREAUTH ... (no login needed) |
|
1562 // * OK ... (normal) |
|
1563 p=p->ToNextL(); |
|
1564 if (p->Compare(KIMAP_BYE)) |
|
1565 { |
|
1566 // Server is busy |
|
1567 return(KErrImapServerBusy); |
|
1568 } |
|
1569 else if (p->Compare(KIMAP_PREAUTH)) |
|
1570 { |
|
1571 // Already authorised, straight into Noselect |
|
1572 iSavedState=EImapStateNoSelect; |
|
1573 } |
|
1574 else if (p->Compare(KIMAP_OK)) |
|
1575 { |
|
1576 // Need to login |
|
1577 iSavedState=EImapStateLoginWait; |
|
1578 |
|
1579 // Is this a CC:Mail server? (paranoid mode) |
|
1580 if (p->Next()) |
|
1581 { |
|
1582 if (p->Next()->Compare(_L8("CC:Mail"))) |
|
1583 { |
|
1584 // We are, note it. |
|
1585 iTalkingToCCMail=ETrue; |
|
1586 |
|
1587 DBG((LogText(_L8("We're talking to a CC:Mail server, modified fetch strategy enabled.")))); |
|
1588 } |
|
1589 else if (p->Next()->Compare(_L8("OpenMail")) ) |
|
1590 { |
|
1591 iTalkingToOpenMail=ETrue; |
|
1592 DBG((LogText(_L8("We're talking to an OpenMail server, modified fetch strategy enabled.")))); |
|
1593 } |
|
1594 } |
|
1595 } |
|
1596 |
|
1597 // Looks ok |
|
1598 return(KErrNone); |
|
1599 } |
|
1600 |
|
1601 // Parse select reply messages |
|
1602 TInt CImImap4Session::ProcessCommandReplyL() |
|
1603 { |
|
1604 CImapAtom *p=iRootAtom->ToChildL(); |
|
1605 |
|
1606 // Which command does this reply belong to? |
|
1607 TInt thisis=iTag-(iCommandsOutstanding-1); |
|
1608 |
|
1609 // A cancelled command? |
|
1610 TBool cancelled(EFalse); |
|
1611 if (thisis<=iCancelledTag) |
|
1612 { |
|
1613 cancelled=ETrue; |
|
1614 } |
|
1615 |
|
1616 // '+' indicates ideling |
|
1617 if (p->Compare(KIMAP_CONTINUATION)) |
|
1618 { |
|
1619 return(ProcessContinuationResponse(p->ToNextL())); |
|
1620 } |
|
1621 |
|
1622 // '*' indicates an untagged message |
|
1623 if (p->Compare(KIMAP_UNTAGGED)) |
|
1624 { |
|
1625 // Process it |
|
1626 return(ProcessUntaggedL(p->ToNextL(),cancelled)); |
|
1627 } |
|
1628 |
|
1629 // If we got here, it's a tagged reply. |
|
1630 // Check it's the one we're expecting |
|
1631 TInt tag(0); |
|
1632 TInt error(p->Value(tag)); |
|
1633 if (error!=KErrNone) |
|
1634 { |
|
1635 // Problem parsing |
|
1636 return error; |
|
1637 } |
|
1638 |
|
1639 // Some command sequencing debugging |
|
1640 DBG((LogText(_L8("Expecting tag %d, got tag %d"),thisis,tag))); |
|
1641 |
|
1642 // One less outstanding command |
|
1643 iCommandsOutstanding--; |
|
1644 |
|
1645 // If the tagged reply is for a command that had been cancelled, |
|
1646 // and there are still commands outstanding, then don't complete: |
|
1647 // instead, just keep reading replies. |
|
1648 if (tag < iCancelledTag + 1) |
|
1649 { |
|
1650 return KErrNotReady; |
|
1651 } |
|
1652 |
|
1653 // Move on to result |
|
1654 p=p->ToNextL(); |
|
1655 |
|
1656 #ifdef PRINTING |
|
1657 // Print success of failure |
|
1658 TPtrC8 n=p->Atom(); |
|
1659 LogText(_L8("Result for command tag %d is '%S'"),iTag,&n); |
|
1660 #endif |
|
1661 // If it's OK, pass it to untagged processor |
|
1662 if (p->Compare(KIMAP_OK)) |
|
1663 { |
|
1664 // It might have stuff like 'READ-WRITE' in it... but only if |
|
1665 // the next atom has a child (ie open bracket) |
|
1666 if (p->Next() && p->Next()->Child()) |
|
1667 { |
|
1668 DBG((LogText(_L8("CImap4Session::ProcessCommandReply(): OK recieved with children")))); |
|
1669 // Ignore the return code: we've got our tagged reply! |
|
1670 ProcessUntaggedL(p,EFalse); |
|
1671 } |
|
1672 else |
|
1673 { |
|
1674 // received ok response |
|
1675 DBG((LogText(_L8("CImap4Session::ProcessCommandReply(): OK received - no children")))); |
|
1676 } |
|
1677 return(KErrNone); |
|
1678 } |
|
1679 else if (p->Compare(KIMAP_NO)) |
|
1680 { |
|
1681 // The server didn't like this |
|
1682 return(KErrIMAPNO); |
|
1683 } |
|
1684 |
|
1685 // It's not OK: there's been an error |
|
1686 return(KErrGeneral); |
|
1687 } |
|
1688 |
|
1689 // Parse Continuation Response |
|
1690 TInt CImImap4Session::ProcessContinuationResponse(CImapAtom* /*aAtom*/) |
|
1691 { |
|
1692 if (iState==EImapStateIdleWait) |
|
1693 { |
|
1694 iState = EImapStateIdling; |
|
1695 if(!iIdleTimer->IsActive()) |
|
1696 { |
|
1697 iIdleTimer->After(iIdleTimeout); |
|
1698 } |
|
1699 return KErrNone; |
|
1700 } |
|
1701 else |
|
1702 { |
|
1703 return KErrArgument; |
|
1704 } |
|
1705 } |
|
1706 |
|
1707 // Parse untagged messages |
|
1708 TInt CImImap4Session::ProcessUntaggedL(CImapAtom *aAtom, const TBool aCancelled) |
|
1709 { |
|
1710 DBG((LogText(_L8("CImap4Session::ProcessUntaggedL(): running...")))); |
|
1711 |
|
1712 CImapAtom *p=aAtom; |
|
1713 |
|
1714 // Look at first atom |
|
1715 if (static_cast<TChar>(p->Atom()[0]).IsDigit()) |
|
1716 { |
|
1717 // First atom is a number |
|
1718 TUint msgnr(0); |
|
1719 |
|
1720 // Got it ok? |
|
1721 if (p->Value(msgnr)!=KErrNone) |
|
1722 User::Leave(KErrArgument); |
|
1723 |
|
1724 // Next atom will be one of: |
|
1725 // EXISTS, RECENT, FETCH, etc |
|
1726 p=p->ToNextL(); |
|
1727 if (p->Compare(KIMAP_EXISTS)) |
|
1728 { |
|
1729 // Mailbox size changed? |
|
1730 DBG((LogText(_L8("Mailbox size now %d was %d"),msgnr,iMailboxSize))); |
|
1731 |
|
1732 // Note it |
|
1733 if (iMailboxSize != static_cast<TInt>(msgnr) ) |
|
1734 { |
|
1735 // Set it to EXISTS |
|
1736 iMailboxSize=msgnr; |
|
1737 |
|
1738 // if the EXISTS didn't report a change in size then |
|
1739 // pretend it wasn't received |
|
1740 iMailboxReceivedExists=ETrue; |
|
1741 } |
|
1742 |
|
1743 // Resize index |
|
1744 iFolderIndex.SetSizeL(iMailboxSize); |
|
1745 } |
|
1746 else if (p->Compare(KIMAP_EXPUNGE)) |
|
1747 { |
|
1748 // Note it |
|
1749 iMailboxReceivedExpunge=ETrue; |
|
1750 } |
|
1751 else if (p->Compare(KIMAP_RECENT)) |
|
1752 { |
|
1753 // Note it |
|
1754 iMailboxRecent=msgnr; |
|
1755 } |
|
1756 else if (!aCancelled && p->Compare(KIMAP_FETCH)) |
|
1757 { |
|
1758 // Process fetch: any fetch data following? |
|
1759 if (p->Next() && p->Next()->Child()) |
|
1760 { |
|
1761 // Got an open bracket situation, looks good |
|
1762 return(ProcessFetchL(msgnr,p->Next()->Child())); |
|
1763 } |
|
1764 else |
|
1765 User::Leave(KErrGeneral); |
|
1766 } |
|
1767 else |
|
1768 { |
|
1769 #ifdef PRINTING |
|
1770 // Unknown |
|
1771 TPtrC8 a=p->Atom(); |
|
1772 LogText(_L8("Unknown reply '* %d %S'"),msgnr,&a); |
|
1773 #endif |
|
1774 } |
|
1775 } |
|
1776 else |
|
1777 { |
|
1778 // First atom not a number. Is it OK? |
|
1779 if (p->Compare(KIMAP_OK)) |
|
1780 { |
|
1781 // OK *can* be followed by bracketed attrib or attrib/value pair |
|
1782 // however, this is not always the case: for example, EXAMINE'ing |
|
1783 // a new folder with Netscape IMAP4rev1 Service 3.56 gives this |
|
1784 // response: |
|
1785 // 24/01/99 13:44:58 >> 53 EXAMINE "Thingy/trevor" |
|
1786 // 24/01/99 13:44:59 << * OK Reset UID sequence counter. |
|
1787 // This is totally legal in the spec, but not awfully useful to us as |
|
1788 // plain text messages are server-specific and are really for carbon- |
|
1789 // based lifeforms to read. |
|
1790 if ((p=p->Next())==NULL) |
|
1791 { |
|
1792 // No message. Just '* OK'. What a pointless waste of bandwidth. |
|
1793 return(KErrNotReady); |
|
1794 } |
|
1795 |
|
1796 // Is this the start of a bracketed construct? |
|
1797 if (p->Compare(_L8("(")) || |
|
1798 p->Compare(_L8("["))) |
|
1799 { |
|
1800 CImapAtom* child=p->ToChildL(); |
|
1801 |
|
1802 if (child->Compare(KIMAP_UIDVALIDITY)) |
|
1803 { |
|
1804 // Save it |
|
1805 child=child->ToNextL(); |
|
1806 if (child->Value(iUidValidity)!=KErrNone) |
|
1807 User::Leave(KErrArgument); |
|
1808 } |
|
1809 else if (child->Compare(KIMAP_UIDNEXT)) |
|
1810 { |
|
1811 // Save it |
|
1812 child=child->ToNextL(); |
|
1813 if (child->Value(iUidNext)!=KErrNone) |
|
1814 User::Leave(KErrArgument); |
|
1815 } |
|
1816 else if (child->Compare(KIMAP_READWRITE)) |
|
1817 { |
|
1818 // Note read-write open |
|
1819 iMailboxWritable=ETrue; |
|
1820 } |
|
1821 else if (child->Compare(KIMAP_READONLY)) |
|
1822 { |
|
1823 // Note read-only open |
|
1824 iMailboxWritable=EFalse; |
|
1825 } |
|
1826 else if (child->Compare(KIMAP_ALERT)) |
|
1827 { |
|
1828 // alerts to be handled here, but return with no error until then |
|
1829 LogText(_L8("Alert received- SessionState:iState %d"),iState); |
|
1830 if(iState==EImapStateIdling) |
|
1831 { |
|
1832 return KErrNone; |
|
1833 } |
|
1834 } |
|
1835 else |
|
1836 { |
|
1837 #ifdef PRINTING |
|
1838 TPtrC8 unk=p->Atom(); |
|
1839 LogText(_L8("* OK [%S ???]"),&unk); |
|
1840 #endif |
|
1841 } |
|
1842 } |
|
1843 else |
|
1844 { |
|
1845 #ifdef PRINTING |
|
1846 TPtrC8 unk=p->Atom(); |
|
1847 LogText(_L8("* OK %S ???"),&unk); |
|
1848 #endif |
|
1849 |
|
1850 if(iState==EImapStateIdling) |
|
1851 { |
|
1852 return(KErrNotReady); |
|
1853 } |
|
1854 } |
|
1855 } |
|
1856 else if (!aCancelled && p->Compare(KIMAP_LIST)) |
|
1857 { |
|
1858 // Ignore it unless we've got somewhere to save it |
|
1859 ProcessListL(p->ToNextL()); |
|
1860 } |
|
1861 else if (!aCancelled && p->Compare(KIMAP_LSUB)) |
|
1862 { |
|
1863 // Process subscription list reply |
|
1864 ProcessLsubL(p->ToNextL()); |
|
1865 } |
|
1866 else if (!aCancelled && p->Compare(KIMAP_SEARCH)) |
|
1867 { |
|
1868 // Process UID search reply |
|
1869 // Need to check that we actually received a list of UIDs in the response |
|
1870 // because under certain circumstances we can get a reply that lists no UIDs. |
|
1871 // This can happen if the search command specifies a set of UIDs but none |
|
1872 // of them can be found on the server because they have all been expunged. |
|
1873 // It can also happen if the search command includes a search string but |
|
1874 // no messages on the server match it. |
|
1875 if (p->Next()) |
|
1876 { |
|
1877 ProcessSearchL(p->ToNextL()); |
|
1878 } |
|
1879 } |
|
1880 else if (p->Compare(KIMAP_BYE)) |
|
1881 { |
|
1882 // Are we already logging out? |
|
1883 if (iState!=EImapStateLogoutWait) |
|
1884 { |
|
1885 // Unexpected disconnection |
|
1886 // WRITE! |
|
1887 } |
|
1888 } |
|
1889 else if (p->Compare(KIMAP_NO)) |
|
1890 { |
|
1891 #ifdef PRINTING |
|
1892 // NO message from server. Display it. |
|
1893 LogText(_L8("Got NO:")); |
|
1894 while((p=p->Next())!=NULL) |
|
1895 { |
|
1896 TPtrC8 word(p->Atom()); |
|
1897 LogText(_L8(" %S"),&word); |
|
1898 } |
|
1899 #endif |
|
1900 if(iState==EImapStateIdling) |
|
1901 { |
|
1902 // ignore this and remain in IDLE. |
|
1903 return KErrNotReady; |
|
1904 } |
|
1905 } |
|
1906 else if (!aCancelled && p->Compare(KIMAP_FLAGS)) |
|
1907 { |
|
1908 // FLAGS response during folder open |
|
1909 iMailboxReceivedFlags=ETrue; |
|
1910 #ifdef PRINTING |
|
1911 LogText(_L8("Got FLAGS:")); |
|
1912 p=p->ToNextL(); |
|
1913 p=p->ToChildL(); |
|
1914 do |
|
1915 { |
|
1916 TPtrC8 word(p->Atom()); |
|
1917 LogText(_L8(" %S"),&word); |
|
1918 p=p->Next(); |
|
1919 } |
|
1920 while(p!=NULL); |
|
1921 #endif |
|
1922 } |
|
1923 else if (p->Compare(KIMAP_CAPABILITY)) |
|
1924 { |
|
1925 // clear here just for good measure |
|
1926 iSeenVersion=EFalse; |
|
1927 iCapabilityIdleSupport = EFalse; |
|
1928 iCapabilityStartTLS=EFalse; |
|
1929 iCapabilityLoginDisabled=EFalse; |
|
1930 |
|
1931 // CAPABILITY reply |
|
1932 while((p=p->Next())!=NULL) |
|
1933 { |
|
1934 if (p->Compare(KIMAP_VERSION)) |
|
1935 iSeenVersion=ETrue; |
|
1936 else if (p->Compare(KIMAP_IDLE)) |
|
1937 iCapabilityIdleSupport = ETrue; |
|
1938 else if (p->Compare(KIMAP_STARTTLS)) |
|
1939 iCapabilityStartTLS=ETrue; |
|
1940 else if (p->Compare(KIMAP_LOGINDISABLED)) |
|
1941 iCapabilityLoginDisabled=ETrue; |
|
1942 } |
|
1943 } |
|
1944 else |
|
1945 { |
|
1946 #ifdef PRINTING |
|
1947 // Unknown |
|
1948 TPtrC8 a=p->Atom(); |
|
1949 LogText(_L8("Unknown reply '* %S'"),&a); |
|
1950 #endif |
|
1951 } |
|
1952 } |
|
1953 |
|
1954 return(KErrNotReady); |
|
1955 } |
|
1956 |
|
1957 // Fill in a CImHeader from an envelope atom |
|
1958 void CImImap4Session::ProcessEnvelopeL(CImHeader* aHeader, TMsvEntry& aEntry, CImapAtom *aAtom) |
|
1959 { |
|
1960 CImapAtom *q=aAtom->ToChildL(); |
|
1961 TPtrC8 tptr; |
|
1962 // ensure that nothing is placed on the cleanup stack between here |
|
1963 // and calls to ProcessAddress/ProcessAddressList |
|
1964 HBufC8 *address=HBufC8::NewLC(KImapAddressSizeInc); |
|
1965 |
|
1966 DBG((LogText(_L8("Processing envelope data")))); |
|
1967 |
|
1968 // Parse date information |
|
1969 tptr.Set(q->Atom()); |
|
1970 TImRfc822DateField date; |
|
1971 date.ParseDateField(tptr,aEntry.iDate); |
|
1972 q=q->ToNextL(); |
|
1973 |
|
1974 // Subject in CImHeader (TMsvEntry is later on after post-processing) |
|
1975 if (!q->Compare(KIMAP_NIL)) |
|
1976 aHeader->SetSubjectL(q->Atom()); |
|
1977 q=q->ToNextL(); |
|
1978 |
|
1979 // From information: both in CImHeader and TMsvEntry |
|
1980 if (q->Child()) |
|
1981 { |
|
1982 DBG((LogText(_L8("Processing 'From' information")))); |
|
1983 |
|
1984 ProcessAddressL(&address,q->ToChildL()); |
|
1985 aHeader->SetFromL(address->Des()); |
|
1986 } |
|
1987 else |
|
1988 { |
|
1989 // No From information. Set blank |
|
1990 aHeader->SetFromL(_L("")); |
|
1991 } |
|
1992 q=q->ToNextL(); |
|
1993 |
|
1994 // Discard sender information |
|
1995 q=q->ToNextL(); |
|
1996 |
|
1997 // ReplyTo information |
|
1998 if (q->Child()) |
|
1999 { |
|
2000 DBG((LogText(_L8("Processing 'ReplyTo' information")))); |
|
2001 |
|
2002 // Replyto exists |
|
2003 ProcessAddressL(&address,q->ToChildL()); |
|
2004 aHeader->SetReplyToL(address->Des()); |
|
2005 } |
|
2006 else |
|
2007 { |
|
2008 // No replyto. Use From info |
|
2009 aHeader->SetReplyToL(aHeader->From()); |
|
2010 } |
|
2011 q=q->ToNextL(); |
|
2012 |
|
2013 // To information |
|
2014 DBG((LogText(_L8("Processing 'To' information")))); |
|
2015 |
|
2016 ProcessAddressListL(&address,aHeader->ToRecipients(),q->Child()); |
|
2017 q=q->ToNextL(); |
|
2018 |
|
2019 // CC list |
|
2020 DBG((LogText(_L8("Processing 'CC' information")))); |
|
2021 |
|
2022 ProcessAddressListL(&address,aHeader->CcRecipients(),q->Child()); |
|
2023 q=q->ToNextL(); |
|
2024 |
|
2025 // BCC list |
|
2026 DBG((LogText(_L8("Processing 'BCC' information")))); |
|
2027 |
|
2028 ProcessAddressListL(&address,aHeader->BccRecipients(),q->Child()); |
|
2029 q=q->ToNextL(); |
|
2030 |
|
2031 // In-Reply-To |
|
2032 q=q->ToNextL(); |
|
2033 |
|
2034 // Message-Id |
|
2035 aHeader->SetImMsgIdL(q->AtomNoAngleBrackets()); |
|
2036 |
|
2037 // Decode any QP encoding in header fields |
|
2038 iHeaderConverter->DecodeAllHeaderFieldsL(*aHeader); |
|
2039 |
|
2040 // Set from line in TMsvEntry |
|
2041 aEntry.iDetails.Set(aHeader->From()); |
|
2042 |
|
2043 // Set subject in TMsvEntry |
|
2044 aEntry.iDescription.Set(aHeader->Subject()); |
|
2045 |
|
2046 // Get rid of buffer |
|
2047 CleanupStack::PopAndDestroy(); |
|
2048 |
|
2049 DBG((LogText(_L8("Finished processing envelope information")))); |
|
2050 } |
|
2051 |
|
2052 void CImImap4Session::StripSpace(HBufC8* aBuf) |
|
2053 { |
|
2054 TInt len = aBuf->Length(); |
|
2055 TInt in = 0; |
|
2056 TInt out = 0; |
|
2057 TPtr8 p = aBuf->Des(); |
|
2058 while (in < len) |
|
2059 { |
|
2060 TUint8 c = p[in++]; |
|
2061 if (c > ' ') |
|
2062 p[out++] = c; |
|
2063 } |
|
2064 // we could shrink the buffer here but we won't bother because it |
|
2065 // is going to get copied and freed anyway |
|
2066 } |
|
2067 |
|
2068 // Fill in a CImHeader from the extra header fields atoms, currently |
|
2069 // Priority and Receipt info. aText is an extract direct from the |
|
2070 // message header, ie lines of name: value\r\n terminated with an |
|
2071 // empty line. Not known whether the lines can be folded so assume |
|
2072 // they may. |
|
2073 void CImImap4Session::ProcessHeaderExtraL(CImHeader* aHeader, CImMimeHeader* aMimeHeader, TMsvEmailEntry* aEntry, TPtrC8 aText) |
|
2074 { |
|
2075 #ifdef _DEBUG |
|
2076 TPtrC8 dump = aText.Left(256); |
|
2077 DBG((LogText(_L8("Processing HeaderExtra data '%S'"), &dump))); |
|
2078 #endif |
|
2079 |
|
2080 // utils class |
|
2081 CImcvUtils* utils=CImcvUtils::NewLC(); |
|
2082 |
|
2083 TPtrC8 line = aText; |
|
2084 HBufC8* valueBuf = NULL; |
|
2085 |
|
2086 TPtrC8 name; |
|
2087 |
|
2088 TBool foundReplyToPrompt = EFalse; |
|
2089 // Check for content-type Application/xxx |
|
2090 // There may be a CAF agent ready to consume the content if it's DRM |
|
2091 // If aMimeHeader is set then this is the mime header prior to the actual mime section download |
|
2092 if(aMimeHeader && aMimeHeader->ContentType().MatchF(KImcvApplication) == 0) |
|
2093 { |
|
2094 // CAF registration requires concatenated content-type and subtype |
|
2095 // The type and subtype have been received and stored. |
|
2096 // Create buffer for concatenating. + 1 creates space for '/' |
|
2097 HBufC8* buf = HBufC8::NewLC(aMimeHeader->ContentSubType().Length() + aMimeHeader->ContentType().Length() + 1); |
|
2098 TPtr8 ptr(buf->Des()); |
|
2099 ptr.Copy(aMimeHeader->ContentType()); |
|
2100 ptr.Append(KImcvForwardSlash); |
|
2101 ptr.Append(aMimeHeader->ContentSubType()); |
|
2102 // Registration does not necessarily succeed but we don't care at this point. |
|
2103 iCaf->RegisterL(ptr); |
|
2104 CleanupStack::PopAndDestroy(buf); |
|
2105 } |
|
2106 |
|
2107 while (line.Length()) |
|
2108 { |
|
2109 TBool processPrevious = valueBuf != NULL; |
|
2110 TPtrC8 current; |
|
2111 |
|
2112 TInt len = line.Find(KImcvCRLF); |
|
2113 if (len > 0) |
|
2114 { |
|
2115 // split line into this one and the rest |
|
2116 current.Set(line.Left(len)); |
|
2117 line.Set(line.Mid(len+2)); |
|
2118 |
|
2119 // handle folded headers |
|
2120 if (current[0] <= ' ' && valueBuf) |
|
2121 { |
|
2122 HBufC8* buf=valueBuf->ReAllocL( valueBuf->Length() + current.Length() ); |
|
2123 if (buf!=valueBuf) |
|
2124 { |
|
2125 CleanupStack::Pop(); |
|
2126 CleanupStack::PushL(valueBuf=buf); |
|
2127 } |
|
2128 valueBuf->Des().Append( current ); |
|
2129 |
|
2130 processPrevious = EFalse; |
|
2131 } |
|
2132 } |
|
2133 else |
|
2134 { |
|
2135 // set line to null |
|
2136 line.Set(line.Left(0)); |
|
2137 } |
|
2138 |
|
2139 // find matching headers, can only be set if valueBuf was |
|
2140 // non-null |
|
2141 if (processPrevious) |
|
2142 { |
|
2143 // Dont put the following line back in as will cause a panic if the subject |
|
2144 // field is too long. Defect EXT-53KD67. |
|
2145 //DBG((LogText(_L8("header: name %S value %S"), &name, valueBuf))); |
|
2146 |
|
2147 if (aEntry) |
|
2148 { |
|
2149 CDesC8ArrayFlat* array = new(ELeave) CDesC8ArrayFlat(4); |
|
2150 CleanupStack::PushL(array); |
|
2151 CImcvUtils::PriorityFieldsL(*array); |
|
2152 for (TInt i(0); i<array->Count(); i++) |
|
2153 { |
|
2154 if(name.CompareF((*array)[i])==0) |
|
2155 { |
|
2156 aEntry->SetPriority(utils->EvaluatePriorityText(*valueBuf)); |
|
2157 } |
|
2158 } |
|
2159 CleanupStack::PopAndDestroy(array); |
|
2160 } |
|
2161 |
|
2162 if (aHeader) |
|
2163 { |
|
2164 CImcvUtils* imcvUtils = CImcvUtils::NewLC(); |
|
2165 if (imcvUtils->EvaluateReturnReceiptFields(name)) |
|
2166 { |
|
2167 aHeader->SetReceiptAddressL(*valueBuf); |
|
2168 |
|
2169 // Only set Receipt flag if this email has not |
|
2170 // been 'seen' by somebody - to prevent multiple |
|
2171 // notifications |
|
2172 if (!aEntry->SeenIMAP4Flag()) |
|
2173 aEntry->SetReceipt(ETrue); |
|
2174 } |
|
2175 |
|
2176 if((name.CompareF(KImcvFromPrompt))==0) |
|
2177 { |
|
2178 // Set from line in TMsvEntry |
|
2179 aHeader->SetFromL(*valueBuf); |
|
2180 } |
|
2181 else if((name.CompareF(KImcvSubjectPrompt))==0) |
|
2182 { |
|
2183 // Set subject in TMsvEntry |
|
2184 aHeader->SetSubjectL(*valueBuf); |
|
2185 } |
|
2186 else if((name.CompareF(KImcvDatePrompt))==0) |
|
2187 { |
|
2188 if(!iParsedTime) |
|
2189 { |
|
2190 // Set date in TMsvEntry |
|
2191 TImRfc822DateField date; |
|
2192 date.ParseDateField(*valueBuf,aEntry->iDate); |
|
2193 } |
|
2194 } |
|
2195 else if((name.CompareF(KImcvReceivedPrompt))==0) |
|
2196 { |
|
2197 if(!iParsedTime) |
|
2198 { |
|
2199 // Set date in TMsvEntry |
|
2200 TImRfc822DateField date; |
|
2201 |
|
2202 //remove the data before the comma, to just leave the date |
|
2203 TPtr8 ptr(valueBuf->Des()); |
|
2204 TInt lPos=ptr.Locate(';'); |
|
2205 ptr = ptr.Right(ptr.Length()-lPos-2); |
|
2206 date.ParseDateField(ptr,aEntry->iDate); |
|
2207 iParsedTime=ETrue; |
|
2208 } |
|
2209 } |
|
2210 else if((name.CompareF(KImcvReplyToPrompt))==0) |
|
2211 { |
|
2212 aHeader->SetReplyToL(*valueBuf); |
|
2213 foundReplyToPrompt = ETrue; |
|
2214 } |
|
2215 else if((name.CompareF(KImcvMessageIdPrompt))==0) |
|
2216 aHeader->SetImMsgIdL(*valueBuf); |
|
2217 else if((name.CompareF(KImcvToPrompt))==0) |
|
2218 ProcessAddressListL(aHeader->ToRecipients(), &valueBuf); |
|
2219 else if((name.CompareF(KImcvCcPrompt))==0) |
|
2220 ProcessAddressListL(aHeader->CcRecipients(), &valueBuf); |
|
2221 else if((name.CompareF(KImcvBccPrompt))==0) |
|
2222 ProcessAddressListL(aHeader->BccRecipients(), &valueBuf); |
|
2223 |
|
2224 CleanupStack::PopAndDestroy(); // imcvUtils |
|
2225 |
|
2226 // we are currently ignoring DispositionOptions as |
|
2227 // there is nowhere to store it |
|
2228 } |
|
2229 |
|
2230 if (aMimeHeader) |
|
2231 { |
|
2232 // Check to see if this extra header data should be passed to the CAF agent |
|
2233 if(iCaf->Registered()) |
|
2234 { |
|
2235 iCaf->AddToMetaDataL(name,valueBuf->Des()); |
|
2236 } |
|
2237 if (name.CompareF(KImcvContentBase) == 0) |
|
2238 { |
|
2239 StripSpace(valueBuf); |
|
2240 aMimeHeader->SetContentBaseL(*valueBuf); |
|
2241 } |
|
2242 else if (name.CompareF(KImcvContentLocation) == 0) |
|
2243 { |
|
2244 StripSpace(valueBuf); |
|
2245 |
|
2246 HBufC *decoded=HBufC::NewLC(valueBuf->Length()); |
|
2247 TPtr decoded_ptr(decoded->Des()); |
|
2248 |
|
2249 iHeaderConverter->DecodeHeaderFieldL(*valueBuf,decoded_ptr); |
|
2250 aMimeHeader->SetContentLocationL(*decoded); |
|
2251 CleanupStack::PopAndDestroy(); // decoded |
|
2252 } |
|
2253 } |
|
2254 |
|
2255 CleanupStack::PopAndDestroy(); // valueBuf |
|
2256 valueBuf = NULL; |
|
2257 } |
|
2258 |
|
2259 if (current.Length() && current[0] > ' ') |
|
2260 { |
|
2261 // split this line into name and value |
|
2262 TInt colon = current.Locate(':'); |
|
2263 |
|
2264 name.Set(current.Left(colon+1)); // include the colon |
|
2265 TPtrC8 value = current.Mid(colon+1); |
|
2266 |
|
2267 // skip any initial WS in the value |
|
2268 while (value.Length() != 0 && value[0] <= ' ') |
|
2269 value.Set(value.Mid(1)); |
|
2270 |
|
2271 valueBuf = value.AllocLC(); |
|
2272 } |
|
2273 } |
|
2274 |
|
2275 if (aHeader) |
|
2276 { |
|
2277 // If no reply to information, use the From value |
|
2278 if (!foundReplyToPrompt) |
|
2279 { |
|
2280 aHeader->SetReplyToL(aHeader->From()); |
|
2281 } |
|
2282 |
|
2283 // Decode any QP encoding in header fields |
|
2284 iHeaderConverter->DecodeAllHeaderFieldsL(*aHeader); |
|
2285 |
|
2286 // Set from line in TMsvEntry |
|
2287 aEntry->iDetails.Set(aHeader->From()); |
|
2288 |
|
2289 // Set subject in TMsvEntry |
|
2290 aEntry->iDescription.Set(aHeader->Subject()); |
|
2291 } |
|
2292 |
|
2293 // just in case |
|
2294 if (valueBuf) |
|
2295 CleanupStack::PopAndDestroy(); // valueBuf |
|
2296 |
|
2297 // pop off the items allocated |
|
2298 CleanupStack::PopAndDestroy(); // utils |
|
2299 |
|
2300 DBG((LogText(_L8("Finished processing HeaderExtra")))); |
|
2301 } |
|
2302 |
|
2303 // adapted this function from the one in CImRecvConvert |
|
2304 void CImImap4Session::GetDefaultFilename(TDes& aName, const TMsvEmailEntry& aMessage, const CImMimeHeader* mime) |
|
2305 { |
|
2306 aName = *iDefaultAttachmentName; |
|
2307 |
|
2308 // Add on appropriate extension |
|
2309 if (aMessage.iType == KUidMsvEmailTextEntry) |
|
2310 { |
|
2311 aName.Append(KTextExtension); |
|
2312 } |
|
2313 else if (aMessage.MHTMLEmail()) |
|
2314 { |
|
2315 aName.Append(KHtmlExtension); |
|
2316 } |
|
2317 else if (aMessage.VCard() || aMessage.VCalendar()) |
|
2318 { |
|
2319 aName.Append(KVCardExtension); |
|
2320 } |
|
2321 else if (aMessage.ICalendar()) |
|
2322 { |
|
2323 aName.Append(KICalExtension); |
|
2324 } |
|
2325 else if ( aMessage.iType == KUidMsvAttachmentEntry ) |
|
2326 { |
|
2327 if ( (mime->ContentSubType()==KImcvBmp) || |
|
2328 (mime->ContentSubType()==KImcvGif) || |
|
2329 (mime->ContentSubType()==KImcvJpeg) || |
|
2330 (mime->ContentSubType()==KImcvTiff) || |
|
2331 (mime->ContentSubType()==KImcvWav) ) |
|
2332 { |
|
2333 TBuf<KMaxExtensionLength> buf; |
|
2334 buf.Copy(mime->ContentSubType()); |
|
2335 aName.Append(KImcvFullStop); |
|
2336 aName.Append(buf); |
|
2337 } |
|
2338 } |
|
2339 } |
|
2340 |
|
2341 TBool CImImap4Session::DoesAtomContainAttachment(CImapAtom *aAtom) |
|
2342 // Check through all of this Atom's Siblings to see if they contain an attachment |
|
2343 { |
|
2344 TBool hasAttachment = EFalse; |
|
2345 CImapAtom* currentAtom = aAtom; |
|
2346 |
|
2347 // Search through all of the Sibling Atoms |
|
2348 while (currentAtom != NULL) |
|
2349 { |
|
2350 // Check if there is a Child Atom with an Attachment |
|
2351 if (currentAtom->Child() != NULL) |
|
2352 { |
|
2353 if (currentAtom->Child()->Compare(KMIME_ATTACHMENT)) |
|
2354 { |
|
2355 // This Sibling contains an Attachment. |
|
2356 hasAttachment = ETrue; |
|
2357 break; |
|
2358 } |
|
2359 } |
|
2360 |
|
2361 // Move onto the next sibling |
|
2362 currentAtom = currentAtom->Next(); |
|
2363 } |
|
2364 |
|
2365 return hasAttachment; |
|
2366 } |
|
2367 |
|
2368 // Build a single entry |
|
2369 void CImImap4Session::BuildTreeOneL(const TMsvId aParent, CImapAtom *aAtom, const TDesC8& aPath, |
|
2370 const TMsvId aThisMessage, TInt& aAttachments, TBool& aIsMHTML, TInt& aRelatedAttachments) |
|
2371 { |
|
2372 DBG((LogText(_L8("BuildTreeOneL(message=%x, parent=%x)"),aThisMessage,aParent))); |
|
2373 |
|
2374 // First, is this actually an entry, or another level of nesting? |
|
2375 if (aAtom->Child()) |
|
2376 { |
|
2377 // Another level of nesting? Call BuildTreeL() |
|
2378 BuildTreeL(aParent,aAtom,aPath,aThisMessage,aAttachments,aIsMHTML, aRelatedAttachments); |
|
2379 return; |
|
2380 } |
|
2381 |
|
2382 // Skeleton for new entry |
|
2383 SetEntryL(aParent); |
|
2384 |
|
2385 TFileName attachmentFilename; // DS somewhere to store an attachment filename |
|
2386 TMsvEmailEntry message; |
|
2387 message.iSize=0; |
|
2388 message.iMtm=KUidMsgTypeIMAP4; |
|
2389 message.iServiceId=iServiceId; |
|
2390 message.SetUID(iMessageUid); |
|
2391 message.SetValidUID(ETrue); |
|
2392 message.SetComplete(EFalse); |
|
2393 |
|
2394 // Reply from server is in this form: |
|
2395 // TYPE SUBTYPE (PARAM1 VALUE1 ...) ID DESCRIPTION ENCODING OCTETS |
|
2396 // |
|
2397 // Text parts: |
|
2398 // TYPE SUBTYPE (PARAM1 VALUE1 ...) ID DESCRIPTION ENCODING OCTETS NLINES |
|
2399 |
|
2400 // Save mime TYPE/SUBTYPE |
|
2401 CImMimeHeader *mime=CImMimeHeader::NewLC(); |
|
2402 CImapAtom *type=aAtom; |
|
2403 CImapAtom *subtype=aAtom->Next(); |
|
2404 mime->SetContentTypeL(type->Atom()); |
|
2405 mime->SetContentSubTypeL(subtype->Atom()); |
|
2406 |
|
2407 #ifdef PRINTING |
|
2408 TPtrC8 mt=type->Atom(),ms=subtype->Atom(); |
|
2409 LogText(_L8(" MIME type %S/%S"),&mt,&ms); |
|
2410 #endif |
|
2411 |
|
2412 // We start by assuming the data will be stored as a binary |
|
2413 // attachment |
|
2414 message.iType=KUidMsvAttachmentEntry; |
|
2415 if (type->Compare(KMIME_TEXT)) |
|
2416 { |
|
2417 // text/html? |
|
2418 if (subtype->Compare(KMIME_HTML)) |
|
2419 { |
|
2420 // If this Atom doesn't contain an Attachment, then this is a MHTML Message. |
|
2421 if (!DoesAtomContainAttachment(subtype)) |
|
2422 { |
|
2423 message.iType=KUidMsvEmailHtmlEntry; |
|
2424 aIsMHTML=ETrue; |
|
2425 } |
|
2426 } |
|
2427 // text/x-vcard? |
|
2428 else if (subtype->Compare(KMIME_XVCARD)) |
|
2429 { |
|
2430 // Set vCard flag in message |
|
2431 message.SetVCard(ETrue); |
|
2432 |
|
2433 // Defaults to binary |
|
2434 } |
|
2435 // text/x-vcalendar |
|
2436 else if (subtype->Compare(KMIME_VCALENDAR)) |
|
2437 { |
|
2438 // Set vCalendar flag in message |
|
2439 message.SetVCalendar(ETrue); |
|
2440 iIsVCalendar = ETrue; |
|
2441 |
|
2442 // Defaults to binary |
|
2443 } |
|
2444 // text/calendar |
|
2445 else if (subtype->Compare(KMIME_ICALENDAR)) |
|
2446 { |
|
2447 // Set iCalendar flag in message |
|
2448 message.SetICalendar(ETrue); |
|
2449 iIsICalendar = ETrue; |
|
2450 |
|
2451 // Defaults to binary |
|
2452 } |
|
2453 else |
|
2454 message.iType=KUidMsvEmailTextEntry; |
|
2455 } |
|
2456 |
|
2457 // ...and mime path |
|
2458 mime->SetRelativePathL(aPath); |
|
2459 |
|
2460 DBG((LogText(_L8(" MIME path %S"),&aPath))); |
|
2461 |
|
2462 // Parameter list |
|
2463 CImapAtom *parameter=subtype->ToNextL(); |
|
2464 |
|
2465 TUint charset = KUidMsvCharsetNone; |
|
2466 |
|
2467 // Store parameter stuff |
|
2468 if (!parameter->Compare(KIMAP_NIL)) |
|
2469 { |
|
2470 DBG((LogText(_L8(" Parameter list:")))); |
|
2471 |
|
2472 // Process list |
|
2473 CImapAtom *type_param=parameter->ToChildL(); |
|
2474 while(type_param && type_param->Next()) |
|
2475 { |
|
2476 CImapAtom *type_value; |
|
2477 type_value=type_param->ToNextL(); |
|
2478 |
|
2479 // All items are 2-tuples (parameter value (...)): get both, and store |
|
2480 TPtrC8 param=type_param->Atom(); |
|
2481 TPtrC8 value=type_value->Atom(); |
|
2482 |
|
2483 DBG((LogText(_L8(" %S %S"),¶m,&value))); |
|
2484 |
|
2485 mime->ContentTypeParams().AppendL(param); |
|
2486 mime->ContentTypeParams().AppendL(value); |
|
2487 |
|
2488 // Have we come across a 'NAME' tuple? If so, force the MIME type of this |
|
2489 // entry to be an attachment. |
|
2490 if ((param.CompareF(KMIME_NAME)==0) |
|
2491 || (param.CompareF(KMIME_NAME_RFC2231) == 0)) |
|
2492 { |
|
2493 DBG((LogText(_L8("It has an attachment filename, therefore this is an attachment")))); |
|
2494 |
|
2495 FindFilenameDecodeL(*mime,attachmentFilename); |
|
2496 StripIllegalCharactersFromFileName(attachmentFilename); |
|
2497 message.iDetails.Set(attachmentFilename); |
|
2498 |
|
2499 // If embedded message do not save as an attachment |
|
2500 if (message.iType!=KUidMsvMessageEntry) |
|
2501 message.iType=KUidMsvAttachmentEntry; |
|
2502 } |
|
2503 else if (param.CompareF(KImcvCharset)==0) |
|
2504 { |
|
2505 // Set the Mime charset from the parameter value |
|
2506 if (value.Length() != 0) |
|
2507 { |
|
2508 charset = iCharConv->GetMimeCharsetUidL(value); |
|
2509 } |
|
2510 } |
|
2511 |
|
2512 |
|
2513 // Next item |
|
2514 type_param=type_value->Next(); |
|
2515 } |
|
2516 } |
|
2517 |
|
2518 mime->SetMimeCharset(charset); |
|
2519 |
|
2520 // ID: save it |
|
2521 CImapAtom *id=parameter->ToNextL(); |
|
2522 if (!id->Compare(_L8("NIL"))) |
|
2523 mime->SetContentIDL(id->AtomNoAngleBrackets()); |
|
2524 |
|
2525 // Description: save it |
|
2526 CImapAtom *description=id->ToNextL(); |
|
2527 if (!description->Compare(_L8("NIL"))) |
|
2528 mime->SetContentDescriptionL(description->Atom()); |
|
2529 |
|
2530 // Encoding |
|
2531 CImapAtom *encoding=description->ToNextL(); |
|
2532 mime->SetContentTransferEncodingL(encoding->Atom()); |
|
2533 |
|
2534 #ifdef PRINTING |
|
2535 TPtrC8 enc=encoding->Atom(); |
|
2536 LogText(_L8(" Encoding %S"),&enc); |
|
2537 #endif |
|
2538 |
|
2539 // Octets (encoded form) |
|
2540 CImapAtom *octets=encoding->ToNextL(); |
|
2541 TInt actualsize; |
|
2542 if (octets->Value(actualsize)!=KErrNone) |
|
2543 User::Leave(KErrGeneral); |
|
2544 |
|
2545 // Twiddle this to show *decoded* size: this is basically the size of |
|
2546 // this part, multiplied by 6/8 if it's BASE64 encoded. For all other |
|
2547 // encodings, we leave the size as-is as there's no hard & fast rule |
|
2548 // which can be applied. |
|
2549 if (encoding->Compare(KMIME_BASE64)) |
|
2550 message.iSize=(actualsize*6)/8; |
|
2551 else |
|
2552 message.iSize=actualsize; |
|
2553 |
|
2554 // Add into total message size |
|
2555 iDecodedSizeOfAllParts+=message.iSize; |
|
2556 |
|
2557 // Store *remote* size in a dodgy place |
|
2558 message.iBioType=actualsize; |
|
2559 |
|
2560 //If any part of email (text/plain mime, text/html mime, attachment....) |
|
2561 // is empty then should not fetch it. |
|
2562 if(actualsize == 0) |
|
2563 { |
|
2564 message.SetComplete(ETrue); |
|
2565 } |
|
2566 |
|
2567 #ifdef PRINTING |
|
2568 LogText(_L8(" Octets %d"),message.iBioType); |
|
2569 |
|
2570 TPtrC8 type_p=type->Atom(),subtype_p=subtype->Atom(); |
|
2571 LogText(_L8("Building mime stuff: %S/%S"),&type_p,&subtype_p); |
|
2572 #endif |
|
2573 |
|
2574 // MD5 block will start after any optional parts |
|
2575 CImapAtom *md5; |
|
2576 |
|
2577 if (type->Compare(KMIME_MESSAGE) && subtype->Compare(KMIME_RFC822)) |
|
2578 { |
|
2579 // Skip RFC822 header, which should *all* be present |
|
2580 // Like this for clarity |
|
2581 CImapAtom *envelope=octets->ToNextL(); |
|
2582 CImapAtom *structure=envelope->ToNextL(); |
|
2583 CImapAtom *nooflines=structure->ToNextL(); |
|
2584 |
|
2585 // embedded message - marked as a message |
|
2586 message.iType=KUidMsvMessageEntry; |
|
2587 |
|
2588 iDecodedSizeOfAllParts-=message.iSize; |
|
2589 |
|
2590 // Next atom is MD5 - IF PRESENT |
|
2591 md5=nooflines->Next(); |
|
2592 } |
|
2593 else |
|
2594 { |
|
2595 // Find MD5 block: if this part is TEXT/* we have number of lines next |
|
2596 if (type->Compare(KMIME_TEXT)) |
|
2597 { |
|
2598 // Number of lines is next atom, followed by MD5 - IF PRESENT |
|
2599 CImapAtom *nooflines=octets->ToNextL(); |
|
2600 md5=nooflines->Next(); |
|
2601 } |
|
2602 else |
|
2603 md5=octets->Next(); |
|
2604 } |
|
2605 |
|
2606 // Do we have any extended fields? If so, deal with them |
|
2607 if (md5) |
|
2608 { |
|
2609 // Next (if present) is Content-Disposition, closely followed by language |
|
2610 CImapAtom *disposition=md5->Next(); |
|
2611 CImapAtom *language=(disposition==NULL)?NULL:disposition->Next(); |
|
2612 language=language; // Stop .aer warnings: we know it's not used (yet) |
|
2613 |
|
2614 DBG((LogText(_L8("Processing content-disposition")))); |
|
2615 |
|
2616 // Store disposition stuff |
|
2617 if (disposition && !disposition->Compare(KIMAP_NIL)) |
|
2618 { |
|
2619 // Process list |
|
2620 CImapAtom *pos=disposition->Child(); |
|
2621 while(pos) |
|
2622 { |
|
2623 // Single item (eg "INLINE") or 2-tuple (eg ("FILENAME" "blah.gif"))? |
|
2624 if (pos->Child()) |
|
2625 { |
|
2626 // Tuple |
|
2627 CImapAtom* tuple = pos->ToChildL(); |
|
2628 while(tuple) |
|
2629 { |
|
2630 mime->ContentDispositionParams().AppendL(tuple->Atom()); |
|
2631 mime->ContentDispositionParams().AppendL(tuple->ToNextL()->Atom()); |
|
2632 |
|
2633 // Filename? If so, force this as an attachment |
|
2634 if ((tuple->Atom().CompareF(KMIME_FILENAME)==0) |
|
2635 || (tuple->Atom().CompareF(KMIME_FILENAME_RFC2231)==0)) |
|
2636 { |
|
2637 DBG((LogText(_L8("It has an attachment filename, therefore this is an attachment")))); |
|
2638 FindFilenameDecodeL(*mime,attachmentFilename); |
|
2639 StripIllegalCharactersFromFileName(attachmentFilename); |
|
2640 message.iDetails.Set(attachmentFilename); |
|
2641 |
|
2642 // If embedded message do not save as an attachment |
|
2643 if (message.iType!=KUidMsvMessageEntry) |
|
2644 message.iType=KUidMsvAttachmentEntry; |
|
2645 } |
|
2646 |
|
2647 // Skip to next tuple |
|
2648 tuple = tuple->ToNextL()->Next(); |
|
2649 } |
|
2650 } |
|
2651 else |
|
2652 { |
|
2653 // Single item |
|
2654 mime->ContentDispositionParams().AppendL(pos->Atom()); |
|
2655 mime->ContentDispositionParams().AppendL(_L8("")); |
|
2656 } |
|
2657 |
|
2658 // Skip to next entry |
|
2659 pos=pos->Next(); |
|
2660 } |
|
2661 } |
|
2662 } |
|
2663 |
|
2664 // Now we're working on the type |
|
2665 if (message.iType==KUidMsvMessageEntry) |
|
2666 { |
|
2667 // MESSAGE/RFC822 |
|
2668 // This means that the next atom will be the envelope info, and the |
|
2669 // one following that will be the body structure of the embedded |
|
2670 // message. |
|
2671 // |
|
2672 // This is an entire message-within-a-message and so gets treated like |
|
2673 // an actual mail (has it's own multipartdata thing) |
|
2674 |
|
2675 // Make CImHeader bits |
|
2676 CImHeader *messageheader=CImHeader::NewLC(); |
|
2677 CImapAtom *envelope=octets->ToNextL(); |
|
2678 ProcessEnvelopeL(messageheader,message,envelope); |
|
2679 |
|
2680 // Create message |
|
2681 User::LeaveIfError(iEntry->CreateEntryBulk(message)); |
|
2682 SetEntryL(message.Id()); |
|
2683 |
|
2684 // Store CImHeader bits |
|
2685 CMsvStore* entryStore=iEntry->EditStoreL(); |
|
2686 CleanupStack::PushL(entryStore); |
|
2687 messageheader->StoreL(*entryStore); |
|
2688 mime->StoreL(*entryStore); |
|
2689 entryStore->CommitL(); |
|
2690 CleanupStack::PopAndDestroy(3); |
|
2691 |
|
2692 #if SET_RELATED_ID |
|
2693 // DS - Set message's iRelatedId to messageId to allow later UI kludges |
|
2694 TMsvEntry changeEntry(iEntry->Entry()); |
|
2695 changeEntry.iRelatedId=changeEntry.Id(); |
|
2696 ChangeEntryBulkL(changeEntry); |
|
2697 #endif |
|
2698 // Descend into attachments of this embedded message |
|
2699 CImapAtom *structure=envelope->ToNextL(); |
|
2700 TInt attachments=0; |
|
2701 TBool isMHTML=EFalse; |
|
2702 |
|
2703 BuildTreeL(message.Id(),structure->ToChildL(),aPath,message.Id(),attachments,isMHTML,aRelatedAttachments); |
|
2704 DBG((LogText(_L8("Build embedded message id %x attachments %d MHTML %d"),message.Id(),attachments,isMHTML))); |
|
2705 |
|
2706 // Save attachment and MHTML flags |
|
2707 if (attachments>0 || isMHTML) |
|
2708 { |
|
2709 SetEntryL(message.Id()); |
|
2710 TMsvEmailEntry thisMessage=iEntry->Entry(); |
|
2711 |
|
2712 if (attachments>0) |
|
2713 { |
|
2714 thisMessage.SetAttachment(ETrue); |
|
2715 } |
|
2716 |
|
2717 if (isMHTML) |
|
2718 { |
|
2719 thisMessage.SetMHTMLEmail(ETrue); |
|
2720 } |
|
2721 |
|
2722 ChangeEntryBulkL(thisMessage); |
|
2723 } |
|
2724 |
|
2725 // we are now counting embedded messages as attachments |
|
2726 aAttachments++; |
|
2727 } |
|
2728 else |
|
2729 { |
|
2730 // Something else - create an attachment entry |
|
2731 SetEntryL(aParent); |
|
2732 |
|
2733 // save parent folder type |
|
2734 TImEmailFolderType parentFolderType = ((TMsvEmailEntry)iEntry->Entry()).MessageFolderType(); |
|
2735 |
|
2736 // set attachment and HTML flags on item |
|
2737 if ( message.iType==KUidMsvAttachmentEntry) |
|
2738 message.SetAttachment(ETrue); |
|
2739 |
|
2740 if ( message.iType==KUidMsvEmailHtmlEntry) |
|
2741 message.SetMHTMLEmail(ETrue); |
|
2742 |
|
2743 // ensure there is a filename if it is a non-text item (which |
|
2744 // also controls iFetchIsText, the flag used in DecodeAndStore |
|
2745 // to say whether to stream to a file or RichText store. |
|
2746 if (message.iType!=KUidMsvEmailTextEntry && message.iDetails.Length() == 0) |
|
2747 { |
|
2748 // use iAttachmentName for temporary buffer |
|
2749 GetDefaultFilename(iAttachmentName, message, mime); |
|
2750 message.iDetails.Set(iAttachmentName); |
|
2751 } |
|
2752 |
|
2753 User::LeaveIfError(iEntry->CreateEntryBulk(message)); |
|
2754 SetEntryL(message.Id()); |
|
2755 |
|
2756 DBG((LogText(_L8("Created attachment id %x as child of %x - type %d"),message.Id(),aParent, parentFolderType))); |
|
2757 |
|
2758 #if SET_RELATED_ID |
|
2759 // DS - Set message's iRelatedId to messageId to allow later UI kludges |
|
2760 TMsvEntry changeEntry(iEntry->Entry()); |
|
2761 changeEntry.iRelatedId=changeEntry.Id(); |
|
2762 ChangeEntryBulkL(changeEntry); |
|
2763 #endif |
|
2764 |
|
2765 DBG((LogText(_L8("Streaming MIME info into id %x"),iEntry->Entry().Id()))); |
|
2766 |
|
2767 // Stream the MIME info out into the message |
|
2768 // This will either stream it to the actual message (if the above if |
|
2769 // evaluated to True, the entry is still set to the message), or to |
|
2770 // the newly created child |
|
2771 CMsvStore* entryStore=iEntry->EditStoreL(); |
|
2772 CleanupStack::PushL(entryStore); |
|
2773 mime->StoreL(*entryStore); |
|
2774 entryStore->CommitL(); |
|
2775 CleanupStack::PopAndDestroy(2, mime); |
|
2776 |
|
2777 // This entry is NOT an attachment in the following cases - |
|
2778 // 1) This is an attachment whose parent is a MULTIPART/RELATED folder. |
|
2779 // In this case, this entry could be a image entity for an MHTML |
|
2780 // entry with the same parent. |
|
2781 // 2) This is an MHTML entry whose parent is a MULTIPART/ALTERNATIVE |
|
2782 // folder. In this case, this entry is the MHTML alternative to a |
|
2783 // text entry with the same parent. |
|
2784 // 3) This is an MHTML entry whose parent is MESSAGE folder. In this |
|
2785 // case, the message is a simple MHTML message with no text |
|
2786 // alternative or embedded image. |
|
2787 // 4) This is an MHTML entry whose parent is a MULTIPART/RELATED folder. |
|
2788 // In this case, this entry is the MHTML for the message. |
|
2789 // 5) This is an MHTML entry whose parent is a MULTIPART/MIXED folder. |
|
2790 // In this case, this entry is the MHTML for the message. It cannot |
|
2791 // be the attachment it self as then it would be of type attachment. |
|
2792 // Therefore, an entry is only an attachment if is of type attachment and |
|
2793 // its parent is not a MULTIPART/RELATED folder. |
|
2794 if( message.iType==KUidMsvAttachmentEntry && parentFolderType != EFolderTypeRelated ) |
|
2795 { |
|
2796 ++aAttachments; |
|
2797 } |
|
2798 // if it is related we might want to include it if the message |
|
2799 // turns out not to be MHTML |
|
2800 else if ( message.iType==KUidMsvAttachmentEntry && |
|
2801 parentFolderType == EFolderTypeRelated ) |
|
2802 { |
|
2803 ++aRelatedAttachments; |
|
2804 } |
|
2805 } |
|
2806 |
|
2807 DBG((LogText(_L8("BuildTreeOneL done: created id %x, attachments so far %d"), message.Id(), aAttachments))); |
|
2808 } |
|
2809 |
|
2810 // Build attachment tree below a message |
|
2811 void CImImap4Session::BuildTreeL(TMsvId aParent, CImapAtom *aAtom, const TDesC8& aPath, |
|
2812 const TMsvId aThisMessage, TInt& aAttachments, TBool& aIsMHTML, TInt& aRelatedAttachments) |
|
2813 { |
|
2814 DBG((LogText(_L8("BuildTreeL(message=%x, parent=%x"),aThisMessage,aParent))); |
|
2815 |
|
2816 // One attachment only? |
|
2817 if (aAtom->Child()==NULL) |
|
2818 { |
|
2819 // Deal with the single entry (doesn't use AllocL) |
|
2820 HBufC8* newpath=HBufC8::NewLC(aPath.Length()+4); |
|
2821 *newpath=aPath; |
|
2822 if (aPath.Length()) |
|
2823 newpath->Des().Append(_L8(".")); |
|
2824 newpath->Des().AppendNum(1); |
|
2825 BuildTreeOneL(aParent,aAtom,newpath->Des(),aThisMessage,aAttachments,aIsMHTML, aRelatedAttachments); |
|
2826 CleanupStack::PopAndDestroy(); |
|
2827 } |
|
2828 else |
|
2829 { |
|
2830 // Nest down a level: create a folder |
|
2831 SetEntryL(aParent); |
|
2832 TMsvEmailEntry message; |
|
2833 message.iMtm=KUidMsgTypeIMAP4; |
|
2834 message.iServiceId=iServiceId; |
|
2835 message.iType=KUidMsvFolderEntry; |
|
2836 message.iSize=0; |
|
2837 message.SetComplete(EFalse); |
|
2838 User::LeaveIfError(iEntry->CreateEntryBulk(message)); |
|
2839 |
|
2840 DBG((LogText(_L8("Created attachment folder id %x as child of %x"),message.Id(),aParent))); |
|
2841 |
|
2842 aParent=message.Id(); |
|
2843 |
|
2844 // CC:Mail server doesn't respond to BODYSTRUCTURE correctly: |
|
2845 // it gives the same response as FETCH BODY, ie it doesn't have |
|
2846 // all the extended MIME stuff. |
|
2847 // Skip to the last 4 atoms: this is the multipart type & stuff |
|
2848 CImapAtom *multipart=aAtom; |
|
2849 while(multipart && multipart->Child()!=NULL) |
|
2850 multipart=multipart->Next(); |
|
2851 |
|
2852 // Got anything? |
|
2853 if (multipart) |
|
2854 { |
|
2855 // Parse multipart type string, do this first so |
|
2856 // information is available when parsing children |
|
2857 TImEmailFolderType ft=EFolderTypeUnknown; |
|
2858 if (multipart->Compare(KImcvRelated)) |
|
2859 ft=EFolderTypeRelated; |
|
2860 if (multipart->Compare(KImcvMixed)) |
|
2861 ft=EFolderTypeMixed; |
|
2862 if (multipart->Compare(KImcvParallel)) |
|
2863 ft=EFolderTypeParallel; |
|
2864 if (multipart->Compare(KImcvAlternative)) |
|
2865 ft=EFolderTypeAlternative; |
|
2866 if (multipart->Compare(KImcvDigest)) |
|
2867 ft=EFolderTypeDigest; |
|
2868 |
|
2869 SetEntryL(aParent); |
|
2870 |
|
2871 // ...and save it |
|
2872 TMsvEmailEntry folder=iEntry->Entry(); |
|
2873 folder.SetMessageFolderType(ft); |
|
2874 #if SET_RELATED_ID |
|
2875 // DS - Set message's iRelatedId to messageId to allow later UI kludges |
|
2876 folder.iRelatedId=folder.Id(); |
|
2877 #endif |
|
2878 ChangeEntryBulkL(folder); |
|
2879 |
|
2880 // Process the multipart object |
|
2881 TInt subnr=1; |
|
2882 while(aAtom && aAtom!=multipart) |
|
2883 { |
|
2884 // Tag or child? |
|
2885 if (aAtom->Child()) |
|
2886 { |
|
2887 // Process item (doesn't use AllocL) |
|
2888 HBufC8* newpath=HBufC8::NewLC(aPath.Length()+4); |
|
2889 *newpath=aPath; |
|
2890 if (aPath.Length()) |
|
2891 newpath->Des().Append(_L8(".")); |
|
2892 newpath->Des().AppendNum(subnr++); |
|
2893 BuildTreeOneL(aParent,aAtom->ToChildL(),newpath->Des(), |
|
2894 aThisMessage,aAttachments,aIsMHTML, aRelatedAttachments); |
|
2895 CleanupStack::PopAndDestroy(); |
|
2896 } |
|
2897 |
|
2898 // Next item |
|
2899 aAtom=aAtom->Next(); |
|
2900 } |
|
2901 } |
|
2902 } |
|
2903 } |
|
2904 |
|
2905 |
|
2906 // convert text from its charset and write to richtext store. aText |
|
2907 // can span multiple and partial lines |
|
2908 void CImImap4Session::WriteToBodyL(const TDesC8& aText) |
|
2909 { |
|
2910 TInt pos = iMessageBody->DocumentLength(); |
|
2911 |
|
2912 // Add bits of body text, converting along the way, till no characters left |
|
2913 // .. to convert. |
|
2914 |
|
2915 // Convert text before writing to body. |
|
2916 TInt rem = 0; |
|
2917 |
|
2918 // there will be a max of one output char per input byte |
|
2919 HBufC16* text16=HBufC16::NewLC(aText.Length()); |
|
2920 TPtr16 ptr16=text16->Des(); |
|
2921 |
|
2922 if (!iPreparedToConvert) |
|
2923 { |
|
2924 ptr16.Copy(aText); |
|
2925 iMessageBody->InsertL(pos, ptr16); |
|
2926 } |
|
2927 else |
|
2928 { |
|
2929 TInt unconvertedChars, firstPos; // not used |
|
2930 rem = iCharConv->ConvertToOurCharsetL(aText, ptr16, |
|
2931 unconvertedChars, firstPos); |
|
2932 if (rem < 0) // error |
|
2933 { |
|
2934 // Copy unconverted characters. |
|
2935 ptr16.Copy(aText); |
|
2936 iMessageBody->InsertL(pos, ptr16); |
|
2937 } |
|
2938 else if (rem && rem < iLeftOver.MaxLength()) |
|
2939 iLeftOver.Copy(aText.Right(rem)); |
|
2940 |
|
2941 // convert CRLF to ELineBreak |
|
2942 TInt start = 0; |
|
2943 TInt length = ptr16.Length(); |
|
2944 TInt i; |
|
2945 for (i=1; i<length; i++) |
|
2946 { |
|
2947 if (ptr16[i-1] == KImcvCR && ptr16[i] == KImcvLF) |
|
2948 { |
|
2949 ptr16[i-1] = CEditableText::ELineBreak; |
|
2950 |
|
2951 // write this line to body |
|
2952 TPtrC ptr = ptr16.Mid(start, i-start); |
|
2953 iMessageBody->InsertL(pos, ptr); |
|
2954 pos += ptr.Length(); |
|
2955 start = i+1; |
|
2956 } |
|
2957 } |
|
2958 |
|
2959 if (start != i) |
|
2960 { |
|
2961 TPtrC ptr = ptr16.Mid(start, i-start); |
|
2962 iMessageBody->InsertL(pos, ptr); |
|
2963 } |
|
2964 } |
|
2965 |
|
2966 CleanupStack::PopAndDestroy(); // text16 |
|
2967 } |
|
2968 |
|
2969 // convert text from its charset and write to file, return error code |
|
2970 // from write |
|
2971 TInt CImImap4Session::WriteToAttachmentL(const TDesC8& aText) |
|
2972 { |
|
2973 TInt error; |
|
2974 |
|
2975 // Convert text before writing to attachment. |
|
2976 TInt rem = 0; |
|
2977 |
|
2978 // there will be a max of one output char per input byte |
|
2979 HBufC16* text16=HBufC16::NewLC(aText.Length()); |
|
2980 TPtr16 ptr16=text16->Des(); |
|
2981 |
|
2982 if (!iPreparedToConvert) |
|
2983 { |
|
2984 if(iCaf->Processing()) |
|
2985 { |
|
2986 error = iCaf->WriteData(aText); |
|
2987 } |
|
2988 else |
|
2989 { |
|
2990 error = iAttachmentFile->WriteFile(aText); |
|
2991 } |
|
2992 } |
|
2993 else |
|
2994 { |
|
2995 TInt unconvertedChars, firstPos; // not used |
|
2996 rem = iCharConv->ConvertToOurCharsetL(aText, ptr16, |
|
2997 unconvertedChars, firstPos); |
|
2998 if (rem < 0) // error |
|
2999 { |
|
3000 ptr16.Copy(aText); // Copy unconverted characters. |
|
3001 } |
|
3002 else if (rem && rem < iLeftOver.MaxLength()) |
|
3003 { |
|
3004 // any remainder is due to partial code sequence not lack of space |
|
3005 iLeftOver.Copy(aText.Right(rem)); |
|
3006 } |
|
3007 |
|
3008 TPtrC8 text8((TUint8*) text16->Des().Ptr(), text16->Des().Size()); |
|
3009 if(iCaf->Processing()) |
|
3010 { |
|
3011 error = iCaf->WriteData(text8); |
|
3012 } |
|
3013 else |
|
3014 { |
|
3015 error = iAttachmentFile->WriteFile(text8); |
|
3016 } |
|
3017 } |
|
3018 |
|
3019 CleanupStack::PopAndDestroy(); // text16 |
|
3020 |
|
3021 return error; |
|
3022 } |
|
3023 |
|
3024 // Copied and adapted this function from the one in CImRecvConvert |
|
3025 TBool CImImap4Session::CheckUUEStartL(const TDesC8& aSourceLine) |
|
3026 { |
|
3027 // Checks if the descriptor contains the UUE begin header |
|
3028 // Extracts the file name if it is |
|
3029 |
|
3030 TInt sourceLength = aSourceLine.Length(); |
|
3031 if(sourceLength < KImcvUueStart().Length()+3) // can't be "begin ###", it's not long enough; 3=length of ### |
|
3032 return EFalse; |
|
3033 |
|
3034 if(!aSourceLine.Left(KImcvUueStart().Length()).CompareF(KImcvUueStart)) // start of line might be UUE boundary |
|
3035 { |
|
3036 // we also need to check that the next three chars are numbers - Unix file access code |
|
3037 const TUint8* sourceLinePtr = aSourceLine.Ptr(); |
|
3038 TInt length=KImcvUueStart().Length();// this defines length as 6 ie. "b e g i n " |
|
3039 if( TChar(sourceLinePtr[length]).IsDigit() && |
|
3040 TChar(sourceLinePtr[length+1]).IsDigit() && |
|
3041 TChar(sourceLinePtr[length+2]).IsDigit() ) |
|
3042 { |
|
3043 // Found 'begin ###' at the start of a line - assume this is a UUencode header |
|
3044 // The attachment name in this header is ignored. We use the value from the MIME header |
|
3045 return ETrue; |
|
3046 } |
|
3047 } |
|
3048 |
|
3049 return EFalse; |
|
3050 } |
|
3051 |
|
3052 |
|
3053 // Decode and store received data |
|
3054 void CImImap4Session::DecodeAndStoreL(const TPtrC8& aBodyData, const TBool aEndOfStream) |
|
3055 { |
|
3056 DBG((LogText(_L8("DecodeAndStore(%d bytes, endofstream=%d, encoding=%d, iLeftOver=%d)"),aBodyData.Length(),aEndOfStream,iEncodingType,iLeftOver.Length()))); |
|
3057 |
|
3058 // Somewhere to store decoded data, at least as long as source (plus anything we have left |
|
3059 // in the partial line buffer which may now get consumed) |
|
3060 TInt outputbuffersize=aBodyData.Length()+4; |
|
3061 if (iPartialLine) |
|
3062 outputbuffersize+=iPartialLine->Des().Length(); |
|
3063 |
|
3064 HBufC8* decoded=HBufC8::NewLC(outputbuffersize); |
|
3065 TPtr8 decoded_ptr=decoded->Des(); |
|
3066 |
|
3067 // Bump progress: bytesdone is *encoded* length, so we just use the encoded length |
|
3068 iProgress.iBytesDone+=aBodyData.Length(); |
|
3069 // Which decoder are we using? |
|
3070 switch(iEncodingType) |
|
3071 { |
|
3072 case EEncodingTypeNone: |
|
3073 case EEncodingType7Bit: |
|
3074 case EEncodingType8Bit: |
|
3075 case EEncodingTypeBinary: |
|
3076 case EEncodingTypeUnknown: |
|
3077 // Nothing to do, just copy data |
|
3078 decoded->Des().Append(aBodyData); |
|
3079 break; |
|
3080 |
|
3081 case EEncodingTypeBASE64: |
|
3082 // Decode Base64 data: just filter it through decoder, it |
|
3083 // ignores line breaks anyway. |
|
3084 iB64Decoder.Decode(aBodyData,decoded_ptr); |
|
3085 break; |
|
3086 |
|
3087 case EEncodingTypeUU: |
|
3088 { |
|
3089 TPtrC8 bodydata=aBodyData; |
|
3090 |
|
3091 // Got a partial buffer? |
|
3092 if (!iPartialLine) |
|
3093 { |
|
3094 // Allocate buffer |
|
3095 iPartialLine=HBufC8::NewL(KUuDecodedLineLength); |
|
3096 iUUDecoding = EFalse; |
|
3097 } |
|
3098 |
|
3099 // Decode UUEncoded data: line by line |
|
3100 TBool decodeEnded = EFalse; |
|
3101 TInt position=0; |
|
3102 while ( bodydata.Length() && !decodeEnded ) |
|
3103 { |
|
3104 // Find() returns the start of "\r\n". The decoding algorithm |
|
3105 // requires that the encoded line contains the "\r\n". |
|
3106 TInt lineEnd = bodydata.Find( _L8("\r\n") ); |
|
3107 if (lineEnd != KErrNotFound) |
|
3108 { |
|
3109 lineEnd = lineEnd + 2; |
|
3110 AppendExtendL( &iPartialLine, bodydata.Left( lineEnd ), EFalse); |
|
3111 |
|
3112 bodydata.Set( bodydata.Mid( lineEnd ) ); |
|
3113 |
|
3114 // Check for a well-formated begin-tag |
|
3115 if ( CheckUUEStartL( iPartialLine->Des() ) ) |
|
3116 { |
|
3117 iUUDecoding = ETrue; |
|
3118 } |
|
3119 else if ( iPartialLine->Compare( KImcvUueEnd ) != 0 && iUUDecoding ) |
|
3120 { |
|
3121 // Every malformatted string is decoded as an empty string |
|
3122 // with length 0. Appending such a string is harmless. |
|
3123 TPtr8 destination((unsigned char*)decoded_ptr.Ptr()+position,0,outputbuffersize-position); |
|
3124 iUUDecoder.Decode(*iPartialLine,destination); |
|
3125 position+=destination.Length(); |
|
3126 } |
|
3127 else if ( iUUDecoding ) |
|
3128 { |
|
3129 decodeEnded = ETrue; |
|
3130 iUUDecoding = EFalse; |
|
3131 } |
|
3132 |
|
3133 iPartialLine->Des().Zero(); |
|
3134 } |
|
3135 else |
|
3136 { |
|
3137 AppendExtendL( &iPartialLine, bodydata, EFalse); |
|
3138 |
|
3139 // advance to end of bodydata |
|
3140 bodydata.Set(bodydata.Ptr()+bodydata.Length(), 0); |
|
3141 } |
|
3142 } |
|
3143 decoded->Des().SetLength(position); |
|
3144 break; |
|
3145 } |
|
3146 |
|
3147 case EEncodingTypeQP: |
|
3148 { |
|
3149 TPtrC8 bodydata=aBodyData; |
|
3150 |
|
3151 // Got a partial buffer? |
|
3152 if (!iPartialLine) |
|
3153 { |
|
3154 // Allocate buffer |
|
3155 iPartialLine=HBufC8::NewL(256); |
|
3156 } |
|
3157 |
|
3158 // Build buffer to decode: basically, QP decoder wants CRLF terminated |
|
3159 // lines, so we build them in the iPartialLine buffer. There may be |
|
3160 // stuff already there from previous data packet - so we just append. |
|
3161 TInt position=0; |
|
3162 while(bodydata.Length()) |
|
3163 { |
|
3164 // Find a line break |
|
3165 TInt lineend=bodydata.Find(_L8("\r\n")); |
|
3166 |
|
3167 // No break? |
|
3168 if (lineend==KErrNotFound && !aEndOfStream) |
|
3169 { |
|
3170 // Stick it all in the partialline buffer, we should get a CRLF |
|
3171 // soon... |
|
3172 AppendExtendL( &iPartialLine,bodydata, EFalse); |
|
3173 break; |
|
3174 } |
|
3175 else |
|
3176 { |
|
3177 if (lineend==KErrNotFound) |
|
3178 { |
|
3179 // Append whole thing left to buffer |
|
3180 AppendExtendL( &iPartialLine,bodydata, EFalse); |
|
3181 |
|
3182 // advance to end of bodydata |
|
3183 bodydata.Set(bodydata.Ptr()+bodydata.Length(), 0); |
|
3184 } |
|
3185 else |
|
3186 { |
|
3187 // Append to buffer up to that point (including the \r\n) |
|
3188 AppendExtendL( &iPartialLine,bodydata.Left(lineend+2), EFalse); |
|
3189 |
|
3190 // Remove from the buffer we're working on (including the \r\n) |
|
3191 bodydata.Set(bodydata.Ptr()+lineend+2,bodydata.Length()-lineend-2); |
|
3192 } |
|
3193 |
|
3194 // Decode & skip on in buffer |
|
3195 TPtr8 destination((unsigned char*)decoded_ptr.Ptr()+position,0,outputbuffersize-position); |
|
3196 iQPDecoder.Decode(*iPartialLine,destination); |
|
3197 position+=destination.Length(); |
|
3198 iPartialLine->Des().Zero(); |
|
3199 } |
|
3200 } |
|
3201 |
|
3202 // Update decoded |
|
3203 decoded->Des().SetLength(position); |
|
3204 break; |
|
3205 } |
|
3206 } |
|
3207 |
|
3208 // put back any partially converted data |
|
3209 if (iLeftOver.Length()) |
|
3210 { |
|
3211 decoded->Des().Insert(0, iLeftOver); |
|
3212 iLeftOver.SetLength(0); |
|
3213 } |
|
3214 |
|
3215 // What format is it? TEXT/* we put into a richtext thingy, otherwise just |
|
3216 // stream it to store |
|
3217 if (iFetchIsText) |
|
3218 { |
|
3219 if(aEndOfStream && (iMessageBody || iBodyBuf) && iBodyPartRemainingSize) |
|
3220 { |
|
3221 CleanupStack::Pop();// decoded |
|
3222 TInt newSize = decoded->Size() + iFooterString->Size(); |
|
3223 decoded = decoded->ReAlloc(newSize); |
|
3224 CleanupStack::PushL(decoded); |
|
3225 decoded->Des().Append(*iFooterString); |
|
3226 delete iFooterString; |
|
3227 iFooterString = NULL; |
|
3228 } |
|
3229 // Got somewhere to put it? Store it! |
|
3230 if (iStore8BitData) |
|
3231 { |
|
3232 if (decoded->Length() && iBodyBuf) |
|
3233 iBodyBuf->InsertL(iBodyBuf->Size(), *decoded); |
|
3234 } |
|
3235 else |
|
3236 { |
|
3237 if (decoded->Length() && iMessageBody) |
|
3238 WriteToBodyL(decoded->Des()); |
|
3239 } |
|
3240 |
|
3241 |
|
3242 // Got the whole thing buffered? |
|
3243 if (aEndOfStream && (iMessageBody || iBodyBuf)) |
|
3244 { |
|
3245 DBG((LogText(_L8("Doing StoreBodyTextL()")))); |
|
3246 |
|
3247 // The whole message is built in iMessageBody or iBodyBuf. Store it. |
|
3248 SetEntryL(iMessageId); |
|
3249 CMsvStore *entryStore=iEntry->EditStoreL(); |
|
3250 CleanupStack::PushL(entryStore); |
|
3251 if (iStore8BitData) |
|
3252 iBodyText->StoreL(*entryStore, *iBodyBuf); |
|
3253 else |
|
3254 entryStore->StoreBodyTextL(*iMessageBody); |
|
3255 entryStore->CommitL(); |
|
3256 CleanupStack::PopAndDestroy(); |
|
3257 |
|
3258 // Get rid of body copy, etc |
|
3259 delete iBodyBuf; |
|
3260 iBodyBuf = NULL; |
|
3261 delete iMessageBody; |
|
3262 iMessageBody=NULL; |
|
3263 } |
|
3264 } |
|
3265 else |
|
3266 { |
|
3267 // Select the entry |
|
3268 SetEntryL(iMessageId); |
|
3269 |
|
3270 // Save it direct to store |
|
3271 if (iAttachmentFileState==EFileNotOpen) |
|
3272 { |
|
3273 // Get and set Attachment File path |
|
3274 TFileName filepath; |
|
3275 |
|
3276 // Retrieving the attachment name from an earlier saved one |
|
3277 // If it's a CAF interested file then this will get overidden |
|
3278 CMsvStore* store = iEntry->ReadStoreL(); |
|
3279 CleanupStack::PushL(store); |
|
3280 MMsvAttachmentManager& attachmentMgr = store->AttachmentManagerL(); |
|
3281 if(attachmentMgr.AttachmentCount()) |
|
3282 { |
|
3283 // get the file path |
|
3284 CMsvAttachment* attachment = attachmentMgr.GetAttachmentInfoL(0); |
|
3285 CleanupStack::PushL(attachment); |
|
3286 filepath = attachment->FilePath(); |
|
3287 CleanupStack::PopAndDestroy(attachment); |
|
3288 } |
|
3289 |
|
3290 if (iAttachmentFullPath) |
|
3291 { |
|
3292 delete iAttachmentFullPath; |
|
3293 iAttachmentFullPath=NULL; |
|
3294 } |
|
3295 if(attachmentMgr.AttachmentCount()) |
|
3296 { |
|
3297 TParse fileParser; |
|
3298 User::LeaveIfError(fileParser.Set(filepath, NULL, NULL)); |
|
3299 iAttachmentFullPath=fileParser.DriveAndPath().AllocL(); |
|
3300 } |
|
3301 // We've already extracted the attachment file name in |
|
3302 // BuildTree so just copy it out of details |
|
3303 iAttachmentName=iEntry->Entry().iDetails; |
|
3304 if(attachmentMgr.AttachmentCount()) |
|
3305 { |
|
3306 DBG((LogText(_L8("name '%S', '%S'"),iAttachmentFullPath,&iAttachmentName))); |
|
3307 } |
|
3308 if (!iAttachmentFile) |
|
3309 iAttachmentFile=new (ELeave) TImAttachmentFile(iFs); |
|
3310 CleanupStack::PopAndDestroy(store); // store opened above |
|
3311 store = iEntry->EditStoreL(); |
|
3312 CleanupStack::PushL(store); |
|
3313 // Could be multiple attachments in the folder. |
|
3314 TInt attachmentCount = store->AttachmentManagerL().AttachmentCount(); |
|
3315 for(TInt i=0;i<attachmentCount;i++) |
|
3316 { |
|
3317 // Remove [0] as array is shuffled. Once index [n] is removed n+1 becomes n |
|
3318 store->AttachmentManagerExtensionsL().RemoveAttachmentL(0); |
|
3319 } |
|
3320 if(attachmentCount) |
|
3321 store->CommitL(); |
|
3322 |
|
3323 // Now create the attachment entry |
|
3324 CMsvAttachment* attachment = CMsvAttachment::NewL(CMsvAttachment::EMsvFile); |
|
3325 CleanupStack::PushL(attachment); |
|
3326 attachment->SetAttachmentNameL(iAttachmentName); |
|
3327 |
|
3328 // Need to create the MIME-type information - first get the MIME headers |
|
3329 CImMimeHeader* mimeHeaders = CImMimeHeader::NewLC(); |
|
3330 mimeHeaders->RestoreL(*store); |
|
3331 |
|
3332 HBufC8* buf = HBufC8::NewLC(mimeHeaders->ContentSubType().Length() + mimeHeaders->ContentType().Length() + 1); |
|
3333 TPtr8 ptr(buf->Des()); |
|
3334 ptr.Copy(mimeHeaders->ContentType()); |
|
3335 ptr.Append(KImcvForwardSlash); |
|
3336 ptr.Append(mimeHeaders->ContentSubType()); |
|
3337 |
|
3338 attachment->SetMimeTypeL(ptr); |
|
3339 |
|
3340 CleanupStack::PopAndDestroy(2, mimeHeaders); |
|
3341 |
|
3342 |
|
3343 RFile file; |
|
3344 if(iCaf->Registered()) |
|
3345 { |
|
3346 iCaf->PrepareProcessingL(); // Init the CAF import file session |
|
3347 RFile startFile; |
|
3348 TFileName suggestedFileName; |
|
3349 if(iCaf->GetSuggestedAttachmentFileName(suggestedFileName) == KErrNone) // CAF agent may provide a filename |
|
3350 { |
|
3351 store->CreateShareProtectedAttachmentL(suggestedFileName,startFile,attachment); |
|
3352 } |
|
3353 else |
|
3354 { |
|
3355 store->CreateShareProtectedAttachmentL(iAttachmentName,startFile,attachment); |
|
3356 } |
|
3357 iCaf->StartProcessing(iDefaultAttachmentName->Des(),attachment->FilePath(),*iEntry,startFile); // Init the CAF session |
|
3358 startFile.Close(); |
|
3359 } |
|
3360 else |
|
3361 { |
|
3362 // Normal behaviour |
|
3363 store->AttachmentManagerExtensionsL().CreateAttachmentL(iAttachmentName,file,attachment); |
|
3364 iAttachmentFile->SetFileHandle(file,TImAttachmentFile::EImFileWrite); |
|
3365 } |
|
3366 |
|
3367 // CreateAttachmentL takes ownership of CMsvAttachment so if call was successful we can pop it here |
|
3368 CleanupStack::Pop(attachment); |
|
3369 |
|
3370 iAttachmentFileState = EFileIsOpen; |
|
3371 store->CommitL(); |
|
3372 CleanupStack::PopAndDestroy(store); |
|
3373 |
|
3374 if (iAttachmentFileState!=EFileIsOpen) |
|
3375 { |
|
3376 DBG((LogText(_L8("Couldn't open file!")))); |
|
3377 } |
|
3378 } |
|
3379 |
|
3380 if (iAttachmentFileState==EFileIsOpen && decoded->Length()) |
|
3381 { |
|
3382 // write decoded data into a file if there is any data there to write |
|
3383 TInt error=WriteToAttachmentL(decoded->Des()); |
|
3384 |
|
3385 if (error!=KErrNone) |
|
3386 { |
|
3387 // the file write failed, (eg.there is no space left set new file state |
|
3388 // and skip any remaining encoded data in message |
|
3389 iAttachmentFileState=EFileIsIncomplete; |
|
3390 |
|
3391 DBG((LogText(_L8("Failed to write %d bytes to attachment file (error=%d): deleting it"),decoded->Length(),error))); |
|
3392 if(iCaf->Processing()) |
|
3393 { |
|
3394 iCaf->EndProcessingL(); |
|
3395 } |
|
3396 else |
|
3397 { |
|
3398 iAttachmentFile->CloseFile(); |
|
3399 } |
|
3400 |
|
3401 CMsvStore* store = iEntry->EditStoreL(); |
|
3402 CleanupStack::PushL(store); |
|
3403 // Could be multiple attachments in the folder. |
|
3404 TInt i; |
|
3405 TInt attachmentCount = store->AttachmentManagerL().AttachmentCount(); |
|
3406 for(i=0;i<attachmentCount;i++) |
|
3407 { |
|
3408 // Remove [0] as array is shuffled. Once index [n] is removed n+1 becomes n |
|
3409 store->AttachmentManagerExtensionsL().RemoveAttachmentL(0); |
|
3410 } |
|
3411 if(attachmentCount) |
|
3412 store->CommitL(); |
|
3413 CleanupStack::PopAndDestroy(store); |
|
3414 TMsvEmailEntry message=iEntry->Entry(); |
|
3415 message.SetAttachment(EFalse); |
|
3416 ChangeEntryBulkL(message); |
|
3417 |
|
3418 // Leave with the error |
|
3419 User::Leave(error); |
|
3420 } |
|
3421 else |
|
3422 { |
|
3423 DBG((LogText(_L8("Written %d bytes to attachment file"),decoded->Length()))); |
|
3424 } |
|
3425 } |
|
3426 |
|
3427 // Finished? |
|
3428 if ((aEndOfStream) && (iAttachmentFileState == EFileIsOpen)) |
|
3429 { |
|
3430 DBG((LogText(_L8("Closing attachment file")))); |
|
3431 if(iCaf->Processing()) |
|
3432 { |
|
3433 iCaf->EndProcessingL(); |
|
3434 } |
|
3435 else |
|
3436 { |
|
3437 iAttachmentFile->CloseFile(); |
|
3438 } |
|
3439 iAttachmentFileState=EFileNotOpen; |
|
3440 } |
|
3441 } |
|
3442 |
|
3443 // Free memory |
|
3444 CleanupStack::PopAndDestroy(); |
|
3445 } |
|
3446 |
|
3447 // Given that aId has become complete see if we can propagate the |
|
3448 // Complete state and partial fetch state flag up |
|
3449 void CImImap4Session::PropagateCompleteFlagL(TMsvId aId, TBool aDoBodyText,TBool aPartialFetched) |
|
3450 { |
|
3451 CMsvEntrySelection* selection=new (ELeave) CMsvEntrySelection; |
|
3452 CleanupStack::PushL(selection); |
|
3453 |
|
3454 // get the siblings of this id |
|
3455 SetEntryL(aId); |
|
3456 TMsvId parent = iEntry->Entry().Parent(); |
|
3457 |
|
3458 // finish if we've managed to reach the top |
|
3459 if (parent == KMsvRootIndexEntryId) |
|
3460 return; |
|
3461 |
|
3462 SetEntryL(parent); |
|
3463 |
|
3464 // finish if we've reached a service |
|
3465 if (iEntry->Entry().iType == KUidMsvServiceEntry) |
|
3466 return; |
|
3467 |
|
3468 GetChildrenL(*selection); |
|
3469 |
|
3470 TBool complete=ETrue; |
|
3471 TBool bodyTextComplete=ETrue; |
|
3472 TBool partiallyFetched=EFalse; |
|
3473 |
|
3474 TBool related=((TMsvEmailEntry) iEntry->Entry()).MessageFolderType()==EFolderTypeRelated ? |
|
3475 ETrue:EFalse; |
|
3476 for (TInt i=0; i < selection->Count(); i++) |
|
3477 { |
|
3478 SetEntryL((*selection)[i]); |
|
3479 if (!iEntry->Entry().Complete()) |
|
3480 { |
|
3481 complete=EFalse; |
|
3482 if((iEntry->Entry().iType==KUidMsvFolderEntry) && aPartialFetched) |
|
3483 complete=ETrue; |
|
3484 // The current part is not complete so... |
|
3485 // if it is either a text part or a HTML part then the body |
|
3486 // text is marked as being incomplete. |
|
3487 // |
|
3488 // This code means that, if present, then both the text/plain |
|
3489 // and text/html alternatives need to be downloaded before |
|
3490 // the body text is marked as being complete. |
|
3491 if ((iEntry->Entry().iType == KUidMsvEmailTextEntry) |
|
3492 || (iEntry->Entry().iType == KUidMsvEmailHtmlEntry ) || related ) |
|
3493 { |
|
3494 if(aPartialFetched) |
|
3495 { |
|
3496 complete = ETrue; |
|
3497 bodyTextComplete=ETrue; |
|
3498 } |
|
3499 else |
|
3500 bodyTextComplete=EFalse; |
|
3501 } |
|
3502 |
|
3503 break; |
|
3504 } |
|
3505 } |
|
3506 |
|
3507 CleanupStack::PopAndDestroy(); // selection |
|
3508 |
|
3509 // if all the siblings were complete then make the parent |
|
3510 // complete and continue up. |
|
3511 if (complete || ((aDoBodyText || related) && bodyTextComplete)) |
|
3512 { |
|
3513 SetEntryL(parent); |
|
3514 TMsvEmailEntry entry = iEntry->Entry(); |
|
3515 |
|
3516 // check whether parent is complete, this wil prevent us |
|
3517 // checking all the messages in a real folder as they will all |
|
3518 // be initialised to Complete |
|
3519 if (!entry.Complete()) |
|
3520 { |
|
3521 if (complete || ((iEntry->Entry().iType==KUidMsvFolderEntry) && aPartialFetched)) |
|
3522 entry.SetComplete(ETrue); |
|
3523 if(aPartialFetched) |
|
3524 { |
|
3525 if((iEntry->Entry().iType != KUidMsvAttachmentEntry) && |
|
3526 (iEntry->Entry().iType != KUidMsvEmailExternalBodyEntry)) |
|
3527 { |
|
3528 entry.SetPartialDownloaded(ETrue); |
|
3529 } |
|
3530 partiallyFetched = ETrue; |
|
3531 } |
|
3532 else |
|
3533 { |
|
3534 entry.SetPartialDownloaded(EFalse); |
|
3535 partiallyFetched = EFalse; |
|
3536 } |
|
3537 entry.SetBodyTextComplete(ETrue); |
|
3538 ChangeEntryL(entry); |
|
3539 |
|
3540 PropagateCompleteFlagL(parent, related|aDoBodyText,partiallyFetched); |
|
3541 } |
|
3542 else if (entry.PartialDownloaded()) |
|
3543 { |
|
3544 entry.SetPartialDownloaded(EFalse); |
|
3545 ChangeEntryL(entry); |
|
3546 PropagateCompleteFlagL(parent, related|aDoBodyText,partiallyFetched); |
|
3547 } |
|
3548 } |
|
3549 } |
|
3550 |
|
3551 void CImImap4Session::CreateAttachmentInfoL(TMsvEmailEntry& aMsvEmailEntry) |
|
3552 { |
|
3553 // create an empty attachment to store the attachment infomation, for the case |
|
3554 // where the attachment is not downloaded due to download limits. |
|
3555 CMsvStore* store = iEntry->EditStoreL(); |
|
3556 CleanupStack::PushL(store); |
|
3557 |
|
3558 MMsvAttachmentManager& attachmentMgr = store->AttachmentManagerL(); |
|
3559 |
|
3560 // Check to see if this entry already has an attachment - if so, then don't |
|
3561 // add it again! |
|
3562 if( attachmentMgr.AttachmentCount() == 0 ) |
|
3563 { |
|
3564 MMsvAttachmentManagerSync& attachmentMgrSync = store->AttachmentManagerExtensionsL(); |
|
3565 |
|
3566 // Now create the attachment entry |
|
3567 CMsvAttachment* attachment = CMsvAttachment::NewL(CMsvAttachment::EMsvFile); |
|
3568 CleanupStack::PushL(attachment); |
|
3569 |
|
3570 // Need to create the MIME-type information - first get the MIME headers |
|
3571 CImMimeHeader* mimeHeaders = CImMimeHeader::NewLC(); |
|
3572 mimeHeaders->RestoreL(*store); |
|
3573 |
|
3574 HBufC8* buf = HBufC8::NewLC(mimeHeaders->ContentSubType().Length() + mimeHeaders->ContentType().Length() + 1); |
|
3575 TPtr8 ptr(buf->Des()); |
|
3576 ptr.Copy(mimeHeaders->ContentType()); |
|
3577 ptr.Append(KImcvForwardSlash); |
|
3578 ptr.Append(mimeHeaders->ContentSubType()); |
|
3579 |
|
3580 attachment->SetMimeTypeL(ptr); |
|
3581 |
|
3582 CleanupStack::PopAndDestroy(2, mimeHeaders); |
|
3583 |
|
3584 attachment->SetComplete(EFalse); |
|
3585 attachment->SetSize(aMsvEmailEntry.iSize); |
|
3586 attachment->SetAttachmentNameL(aMsvEmailEntry.iDetails); |
|
3587 RFile file; |
|
3588 attachmentMgrSync.CreateAttachmentL(aMsvEmailEntry.iDetails,file,attachment); |
|
3589 CleanupStack::Pop(attachment); // ownership passed to attachment manager |
|
3590 file.Close(); |
|
3591 store->CommitL(); |
|
3592 } |
|
3593 CleanupStack::PopAndDestroy(store); |
|
3594 } |
|
3595 |
|
3596 |
|
3597 // Parse fetch messages |
|
3598 TInt CImImap4Session::ProcessFetchL(const TUint aMsgnr, CImapAtom *aAtom) |
|
3599 { |
|
3600 CImapAtom *p=aAtom; |
|
3601 CImapAtom *attribute; |
|
3602 CImapAtom *structure=NULL; |
|
3603 CImapAtom *flags=NULL; |
|
3604 CImapAtom *bodydata=NULL; |
|
3605 CImapAtom *header=NULL; |
|
3606 TInt error=KErrNotReady; |
|
3607 TInt rfc822size=0; |
|
3608 TBool foundUnwantedMimeHeader=EFalse; |
|
3609 TBool wholeMessage=EFalse; |
|
3610 TInt fetchSizeBytes = static_cast<TInt>(iServiceSettings->FetchSize()); |
|
3611 |
|
3612 // Fetch data consists of attribute/value pairs |
|
3613 while(p!=NULL) |
|
3614 { |
|
3615 // Get attribute & value |
|
3616 attribute=p; |
|
3617 // broken servers can give us just an attribute rather than an |
|
3618 // attribute pair, if so then just finish the scan here and |
|
3619 // process what we've got |
|
3620 p=p->Next(); |
|
3621 if (p==NULL) |
|
3622 break; |
|
3623 |
|
3624 // Work on attributes |
|
3625 if (attribute->Compare(KIMAP_UID)) |
|
3626 { |
|
3627 iFoundUid = ETrue; |
|
3628 |
|
3629 // Lex it ok? |
|
3630 if (p->Value(iMessageUid)!=KErrNone) |
|
3631 User::Leave(KErrArgument); |
|
3632 |
|
3633 // Skip to next attribute |
|
3634 p=p->Next(); |
|
3635 } |
|
3636 |
|
3637 else if (attribute->Compare(KIMAP_BODY)) |
|
3638 { |
|
3639 // some example responses |
|
3640 |
|
3641 // expected without extra header fields |
|
3642 // * 1 FETCH (UID 1 BODY[2]<0> {1024} |
|
3643 |
|
3644 // expected with extra (empty) header fields |
|
3645 // * 2 FETCH (UID 35 BODY[1.HEADER.FIELDS ("CONTENT-BASE" "CONTENT-LOCATION")] "" BODY[1]<0> {60} |
|
3646 |
|
3647 // unwanted BODYSTRUCTURE info |
|
3648 // * 3 FETCH (UID 2 BODY ("text" "plain" ("CHARSET" "us-ascii") NIL NIL "7bit" 4337 67) BODY[1]<0> {1024} |
|
3649 |
|
3650 // unwanted nested BODYSTRUCTURE info |
|
3651 // * 4 FETCH (UID 1 BODY (("text" "plain" ("CHARSET" "us-ascii") NIL NIL "7bit" 1346 53) |
|
3652 // ("text" "plain" ("NAME" "install.ins") NIL NIL "7bit" 5156 215) "MIXED") |
|
3653 // BODY[2]<0> {1024} |
|
3654 |
|
3655 // unwanted BODYSTRUCTURE info and complete message |
|
3656 // * 5 FETCH (UID 2 BODY ("text" "plain" ("CHARSET" "us-ascii") NIL NIL "7bit" 4337 67) BODY[1] {1550} |
|
3657 |
|
3658 // Body part is across & down one (it's in []) |
|
3659 CImapAtom* bodypart=p->ToChildL(); |
|
3660 |
|
3661 if (bodypart->CompareTail(KIMAP_HEADERFIELDS)) |
|
3662 { |
|
3663 // Got BODY[HEADER.FIELDS ("header1" "header2" ...)] {LEN} data ... |
|
3664 // or BODY[<part>.HEADER.FIELDS ("header1" "header2" ...)] {LEN} data ... |
|
3665 header=p->ToNextL(); |
|
3666 #ifdef PRINTING |
|
3667 TPtrC8 bpt=bodypart->Next()->Child()->Atom(); |
|
3668 LogText(_L8("Found header fields (%S...)"),&bpt); |
|
3669 #endif |
|
3670 // Skip to next attribute |
|
3671 p=header->Next(); |
|
3672 } |
|
3673 else if (bodypart->CompareTail(KIMAP_MIME)) |
|
3674 { |
|
3675 // Got BODY[MIME] {LEN} data ... |
|
3676 // or BODY[<part>.MIME] {LEN} data ... |
|
3677 header=p->ToNextL(); |
|
3678 |
|
3679 DBG((LogText(_L8("Found MIME header fields")))); |
|
3680 |
|
3681 // Skip to next attribute |
|
3682 p=header->Next(); |
|
3683 } |
|
3684 else if (bodypart->Child() != NULL || bodypart->Next() != NULL) |
|
3685 { |
|
3686 // we've been unexpectedly returned a BODY () response |
|
3687 // which we don't want, so just skip it |
|
3688 DBG((LogText(_L8("Unexpected BODY response, ignoring")))); |
|
3689 p=p->Next(); |
|
3690 } |
|
3691 else |
|
3692 { |
|
3693 // is body data, ie BODY[part]<offset> or BODY[part] |
|
3694 |
|
3695 // Offset is next atom |
|
3696 CImapAtom* offset=p->ToNextL(); |
|
3697 |
|
3698 // there may not be an offset in which case offset is |
|
3699 // actually bodydata |
|
3700 |
|
3701 // Get the offset |
|
3702 TUint offsetn=0; |
|
3703 // see if this is an offset |
|
3704 if( offset->Child() != NULL ) |
|
3705 { |
|
3706 TLex8 lex(offset->Child()->Atom()); |
|
3707 if( lex.Val(offsetn)!=KErrNone ) |
|
3708 { |
|
3709 error=KErrGeneral; |
|
3710 break; |
|
3711 } |
|
3712 |
|
3713 // Body data is next atom |
|
3714 bodydata=offset->Next(); |
|
3715 } |
|
3716 else |
|
3717 { |
|
3718 // if not an offset then this is not a partial |
|
3719 // message |
|
3720 bodydata=offset; |
|
3721 wholeMessage=ETrue; |
|
3722 } |
|
3723 |
|
3724 // Additional code added to address additional unrequested non-RFC data sent by the |
|
3725 // imap server caused problems in downloading the email and any attachments. |
|
3726 // Upto this point the BODY tag has correctly been interpreted. |
|
3727 // |
|
3728 // We may however have an unwanted MIME header data which we will need to process or get rid of. |
|
3729 // If we have an unwanted mime header then we currently have the bodydata atom storing: |
|
3730 // |
|
3731 // BODY[x.MIME]<offset> |
|
3732 // MIME_HEADER_INFO |
|
3733 // BODY_DATA |
|
3734 // |
|
3735 // or |
|
3736 // |
|
3737 // BODY.PEEK[x.MIME]<offset> |
|
3738 // BODY_DATA |
|
3739 // |
|
3740 // The problem here is that the BODY or BODY.PEEK tag will be read as the actual body data. |
|
3741 // We need to move the bodydata pointer to the beginning of the MIME_HEADER_INFO. |
|
3742 // When decoding the information we will send the data after the header info to be decoded. |
|
3743 |
|
3744 CImapAtom* nextbodypart = NULL; |
|
3745 CImapAtom* mimetest =NULL; |
|
3746 |
|
3747 // Check if the unwanted body tag is "BODY" or "BODY.PEEK" |
|
3748 if((bodydata->Compare(KIMAP_BODY)) || (bodydata->Compare(KIMAP_BODYPEEK))) //we have an extra body tag |
|
3749 { |
|
3750 //find out if it is MIME |
|
3751 nextbodypart=bodydata->Next(); |
|
3752 if(nextbodypart) |
|
3753 { |
|
3754 mimetest = nextbodypart->Child(); |
|
3755 if(mimetest) |
|
3756 { |
|
3757 if (mimetest->CompareTail(KIMAP_MIME)) |
|
3758 { |
|
3759 |
|
3760 // Unrequested tag is "BODY.PEEK" |
|
3761 if((bodydata->Compare(KIMAP_BODYPEEK))) |
|
3762 { |
|
3763 LogText(_L8("UNEXPECTED RESPONSE: TAG \"BODY.PEEK\"- Found unwanted additional \"BODY.PEEK\" tag in the FETCH response")); |
|
3764 LogText(_L8("PROCESSING UNEXPECTED RESPONSE: TAG \"BODY.PEEK\" - Additional MIME header data NOT expected to PREFIX the bodypart - unlike additional \"BODY\" tag")); |
|
3765 // Not expecting additional / unwanted mime header info at the beginning of the body of the message part |
|
3766 // Hence, no additional processing required on body data |
|
3767 foundUnwantedMimeHeader=EFalse; |
|
3768 } |
|
3769 else // Unrequested tag is "BODY" |
|
3770 { |
|
3771 LogText(_L8("UNEXPECTED RESPONSE: TAG \"BODY\" Found unwanted additional \"BODY\" tag in the FETCH response")); |
|
3772 LogText(_L8("PROCESSING UNEXPECTED RESPONSE: TAG \"BODY\"- Expecting UNWANTED MIME HEADER DATA prefixing the BODY DATA of the message part")); |
|
3773 // Expecting additional / unwanted mime header info at the beginning of the body data of the message part. |
|
3774 // Hence, the atom pointed to by bodydata pointer will be parsed / truncated appropriately to extract just |
|
3775 // the bodypart later. |
|
3776 foundUnwantedMimeHeader=ETrue; |
|
3777 } |
|
3778 //we may have an offset that we need to ignore |
|
3779 CImapAtom* possOffset = nextbodypart->Next(); |
|
3780 TLex8 lex(possOffset->Atom()); |
|
3781 if (lex.Get()=='<') //has an offset |
|
3782 { |
|
3783 // Body data is next atom |
|
3784 bodydata=possOffset->Next(); |
|
3785 } |
|
3786 else |
|
3787 { |
|
3788 bodydata=possOffset; |
|
3789 } |
|
3790 } |
|
3791 } |
|
3792 } |
|
3793 }//end of code addressing |
|
3794 |
|
3795 |
|
3796 #ifdef PRINTING |
|
3797 TPtrC8 bpt=bodypart->Atom(); |
|
3798 LogText(_L8("Found body part [%S] iSizeWait %d"),&bpt,iSizeWait); |
|
3799 #endif |
|
3800 if (iSizeWait && bodydata!=NULL) |
|
3801 { |
|
3802 // No longer waiting for the size |
|
3803 iSizeWait=EFalse; |
|
3804 |
|
3805 // Size of this part |
|
3806 TUint sizen=bodydata->Atom().Length(); |
|
3807 |
|
3808 DBG((LogText(_L8(" offset=%d, length=%d"),offsetn,sizen))); |
|
3809 TInt fetchSize = fetchSizeBytes; |
|
3810 |
|
3811 // In CC:Mail workaround mode? |
|
3812 if (iTalkingToCCMail || iTalkingToOpenMail) |
|
3813 { |
|
3814 // How much message is there left to fetch? |
|
3815 TInt sizeleft=iSizeOfThisPart-(offsetn+sizen); |
|
3816 |
|
3817 if (sizeleft>0) |
|
3818 { |
|
3819 if( iState != EImapStateFetchCancelWait ) |
|
3820 { |
|
3821 // Limit chunk size |
|
3822 if(iFetchPartialMail) |
|
3823 { |
|
3824 fetchSize = GetFetchSizeL(sizeleft,offsetn+sizen); |
|
3825 if(fetchSize > fetchSizeBytes ) |
|
3826 { |
|
3827 fetchSize = fetchSizeBytes; |
|
3828 } |
|
3829 } |
|
3830 else |
|
3831 { |
|
3832 if (sizeleft>fetchSizeBytes) |
|
3833 { |
|
3834 fetchSize=fetchSizeBytes; |
|
3835 } |
|
3836 } |
|
3837 // Issue new fetch command |
|
3838 NewTag(); |
|
3839 TPtrC8 bp(bodypart->Atom()); |
|
3840 if (iServiceSettings->UpdatingSeenFlags()) |
|
3841 { |
|
3842 iImapIO->SendL(iStatus,KImapFetchBodyPeek, |
|
3843 iTag,iMessageFetching,&bp,(offsetn+sizen),sizeleft); |
|
3844 } |
|
3845 else |
|
3846 { |
|
3847 iImapIO->SendL(iStatus,KImapFetchBody, |
|
3848 iTag,iMessageFetching,&bp,(offsetn+sizen),sizeleft); |
|
3849 } |
|
3850 |
|
3851 NewTagSent(); |
|
3852 |
|
3853 // Get the rest of this line uninterrupted |
|
3854 error=KErrWrite; |
|
3855 } |
|
3856 } |
|
3857 else |
|
3858 { |
|
3859 // Got the whole message |
|
3860 TInt sizeleft=iSizeOfThisPart-(offsetn+sizen); |
|
3861 if(iFetchPartialMail && (sizeleft || !iHtmlEntryPart)) |
|
3862 { |
|
3863 ProcessFooterMessageL(sizeleft); |
|
3864 } |
|
3865 } |
|
3866 } |
|
3867 else |
|
3868 { |
|
3869 // Anything more to get? We decide this on wether we got near to |
|
3870 // our requested packet size on the last fetch: if we were within |
|
3871 // 100 bytes of the requested size, we ask for another load just |
|
3872 // in case the server is serving us line by line. Otherwise, we |
|
3873 // assume that was the end of the data and flush it out. |
|
3874 |
|
3875 // Check whether we have downloaded the message completely or not, |
|
3876 // before sending the FETCH command again. |
|
3877 iSizeLeftToFetch = iSizeOfThisPart-(offsetn+sizen); |
|
3878 if ((fetchSizeBytes-sizen)<100 && iSizeLeftToFetch>0) |
|
3879 { |
|
3880 if( iState != EImapStateFetchCancelWait ) |
|
3881 { |
|
3882 TInt sizeleft=iSizeOfThisPart-(offsetn+sizen); |
|
3883 fetchSize=sizeleft; |
|
3884 if(iFetchPartialMail) |
|
3885 { |
|
3886 fetchSize = GetFetchSizeL(sizeleft,offsetn+sizen); |
|
3887 } |
|
3888 if(fetchSize > fetchSizeBytes) |
|
3889 { |
|
3890 fetchSize = fetchSizeBytes; |
|
3891 } |
|
3892 // Yes, issue a new fetch command |
|
3893 NewTag(); |
|
3894 TPtrC8 bp(bodypart->Atom()); |
|
3895 if (iServiceSettings->UpdatingSeenFlags()) |
|
3896 { |
|
3897 iImapIO->SendL(iStatus,KImapFetchBodyPeek, |
|
3898 iTag,iMessageFetching,&bp,(offsetn+sizen),fetchSize); |
|
3899 |
|
3900 } |
|
3901 else |
|
3902 { |
|
3903 iImapIO->SendL(iStatus,KImapFetchBody, |
|
3904 iTag,iMessageFetching,&bp,(offsetn+sizen),fetchSize); |
|
3905 } |
|
3906 NewTagSent(); |
|
3907 |
|
3908 // Get the rest of this line uninterrupted |
|
3909 error=KErrWrite; |
|
3910 } |
|
3911 } |
|
3912 else |
|
3913 { |
|
3914 // Got the whole message |
|
3915 TInt sizeleft = iSizeOfThisPart-(offsetn+sizen); |
|
3916 |
|
3917 if(iFetchPartialMail && (sizeleft || !iHtmlEntryPart)) |
|
3918 { |
|
3919 ProcessFooterMessageL(sizeleft); |
|
3920 } |
|
3921 } |
|
3922 } |
|
3923 } |
|
3924 |
|
3925 // Finish processing here if we didn't get everything |
|
3926 if (!iGotWholeLine) |
|
3927 break; |
|
3928 |
|
3929 // Skip to next attribute |
|
3930 p=bodydata->Next(); |
|
3931 } |
|
3932 } |
|
3933 else if (attribute->Compare(KIMAP_BODYSTRUCTURE)) |
|
3934 { |
|
3935 // Body structure: Save it until later when we have created |
|
3936 // the message - then we can create the attachment tree |
|
3937 // underneath it |
|
3938 structure=p->ToChildL(); |
|
3939 |
|
3940 // Skip to next attribute |
|
3941 p=p->Next(); |
|
3942 } |
|
3943 else if (attribute->Compare(KIMAP_FLAGS)) |
|
3944 { |
|
3945 // Process flag list later |
|
3946 flags=p; |
|
3947 |
|
3948 // Skip to next attribute |
|
3949 p=p->Next(); |
|
3950 } |
|
3951 else if (attribute->Compare(KIMAP_RFC822SIZE)) |
|
3952 { |
|
3953 // Save total message size |
|
3954 if (p->Value(rfc822size)!=KErrNone) |
|
3955 User::Leave(KErrGeneral); |
|
3956 |
|
3957 // Skip to next attribute |
|
3958 p=p->Next(); |
|
3959 } |
|
3960 |
|
3961 else |
|
3962 { |
|
3963 #ifdef PRINTING |
|
3964 TPtrC8 att=attribute->Atom(); |
|
3965 LogText(_L8("Unknown attribute '%S'"),&att); |
|
3966 //showtree(attribute,0); |
|
3967 #endif |
|
3968 } |
|
3969 } |
|
3970 |
|
3971 DBG((LogText(_L8("About to process, error=%d, iGotWholeLine=%d, iSyncState=%d, bodydata=%x, wholeMessage=%d"), |
|
3972 error,iGotWholeLine,iSyncState,(int)bodydata,wholeMessage))); |
|
3973 |
|
3974 // No error? |
|
3975 if ((error==KErrNotReady || error==KErrWrite) && iGotWholeLine) |
|
3976 { |
|
3977 // check to see if uid is present in server message. If not present, we are getting a |
|
3978 // message flag update |
|
3979 if(!iFoundUid) |
|
3980 { |
|
3981 #ifdef PRINTING |
|
3982 DBG((LogText(_L8("UID not present in Fetch, so process flags")))); |
|
3983 #endif |
|
3984 |
|
3985 // this is just a message flag update |
|
3986 |
|
3987 TInt msgnr = aMsgnr; |
|
3988 |
|
3989 // if the aMsgnr index into the local message array points to a valid message entry |
|
3990 if((msgnr < iFolderIndex.Size()) && (iFolderIndex[aMsgnr-1].iMsvId != 0)) |
|
3991 { |
|
3992 |
|
3993 #ifdef PRINTING |
|
3994 for(TInt i=0; i<iFolderIndex.Size(); i++) |
|
3995 { |
|
3996 DBG((LogText(_L8("iFolderIndex[%d].iMsvId=%d"), i, iFolderIndex[i].iMsvId))); |
|
3997 } |
|
3998 #endif |
|
3999 // Mirror flags and ensure that the message is visible |
|
4000 // as it might have been made invisible by |
|
4001 // unsubscribing. |
|
4002 |
|
4003 // set the current entry to be the message pointed to by aMsgnr |
|
4004 SetEntryL(iFolderIndex[aMsgnr - 1].iMsvId); |
|
4005 |
|
4006 #ifdef PRINTING |
|
4007 DBG((LogText(_L8("SetEntry for aMsgnr: %d with msvid %d"), aMsgnr-1, iFolderIndex[aMsgnr-1].iMsvId))); |
|
4008 #endif |
|
4009 |
|
4010 TMsvEmailEntry message=iEntry->Entry(); |
|
4011 |
|
4012 // since there is no uid associated with this server response, we just need to update flags |
|
4013 if (ProcessFlagsL(flags,message)|| !message.Visible()) |
|
4014 { |
|
4015 message.SetVisible(ETrue); |
|
4016 ChangeEntryL(message); |
|
4017 } |
|
4018 |
|
4019 #ifdef PRINTING |
|
4020 DBG((LogText(_L8("check for deleted imap 4 flags")))); |
|
4021 #endif |
|
4022 |
|
4023 if (message.DeletedIMAP4Flag()) |
|
4024 { |
|
4025 iRemoteMessagesDeleteTagged++; |
|
4026 } |
|
4027 } |
|
4028 |
|
4029 return(KErrNotReady); |
|
4030 } |
|
4031 |
|
4032 // What synchronisation state are we in? |
|
4033 switch(iSyncState) |
|
4034 { |
|
4035 case ENotSyncing: |
|
4036 // Ignore it |
|
4037 break; |
|
4038 |
|
4039 case EFetching: |
|
4040 { |
|
4041 if (header) |
|
4042 { |
|
4043 ProcessHeaderExtraL(NULL,iAttachmentMimeInfo,NULL,header->Atom()); |
|
4044 |
|
4045 // Store CImMimeHeader info |
|
4046 SetEntryL(iMessageId); |
|
4047 CMsvStore* entryStore=iEntry->EditStoreL(); |
|
4048 CleanupStack::PushL(entryStore); |
|
4049 iAttachmentMimeInfo->StoreL(*entryStore); |
|
4050 entryStore->CommitL(); |
|
4051 CleanupStack::PopAndDestroy(entryStore); |
|
4052 } |
|
4053 |
|
4054 // Now, decode the body data and store it: completion can't be |
|
4055 // indicated by 'complete' as we may have been called with a partial line, |
|
4056 // so we just rely on the same 'early a full buffer' indicator as |
|
4057 // we do when issuing the pipelined fetches above |
|
4058 |
|
4059 TBool endOfStream = EFalse; |
|
4060 if (iTalkingToCCMail || iTalkingToOpenMail) |
|
4061 { |
|
4062 // As we'll never get the 0 byte terminating read with CC:mail, we have to use |
|
4063 // our own definition of "end of file", which is simply a packet smaller than |
|
4064 // the maximum fetch size |
|
4065 if (bodydata) |
|
4066 { |
|
4067 endOfStream=bodydata->Atom().Length()!=fetchSizeBytes; |
|
4068 DecodeAndStoreL(bodydata->Atom(),endOfStream); |
|
4069 } |
|
4070 } |
|
4071 else |
|
4072 { |
|
4073 // Here, we'll treat anything 100 or more bytes shy of the maximum packet size as |
|
4074 // end of file. If we're closer than that, a new read will have been issued which will |
|
4075 // return 0 bytes, which *will* cause the EOF to be signalled. |
|
4076 if (bodydata) |
|
4077 { |
|
4078 endOfStream=wholeMessage || (!((fetchSizeBytes-bodydata->Atom().Length())<100) || (iSizeLeftToFetch == 0)); |
|
4079 |
|
4080 // depending on whether or not we found an unwanted mime header we need to get rid of it. |
|
4081 // This is done in response to the imap server issues where the server was sending additional information. |
|
4082 // At this point we know how big the extra header is and we can calculate the correct size of the bodydata by subtracting the |
|
4083 // size of the header. |
|
4084 if(foundUnwantedMimeHeader) |
|
4085 { |
|
4086 foundUnwantedMimeHeader=EFalse; |
|
4087 //calculate size of actual bodydata without the additional header |
|
4088 TInt length = bodydata->Atom().Length() - header->Atom().Length(); |
|
4089 //call decode and store, but only pass in the data we are interested in i.e.: skip the additional header. |
|
4090 DecodeAndStoreL(bodydata->Atom().Right(length),endOfStream); |
|
4091 } |
|
4092 else //bodydata only points to the data |
|
4093 { |
|
4094 DecodeAndStoreL(bodydata->Atom(),endOfStream); |
|
4095 } |
|
4096 } |
|
4097 } |
|
4098 |
|
4099 // Update flags on message: it should have been marked as read if it wasn't |
|
4100 // already |
|
4101 if (flags || endOfStream && ( iState != EImapStateFetchCancelWait )) |
|
4102 { |
|
4103 // SJM 19990922: Previously this only set the Unread |
|
4104 // flag in a rather inefficient way. Change to use new |
|
4105 // return value of ProcessFlags |
|
4106 SetEntryL(iMessageId); |
|
4107 TMsvEmailEntry message=iEntry->Entry(); |
|
4108 |
|
4109 TBool hasBodyText = message.iType == KUidMsvEmailTextEntry || message.iType == KUidMsvEmailHtmlEntry; |
|
4110 TBool partiallyDownloaded = EFalse; |
|
4111 if (endOfStream) |
|
4112 { |
|
4113 message.SetComplete(ETrue); |
|
4114 if(iFetchPartialMail && iBodyPartRemainingSize && message.iType == KUidMsvEmailTextEntry) |
|
4115 { |
|
4116 message.SetPartialDownloaded(ETrue); |
|
4117 partiallyDownloaded = ETrue; |
|
4118 } |
|
4119 else |
|
4120 { |
|
4121 message.SetPartialDownloaded(EFalse); |
|
4122 } |
|
4123 |
|
4124 if (hasBodyText) |
|
4125 message.SetBodyTextComplete(ETrue); |
|
4126 } |
|
4127 |
|
4128 // Process flags in this fetch response |
|
4129 TBool changed = EFalse; |
|
4130 if (flags) |
|
4131 changed = ProcessFlagsL(flags,message); |
|
4132 |
|
4133 if (changed || endOfStream) |
|
4134 ChangeEntryBulkL(message); |
|
4135 |
|
4136 if (endOfStream) |
|
4137 { |
|
4138 //iMessagePartsFetchOK++; |
|
4139 PropagateCompleteFlagL(iMessageId, hasBodyText, partiallyDownloaded); |
|
4140 } |
|
4141 } |
|
4142 break; |
|
4143 } |
|
4144 |
|
4145 case ESyncListNew: |
|
4146 case ESyncOld: |
|
4147 // Got a message's details |
|
4148 |
|
4149 // check folder position is not out of bounds and that we have not run out of the local index. |
|
4150 if (iFolderPosition >= iFolderIndex.Size()) |
|
4151 { |
|
4152 // All done/ array was out of bounds |
|
4153 DBG((LogText(_L8("ERROR - Position %d was out of bounds for the FolderIndex arrays size (%d)"),iFolderPosition,iFolderIndex.Size()))); |
|
4154 iSyncState = ENotSyncing; |
|
4155 break; |
|
4156 } |
|
4157 |
|
4158 // Collecting UIDs of messages in the folder |
|
4159 DBG((LogText(_L8("At pos %d, expecting UID %u, got UID %u"), |
|
4160 iFolderPosition,iFolderIndex[iFolderPosition].iUid,iMessageUid))); |
|
4161 |
|
4162 // Messages deleted from remote mailbox? |
|
4163 while(iFolderPosition<iFolderIndex.Size() && |
|
4164 iMessageUid>iFolderIndex[iFolderPosition].iUid) |
|
4165 { |
|
4166 // Orphan this message |
|
4167 DBG((LogText(_L8("Orphaning UID %u"),iFolderIndex[iFolderPosition].iUid))); |
|
4168 |
|
4169 if (iFolderIndex[iFolderPosition].iUid != KIllegalUID) |
|
4170 { |
|
4171 // Do it |
|
4172 OrphanMessageL(iFolderIndex[iFolderPosition].iMsvId); |
|
4173 } |
|
4174 else |
|
4175 { |
|
4176 DBG((LogText(_L8("Illegal UID, do not delete.")))); |
|
4177 } |
|
4178 |
|
4179 // Remove it from the index |
|
4180 iFolderIndex.Expunge(iFolderPosition+1); |
|
4181 |
|
4182 // Increment stats |
|
4183 iOrphanedMessages++; |
|
4184 } |
|
4185 |
|
4186 // Run out of local index? |
|
4187 if (iFolderPosition>=iFolderIndex.Size()) |
|
4188 { |
|
4189 // All done |
|
4190 iSyncState=ENotSyncing; |
|
4191 } |
|
4192 // In sync again? |
|
4193 else if (iMessageUid==iFolderIndex[iFolderPosition].iUid) |
|
4194 { |
|
4195 // Fine, mirror flag information onto local message |
|
4196 DBG((LogText(_L8("Match UID %u, mirroring flags"),iMessageUid))); |
|
4197 |
|
4198 // Mirror flags and ensure that the message is visible |
|
4199 // as it might have been made invisible by |
|
4200 // unsubscribing. |
|
4201 SetEntryL(iFolderIndex[iFolderPosition].iMsvId); |
|
4202 TMsvEmailEntry message=iEntry->Entry(); |
|
4203 if (ProcessFlagsL(flags,message)|| !message.Visible()) |
|
4204 { |
|
4205 message.SetVisible(ETrue); |
|
4206 ChangeEntryBulkL(message); |
|
4207 } |
|
4208 |
|
4209 if (message.DeletedIMAP4Flag()) |
|
4210 iRemoteMessagesDeleteTagged++; |
|
4211 |
|
4212 // Next message |
|
4213 iFolderPosition++; |
|
4214 |
|
4215 // Update counters. |
|
4216 iMsgsDone++; |
|
4217 iHeadersFetched++; |
|
4218 } |
|
4219 else if (iMessageUid<iHighestUid) |
|
4220 { |
|
4221 // If we are seeing uids below the oldest mirrored message, then it is likely |
|
4222 // that the sync limit has changed. The following code captures the range of |
|
4223 // "missing" messages. |
|
4224 if (iMessageUid<iMissingUidLow || iMissingUidLow==0) |
|
4225 iMissingUidLow=iMessageUid; |
|
4226 if (iMessageUid>iMissingUidHigh || iMissingUidHigh==0) |
|
4227 iMissingUidHigh=iMessageUid; |
|
4228 } |
|
4229 break; |
|
4230 |
|
4231 case EGettingStructure: |
|
4232 { |
|
4233 error = KErrNone; |
|
4234 User::LeaveIfError(iEntry->SetEntry(iGetPart)); |
|
4235 |
|
4236 if (!(iEntry->Entry().Owner())) |
|
4237 { |
|
4238 TMsvEmailEntry entry = iEntry->Entry(); |
|
4239 |
|
4240 //reset flag for new message |
|
4241 iParsedTime=EFalse; |
|
4242 //initialise the time-stamp to the current UTC time |
|
4243 entry.iDate.UniversalTime(); |
|
4244 |
|
4245 // Make a CImHeader to populate with the data |
|
4246 CImHeader *messageheader=CImHeader::NewLC(); |
|
4247 if (header) |
|
4248 ProcessHeaderExtraL(messageheader,NULL,&entry,header->Atom()); |
|
4249 |
|
4250 // Set correct 'remote size' in CImHeader |
|
4251 messageheader->SetRemoteSize(rfc822size); |
|
4252 |
|
4253 // Create a message store. |
|
4254 CMsvStore* entryStore=iEntry->EditStoreL(); |
|
4255 CleanupStack::PushL(entryStore); |
|
4256 |
|
4257 // Store the RFC822 header information. |
|
4258 messageheader->StoreL(*entryStore); |
|
4259 entryStore->CommitL(); |
|
4260 CleanupStack::PopAndDestroy(entryStore); |
|
4261 |
|
4262 TInt attachments=0; |
|
4263 TInt relatedAttachments=0; |
|
4264 TBool isMHTML=EFalse; |
|
4265 iDecodedSizeOfAllParts = 0; |
|
4266 |
|
4267 // Create the message entry structure under the root message |
|
4268 BuildTreeL(entry.Id(),structure,_L8(""),entry.Id(),attachments,isMHTML,relatedAttachments); |
|
4269 if(isMHTML==EFalse) |
|
4270 attachments+=relatedAttachments; |
|
4271 |
|
4272 // Now that the structure has been created we can set the real message attributes. |
|
4273 // The MHTML, attachment flags and size were estimated (hopefully correctly) when the envelope was downloaded. |
|
4274 entry.iSize = iDecodedSizeOfAllParts; |
|
4275 entry.SetMHTMLEmail(isMHTML); |
|
4276 entry.SetAttachment(attachments); |
|
4277 entry.SetICalendar(iIsICalendar); |
|
4278 entry.SetVCalendar(iIsVCalendar); |
|
4279 |
|
4280 /* If IDLE is enabled for the account , a new session is not created, |
|
4281 so iIsICalendar,iIsVCalendar are not initialised to EFalse |
|
4282 when we fetch a new message, so do it HERE */ |
|
4283 |
|
4284 iIsICalendar = EFalse; |
|
4285 iIsVCalendar = EFalse; |
|
4286 |
|
4287 User::LeaveIfError(iEntry->SetEntry(entry.Id())); |
|
4288 User::LeaveIfError(iEntry->ChangeEntryBulk(entry)); |
|
4289 CleanupStack::PopAndDestroy(messageheader); |
|
4290 } |
|
4291 |
|
4292 if (ImapIdleSupported()==EFalse) |
|
4293 { |
|
4294 CancelDummy(); |
|
4295 } |
|
4296 |
|
4297 DoFetchL(); |
|
4298 iJustSentFetch = ETrue; |
|
4299 } |
|
4300 break; |
|
4301 |
|
4302 case ESyncNew: |
|
4303 { |
|
4304 // Got a message's details |
|
4305 |
|
4306 // First, let's check we asked for it: for example, the UW server |
|
4307 // gives us messages not in the correct range! |
|
4308 if (iMessageUid<=iHighestUid) |
|
4309 { |
|
4310 DBG((LogText(_L8("Searching local messages for %d"),iMessageUid))); |
|
4311 while (iFolderPosition<iFolderIndex.Size()) |
|
4312 { |
|
4313 if (iMessageUid==iFolderIndex[iFolderPosition].iUid) |
|
4314 { |
|
4315 DBG((LogText(_L8("Got duplicate UID %d - ignoring"),iMessageUid))); |
|
4316 return(error); |
|
4317 } |
|
4318 else if (iFolderIndex[iFolderPosition].iUid>iMessageUid || iFolderIndex[iFolderPosition].iMsvId==-1) |
|
4319 break; |
|
4320 iFolderPosition++; |
|
4321 } |
|
4322 } |
|
4323 |
|
4324 // Update counters. |
|
4325 iMsgsDone++; |
|
4326 iHeadersFetched++; |
|
4327 |
|
4328 // Creating messages in current folder: create this one |
|
4329 SetEntryL(iMailboxId); |
|
4330 |
|
4331 // Check to see we have at least the minimum free disk space available |
|
4332 if (--iCheckDiskSpaceCounter <= 0) |
|
4333 { |
|
4334 // If we are running low on disk space then leave |
|
4335 ImCheckDiskSpace::LeaveIfLowDiskL(iFs, iCurrentDrive); |
|
4336 iCheckDiskSpaceCounter = KCheckDiskSpaceEveryNMessages; |
|
4337 } |
|
4338 |
|
4339 TMsvEmailEntry message; |
|
4340 message.iType=KUidMsvMessageEntry; |
|
4341 message.iMtm=KUidMsgTypeIMAP4; |
|
4342 message.iServiceId=iServiceId; |
|
4343 message.SetUID(iMessageUid); |
|
4344 message.SetValidUID(ETrue); |
|
4345 message.SetComplete(EFalse); |
|
4346 message.SetUnread(ETrue); |
|
4347 |
|
4348 //reset flag for new message |
|
4349 iParsedTime=EFalse; |
|
4350 //initialise the time-stamp to the current UTC time |
|
4351 message.iDate.UniversalTime(); |
|
4352 |
|
4353 // Process message flags |
|
4354 ProcessFlagsL(flags,message); |
|
4355 |
|
4356 if (message.DeletedIMAP4Flag()) |
|
4357 iRemoteMessagesDeleteTagged++; |
|
4358 |
|
4359 // Size of root message entry gets twiddled later |
|
4360 message.iSize=0; |
|
4361 |
|
4362 // Set new flag |
|
4363 message.SetNew(ETrue); |
|
4364 |
|
4365 // initialise the send state since the constructor sets it |
|
4366 // to StateUnknown |
|
4367 message.SetSendingState(KMsvSendStateNotApplicable); |
|
4368 |
|
4369 // Make a CImHeader to populate with the data |
|
4370 CImHeader *messageheader=CImHeader::NewLC(); |
|
4371 |
|
4372 if (header) |
|
4373 { |
|
4374 ProcessHeaderExtraL(messageheader,NULL,&message,header->Atom()); |
|
4375 } |
|
4376 |
|
4377 // Set correct 'remote size' in CImHeader |
|
4378 messageheader->SetRemoteSize(rfc822size); |
|
4379 |
|
4380 // Save message size & attachment flag |
|
4381 SetMessageFlagsL(message, structure); |
|
4382 |
|
4383 // Create message |
|
4384 User::LeaveIfError(iEntry->CreateEntryBulk(message)); |
|
4385 SetEntryL(iMessageId=message.Id()); |
|
4386 |
|
4387 |
|
4388 #if SET_RELATED_ID |
|
4389 // DS - Set message's iRelatedId to messageId to allow later UI kludges |
|
4390 message.iRelatedId=iMessageId; |
|
4391 #endif |
|
4392 ChangeEntryBulkL(message); |
|
4393 CleanupStack::PopAndDestroy(messageheader); |
|
4394 break; |
|
4395 } |
|
4396 default: |
|
4397 // ESyncListNew and ESyncSearch should not result in a FETCH reply. |
|
4398 __ASSERT_DEBUG(EFalse,gPanic(EUnknownState)); |
|
4399 break; |
|
4400 } |
|
4401 } |
|
4402 |
|
4403 return(error); |
|
4404 } |
|
4405 |
|
4406 // code originally from void CImRecvConvert::ParseRecipientListL(...) |
|
4407 void CImImap4Session::ProcessAddressListL(CDesCArray& aWhere, HBufC8** aAddresses) |
|
4408 { |
|
4409 TInt length((*aAddresses)->Length()); |
|
4410 HBufC8* pBuf=HBufC8::NewLC(length); |
|
4411 TPtrC8 source((*aAddresses)->Ptr(), length); |
|
4412 const TUint8* ptr(source.Ptr()); |
|
4413 const TUint8* lastCharPtr(ptr + source.Length() - 1); |
|
4414 TUint8 lookFor(0); |
|
4415 TInt count(0); |
|
4416 TBool finishedEntry(EFalse); |
|
4417 |
|
4418 // get past white space |
|
4419 while(*ptr&&((*ptr==KImcvSP)||(*ptr==KImcvSemiColon))) ptr++; |
|
4420 |
|
4421 // Entries are separated by commas or semicolons. |
|
4422 // Separators do not count if they appear within |
|
4423 // "", <>, () or embedded series of these, eg "(one, two)" |
|
4424 // so we need to keep track of these, including nesting. |
|
4425 while(*ptr && ptr <= lastCharPtr) |
|
4426 { |
|
4427 if(pBuf->Length()==0) |
|
4428 { |
|
4429 finishedEntry = EFalse; |
|
4430 } |
|
4431 |
|
4432 switch(*ptr) |
|
4433 { |
|
4434 case KImcvLeftBracket: |
|
4435 if(lookFor==KImcvRightBracket) |
|
4436 { // We've already had a "(", so now we need another one |
|
4437 count++; |
|
4438 } |
|
4439 else if(lookFor==0) |
|
4440 { //We weren't looking for anything else, now we need to |
|
4441 lookFor = KImcvRightBracket; |
|
4442 count = 1; |
|
4443 } |
|
4444 // else we were already looking for something else, ignore this |
|
4445 break; |
|
4446 case KImcvLeftChevron: |
|
4447 if(lookFor==KImcvRightChevron) |
|
4448 { //We've already had a "<", so now we need another one |
|
4449 count++; |
|
4450 } |
|
4451 else if(lookFor==0) |
|
4452 { //We weren't looking for anything else |
|
4453 lookFor = KImcvRightChevron; |
|
4454 count = 1; |
|
4455 } |
|
4456 // else we were already looking for something else, ignore this |
|
4457 break; |
|
4458 case KImcvDoubleQuote: |
|
4459 if(lookFor==KImcvDoubleQuote) |
|
4460 { // We already had a quote, so this matches it |
|
4461 lookFor = 0; |
|
4462 } |
|
4463 else if(lookFor==0) |
|
4464 { //We weren't looking for anything else |
|
4465 lookFor = KImcvDoubleQuote; |
|
4466 } |
|
4467 // else we were already looking for something else, ignore this |
|
4468 break; |
|
4469 case KImcvRightBracket: |
|
4470 case KImcvRightChevron: |
|
4471 if(*ptr == lookFor) |
|
4472 { //If we have found what we were looking for, decrease the count |
|
4473 count--; |
|
4474 if(count==0) |
|
4475 { // Got everything, now we're not looking for anything |
|
4476 lookFor = 0; |
|
4477 } |
|
4478 // else keep looking for the same thing again |
|
4479 } |
|
4480 // else we're looking for something else, ignore it |
|
4481 break; |
|
4482 case KImcvComma: |
|
4483 case KImcvSemiColon: |
|
4484 // If we're not looking for anything, we're finished |
|
4485 if (lookFor == 0) |
|
4486 finishedEntry = ETrue; |
|
4487 // else this comma or semicolon is part of a different token, ignore it |
|
4488 break; |
|
4489 } |
|
4490 |
|
4491 if(!finishedEntry) |
|
4492 { |
|
4493 pBuf->Des().Append((TChar)*ptr); |
|
4494 // move to the next character |
|
4495 ptr++; |
|
4496 } |
|
4497 else |
|
4498 { |
|
4499 // that's it! store the address away |
|
4500 #ifdef UNICODE |
|
4501 HBufC16* pBuf16 = HBufC16::NewLC(pBuf->Des().Length()); |
|
4502 pBuf16->Des().Copy(pBuf->Des()); |
|
4503 aWhere.AppendL( (HBufC16&) *pBuf16 ); |
|
4504 CleanupStack::PopAndDestroy(pBuf16); // pBuf16 |
|
4505 #else |
|
4506 aWhere.AppendL( *pBuf ); |
|
4507 #endif |
|
4508 pBuf->Des().SetLength(0); |
|
4509 finishedEntry = EFalse; //Ready for next entry |
|
4510 |
|
4511 // get past the separator |
|
4512 ptr++; |
|
4513 |
|
4514 // get past white space (& any other separators) |
|
4515 while(*ptr && (*ptr==KImcvSP || *ptr==KImcvTab || *ptr==KImcvComma || *ptr==KImcvSemiColon)) ptr++; |
|
4516 } |
|
4517 } |
|
4518 // catch the last name in the list |
|
4519 if (pBuf) |
|
4520 { |
|
4521 TInt recipientLength(pBuf->Length()); |
|
4522 if (recipientLength > 0) |
|
4523 { |
|
4524 #ifdef UNICODE |
|
4525 HBufC16* pBuf16 = HBufC16::NewLC(recipientLength); |
|
4526 pBuf16->Des().Copy(*pBuf); |
|
4527 aWhere.AppendL(*pBuf16); |
|
4528 CleanupStack::PopAndDestroy(pBuf16); // pBuf16 |
|
4529 #else |
|
4530 aWhere.AppendL( *pBuf ); |
|
4531 #endif |
|
4532 } |
|
4533 } |
|
4534 CleanupStack::PopAndDestroy(pBuf); // pBuf |
|
4535 } |
|
4536 |
|
4537 void CImImap4Session::ProcessFooterMessageL(TInt aSizeLeft) |
|
4538 { |
|
4539 TUid type = iEntry->Entry().iType; |
|
4540 if (type == KUidMsvEmailTextEntry) |
|
4541 { |
|
4542 if(iHtmlEntrySize) |
|
4543 { |
|
4544 iBodyPartRemainingSize = aSizeLeft + iHtmlEntrySize; |
|
4545 } |
|
4546 else |
|
4547 { |
|
4548 iBodyPartRemainingSize = aSizeLeft; |
|
4549 } |
|
4550 // Message has both text and html and if sizeleft = 0 , |
|
4551 // then there could be only html part left on server |
|
4552 // Message has only plain text, then footer message not required if sizeleft = 0 |
|
4553 if(iBodyPartRemainingSize) |
|
4554 AttachFooterInfoL(); |
|
4555 } |
|
4556 } |
|
4557 |
|
4558 void CImImap4Session::AttachFooterInfoL() |
|
4559 { |
|
4560 DBG((LogText(_L8("AttachFooterInfoL(): Footer Sting for this partially downloaded message")))); |
|
4561 RResourceFile resFile; |
|
4562 OpenResourceFileL(resFile,iFs); // NB leaves if file not found |
|
4563 const TInt KNoOfDigitsInMailSize = 10; // maximum length for no of digits for remaining body parts size |
|
4564 _LIT(KIntegerDirective,"%d"); |
|
4565 // make sure the resource file will be closed if anything goes wrong |
|
4566 // CloseResourceFile is declared in IMCMMAIN.H and defined in IMCMMAIN.CPP |
|
4567 TCleanupItem close(CloseResourceFile,&resFile); |
|
4568 CleanupStack::PushL(close); |
|
4569 |
|
4570 // Read the string for remaining mail size for footer |
|
4571 HBufC8* buf = NULL; |
|
4572 buf = resFile.AllocReadLC( PARTIAL_DOWNLOAD_FOOTER_MESSAGE ); |
|
4573 TResourceReader reader; |
|
4574 reader.SetBuffer(buf); |
|
4575 // Check if %d is not found in the resource string (To avoid problems due to localisation) |
|
4576 if(buf->Find((TDesC8&)KIntegerDirective) == KErrNotFound) |
|
4577 { |
|
4578 iFooterString = (reader.ReadTPtrC()).AllocL(); |
|
4579 } |
|
4580 else |
|
4581 { |
|
4582 HBufC* resourceBuf = (reader.ReadTPtrC()).AllocL(); |
|
4583 iFooterString = HBufC::NewL(resourceBuf->Length()+KNoOfDigitsInMailSize); |
|
4584 iFooterString->Des().Format(*resourceBuf,(iBodyPartRemainingSize / KKiloByteSize)); |
|
4585 delete resourceBuf; |
|
4586 } |
|
4587 CleanupStack::PopAndDestroy(2); // buf, resFile (Close resfile) |
|
4588 } |
|
4589 |
|
4590 // Set the following attributes of the given message entry for the IMAP information contained in atom tree: |
|
4591 // iSize |
|
4592 // |
|
4593 void CImImap4Session::SetMessageFlagsL(TMsvEmailEntry& aMessageEntry, CImapAtom* aRootAtom) |
|
4594 { |
|
4595 CArrayFixFlat<CImapAtom*>* atomStack = new (ELeave) CArrayFixFlat<CImapAtom*>(10); |
|
4596 CleanupStack::PushL(atomStack); |
|
4597 atomStack->AppendL(aRootAtom); |
|
4598 |
|
4599 TBool hasAttachments = EFalse; |
|
4600 TBool hasHtml = EFalse; |
|
4601 TBool possibleHtml = EFalse; |
|
4602 TBool afterRelated = EFalse; |
|
4603 TBool afterAlternative = EFalse; |
|
4604 TBool htmlAfterAltRel = EFalse; |
|
4605 TBool hasICalendar = EFalse; |
|
4606 TBool hasVCalendar = EFalse; |
|
4607 TInt size = 0; |
|
4608 |
|
4609 CImapAtom* currentAtom; |
|
4610 |
|
4611 TBool doneAllSiblings; |
|
4612 TBool base64; |
|
4613 TInt index; |
|
4614 TBool numeric; |
|
4615 TInt atomValue; |
|
4616 TBool foundSize; |
|
4617 |
|
4618 // the root atom is of these types, the message body is an attachment |
|
4619 if(aRootAtom->Compare(KMIME_IMAGE) || |
|
4620 aRootAtom->Compare(KMIME_AUDIO) || |
|
4621 aRootAtom->Compare(KMIME_APPLICATION)|| |
|
4622 aRootAtom->Compare(KMIME_VIDEO)) |
|
4623 { |
|
4624 hasAttachments = ETrue; |
|
4625 } |
|
4626 |
|
4627 while (atomStack->Count() != 0) |
|
4628 { |
|
4629 // Pop the top atom off of the stack |
|
4630 currentAtom = (*atomStack)[atomStack->Count() - 1]; |
|
4631 atomStack->ResizeL(atomStack->Count() - 1); |
|
4632 base64 = EFalse; |
|
4633 |
|
4634 // Run through all the sibling atoms unless this atom is a message/rfc822 |
|
4635 if( currentAtom->Compare(KMIME_MESSAGE) && currentAtom->Next()->Compare(KMIME_RFC822) ) |
|
4636 doneAllSiblings = ETrue; |
|
4637 else |
|
4638 doneAllSiblings = EFalse; |
|
4639 foundSize = EFalse; |
|
4640 possibleHtml = EFalse; |
|
4641 TInt siblingIndex = 0; |
|
4642 while (!doneAllSiblings) |
|
4643 { |
|
4644 // If a previous Sibling Atom has a HTML tag, then this could possibly |
|
4645 // be an HTML message. So we need to check all Sibling Atoms (ie the |
|
4646 // current atom) to see if they contain Attachments (ie check their |
|
4647 // child atoms for the Tag "ATTACHMENT"). If an attachment is found |
|
4648 // then ignore the HTML flag. If none of the siblings contain an |
|
4649 // attachment, then set the HTML flag, as this is a MHTML message. |
|
4650 if (possibleHtml) |
|
4651 { |
|
4652 // Check if the Child has an Attachment |
|
4653 if (currentAtom->Child() != NULL) |
|
4654 { |
|
4655 if (currentAtom->Child()->Compare(KMIME_ATTACHMENT)) |
|
4656 { |
|
4657 // This Sibling contains an Attachment, so ignore the HTML flag. |
|
4658 possibleHtml = EFalse; |
|
4659 } |
|
4660 } |
|
4661 |
|
4662 // Check if we have searched all Sibling Atoms |
|
4663 if (possibleHtml && currentAtom->Next() == NULL) |
|
4664 { |
|
4665 // None of the Siblings have attachments, so set the HTML flag. |
|
4666 hasHtml = ETrue; |
|
4667 possibleHtml = EFalse; |
|
4668 } |
|
4669 |
|
4670 } |
|
4671 |
|
4672 // If there is a child atom then add it to the stack, we will check it later |
|
4673 if (currentAtom->Child() != NULL) |
|
4674 atomStack->AppendL(currentAtom->Child()); |
|
4675 |
|
4676 // If we pass a related atom then we may have an html message |
|
4677 // remember for later |
|
4678 if (currentAtom->Compare(KMIME_RELATED)) |
|
4679 afterRelated = ETrue; |
|
4680 |
|
4681 // If we pass an alternative atom then we may have an html message |
|
4682 // remember for later |
|
4683 if (currentAtom->Compare(KMIME_ALTERNATIVE)) |
|
4684 afterAlternative = ETrue; |
|
4685 |
|
4686 // If we find an html under a related or alternative then we |
|
4687 // can ignore the mixed section and assume that we have an html mail |
|
4688 if (afterAlternative || afterRelated) |
|
4689 { |
|
4690 if (currentAtom->Compare(KMIME_HTML)) |
|
4691 htmlAfterAltRel = ETrue; |
|
4692 } |
|
4693 |
|
4694 // MIXED ? If so then this email probably contains attachments |
|
4695 // or if there is a message/delivery-status then that is an |
|
4696 // attachment. |
|
4697 if (currentAtom->Compare(KMIME_MIXED) || currentAtom->Compare(KMIME_DELIVERY_STATUS)) |
|
4698 { |
|
4699 hasAttachments = ETrue; |
|
4700 } |
|
4701 |
|
4702 // HTML ? If so this email could be an HTML message. To make sure |
|
4703 // we need to check this Atom's siblings |
|
4704 if (currentAtom->Compare(KMIME_HTML)) |
|
4705 possibleHtml = ETrue; |
|
4706 |
|
4707 // Does this sibling atom say that the data is base64 encoded ? |
|
4708 // If so then we need to remember it to calculate the size. |
|
4709 if (currentAtom->Compare(KMIME_BASE64)) |
|
4710 base64 = ETrue; |
|
4711 |
|
4712 if (currentAtom->Compare(KMIME_ICALENDAR)) |
|
4713 hasICalendar = ETrue; |
|
4714 |
|
4715 if (currentAtom->Compare(KMIME_VCALENDAR)) |
|
4716 hasVCalendar = ETrue; |
|
4717 |
|
4718 // If this is the first numeric value of the current siblings then it is a size |
|
4719 // and must be added to the message size total |
|
4720 // Note that this size must be multiplied by 3/4 if it is base64 |
|
4721 if (!foundSize && siblingIndex == 6) |
|
4722 { |
|
4723 index = currentAtom->Atom().Length(); |
|
4724 |
|
4725 if (index != 0) |
|
4726 numeric = ETrue; |
|
4727 else |
|
4728 // If the atom is of 0 length then it can't possibly be numeric. |
|
4729 numeric = EFalse; |
|
4730 |
|
4731 while ((index--) && (numeric)) |
|
4732 { |
|
4733 if ((currentAtom->Atom()[index] < '0') |
|
4734 || (currentAtom->Atom()[index] > '9')) |
|
4735 numeric = EFalse; |
|
4736 } |
|
4737 |
|
4738 if (numeric) |
|
4739 { |
|
4740 TLex8 lex(currentAtom->Atom()); |
|
4741 User::LeaveIfError(lex.Val(atomValue)); |
|
4742 if (base64) |
|
4743 atomValue = (atomValue * 3) / 4; |
|
4744 size += atomValue; |
|
4745 foundSize = ETrue; |
|
4746 } |
|
4747 } |
|
4748 |
|
4749 siblingIndex++; |
|
4750 currentAtom = currentAtom->Next(); |
|
4751 if (currentAtom == NULL) |
|
4752 doneAllSiblings = ETrue; |
|
4753 } |
|
4754 } |
|
4755 |
|
4756 // Set the size |
|
4757 aMessageEntry.iSize = size; |
|
4758 |
|
4759 // Set the Attachment, MHTML, ICalendar and VCalendar flags, if required. |
|
4760 if (hasAttachments) |
|
4761 { |
|
4762 aMessageEntry.SetAttachment(ETrue); |
|
4763 } |
|
4764 |
|
4765 if( hasHtml || htmlAfterAltRel ) |
|
4766 { |
|
4767 aMessageEntry.SetMHTMLEmail(ETrue); |
|
4768 } |
|
4769 |
|
4770 if(hasICalendar) |
|
4771 { |
|
4772 aMessageEntry.SetICalendar(ETrue); |
|
4773 } |
|
4774 |
|
4775 if(hasVCalendar) |
|
4776 { |
|
4777 aMessageEntry.SetVCalendar(ETrue); |
|
4778 } |
|
4779 |
|
4780 CleanupStack::PopAndDestroy(atomStack); |
|
4781 } |
|
4782 |
|
4783 TInt32 CImImap4Session::GetFetchSizeL(TInt32 aSizeLeft, TInt32 aSizeDownLoaded) |
|
4784 { |
|
4785 TInt fetchSizeBytes = static_cast<TInt>(iServiceSettings->FetchSize()); |
|
4786 TInt32 minimumLimit = fetchSizeBytes; |
|
4787 TInt32 fetchSize = fetchSizeBytes; |
|
4788 TUid type = iEntry->Entry().iType; |
|
4789 |
|
4790 if(iGetPartialMailInfo.iPartialMailOptions == ENoSizeLimits) |
|
4791 { |
|
4792 return KMaxTInt; |
|
4793 } |
|
4794 |
|
4795 if (type == KUidMsvEmailTextEntry || type == KUidMsvEmailHtmlEntry) |
|
4796 { |
|
4797 if(type == KUidMsvEmailHtmlEntry) |
|
4798 { |
|
4799 // iHtmlEntrySize = iSizeOfThisPart; |
|
4800 minimumLimit = Minimum(iGetPartialMailInfo.iBodyTextSizeLimit,iGetPartialMailInfo.iTotalSizeLimit-iBodyTextSize); |
|
4801 } |
|
4802 else |
|
4803 { |
|
4804 // store body text size so that we can check whether html part for |
|
4805 // this message can be downloaded |
|
4806 // text size + html size < iBodyTextSizeLimit then download html part |
|
4807 iBodyTextSize = iSizeOfThisPart; |
|
4808 minimumLimit = Minimum(iGetPartialMailInfo.iBodyTextSizeLimit,iGetPartialMailInfo.iTotalSizeLimit); |
|
4809 } |
|
4810 // check disk space |
|
4811 if(!iIsDiskSpaceChecked) |
|
4812 { |
|
4813 CheckForDiskSpaceL(minimumLimit); |
|
4814 iIsDiskSpaceChecked = ETrue; |
|
4815 } |
|
4816 |
|
4817 fetchSize = FetchSize(minimumLimit,aSizeDownLoaded,aSizeLeft); |
|
4818 } |
|
4819 else if (type == KUidMsvAttachmentEntry || type == KUidMsvEmailExternalBodyEntry) |
|
4820 { |
|
4821 minimumLimit = Minimum(iGetPartialMailInfo.iAttachmentSizeLimit, |
|
4822 iGetPartialMailInfo.iTotalSizeLimit-(iBodyTextSize+iHtmlEntrySize)); |
|
4823 |
|
4824 if(!iIsDiskSpaceChecked) |
|
4825 { |
|
4826 CheckForDiskSpaceL(minimumLimit); |
|
4827 iIsDiskSpaceChecked = ETrue; |
|
4828 } |
|
4829 |
|
4830 fetchSize = FetchSize(minimumLimit,aSizeDownLoaded,aSizeLeft); |
|
4831 } |
|
4832 return fetchSize; |
|
4833 } |
|
4834 |
|
4835 TInt32 CImImap4Session::FetchSize(TInt32 aMinimumLimit,TInt32 aSizeDownLoaded, TInt32 aSizeLeft) |
|
4836 { |
|
4837 TInt fetchSizeBytes = static_cast<TInt>(iServiceSettings->FetchSize()); |
|
4838 TInt32 fetchSize = fetchSizeBytes; |
|
4839 if(aSizeLeft > (aMinimumLimit-aSizeDownLoaded)) |
|
4840 { |
|
4841 if((aMinimumLimit-aSizeDownLoaded) < fetchSizeBytes) |
|
4842 { |
|
4843 fetchSize = aMinimumLimit-aSizeDownLoaded; |
|
4844 } |
|
4845 } |
|
4846 else |
|
4847 { |
|
4848 if(aSizeLeft < fetchSizeBytes) |
|
4849 { |
|
4850 fetchSize = aSizeLeft; |
|
4851 } |
|
4852 } |
|
4853 return fetchSize; |
|
4854 } |
|
4855 |
|
4856 // Process an address list structure into a CImHeader |
|
4857 void CImImap4Session::ProcessAddressListL(HBufC8 **aBufferPtr, CDesCArray& aWhere, CImapAtom *aAtom) |
|
4858 { |
|
4859 while(aAtom) |
|
4860 { |
|
4861 // Process this address and add it to ToRecipients() |
|
4862 ProcessAddressL(aBufferPtr,aAtom); |
|
4863 |
|
4864 #ifdef UNICODE |
|
4865 HBufC *newaddress=HBufC::NewL((*aBufferPtr)->Length()); |
|
4866 CleanupStack::PushL(newaddress); |
|
4867 newaddress->Des().Copy((*aBufferPtr)->Des()); |
|
4868 aWhere.AppendL(newaddress->Des()); |
|
4869 CleanupStack::PopAndDestroy(); |
|
4870 #else |
|
4871 aWhere.AppendL((*aBufferPtr)->Des()); |
|
4872 #endif |
|
4873 |
|
4874 // Next address |
|
4875 aAtom=aAtom->Next(); |
|
4876 } |
|
4877 } |
|
4878 |
|
4879 void CImImap4Session::AppendExtendL(HBufC8** aBufferPtr, const TDesC8& aText, TBool aOnStack) |
|
4880 { |
|
4881 HBufC8 *buffer = *aBufferPtr; |
|
4882 TInt32 space = buffer->Des().MaxLength() - (buffer->Des().Length() + aText.Length()); |
|
4883 if (space < 0) |
|
4884 { |
|
4885 TInt32 inc = (-space) + KImapAddressSizeInc - ((-space) % KImapAddressSizeInc); |
|
4886 TInt32 newSize = buffer->Des().MaxLength() + inc; |
|
4887 HBufC8 *newBuf = buffer->ReAllocL(newSize); |
|
4888 |
|
4889 if (aOnStack && newBuf!=buffer) |
|
4890 { |
|
4891 CleanupStack::Pop(); |
|
4892 CleanupStack::PushL(newBuf); |
|
4893 } |
|
4894 |
|
4895 *aBufferPtr = buffer = newBuf; |
|
4896 } |
|
4897 buffer->Des().Append(aText); |
|
4898 } |
|
4899 |
|
4900 // Process an address structure into a buffer |
|
4901 void CImImap4Session::ProcessAddressL(HBufC8** aBufferPtr, CImapAtom *aAtom) |
|
4902 { |
|
4903 // Erase buffer |
|
4904 (*aBufferPtr)->Des().Zero(); |
|
4905 |
|
4906 // Descend into address |
|
4907 aAtom=aAtom->ToChildL(); |
|
4908 |
|
4909 // Save name |
|
4910 CImapAtom* name=aAtom; |
|
4911 aAtom=aAtom->ToNextL(); |
|
4912 |
|
4913 // Skip route |
|
4914 aAtom=aAtom->ToNextL(); |
|
4915 |
|
4916 // Save user |
|
4917 CImapAtom* user=aAtom; |
|
4918 aAtom=aAtom->ToNextL(); |
|
4919 |
|
4920 // Save host |
|
4921 CImapAtom* host=aAtom; |
|
4922 |
|
4923 // Build address string: is there a name? |
|
4924 if (name->Compare(_L8("")) || name->Compare(_L8("NIL"))) |
|
4925 { |
|
4926 // No, just save user@host |
|
4927 AppendExtendL(aBufferPtr, user->Atom()); |
|
4928 AppendExtendL(aBufferPtr, _L8("@")); |
|
4929 AppendExtendL(aBufferPtr, host->Atom()); |
|
4930 } |
|
4931 else |
|
4932 { |
|
4933 // Yes, in the form 'Name <user@host>' |
|
4934 AppendExtendL(aBufferPtr, _L8("\"")); |
|
4935 AppendExtendL(aBufferPtr, name->Atom()); |
|
4936 AppendExtendL(aBufferPtr, _L8("\"")); |
|
4937 AppendExtendL(aBufferPtr, _L8(" <")); |
|
4938 AppendExtendL(aBufferPtr, user->Atom()); |
|
4939 AppendExtendL(aBufferPtr, _L8("@")); |
|
4940 AppendExtendL(aBufferPtr, host->Atom()); |
|
4941 AppendExtendL(aBufferPtr, _L8(">")); |
|
4942 } |
|
4943 |
|
4944 #ifdef PRINTING |
|
4945 TPtrC8 addr=(*aBufferPtr)->Des(); |
|
4946 if (addr.Length() > 256) |
|
4947 addr.Set(addr.Ptr(), 256); |
|
4948 LogText(_L8(" address '%S'"),&addr); |
|
4949 #endif |
|
4950 } |
|
4951 |
|
4952 // SJM 19990922. Previous version of this function used SetUnread |
|
4953 // rather than SetIMAP4Unread or SetIMAP4Flags. This meant that the |
|
4954 // IMAP4Unread flag itself was never set. This seems like a bug. |
|
4955 |
|
4956 TBool CImImap4Session::ProcessFlagsL(CImapAtom* aAtom, TMsvEmailEntry& aMessage) |
|
4957 { |
|
4958 TBool unread = EFalse; |
|
4959 TBool seen = EFalse; |
|
4960 TBool answered = EFalse; |
|
4961 TBool flagged = EFalse; |
|
4962 TBool deleted = EFalse; |
|
4963 TBool draft = EFalse; |
|
4964 TBool recent = EFalse; |
|
4965 TBool flagsUpdated = EFalse; |
|
4966 |
|
4967 // Descend |
|
4968 aAtom=aAtom->Child(); |
|
4969 |
|
4970 // Process flags |
|
4971 while(aAtom!=NULL) |
|
4972 { |
|
4973 // Check for standard IMAP flags |
|
4974 if (aAtom->Compare(KIMAPFLAG_ANSWERED)) |
|
4975 answered = ETrue; |
|
4976 else if (aAtom->Compare(KIMAPFLAG_DELETED)) |
|
4977 deleted = ETrue; |
|
4978 else if (aAtom->Compare(KIMAPFLAG_DRAFT)) |
|
4979 draft = ETrue; |
|
4980 else if (aAtom->Compare(KIMAPFLAG_FLAGGED)) |
|
4981 flagged = ETrue; |
|
4982 else if (aAtom->Compare(KIMAPFLAG_RECENT)) |
|
4983 recent = ETrue; |
|
4984 else if (aAtom->Compare(KIMAPFLAG_SEEN)) |
|
4985 seen = ETrue; |
|
4986 else if (aAtom->Compare(KIMAPFLAG_UNREAD)) |
|
4987 { |
|
4988 unread = ETrue; |
|
4989 |
|
4990 // There is at least one unread message in this folder |
|
4991 iSomeUnread=ETrue; |
|
4992 } |
|
4993 |
|
4994 // Next atom |
|
4995 aAtom=aAtom->Next(); |
|
4996 } |
|
4997 |
|
4998 |
|
4999 TBool oUnread, oSeen, oAnswered, oFlagged, oDeleted, oDraft, oRecent; |
|
5000 aMessage.GetIMAP4Flags(oUnread, oSeen, oAnswered, oFlagged, oDeleted, oDraft, oRecent); |
|
5001 |
|
5002 // Are we configured to update the \seen flag on the server? |
|
5003 if (iServiceSettings->UpdatingSeenFlags()) |
|
5004 { |
|
5005 // Make a note to update the servers \Seen flag if CHANGED on the client |
|
5006 // and different to the servers version |
|
5007 if ( FIXBOOL(aMessage.Unread()) == seen && FIXBOOL(oSeen) == seen) |
|
5008 { |
|
5009 if (aMessage.Unread()) |
|
5010 iClearSeenList->AppendL(aMessage.Id()); |
|
5011 else |
|
5012 iSetSeenList->AppendL(aMessage.Id()); |
|
5013 } |
|
5014 } |
|
5015 |
|
5016 if ( FIXBOOL(oUnread) != unread || FIXBOOL(oSeen) != seen || FIXBOOL(oAnswered) != answered |
|
5017 || FIXBOOL(oFlagged) != flagged || FIXBOOL(oDeleted) != deleted |
|
5018 || FIXBOOL(oDraft) != draft || FIXBOOL(oRecent) != recent ) |
|
5019 { |
|
5020 aMessage.SetIMAP4Flags(unread, seen, answered, flagged, deleted, draft, recent); |
|
5021 flagsUpdated = ETrue; |
|
5022 } |
|
5023 |
|
5024 // Are we configured to update the \seen flag on the server? |
|
5025 if (iServiceSettings->UpdatingSeenFlags()) |
|
5026 { |
|
5027 // Now copy the inverse of the \Seen flag down to the clients Unread flag |
|
5028 // except when LastSyncSeen is set (ie when the client version is more up to date) |
|
5029 // This means that the client Read status ALWAYS reflects the IMAP \Seen state |
|
5030 if ( FIXBOOL(aMessage.Unread()) == seen && FIXBOOL(oSeen) != seen) |
|
5031 { |
|
5032 aMessage.SetUnread(!seen); |
|
5033 flagsUpdated = ETrue; |
|
5034 } |
|
5035 } |
|
5036 |
|
5037 return flagsUpdated; |
|
5038 } |
|
5039 |
|
5040 // Process output from list command |
|
5041 void CImImap4Session::ProcessListL(CImapAtom *aAtom) |
|
5042 { |
|
5043 // Just getting hierarchy separator? |
|
5044 if (iState==EImapStateSeparatorWait) |
|
5045 { |
|
5046 // Save it away |
|
5047 aAtom=aAtom->ToNextL(); |
|
5048 |
|
5049 // Is it one character long? (all should be) - if not, ignore it |
|
5050 if (aAtom->Atom().Length()!=1) |
|
5051 return; |
|
5052 |
|
5053 // Save it for local use |
|
5054 iHierarchySeparator=aAtom->Atom(); |
|
5055 |
|
5056 // Update service entry |
|
5057 CEmailAccounts* account = CEmailAccounts::NewLC(); |
|
5058 TImapAccount id; |
|
5059 id.iImapAccountId = iEntry->Entry().MtmData2(); // iMtmData2 of the service entry contains TImapAccountId |
|
5060 id.iImapAccountName = iEntry->Entry().iDetails; |
|
5061 id.iImapService = iEntry->Entry().iServiceId; |
|
5062 id.iSmtpService = iEntry->Entry().iRelatedId; |
|
5063 |
|
5064 account->LoadImapSettingsL(id, *iServiceSettings); |
|
5065 |
|
5066 // Set new path separator |
|
5067 iServiceSettings->SetPathSeparator(iHierarchySeparator[0]); |
|
5068 id.iImapAccountName = KNullDesC; // So that account name is not updated |
|
5069 account->SaveImapSettingsL(id, *iServiceSettings); |
|
5070 CleanupStack::PopAndDestroy(account); |
|
5071 return; |
|
5072 } |
|
5073 |
|
5074 // Anywhere to save it? |
|
5075 if (!iList) return; |
|
5076 |
|
5077 // Getting hierarchy from server - process the lot |
|
5078 CImapAtom *fl; |
|
5079 CImImap4DirStruct *entry=new (ELeave) CImImap4DirStruct; |
|
5080 CleanupStack::PushL(entry); |
|
5081 |
|
5082 // List reply is of the form: * LIST (flags) "pathsep" "path" |
|
5083 |
|
5084 // At the start, it's a mailbox |
|
5085 entry->iIsMailbox=ETrue; |
|
5086 entry->iIsFolder=ETrue; |
|
5087 |
|
5088 // Process flags |
|
5089 if ((fl=aAtom->Child())!=NULL) |
|
5090 { |
|
5091 while(fl) |
|
5092 { |
|
5093 // Check flags |
|
5094 if (fl->Compare(KIMAPFLAG_NOSELECT)) |
|
5095 { |
|
5096 // \Noselect means it isn't a mailbox |
|
5097 entry->iIsMailbox=EFalse; |
|
5098 } |
|
5099 else if (fl->Compare(KIMAPFLAG_NOINFERIORS)) |
|
5100 { |
|
5101 // \Noinferiors means it can't be a folder |
|
5102 entry->iIsFolder=EFalse; |
|
5103 } |
|
5104 |
|
5105 // Next flag |
|
5106 fl=fl->Next(); |
|
5107 } |
|
5108 } |
|
5109 |
|
5110 // Path separator: only save this if it is a valid length |
|
5111 aAtom=aAtom->ToNextL(); |
|
5112 if (aAtom->Atom().Length()==1) |
|
5113 iHierarchySeparator=aAtom->Atom(); |
|
5114 |
|
5115 // Path |
|
5116 aAtom=aAtom->ToNextL(); |
|
5117 TPtrC8 path(aAtom->Atom()); |
|
5118 |
|
5119 // In case server has returned us the parent when we asked for children, |
|
5120 // ignore items which are too small to be path(separator)child names. |
|
5121 if (path.Length()>iCommandBuf.Length()) |
|
5122 { |
|
5123 // CC:Mail has an annoying habit of not listening to us when we ask for |
|
5124 // children, and returning siblings instead: this takes some extra CPU |
|
5125 // time, so we only do it if we're talking to a CC:Mail server, but here |
|
5126 // we check that the returned path is actually what we asked for before |
|
5127 // proceding. |
|
5128 if (iTalkingToCCMail) |
|
5129 { |
|
5130 // Check path starts with iCommandBuf's contents |
|
5131 #ifdef UNICODE |
|
5132 HBufC8 *narrow=HBufC8::NewL(iCommandBuf.Length()); |
|
5133 narrow->Des().Copy(iCommandBuf); |
|
5134 if (path.Find(*narrow)!=0) |
|
5135 { |
|
5136 // Nope. Ignore this. Silly server. |
|
5137 delete narrow; |
|
5138 CleanupStack::PopAndDestroy(); |
|
5139 return; |
|
5140 } |
|
5141 delete narrow; |
|
5142 #else |
|
5143 if (path.Find(iCommandBuf)!=0) |
|
5144 { |
|
5145 // Nope. Ignore this. Silly server. |
|
5146 CleanupStack::PopAndDestroy(); |
|
5147 return; |
|
5148 } |
|
5149 #endif |
|
5150 } |
|
5151 |
|
5152 // Add leaf, ignoring the path that all servers return before |
|
5153 // the leaf. |
|
5154 |
|
5155 // Removed the UNICODE special case since we might need to |
|
5156 // modify the buffer now to do the UnModUTF7. |
|
5157 #if 1 |
|
5158 TPtrC8 leaf = path.Mid(iCommandBuf.Length()); |
|
5159 HBufC* text = DoUnModUTF7LC( leaf ); |
|
5160 entry->SetLeafnameL(text->Des()); |
|
5161 |
|
5162 CleanupStack::PopAndDestroy(); |
|
5163 #else |
|
5164 HBufC *newleaf=HBufC::NewL(path.Length()-iCommandBuf.Length()); |
|
5165 CleanupStack::PushL(newleaf); |
|
5166 newleaf->Des().Copy(path.Mid(iCommandBuf.Length())); |
|
5167 entry->SetLeafnameL(newleaf->Des()); |
|
5168 CleanupStack::PopAndDestroy(); |
|
5169 #endif |
|
5170 |
|
5171 // If there's anything there (might just be the path), then add it to array |
|
5172 if (entry->Leafname().Length()>0) |
|
5173 { |
|
5174 // Add to array |
|
5175 iList->AppendL(entry); |
|
5176 |
|
5177 // It's in the array now |
|
5178 CleanupStack::Pop(); |
|
5179 } |
|
5180 else |
|
5181 { |
|
5182 // Get rid of it - it's not needed |
|
5183 CleanupStack::PopAndDestroy(); |
|
5184 } |
|
5185 } |
|
5186 else |
|
5187 { |
|
5188 // Get rid of it - it's not needed |
|
5189 CleanupStack::PopAndDestroy(); |
|
5190 } |
|
5191 } |
|
5192 |
|
5193 // Process output from list command |
|
5194 void CImImap4Session::ProcessLsubL(CImapAtom *aAtom) |
|
5195 { |
|
5196 // Process the lot: it'll be a full path which we need to cut up |
|
5197 // Reply is of the form: * LSUB (flags) "pathsep" "path" |
|
5198 |
|
5199 // Skip flags |
|
5200 aAtom=aAtom->ToNextL(); |
|
5201 |
|
5202 // We already know the path separator, so skip it |
|
5203 aAtom=aAtom->ToNextL(); |
|
5204 |
|
5205 // Strip folder path: to do this we just remove the start of the path from |
|
5206 // the returned string, so we need the length of it. |
|
5207 TInt pathlength=iFolderPath.Length(); |
|
5208 |
|
5209 // If the length is non-zero, this means that there is a folderpath, and |
|
5210 // we will need to strip both it, and the following path separator character. |
|
5211 // If there is no path, we don't want to strip anything, so we don't |
|
5212 // increment it at all. |
|
5213 if (pathlength) |
|
5214 pathlength++; |
|
5215 |
|
5216 // Get a (possibly wide) copy of the path, skipping the prefix |
|
5217 TPtrC8 skippedPrefix(aAtom->Atom().Mid(pathlength)); |
|
5218 HBufC* basePath = DoUnModUTF7LC( skippedPrefix ); |
|
5219 TPtrC path(basePath->Des()); |
|
5220 |
|
5221 // Go down through path |
|
5222 TMsvId where=iServiceId; |
|
5223 while(where) |
|
5224 { |
|
5225 // Truncate this bit of path: look for a hierarchy separator |
|
5226 TInt separator=path.Locate(iHierarchySeparator[0]); |
|
5227 TPtrC element(path); |
|
5228 |
|
5229 // Anything? |
|
5230 if (separator!=KErrNotFound) |
|
5231 { |
|
5232 // Truncate this element there |
|
5233 element.Set(element.Ptr(),separator); |
|
5234 } |
|
5235 |
|
5236 // Try to find this element at the search level |
|
5237 SetEntryL(where); |
|
5238 GetChildrenL(*iSelection); |
|
5239 |
|
5240 // Nothing? Give up. |
|
5241 if (!iSelection->Count()) |
|
5242 break; // out of while |
|
5243 |
|
5244 // Check all the children |
|
5245 TInt a; |
|
5246 for(a=0;a<iSelection->Count();a++) |
|
5247 { |
|
5248 // This one? |
|
5249 SetEntryL((*iSelection)[a]); |
|
5250 |
|
5251 if (iEntry->Entry().iDetails.Compare(element)==0) |
|
5252 { |
|
5253 // Found it! Are we at the end, ie no more elements? |
|
5254 if (separator==KErrNotFound) |
|
5255 { |
|
5256 // Set subscribed flag |
|
5257 TMsvEmailEntry message=iEntry->Entry(); |
|
5258 if (!message.Subscribed()) |
|
5259 { |
|
5260 // It needs changing, do it |
|
5261 message.SetSubscribed(ETrue); |
|
5262 ChangeEntryL(message); |
|
5263 } |
|
5264 |
|
5265 // All done: this will exit the loop |
|
5266 where=0; |
|
5267 break; |
|
5268 } |
|
5269 else |
|
5270 { |
|
5271 // Update path |
|
5272 where=(*iSelection)[a]; |
|
5273 path.Set(path.Ptr()+separator+1,path.Length()-separator-1); |
|
5274 |
|
5275 // Exit loop |
|
5276 break; |
|
5277 } |
|
5278 } |
|
5279 } |
|
5280 |
|
5281 if (a==iSelection->Count()) |
|
5282 { |
|
5283 // Didn't find it. Humm |
|
5284 DBG((LogText(_L8("Didn't find entry '%S': tree out of date?"),&element))); |
|
5285 |
|
5286 break; // out of while |
|
5287 } |
|
5288 } |
|
5289 |
|
5290 CleanupStack::PopAndDestroy(); |
|
5291 } |
|
5292 |
|
5293 void CImImap4Session::SendLoginL() |
|
5294 { |
|
5295 // We need to login in as few steps as possible, ie avoid using |
|
5296 // literals if possible as this incurrs a RTT delay as we have to |
|
5297 // wait for server's OK before sending the literal. |
|
5298 |
|
5299 // Set up to send a command |
|
5300 NewTag(); |
|
5301 |
|
5302 // No need to quote? Do it in one line if we can, |
|
5303 if (!iLiteralUsername && !iLiteralPassword) |
|
5304 { |
|
5305 // Send login line. Turn off logging before sending |
|
5306 iImapIO->PerformLogging(EFalse); |
|
5307 iImapIO->SendL(iStatus,_L8("%d LOGIN %S %S\r\n"),iTag,iUsername,iPassword); |
|
5308 iImapIO->PerformLogging(ETrue); |
|
5309 } |
|
5310 else |
|
5311 { |
|
5312 if (iLiteralUsername) |
|
5313 { |
|
5314 // Send literal username |
|
5315 iImapIO->SendL(iStatus,_L8("%d LOGIN {%d}\r\n"),iTag,iUsername->Length()); |
|
5316 iState=EImapStateLoginSendUser; |
|
5317 } |
|
5318 else |
|
5319 { |
|
5320 // Send username and literal password. Turn off logging before sending |
|
5321 iImapIO->PerformLogging(EFalse); |
|
5322 iImapIO->SendL(iStatus,_L8("%d LOGIN %S {%d}\r\n"),iTag,iUsername,iPassword->Length()); |
|
5323 iImapIO->PerformLogging(ETrue); |
|
5324 |
|
5325 iState=EImapStateLoginSendPassword; |
|
5326 } |
|
5327 } |
|
5328 |
|
5329 // Don't complete yet! |
|
5330 NewTagSent(); |
|
5331 } |
|
5332 |
|
5333 void CImImap4Session::SendCapabilityL() |
|
5334 { |
|
5335 // Check server capabilities |
|
5336 iState=EImapStateCapabilityWait; |
|
5337 iSeenVersion=EFalse; |
|
5338 |
|
5339 // Send the command |
|
5340 NewTag(); |
|
5341 iImapIO->SendL(iStatus,_L8("%d CAPABILITY\r\n"),iTag); |
|
5342 NewTagSent(); |
|
5343 } |
|
5344 |
|
5345 void CImImap4Session::StartIdle(TRequestStatus& aRequestStatus) |
|
5346 { |
|
5347 DBG((LogText(_L8("CImImap4Session::StartIdle()")))); |
|
5348 TInt err=KErrNone; |
|
5349 if (!Connected() || iState==EImapStateIdleWait) |
|
5350 { |
|
5351 Queue(aRequestStatus); |
|
5352 err=KErrDisconnected; |
|
5353 } |
|
5354 else |
|
5355 TRAP(err,StartIdleL(aRequestStatus)); |
|
5356 if (err!=KErrNone) |
|
5357 { |
|
5358 DBG((LogText(_L8("-----------------------------------------------------------")))); |
|
5359 DBG((LogText(_L8("CImap4Session::StartIdle(): calling Complete()")))); |
|
5360 DBG((LogText(_L8("-----------------------------------------------------------")))); |
|
5361 |
|
5362 |
|
5363 Complete(err); |
|
5364 } |
|
5365 } |
|
5366 |
|
5367 void CImImap4Session::StartIdleL(TRequestStatus& aRequestStatus) |
|
5368 { |
|
5369 DBG((LogText(_L8("CImImap4Session::StartIdleL()")))); |
|
5370 Queue(aRequestStatus); |
|
5371 DoStartIdleL(); |
|
5372 } |
|
5373 |
|
5374 void CImImap4Session::DoStartIdleL() |
|
5375 { |
|
5376 DBG((LogText(_L8("CImImap4Session::DoStartIdleL()")))); |
|
5377 __ASSERT_DEBUG(ImapIdleSupported(), gPanic(EBadUseOfImap4Op)); |
|
5378 if(!ImapIdleSupported()) |
|
5379 { |
|
5380 User::LeaveIfError(KErrGeneral);//Bad use of Imap4Op |
|
5381 } |
|
5382 __ASSERT_DEBUG(iState==EImapStateSelected, gPanic(ESyncWhenNotSelected)); |
|
5383 if(!(iState==EImapStateSelected)) |
|
5384 { |
|
5385 User::LeaveIfError(KErrArgument);// Sync when not selected . |
|
5386 } |
|
5387 // Reset flags |
|
5388 iMailboxReceivedExists=EFalse; |
|
5389 iMailboxReceivedExpunge=EFalse; |
|
5390 iMailboxReceivedFlags=EFalse; |
|
5391 |
|
5392 iState=EImapStateIdleWait; |
|
5393 SendMessageL(KIMAPC_IDLE); |
|
5394 } |
|
5395 |
|
5396 void CImImap4Session::StopIdle(TRequestStatus& aRequestStatus) |
|
5397 { |
|
5398 DBG((LogText(_L8("CImImap4Session::StopIdle()")))); |
|
5399 TInt err(KErrNone); |
|
5400 if (!Connected()) |
|
5401 { |
|
5402 Queue(aRequestStatus); |
|
5403 err=KErrDisconnected; |
|
5404 } |
|
5405 else |
|
5406 { |
|
5407 // stop idle is only called by compound command |
|
5408 // so flag this |
|
5409 iCompoundStopIdle = ETrue; |
|
5410 TRAP(err,StopIdleL(aRequestStatus)); |
|
5411 } |
|
5412 if (err!=KErrNone) |
|
5413 { |
|
5414 DBG((LogText(_L8("-----------------------------------------------------------")))); |
|
5415 DBG((LogText(_L8("CImap4Session::StopIdle(): calling Complete()")))); |
|
5416 DBG((LogText(_L8("-----------------------------------------------------------")))); |
|
5417 Complete(err); |
|
5418 } |
|
5419 } |
|
5420 |
|
5421 void CImImap4Session::SyncStopIdleL(TRequestStatus& aRequestStatus) |
|
5422 { |
|
5423 DBG(LogText(_L8("CImImap4Session::SyncStopIdleL (state=%d)"), iState)); |
|
5424 iStoppingIdleForSync = ETrue; |
|
5425 StopIdleL(aRequestStatus); |
|
5426 } |
|
5427 |
|
5428 void CImImap4Session::StopIdleL(TRequestStatus& aRequestStatus) |
|
5429 { |
|
5430 DBG((LogText(_L8("CImImap4Session::StopIdleL()")))); |
|
5431 Cancel(); |
|
5432 Queue(aRequestStatus); |
|
5433 DoStopIdleL(); |
|
5434 } |
|
5435 |
|
5436 void CImImap4Session::DoStopIdleL() |
|
5437 { |
|
5438 DBG(LogText(_L8("CImImap4Session::DoStopIdleL(state=%d)"),iState)) ; |
|
5439 __ASSERT_DEBUG(ImapIdleSupported(), gPanic(EBadUseOfImap4Op)); |
|
5440 if(!ImapIdleSupported()) |
|
5441 { |
|
5442 User::LeaveIfError(KErrGeneral);//Bad Use of Imap4Op |
|
5443 } |
|
5444 __ASSERT_DEBUG(IsIdling(), gPanic(EInvalidStatus)); |
|
5445 if(!IsIdling()) |
|
5446 { |
|
5447 User::LeaveIfError(KErrGeneral); // Invalid Status |
|
5448 } |
|
5449 |
|
5450 iIdleTimer->Cancel(); |
|
5451 iState=EImapStateStopIdleWait; |
|
5452 |
|
5453 SendUntaggedMessageWithTimeoutL(KIMAPC_DONE, KImapDoneInactivityTimeSeconds); |
|
5454 } |
|
5455 |
|
5456 // Process output from search command |
|
5457 // V2 version could alter significantly and become #ifdef-unreadable |
|
5458 void CImImap4Session::ProcessSearchL(CImapAtom *aAtom) |
|
5459 { |
|
5460 // Process the reply from a uid search command into an fixed array of uids. |
|
5461 // The data is recieved as a series of atoms containing numerical only data. |
|
5462 // If non-numerical data is recieved, then this function will leave. |
|
5463 // Use the key to do inserts in sequence. Takes care of duplicates |
|
5464 TKeyArrayFix key(0,ECmpTInt32); |
|
5465 TUint atomUid; |
|
5466 while (aAtom) |
|
5467 { |
|
5468 if (aAtom->Atom().Length()>0) |
|
5469 { |
|
5470 // Check for numerical value. |
|
5471 TChar atomChar(aAtom->Atom()[0]); |
|
5472 if (atomChar.IsDigit() && aAtom->Value(atomUid)==KErrNone) |
|
5473 { |
|
5474 // Append it to the end search UID list |
|
5475 // Put in sequence no duplicates |
|
5476 TRAPD(err,iSearchList->InsertIsqL(static_cast<TUint32>(atomUid),key)); |
|
5477 if(err != KErrNone && err != KErrAlreadyExists) |
|
5478 { |
|
5479 User::Leave(err); |
|
5480 } |
|
5481 } |
|
5482 else |
|
5483 // Not a number or cant get its value. |
|
5484 User::Leave(KErrArgument); |
|
5485 } |
|
5486 else |
|
5487 { |
|
5488 // Null atom. |
|
5489 User::Leave(KErrArgument); |
|
5490 } |
|
5491 aAtom=aAtom->Next(); |
|
5492 } |
|
5493 |
|
5494 DBG((LogText(_L8("UID search found %d UIDs in remote folder"),iSearchList->Count()))); |
|
5495 } |
|
5496 |
|
5497 // A tag has completed OK |
|
5498 void CImImap4Session::CommandCompleteL(TInt aResult) |
|
5499 { |
|
5500 DBG((LogText(_L8("CImImap4Session::CommandComplete(state=%d, result=%d)"),iState,aResult))); |
|
5501 iCommandFailure=aResult; |
|
5502 |
|
5503 // Data has been received from the remote server - can cancel the cancel-timer, |
|
5504 // cancel wasn't cos of hanging due to GPRS suspend. |
|
5505 iCancelTimer->Cancel(); |
|
5506 |
|
5507 switch(iState) |
|
5508 { |
|
5509 case EImapStateCapabilityWait: |
|
5510 // Did we see the correct version ID? |
|
5511 if (!iSeenVersion) |
|
5512 { |
|
5513 // No - not in any of the lines between issuing the capability |
|
5514 // command and the completion of the command |
|
5515 Fail(KErrImapServerVersion); |
|
5516 return; |
|
5517 } |
|
5518 |
|
5519 // Move into whatever logon state is required |
|
5520 iState=iSavedState; |
|
5521 |
|
5522 // Need to login? |
|
5523 if (iState==EImapStateLoginWait) |
|
5524 { |
|
5525 switch (iSecurityState) |
|
5526 { |
|
5527 case ESecure: |
|
5528 case EUnsecure: |
|
5529 // shouldn't ever get here... |
|
5530 SendLoginL(); |
|
5531 break; |
|
5532 |
|
5533 case ENegotiating: |
|
5534 SendLoginL(); |
|
5535 iSecurityState=ESecure; |
|
5536 break; |
|
5537 |
|
5538 case EUnknown: |
|
5539 if (iServiceSettings->SecureSockets()) |
|
5540 { |
|
5541 if (iCapabilityStartTLS) |
|
5542 { |
|
5543 // send StartTLS |
|
5544 NewTag(); |
|
5545 iImapIO->SetTLSResponseL(); |
|
5546 iImapIO->SendL(iStatus,_L8("%d STARTTLS\r\n"),iTag); |
|
5547 NewTagSent(); |
|
5548 iState=EImapStateStartTLSWait; |
|
5549 iSendQueued=EFalse; // no need to queue 'send' cause session will take care of the response. |
|
5550 } |
|
5551 else |
|
5552 Fail(KErrImapServerNoSecurity); |
|
5553 } |
|
5554 else |
|
5555 { |
|
5556 if (iCapabilityLoginDisabled) |
|
5557 Fail(KErrImapServerLoginDisabled); |
|
5558 else |
|
5559 { |
|
5560 SendLoginL(); |
|
5561 iSecurityState=EUnsecure; |
|
5562 } |
|
5563 } |
|
5564 break; |
|
5565 } |
|
5566 |
|
5567 return; |
|
5568 } |
|
5569 break; |
|
5570 |
|
5571 case EImapStateStartTLSWait: |
|
5572 SendCapabilityL(); |
|
5573 iSecurityState = ENegotiating; |
|
5574 return; |
|
5575 |
|
5576 case EImapStateSelectWait: |
|
5577 switch(aResult) |
|
5578 { |
|
5579 case KErrNone: |
|
5580 // Select OK |
|
5581 DBG((LogText(_L8("CImImap4Session::CommandCompleteL(): setting iState to EImapStateSelected")))); |
|
5582 iState=EImapStateSelected; |
|
5583 if( iCancelAndIdle ) |
|
5584 { |
|
5585 if( iReissueIdle ) |
|
5586 { |
|
5587 // Need to re-issue the IDLE command - probably due to a cancel |
|
5588 // during a populate commmand and session was IDLE before the |
|
5589 // command. |
|
5590 // As the populate could have been for a message not in the Inbox, |
|
5591 // (and this is the completion of SELECT command for appropriate |
|
5592 // mailbox) need to ensure IDLE started in inbox. |
|
5593 |
|
5594 // Do the select (if we really need to) or skip if possible. |
|
5595 if( iMailboxId==GetInbox() && iMailboxWritable ) |
|
5596 { |
|
5597 DBG((LogText(_L8("Need to re-issue IDLE command")))); |
|
5598 |
|
5599 // No need to do the select - so re-issue the IDLE |
|
5600 DoStartIdleL(); |
|
5601 } |
|
5602 else |
|
5603 { |
|
5604 DBG((LogText(_L8("Need to issue IDLE - first select Inbox (writable)")))); |
|
5605 |
|
5606 // Looks like we need to do the select... |
|
5607 DoSelectL(GetInbox(), ETrue); |
|
5608 } |
|
5609 } |
|
5610 else |
|
5611 { |
|
5612 DBG((LogText(_L8("Do not re-issue IDLE - cancel completed")))); |
|
5613 |
|
5614 // Cancelling completed - stay in this mailbox and do not issue idle. |
|
5615 iCancelAndIdle = EFalse; |
|
5616 } |
|
5617 } |
|
5618 break; |
|
5619 |
|
5620 case KErrIMAPNO: |
|
5621 DBG((LogText(_L8("CImap4Session::CommandComplete(): iState=EImapStateSelectWait")))); |
|
5622 DBG((LogText(_L8("-----------------------------------------------------------")))); |
|
5623 DBG((LogText(_L8("CImap4Session::CommandComplete(): calling Complete()")))); |
|
5624 DBG((LogText(_L8("-----------------------------------------------------------")))); |
|
5625 // Select failed |
|
5626 Complete(KErrIMAPNO); |
|
5627 return; |
|
5628 } |
|
5629 break; |
|
5630 |
|
5631 case EImapStateSeparatorWait: |
|
5632 case EImapStateCloseWait: |
|
5633 // Back to unselected |
|
5634 iState=EImapStateNoSelect; |
|
5635 break; |
|
5636 |
|
5637 case EImapStateCommandWait: |
|
5638 case EImapStateListWait: |
|
5639 case EImapStateLsubWait: |
|
5640 case EImapStateAppendResultWait: |
|
5641 // Restore old state: could have been selected/unselected |
|
5642 iState=iSavedState; |
|
5643 break; |
|
5644 |
|
5645 case EImapStateFetchCancelWait: |
|
5646 // No more parts to fetch - reset the fetch list |
|
5647 iFetchList->Reset(); |
|
5648 |
|
5649 // Dispose of any buffers we've got left |
|
5650 delete iPartialLine; |
|
5651 iPartialLine=NULL; |
|
5652 delete iAttachmentFile; |
|
5653 iAttachmentFile=NULL; |
|
5654 delete iAttachmentFullPath; |
|
5655 iAttachmentFullPath=NULL; |
|
5656 delete iAttachmentMimeInfo; |
|
5657 iAttachmentMimeInfo=NULL; |
|
5658 |
|
5659 // Ensure attachment file state correct so that any re-fetch will be ok |
|
5660 iAttachmentFileState=EFileNotOpen; |
|
5661 |
|
5662 // All done - ensure any changes are committed to disk |
|
5663 iEntry->CompleteBulk(); |
|
5664 |
|
5665 // Back in selected state - do we need to move back to inbox? |
|
5666 iState=EImapStateSelected; |
|
5667 iSyncState=ENotSyncing; |
|
5668 if( iCancelAndIdle ) |
|
5669 { |
|
5670 if( iReissueIdle ) |
|
5671 { |
|
5672 // Need to re-issue the IDLE command - probably due to a cancel |
|
5673 // during a populate commmand and session was IDLE before the |
|
5674 // command. |
|
5675 // As the populate could have been for a message not in the Inbox, |
|
5676 // (and this is the completion of SELECT command for appropriate |
|
5677 // mailbox) need to ensure IDLE started in inbox. |
|
5678 |
|
5679 // Do the select (if we really need to) or skip if possible. |
|
5680 if( iMailboxId==GetInbox() && iMailboxWritable ) |
|
5681 { |
|
5682 DBG((LogText(_L8("Need to re-issue IDLE command")))); |
|
5683 |
|
5684 // No need to do the select - so re-issue the IDLE |
|
5685 DoStartIdleL(); |
|
5686 } |
|
5687 else |
|
5688 { |
|
5689 DBG((LogText(_L8("Need to issue IDLE - first select Inbox (writable)")))); |
|
5690 |
|
5691 // Looks like we need to do the select... |
|
5692 DoSelectL(GetInbox(), ETrue); |
|
5693 } |
|
5694 } |
|
5695 else |
|
5696 { |
|
5697 DBG((LogText(_L8("Do not re-issue IDLE - cancel completed")))); |
|
5698 |
|
5699 // Cancelling completed - stay in this mailbox and do not issue idle. |
|
5700 iCancelAndIdle = EFalse; |
|
5701 } |
|
5702 } |
|
5703 break; |
|
5704 |
|
5705 case EImapStateFetchWait: |
|
5706 // Fetch has completed: any more parts to go? |
|
5707 iProgress.iPartsDone++; |
|
5708 |
|
5709 if (iFetchList->Count()) |
|
5710 { |
|
5711 // Yes, fetch next one |
|
5712 FetchAnItemL((*iFetchList)[0]); |
|
5713 iFetchList->Delete(0,1); |
|
5714 return; |
|
5715 } |
|
5716 |
|
5717 // Dispose of any buffers we've got left |
|
5718 delete iPartialLine; |
|
5719 iPartialLine=NULL; |
|
5720 delete iAttachmentFile; |
|
5721 iAttachmentFile=NULL; |
|
5722 delete iAttachmentFullPath; |
|
5723 iAttachmentFullPath=NULL; |
|
5724 delete iAttachmentMimeInfo; |
|
5725 iAttachmentMimeInfo=NULL; |
|
5726 // All done - ensure any changes are committed to disk |
|
5727 iEntry->CompleteBulk(); |
|
5728 iState=EImapStateSelected; |
|
5729 iSyncState=ENotSyncing; |
|
5730 break; |
|
5731 |
|
5732 case EImapStateLoginWait: |
|
5733 switch(aResult) |
|
5734 { |
|
5735 case KErrNone: |
|
5736 // Login OK: do we know the separator character? |
|
5737 if (!iHierarchySeparator.Length()) |
|
5738 { |
|
5739 // No, ask server for it |
|
5740 NewTag(); |
|
5741 iImapIO->SendL(iStatus,_L8("%d LIST \"\" \"\"\r\n"),iTag); |
|
5742 NewTagSent(); |
|
5743 iState=EImapStateSeparatorWait; |
|
5744 return; |
|
5745 } |
|
5746 |
|
5747 iState=EImapStateNoSelect; |
|
5748 break; |
|
5749 |
|
5750 case KErrIMAPNO: |
|
5751 case KErrImapBadLogon: |
|
5752 // Bad username/password |
|
5753 Fail(KErrImapBadLogon); |
|
5754 return; |
|
5755 } |
|
5756 break; |
|
5757 case EImapStateSelected: |
|
5758 { |
|
5759 // reset flag after stop idle completes |
|
5760 iCompoundStopIdle = EFalse; |
|
5761 iStoppingIdleForSync = EFalse; |
|
5762 } |
|
5763 case EImapStateCreateWait: |
|
5764 case EImapStateRenameWait: |
|
5765 case EImapStateDeleteWait: |
|
5766 case EImapStateSubscribeWait: |
|
5767 // Modification operation has happened: did it go OK? |
|
5768 if (aResult==KErrNone) |
|
5769 { |
|
5770 TMsvEmailEntry message; |
|
5771 SetEntryL(iCommandIds[0]); |
|
5772 |
|
5773 switch(iState) |
|
5774 { |
|
5775 case EImapStateCreateWait: |
|
5776 { |
|
5777 // We need to create the actual folder, as it now exists on the server |
|
5778 message.iType=KUidMsvFolderEntry; |
|
5779 message.iMtm=KUidMsgTypeIMAP4; |
|
5780 message.iServiceId=iServiceId; |
|
5781 message.SetMtmData1(0); |
|
5782 message.SetMtmData2(0); |
|
5783 message.SetMtmData3(0); |
|
5784 message.SetValidUID(EFalse); |
|
5785 message.SetMailbox(ETrue); // Default to creating a mailbox |
|
5786 message.SetComplete(ETrue); |
|
5787 message.iSize=0; |
|
5788 message.iDetails.Set(iCommandBuf); |
|
5789 iEntry->CreateEntry(message); |
|
5790 // Save the created id in the progress buffer |
|
5791 iProgress.iReturnedMsvId=message.Id(); |
|
5792 break; |
|
5793 } |
|
5794 |
|
5795 case EImapStateRenameWait: |
|
5796 { |
|
5797 // Modify the entry |
|
5798 message=iEntry->Entry(); |
|
5799 message.iDetails.Set(iCommandBuf); |
|
5800 ChangeEntryL(message); |
|
5801 break; |
|
5802 } |
|
5803 |
|
5804 case EImapStateDeleteWait: |
|
5805 // Delete the entry: we are set to the parent currently |
|
5806 iEntry->DeleteEntry(iCommandIds[1]); |
|
5807 break; |
|
5808 |
|
5809 case EImapStateSubscribeWait: |
|
5810 // Set/reset the subscription flag |
|
5811 message=iEntry->Entry(); |
|
5812 message.SetSubscribed(iCommandFlags[0]); |
|
5813 ChangeEntryL(message); |
|
5814 break; |
|
5815 |
|
5816 default: // To stop AER warnings |
|
5817 break; |
|
5818 } |
|
5819 |
|
5820 // Back to previous state |
|
5821 iState=iSavedState; |
|
5822 } |
|
5823 DBG((LogText(_L8("CImap4Session::CommandComplete(): iState=EImapStateSubscribeWait")))); |
|
5824 DBG((LogText(_L8("-----------------------------------------------------------")))); |
|
5825 DBG((LogText(_L8("CImap4Session::CommandComplete(): calling Complete()")))); |
|
5826 DBG((LogText(_L8("-----------------------------------------------------------")))); |
|
5827 Complete(aResult); |
|
5828 return; |
|
5829 |
|
5830 case EImapStateDeleteAllWait: |
|
5831 // Expunge the folder by closing |
|
5832 NewTag(); |
|
5833 iImapIO->SendL(iStatus,_L8("%d CLOSE\r\n"),iTag); |
|
5834 NewTagSent(); |
|
5835 iState=EImapStateDeleteFolderWait; |
|
5836 return; |
|
5837 |
|
5838 case EImapStateDeleteFolderWait: |
|
5839 { |
|
5840 // Delete all children in folder locally as they have all been expunged |
|
5841 SetEntryL(iMailboxId); |
|
5842 GetChildrenL(*iSelection); |
|
5843 TInt a=0; |
|
5844 while(a<iSelection->Count()) |
|
5845 iEntry->DeleteEntry((*iSelection)[a++]); |
|
5846 iSelection->Reset(); |
|
5847 |
|
5848 // Back to unselected as we've closed the folder |
|
5849 iState=EImapStateNoSelect; |
|
5850 break; |
|
5851 } |
|
5852 |
|
5853 case EImapStateSynchroniseWait: |
|
5854 { |
|
5855 if (aResult==KErrNone && iSyncState==ESyncSearch) |
|
5856 { |
|
5857 iFolderPosition+=KImapUidSearchSize; |
|
5858 // Check to see if we have all the messages. |
|
5859 if (iFolderPosition>=iMailboxSize) |
|
5860 { |
|
5861 DBG((LogText(_L8("UID search complete")))); |
|
5862 |
|
5863 // Set the mailbox size to the number of UIDs returned by the search |
|
5864 iMailboxSize = iSearchList->Count(); |
|
5865 |
|
5866 iSyncState=ESyncListOld; |
|
5867 // We have the full list of remote UIDs - fall through. |
|
5868 } |
|
5869 else |
|
5870 { |
|
5871 // Should be able to hit this code if KImapUidSearchSize is reduced to < the size |
|
5872 // of the remote mailbox (iMailboxSize) |
|
5873 // SearchString non 0 means a refined UID SEARCH is required |
|
5874 DBG((LogText(_L8("UID search - get next manageable block of UIDs")))); |
|
5875 NewTag(); |
|
5876 if(iServiceSettings->SearchString().Length() != 0) |
|
5877 { |
|
5878 // Refined search required |
|
5879 // Still uses the indexes but appends user specified search criteria |
|
5880 _LIT8(KSearchString,"%d UID SEARCH %d:%d %S\r\n"); |
|
5881 TPtrC8 ptr = iServiceSettings->SearchString(); |
|
5882 iImapIO->SendL(iStatus,KSearchString,iTag,iFolderPosition+1,Min(iMailboxSize,iFolderPosition+KImapUidSearchSize),&ptr); |
|
5883 } |
|
5884 else |
|
5885 // Normal unrefined SEARCH. Will pull back all the UIDs between indexes |
|
5886 { |
|
5887 _LIT8(KSearchString,"%d UID SEARCH %d:%d\r\n"); |
|
5888 iImapIO->SendL(iStatus,KSearchString,iTag,iFolderPosition+1,Min(iMailboxSize,iFolderPosition+KImapUidSearchSize)); |
|
5889 } |
|
5890 NewTagSent(); |
|
5891 return; |
|
5892 } |
|
5893 } |
|
5894 |
|
5895 if (aResult==KErrNone && iSyncState==ESyncListOld) |
|
5896 { |
|
5897 // At this point the remote command to search out folder UIDs has completed. This |
|
5898 // list will contain all UIDs for messages in the remote folder. This may be too |
|
5899 // many, and if so, the list is truncated to only include the N most recent |
|
5900 // messages in accordance with the synchronisation limit. |
|
5901 |
|
5902 TInt local=0; |
|
5903 TInt remote=0; |
|
5904 // if no UID Search string defined then the old logic is used |
|
5905 TInt syncThresh = 0; |
|
5906 if(iServiceSettings->SearchString().Length() != 0) |
|
5907 { |
|
5908 syncThresh = 0; |
|
5909 } |
|
5910 else // If no search string is set we will use the old behaviour |
|
5911 { |
|
5912 syncThresh=(iSyncLimit<iMailboxSize)?iMailboxSize-iSyncLimit:0; |
|
5913 } |
|
5914 TBool orphanThis=EFalse; |
|
5915 TBool folderPositionFound=EFalse; |
|
5916 iFolderPosition=0; |
|
5917 |
|
5918 // At this stage (prior to truncation), we have a full list of all the message |
|
5919 // UIDs in the remote folder. We also have a list of all UIDs for messages in |
|
5920 // the local folder. With these lists, we can establish which local messages |
|
5921 // have been orphaned and which have not using the following rules. |
|
5922 |
|
5923 // * If the remote message is no longer there: |
|
5924 // (1) Then the local message is orphaned (always). |
|
5925 // * If the remote message is still there and it is not one of the N most recent: |
|
5926 // (2) If the local message has body parts do nothing. |
|
5927 // (3) If the local message does not have body parts, then it is orphaned |
|
5928 // unless is is a message we have selected for download. |
|
5929 // * If the remote message is still there and it is one of the N most recent: |
|
5930 // (4) Do nothing. |
|
5931 |
|
5932 // Search through the local folder list while checking the remote folder list. |
|
5933 while (local<iFolderIndex.Size() && iFolderIndex[local].iMsvId!=-1) |
|
5934 { |
|
5935 orphanThis=EFalse; // Default orphaning of this message to false. |
|
5936 |
|
5937 // Find next uid in remote folder uid list that is >= local entry uid. |
|
5938 while (remote<iMailboxSize) |
|
5939 { |
|
5940 if (remote < iSearchList->Count() && |
|
5941 (*iSearchList)[remote]>=iFolderIndex[local].iUid) |
|
5942 break; |
|
5943 remote++; |
|
5944 } |
|
5945 |
|
5946 // Within scope of search list? |
|
5947 if (remote<iMailboxSize) |
|
5948 { |
|
5949 TBool inSyncRange=(remote>=syncThresh); |
|
5950 TBool uidMatch=((*iSearchList)[remote]==iFolderIndex[local].iUid); |
|
5951 TBool uidNewer=((*iSearchList)[remote]>iFolderIndex[local].iUid); |
|
5952 |
|
5953 // Folder position must point to 1st old local message that matches a |
|
5954 // remote message that will be sync'ed. |
|
5955 if (uidMatch && inSyncRange && !folderPositionFound) |
|
5956 { |
|
5957 iFolderPosition=local; |
|
5958 folderPositionFound=ETrue; |
|
5959 } |
|
5960 |
|
5961 if (uidNewer) |
|
5962 { |
|
5963 // Here the next remote uid is greater than the local uid indicating that |
|
5964 // a message has been removed on the remote folder and that this local |
|
5965 // message should be orphaned. See case (1) above. |
|
5966 |
|
5967 DBG((LogText(_L8("Message no longer available on remote server, orphaning")))); |
|
5968 orphanThis=ETrue; |
|
5969 } |
|
5970 else if (uidMatch && !inSyncRange) |
|
5971 { |
|
5972 // Here the remote uid matches the local uid, but the message falls outside |
|
5973 // of the N most recent messages. See cases (2) & (3). |
|
5974 |
|
5975 DBG((LogText(_L8("Local message old (%u)"),iFolderIndex[local].iUid))); |
|
5976 |
|
5977 SetEntryL(iFolderIndex[local].iMsvId); |
|
5978 TMsvEmailEntry message(iEntry->Entry()); |
|
5979 TBool inSyncSelection = EFalse; |
|
5980 |
|
5981 // Is the message part of the synchronisation selection? |
|
5982 // If so, we will want to view the message after the sync so don't delete. |
|
5983 if (iSynchronisationSelection && iSynchronisationSelection->Count() > 1) |
|
5984 { |
|
5985 if (iSynchronisationSelection->Find(iFolderIndex[local].iMsvId) != KErrNotFound) |
|
5986 { |
|
5987 inSyncSelection = ETrue; |
|
5988 } |
|
5989 } |
|
5990 |
|
5991 // Does message have any downloaded parts? |
|
5992 if (!message.Complete() && |
|
5993 !message.BodyTextComplete() && |
|
5994 !inSyncSelection) |
|
5995 { |
|
5996 // The local message does not have any body parts and |
|
5997 // is not selected for download, so it is orphaned. |
|
5998 // See case (3) above. |
|
5999 |
|
6000 DBG((LogText(_L8("Local message (%u) is only header and not selected for download, deleting"),iFolderIndex[local].iUid))); |
|
6001 orphanThis=ETrue; |
|
6002 } |
|
6003 } |
|
6004 } |
|
6005 else |
|
6006 { |
|
6007 // Outside of scope of search list, so none of the remaining local |
|
6008 // messages are present in the remote folder and therefore are orphaned. |
|
6009 // See case (1) above. |
|
6010 |
|
6011 DBG((LogText(_L8("Message no longer available on remote server, orphaning")))); |
|
6012 orphanThis=ETrue; |
|
6013 } |
|
6014 |
|
6015 // Orphan this one? |
|
6016 if (orphanThis) |
|
6017 { |
|
6018 OrphanMessageL(iFolderIndex[local].iMsvId); |
|
6019 iFolderIndex.Expunge(local+1); |
|
6020 iOrphanedMessages++; |
|
6021 } |
|
6022 else |
|
6023 { |
|
6024 // If we have arrive here, then the local message is one of the N most |
|
6025 // recent and still exists remotely OR it exists remotely, is not one of |
|
6026 // the N most recent and the local message had body parts. |
|
6027 // See cases (2) & (4) above. |
|
6028 |
|
6029 local++; |
|
6030 } |
|
6031 } // End of the big while() |
|
6032 |
|
6033 // Trim the list down to the most recent UIDs consistant with the sync limit. |
|
6034 if (syncThresh && iSearchList->Count() > syncThresh) |
|
6035 iSearchList->Delete(0,syncThresh); |
|
6036 |
|
6037 // So now "iFolderIndex" will only have messages that are in the remote folder. |
|
6038 // And therefore, the highest UID stored in "iFolderIndex" will be the most |
|
6039 // recent old message. Also, the lowest UID in "iSearchList" will be the oldest |
|
6040 // sync'able remote message. |
|
6041 |
|
6042 // Are there any old messages left? |
|
6043 if (iFolderIndex.Size() && folderPositionFound) |
|
6044 { |
|
6045 DBG((LogText(_L8("Updating flags for %d old messages (UIDs %u to %u)"), |
|
6046 iFolderIndex.Size(),(*iSearchList)[0],iHighestUid))); |
|
6047 |
|
6048 // Re-assign highest UID, the previous highest may no longer exist. |
|
6049 iHighestUid=iFolderIndex[iFolderIndex.Size()-1].iUid; |
|
6050 iSyncState=ESyncListNew; |
|
6051 |
|
6052 // Fetch old messages. |
|
6053 NewTag(); |
|
6054 // If a UID search string has been specified, the we should create the UID FETCH |
|
6055 // string from the UID integer list otherwise they'll all come down. |
|
6056 if(iServiceSettings->SearchString().Length() != 0) |
|
6057 { |
|
6058 CreateUidStringL(); // Construct the UID string from the UID list |
|
6059 TPtrC8 ptr(iUidString->Des()); |
|
6060 _LIT8(KFetchString,"%d UID FETCH %S (UID FLAGS)\r\n"); |
|
6061 iImapIO->SendL(iStatus,KFetchString,iTag,&ptr); |
|
6062 } |
|
6063 else |
|
6064 { |
|
6065 _LIT8(KFetchString,"%d UID FETCH %d:%d (UID FLAGS)\r\n"); |
|
6066 iImapIO->SendL(iStatus,KFetchString,iTag,(*iSearchList)[0],iHighestUid); |
|
6067 } |
|
6068 NewTagSent(); |
|
6069 return; |
|
6070 } |
|
6071 else |
|
6072 { |
|
6073 DBG((LogText(_L8("No old message headers to update")))); |
|
6074 |
|
6075 iSyncState=ESyncListNew; |
|
6076 iHighestUid=0; |
|
6077 // All remote messages are new - fall through. |
|
6078 } |
|
6079 } |
|
6080 |
|
6081 if (aResult==KErrNone && iSyncState==ESyncListNew) |
|
6082 { |
|
6083 // At this point, the remote command to fetch all old messages has completed. |
|
6084 // Now we can look at fetching all new messages. 'iHighestUid' will contain the |
|
6085 // highest UID of the old messages. The top entry in 'iSearchList' will contain |
|
6086 // the highest UID in the remote folder. This gives us the range of UID to fetch |
|
6087 // for new messages. |
|
6088 |
|
6089 // First check are there any new messages to fetch? If 'iHighestUid' is the highest |
|
6090 // UID locally and remotely, then we finished sync'ing when we completed the old |
|
6091 // sync. |
|
6092 if (iSearchList->Count() == 0) |
|
6093 { |
|
6094 DBG((LogText(_L8("Search List is empty")))); |
|
6095 } |
|
6096 else if (iHighestUid<(*iSearchList)[iSearchList->Count()-1]) |
|
6097 { |
|
6098 TUint32 uidLow=iHighestUid; |
|
6099 TUint32 uidHigh=(*iSearchList)[iSearchList->Count()-1]; |
|
6100 |
|
6101 // Only want new messages. |
|
6102 uidLow++; |
|
6103 |
|
6104 // Are there only new messages (and no old)? |
|
6105 if (iHighestUid==0) |
|
6106 { |
|
6107 // Set this to ensure range is correct. |
|
6108 uidLow=(*iSearchList)[0]; |
|
6109 } |
|
6110 |
|
6111 // Perform the new sync. |
|
6112 SynchroniseNewL(uidLow,uidHigh); |
|
6113 return; |
|
6114 } |
|
6115 else |
|
6116 { |
|
6117 DBG((LogText(_L8("No new message headers to sync")))); |
|
6118 |
|
6119 // Synchronisation complete - fall through. |
|
6120 } |
|
6121 iSyncState=ESyncNew; |
|
6122 } |
|
6123 |
|
6124 // Synchronising: moving on to the next state? |
|
6125 if (aResult==KErrNone && iSyncState==ESyncOld) |
|
6126 { |
|
6127 // At this point, the remote command to list old messages has |
|
6128 // completed: however, if there was a 'gap' at the end (ie messages at |
|
6129 // the end of our copy of the mailbox had been deleted) we won't have |
|
6130 // noticed it until this point as we woulnd't have got back in sync which |
|
6131 // happens when we get a UID which isn't sequential. |
|
6132 // So, as we now know there are no more messages in the range we know about |
|
6133 // locally, anything else must have been deleted remotely. |
|
6134 while(iFolderPosition<iFolderIndex.Size()) |
|
6135 { |
|
6136 // Orphan this message |
|
6137 DBG((LogText(_L8("Orphaning UID %u"),iFolderIndex[iFolderPosition].iUid))); |
|
6138 |
|
6139 // Do it |
|
6140 OrphanMessageL(iFolderIndex[iFolderPosition].iMsvId); |
|
6141 |
|
6142 // Remove it from the index |
|
6143 iFolderIndex.Expunge(iFolderPosition+1); |
|
6144 |
|
6145 // Increment stats |
|
6146 iOrphanedMessages++; |
|
6147 } |
|
6148 |
|
6149 // Anything to new sync? If we've processed all the messages up to |
|
6150 // iMailboxSize, then there's nothing left that might possibly be |
|
6151 // new |
|
6152 if (iMsgsDone<iMailboxSize) |
|
6153 { |
|
6154 // Do new sync |
|
6155 SynchroniseNewL(); |
|
6156 return; |
|
6157 } |
|
6158 |
|
6159 iSyncState=ESyncNew; |
|
6160 } |
|
6161 |
|
6162 if (aResult==KErrNone && iSyncState==ESyncNew) |
|
6163 { |
|
6164 // If there were any "missing" messages found during the sync that we should have |
|
6165 // mirrored previously, get these now. |
|
6166 if (iMissingUidLow!=0 && iMissingUidHigh!=0) |
|
6167 { |
|
6168 DBG((LogText(_L8("Missing messages detected %d - %d"),iMissingUidLow,iMissingUidHigh))); |
|
6169 |
|
6170 SynchroniseNewL(iMissingUidLow,iMissingUidHigh); |
|
6171 iMissingUidLow=iMissingUidHigh=0; |
|
6172 return; |
|
6173 } |
|
6174 } |
|
6175 |
|
6176 // If we got a NO response in the course of the sync, we've not fully sync'ed the folder, |
|
6177 // so we shouldn't update the last sync date, etc. BUT as NO isn't a fatal response, we should |
|
6178 // not disconnect. |
|
6179 if (aResult!=KErrIMAPNO) |
|
6180 { |
|
6181 // Are we configured to update the \seen flag on the server? |
|
6182 if (iServiceSettings->UpdatingSeenFlags()) |
|
6183 { |
|
6184 DBG((LogText(_L8("Sync completed: updating servers flags")))); |
|
6185 |
|
6186 // Now drop through to EImapStateSetSeenWait case |
|
6187 iState = EImapStateSetSeenWait; |
|
6188 iSyncState=ENotSyncing; |
|
6189 } |
|
6190 else |
|
6191 { |
|
6192 // Not updating servers \seen flags - use old code to continue after sync |
|
6193 // Back to selected state |
|
6194 iState=EImapStateSelected; |
|
6195 iSyncState=ENotSyncing; |
|
6196 |
|
6197 // We've synchronised the folder. Update iDate field to |
|
6198 // indicate last sync date of this folder. |
|
6199 // This also updates the 'folders done' count |
|
6200 SyncCompleteL(); |
|
6201 DBG((LogText(_L8("CImap4Session::CommandComplete(): iState=EImapStateSynchroniseWait")))); |
|
6202 DBG((LogText(_L8("-----------------------------------------------------------")))); |
|
6203 DBG((LogText(_L8("CImap4Session::CommandComplete(): calling Complete()")))); |
|
6204 DBG((LogText(_L8("-----------------------------------------------------------")))); |
|
6205 Complete(aResult); |
|
6206 return; |
|
6207 } |
|
6208 } |
|
6209 else |
|
6210 { |
|
6211 // NO isn't a fatal response.Ignore it,continue... |
|
6212 iState=EImapStateSelected; |
|
6213 iSyncState=ENotSyncing; |
|
6214 DBG((LogText(_L8("CImap4Session::CommandComplete(): iState=EImapStateSynchroniseWait, aResult == KErrIMAPNO")))); |
|
6215 DBG((LogText(_L8("-----------------------------------------------------------")))); |
|
6216 DBG((LogText(_L8("CImap4Session::CommandComplete(): calling Complete()")))); |
|
6217 DBG((LogText(_L8("-----------------------------------------------------------")))); |
|
6218 Complete(aResult); |
|
6219 return; |
|
6220 } |
|
6221 } |
|
6222 |
|
6223 case EImapStateSetSeenWait: // Only a valid state when iServiceSettings->UpdatingSeenFlags() is true |
|
6224 // We may not be able to set all the seen flags due to a large list, |
|
6225 // so call ProcessSeenFlagsL until False is returned |
|
6226 if (ProcessSeenFlagsL(ESetSeenFlag)) |
|
6227 { |
|
6228 // More have been processed - wait for response in same state. |
|
6229 return; |
|
6230 } |
|
6231 // Now drop through to EImapStateClearSeenWait state |
|
6232 iState=EImapStateClearSeenWait; |
|
6233 iSyncState=ENotSyncing; |
|
6234 |
|
6235 case EImapStateClearSeenWait: // Only a valid state when iServiceSettings->UpdatingSeenFlags() is true |
|
6236 // We may not be able to clear all the seen flags due to a large list, |
|
6237 // so call ProcessSeenFlagsL until False is returned |
|
6238 if (ProcessSeenFlagsL(EClearSeenFlag)) |
|
6239 { |
|
6240 // More have been processed - wait for response in same state. |
|
6241 return; |
|
6242 } |
|
6243 |
|
6244 // All seen flags processed - return to selected state |
|
6245 // Will only arrive here if iServiceSettings->UpdatingSeenFlags() is true |
|
6246 iState=EImapStateSelected; |
|
6247 iSyncState=ENotSyncing; |
|
6248 |
|
6249 // We've synchronised the folder. Update iDate field to |
|
6250 // indicate last sync date of this folder. |
|
6251 // This also updates the 'folders done' count |
|
6252 SyncCompleteL(); |
|
6253 DBG((LogText(_L8("CImap4Session::CommandComplete(): iState=EImapStateClearSeenWait")))); |
|
6254 DBG((LogText(_L8("-----------------------------------------------------------")))); |
|
6255 DBG((LogText(_L8("CImap4Session::CommandComplete(): calling Complete()")))); |
|
6256 DBG((LogText(_L8("-----------------------------------------------------------")))); |
|
6257 Complete(aResult); |
|
6258 return; |
|
6259 |
|
6260 case EImapStateLogoutWait: |
|
6261 DoDisconnect(); |
|
6262 break; |
|
6263 |
|
6264 case EImapStateDeleteMarkWait: |
|
6265 // Messages marked for deletion OK, send close command |
|
6266 SendMessageL(KIMAPC_CLOSE); |
|
6267 iState=EImapStateExpungeWait; |
|
6268 return; |
|
6269 |
|
6270 case EImapStateExpungeWait: |
|
6271 { |
|
6272 DBG((LogText(_L8("Close completed OK: expunging messages locally")))); |
|
6273 |
|
6274 // Expunge the messages locally, the close was successful |
|
6275 for(TInt a=0;a<iFolderIndex.Size();a++) |
|
6276 { |
|
6277 // If it's got a valid MsvId, delete it! |
|
6278 if (iFolderIndex[a].iMsvId) |
|
6279 { |
|
6280 DBG((LogText(_L8("Expunging message %x"),iFolderIndex[a].iMsvId))); |
|
6281 |
|
6282 DeleteMessageL(iFolderIndex[a].iMsvId); |
|
6283 } |
|
6284 } |
|
6285 |
|
6286 // Back to unselected state (close sucessful) |
|
6287 iState=EImapStateNoSelect; |
|
6288 break; |
|
6289 } |
|
6290 |
|
6291 case EImapStateIdleWait: |
|
6292 { |
|
6293 DBG((LogText(_L8("CImImap4Session::CommandCompleteL(): iState = EImapStateIdleWait")))); |
|
6294 if (iCompoundStopIdle) |
|
6295 { |
|
6296 // the reply from server is missed if |
|
6297 // compound command |
|
6298 GetReply(EFalse); |
|
6299 } |
|
6300 break; |
|
6301 } |
|
6302 |
|
6303 case EImapStateIdling: |
|
6304 { |
|
6305 DBG((LogText(_L8("CImImap4Session::CommandCompleteL(): iState = EImapStateIdling")))); |
|
6306 if (iReissueIdle) |
|
6307 { |
|
6308 // IDLE command has been re-issued after synchronise OR a re-issue |
|
6309 // after a cancel-and-idle (if idling before). |
|
6310 // NOTE - also unset cancel-and-idle flag here too. |
|
6311 iReissueIdle = EFalse; |
|
6312 iCancelAndIdle = EFalse; |
|
6313 IssueIdleRead(); |
|
6314 } |
|
6315 break; |
|
6316 } |
|
6317 |
|
6318 case EImapStateStopIdleWait: |
|
6319 { |
|
6320 DBG((LogText(_L8("Idling completed OK")))); |
|
6321 |
|
6322 iState=EImapStateSelected; |
|
6323 |
|
6324 if (FolderChanged()) |
|
6325 { |
|
6326 DBG((LogText(_L8("CImImap4Session::CommandCompleteL(): FolderChanged")))); |
|
6327 |
|
6328 // If the folder has changed then we are going to do a synchronise to |
|
6329 // ensure we have up to date message information. At the end of the |
|
6330 // synchronise we would normally reissue the idle command, however |
|
6331 // we don't want to do this if we are stopping idle due to a |
|
6332 // compound command or a sync command. The compound / sync active |
|
6333 // objects will reissue the idle themselves. |
|
6334 if(!iCompoundStopIdle && !iStoppingIdleForSync) |
|
6335 { |
|
6336 DBG((LogText(_L8("CImImap4Session::CommandComplete(): ReissueIdle = true")))); |
|
6337 iReissueIdle=ETrue; |
|
6338 } |
|
6339 DoSynchroniseL(EFalse); |
|
6340 return; |
|
6341 } |
|
6342 else if (iReissueIdle) |
|
6343 { |
|
6344 if( !iCancelAndIdle && !iIdleTimerExpired ) |
|
6345 { |
|
6346 // Don't unset the iReissueIdle flag - need it to be true to ensure |
|
6347 // that IssueIdleRead is issued once 'Done' is received. |
|
6348 // FYI - IssueIdleRead is normally called in the DoComplete method |
|
6349 // when the StartIdle request completes. If we've cancel-and-idle-ed, |
|
6350 // then DoComplete won't be called when we do actually go idle. |
|
6351 DBG((LogText(_L8("CImImap4Session::CommandComplete(): ReissueIdle = false")))); |
|
6352 iReissueIdle = EFalse; |
|
6353 } |
|
6354 iIdleTimerExpired = EFalse; |
|
6355 DoStartIdleL(); |
|
6356 return; |
|
6357 } |
|
6358 else if (iDisconnectAfterIdleStopped) |
|
6359 { |
|
6360 iDisconnectAfterIdleStopped=EFalse; |
|
6361 DoDisconnectL(); |
|
6362 return; |
|
6363 } |
|
6364 |
|
6365 break; |
|
6366 } |
|
6367 |
|
6368 default: |
|
6369 gPanic(ECommandCompleteInUnknownState); |
|
6370 return; |
|
6371 } |
|
6372 |
|
6373 // Completing an operation upwards: ensure iEntry is not pointing to anything |
|
6374 SetEntryL(NULL); |
|
6375 |
|
6376 DBG((LogText(_L8("CImap4Session::CommandComplete(): End of method")))); |
|
6377 DBG((LogText(_L8("-----------------------------------------------------------")))); |
|
6378 DBG((LogText(_L8("CImap4Session::CommandComplete(): calling Complete()")))); |
|
6379 DBG((LogText(_L8("-----------------------------------------------------------")))); |
|
6380 |
|
6381 Complete(KErrNone); |
|
6382 } |
|
6383 |
|
6384 |
|
6385 /* |
|
6386 Overrides CMsgActive::RunL() to allow check for errored completion of |
|
6387 autonomously initiated IO requests, specifically the reissue of IDLE |
|
6388 commands on idle timeout. |
|
6389 */ |
|
6390 void CImImap4Session::RunL() |
|
6391 { |
|
6392 TInt status=iStatus.Int(); |
|
6393 if (status < KErrNone && |
|
6394 iIdleTimerExpired && |
|
6395 (iState==EImapStateIdleWait || iState==EImapStateStopIdleWait)) |
|
6396 { |
|
6397 DoComplete(status); |
|
6398 } |
|
6399 else |
|
6400 { |
|
6401 // Behave exactly as CMsgActive::RunL |
|
6402 if (status>=KErrNone) |
|
6403 { |
|
6404 TRAPD(error,DoRunL()); // continue operations, may re-queue |
|
6405 __ASSERT_DEBUG(error==KErrNone || !IsActive(),User::Invariant()); // must not requeue in error situations |
|
6406 if (IsActive()) // requeud |
|
6407 return; |
|
6408 status=error; |
|
6409 } |
|
6410 Complete(status); |
|
6411 } |
|
6412 } |
|
6413 |
|
6414 |
|
6415 |
|
6416 // A child async process has completed |
|
6417 void CImImap4Session::DoRunL() |
|
6418 { |
|
6419 DBG((LogText(_L8("CImImap4Session::DoRunL(status=%d, state=%d)"),iStatus.Int(),iState))); |
|
6420 |
|
6421 // Did we have a send queued? If so, this is just the send completion: we |
|
6422 // now queue a receive and clear the send flag |
|
6423 if (iSendQueued) |
|
6424 { |
|
6425 DBG((LogText(_L8("CImImap4Session::DoRunL(): iSendQueued")))); |
|
6426 |
|
6427 // No send queued anymore... |
|
6428 iSendQueued=EFalse; |
|
6429 // Any problems sending? |
|
6430 if(iStatus.Int()!=KErrNone) |
|
6431 { |
|
6432 // Yes, humm... |
|
6433 DBG((LogText(_L8("Error during send %d"),iStatus.Int()))); |
|
6434 |
|
6435 // We have to Fail() here, as the connection might be screwed: |
|
6436 // we just don't know. Fail() will disconnect us and reset our |
|
6437 // internal state. |
|
6438 Fail(KErrImapSendFail); |
|
6439 } |
|
6440 else |
|
6441 { |
|
6442 // Have we just issued a partial fetch, ie waiting for body |
|
6443 // size? |
|
6444 if (iSizeWait) |
|
6445 { |
|
6446 // Queue partial line fetch |
|
6447 GetReply(ETrue); |
|
6448 } |
|
6449 else |
|
6450 { |
|
6451 // Queue full line read |
|
6452 GetReply(EFalse); |
|
6453 } |
|
6454 } |
|
6455 return; |
|
6456 } |
|
6457 |
|
6458 // Was a receive queued? If so, we need to get the new root pointer |
|
6459 if (iReceiveQueued) |
|
6460 { |
|
6461 DBG((LogText(_L8("CImImap4Session::DoRunL(): iReceiveQueued")))); |
|
6462 // Not anymore |
|
6463 iReceiveQueued=EFalse; |
|
6464 |
|
6465 // Get root |
|
6466 iRootAtom=iImapIO->RootAtom(); |
|
6467 |
|
6468 DBG((LogText(_L8("CImImap4Session::DoRunL(): Got root")))); |
|
6469 |
|
6470 // Did we get the whole line? |
|
6471 if (iStatus.Int()==KErrFoundEOL) |
|
6472 { |
|
6473 DBG((LogText(_L8("CImImap4Session::DoRunL(): got the whole line")))); |
|
6474 iGotWholeLine=ETrue; |
|
6475 iStatus=KErrNone; |
|
6476 } |
|
6477 else |
|
6478 { |
|
6479 DBG((LogText(_L8("CImImap4Session::DoRunL(): didn't got the whole line")))); |
|
6480 iGotWholeLine=EFalse; |
|
6481 } |
|
6482 } |
|
6483 |
|
6484 // Problems with connection? |
|
6485 if (iStatus.Int()!=KErrNone) |
|
6486 { |
|
6487 DBG((LogText(_L8("CImImap4Session::DoRunL(): Problems with connection")))); |
|
6488 if (iState==EImapStateConnectWait) |
|
6489 Fail(KErrImapConnectFail); |
|
6490 else |
|
6491 Fail(KErrImapServerFail); |
|
6492 |
|
6493 return; |
|
6494 } |
|
6495 |
|
6496 DBG((LogText(_L8("CImImap4Session::DoRunL(): checking iState")))); |
|
6497 |
|
6498 switch(iState) |
|
6499 { |
|
6500 // Waiting for connect response |
|
6501 case EImapStateConnectWait: |
|
6502 { |
|
6503 // Get the bearer idle timeout |
|
6504 TUint32 timeout; |
|
6505 User::LeaveIfError(iImapIO->GetLastSocketActivityTimeout(timeout)); |
|
6506 |
|
6507 // Sets timeout to iMtmData1. This is used by Imcm. |
|
6508 SetEntryL(iServiceId); |
|
6509 TMsvEntry entry = iEntry->Entry(); |
|
6510 entry.SetMtmData1(timeout); |
|
6511 iEntry->ChangeEntry(entry); |
|
6512 DBG((LogText(_L8("IdleTimeout %d"),timeout))); |
|
6513 |
|
6514 // Connected, queue receive for greeting line |
|
6515 iState=EImapStateGreetingWait; |
|
6516 GetReply(ETrue); |
|
6517 break; |
|
6518 } |
|
6519 |
|
6520 // Waiting for greeting line |
|
6521 case EImapStateGreetingWait: |
|
6522 { |
|
6523 TInt result=KErrNone; // To stop .AER warnings... |
|
6524 |
|
6525 // Process line |
|
6526 TRAPD(err,result=ProcessGreetingL()); |
|
6527 if (err!=KErrNone) |
|
6528 Fail(KErrImapServerFail); |
|
6529 else if (result!=KErrNone) |
|
6530 { |
|
6531 // Greeting process returned an error |
|
6532 DBG((LogText(_L8("CImap4Session::CommandComplete(): iState=EImapStateGreetingWait")))); |
|
6533 DBG((LogText(_L8("-----------------------------------------------------------")))); |
|
6534 DBG((LogText(_L8("CImap4Session::CommandComplete(): calling Complete()")))); |
|
6535 DBG((LogText(_L8("-----------------------------------------------------------")))); |
|
6536 |
|
6537 Complete(result); |
|
6538 } |
|
6539 else |
|
6540 { |
|
6541 SendCapabilityL(); |
|
6542 } |
|
6543 break; |
|
6544 } |
|
6545 |
|
6546 // Waiting for continuation response to send username |
|
6547 case EImapStateLoginSendUser: |
|
6548 { |
|
6549 // Looking for a '+' |
|
6550 CImapAtom *p=iRootAtom->ToChildL(); |
|
6551 |
|
6552 // '*' indicates an untagged message |
|
6553 if (p->Compare(KIMAP_UNTAGGED)) |
|
6554 { |
|
6555 // Process it |
|
6556 TRAPD(err,ProcessUntaggedL(p->ToNextL(),EFalse)); |
|
6557 |
|
6558 // A problem here is a server fail, nothing else |
|
6559 if (err!=KErrNone) |
|
6560 Fail(KErrImapServerFail); |
|
6561 } |
|
6562 else if (p->Compare(KIMAP_CONTINUATION)) |
|
6563 { |
|
6564 // Send username literal |
|
6565 |
|
6566 // Literal password? |
|
6567 if (iLiteralPassword) |
|
6568 { |
|
6569 // Turn off logging before sending |
|
6570 iImapIO->PerformLogging(EFalse); |
|
6571 iImapIO->SendL(iStatus,_L8("%S {%d}\r\n"),iUsername,iPassword->Length()); |
|
6572 iImapIO->PerformLogging(ETrue); |
|
6573 |
|
6574 iState=EImapStateLoginSendPassword; |
|
6575 } |
|
6576 else |
|
6577 { |
|
6578 // Turn off logging before sending |
|
6579 iImapIO->PerformLogging(EFalse); |
|
6580 iImapIO->SendL(iStatus,_L8("%S %S\r\n"),iUsername,iPassword); |
|
6581 iImapIO->PerformLogging(ETrue); |
|
6582 |
|
6583 iState=EImapStateLoginWait; |
|
6584 } |
|
6585 |
|
6586 iSendQueued=ETrue; |
|
6587 DBG((LogText(_L8("*******************************************************")))); |
|
6588 DBG((LogText(_L8("CImap4Session::DoRunL(): waiting for iImapIO to wake me")))); |
|
6589 DBG((LogText(_L8("*******************************************************")))); |
|
6590 |
|
6591 SetActive(); |
|
6592 } |
|
6593 break; |
|
6594 } |
|
6595 |
|
6596 // Waiting for continuation response to send password |
|
6597 case EImapStateLoginSendPassword: |
|
6598 { |
|
6599 // Looking for a '+' |
|
6600 CImapAtom *p=iRootAtom->ToChildL(); |
|
6601 |
|
6602 // '*' indicates an untagged message |
|
6603 if (p->Compare(KIMAP_UNTAGGED)) |
|
6604 { |
|
6605 // Process it |
|
6606 TRAPD(err,ProcessUntaggedL(p->ToNextL(),EFalse)); |
|
6607 |
|
6608 // A problem here is a server fail, nothing else |
|
6609 if (err!=KErrNone) |
|
6610 Fail(KErrImapServerFail); |
|
6611 } |
|
6612 else if (p->Compare(KIMAP_CONTINUATION)) |
|
6613 { |
|
6614 // Send password literal |
|
6615 iSendQueued=ETrue; |
|
6616 DBG((LogText(_L8("*******************************************************")))); |
|
6617 DBG((LogText(_L8("CImap4Session::DoRunL(): waiting for iImapIO to wake me")))); |
|
6618 DBG((LogText(_L8("*******************************************************")))); |
|
6619 |
|
6620 SetActive(); |
|
6621 // Turn off logging before sending |
|
6622 iImapIO->PerformLogging(EFalse); |
|
6623 iImapIO->SendL(iStatus,_L8("%S\r\n"),iPassword); |
|
6624 iImapIO->PerformLogging(ETrue); |
|
6625 |
|
6626 iState=EImapStateLoginWait; |
|
6627 } |
|
6628 break; |
|
6629 } |
|
6630 |
|
6631 case EImapStateSetSeenWait: // Wait for setting the send flags response |
|
6632 case EImapStateClearSeenWait: // Wait for clearing the send flags response |
|
6633 case EImapStateCapabilityWait: // Wait for reply to capability command |
|
6634 case EImapStateLoginWait: // Wait for reply to login command |
|
6635 case EImapStateCommandWait: // Generic OK/BAD command issued, wait for response |
|
6636 case EImapStateCreateWait: // Create command issued, wait for response |
|
6637 case EImapStateRenameWait: // Rename command issued, wait for response |
|
6638 case EImapStateDeleteWait: // Delete command issued, wait for response |
|
6639 case EImapStateDeleteAllWait: // DeleteAll command issued, wait for response |
|
6640 case EImapStateDeleteFolderWait:// DeleteAll has issued a folder delete, wait for response |
|
6641 case EImapStateSeparatorWait: // Wait for reply to null list command |
|
6642 case EImapStateSubscribeWait: // Wait for subscription command reply |
|
6643 case EImapStateSelectWait: // Wait for all select information |
|
6644 case EImapStateSynchroniseWait: // Wait for synchronise |
|
6645 case EImapStateListWait: // Wait for all list information |
|
6646 case EImapStateLsubWait: // Wait for all lsub information |
|
6647 case EImapStateAppendResultWait:// Wait for append result |
|
6648 case EImapStateDeleteMarkWait: // Marking messages for deletion before CLOSE |
|
6649 case EImapStateExpungeWait: // Messages expunged: remove them locally |
|
6650 case EImapStateExpungeAllWait: // Messages expunged from remote, remove them |
|
6651 case EImapStateCloseWait: // Wait for close to finish |
|
6652 case EImapStateLogoutWait: // Wait for logout |
|
6653 case EImapStateStartTLSWait: // Wait for starttls to return |
|
6654 case EImapStateIdleWait: |
|
6655 case EImapStateIdling: |
|
6656 case EImapStateStopIdleWait: |
|
6657 { |
|
6658 TInt result=KErrNone; // To stop .AER warnings... |
|
6659 |
|
6660 // Process it |
|
6661 TRAPD(err,result=ProcessCommandReplyL()); |
|
6662 if (err!=KErrNone) |
|
6663 { |
|
6664 // check for TLS errors |
|
6665 if (iState == EImapStateCapabilityWait && iSecurityState == ENegotiating) |
|
6666 err = KErrImapTLSNegotiateFailed; |
|
6667 |
|
6668 // We've had a fail during reply processing: pass it on |
|
6669 Fail(err); |
|
6670 } |
|
6671 else |
|
6672 { |
|
6673 switch(result) |
|
6674 { |
|
6675 case KErrNotReady: |
|
6676 { |
|
6677 if (IsIdling() && FolderChanged()) |
|
6678 { |
|
6679 DBG((LogText(_L8("CImImap4Session::DoRunL(): Calling DoStopIdleL")))); |
|
6680 DoStopIdleL(); |
|
6681 } |
|
6682 else if (IsIdling()) |
|
6683 { |
|
6684 IssueIdleRead(); |
|
6685 return; |
|
6686 } |
|
6687 else |
|
6688 { |
|
6689 // Need the next line/part of line |
|
6690 GetReply(EFalse); |
|
6691 } |
|
6692 break; |
|
6693 } |
|
6694 |
|
6695 default: |
|
6696 // Complete it if there's no error or if it's just a 'NO' |
|
6697 // reply (ie, KErrNotSupported) |
|
6698 if (result>=KErrNone || result==KErrNotSupported) |
|
6699 { |
|
6700 DBG((LogText(_L8("CImImap4Session::DoRunL(): No error - calling CommandComplete()")))); |
|
6701 CommandCompleteL(result); |
|
6702 } |
|
6703 else |
|
6704 Fail(result); |
|
6705 break; |
|
6706 } |
|
6707 } |
|
6708 break; |
|
6709 } |
|
6710 |
|
6711 case EImapStateAppendSizeWait: // Work out the size of the append |
|
6712 { |
|
6713 // Sizing operation completed, get the size |
|
6714 iCommandSize=iMessageSizer->MessageSize(); |
|
6715 delete iMessageSizer; |
|
6716 iMessageSizer=NULL; |
|
6717 |
|
6718 DBG((LogText(_L8("Sized message to %d bytes"),iCommandSize))); |
|
6719 |
|
6720 iProgress.iBytesToDo=iCommandSize; |
|
6721 iProgress.iBytesDone=0; |
|
6722 |
|
6723 // Make a sender, to actually send the text |
|
6724 delete iMessageSender; |
|
6725 iMessageSender=NULL; |
|
6726 iMessageSender=CImSendMessage::NewL(/*iFs,*/*iEntry); |
|
6727 |
|
6728 // Change in API, should be retrieving iSettings.SendCopyToSelf(), |
|
6729 // instead using default ESendNoCopy. |
|
6730 iMessageSender->InitialiseL(iCommandIds[0], ESendAsMimeEmail, |
|
6731 iMessageDate, iHost, iCharset, ESendNoCopy); |
|
6732 |
|
6733 // Make path to the message's destination, and send command |
|
6734 HBufC8* path=MakePathL(iCommandIds[1],ETrue); |
|
6735 |
|
6736 // Reset the entry to first param(message id), as it will be changed to |
|
6737 // second param(directory id) in the MakePathL function. This entry |
|
6738 // will be sent to destination as message(line by line) by CImSendMessage class. |
|
6739 SetEntryL(iCommandIds[0]); |
|
6740 |
|
6741 CleanupStack::PushL(path); |
|
6742 DoQuoteL(path); |
|
6743 TPtrC8 pathptr=path->Des(); |
|
6744 NewTag(); |
|
6745 iImapIO->SendL(iStatus,_L8("%d APPEND \"%S\" {%d}\r\n"),iTag,&pathptr,iCommandSize); |
|
6746 NewTagSent(); |
|
6747 CleanupStack::PopAndDestroy(); |
|
6748 |
|
6749 // Append the actual message |
|
6750 iState=EImapStateAppendPromptWait; |
|
6751 break; |
|
6752 } |
|
6753 |
|
6754 case EImapStateAppendPromptWait: // Wait for prompt before sending message body |
|
6755 { |
|
6756 // Looking for a '+' |
|
6757 CImapAtom *p=iRootAtom->ToChildL(); |
|
6758 |
|
6759 // '*' indicates an untagged message |
|
6760 if (p->Compare(KIMAP_UNTAGGED)) |
|
6761 { |
|
6762 // Process it |
|
6763 TRAPD(err,ProcessUntaggedL(p->ToNextL(),EFalse)); |
|
6764 if (err!=KErrNone) |
|
6765 Fail(err); |
|
6766 } |
|
6767 else if (p->Compare(KIMAP_CONTINUATION)) |
|
6768 { |
|
6769 // Start sending message body: move into sending body state |
|
6770 iState=EImapStateAppendWait; |
|
6771 |
|
6772 // Make line buffer |
|
6773 delete iLineBuffer; |
|
6774 iLineBuffer=NULL; |
|
6775 iLineBuffer=HBufC8::NewL(KImMailMaxBufferSize); |
|
6776 |
|
6777 // Send first line: Must be more than one line, so we don't bother |
|
6778 // with checking the return code. |
|
6779 TInt padcount=0; |
|
6780 TPtr8 line=iLineBuffer->Des(); |
|
6781 iMessageSender->NextLineL(line,padcount); |
|
6782 iImapIO->SendL(iStatus,_L8("%S"),&line); |
|
6783 |
|
6784 DBG((LogText(_L8("CImap4Session::DoRunL(): iState = EImapStateAppendWait")))); |
|
6785 DBG((LogText(_L8("*******************************************************")))); |
|
6786 DBG((LogText(_L8("CImap4Session::DoRunL(): waiting for iImapIO to wake me")))); |
|
6787 DBG((LogText(_L8("*******************************************************")))); |
|
6788 |
|
6789 SetActive(); |
|
6790 iSendQueued=EFalse; // We want to come back to here - we've not sent a command |
|
6791 iProgress.iBytesDone+=line.Length(); |
|
6792 |
|
6793 DBG((LogText(_L8("Sent: %d '%S'"),line.Length(),&line))); |
|
6794 } |
|
6795 break; |
|
6796 } |
|
6797 |
|
6798 case EImapStateAppendWait: |
|
6799 { |
|
6800 // Send line of message |
|
6801 TInt padcount=0; |
|
6802 TPtr8 line=iLineBuffer->Des(); |
|
6803 if (iMessageSender->NextLineL(line,padcount)==KImCvFinished) |
|
6804 { |
|
6805 // Send last line, plus CRLF to terminate command |
|
6806 iImapIO->SendL(iStatus,_L8("%S\r\n"),&line); |
|
6807 iProgress.iBytesDone+=line.Length(); |
|
6808 |
|
6809 DBG((LogText(_L8("Sent: %d '%S'"),line.Length(),&line))); |
|
6810 DBG((LogText(_L8("Total bytes sent %d plus the CRLF"),iProgress.iBytesDone))); |
|
6811 |
|
6812 delete iLineBuffer; |
|
6813 iLineBuffer=NULL; |
|
6814 delete iMessageSender; |
|
6815 iMessageSender=NULL; |
|
6816 iSendQueued=ETrue; |
|
6817 iState=EImapStateAppendResultWait; |
|
6818 } |
|
6819 else |
|
6820 { |
|
6821 // Send a line |
|
6822 iImapIO->SendL(iStatus,_L8("%S"),&line); |
|
6823 iSendQueued=EFalse; |
|
6824 iProgress.iBytesDone+=line.Length(); |
|
6825 |
|
6826 DBG((LogText(_L8("Sent: %d '%S'"),line.Length(),&line))); |
|
6827 } |
|
6828 |
|
6829 DBG((LogText(_L8("CImap4Session::DoRunL(): iState = EImapStateAppendWait")))); |
|
6830 DBG((LogText(_L8("*******************************************************")))); |
|
6831 DBG((LogText(_L8("CImap4Session::DoRunL(): waiting for iImapIO to wake me")))); |
|
6832 DBG((LogText(_L8("*******************************************************")))); |
|
6833 |
|
6834 SetActive(); |
|
6835 break; |
|
6836 } |
|
6837 |
|
6838 case EImapStateFetchCancelWait: |
|
6839 case EImapStateFetchWait:// Wait for body length/data |
|
6840 { |
|
6841 TInt result=KErrNone; // To stop .AER warnings... |
|
6842 |
|
6843 // Process it |
|
6844 TRAPD(err,result=ProcessCommandReplyL()); |
|
6845 |
|
6846 if (err!=KErrNone) |
|
6847 Fail(err); |
|
6848 else |
|
6849 { |
|
6850 //LogText(_L8("ProcessCommandReplyL() returned %d\n"),result); |
|
6851 switch(result) |
|
6852 { |
|
6853 case KErrNotReady: |
|
6854 // Still waiting for body size, another partial fetch |
|
6855 if (iSizeWait) |
|
6856 GetReply(ETrue); |
|
6857 else |
|
6858 GetReply(EFalse); |
|
6859 break; |
|
6860 case KErrImapInvalidServerResponse: |
|
6861 // Nothing to do , return back |
|
6862 break; |
|
6863 |
|
6864 case KErrWrite: |
|
6865 // Process has issued a command of its own, nothing for us to |
|
6866 // do here |
|
6867 break; |
|
6868 |
|
6869 default: |
|
6870 // Complete it |
|
6871 if (!iCommandsOutstanding) |
|
6872 { |
|
6873 DBG((LogText(_L8("CImap4Session::DoRunL(): No commands outstanding- calling CommandComplete()")))); |
|
6874 CommandCompleteL(result); |
|
6875 } |
|
6876 else |
|
6877 { |
|
6878 if (iJustSentFetch) |
|
6879 { |
|
6880 DBG((LogText(_L8("CImap4Session::DoRunL(): Just sent fetch")))); |
|
6881 iJustSentFetch = EFalse; |
|
6882 } |
|
6883 else |
|
6884 { |
|
6885 // Get next fetch result |
|
6886 DBG((LogText(_L8("CImap4Session::DoRunL(): NOT just sent fetch")))); |
|
6887 iSizeWait=ETrue; |
|
6888 GetReply(ETrue); |
|
6889 } |
|
6890 } |
|
6891 break; |
|
6892 } |
|
6893 } |
|
6894 break; |
|
6895 } |
|
6896 |
|
6897 case EImapStateMoveEntryWait: |
|
6898 { |
|
6899 // We're done with the moveentry |
|
6900 |
|
6901 // Park the move entry again |
|
6902 iMoveEntry->SetEntry(NULL); |
|
6903 |
|
6904 // Copy the structure: the MsvId is still the same, it's just not in the same |
|
6905 // place. |
|
6906 |
|
6907 //DS - Selectively using a copy or a move depending on user intention, so no need for this |
|
6908 //"copy structure back to mirror" malarky. |
|
6909 // TMsvId newid=CopyLevelL(iMoveSource,iMoveSourceFolder); |
|
6910 |
|
6911 // Note this ID in the iRelatedId, so that higher levels know where we are again... |
|
6912 |
|
6913 //DS iMoveSource could be either the same message moved to a different place |
|
6914 //or still the original message - could employ some logic for differentiating if there |
|
6915 //is some need to twiddle flags etc, but I'll leave that for someone else to do... |
|
6916 SetEntryL(iMoveSource); |
|
6917 TMsvEntry entry=iEntry->Entry(); |
|
6918 |
|
6919 entry=iEntry->Entry(); |
|
6920 entry.SetNew(EFalse); |
|
6921 ChangeEntryL(entry); |
|
6922 |
|
6923 // Park iEntry |
|
6924 SetEntryL(NULL); |
|
6925 |
|
6926 // Inform caller of new ID |
|
6927 //DS as far as I can tell, this isn't used! |
|
6928 //*iNewSource=newid; |
|
6929 |
|
6930 // Back to previous state |
|
6931 iState=iSavedState; |
|
6932 break; |
|
6933 } |
|
6934 |
|
6935 default: |
|
6936 gPanic(ERunLInUnknownState); |
|
6937 return; |
|
6938 } |
|
6939 DBG((LogText(_L8("CImImap4Session::DoRunL(): exiting...")))); |
|
6940 } |
|
6941 |
|
6942 |
|
6943 // The IMAP Idle case for doing a "GetReply". |
|
6944 // Called by CImImap4SessionIdleRead::Start() to get the whole thing going. |
|
6945 void CImImap4Session::DoIdleRead(TRequestStatus& aIdleReadStatus) |
|
6946 { |
|
6947 // Unlike a normal read, give the reponse to the CImImap4SessionIdleRead when done |
|
6948 iImapIO->GetReply(aIdleReadStatus); |
|
6949 iReceiveQueued=ETrue; |
|
6950 } |
|
6951 |
|
6952 void CImImap4Session::CancelIdleRead() |
|
6953 { |
|
6954 // Undo what DoIdleRead() did |
|
6955 iImapIO->Cancel(); |
|
6956 iReceiveQueued=EFalse; |
|
6957 } |
|
6958 |
|
6959 // The standard case for a normal transaction "GetReply" |
|
6960 // Queue request from IO layer to get the next atom |
|
6961 void CImImap4Session::GetReply(const TBool aPartialReturn) |
|
6962 { |
|
6963 // Cancel any dummy operation |
|
6964 if (ImapIdleSupported()==EFalse) |
|
6965 { |
|
6966 CancelDummy(); |
|
6967 } |
|
6968 |
|
6969 // Queue the receive |
|
6970 if (aPartialReturn) |
|
6971 { |
|
6972 // Get a partial line, as we need to see how much data is on its way |
|
6973 // for flood control/issuing next fetch command to keep it streaming. |
|
6974 iImapIO->GetReply(iStatus,80,ETrue); |
|
6975 } |
|
6976 else |
|
6977 { |
|
6978 // Get a *whole* line, we don't want a partial return |
|
6979 iImapIO->GetReply(iStatus); |
|
6980 } |
|
6981 |
|
6982 // Note that we have ??? |
|
6983 iReceiveQueued=ETrue; |
|
6984 DBG((LogText(_L8("*******************************************************")))); |
|
6985 DBG((LogText(_L8("CImap4Session::GetReply(): waiting for iImapIO to wake me")))); |
|
6986 DBG((LogText(_L8("*******************************************************")))); |
|
6987 |
|
6988 SetActive(); |
|
6989 } |
|
6990 |
|
6991 // Make a new tag, and queue send |
|
6992 void CImImap4Session::NewTag() |
|
6993 { |
|
6994 if (ImapIdleSupported()==EFalse) |
|
6995 { |
|
6996 // Cancel any dummy operation that might be outstanding |
|
6997 CancelDummy(); |
|
6998 } |
|
6999 |
|
7000 // Make a new tag |
|
7001 iTag++; |
|
7002 |
|
7003 // One more outstanding command |
|
7004 iCommandsOutstanding++; |
|
7005 } |
|
7006 |
|
7007 void CImImap4Session::NewTagSent() |
|
7008 { |
|
7009 // Go active and note that a send has been queued |
|
7010 SetActive(); |
|
7011 iSendQueued=ETrue; |
|
7012 } |
|
7013 |
|
7014 // Queue sending of a command line |
|
7015 void CImImap4Session::SendMessageL(const TDesC8& aMessage) |
|
7016 { |
|
7017 __ASSERT_DEBUG(iSendQueued==EFalse,gPanic(EAlreadySending)); |
|
7018 if(!(iSendQueued==EFalse)) |
|
7019 { |
|
7020 User::LeaveIfError(KErrInUse);//Already Sending |
|
7021 } |
|
7022 // Make a new tag |
|
7023 NewTag(); |
|
7024 iImapIO->SendL(iStatus,_L8("%d %S\r\n"),iTag,&aMessage); |
|
7025 NewTagSent(); |
|
7026 } |
|
7027 |
|
7028 void CImImap4Session::SendUntaggedMessageL(const TDesC8 &aMessage) |
|
7029 { |
|
7030 __ASSERT_DEBUG(iSendQueued==EFalse,gPanic(EAlreadySending)); |
|
7031 if(!(iSendQueued==EFalse)) |
|
7032 { |
|
7033 User::LeaveIfError(KErrInUse); // Already Sending |
|
7034 } |
|
7035 if (ImapIdleSupported()==EFalse) |
|
7036 { |
|
7037 // Cancel any dummy operation that might be outstanding |
|
7038 CancelDummy(); |
|
7039 } |
|
7040 |
|
7041 iImapIO->SendL(iStatus,KImapCommand,&aMessage); |
|
7042 DBG((LogText(_L8("*******************************************************")))); |
|
7043 DBG((LogText(_L8("CImap4Session::SendUntaggedMessageL(): waiting for iImapIO to wake me")))); |
|
7044 DBG((LogText(_L8("*******************************************************")))); |
|
7045 |
|
7046 SetActive(); |
|
7047 iSendQueued=ETrue; |
|
7048 } |
|
7049 |
|
7050 /** |
|
7051 Allows a untagged message to be sent to the server with a short idle timeout applied. |
|
7052 This is used when a fast response is expected |
|
7053 */ |
|
7054 void CImImap4Session::SendUntaggedMessageWithTimeoutL(const TDesC8 &aMessage, TInt aTimeout) |
|
7055 { |
|
7056 __ASSERT_ALWAYS(iSendQueued==EFalse,gPanic(EAlreadySending)); |
|
7057 if(!(iSendQueued==EFalse)) |
|
7058 { |
|
7059 User::LeaveIfError(KErrInUse); // Already Sending |
|
7060 } |
|
7061 if (ImapIdleSupported()==EFalse) |
|
7062 { |
|
7063 // Cancel any dummy operation that might be outstanding |
|
7064 CancelDummy(); |
|
7065 } |
|
7066 |
|
7067 iImapIO->SendWithTimeoutL(iStatus, aTimeout, KImapCommand, &aMessage); |
|
7068 DBG((LogText(_L8("*******************************************************")))); |
|
7069 DBG((LogText(_L8("CImap4Session::SendUntaggedMessageWithTimeoutL(): waiting for iImapIO to wake me")))); |
|
7070 DBG((LogText(_L8("*******************************************************")))); |
|
7071 |
|
7072 SetActive(); |
|
7073 iSendQueued=ETrue; |
|
7074 } |
|
7075 |
|
7076 // Construct a full mailbox path, given a TMsvId |
|
7077 // This is expensive in memory movement terms, as it works UP the path, |
|
7078 // inserting new data at the start. This is based on the principle that it's |
|
7079 // more expensive to find an entry in the index with SetEntryL() than it is to |
|
7080 // move some bytes about, otherwise we'd find the path upwards then create the |
|
7081 // string downwards. |
|
7082 HBufC8* CImImap4Session::MakePathL(const TMsvId aTarget, const TBool aIncludeLeaf) |
|
7083 { |
|
7084 __ASSERT_DEBUG(iState>=EImapStateNoSelect,gPanic(ENotLoggedOn)); |
|
7085 if(!(iState>=EImapStateNoSelect)) |
|
7086 { |
|
7087 User::LeaveIfError(KErrGeneral); |
|
7088 } |
|
7089 // Making a path: we start with nothing |
|
7090 HBufC8 *path=HBufC8::NewLC(256); |
|
7091 TBool skipfirst=ETrue; |
|
7092 TMsvId traverse=aTarget; |
|
7093 |
|
7094 // Move to the entry |
|
7095 SetEntryL(traverse); |
|
7096 |
|
7097 // Skipping the leaf? |
|
7098 if (!aIncludeLeaf && iEntry->Entry().iType!=KUidMsvServiceEntry) |
|
7099 { |
|
7100 // Up a level before we generate the path |
|
7101 SetEntryL(traverse=iEntry->Entry().Parent()); |
|
7102 } |
|
7103 |
|
7104 // check and see if we are dealing with the INBOX, in which case |
|
7105 // return immediately |
|
7106 if (iEntry->Entry().Parent()==iServiceId && |
|
7107 iEntry->Entry().iDetails.CompareF(KIMAP_INBOX)==0) |
|
7108 { |
|
7109 path->Des().Insert(0,_L8("INBOX")); |
|
7110 CleanupStack::Pop(); |
|
7111 return path; |
|
7112 } |
|
7113 |
|
7114 iCharConv->PrepareToConvertToFromOurCharsetL(KCharacterSetIdentifierImapUtf7); |
|
7115 |
|
7116 // While we can still go up within this service... |
|
7117 while(iEntry->Entry().iType!=KUidMsvServiceEntry) |
|
7118 { |
|
7119 // Add the name of this component to the path |
|
7120 if (!skipfirst) |
|
7121 path->Des().Insert(0,iHierarchySeparator); |
|
7122 else |
|
7123 skipfirst=EFalse; |
|
7124 |
|
7125 // this should be a better sized allocation but the path is |
|
7126 // fixed to 256 anyway so this will do |
|
7127 HBufC8* utf7=HBufC8::NewL(256); |
|
7128 CleanupStack::PushL(utf7); |
|
7129 |
|
7130 TInt numUC, indexUC; |
|
7131 TPtr8 des = utf7->Des(); |
|
7132 iCharConv->ConvertFromOurCharsetL(iEntry->Entry().iDetails, des, numUC, indexUC); |
|
7133 path->Des().Insert(0,utf7->Des()); |
|
7134 |
|
7135 CleanupStack::PopAndDestroy(); |
|
7136 |
|
7137 // Go up a level |
|
7138 SetEntryL(traverse=iEntry->Entry().Parent()); |
|
7139 } |
|
7140 |
|
7141 // Add the path at the very start, if it exists |
|
7142 if (iFolderPath.Length()) |
|
7143 { |
|
7144 // Anything there already? If not, don't bother with the separator |
|
7145 if (path->Des().Length()) path->Des().Insert(0,iHierarchySeparator); |
|
7146 path->Des().Insert(0,iFolderPath); |
|
7147 } |
|
7148 |
|
7149 // Pop it off cleanup stack |
|
7150 CleanupStack::Pop(); |
|
7151 |
|
7152 // Return the path |
|
7153 return(path); |
|
7154 } |
|
7155 |
|
7156 // Queue a connection |
|
7157 void CImImap4Session::ConnectL(TRequestStatus& aRequestStatus, const TMsvId aService) |
|
7158 { |
|
7159 LOG_COMMANDS((LogText(_L8("COMMAND Connect(%x)"),aService))); |
|
7160 __ASSERT_DEBUG(iState==EImapStateDisconnected,gPanic(EOpenWhenNotClosed)); |
|
7161 if(!(iState==EImapStateDisconnected)) |
|
7162 { |
|
7163 User::LeaveIfError(KErrInUse); // Open when not closed. |
|
7164 } |
|
7165 // Any progress we give now should be related to this connect, so we need to |
|
7166 // clear any previous progress first. |
|
7167 ResetStats(); |
|
7168 |
|
7169 Queue(aRequestStatus); |
|
7170 |
|
7171 // Get host details from server |
|
7172 iServiceId=aService; |
|
7173 SetEntryL(iServiceId); |
|
7174 |
|
7175 // get the iap preferences |
|
7176 CEmailAccounts* account = CEmailAccounts::NewLC(); |
|
7177 if (iPrefs == NULL) |
|
7178 { |
|
7179 iPrefs = CImIAPPreferences::NewLC(); |
|
7180 CleanupStack::Pop(iPrefs); |
|
7181 } |
|
7182 |
|
7183 TImapAccount id; |
|
7184 id.iImapAccountId = iEntry->Entry().MtmData2(); // iMtmData2 of the service entry contains TImapAccountId |
|
7185 id.iImapAccountName = iEntry->Entry().iDetails; |
|
7186 id.iImapService = iEntry->Entry().iServiceId; |
|
7187 id.iSmtpService = iEntry->Entry().iRelatedId; |
|
7188 |
|
7189 account->LoadImapSettingsL(id, *iServiceSettings); |
|
7190 account->LoadImapIapSettingsL(id, *iPrefs); |
|
7191 CleanupStack::PopAndDestroy(account); |
|
7192 |
|
7193 // Copy details |
|
7194 delete iUsername; //need to delete iUsername first, just in case already it has a value. |
|
7195 iUsername = NULL; |
|
7196 iUsername=iServiceSettings->LoginName().AllocL(); |
|
7197 delete iPassword; //need to delete iPassword first, just in case already it has a value. |
|
7198 iPassword = NULL; |
|
7199 iPassword=iServiceSettings->Password().AllocL(); |
|
7200 iFolderPath=iServiceSettings->FolderPath(); |
|
7201 iHost=iServiceSettings->ServerAddress(); |
|
7202 iPort=iServiceSettings->Port(); |
|
7203 iUseIdleCommand = iServiceSettings->ImapIdle(); |
|
7204 |
|
7205 iIdleTimeout = iServiceSettings->ImapIdleTimeout(); |
|
7206 DBG((LogText(_L8("ImapIdleTimeout %d"),iIdleTimeout))); |
|
7207 // convert from seconds to microseconds |
|
7208 iIdleTimeout *= 1000000; |
|
7209 |
|
7210 // Path separator: we store it as a string locally |
|
7211 iHierarchySeparator.Zero(); |
|
7212 if (iServiceSettings->PathSeparator()) |
|
7213 { |
|
7214 // Append to string |
|
7215 iHierarchySeparator.Append(iServiceSettings->PathSeparator()); |
|
7216 } |
|
7217 |
|
7218 // Any characters that will need quoting in them? |
|
7219 iLiteralUsername=EFalse; |
|
7220 int a; |
|
7221 TPtr8 userName = iUsername->Des(); |
|
7222 for(a=0;a<iUsername->Length();a++) |
|
7223 { |
|
7224 if (userName[a]<=32 || userName[a]>=127 || userName[a]=='\"' || userName[a]=='%' || |
|
7225 userName[a]=='(' || userName[a]==')' || userName[a]=='*' || userName[a]=='\\' || |
|
7226 userName[a]=='{' || userName[a]=='}' ) |
|
7227 { |
|
7228 iLiteralUsername=ETrue; |
|
7229 break; |
|
7230 } |
|
7231 } |
|
7232 |
|
7233 iLiteralPassword=EFalse; |
|
7234 TPtr8 passWord = iPassword->Des(); |
|
7235 for(a=0;a<iPassword->Length();a++) |
|
7236 { |
|
7237 if (passWord[a]<=32 || passWord[a]>=127 || passWord[a]=='\"' || passWord[a]=='%' || |
|
7238 passWord[a]=='(' || passWord[a]==')' || passWord[a]=='*' || passWord[a]=='\\' || |
|
7239 passWord[a]=='{' || passWord[a]=='}' ) |
|
7240 { |
|
7241 iLiteralPassword=ETrue; |
|
7242 break; |
|
7243 } |
|
7244 } |
|
7245 |
|
7246 // Until we know we're seeing CC:Mail... |
|
7247 iTalkingToCCMail=EFalse; |
|
7248 iTalkingToOpenMail=EFalse; |
|
7249 |
|
7250 // Start the connect |
|
7251 iState=EImapStateConnectWait; |
|
7252 iSendQueued=EFalse; |
|
7253 TBool sslWrappedSocket=iServiceSettings->SSLWrapper(); |
|
7254 |
|
7255 // if local primarysession is active then set the local textserversion of ImapIO object. |
|
7256 if(iPrimarySession) |
|
7257 { |
|
7258 // Setting of PrimaryTextServerSession, Going to be set on the secondary session. |
|
7259 iImapIO->SetPrimaryTextServerSession(iPrimarySession->GetImap4Session()->GetTextServerSession()); |
|
7260 } |
|
7261 iImapIO->ConnectL(iStatus,iHost,iPort,*iPrefs, sslWrappedSocket); |
|
7262 |
|
7263 #ifdef PRINTING |
|
7264 // Log version number now logfile is open |
|
7265 LogText(_L8("IMPS release 022.8")); |
|
7266 |
|
7267 // Log connection destination |
|
7268 LogText(_L8("Connection queued to %S, port %d"),&iHost,iPort); |
|
7269 |
|
7270 // Note any literal usage |
|
7271 if (iLiteralUsername) |
|
7272 LogText(_L8("Username contains unusual characters: using literal for username")); |
|
7273 if (iLiteralPassword) |
|
7274 LogText(_L8("Password contains unusual characters: using literal for password")); |
|
7275 #endif |
|
7276 SetActive(); |
|
7277 } |
|
7278 |
|
7279 // Queue a disconnection |
|
7280 void CImImap4Session::DisconnectL(TRequestStatus& aRequestStatus) |
|
7281 { |
|
7282 LOG_COMMANDS((LogText(_L8("COMMAND Disconnect")))); |
|
7283 Cancel(); |
|
7284 |
|
7285 Queue(aRequestStatus); |
|
7286 |
|
7287 // What are we doing at the moment? |
|
7288 if (iState<EImapStateNoSelect) |
|
7289 { |
|
7290 DoDisconnect(); |
|
7291 DBG((LogText(_L8("-----------------------------------------------------------")))); |
|
7292 DBG((LogText(_L8("CImap4Session::DisconnectL(): calling Complete()")))); |
|
7293 DBG((LogText(_L8("-----------------------------------------------------------")))); |
|
7294 |
|
7295 Complete(KErrNone); |
|
7296 } |
|
7297 else if (IsIdling()) |
|
7298 { |
|
7299 iDisconnectAfterIdleStopped = ETrue; |
|
7300 DoStopIdleL(); |
|
7301 } |
|
7302 else |
|
7303 { |
|
7304 DoDisconnectL(); |
|
7305 } |
|
7306 } |
|
7307 |
|
7308 void CImImap4Session::DoDisconnectL() |
|
7309 { |
|
7310 // Send logout command |
|
7311 iState=EImapStateLogoutWait; |
|
7312 SendMessageL(KIMAPC_LOGOUT); |
|
7313 } |
|
7314 |
|
7315 // Are we connected? |
|
7316 TBool CImImap4Session::Connected() |
|
7317 { |
|
7318 LOG_COMMANDS((LogText(_L8("COMMAND Connected?")))); |
|
7319 |
|
7320 if (iState>=EImapStateNoSelect) |
|
7321 return(ETrue); |
|
7322 return(EFalse); |
|
7323 } |
|
7324 |
|
7325 // Are we busy? |
|
7326 TBool CImImap4Session::Busy() |
|
7327 { |
|
7328 LOG_COMMANDS((LogText(_L8("COMMAND Busy?")))); |
|
7329 |
|
7330 if (iState==EImapStateDisconnected || |
|
7331 iState==EImapStateNoSelect || |
|
7332 iState==EImapStateSelected || |
|
7333 iState==EImapStateIdling) |
|
7334 return(EFalse); |
|
7335 return(ETrue); |
|
7336 } |
|
7337 |
|
7338 // Setting of PrimarySession, Going to be set on the secondary session. |
|
7339 void CImImap4Session::SetPrimarySession(CActiveWrapper* aPrimarySession) |
|
7340 { |
|
7341 iPrimarySession=aPrimarySession; |
|
7342 } |
|
7343 |
|
7344 // Return of current textserversession |
|
7345 CImTextServerSession* CImImap4Session::GetTextServerSession() |
|
7346 { |
|
7347 return iImapIO->GetTextServerSession(); |
|
7348 } |
|
7349 |
|
7350 // Return the service settings |
|
7351 CImImap4Settings* CImImap4Session::ServiceSettings() |
|
7352 { |
|
7353 // Return them |
|
7354 return(iServiceSettings); |
|
7355 } |
|
7356 |
|
7357 // List folder structure |
|
7358 void CImImap4Session::ListL(TRequestStatus& aRequestStatus, const TMsvId aFolder, CArrayPtr<CImImap4DirStruct>* aList) |
|
7359 { |
|
7360 LOG_COMMANDS((LogText(_L8("COMMAND List(%x)"),aFolder))); |
|
7361 __ASSERT_DEBUG(iState==EImapStateNoSelect || iState==EImapStateSelected,gPanic(ESelectWhenNotReady)); |
|
7362 if(!(iState==EImapStateNoSelect || iState==EImapStateSelected)) |
|
7363 { |
|
7364 User::LeaveIfError(KErrNotReady);// Select when not ready |
|
7365 } |
|
7366 Queue(aRequestStatus); |
|
7367 |
|
7368 // Form the path |
|
7369 HBufC8* path=NULL; |
|
7370 TRAPD(err,path=MakePathL(aFolder,ETrue)); |
|
7371 if (err!=KErrNone) |
|
7372 { |
|
7373 DBG((LogText(_L8("-----------------------------------------------------------")))); |
|
7374 DBG((LogText(_L8("CImap4Session::ListL(): calling Complete()")))); |
|
7375 DBG((LogText(_L8("-----------------------------------------------------------")))); |
|
7376 |
|
7377 Complete(err); |
|
7378 return; |
|
7379 } |
|
7380 CleanupStack::PushL(path); |
|
7381 |
|
7382 // Empty path? If not, append hierarchy separator |
|
7383 if (path->Length()) |
|
7384 { |
|
7385 // Get more space |
|
7386 HBufC8 *rpath=path->ReAllocL(path->Length()+1); |
|
7387 |
|
7388 // Moved? |
|
7389 if (path!=rpath) |
|
7390 { |
|
7391 // Get rid of old one and push new one |
|
7392 CleanupStack::Pop(); |
|
7393 CleanupStack::PushL(path=rpath); |
|
7394 } |
|
7395 |
|
7396 path->Des().Append(iHierarchySeparator); |
|
7397 } |
|
7398 |
|
7399 // Save path, as we take this off replies to get the leaf: we need to |
|
7400 // do this *before* we quote it as replies will be unquoted by the |
|
7401 // time we process them. |
|
7402 iCommandBuf.Copy(*path); |
|
7403 |
|
7404 // Quote it |
|
7405 DoQuoteL(path); |
|
7406 |
|
7407 // Send the command |
|
7408 NewTag(); |
|
7409 iImapIO->SendL(iStatus,_L8("%d LIST \"\" \"%S%%\"\r\n"),iTag,path); |
|
7410 NewTagSent(); |
|
7411 |
|
7412 // Save list pointer to add to, and reset it |
|
7413 iList=aList; |
|
7414 iList->ResetAndDestroy(); |
|
7415 |
|
7416 // Dispose of path |
|
7417 CleanupStack::PopAndDestroy(); |
|
7418 |
|
7419 // Save last state (selected/unselected) for restoring afterwards |
|
7420 iSavedState=iState; |
|
7421 iState=EImapStateListWait; |
|
7422 } |
|
7423 |
|
7424 // Update subscribed bits on local folder structure |
|
7425 // Local structure must have been refreshed before this is used: |
|
7426 // if folders listed in the LSUB reply don't exist, they're ignored |
|
7427 // silently. |
|
7428 void CImImap4Session::LsubL(TRequestStatus& aRequestStatus) |
|
7429 { |
|
7430 LOG_COMMANDS((LogText(_L8("COMMAND Lsub")))); |
|
7431 __ASSERT_DEBUG(iState==EImapStateNoSelect || iState==EImapStateSelected,gPanic(ESelectWhenNotReady)); |
|
7432 if(!(iState==EImapStateNoSelect || iState==EImapStateSelected)) |
|
7433 { |
|
7434 User::LeaveIfError(KErrNotReady); |
|
7435 } |
|
7436 |
|
7437 Queue(aRequestStatus); |
|
7438 |
|
7439 // First, we need to go through the entire service, resetting all |
|
7440 // the 'remote subscribed' flags. |
|
7441 ResetSubscriptionFlagsL(iServiceId); |
|
7442 |
|
7443 // Build a buffer to quote the folder path (it may be necessary) |
|
7444 HBufC8* path=HBufC8::NewL(iFolderPath.Length()+1); |
|
7445 CleanupStack::PushL(path); |
|
7446 path->Des().Append(iFolderPath); |
|
7447 if (iFolderPath.Length()) |
|
7448 path->Des().Append(iHierarchySeparator); |
|
7449 |
|
7450 // Quote it |
|
7451 DoQuoteL(path); |
|
7452 |
|
7453 // Send the command to list all of the folders in the tree: we |
|
7454 // can't do it hierarchically, as the servers aren't clever enough |
|
7455 // to tell us at parent levels about folders that contain subscribed |
|
7456 // children. Pah. |
|
7457 NewTag(); |
|
7458 |
|
7459 // Make a Des & send it |
|
7460 TPtrC8 pathdes(path->Des()); |
|
7461 iImapIO->SendL(iStatus,_L8("%d LSUB \"\" \"%S*\"\r\n"), |
|
7462 iTag,&pathdes); |
|
7463 NewTagSent(); |
|
7464 |
|
7465 // Clear up |
|
7466 CleanupStack::PopAndDestroy(); |
|
7467 |
|
7468 // Wait for reply |
|
7469 iSavedState=iState; |
|
7470 iState=EImapStateLsubWait; |
|
7471 } |
|
7472 |
|
7473 |
|
7474 // Create a mailbox or folder |
|
7475 void CImImap4Session::Create(TRequestStatus& aRequestStatus, const TMsvId aParent, const TDesC& aLeafName, const TBool aFolder) |
|
7476 { |
|
7477 TInt err=KErrNone; |
|
7478 if (!Connected()) |
|
7479 { |
|
7480 Queue(aRequestStatus); |
|
7481 err=KErrDisconnected; |
|
7482 } |
|
7483 else |
|
7484 TRAP(err,CreateL(aRequestStatus, aParent, aLeafName, aFolder)); |
|
7485 if (err!=KErrNone) |
|
7486 { |
|
7487 DBG((LogText(_L8("-----------------------------------------------------------")))); |
|
7488 DBG((LogText(_L8("CImap4Session::Create(): calling Complete()")))); |
|
7489 DBG((LogText(_L8("-----------------------------------------------------------")))); |
|
7490 |
|
7491 Complete(err); |
|
7492 } |
|
7493 } |
|
7494 |
|
7495 // Create a mailbox or folder |
|
7496 void CImImap4Session::CreateL(TRequestStatus& aRequestStatus, const TMsvId aParent, const TDesC& aLeafName, const TBool aFolder) |
|
7497 { |
|
7498 LOG_COMMANDS((LogText(_L8("COMMAND Create(%x,%S,%d)"),aParent,&aLeafName,aFolder))); |
|
7499 __ASSERT_DEBUG(iState==EImapStateNoSelect || iState==EImapStateSelected,gPanic(ECreateWhenNotReady)); |
|
7500 if(!(iState==EImapStateNoSelect || iState==EImapStateSelected)) |
|
7501 { |
|
7502 User::LeaveIfError(KErrNotReady); |
|
7503 } |
|
7504 Queue(aRequestStatus); |
|
7505 |
|
7506 // Make the path |
|
7507 iSavedState=iState; |
|
7508 iState=EImapStateCreateWait; |
|
7509 HBufC8 *path=NULL; // To stop .AER warnings... |
|
7510 TRAPD(err,path=MakePathL(aParent,ETrue)); |
|
7511 if (err!=KErrNone) |
|
7512 { |
|
7513 DBG((LogText(_L8("-----------------------------------------------------------")))); |
|
7514 DBG((LogText(_L8("CImap4Session::CreateL(): calling Complete()")))); |
|
7515 DBG((LogText(_L8("-----------------------------------------------------------")))); |
|
7516 |
|
7517 Complete(err); |
|
7518 return; |
|
7519 } |
|
7520 CleanupStack::PushL(path); |
|
7521 |
|
7522 // Increase length of buffer for full name |
|
7523 TInt encodedLeafMaxSize = 2+aLeafName.Size()*4/3 + 1; |
|
7524 HBufC8* rpath=path->ReAllocL(path->Length()+2+encodedLeafMaxSize); |
|
7525 |
|
7526 // Moved? |
|
7527 if (rpath!=path) |
|
7528 { |
|
7529 // Destroy old one and push new one |
|
7530 CleanupStack::Pop(); |
|
7531 CleanupStack::PushL(path=rpath); |
|
7532 } |
|
7533 |
|
7534 // Path not blank? Put a separator in there |
|
7535 if (path->Des().Length()) |
|
7536 path->Des().Append(iHierarchySeparator); |
|
7537 |
|
7538 iCharConv->PrepareToConvertToFromOurCharsetL(KCharacterSetIdentifierImapUtf7); |
|
7539 |
|
7540 // Add leafname, via the utf7 encoder |
|
7541 HBufC8* utf7=HBufC8::NewL(encodedLeafMaxSize); |
|
7542 CleanupStack::PushL(utf7); |
|
7543 |
|
7544 TInt numUC, indexUC; |
|
7545 TPtr8 des = utf7->Des(); |
|
7546 iCharConv->ConvertFromOurCharsetL(aLeafName, des, numUC, indexUC); |
|
7547 path->Des().Append(des); |
|
7548 |
|
7549 CleanupStack::PopAndDestroy(); // utf7 |
|
7550 |
|
7551 // Put a trailing hierarchy separator on there too if necessary |
|
7552 if (aFolder) |
|
7553 path->Des().Append(iHierarchySeparator); |
|
7554 |
|
7555 // Quote it if necessary |
|
7556 DoQuoteL(path); |
|
7557 TPtrC8 pathdes(path->Des()); |
|
7558 |
|
7559 // Creating a folder |
|
7560 NewTag(); |
|
7561 iImapIO->SendL(iStatus,_L8("%d CREATE \"%S\"\r\n"),iTag,&pathdes); |
|
7562 NewTagSent(); |
|
7563 |
|
7564 // Save what type of create we were doing |
|
7565 iCommandFlags[0]=aFolder; |
|
7566 iCommandIds[0]=aParent; |
|
7567 iCommandBuf=aLeafName; |
|
7568 |
|
7569 // Free memory |
|
7570 CleanupStack::PopAndDestroy(); |
|
7571 } |
|
7572 |
|
7573 // Rename a mailbox |
|
7574 void CImImap4Session::Rename(TRequestStatus& aRequestStatus, const TMsvId aTarget, const TDesC& aNewName) |
|
7575 { |
|
7576 TInt err=KErrNone; |
|
7577 if (!Connected()) |
|
7578 { |
|
7579 Queue(aRequestStatus); |
|
7580 err=KErrDisconnected; |
|
7581 } |
|
7582 else |
|
7583 TRAP(err,RenameL(aRequestStatus, aTarget, aNewName)); |
|
7584 if (err!=KErrNone) |
|
7585 Complete(err); |
|
7586 } |
|
7587 |
|
7588 // Rename a mailbox |
|
7589 void CImImap4Session::RenameL(TRequestStatus& aRequestStatus, const TMsvId aTarget, const TDesC& aNewName) |
|
7590 { |
|
7591 LOG_COMMANDS((LogText(_L8("COMMAND Rename(%x,%S)"),aTarget,&aNewName))); |
|
7592 __ASSERT_DEBUG(iState==EImapStateNoSelect || iState==EImapStateSelected,gPanic(ERenameWhenNotReady)); |
|
7593 if(!(iState==EImapStateNoSelect || iState==EImapStateSelected)) |
|
7594 { |
|
7595 User::LeaveIfError(KErrNotReady); |
|
7596 } |
|
7597 |
|
7598 Queue(aRequestStatus); |
|
7599 |
|
7600 // Make the paths |
|
7601 HBufC8* path=NULL; // To stop .AER warnings... |
|
7602 TRAPD(err,path=MakePathL(aTarget,ETrue)); |
|
7603 if (err!=KErrNone) |
|
7604 { |
|
7605 Complete(err); |
|
7606 return; |
|
7607 } |
|
7608 CleanupStack::PushL(path); |
|
7609 |
|
7610 DoQuoteL(path); |
|
7611 |
|
7612 HBufC8* newpath=NULL; // To stop .AER warnings... |
|
7613 TRAP(err,newpath=MakePathL(aTarget,EFalse)); |
|
7614 if (err!=KErrNone) |
|
7615 { |
|
7616 CleanupStack::PopAndDestroy(); |
|
7617 Complete(err); |
|
7618 return; |
|
7619 } |
|
7620 CleanupStack::PushL(newpath); |
|
7621 |
|
7622 // Extend buffer for new name |
|
7623 TInt encodedLeafMaxSize = 2+aNewName.Size()*4/3 + 1; |
|
7624 HBufC8* rnewpath=newpath->ReAllocL(newpath->Length()+2+encodedLeafMaxSize); |
|
7625 |
|
7626 // Moved? |
|
7627 if (rnewpath!=newpath) |
|
7628 { |
|
7629 // Destroy old one and push new one |
|
7630 CleanupStack::Pop(); |
|
7631 CleanupStack::PushL(newpath=rnewpath); |
|
7632 } |
|
7633 |
|
7634 if (newpath->Length()) |
|
7635 newpath->Des().Append(iHierarchySeparator); |
|
7636 |
|
7637 iCharConv->PrepareToConvertToFromOurCharsetL(KCharacterSetIdentifierImapUtf7); |
|
7638 |
|
7639 // Add leafname, via the utf7 encoder |
|
7640 HBufC8* utf7=HBufC8::NewL(encodedLeafMaxSize); |
|
7641 CleanupStack::PushL(utf7); |
|
7642 |
|
7643 TInt numUC, indexUC; |
|
7644 TPtr8 des = utf7->Des(); |
|
7645 iCharConv->ConvertFromOurCharsetL(aNewName, des, numUC, indexUC); |
|
7646 newpath->Des().Append(des); |
|
7647 |
|
7648 CleanupStack::PopAndDestroy(); // utf7 |
|
7649 |
|
7650 DoQuoteL(newpath); |
|
7651 |
|
7652 // Save rename parameters |
|
7653 iCommandIds[0]=aTarget; |
|
7654 iCommandBuf=aNewName; // this is still the original, unencoded name |
|
7655 |
|
7656 // Set states |
|
7657 iSavedState=iState; |
|
7658 iState=EImapStateRenameWait; |
|
7659 |
|
7660 // Send the command |
|
7661 NewTag(); |
|
7662 TPtrC8 pathdes(path->Des()); |
|
7663 TPtrC8 newpathdes(newpath->Des()); |
|
7664 iImapIO->SendL(iStatus,_L8("%d RENAME \"%S\" \"%S\"\r\n"),iTag,&pathdes,&newpathdes); |
|
7665 NewTagSent(); |
|
7666 |
|
7667 // Free memory |
|
7668 CleanupStack::PopAndDestroy(2); |
|
7669 } |
|
7670 |
|
7671 // Delete a message/mailbox |
|
7672 void CImImap4Session::Delete(TRequestStatus& aRequestStatus, const CMsvEntrySelection& aTargetSel) |
|
7673 { |
|
7674 TInt err=KErrNone; |
|
7675 if (!Connected()) |
|
7676 err=KErrDisconnected; |
|
7677 else |
|
7678 TRAP(err,DeleteL(aRequestStatus,aTargetSel)); |
|
7679 if (err!=KErrNone) |
|
7680 { |
|
7681 Queue(aRequestStatus); |
|
7682 Complete(err); |
|
7683 } |
|
7684 } |
|
7685 |
|
7686 // Delete a message/mailbox |
|
7687 void CImImap4Session::Delete(TRequestStatus& aRequestStatus, const TMsvId aTarget) |
|
7688 { |
|
7689 TInt err=KErrNone; |
|
7690 if (!Connected()) |
|
7691 err=KErrDisconnected; |
|
7692 else |
|
7693 TRAP(err,DeleteL(aRequestStatus,aTarget)); |
|
7694 if (err!=KErrNone) |
|
7695 { |
|
7696 Queue(aRequestStatus); |
|
7697 Complete(err); |
|
7698 } |
|
7699 } |
|
7700 void CImImap4Session::DeleteEntryL( const TMsvId aTarget) |
|
7701 { |
|
7702 LOG_COMMANDS((LogText(_L8("COMMAND Delete(%x)"),aTarget))); |
|
7703 |
|
7704 // Move to the entry in question |
|
7705 SetEntryL(aTarget); |
|
7706 |
|
7707 LOG_COMMANDS(( LogText(_L8("COMMAND Delete(message)")))); |
|
7708 __ASSERT_DEBUG(iState==EImapStateSelected,gPanic(EDeleteWhenNotReady)); |
|
7709 if(!(iState==EImapStateSelected)) |
|
7710 { |
|
7711 User::LeaveIfError(KErrNotReady); |
|
7712 } |
|
7713 |
|
7714 // SJM, remove check for right mailbox as we may be trying to |
|
7715 // delete a moved entry which is in fact no longer in the |
|
7716 // right mailbox. |
|
7717 #if 0 |
|
7718 // Check we're in the right mailbox |
|
7719 if (iEntry->Entry().Parent()!=iMailboxId) |
|
7720 { |
|
7721 // Nope. |
|
7722 Queue(aRequestStatus); |
|
7723 Complete(KErrImapWrongFolder); |
|
7724 return; |
|
7725 } |
|
7726 #endif |
|
7727 |
|
7728 // Set deleted flag on this entry |
|
7729 TMsvEmailEntry entry=iEntry->Entry(); |
|
7730 entry.SetDeletedIMAP4Flag(ETrue); |
|
7731 ChangeEntryL(entry); |
|
7732 } |
|
7733 |
|
7734 void CImImap4Session::DeleteL(TRequestStatus& aRequestStatus, const CMsvEntrySelection& aTargetSel) |
|
7735 { |
|
7736 LOG_COMMANDS((LogText(_L8("COMMAND Delete (%x)"),aTargetSel[0]))); |
|
7737 |
|
7738 // Move to the entry in question |
|
7739 SetEntryL(aTargetSel[0]); |
|
7740 |
|
7741 CMsvEntrySelection* sel=aTargetSel.CopyL(); |
|
7742 delete iSelection; |
|
7743 iSelection=sel; |
|
7744 |
|
7745 // Only deleting message seletion currently |
|
7746 if (iEntry->Entry().iType==KUidMsvMessageEntry) |
|
7747 { |
|
7748 // Set delete flag on all selected entries. |
|
7749 TInt count=iSelection->Count(); |
|
7750 while (count--) |
|
7751 DeleteEntryL((*iSelection)[count]); |
|
7752 |
|
7753 // Force a folder close with expunge |
|
7754 CloseL(aRequestStatus,ETrue); |
|
7755 } |
|
7756 else |
|
7757 { |
|
7758 LOG_COMMANDS(( LogText(_L8("COMMAND Delete - Can only delete selection of Messages")))); |
|
7759 |
|
7760 // Deleting selection of entries whicxh are not messages |
|
7761 Queue(aRequestStatus); |
|
7762 Complete(KErrNotSupported); |
|
7763 } |
|
7764 } |
|
7765 |
|
7766 void CImImap4Session::DeleteL(TRequestStatus& aRequestStatus, const TMsvId aTarget) |
|
7767 { |
|
7768 LOG_COMMANDS((LogText(_L8("COMMAND Delete(%x)"),aTarget))); |
|
7769 |
|
7770 // Move to the entry in question |
|
7771 SetEntryL(aTarget); |
|
7772 |
|
7773 // A message? |
|
7774 if (iEntry->Entry().iType==KUidMsvMessageEntry) |
|
7775 { |
|
7776 DeleteEntryL(aTarget); |
|
7777 |
|
7778 // Temporary: force a folder close with expunge |
|
7779 CloseL(aRequestStatus,ETrue); |
|
7780 } |
|
7781 // A folder? |
|
7782 else if (iEntry->Entry().iType==KUidMsvFolderEntry) |
|
7783 { |
|
7784 LOG_COMMANDS(( LogText(_L8("COMMAND Delete(folder)")))); |
|
7785 __ASSERT_DEBUG(iState==EImapStateNoSelect,gPanic(EDeleteWhenNotReady)); |
|
7786 if(!(iState==EImapStateNoSelect)) |
|
7787 { |
|
7788 User::LeaveIfError(KErrNotReady); |
|
7789 } |
|
7790 |
|
7791 Queue(aRequestStatus); |
|
7792 |
|
7793 // Save IDs of parent and target for actually doing the local delete when |
|
7794 // the remote one completes successfully. |
|
7795 iCommandIds[0]=iEntry->Entry().Parent(); |
|
7796 iCommandIds[1]=aTarget; |
|
7797 |
|
7798 // Get path to delete |
|
7799 HBufC8* path=NULL; // To stop .AER warnings... |
|
7800 path=MakePathL(aTarget,ETrue); |
|
7801 CleanupStack::PushL(path); |
|
7802 DoQuoteL(path); |
|
7803 |
|
7804 // Set state |
|
7805 iSavedState=iState; |
|
7806 iState=EImapStateDeleteWait; |
|
7807 |
|
7808 // Send command |
|
7809 NewTag(); |
|
7810 TPtrC8 pathdes(path->Des()); |
|
7811 iImapIO->SendL(iStatus,_L8("%d DELETE \"%S\"\r\n"),iTag,&pathdes); |
|
7812 NewTagSent(); |
|
7813 |
|
7814 // Destroy buffer |
|
7815 CleanupStack::PopAndDestroy(); |
|
7816 } |
|
7817 // Something else? |
|
7818 else |
|
7819 { |
|
7820 LOG_COMMANDS(( LogText(_L8("COMMAND Delete(unknown)")))); |
|
7821 |
|
7822 // Delete of something that isn't a folder or a message. Erk! |
|
7823 Queue(aRequestStatus); |
|
7824 Complete(KErrNotSupported); |
|
7825 } |
|
7826 } |
|
7827 |
|
7828 // Delete everything in this folder |
|
7829 void CImImap4Session::DeleteAllMessagesL(TRequestStatus& aRequestStatus) |
|
7830 { |
|
7831 LOG_COMMANDS((LogText(_L8("COMMAND DeleteAllMessages(%x)"),iMailboxId))); |
|
7832 |
|
7833 // We have to be selected |
|
7834 __ASSERT_DEBUG(iState==EImapStateSelected,gPanic(EDeleteWhenNotReady)); |
|
7835 if(!(iState==EImapStateSelected)) |
|
7836 { |
|
7837 User::LeaveIfError(KErrNotReady); |
|
7838 } |
|
7839 |
|
7840 Queue(aRequestStatus); |
|
7841 |
|
7842 // We're going to send a command of one style or another |
|
7843 NewTag(); |
|
7844 |
|
7845 // Are there any messages remotely to delete? |
|
7846 if (iMailboxSize==0) |
|
7847 { |
|
7848 // No: just do a close |
|
7849 iState=EImapStateCommandWait; |
|
7850 iSavedState=EImapStateNoSelect; |
|
7851 iImapIO->SendL(iStatus,_L8("%d CLOSE\r\n"),iTag); |
|
7852 } |
|
7853 else |
|
7854 { |
|
7855 // DeleteAllMessages is a special case: we want to delete everything, |
|
7856 // regardless of wether it's in the mirror or not. So, we set deleted |
|
7857 // flags on everything then expunge the folder |
|
7858 iState=EImapStateDeleteAllWait; |
|
7859 |
|
7860 // Send command: we go into deleteall wait as the next |
|
7861 iImapIO->SendL(iStatus,_L8("%d STORE 1:* +FLAGS (\\Deleted)\r\n"),iTag); |
|
7862 } |
|
7863 |
|
7864 // Sent it |
|
7865 NewTagSent(); |
|
7866 } |
|
7867 |
|
7868 // Select a folder |
|
7869 void CImImap4Session::Select(TRequestStatus& aRequestStatus, const TMsvId aFolder, const TBool aReadWrite) |
|
7870 { |
|
7871 TInt err=KErrNone; |
|
7872 if (!Connected()) |
|
7873 { |
|
7874 Queue(aRequestStatus); |
|
7875 err=KErrDisconnected; |
|
7876 } |
|
7877 else |
|
7878 TRAP(err,SelectL(aRequestStatus, aFolder, aReadWrite)); |
|
7879 if (err!=KErrNone) |
|
7880 { |
|
7881 DBG((LogText(_L8("-----------------------------------------------------------")))); |
|
7882 DBG((LogText(_L8("CImap4Session::Select(): calling Complete()")))); |
|
7883 DBG((LogText(_L8("-----------------------------------------------------------")))); |
|
7884 |
|
7885 Complete(err); |
|
7886 } |
|
7887 } |
|
7888 |
|
7889 void CImImap4Session::SelectL(TRequestStatus& aRequestStatus, const TMsvId aFolder, const TBool aReadWrite) |
|
7890 { |
|
7891 LOG_COMMANDS((LogText(_L8("COMMAND Select(%x (rw=%d), in state %d. Current mailbox=%x)"),aFolder,aReadWrite?1:0,iState,iMailboxId))); |
|
7892 |
|
7893 if (!(iState==EImapStateNoSelect || iState==EImapStateSelected)) |
|
7894 { |
|
7895 User::LeaveIfError(KErrNotReady); |
|
7896 } |
|
7897 Queue(aRequestStatus); |
|
7898 |
|
7899 // reset counts to safe values here to avoid reporting left over |
|
7900 // values from previous fetch. Correct values will be set up once |
|
7901 // headers have been fetched and parts counted. |
|
7902 iProgress.iPartsToDo=iProgress.iBytesToDo=1; |
|
7903 iProgress.iPartsDone=iProgress.iBytesDone=0; |
|
7904 |
|
7905 // Do the select |
|
7906 |
|
7907 // Is it already selected and the read/write state is compatible? |
|
7908 // Skip the command if possible! |
|
7909 if (iMailboxId==aFolder && iState==EImapStateSelected && |
|
7910 ((aReadWrite && iMailboxWritable) || !aReadWrite)) |
|
7911 { |
|
7912 if (ImapIdleSupported()) |
|
7913 { |
|
7914 DBG((LogText(_L8("-----------------------------------------------------------")))); |
|
7915 DBG((LogText(_L8("CImap4Session::SelectL(): calling Complete()")))); |
|
7916 DBG((LogText(_L8("-----------------------------------------------------------")))); |
|
7917 |
|
7918 Complete(KErrNone); |
|
7919 return; |
|
7920 } |
|
7921 else |
|
7922 { |
|
7923 // Just NOOP it so that we know about any mailbox size changes |
|
7924 NewTag(); |
|
7925 iImapIO->SendL(iStatus,_L8("%d NOOP\r\n"),iTag); |
|
7926 iState=EImapStateSelectWait; |
|
7927 NewTagSent(); |
|
7928 return; |
|
7929 } |
|
7930 } |
|
7931 |
|
7932 // Ok looks as if the SELECT actually needs to be done. |
|
7933 DoSelectL(aFolder, aReadWrite); |
|
7934 } |
|
7935 |
|
7936 void CImImap4Session::DoSelectL(const TMsvId aFolder, const TBool aReadWrite) |
|
7937 { |
|
7938 NewTag(); |
|
7939 |
|
7940 // We should always get an EXISTS after a SELECT but just in case |
|
7941 // we will force it true here. This ensures that a NewOnlySync |
|
7942 // will always do the sync when it involves selecting a new folder |
|
7943 iMailboxReceivedExists=ETrue; |
|
7944 |
|
7945 // Store name of new mailbox |
|
7946 iMailboxId=aFolder; |
|
7947 |
|
7948 // Get rid of old index and reset everything |
|
7949 iFolderIndex.Reset(); |
|
7950 iMailboxSize=0; |
|
7951 iMsgsDone=0; |
|
7952 iMailboxRecent=0; |
|
7953 iUidValidity=0; |
|
7954 iUidNext=0; |
|
7955 |
|
7956 // Is it special-case inbox? |
|
7957 SetEntryL(iMailboxId); |
|
7958 #if 0 |
|
7959 if (iEntry->Entry().Parent()==iServiceId && iEntry->Entry().iDetails.CompareF(KIMAP_INBOX)==0) |
|
7960 { |
|
7961 // Inbox: no path prepended |
|
7962 iImapIO->SendL(iStatus,aReadWrite?_L8("%d SELECT INBOX\r\n"):_L8("%d EXAMINE INBOX\r\n"),iTag); |
|
7963 iMailboxIsInbox=ETrue; |
|
7964 } |
|
7965 else |
|
7966 { |
|
7967 #endif |
|
7968 // Create path and send select command |
|
7969 HBufC8* path=MakePathL(iMailboxId,ETrue); |
|
7970 CleanupStack::PushL(path); |
|
7971 DoQuoteL(path); |
|
7972 TPtrC8 pathptr=path->Des(); |
|
7973 iImapIO->SendL(iStatus,aReadWrite?_L8("%d SELECT \"%S\"\r\n"):_L8("%d EXAMINE \"%S\"\r\n"),iTag,&pathptr); |
|
7974 CleanupStack::PopAndDestroy(); |
|
7975 #if 0 |
|
7976 iMailboxIsInbox=EFalse; |
|
7977 } |
|
7978 #endif |
|
7979 // Sent command |
|
7980 iState=EImapStateSelectWait; |
|
7981 NewTagSent(); |
|
7982 } |
|
7983 |
|
7984 // Copy a message to a new folder |
|
7985 void CImImap4Session::Copy(TRequestStatus& aRequestStatus, const TMsvId aSource, const TMsvId aDestination, TBool aUnSelectIfSameFolder) |
|
7986 { |
|
7987 TInt err=KErrNone; |
|
7988 if (!Connected()) |
|
7989 { |
|
7990 Queue(aRequestStatus); |
|
7991 err=KErrDisconnected; |
|
7992 } |
|
7993 else |
|
7994 TRAP(err,CopyL(aRequestStatus, aSource, aDestination, aUnSelectIfSameFolder)); |
|
7995 if (err!=KErrNone) |
|
7996 { |
|
7997 DBG((LogText(_L8("-----------------------------------------------------------")))); |
|
7998 DBG((LogText(_L8("CImap4Session::Copy(): calling Complete()")))); |
|
7999 DBG((LogText(_L8("-----------------------------------------------------------")))); |
|
8000 |
|
8001 Complete(err); |
|
8002 } |
|
8003 } |
|
8004 |
|
8005 void CImImap4Session::CopyL(TRequestStatus& aRequestStatus, const TMsvId aSource, const TMsvId aDestination, TBool aUnSelectIfSameFolder) |
|
8006 { |
|
8007 LOG_COMMANDS((LogText(_L8("COMMAND Copy(%x,%x)"),aSource,aDestination))); |
|
8008 __ASSERT_DEBUG(iState==EImapStateSelected,gPanic(ECopyWhenNotSelected)); |
|
8009 if(!(iState==EImapStateSelected)) |
|
8010 { |
|
8011 User::LeaveIfError(KErrArgument);//Copy when not selected |
|
8012 } |
|
8013 |
|
8014 Queue(aRequestStatus); |
|
8015 |
|
8016 // Make destination folder path |
|
8017 HBufC8 *command=NULL; // To stop .AER warnings... |
|
8018 TRAPD(err,command=MakePathL(aDestination,ETrue)); |
|
8019 if (err!=KErrNone) |
|
8020 { |
|
8021 DBG((LogText(_L8("-----------------------------------------------------------")))); |
|
8022 DBG((LogText(_L8("CImap4Session::CopyL(): calling Complete()")))); |
|
8023 DBG((LogText(_L8("-----------------------------------------------------------")))); |
|
8024 |
|
8025 Complete(err); |
|
8026 return; |
|
8027 } |
|
8028 CleanupStack::PushL(command); |
|
8029 DoQuoteL(command); |
|
8030 |
|
8031 // Check that source is in the folder we have got selected: this also |
|
8032 // ensures it's actually a message, as the parent of attachments wouldn't |
|
8033 // be the folder we have selected... |
|
8034 SetEntryL(aSource); |
|
8035 __ASSERT_DEBUG(iEntry->Entry().Parent()==iMailboxId,gPanic(ECopyNotFromSelectedFolder)); |
|
8036 if(!(iEntry->Entry().Parent()==iMailboxId)) |
|
8037 { |
|
8038 User::LeaveIfError(KErrGeneral); |
|
8039 } |
|
8040 // Make command |
|
8041 TMsvEmailEntry entry=iEntry->Entry(); |
|
8042 NewTag(); |
|
8043 TPtrC8 commanddes(command->Des()); |
|
8044 iImapIO->SendL(iStatus,_L8("%d UID COPY %u \"%S\"\r\n"),iTag,entry.UID(),&commanddes); |
|
8045 NewTagSent(); |
|
8046 |
|
8047 // If we're copying to the currently selected folder, pretend we've unselected it. |
|
8048 // This ensures we'll pick up on any changes in the next new sync |
|
8049 if (aUnSelectIfSameFolder && iMailboxId==aDestination && iState==EImapStateSelected) |
|
8050 { |
|
8051 // Return to 'no select' state after this |
|
8052 iSavedState=EImapStateNoSelect; |
|
8053 } |
|
8054 else |
|
8055 { |
|
8056 // Save existing selected/unselected state |
|
8057 iSavedState=iState; |
|
8058 } |
|
8059 |
|
8060 // Set up state |
|
8061 iState=EImapStateCommandWait; |
|
8062 |
|
8063 // Delete buffer |
|
8064 CleanupStack::PopAndDestroy(); |
|
8065 } |
|
8066 |
|
8067 // Copy a message to a new folder |
|
8068 void CImImap4Session::Append(TRequestStatus& aRequestStatus, const TMsvId aSource, const TMsvId aDestination) |
|
8069 { |
|
8070 TInt err=KErrNone; |
|
8071 if (!Connected()) |
|
8072 { |
|
8073 Queue(aRequestStatus); |
|
8074 err=KErrDisconnected; |
|
8075 } |
|
8076 else |
|
8077 TRAP(err,AppendL(aRequestStatus, aSource, aDestination)); |
|
8078 if (err!=KErrNone) |
|
8079 Complete(err); |
|
8080 } |
|
8081 |
|
8082 void CImImap4Session::AppendL(TRequestStatus& aRequestStatus, const TMsvId aSource, const TMsvId aDestination) |
|
8083 { |
|
8084 LOG_COMMANDS((LogText(_L8("COMMAND Append(%x,%x)"),aSource,aDestination))); |
|
8085 |
|
8086 Queue(aRequestStatus); |
|
8087 |
|
8088 // Check that source is a complete message |
|
8089 SetEntryL(aSource); |
|
8090 |
|
8091 // Check it's a message |
|
8092 if (iEntry->Entry().iType!=KUidMsvMessageEntry) |
|
8093 { |
|
8094 // Can't do it! |
|
8095 Complete(KErrGeneral); |
|
8096 return; |
|
8097 } |
|
8098 |
|
8099 // Cancel any dummy operation that might be outstanding |
|
8100 if (ImapIdleSupported()==EFalse) |
|
8101 { |
|
8102 CancelDummy(); |
|
8103 } |
|
8104 |
|
8105 // Size message for sending using a CImCalculateMessageSize |
|
8106 delete iMessageSizer; |
|
8107 iMessageSizer=NULL; |
|
8108 iMessageSizer=CImCalculateMsgSize::NewL(iFs,*iEntry); |
|
8109 |
|
8110 // Start sizing operation, using MIME: save source & destination for next step |
|
8111 // Use iHost (hostname of remote server) as the domain name seed for the MsgId |
|
8112 iCommandIds[0]=aSource; |
|
8113 iCommandIds[1]=aDestination; |
|
8114 iMessageDate=iEntry->Entry().iDate; |
|
8115 iMessageSizer->StartL(iStatus,iCommandIds[0], ESendAsMimeEmail, |
|
8116 iMessageDate, iHost, iCharset); |
|
8117 |
|
8118 // If we're appending to the currently selected folder, pretend we've unselected it. |
|
8119 // This ensures we'll pick up on any changes in the next new sync |
|
8120 if (iMailboxId==aDestination && iState==EImapStateSelected) |
|
8121 { |
|
8122 // Return to 'no select' state after this |
|
8123 iSavedState=EImapStateNoSelect; |
|
8124 } |
|
8125 else |
|
8126 { |
|
8127 // Save existing selected/unselected state |
|
8128 iSavedState=iState; |
|
8129 } |
|
8130 |
|
8131 iState=EImapStateAppendSizeWait; |
|
8132 if (!IsActive()) SetActive(); |
|
8133 } |
|
8134 |
|
8135 // Close a selected folder |
|
8136 void CImImap4Session::Close(TRequestStatus& aRequestStatus, const TBool aExpunge) |
|
8137 { |
|
8138 TInt err=KErrNone; |
|
8139 if (!Connected()) |
|
8140 { |
|
8141 Queue(aRequestStatus); |
|
8142 err=KErrDisconnected; |
|
8143 } |
|
8144 else |
|
8145 TRAP(err,CloseL(aRequestStatus, aExpunge)); |
|
8146 if (err!=KErrNone) |
|
8147 Complete(err); |
|
8148 } |
|
8149 |
|
8150 void CImImap4Session::CloseL(TRequestStatus& aRequestStatus, const TBool aExpunge) |
|
8151 { |
|
8152 LOG_COMMANDS((LogText(_L8("COMMAND Close(%d)"),aExpunge))); |
|
8153 |
|
8154 Queue(aRequestStatus); |
|
8155 |
|
8156 // Folder currently open? Just complete if we're not selected |
|
8157 if (iState==EImapStateNoSelect) |
|
8158 { |
|
8159 Complete(KErrNone); |
|
8160 return; |
|
8161 } |
|
8162 |
|
8163 // Expunging? If not, just send close command |
|
8164 if (!aExpunge) |
|
8165 { |
|
8166 // Send close |
|
8167 SendMessageL(KIMAPC_CLOSE); |
|
8168 iSavedState=EImapStateNoSelect; |
|
8169 iState=EImapStateCommandWait; |
|
8170 } |
|
8171 else |
|
8172 { |
|
8173 // The deletion strategy will build up runs of UIDs in order to make the |
|
8174 // most efficient use of bandwidth. However, as UIDs are not necessarily |
|
8175 // contiguous, so we use their sorted position in the mirror to decide on |
|
8176 // runs. UIDs will be allocated on an increasing basis,as the server cannot |
|
8177 // have magically invented new UIDs between 2 existing ones. |
|
8178 // UIds can miss from run by using email sync limits. |
|
8179 // |
|
8180 // We use TInt64's because AppendNum() cannot take a TUint32, which is |
|
8181 // what a UID is. |
|
8182 SetEntryL(iMailboxId); |
|
8183 GetChildrenL(*iSelection); |
|
8184 TRAPD(err,MakeSortedFolderIndexL()); |
|
8185 if (err!=KErrNone) |
|
8186 { |
|
8187 Complete(err); |
|
8188 return; |
|
8189 } |
|
8190 TInt pos=0; |
|
8191 TInt run=0; |
|
8192 TInt64 last=0; |
|
8193 TInt deleted=0; |
|
8194 |
|
8195 // Build command |
|
8196 HBufC8* command=HBufC8::NewLC(256); |
|
8197 |
|
8198 // Start command |
|
8199 command->Des().Append(_L8("UID STORE ")); |
|
8200 |
|
8201 while(pos<iFolderIndex.Size()) |
|
8202 { |
|
8203 // Look for messages with deleted flag set |
|
8204 SetEntryL(iFolderIndex[pos].iMsvId); |
|
8205 if (((TMsvEmailEntry)iEntry->Entry()).DeletedIMAP4Flag()) |
|
8206 { |
|
8207 LOG_COMMANDS((LogText(_L8("Message #%d marked as deleted"),pos+1))); |
|
8208 |
|
8209 deleted++; |
|
8210 // If uids are missing from run |
|
8211 if ((pos > 0 )&& (((TUint)iFolderIndex[pos].iUid - (TUint)iFolderIndex[pos-1].iUid) > 1)) |
|
8212 { |
|
8213 // Breaking a run |
|
8214 if(run > 1) |
|
8215 { |
|
8216 // A run of at least 2 is a range. Append 'last' UID, |
|
8217 // after removing comma and append a comma |
|
8218 command->Des().Delete(command->Des().Length()-1,1); |
|
8219 command->Des().Append(_L8(":")); |
|
8220 command->Des().AppendNum(last); |
|
8221 command->Des().Append(_L8(",")); |
|
8222 } |
|
8223 // run broken |
|
8224 run = 0; |
|
8225 } |
|
8226 |
|
8227 // This one is deleted. Are we in a run? |
|
8228 if (!run) |
|
8229 { |
|
8230 // No, start of a run/single item. Add to command |
|
8231 // Enough room for this? |
|
8232 if ((command->Length()+32)>command->Size()) |
|
8233 { |
|
8234 // Extend buffer |
|
8235 HBufC8* rcommand=command->ReAllocL(command->Size()+64); |
|
8236 |
|
8237 // Moved? |
|
8238 if (rcommand!=command) |
|
8239 { |
|
8240 // Destroy old one and push new one |
|
8241 CleanupStack::Pop(); |
|
8242 CleanupStack::PushL(command=rcommand); |
|
8243 } |
|
8244 } |
|
8245 |
|
8246 // Single number, plus a comma to terminate |
|
8247 TInt64 uid=(TUint)((TMsvEmailEntry)iEntry->Entry()).UID(); |
|
8248 command->Des().AppendNum(uid); |
|
8249 command->Des().Append(_L8(",")); |
|
8250 |
|
8251 // A run of 1 :-) |
|
8252 run++; |
|
8253 } |
|
8254 else |
|
8255 { |
|
8256 // We're in a run already. Extend it: it will be terminated |
|
8257 // when the run is broken or we exit. |
|
8258 last=(TUint)iFolderIndex[pos].iUid; |
|
8259 run++; |
|
8260 } |
|
8261 } |
|
8262 else |
|
8263 { |
|
8264 // Mark this MsvId as 0 (so it will be kept in local expunge) |
|
8265 iFolderIndex[pos].iMsvId=0; |
|
8266 |
|
8267 // Breaking a run? |
|
8268 if (run>1) |
|
8269 { |
|
8270 // A run of at least 2 is a range. Append 'last' UID, |
|
8271 // after removing comma |
|
8272 command->Des().Delete(command->Des().Length()-1,1); |
|
8273 command->Des().Append(_L8(":")); |
|
8274 command->Des().AppendNum(last); |
|
8275 command->Des().Append(_L8(",")); |
|
8276 } |
|
8277 |
|
8278 // Run broken |
|
8279 run=0; |
|
8280 } |
|
8281 |
|
8282 // Next message |
|
8283 pos++; |
|
8284 } |
|
8285 |
|
8286 // Anything deleted? |
|
8287 if (deleted) |
|
8288 { |
|
8289 // Remove the last character in the command string |
|
8290 command->Des().Delete(command->Des().Length()-1,1); |
|
8291 |
|
8292 // A run to complete? |
|
8293 if (run>1) |
|
8294 { |
|
8295 // A run of at least 2 is a range. Append 'last' UID. |
|
8296 command->Des().Append(_L8(":")); |
|
8297 command->Des().AppendNum(last); |
|
8298 } |
|
8299 |
|
8300 // Append flags & send command |
|
8301 command->Des().Append(_L8(" +FLAGS (\\Deleted)")); |
|
8302 SendMessageL(command->Des()); |
|
8303 iState=EImapStateDeleteMarkWait; |
|
8304 } |
|
8305 else |
|
8306 { |
|
8307 // Nothing to do: Just close the folder |
|
8308 iState=EImapStateCloseWait; |
|
8309 SendMessageL(KIMAPC_CLOSE); |
|
8310 } |
|
8311 |
|
8312 // Get rid of command buffer |
|
8313 CleanupStack::PopAndDestroy(); |
|
8314 } |
|
8315 } |
|
8316 |
|
8317 // Orphan a local message |
|
8318 void CImImap4Session::OrphanMessageL(const TMsvId aMessage) |
|
8319 { |
|
8320 DBG((LogText(_L8("OrphanMessageL(%x)"),aMessage))); |
|
8321 |
|
8322 // We no longer orphan the messages based on whether they have been downloaded. |
|
8323 // That was just delaying the inevitable and causing other problems |
|
8324 DeleteMessageL(aMessage); |
|
8325 DBG((LogText(_L8(" Deleting message")))); |
|
8326 } |
|
8327 |
|
8328 // Delete a local message |
|
8329 void CImImap4Session::DeleteMessageL(const TMsvId aMessage) |
|
8330 { |
|
8331 DBG((LogText(_L8("CImImap4Session::DeleteMessageL(%x)"),aMessage))); |
|
8332 |
|
8333 if(aMessage == KMsvNullIndexEntryId) |
|
8334 { |
|
8335 DBG((LogText(_L8("Attempted delete of null entry(%d)"),aMessage))); |
|
8336 return; |
|
8337 } |
|
8338 // Delete message and all subparts: first, move to parent |
|
8339 DBG((LogText(_L8(" SetEntry(%x)"),aMessage))); |
|
8340 SetEntryL(aMessage); |
|
8341 |
|
8342 DBG((LogText(_L8(" SetEntry(%x)"),iEntry->Entry().Parent()))); |
|
8343 SetEntryL(iEntry->Entry().Parent()); |
|
8344 |
|
8345 // Do it |
|
8346 DBG((LogText(_L8(" About to DeleteEntry(%x)"),aMessage))); |
|
8347 // Do not leave when entry is in use |
|
8348 TInt err (iEntry->DeleteEntry(aMessage)); |
|
8349 if(err==KErrInUse) |
|
8350 { |
|
8351 DBG((LogText(_L8("CImImap4Session::DeleteMessageL() dont leave if err = KErrInUse")))); |
|
8352 } |
|
8353 else |
|
8354 { |
|
8355 User::LeaveIfError(err); |
|
8356 } |
|
8357 |
|
8358 DBG((LogText(_L8(" Done!")))); |
|
8359 } |
|
8360 |
|
8361 // Get MESSAGE ONLY children of a folder. Ignore shadows as they are |
|
8362 // not going to be synced against the server |
|
8363 void CImImap4Session::GetMessageChildrenL(const TMsvId aFolder, CMsvEntrySelection* aChildren) |
|
8364 { |
|
8365 // Get *all* the children |
|
8366 SetEntryL(aFolder); |
|
8367 GetChildrenL(*aChildren); |
|
8368 |
|
8369 if(iCachedEntryData) |
|
8370 { |
|
8371 delete iCachedEntryData; |
|
8372 iCachedEntryData = 0; |
|
8373 } |
|
8374 iCachedEntryData = new(ELeave) CArrayFixFlat<TMsvCacheData>(5); |
|
8375 |
|
8376 // Go through them, checking to see if they're messages and removing ones that aren't |
|
8377 TInt pos=0; |
|
8378 while(pos<aChildren->Count()) |
|
8379 { |
|
8380 TMsvEntry* entryPtr; |
|
8381 TMsvId id = (*aChildren)[pos]; |
|
8382 User::LeaveIfError(iEntry->GetEntryFromId(id,entryPtr)); |
|
8383 |
|
8384 // Is it a message? And is it real (not shadow) |
|
8385 if (entryPtr->iType!=KUidMsvMessageEntry || |
|
8386 entryPtr->iRelatedId != KMsvNullIndexEntryId ) |
|
8387 { |
|
8388 // No, remove it |
|
8389 aChildren->Delete(pos,1); |
|
8390 } |
|
8391 else |
|
8392 { |
|
8393 //cache two parts of the TMsvEntry data to avoid having to refind it later |
|
8394 TMsvCacheData data; |
|
8395 data.iOrphan = ((TMsvEmailEntry)(*entryPtr)).Orphan(); |
|
8396 data.iUid = ((TMsvEmailEntry)(*entryPtr)).UID(); |
|
8397 iCachedEntryData->AppendL(data); |
|
8398 // Next entry |
|
8399 pos++; |
|
8400 } |
|
8401 } |
|
8402 } |
|
8403 |
|
8404 // Synchronise a folder |
|
8405 void CImImap4Session::Synchronise(TRequestStatus& aRequestStatus, TBool aNewOnly) |
|
8406 { |
|
8407 DBG((LogText(_L8("CImImap4Session::Synchronise()")))); |
|
8408 TInt err=KErrNone; |
|
8409 if (!Connected()) |
|
8410 { |
|
8411 Queue(aRequestStatus); |
|
8412 err=KErrDisconnected; |
|
8413 } |
|
8414 else |
|
8415 TRAP(err,SynchroniseL(aRequestStatus, aNewOnly)); |
|
8416 if (err!=KErrNone) |
|
8417 { |
|
8418 DBG((LogText(_L8("-----------------------------------------------------------")))); |
|
8419 DBG((LogText(_L8("CImap4Session::Synchronise(): calling Complete()")))); |
|
8420 DBG((LogText(_L8("-----------------------------------------------------------")))); |
|
8421 |
|
8422 |
|
8423 Complete(err); |
|
8424 } |
|
8425 } |
|
8426 |
|
8427 void CImImap4Session::SynchroniseL(TRequestStatus& aRequestStatus, TBool aNewOnly) |
|
8428 { |
|
8429 __ASSERT_DEBUG(iState==EImapStateSelected,gPanic(ESyncWhenNotSelected)); |
|
8430 if(!(iState==EImapStateSelected)) |
|
8431 { |
|
8432 User::LeaveIfError(KErrArgument);// Sync when not selected . |
|
8433 } |
|
8434 |
|
8435 Queue(aRequestStatus); |
|
8436 |
|
8437 DoSynchroniseL(aNewOnly); |
|
8438 } |
|
8439 |
|
8440 void CImImap4Session::DoSynchroniseL(TBool aNewOnly) |
|
8441 { |
|
8442 LOG_COMMANDS((LogText(_L8("COMMAND Synchronise")))); |
|
8443 __ASSERT_DEBUG(iState==EImapStateSelected,gPanic(ESyncWhenNotSelected)); |
|
8444 if(!(iState==EImapStateSelected)) |
|
8445 { |
|
8446 User::LeaveIfError(KErrArgument);// Sync when not selected . |
|
8447 } |
|
8448 // clear flags that may have been be set by SELECT or NOOP to say |
|
8449 // that a sync is required |
|
8450 iMailboxReceivedExists=EFalse; |
|
8451 iMailboxReceivedExpunge=EFalse; |
|
8452 iMailboxReceivedFlags=EFalse; |
|
8453 |
|
8454 // Some pre-bits that need doing - get the children & count them |
|
8455 SetEntryL(iMailboxId); |
|
8456 |
|
8457 TMsvEmailEntry message=iEntry->Entry(); |
|
8458 GetMessageChildrenL(iMailboxId,iSelection); |
|
8459 TInt noofchildren=iSelection->Count(); |
|
8460 |
|
8461 if (!aNewOnly && noofchildren) |
|
8462 { |
|
8463 // Delete any orphaned messages completely at this point |
|
8464 DBG((LogText(_L8("Looking for orphans in %d local messages"),noofchildren))); |
|
8465 |
|
8466 TInt pos=0; |
|
8467 while(pos<noofchildren) |
|
8468 { |
|
8469 if((*iCachedEntryData)[pos].iOrphan) |
|
8470 { |
|
8471 DBG((LogText(_L8(" Deleting orphan %x"),(*iSelection)[pos]))); |
|
8472 |
|
8473 // Delete it |
|
8474 SetEntryL(iMailboxId); |
|
8475 iEntry->DeleteEntry((*iSelection)[pos]); |
|
8476 // Remove it from selection |
|
8477 iSelection->Delete(pos,1); |
|
8478 noofchildren--; |
|
8479 } |
|
8480 else |
|
8481 { |
|
8482 // Move on to next entry |
|
8483 pos++; |
|
8484 } |
|
8485 } |
|
8486 } |
|
8487 |
|
8488 // First thing we have to do: check the UIDVALIDITY of the mirror and the |
|
8489 // remote folder match. If not, we have to orphan everything in the mirror |
|
8490 // and start again. |
|
8491 // We also do this if there are 0 messages in the remote mailbox (0 EXISTS) |
|
8492 // and there are messages locally |
|
8493 if (!message.ValidUID() || iUidValidity!=message.UID() || iMailboxSize==0) |
|
8494 { |
|
8495 // They don't match: do we have local children? |
|
8496 #ifdef PRINTING |
|
8497 if (!iMailboxSize) |
|
8498 LogText(_L8("No remote messages")); |
|
8499 else |
|
8500 LogText(_L8("UIDVALIDITY changed: local %u, remote %u"), |
|
8501 message.UID(),iUidValidity); |
|
8502 #endif |
|
8503 |
|
8504 // If we were doing a new-only sync, change this to a full sync as the |
|
8505 // UIDVALIDITY shows major changes |
|
8506 aNewOnly=EFalse; |
|
8507 |
|
8508 if (noofchildren) |
|
8509 { |
|
8510 // We've got local children: orphan them |
|
8511 DBG((LogText(_L8("Orphaning %d local messages"),noofchildren))); |
|
8512 |
|
8513 for(TInt a=0;a<noofchildren;a++) |
|
8514 { |
|
8515 // ...we should be skipping locally generated messages |
|
8516 OrphanMessageL((*iSelection)[a]); |
|
8517 } |
|
8518 |
|
8519 // Reget the number of children as this may have changed due to |
|
8520 // the orphaning process |
|
8521 GetMessageChildrenL(iMailboxId,iSelection); |
|
8522 noofchildren=iSelection->Count(); |
|
8523 } |
|
8524 |
|
8525 // Now, we match the remote's UIDVALIDITY: reset the pointer as it may |
|
8526 // well have been used by the orphaning process above. |
|
8527 SetEntryL(iMailboxId); |
|
8528 if (message.UID()!=iUidValidity || !message.ValidUID()) |
|
8529 { |
|
8530 // Do the change if necessary |
|
8531 message.SetUID(iUidValidity); |
|
8532 message.SetValidUID(ETrue); |
|
8533 ChangeEntryBulkL(message); |
|
8534 } |
|
8535 } |
|
8536 |
|
8537 // We've processed none of the remote messages yet |
|
8538 iMsgsDone=0; |
|
8539 |
|
8540 // Any remote messages? If not, complete now as there's nothing else to do |
|
8541 if (iMailboxSize==0) |
|
8542 { |
|
8543 // This folder is now sync'ed |
|
8544 // No need to set seen flags as no messages in remote mailbox |
|
8545 SyncCompleteL(); |
|
8546 DBG((LogText(_L8("CImap4Session::DoSynchroniseL(): iMailboxSize=0, folder now synched")))); |
|
8547 DBG((LogText(_L8("-----------------------------------------------------------")))); |
|
8548 DBG((LogText(_L8("CImap4Session::DoSynchroniseL(): calling Complete()")))); |
|
8549 DBG((LogText(_L8("-----------------------------------------------------------")))); |
|
8550 |
|
8551 Complete(KErrNone); |
|
8552 return; |
|
8553 } |
|
8554 |
|
8555 // Start the synchronise with sync'ing old messages: are there any |
|
8556 // messages in our mirror folder? |
|
8557 DBG((LogText(_L8("CImImap4Session::DoSynchroniseL(): Setting iState to EImapStateSynchroniseWait")))); |
|
8558 iState=EImapStateSynchroniseWait; |
|
8559 iSomeUnread=EFalse; |
|
8560 iHighestUid=0; |
|
8561 |
|
8562 // Zero the "missing" message range limits. |
|
8563 iMissingUidLow=0; |
|
8564 iMissingUidHigh=0; |
|
8565 |
|
8566 // Clear RX byte counter |
|
8567 iImapIO->RXbytes(ETrue); |
|
8568 |
|
8569 // Any children? |
|
8570 iFolderIndex.Reset(); |
|
8571 if (noofchildren>0) |
|
8572 { |
|
8573 // Children exist, we need to do an old-sync to check all the messages |
|
8574 // are still there. |
|
8575 |
|
8576 // Build an index of UIDs/TMsvIds currently in the mirror folder, and |
|
8577 // sort this by UID: this is the order in which we expect the fetch to |
|
8578 // return UIDs - any missing have been deleted on the server. They may |
|
8579 // well not be in UID order in the index because locally-appended |
|
8580 // messages will not have been added to the index in UID order. |
|
8581 TRAPD(err,MakeSortedFolderIndexL(ETrue)); |
|
8582 if (err!=KErrNone) |
|
8583 { |
|
8584 DBG((LogText(_L8("CImap4Session::DoSynchroniseL(): children exist, need to do old sync")))); |
|
8585 DBG((LogText(_L8("-----------------------------------------------------------")))); |
|
8586 DBG((LogText(_L8("CImap4Session::DoSynchroniseL(): calling Complete()")))); |
|
8587 DBG((LogText(_L8("-----------------------------------------------------------")))); |
|
8588 |
|
8589 Complete(err); |
|
8590 return; |
|
8591 } |
|
8592 |
|
8593 // Find the highest UID in the index |
|
8594 iHighestUid=iFolderIndex[noofchildren-1].iUid; |
|
8595 } |
|
8596 |
|
8597 // Retrieve folder synchronisation limit. |
|
8598 if (iEntry->Entry().Parent()==iServiceId && iEntry->Entry().iDetails.CompareF(KIMAP_INBOX)==0) |
|
8599 { |
|
8600 DBG((LogText(_L8("Folder sync of inbox folder")))); |
|
8601 |
|
8602 // Leave iSyncLimit at the maximum if a Search String is set |
|
8603 // If no Search String is set and this is the inbox, then use the inbox sync limit. |
|
8604 if(iServiceSettings->SearchString().Length() == 0) |
|
8605 { |
|
8606 iSyncLimit=iServiceSettings->InboxSynchronisationLimit(); |
|
8607 } |
|
8608 } |
|
8609 else |
|
8610 { |
|
8611 // Otherwise use the folder sync limit. |
|
8612 // Leave iSyncLimit at the maximum if a Search String is set |
|
8613 DBG((LogText(_L8("Folder sync of non-inbox folder")))); |
|
8614 |
|
8615 if(iServiceSettings->SearchString().Length() == 0) |
|
8616 { |
|
8617 iSyncLimit=iServiceSettings->MailboxSynchronisationLimit(); |
|
8618 } |
|
8619 } |
|
8620 |
|
8621 // Get the user defined UID SEARCH string if there is one |
|
8622 // Do a refined search if there's a string |
|
8623 if(iServiceSettings->SearchString().Length() != 0) |
|
8624 { |
|
8625 iSyncState=ESyncSearch; |
|
8626 iFolderPosition=0; |
|
8627 iSearchList->Reset(); |
|
8628 NewTag(); |
|
8629 // Refined search |
|
8630 _LIT8(KSearchString,"%d UID SEARCH 1:%d %S\r\n"); |
|
8631 TPtrC8 ptr = iServiceSettings->SearchString(); |
|
8632 iImapIO->SendL(iStatus,KSearchString,iTag,Min(iMailboxSize,KImapUidSearchSize),&ptr); |
|
8633 NewTagSent(); |
|
8634 return; |
|
8635 } |
|
8636 else // if no search string we use the old behaviour |
|
8637 // Check the folder synchronisation limit. |
|
8638 if (iSyncLimit>KImImapSynchroniseNone) |
|
8639 { |
|
8640 DBG((LogText(_L8("Folder sync limited to %d messages"),iSyncLimit))); |
|
8641 |
|
8642 // Limited folder synchronisation, perform a UID search. |
|
8643 iSyncState=ESyncSearch; |
|
8644 iFolderPosition=0; |
|
8645 |
|
8646 // Reset the search list. |
|
8647 iSearchList->Reset(); |
|
8648 |
|
8649 // Perform a UID search on this folder. |
|
8650 NewTag(); |
|
8651 iImapIO->SendL(iStatus,_L8("%d UID SEARCH 1:%d\r\n"),iTag,Min(iMailboxSize,KImapUidSearchSize)); |
|
8652 NewTagSent(); |
|
8653 return; |
|
8654 } |
|
8655 else if (iSyncLimit==KImImapSynchroniseNone) |
|
8656 { |
|
8657 DBG((LogText(_L8("No folder sync required")))); |
|
8658 |
|
8659 // No synchronisation required. |
|
8660 // This folder is now sync'ed |
|
8661 SyncCompleteL(); |
|
8662 // Back to selected state |
|
8663 iState=EImapStateSelected; |
|
8664 iSyncState=ENotSyncing; |
|
8665 DBG((LogText(_L8("CImap4Session::DoSynchroniseL(): iSyncLimit=KImImapSynchroniseNone, no sync required")))); |
|
8666 DBG((LogText(_L8("-----------------------------------------------------------")))); |
|
8667 DBG((LogText(_L8("CImap4Session::DoSynchroniseL(): calling Complete()")))); |
|
8668 DBG((LogText(_L8("-----------------------------------------------------------")))); |
|
8669 |
|
8670 Complete(KErrNone); |
|
8671 return; |
|
8672 } |
|
8673 else if (iSyncLimit<=KImImapSynchroniseAll) |
|
8674 { |
|
8675 DBG((LogText(_L8("Full folder sync required")))); |
|
8676 |
|
8677 // Full synchronisation required - fall through. |
|
8678 } |
|
8679 |
|
8680 if (noofchildren>0) |
|
8681 { |
|
8682 if (!aNewOnly && iHighestUid>0) |
|
8683 { |
|
8684 // Do old sync |
|
8685 iSyncState=ESyncOld; |
|
8686 iFolderPosition=0; |
|
8687 NewTag(); |
|
8688 // If a UID Search String is used it looks like this is FULL sync only |
|
8689 // so leave as is |
|
8690 iImapIO->SendL(iStatus,_L8("%d UID FETCH 1:%d (UID FLAGS)\r\n"), |
|
8691 iTag,iHighestUid); |
|
8692 NewTagSent(); |
|
8693 return; |
|
8694 } |
|
8695 } |
|
8696 |
|
8697 // Do new sync |
|
8698 SynchroniseNewL(); |
|
8699 } |
|
8700 |
|
8701 void CImImap4Session::ColonSeparatorToSpace(TDes8& buf) |
|
8702 { |
|
8703 TInt colon; |
|
8704 while ((colon = buf.Locate(':')) != KErrNotFound) |
|
8705 buf.Replace(colon,1,_L8(" ")); |
|
8706 buf.TrimRight(); |
|
8707 } |
|
8708 |
|
8709 // Do second stage of sync with uid list, ie new synchronise. |
|
8710 void CImImap4Session::SynchroniseNewL(const TUint32 aLowUid,const TUint32 aHighUid) |
|
8711 { |
|
8712 DBG((LogText(_L8("CImImap4Session::SynchroniseNewL()")))); |
|
8713 iSyncState=ESyncNew; |
|
8714 iCheckDiskSpaceCounter = 0; |
|
8715 iFolderPosition = 0; |
|
8716 |
|
8717 // First, resize folder index to hold all messages in the folder, |
|
8718 // as opposed to the old sync list. This will preserve the old |
|
8719 // contents of the index, which is what we want as it's up-to-date |
|
8720 // and correct. |
|
8721 iFolderIndex.SetSizeL(iMailboxSize); |
|
8722 |
|
8723 DBG((LogText(_L8("Synchronising new messages (UIDs %u to %u)"),aLowUid,aHighUid))); |
|
8724 |
|
8725 // Create list of priority fields to request |
|
8726 TBuf8<256> priorityFields; |
|
8727 CDesC8ArrayFlat* array = new(ELeave) CDesC8ArrayFlat(4); |
|
8728 CleanupStack::PushL(array); |
|
8729 CImcvUtils::PriorityFieldsL(*array); |
|
8730 for (TInt i=0; i<array->Count(); i++) |
|
8731 { |
|
8732 priorityFields.Append((*array)[i]); |
|
8733 } |
|
8734 CleanupStack::PopAndDestroy(array); |
|
8735 ColonSeparatorToSpace(priorityFields); |
|
8736 |
|
8737 // Send command |
|
8738 NewTag(); |
|
8739 |
|
8740 // If a UID search string has been specified, the we should create the UID FETCH |
|
8741 // string from the UID integer list. |
|
8742 if(iServiceSettings->SearchString().Length() != 0) |
|
8743 { |
|
8744 CreateUidStringL(); |
|
8745 TPtrC8 ptr(iUidString->Des()); |
|
8746 iImapIO->SendL(iStatus,KImapFetchSmallHeaderRangeRefined,iTag,&ptr, &priorityFields); |
|
8747 } |
|
8748 else |
|
8749 { |
|
8750 iImapIO->SendL(iStatus,KImapFetchSmallHeaderRange,iTag,aLowUid,aHighUid, &priorityFields); |
|
8751 } |
|
8752 NewTagSent(); |
|
8753 } |
|
8754 |
|
8755 // Do second stage of sync, ie new synchronise. |
|
8756 void CImImap4Session::SynchroniseNewL() |
|
8757 { |
|
8758 iSyncState=ESyncNew; |
|
8759 iCheckDiskSpaceCounter = 0; |
|
8760 iFolderPosition = 0; |
|
8761 |
|
8762 // First, resize folder index to hold all messages in the folder, |
|
8763 // as opposed to the old sync list. This will preserve the old |
|
8764 // contents of the index, which is what we want as it's up-to-date |
|
8765 // and correct. |
|
8766 iFolderIndex.SetSizeL(iMailboxSize); |
|
8767 |
|
8768 // fetch just the header of the new mails |
|
8769 FetchHeaderL(iHighestUid+1); |
|
8770 } |
|
8771 |
|
8772 // Build the fetch list |
|
8773 void CImImap4Session::AddFetchItemL(TMsvId aPart, TImap4GetMailOptions aPartTypes, TBool& aHasTextParts) |
|
8774 { |
|
8775 DBG((LogText(_L8("AddFetchItemL(id %x, parts=%d)"),aPart,aPartTypes))); |
|
8776 |
|
8777 // Is this part fetchable? |
|
8778 SetEntryL(aPart); |
|
8779 |
|
8780 // if the part is complete, then this means everything below it is |
|
8781 // complete and therefore we don't need to fetch anything. |
|
8782 if ( iEntry->Entry().iType != KUidMsvFolderEntry && iEntry->Entry().Complete() |
|
8783 && !(((TMsvEmailEntry)iEntry->Entry()).PartialDownloaded())) |
|
8784 { |
|
8785 DBG((LogText(_L8("Skipping, already complete")))); |
|
8786 |
|
8787 // If this is an attachment which has been marked complete because it has |
|
8788 // zero size, we still need to add it to the attachment manager. |
|
8789 if ((iEntry->Entry().iType == KUidMsvAttachmentEntry || |
|
8790 iEntry->Entry().iType == KUidMsvEmailExternalBodyEntry) && |
|
8791 (iEntry->Entry().iSize == 0)) |
|
8792 { |
|
8793 DBG((LogText(_L8("Creating zero length attachment")))); |
|
8794 CreateAttachmentInfoL((TMsvEmailEntry&)iEntry->Entry()); |
|
8795 } |
|
8796 |
|
8797 return; |
|
8798 } |
|
8799 |
|
8800 TBool addChildren = EFalse; |
|
8801 TBool addPart = EFalse; |
|
8802 |
|
8803 TUid type = iEntry->Entry().iType; |
|
8804 if (type == KUidMsvFolderEntry || type == KUidMsvMessageEntry) |
|
8805 { |
|
8806 // don't fetch anything - just let it recurse |
|
8807 addChildren = ETrue; |
|
8808 } |
|
8809 else if (type == KUidMsvEmailTextEntry || type == KUidMsvEmailHtmlEntry) |
|
8810 { |
|
8811 aHasTextParts = ETrue; |
|
8812 |
|
8813 if (aPartTypes == EGetImap4EmailBodyText || |
|
8814 aPartTypes == EGetImap4EmailBodyTextAndAttachments || |
|
8815 aPartTypes == EGetImap4EmailBodyAlternativeText) |
|
8816 { |
|
8817 addPart = ETrue; |
|
8818 } |
|
8819 } |
|
8820 else if (type == KUidMsvAttachmentEntry || type == KUidMsvEmailExternalBodyEntry) |
|
8821 { |
|
8822 if (aPartTypes == EGetImap4EmailBodyTextAndAttachments || |
|
8823 aPartTypes == EGetImap4EmailAttachments) |
|
8824 { |
|
8825 addPart = ETrue; |
|
8826 } |
|
8827 else |
|
8828 { |
|
8829 SetEntryL(iEntry->Entry().Parent()); |
|
8830 TImEmailFolderType folderType = static_cast<TMsvEmailEntry>((iEntry->Entry())).MessageFolderType(); |
|
8831 SetEntryL(aPart); |
|
8832 |
|
8833 if( folderType==EFolderTypeRelated ) |
|
8834 { |
|
8835 // if asked for bodytext and it is an attachment then |
|
8836 // fetch it if attachment is in a folder of |
|
8837 // Multipart/Related as it is most likely part of an MHTML |
|
8838 // document |
|
8839 addPart = ETrue; |
|
8840 } |
|
8841 else if( ( folderType == EFolderTypeAlternative || folderType == EFolderTypeUnknown ) && aPartTypes == EGetImap4EmailBodyAlternativeText) |
|
8842 { |
|
8843 // if non-HTML text alternative parts are requested, the alternative |
|
8844 // folder is checked and get the mime content type for the part |
|
8845 CMsvStore* store = iEntry->ReadStoreL(); |
|
8846 CleanupStack::PushL(store); |
|
8847 CImMimeHeader* mimeHeaders = CImMimeHeader::NewLC(); |
|
8848 mimeHeaders->RestoreL(*store); |
|
8849 |
|
8850 if( mimeHeaders->ContentType().CompareF(KMIME_TEXT)==0 ) |
|
8851 { |
|
8852 // This is a alternative text part, and should be treated |
|
8853 // as a text part |
|
8854 addPart = ETrue; |
|
8855 } |
|
8856 |
|
8857 CleanupStack::PopAndDestroy(2, store); // mimeHeaders, store |
|
8858 } |
|
8859 |
|
8860 // Store needs to be closed before calling CreateAttachmentInfoL |
|
8861 if(!addPart) |
|
8862 { |
|
8863 CreateAttachmentInfoL((TMsvEmailEntry&)iEntry->Entry()); |
|
8864 } |
|
8865 } |
|
8866 } |
|
8867 else |
|
8868 { |
|
8869 __ASSERT_DEBUG(0, gPanic(EUnknownMsvType)); |
|
8870 |
|
8871 // for anything else, if not debug mode then fetch anyway |
|
8872 addPart = ETrue; |
|
8873 } |
|
8874 |
|
8875 if (addPart) |
|
8876 { |
|
8877 iFetchList->AppendL(aPart); |
|
8878 |
|
8879 // Add this part's size to the size total |
|
8880 iProgress.iBytesToDo+=iEntry->Entry().iBioType; |
|
8881 } |
|
8882 |
|
8883 if (addChildren) |
|
8884 { |
|
8885 // Check the children |
|
8886 CMsvEntrySelection *selection=new (ELeave) CMsvEntrySelection; |
|
8887 CleanupStack::PushL(selection); |
|
8888 GetChildrenL(*selection); |
|
8889 |
|
8890 DBG((LogText(_L8("...ID %x has %d children"),iEntry->Entry().Id(),selection->Count()))); |
|
8891 |
|
8892 for(TInt a=0;a<selection->Count();a++) |
|
8893 { |
|
8894 // Process child |
|
8895 AddFetchItemL((*selection)[a],aPartTypes,aHasTextParts); |
|
8896 } |
|
8897 CleanupStack::PopAndDestroy(); |
|
8898 } |
|
8899 } |
|
8900 |
|
8901 void CImImap4Session::AddFetchItemL(TMsvId aPart, TImImap4GetPartialMailInfo aGetPartialMailInfo, TBool& aHasTextParts) |
|
8902 { |
|
8903 DBG((LogText(_L8("AddFetchItemL(id %x, parts=%d)"),aPart,aGetPartialMailInfo.iPartialMailOptions))); |
|
8904 |
|
8905 // Is this part fetchable? |
|
8906 SetEntryL(aPart); |
|
8907 |
|
8908 //if the part is complete, then this means everything below it is |
|
8909 // complete and therefore we don't need to fetch anything. |
|
8910 if (iEntry->Entry().iType != KUidMsvFolderEntry && iEntry->Entry().Complete()) |
|
8911 { |
|
8912 DBG((LogText(_L8("Skipping, already complete")))); |
|
8913 // If this is an attachment which has been marked complete because it has |
|
8914 // zero size, we still need to add it to the attachment manager. |
|
8915 if ((iEntry->Entry().iType == KUidMsvAttachmentEntry || |
|
8916 iEntry->Entry().iType == KUidMsvEmailExternalBodyEntry) && |
|
8917 (iEntry->Entry().iSize == 0)) |
|
8918 { |
|
8919 DBG((LogText(_L8("Creating zero length attachment")))); |
|
8920 CreateAttachmentInfoL((TMsvEmailEntry&)iEntry->Entry()); |
|
8921 } |
|
8922 |
|
8923 return; |
|
8924 } |
|
8925 |
|
8926 TBool addChildren = EFalse; |
|
8927 TBool addPart = EFalse; |
|
8928 |
|
8929 TUid type = iEntry->Entry().iType; |
|
8930 if (type == KUidMsvFolderEntry || type == KUidMsvMessageEntry) |
|
8931 { |
|
8932 // don't fetch anything - just let it recurse |
|
8933 addChildren = ETrue; |
|
8934 } |
|
8935 else if (type == KUidMsvEmailTextEntry) |
|
8936 { |
|
8937 aHasTextParts = ETrue; |
|
8938 |
|
8939 if(aGetPartialMailInfo.iPartialMailOptions == ENoSizeLimits) |
|
8940 addPart = ETrue; |
|
8941 else if (aGetPartialMailInfo.iPartialMailOptions == ECumulative) |
|
8942 { |
|
8943 if(iGetPartialMailInfo.iTotalSizeLimit > 0) |
|
8944 { |
|
8945 addPart = ETrue; |
|
8946 iBodyTextSize = iEntry->Entry().iBioType; |
|
8947 } |
|
8948 } |
|
8949 else if (aGetPartialMailInfo.iPartialMailOptions == EBodyTextOnly || |
|
8950 aGetPartialMailInfo.iPartialMailOptions == EBodyTextAndAttachments || |
|
8951 aGetPartialMailInfo.iPartialMailOptions == EBodyAlternativeText ) |
|
8952 { |
|
8953 addPart = ETrue; |
|
8954 iBodyTextSize = iEntry->Entry().iBioType; |
|
8955 } |
|
8956 } |
|
8957 else if (type == KUidMsvEmailHtmlEntry) |
|
8958 { |
|
8959 aHasTextParts = ETrue; |
|
8960 |
|
8961 iHtmlEntrySize = iEntry->Entry().iBioType; |
|
8962 if(aGetPartialMailInfo.iPartialMailOptions == ENoSizeLimits) |
|
8963 addPart = ETrue; |
|
8964 else if (aGetPartialMailInfo.iPartialMailOptions == ECumulative) |
|
8965 { |
|
8966 if((iGetPartialMailInfo.iTotalSizeLimit > 0 ) && |
|
8967 ((iBodyTextSize + iEntry->Entry().iBioType) <= iGetPartialMailInfo.iTotalSizeLimit)) |
|
8968 { |
|
8969 addPart = ETrue; |
|
8970 } |
|
8971 } |
|
8972 else if (aGetPartialMailInfo.iPartialMailOptions == EBodyTextOnly || |
|
8973 aGetPartialMailInfo.iPartialMailOptions == EBodyTextAndAttachments || |
|
8974 aGetPartialMailInfo.iPartialMailOptions == EBodyAlternativeText ) |
|
8975 |
|
8976 { |
|
8977 if(iBodyTextSize + iEntry->Entry().iBioType <= |
|
8978 Minimum(iGetPartialMailInfo.iBodyTextSizeLimit,iGetPartialMailInfo.iTotalSizeLimit)) |
|
8979 { |
|
8980 addPart = ETrue; |
|
8981 } |
|
8982 } |
|
8983 // In case of html entry, store html entry id to check later,(when attaching partial footer |
|
8984 // message)if whole body text is downloaded and the html size is not to be downloaded |
|
8985 if(addPart) |
|
8986 iHtmlEntryPart = aPart; |
|
8987 } |
|
8988 else if (type == KUidMsvAttachmentEntry || type == KUidMsvEmailExternalBodyEntry) |
|
8989 { |
|
8990 |
|
8991 if(aGetPartialMailInfo.iPartialMailOptions == ENoSizeLimits) |
|
8992 addPart = ETrue; |
|
8993 else if (aGetPartialMailInfo.iPartialMailOptions == ECumulative) |
|
8994 { |
|
8995 if(iGetPartialMailInfo.iTotalSizeLimit > 0 && |
|
8996 ((iBodyTextSize + iSizeOfToBeFetchedAttachments + iEntry->Entry().iBioType) <= iGetPartialMailInfo.iTotalSizeLimit)) |
|
8997 { |
|
8998 addPart = ETrue; |
|
8999 if((iBodyTextSize + iSizeOfToBeFetchedAttachments + iEntry->Entry().iBioType + iHtmlEntrySize) |
|
9000 >= iGetPartialMailInfo.iTotalSizeLimit) |
|
9001 { |
|
9002 RemoveHtmlPart(iHtmlEntryPart); |
|
9003 } |
|
9004 iSizeOfToBeFetchedAttachments+=iEntry->Entry().iBioType; |
|
9005 } |
|
9006 else |
|
9007 { |
|
9008 CreateAttachmentInfoL((TMsvEmailEntry&)iEntry->Entry()); |
|
9009 // for Ecumulative option ,after the body part downloading, check if there is any |
|
9010 // attachment which can be downloaded , then check if the html part can be included. |
|
9011 if((iBodyTextSize + iSizeOfToBeFetchedAttachments + iHtmlEntrySize) >= iGetPartialMailInfo.iTotalSizeLimit) |
|
9012 { |
|
9013 RemoveHtmlPart(iHtmlEntryPart); |
|
9014 } |
|
9015 } |
|
9016 } |
|
9017 else if (aGetPartialMailInfo.iPartialMailOptions == EAttachmentsOnly || |
|
9018 aGetPartialMailInfo.iPartialMailOptions == EBodyTextAndAttachments) |
|
9019 |
|
9020 { |
|
9021 if(iEntry->Entry().iBioType <= |
|
9022 Minimum(iGetPartialMailInfo.iAttachmentSizeLimit,iGetPartialMailInfo.iTotalSizeLimit)) |
|
9023 { |
|
9024 addPart = ETrue; |
|
9025 } |
|
9026 else |
|
9027 { |
|
9028 CreateAttachmentInfoL((TMsvEmailEntry&)iEntry->Entry()); |
|
9029 } |
|
9030 } |
|
9031 else |
|
9032 { |
|
9033 SetEntryL(iEntry->Entry().Parent()); |
|
9034 TImEmailFolderType folderType = static_cast<TMsvEmailEntry>((iEntry->Entry())).MessageFolderType(); |
|
9035 SetEntryL(aPart); |
|
9036 |
|
9037 if( folderType==EFolderTypeRelated ) |
|
9038 { |
|
9039 // if asked for bodytext and it is an attachment then |
|
9040 // fetch it if attachment is in a folder of |
|
9041 // Multipart/Related as it is most likely part of an MHTML |
|
9042 // document |
|
9043 addPart = ETrue; |
|
9044 } |
|
9045 else if( folderType==EFolderTypeAlternative && |
|
9046 aGetPartialMailInfo.iPartialMailOptions==EBodyAlternativeText && |
|
9047 iEntry->Entry().iBioType <= Minimum(iGetPartialMailInfo.iAttachmentSizeLimit, |
|
9048 iGetPartialMailInfo.iTotalSizeLimit) ) |
|
9049 { |
|
9050 // if non-HTML text alternative parts are requested, the alternative |
|
9051 // folder is checked and get the mime content type for the part |
|
9052 CMsvStore* store = iEntry->ReadStoreL(); |
|
9053 CleanupStack::PushL(store); |
|
9054 CImMimeHeader* mimeHeaders = CImMimeHeader::NewLC(); |
|
9055 mimeHeaders->RestoreL(*store); |
|
9056 |
|
9057 if( mimeHeaders->ContentType().CompareF(KMIME_TEXT)==0 ) |
|
9058 { |
|
9059 // This is a alternative text part, and should be treated |
|
9060 // as a text part |
|
9061 addPart = ETrue; |
|
9062 } |
|
9063 |
|
9064 CleanupStack::PopAndDestroy(2, store); // mimeHeaders, store |
|
9065 } |
|
9066 |
|
9067 if(!addPart) |
|
9068 { |
|
9069 CreateAttachmentInfoL((TMsvEmailEntry&)iEntry->Entry()); |
|
9070 } |
|
9071 } |
|
9072 } |
|
9073 else |
|
9074 { |
|
9075 __ASSERT_DEBUG(0, gPanic(EUnknownMsvType)); |
|
9076 |
|
9077 // for anything else, if not debug mode then fetch anyway |
|
9078 addPart = ETrue; |
|
9079 } |
|
9080 |
|
9081 if (addPart) |
|
9082 { |
|
9083 iFetchList->AppendL(aPart); |
|
9084 // Add this part's size to the size total |
|
9085 iProgress.iBytesToDo+=iEntry->Entry().iBioType; |
|
9086 } |
|
9087 |
|
9088 if (addChildren) |
|
9089 { |
|
9090 // Check the children |
|
9091 CMsvEntrySelection *selection=new (ELeave) CMsvEntrySelection; |
|
9092 CleanupStack::PushL(selection); |
|
9093 GetChildrenL(*selection); |
|
9094 |
|
9095 DBG((LogText(_L8("...ID %x has %d children"),iEntry->Entry().Id(),selection->Count()))); |
|
9096 |
|
9097 for(TInt a=0;a<selection->Count();a++) |
|
9098 { |
|
9099 // Process child |
|
9100 AddFetchItemL((*selection)[a],aGetPartialMailInfo,aHasTextParts); |
|
9101 } |
|
9102 CleanupStack::PopAndDestroy(); |
|
9103 } |
|
9104 } |
|
9105 |
|
9106 void CImImap4Session::RemoveHtmlPart(TMsvId aPart) |
|
9107 { |
|
9108 // removes the html part from the download list only if it exists in the list |
|
9109 if(aPart) |
|
9110 { |
|
9111 TInt aIndex = 0; |
|
9112 TKeyArrayFix sortKey(0, ECmpTInt32); |
|
9113 iFetchList->Find(aPart,sortKey,aIndex); |
|
9114 iFetchList->Delete(aIndex,1); |
|
9115 iHtmlEntryPart=0; |
|
9116 } |
|
9117 } |
|
9118 |
|
9119 // Checks for the minimum size limit between message type size limit |
|
9120 // (attachment size limit/body text sizelimit) |
|
9121 TInt32 CImImap4Session::Minimum(TInt32 aThisPartTypeSizeLimit,TInt32 aTotalMailSizeLimit) |
|
9122 { |
|
9123 if(aTotalMailSizeLimit > 0) |
|
9124 { |
|
9125 if(aThisPartTypeSizeLimit > aTotalMailSizeLimit) |
|
9126 return aTotalMailSizeLimit; |
|
9127 else |
|
9128 return aThisPartTypeSizeLimit; |
|
9129 } |
|
9130 else |
|
9131 return aThisPartTypeSizeLimit; |
|
9132 } |
|
9133 |
|
9134 // Issue command to fetch an item in the fetch list |
|
9135 void CImImap4Session::FetchAnItemL(const TMsvId aPart) |
|
9136 { |
|
9137 DBG((LogText(_L8("FetchAnItemL(%x)"),aPart))); |
|
9138 |
|
9139 // set the iFoundUid member variable to false to show that a UID has not been |
|
9140 // found in the fetch response yet |
|
9141 iFoundUid = EFalse; |
|
9142 |
|
9143 // Get part ID and read the MIME header so we can work out encoding style |
|
9144 iMessageId=aPart; |
|
9145 SetEntryL(iMessageId); |
|
9146 CMsvStore *store=iEntry->ReadStoreL(); |
|
9147 CleanupStack::PushL(store); |
|
9148 |
|
9149 iIsDiskSpaceChecked = EFalse; |
|
9150 // Get MIME header |
|
9151 if (!iAttachmentMimeInfo) |
|
9152 iAttachmentMimeInfo=CImMimeHeader::NewL(); |
|
9153 iAttachmentMimeInfo->RestoreL(*store); |
|
9154 |
|
9155 |
|
9156 // We don't reset the stats here, as they're stats for a single |
|
9157 // fetch operation, which may include multiple parts of the same |
|
9158 // message. Stats initialisation is done in the FetchBody() |
|
9159 // function. |
|
9160 |
|
9161 // Find the UID we need to fetch |
|
9162 TMsvEmailEntry entry = iEntry->Entry(); |
|
9163 iMessageFetching=entry.UID(); |
|
9164 |
|
9165 // check there is enough disk space for this part (plus slop) |
|
9166 // iBioType contains remote size which will never be less than the |
|
9167 // local size so is a safe value to use |
|
9168 if(!iFetchPartialMail) |
|
9169 { |
|
9170 CheckForDiskSpaceL(entry.iBioType); |
|
9171 } |
|
9172 |
|
9173 // Save encoding type |
|
9174 iEncodingType=iAttachmentMimeInfo->ContentTransferEncoding(); |
|
9175 iB64Decoder.Initialise(); |
|
9176 |
|
9177 // Clear QP buffer |
|
9178 if (iPartialLine && iPartialLine->Length()) |
|
9179 iPartialLine->Des().Zero(); |
|
9180 |
|
9181 // Is this a text fetch? (if so, save as a richtext in the database, as |
|
9182 // opposed to a separate file). Default to being an attachment. |
|
9183 |
|
9184 // SJM: This code used to check for an attachment filename and |
|
9185 // force FetchIsTest False in that case. This is unnecessary as |
|
9186 // this check is already done in BuildTreeOne and reflected in the |
|
9187 // entry.iType. |
|
9188 |
|
9189 iFetchIsText = EFalse; |
|
9190 if (entry.iType == KUidMsvEmailTextEntry) |
|
9191 { |
|
9192 iFetchIsText = ETrue; |
|
9193 // New message body |
|
9194 if (iStore8BitData) |
|
9195 { |
|
9196 delete iBodyBuf; |
|
9197 iBodyBuf = NULL; |
|
9198 iBodyBuf = CBufSeg::NewL(KBodyTextChunkSizeBytes); |
|
9199 delete iBodyText; |
|
9200 iBodyText = NULL; |
|
9201 iBodyText = CMsvBodyText::NewL(); |
|
9202 } |
|
9203 else |
|
9204 { |
|
9205 delete iMessageBody; |
|
9206 iMessageBody = NULL; |
|
9207 iMessageBody=CRichText::NewL(iParaLayer, iCharLayer); |
|
9208 } |
|
9209 } |
|
9210 |
|
9211 // Waiting for size |
|
9212 iSizeWait=ETrue; |
|
9213 |
|
9214 // Size of item (mainly for CC:Mail bug workaround) */ |
|
9215 iSizeOfThisPart=entry.iBioType; |
|
9216 |
|
9217 // if going into richtext only then setup charset conversion |
|
9218 iPreparedToConvert=EFalse; |
|
9219 TInt fetchSizeBytes = static_cast<TInt>(iServiceSettings->FetchSize()); |
|
9220 // If this was a partially fetched message then there is some already fetched |
|
9221 // message content in the message store |
|
9222 TInt32 fetchSize = fetchSizeBytes; |
|
9223 |
|
9224 if (iFetchIsText) |
|
9225 { |
|
9226 |
|
9227 TUint charsetId = iAttachmentMimeInfo->MimeCharset(); |
|
9228 |
|
9229 if (iStore8BitData) |
|
9230 { |
|
9231 iBodyText->SetDefaultCharacterSet(iCharConv->SystemDefaultCharset()); |
|
9232 if (charsetId == KUidMsvCharsetNone) |
|
9233 iBodyText->SetCharacterSet(0); |
|
9234 else |
|
9235 iBodyText->SetCharacterSet(charsetId); |
|
9236 } |
|
9237 else |
|
9238 { |
|
9239 if (charsetId == KUidMsvCharsetNone) |
|
9240 charsetId = iCharConv->SystemDefaultCharset(); |
|
9241 } |
|
9242 |
|
9243 iAttachmentMimeInfo->SetMimeCharset(charsetId); |
|
9244 if (!iStore8BitData) |
|
9245 { |
|
9246 if (charsetId != KUidMsvCharsetNone) |
|
9247 iPreparedToConvert = iCharConv->PrepareToConvertToFromOurCharsetL(charsetId); |
|
9248 } |
|
9249 } |
|
9250 |
|
9251 // ensure nothing left over |
|
9252 iLeftOver.SetLength(0); |
|
9253 |
|
9254 DBG((LogText(_L8("Starting fetch for body part (MsvId=%x, iSizeOfThisPart=%d, iFetchIsText=%d, convert=%d)"), |
|
9255 iMessageId,iSizeOfThisPart,iFetchIsText, iPreparedToConvert))); |
|
9256 |
|
9257 // Issue fetch command |
|
9258 NewTag(); |
|
9259 TPtrC8 path=iAttachmentMimeInfo->RelativePath(); |
|
9260 |
|
9261 if(iFetchPartialMail) |
|
9262 fetchSize = GetFetchSizeL(iSizeOfThisPart,0); // As this is the first time 0 size downloaded already |
|
9263 |
|
9264 if(fetchSize > fetchSizeBytes) |
|
9265 fetchSize = fetchSizeBytes; |
|
9266 |
|
9267 //Some servers dont support MIME.And it contains single body type. |
|
9268 //Using KImapFetchBodyPeek/KImapFetchBody to fetch the body of mails. |
|
9269 if(iProgress.iPartsToDo == 1) |
|
9270 { |
|
9271 if (iServiceSettings->UpdatingSeenFlags()) |
|
9272 { |
|
9273 iImapIO->SendL(iStatus,KImapFetchBodyPeek, iTag,iMessageFetching,&path,0,fetchSize); |
|
9274 } |
|
9275 else |
|
9276 { |
|
9277 iImapIO->SendL(iStatus,KImapFetchBody, iTag,iMessageFetching,&path,0,fetchSize); |
|
9278 } |
|
9279 } |
|
9280 else |
|
9281 { |
|
9282 if (iServiceSettings->UpdatingSeenFlags()) |
|
9283 { |
|
9284 iImapIO->SendL(iStatus,KImapFetchMimeBodyPeek, iTag,iMessageFetching,&path,fetchSize,&path); |
|
9285 } |
|
9286 else |
|
9287 { |
|
9288 iImapIO->SendL(iStatus,KImapFetchMimeBody, iTag,iMessageFetching,&path,fetchSize,&path); |
|
9289 } |
|
9290 } |
|
9291 NewTagSent(); |
|
9292 CleanupStack::PopAndDestroy(); |
|
9293 } |
|
9294 |
|
9295 void CImImap4Session::CheckForDiskSpaceL(TInt aSizeToBeDownloaded) |
|
9296 { |
|
9297 TInt needSpace = 0; |
|
9298 TVolumeInfo volumeInfo; |
|
9299 User::LeaveIfError(iFs.Volume(volumeInfo, iCurrentDrive)); |
|
9300 |
|
9301 needSpace = KMinimumDiskSpaceForSync + aSizeToBeDownloaded; |
|
9302 if (volumeInfo.iFree < needSpace) |
|
9303 User::Leave(KErrDiskFull); |
|
9304 } |
|
9305 |
|
9306 // Fetch body for partial download |
|
9307 void CImImap4Session::FetchBody(TRequestStatus& aRequestStatus, const TMsvId aPart, |
|
9308 TImImap4GetPartialMailInfo aGetPartialMailInfo) |
|
9309 { |
|
9310 LOG_COMMANDS((LogText(_L8("COMMAND FetchBody(%x)"),aPart))); |
|
9311 TInt err=KErrNone; |
|
9312 |
|
9313 CheckForPartialPopulate(aGetPartialMailInfo); |
|
9314 if (!Connected()) |
|
9315 { |
|
9316 Queue(aRequestStatus); |
|
9317 err=KErrDisconnected; |
|
9318 } |
|
9319 else |
|
9320 { |
|
9321 TRAP(err,FetchBodyL(aRequestStatus, aPart)); |
|
9322 } |
|
9323 if (err!=KErrNone) |
|
9324 { |
|
9325 DBG((LogText(_L8("-----------------------------------------------------------")))); |
|
9326 DBG((LogText(_L8("CImap4Session::FetchBody(): calling Complete()")))); |
|
9327 DBG((LogText(_L8("-----------------------------------------------------------")))); |
|
9328 |
|
9329 |
|
9330 Complete(err); |
|
9331 } |
|
9332 } |
|
9333 |
|
9334 // Checks if the partial mail download parameters are set to default |
|
9335 // and the full download mail option is set, then this is a request for full download. |
|
9336 void CImImap4Session::CheckForPartialPopulate(TImImap4GetPartialMailInfo aGetPartialMailInfo) |
|
9337 { |
|
9338 if(aGetPartialMailInfo.iPartialMailOptions == ENoSizeLimits && |
|
9339 aGetPartialMailInfo.iTotalSizeLimit == KMaxTInt && |
|
9340 aGetPartialMailInfo.iBodyTextSizeLimit == KMaxTInt && |
|
9341 aGetPartialMailInfo.iAttachmentSizeLimit == KMaxTInt && |
|
9342 (aGetPartialMailInfo.iGetMailBodyParts == EGetImap4EmailHeaders || |
|
9343 aGetPartialMailInfo.iGetMailBodyParts == EGetImap4EmailBodyText || |
|
9344 aGetPartialMailInfo.iGetMailBodyParts == EGetImap4EmailBodyTextAndAttachments || |
|
9345 aGetPartialMailInfo.iGetMailBodyParts == EGetImap4EmailAttachments || |
|
9346 aGetPartialMailInfo.iGetMailBodyParts == EGetImap4EmailBodyAlternativeText)) |
|
9347 { |
|
9348 DBG((LogText(_L8("Populate option = %d)"),aGetPartialMailInfo.iGetMailBodyParts))); |
|
9349 iFetchPartialMail = EFalse; |
|
9350 iGetOptions = aGetPartialMailInfo.iGetMailBodyParts; |
|
9351 } |
|
9352 else |
|
9353 { |
|
9354 DBG((LogText(_L8("Populate option = %d)"),aGetPartialMailInfo.iPartialMailOptions))); |
|
9355 iFetchPartialMail = ETrue; |
|
9356 iGetPartialMailInfo = aGetPartialMailInfo; |
|
9357 } |
|
9358 } |
|
9359 |
|
9360 void CImImap4Session::FetchBodyL(TRequestStatus& aRequestStatus, const TMsvId aPart) |
|
9361 { |
|
9362 __ASSERT_DEBUG(iState==EImapStateSelected,gPanic(EFetchWhenNotSelected)); |
|
9363 if(!(iState==EImapStateSelected)) |
|
9364 { |
|
9365 User::LeaveIfError(KErrArgument);// Fetch when not selected . |
|
9366 } |
|
9367 iGetPart = aPart; |
|
9368 |
|
9369 Queue(aRequestStatus); |
|
9370 |
|
9371 // if we only want headers then there is nothing to do as they |
|
9372 // have already been fetched on synchronisation. We complete here |
|
9373 // and then the Compound object will copy the structure across |
|
9374 if(!iFetchPartialMail) |
|
9375 { |
|
9376 if (iGetOptions == EGetImap4EmailHeaders) |
|
9377 { |
|
9378 DBG((LogText(_L8("-----------------------------------------------------------")))); |
|
9379 DBG((LogText(_L8("CImap4Session::FetchBodyL(): calling Complete()")))); |
|
9380 DBG((LogText(_L8("-----------------------------------------------------------")))); |
|
9381 |
|
9382 Complete(KErrNone); |
|
9383 return; |
|
9384 } |
|
9385 } |
|
9386 // Get some basic info on what we're fetching |
|
9387 SetEntryL(aPart); |
|
9388 |
|
9389 // First, check that we're in the right folder: this involves working up from the |
|
9390 // base part looking for a folder which matches the current iMailboxId. If we get |
|
9391 // to the service without finding the current folder, then we're not in the |
|
9392 // right place... |
|
9393 TBool messageFound = EFalse; |
|
9394 while((iEntry->Entry().Id()!=iMailboxId) && (!messageFound)) |
|
9395 { |
|
9396 // Reached the service? |
|
9397 if (iEntry->Entry().iType==KUidMsvServiceEntry) |
|
9398 { |
|
9399 // Didn't find the folder - we must have the wrong one selected |
|
9400 Complete(KErrImapWrongFolder); |
|
9401 |
|
9402 DBG((LogText(_L8("In the wrong folder!")))); |
|
9403 |
|
9404 return; |
|
9405 } |
|
9406 |
|
9407 // Reached the message ? |
|
9408 if (iEntry->Entry().iType==KUidMsvMessageEntry) |
|
9409 { |
|
9410 iMessageUid = ((TMsvEmailEntry&)iEntry->Entry()).UID(); |
|
9411 iSyncState = EGettingStructure; |
|
9412 iState = EImapStateFetchWait; |
|
9413 if (!(iEntry->Entry().Owner())) |
|
9414 { |
|
9415 // If there are no child entries then we need to fetch the envelope and some headers (again.) |
|
9416 // We can generate the message structure and header stores from the envelope and headers after they have been fetched. |
|
9417 if (ImapIdleSupported()==EFalse) |
|
9418 { |
|
9419 CancelDummy(); |
|
9420 } |
|
9421 // We don't know the size of the body part yet as there is no structure. |
|
9422 // The envelope will be fetched shortly so clear the progress information for now. |
|
9423 // Set the iBytesToDo to 1 rather than 0 to avoid any possible division by 0 errors |
|
9424 iProgress.iBytesToDo=1; |
|
9425 iProgress.iBytesDone=0; |
|
9426 FetchLargeHeaderL(iMessageUid, EFalse); |
|
9427 } |
|
9428 else |
|
9429 { |
|
9430 // If the structure is already present then do the fetch |
|
9431 if (ImapIdleSupported()==EFalse) |
|
9432 { |
|
9433 CancelDummy(); |
|
9434 } |
|
9435 DoFetchL(); |
|
9436 messageFound = ETrue; |
|
9437 } |
|
9438 } |
|
9439 |
|
9440 // Up a level |
|
9441 SetEntryL(iEntry->Entry().Parent()); |
|
9442 } |
|
9443 } |
|
9444 |
|
9445 // Set/reset local subscribed flag |
|
9446 void CImImap4Session::LocalSubscribeL(const TMsvId aTarget, const TBool aSubscribe) |
|
9447 { |
|
9448 LOG_COMMANDS((LogText(_L8("COMMAND LocalSubscribe(%d,%d)"),aTarget,aSubscribe))); |
|
9449 |
|
9450 // Set/reset subscribed flag |
|
9451 SetEntryL(aTarget); |
|
9452 TMsvEmailEntry message=iEntry->Entry(); |
|
9453 |
|
9454 // Change only if necessary |
|
9455 if (message.Subscribed()!=aSubscribe) |
|
9456 { |
|
9457 message.SetSubscribed(aSubscribe); |
|
9458 ChangeEntryL(message); |
|
9459 } |
|
9460 } |
|
9461 |
|
9462 // Set/reset remote subscribed flag |
|
9463 void CImImap4Session::RemoteSubscribeL(TRequestStatus& aRequestStatus, const TMsvId aTarget, const TBool aSubscribe) |
|
9464 { |
|
9465 LOG_COMMANDS((LogText(_L8("COMMAND RemoteSubscribe(%d,%d)"),aTarget,aSubscribe))); |
|
9466 __ASSERT_DEBUG(iState==EImapStateNoSelect || iState==EImapStateSelected,gPanic(ESubscribeWhenNotReady)); |
|
9467 if(!(iState==EImapStateNoSelect || iState==EImapStateSelected)) |
|
9468 { |
|
9469 User::LeaveIfError(KErrInUse);// Subscribe when not ready. |
|
9470 } |
|
9471 Queue(aRequestStatus); |
|
9472 |
|
9473 // Save ID for updating subscription flag when the remote one completes |
|
9474 // successfully. |
|
9475 iCommandIds[0]=aTarget; |
|
9476 iCommandFlags[0]=aSubscribe; |
|
9477 |
|
9478 // Get path to delete |
|
9479 HBufC8* path=NULL; // To stop .AER warnings... |
|
9480 TRAPD(err,path=MakePathL(aTarget,ETrue)); |
|
9481 if (err!=KErrNone) |
|
9482 { |
|
9483 Complete(err); |
|
9484 return; |
|
9485 } |
|
9486 CleanupStack::PushL(path); |
|
9487 DoQuoteL(path); |
|
9488 |
|
9489 // Set state |
|
9490 iSavedState=iState; |
|
9491 iState=EImapStateSubscribeWait; |
|
9492 |
|
9493 // Send command |
|
9494 NewTag(); |
|
9495 TPtrC8 pathdes(path->Des()); |
|
9496 if(aSubscribe) |
|
9497 iImapIO->SendL(iStatus,_L8("%d %S\"%S\"\r\n"),iTag,&KIMAPC_SUBSCRIBE,&pathdes); |
|
9498 else |
|
9499 iImapIO->SendL(iStatus,_L8("%d %S\"%S\"\r\n"),iTag,&KIMAPC_UNSUBSCRIBE,&pathdes); |
|
9500 NewTagSent(); |
|
9501 |
|
9502 // Destroy buffer |
|
9503 CleanupStack::PopAndDestroy(); |
|
9504 } |
|
9505 |
|
9506 // Reset sync/fetch stats |
|
9507 void CImImap4Session::ResetStats() |
|
9508 { |
|
9509 // Reset all the stats |
|
9510 iHeadersFetched=0; |
|
9511 iOrphanedMessages=0; |
|
9512 iRemoteMessagesDeleteTagged=0; |
|
9513 iMsgsDone=0; |
|
9514 |
|
9515 iProgress.iState = TImap4GenericProgress::EIdle; |
|
9516 iProgress.iImap4SubStateProgress = TImap4GenericProgress::EIdle; |
|
9517 iProgress.iMsgsToDo = iProgress.iMsgsDone = 0; |
|
9518 iProgress.iPartsToDo = iProgress.iPartsDone = 0; |
|
9519 iProgress.iBytesToDo = iProgress.iBytesDone = 0; |
|
9520 iProgress.iErrorCode = KErrNone; |
|
9521 iProgress.iReturnedMsvId = 0; |
|
9522 iProgress.iTotalSize = 0; |
|
9523 } |
|
9524 |
|
9525 // Return progress |
|
9526 |
|
9527 // Msgs ToDo/Done are only setup when doing a Synchronise command, ie |
|
9528 // in state EImapStateSynchroniseWait. |
|
9529 |
|
9530 // Parts/Bytes ToDo/Done are set up and used in a FetchBody command, |
|
9531 // ie in state EImapStateFetchWait. |
|
9532 |
|
9533 // BytesToDo/Done are used in Append command. |
|
9534 |
|
9535 void CImImap4Session::IncSyncStats(TImap4SyncProgress& aSync) |
|
9536 { |
|
9537 // do the synchronising stats |
|
9538 aSync.iHeadersFetched += iHeadersFetched; |
|
9539 aSync.iOrphanedMessages += iOrphanedMessages; |
|
9540 aSync.iRemoteMessagesDeleteTagged += iRemoteMessagesDeleteTagged; |
|
9541 |
|
9542 if(iServiceSettings->SearchString().Length() != 0) |
|
9543 { |
|
9544 aSync.iMsgsToDo=iMailboxSize; |
|
9545 } |
|
9546 else |
|
9547 { |
|
9548 aSync.iMsgsToDo=(iSyncLimit<=0)?iMailboxSize:Min(iMailboxSize,iSyncLimit); |
|
9549 } |
|
9550 aSync.iMsgsDone = Min(iMsgsDone,aSync.iMsgsToDo); |
|
9551 } |
|
9552 |
|
9553 TImap4GenericProgress CImImap4Session::Progress() |
|
9554 { |
|
9555 // update the state with what we're doing |
|
9556 if (iState==EImapStateDisconnected) |
|
9557 { |
|
9558 iProgress.iState=TImap4GenericProgress::EDisconnected; |
|
9559 } |
|
9560 else if (iState<EImapStateNoSelect) |
|
9561 { |
|
9562 iProgress.iState = TImap4GenericProgress::EConnecting; |
|
9563 // If we're in the connecting state, get the stage and IAP values |
|
9564 // and store them in iMsgsDone and iMsgsToDo to be accessed via |
|
9565 // TImap4GenericProgress::ConnectionState() and TImap4GenericProgress::ConnectionIAP() |
|
9566 iProgress.iMsgsDone = iImapIO->GetConnectionStage(); |
|
9567 TUint32 iap; |
|
9568 TInt err = iImapIO->GetIAPValue(iap); |
|
9569 if (err == KErrNone) |
|
9570 { |
|
9571 iProgress.iMsgsToDo = iap; |
|
9572 } |
|
9573 else |
|
9574 { |
|
9575 iProgress.iMsgsToDo = err; |
|
9576 } |
|
9577 } |
|
9578 else if (iState==EImapStateNoSelect || iState==EImapStateSelected || iState==EImapStateIdling) |
|
9579 { |
|
9580 iProgress.iState=TImap4GenericProgress::EIdle; |
|
9581 } |
|
9582 else |
|
9583 { |
|
9584 switch(iState) |
|
9585 { |
|
9586 case EImapStateFetchWait: |
|
9587 iProgress.iState=TImap4GenericProgress::EFetching; |
|
9588 break; |
|
9589 |
|
9590 case EImapStateAppendSizeWait: |
|
9591 case EImapStateAppendPromptWait: |
|
9592 case EImapStateAppendWait: |
|
9593 case EImapStateAppendResultWait: |
|
9594 iProgress.iState=TImap4GenericProgress::EAppending; |
|
9595 break; |
|
9596 |
|
9597 case EImapStateSynchroniseWait: |
|
9598 iProgress.iState=TImap4GenericProgress::ESyncing; |
|
9599 break; |
|
9600 |
|
9601 case EImapStateLogoutWait: |
|
9602 iProgress.iState=TImap4GenericProgress::EDisconnecting; |
|
9603 break; |
|
9604 |
|
9605 case EImapStateDeleteWait: |
|
9606 case EImapStateDeleteAllWait: |
|
9607 case EImapStateDeleteFolderWait: |
|
9608 case EImapStateDeleteMarkWait: |
|
9609 case EImapStateExpungeWait: |
|
9610 case EImapStateExpungeAllWait: |
|
9611 iProgress.iState = TImap4GenericProgress::EDeleting; |
|
9612 break; |
|
9613 |
|
9614 case EImapStateSelectWait: |
|
9615 iProgress.iState = TImap4GenericProgress::ESelecting; |
|
9616 break; |
|
9617 |
|
9618 default: |
|
9619 // Just 'busy' otherwise |
|
9620 iProgress.iState=TImap4GenericProgress::EBusy; |
|
9621 break; |
|
9622 } |
|
9623 } |
|
9624 |
|
9625 return iProgress; |
|
9626 } |
|
9627 |
|
9628 // Set entry pointer |
|
9629 void CImImap4Session::SetEntry(CMsvServerEntry *aEntry) |
|
9630 { |
|
9631 // Take note of this CMsvServerEntry |
|
9632 iEntry=aEntry; |
|
9633 |
|
9634 // Park entry |
|
9635 iEntry->SetEntry(NULL); |
|
9636 } |
|
9637 |
|
9638 // Park entries |
|
9639 void CImImap4Session::Park() |
|
9640 { |
|
9641 // Park normal entry |
|
9642 iEntry->SetEntry(NULL); |
|
9643 |
|
9644 // Park moveentry if it exists |
|
9645 if (iMoveEntry) iMoveEntry->SetEntry(NULL); |
|
9646 } |
|
9647 |
|
9648 TInt CImImap4Session::CommandFailure() const |
|
9649 { |
|
9650 return iCommandFailure; |
|
9651 } |
|
9652 |
|
9653 void CImImap4Session::FetchHeaderL(TUint aUid) |
|
9654 { |
|
9655 iFolderIndex.SetSizeL(iMailboxSize); |
|
9656 |
|
9657 // Create list of priority fields to request |
|
9658 TBuf8<256> priorityFields; |
|
9659 CDesC8ArrayFlat* array = new(ELeave) CDesC8ArrayFlat(4); |
|
9660 CleanupStack::PushL(array); |
|
9661 CImcvUtils::PriorityFieldsL(*array); |
|
9662 for (TInt i=0; i<array->Count(); i++) |
|
9663 { |
|
9664 priorityFields.Append((*array)[i]); |
|
9665 } |
|
9666 CleanupStack::PopAndDestroy(array); |
|
9667 ColonSeparatorToSpace(priorityFields); |
|
9668 |
|
9669 NewTag(); |
|
9670 |
|
9671 iImapIO->SendL(iStatus, KImapFetchSmallHeaderToEnd,iTag,aUid, &priorityFields); |
|
9672 NewTagSent(); |
|
9673 } |
|
9674 |
|
9675 void CImImap4Session::FetchLargeHeaderL(TUint aUid, TBool aRange) |
|
9676 { |
|
9677 // First, resize folder index to hold all messages in the folder, |
|
9678 // as opposed to the old sync list. This will preserve the old |
|
9679 // contents of the index, which is what we want as it's up-to-date |
|
9680 // and correct. |
|
9681 iFolderIndex.SetSizeL(iMailboxSize); |
|
9682 |
|
9683 // build list of header fields we want note that |
|
9684 // ReceiptFieldStrings returns strings colon terminated which we |
|
9685 // convert to spaces for the fetch |
|
9686 TBuf8<256> buf; |
|
9687 |
|
9688 CDesC8ArrayFlat* array = new(ELeave) CDesC8ArrayFlat(4); |
|
9689 CleanupStack::PushL(array); |
|
9690 |
|
9691 CImcvUtils::ReceiptFieldsL(*array); |
|
9692 TInt i; |
|
9693 for (i=0; i<array->Count(); i++) |
|
9694 buf.Append((*array)[i]); |
|
9695 |
|
9696 CImcvUtils::PriorityFieldsL(*array); |
|
9697 for (i=0; i<array->Count(); i++) |
|
9698 buf.Append((*array)[i]); |
|
9699 |
|
9700 CleanupStack::PopAndDestroy(); // array |
|
9701 |
|
9702 ColonSeparatorToSpace(buf); |
|
9703 |
|
9704 // Send command |
|
9705 NewTag(); |
|
9706 if (!aRange) |
|
9707 { |
|
9708 // We only want one envelope |
|
9709 iImapIO->SendL(iStatus, KImapFetchLargeHeader, iTag, aUid, &buf); |
|
9710 } |
|
9711 else |
|
9712 { |
|
9713 // If a UID search string has been specified, then we should create the UID FETCH |
|
9714 // string from the UID integer list. |
|
9715 if(iServiceSettings->SearchString().Length() !=0) |
|
9716 { |
|
9717 CreateUidStringL(); |
|
9718 TPtrC8 ptr(iUidString->Des()); |
|
9719 iImapIO->SendL(iStatus, KImapFetchLargeHeaderRangeRefined,iTag,&ptr,&buf); |
|
9720 } |
|
9721 else |
|
9722 { |
|
9723 iImapIO->SendL(iStatus, KImapFetchLargeHeaderRange,iTag,aUid, &buf); |
|
9724 } |
|
9725 } |
|
9726 NewTagSent(); |
|
9727 } |
|
9728 |
|
9729 void CImImap4Session::DoFetchL() |
|
9730 { |
|
9731 DBG((LogText(_L8("CImap4Session::DoFetchL(): running...")))); |
|
9732 |
|
9733 User::LeaveIfError(iEntry->SetEntry(iGetPart)); |
|
9734 TUid type=iEntry->Entry().iType; |
|
9735 |
|
9736 // if we are not asking for a Message type then override the get |
|
9737 // options to ensure that this is fetched |
|
9738 if(!iFetchPartialMail) |
|
9739 { |
|
9740 if (type != KUidMsvMessageEntry) |
|
9741 iGetOptions = EGetImap4EmailBodyTextAndAttachments; |
|
9742 } |
|
9743 |
|
9744 User::LeaveIfError(iEntry->SetEntry(iServiceId)); |
|
9745 |
|
9746 // What have we been asked to fetch? Build a list of parts to fetch: if the |
|
9747 // part requested has any children, that is. |
|
9748 // Reset stats |
|
9749 iProgress.iBytesToDo=0; |
|
9750 iProgress.iBytesDone=0; |
|
9751 iFetchList->Reset(); |
|
9752 iHtmlEntryPart = 0; |
|
9753 iBodyTextSize = 0; |
|
9754 iHtmlEntrySize = 0; |
|
9755 iBodyPartRemainingSize = 0; |
|
9756 iFooterString = NULL; |
|
9757 iSizeOfToBeFetchedAttachments=0; |
|
9758 TBool hasTextParts = EFalse; |
|
9759 if(iFetchPartialMail) |
|
9760 { |
|
9761 DBG((LogText(_L8("Using partial mail options")))); |
|
9762 AddFetchItemL(iGetPart,iGetPartialMailInfo,hasTextParts); |
|
9763 DBG((LogText(_L8("Found %d parts to fetch (options=%d)"),iFetchList->Count(),iGetPartialMailInfo.iPartialMailOptions))); |
|
9764 } |
|
9765 else |
|
9766 { |
|
9767 AddFetchItemL(iGetPart,iGetOptions,hasTextParts); |
|
9768 DBG((LogText(_L8("Found %d parts to fetch (options=%d)"),iFetchList->Count(),iGetOptions))); |
|
9769 } |
|
9770 |
|
9771 if( !hasTextParts && type == KUidMsvMessageEntry ) |
|
9772 { |
|
9773 // There are no text parts to this message - need to set body text |
|
9774 // complete flag to true otherwise UI may allow such a message to |
|
9775 // repeatedly be 'fetched' even though there is no text to fetch! |
|
9776 // |
|
9777 // So, set body text complete and message complete flags on the entry |
|
9778 // specified by iGetPart. |
|
9779 DBG((LogText(_L8("Message %d has no text parts - setting complete flag and body text complete flag to ETrue"),iGetPart))); |
|
9780 |
|
9781 User::LeaveIfError(iEntry->SetEntry(iGetPart)); |
|
9782 TMsvEmailEntry message = iEntry->Entry(); |
|
9783 |
|
9784 message.SetBodyTextComplete(ETrue); |
|
9785 |
|
9786 ChangeEntryL(message); |
|
9787 |
|
9788 // NOTE - not sure if necessary, but changing back to service entry to |
|
9789 // ensure consistent behaviour. |
|
9790 User::LeaveIfError(iEntry->SetEntry(iServiceId)); |
|
9791 } |
|
9792 |
|
9793 // Any parts at all? |
|
9794 if (iFetchList->Count() == 0 || iState == EImapStateFetchCancelWait) |
|
9795 { |
|
9796 // No, complete the fetch. |
|
9797 if( iState != EImapStateFetchCancelWait ) |
|
9798 { |
|
9799 iState=EImapStateSelectWait; |
|
9800 iSyncState=ENotSyncing; |
|
9801 } |
|
9802 |
|
9803 if( iCommandsOutstanding ) |
|
9804 { |
|
9805 // Waiting for tagged response for fetch command that got the |
|
9806 // email structure |
|
9807 GetReply(EFalse); |
|
9808 } |
|
9809 else |
|
9810 { |
|
9811 // Message structure already known - therefore not waiting for the |
|
9812 // tagged response, complete immediately |
|
9813 CommandCompleteL(KErrNone); |
|
9814 } |
|
9815 return; |
|
9816 } |
|
9817 |
|
9818 // Part count for stats |
|
9819 if (iFetchList->Count() > 0) |
|
9820 { |
|
9821 iProgress.iPartsToDo=iFetchList->Count(); |
|
9822 iProgress.iPartsDone=0; |
|
9823 |
|
9824 // Do the fetch |
|
9825 iState=EImapStateFetchWait; |
|
9826 iSyncState=EFetching; |
|
9827 |
|
9828 DBG((LogText(_L8("Starting body fetch of %d parts (%d bytes)"), |
|
9829 iProgress.iPartsToDo,iProgress.iBytesToDo))); |
|
9830 |
|
9831 // Make the command to send to the server |
|
9832 FetchAnItemL((*iFetchList)[0]); |
|
9833 iFetchList->Delete(0,1); |
|
9834 } |
|
9835 |
|
9836 } |
|
9837 |
|
9838 TInt CImImap4Session::CalculateDownloadSizeL(const CMsvEntrySelection& aSelection) |
|
9839 { |
|
9840 TInt totalSize = 0; |
|
9841 |
|
9842 // Do a quick tally on the size of messages which are to be copied / moved. |
|
9843 TInt count=aSelection.Count(); |
|
9844 while (count--) |
|
9845 { |
|
9846 SetEntryL(aSelection.At(count)); |
|
9847 // Only add the size up if the message is not complete. |
|
9848 if(!iEntry->Entry().Complete()) |
|
9849 { |
|
9850 totalSize += iEntry->Entry().iSize; |
|
9851 } |
|
9852 } |
|
9853 return totalSize; |
|
9854 } |
|
9855 |
|
9856 void CImImap4Session::SetSynchronisationSelectionL(CMsvEntrySelection &aSelection) |
|
9857 { |
|
9858 // Used by the server mtm to prevent any messages selected for retrieval |
|
9859 // from being deleted during a synchronisation with a synchronisation limit set. |
|
9860 delete iSynchronisationSelection; |
|
9861 iSynchronisationSelection = 0; |
|
9862 iSynchronisationSelection = aSelection.CopyL(); |
|
9863 } |
|
9864 |
|
9865 void CImImap4Session::SetInbox(TMsvId aInbox) |
|
9866 { |
|
9867 iInbox=aInbox; |
|
9868 } |
|
9869 |
|
9870 TMsvId CImImap4Session::GetInbox() |
|
9871 { |
|
9872 return iInbox; |
|
9873 } |
|
9874 |
|
9875 // Set or clear the \Seen flags on the server (aSettingsFlag = ETrue -> Sets the flag) |
|
9876 // Returns False if no messages need to be processed |
|
9877 TBool CImImap4Session::ProcessSeenFlagsL(TSeenFlagUpdateMode aUpdateMode) |
|
9878 { |
|
9879 CArrayFixFlat<TMsvId>* pendingList; |
|
9880 TBool settingFlag = (aUpdateMode == ESetSeenFlag); |
|
9881 |
|
9882 // Point pendingList to the correct list |
|
9883 pendingList = (settingFlag ? iSetSeenList: iClearSeenList); |
|
9884 |
|
9885 // Exit if nothing to process |
|
9886 if (!pendingList->Count()) |
|
9887 { |
|
9888 return EFalse; |
|
9889 } |
|
9890 |
|
9891 #ifdef PRINTING |
|
9892 _LIT8(KCommandProcessFlags, "COMMAND ProcessSeenFlags(%d)"); |
|
9893 LOG_COMMANDS((LogText(KCommandProcessFlags, aUpdateMode))); |
|
9894 #endif |
|
9895 |
|
9896 _LIT8(KStoreFlagsSetCommand, "%d UID STORE %S +FLAGS (\\Seen)\r\n"); |
|
9897 _LIT8(KStoreFlagsClearCommand, "%d UID STORE %S -FLAGS (\\Seen)\r\n"); |
|
9898 const TInt KMaxUIDsToProcess = 50; |
|
9899 const TInt KMaxCharsPerUID = 12; |
|
9900 |
|
9901 // Ensure that the buffer passed to CImapIO does not exceed 1024 |
|
9902 __ASSERT_DEBUG(KMaxUIDsToProcess * KMaxCharsPerUID < 1024 - (KStoreFlagsSetCommand().Length() + 10), gPanic(KMaxBufferLengthExceeded)); |
|
9903 |
|
9904 TInt pos = 0; |
|
9905 TInt stored = 0; |
|
9906 TInt run = 0; |
|
9907 TInt UID; |
|
9908 TInt lastUID = -1; |
|
9909 TInt listCount = pendingList->Count(); |
|
9910 TBool haveSentCommand = EFalse; |
|
9911 TMsvEmailEntry message; |
|
9912 HBufC8* command=HBufC8::NewLC(KMaxUIDsToProcess * KMaxCharsPerUID); |
|
9913 TPtr8 commandDes = command->Des(); |
|
9914 |
|
9915 // Build up a list of UID's who's \Seen flag needs changing. |
|
9916 // To save bandwidth, group contiguous blocks of UID's together, |
|
9917 // e.g. 1:6,8:12,14 instead of 1,2,3,4,5,6,8,9,10,12,14 |
|
9918 while(pos < listCount && stored < KMaxUIDsToProcess) |
|
9919 { |
|
9920 SetEntryL(pendingList->At(pos)); |
|
9921 message = iEntry->Entry(); |
|
9922 UID = static_cast<TInt>(message.UID()); |
|
9923 |
|
9924 // Should never have the state when the list contains flags that match the servers flag |
|
9925 __ASSERT_DEBUG(FIXBOOL(message.Unread()) != settingFlag, gPanic(KSettingSeenFlagToExistingState)); |
|
9926 |
|
9927 // Set the Seen flag for the message |
|
9928 message.SetSeenIMAP4Flag(settingFlag); |
|
9929 ChangeEntryL(message); |
|
9930 // If not first time through and the UID follows the previous one |
|
9931 if (pos !=0 && UID == lastUID+1) |
|
9932 { |
|
9933 // We are in a run |
|
9934 run++; |
|
9935 } |
|
9936 else |
|
9937 { |
|
9938 // If we were in a run |
|
9939 if (run) |
|
9940 { |
|
9941 // Append a ':' followed by the UID at the end of the run |
|
9942 commandDes.Append(TChar(':')); |
|
9943 commandDes.AppendNum(lastUID); |
|
9944 stored++; |
|
9945 // Reset the run count |
|
9946 run = 0; |
|
9947 } |
|
9948 // Append ',' only if not the first time through |
|
9949 if (pos!=0) |
|
9950 { |
|
9951 commandDes.Append(TChar(',')); |
|
9952 } |
|
9953 // Append the current UID |
|
9954 commandDes.AppendNum(UID); |
|
9955 stored++; |
|
9956 } |
|
9957 // Next message |
|
9958 pos++; |
|
9959 lastUID = UID; |
|
9960 } // end while |
|
9961 |
|
9962 // If we are at the end of the list but still in a run, add the last uid to the command buffer |
|
9963 if (run) |
|
9964 { |
|
9965 commandDes.Append(TChar(':')); |
|
9966 commandDes.AppendNum(lastUID); |
|
9967 } |
|
9968 |
|
9969 // Anything stored? |
|
9970 if (stored) |
|
9971 { |
|
9972 // Get a new tag |
|
9973 NewTag(); |
|
9974 |
|
9975 if (settingFlag) |
|
9976 iImapIO->SendL(iStatus,KStoreFlagsSetCommand, iTag, command); |
|
9977 else |
|
9978 iImapIO->SendL(iStatus,KStoreFlagsClearCommand, iTag, command); |
|
9979 |
|
9980 // Send the command |
|
9981 NewTagSent(); |
|
9982 // Remove the processed flags from the list (pos = num_to_remove) |
|
9983 pendingList->Delete(0, pos); |
|
9984 haveSentCommand = ETrue; |
|
9985 } |
|
9986 else |
|
9987 { |
|
9988 // Should never have the state when the list contained flags but we never sent the 'STORE FLAGS' command to the server |
|
9989 __ASSERT_DEBUG(FIXBOOL(message.Unread()) != settingFlag, gPanic(KErrorBuildingStoreFlagsCommand)); |
|
9990 } |
|
9991 |
|
9992 // Get rid of command buffer |
|
9993 CleanupStack::PopAndDestroy(command); |
|
9994 return haveSentCommand; |
|
9995 } |
|
9996 // Creates an IMAP4 defined optimised string of UIDs from the integer list of UIDs |
|
9997 // retrieved by the UID SEARCH command. |
|
9998 // Example: If the input array is 123 125 126 127 300 302 303 304 308 |
|
9999 // The output string will be "123,125:127,300,302,303:304,308 |
|
10000 // For the time being at least, keep the string as a class instance variable for possible re-use |
|
10001 void CImImap4Session::CreateUidStringL() |
|
10002 { |
|
10003 iUidString->Des().Zero(); |
|
10004 TPtr8 ptr(iUidString->Des()); |
|
10005 TUint32* current = &iSearchList->At(0); // Loop iterator |
|
10006 TUint32* start(NULL); // Keep track of the start of a UID sequence |
|
10007 TInt count = iSearchList->Count(); // Quick access |
|
10008 while(current < &iSearchList->At(0) + count) // Loop till the end of the integer list |
|
10009 { |
|
10010 // Check for room to add 2 integers plus 2 separators. |
|
10011 // If not enough room then reallocate |
|
10012 if((ptr.MaxLength() - ptr.Length()) < (KMaxUint32Chars)+2) |
|
10013 { |
|
10014 iUidString = iUidString->ReAllocL(ptr.MaxLength()*2); // Make sure this time |
|
10015 ptr.Set(iUidString->Des()); |
|
10016 } |
|
10017 start = current; // Mark the start of a possible sequence |
|
10018 if(current != &iSearchList->At(0)) // If it's the first time through the loop don't append separator |
|
10019 { |
|
10020 ptr.Append(','); |
|
10021 } |
|
10022 ptr.AppendNum(*start); // First integer always written |
|
10023 // Loop until end of sequence or end of list. |
|
10024 // will loop through 125 126 127 |
|
10025 while(current < &iSearchList->At(count-1) && *current == (*(current+1))-1) |
|
10026 { |
|
10027 ++current; |
|
10028 } |
|
10029 // If there was a run then current won't equal start. |
|
10030 // That being the case, append the sequence separator followed by last in sequence |
|
10031 // 125:127 |
|
10032 // If there's no sequence, the next int is written by ptr.AppendNum(*start) |
|
10033 if(current != start) |
|
10034 { |
|
10035 ptr.Append(':'); |
|
10036 ptr.AppendNum(*current); |
|
10037 } |
|
10038 ++current; |
|
10039 } |
|
10040 } |
|
10041 |
|
10042 void CImImap4Session::IdleTimerExpiredL() |
|
10043 { |
|
10044 iIdleTimerExpired = ETrue; |
|
10045 ReissueIdleL(); |
|
10046 } |
|
10047 |
|
10048 void CImImap4Session::CancelTimerExpired() |
|
10049 { |
|
10050 Cancel(); |
|
10051 if (iState != EImapStateDisconnected) |
|
10052 { |
|
10053 DoDisconnect(); |
|
10054 } |
|
10055 iObserver.NonCompletedFailure(); |
|
10056 } |
|
10057 |
|
10058 |
|
10059 CIdleTimeoutTimer::CIdleTimeoutTimer(CImImap4Session& aOperation) |
|
10060 : CTimer(EPriorityHigh), iOperation(aOperation) |
|
10061 {} |
|
10062 |
|
10063 void CIdleTimeoutTimer::RunL() |
|
10064 { |
|
10065 iOperation.IdleTimerExpiredL(); |
|
10066 } |
|
10067 |
|
10068 CIdleTimeoutTimer* CIdleTimeoutTimer::NewL(CImImap4Session& aOperation) |
|
10069 { |
|
10070 CIdleTimeoutTimer* self = new(ELeave) CIdleTimeoutTimer(aOperation); |
|
10071 CleanupStack::PushL(self); |
|
10072 self->ConstructL(); // CTimer |
|
10073 CActiveScheduler::Add(self); |
|
10074 CleanupStack::Pop(); |
|
10075 return self; |
|
10076 } |
|
10077 |
|
10078 |
|
10079 CImImap4SessionDummyRead* CImImap4SessionDummyRead::NewL(CImImap4Session& aSession, CImapIO& aImapIO, TInt aPriority) |
|
10080 { |
|
10081 CImImap4SessionDummyRead* self = new (ELeave) CImImap4SessionDummyRead(aSession, aImapIO, aPriority); |
|
10082 return self; |
|
10083 } |
|
10084 |
|
10085 CImImap4SessionDummyRead::CImImap4SessionDummyRead(CImImap4Session& aSession, CImapIO& aImapIO, TInt aPriority) |
|
10086 : CActive(aPriority), iSession(aSession), iImapIO(aImapIO) |
|
10087 { |
|
10088 DBG(iSession.LogText(_L8("+ CImImap4SessionDummyRead()") ) ); |
|
10089 CActiveScheduler::Add(this); |
|
10090 DBG(iSession.LogText(_L8("- CImImap4SessionDummyRead()") ) ); |
|
10091 } |
|
10092 |
|
10093 CImImap4SessionDummyRead::~CImImap4SessionDummyRead() |
|
10094 { |
|
10095 DBG(iSession.LogText(_L8("+ ~CImImap4SessionDummyRead") ) ); |
|
10096 Cancel(); |
|
10097 DBG(iSession.LogText(_L8("- ~CImImap4SessionDummyRead") ) ); |
|
10098 } |
|
10099 |
|
10100 void CImImap4SessionDummyRead::Start() |
|
10101 { |
|
10102 DBG( iSession.LogText( _L8("+ CImImap4SessionDummyRead::Start()") ) ); |
|
10103 if(IsActive()) |
|
10104 { |
|
10105 // already have an outstanding dummy read, ignore this |
|
10106 DBG( iSession.LogText( _L8("CImImap4SessionDummyRead::Start():Already active") ) ); |
|
10107 return; |
|
10108 } |
|
10109 |
|
10110 DBG( iSession.LogText( _L8("+ CImImap4SessionDummyRead::Start()") ) ); |
|
10111 // As the spec is ambiguous about when we can get untagged responses, |
|
10112 // we can get them any time, so ask for a proper reply |
|
10113 iImapIO.GetReply(iStatus); |
|
10114 |
|
10115 DBG((iSession.LogText(_L8("*******************************************************")))); |
|
10116 DBG((iSession.LogText(_L8("CImap4SessionDummyRead::Start(): waiting for iImapIO to wake me")))); |
|
10117 DBG((iSession.LogText(_L8("*******************************************************")))); |
|
10118 |
|
10119 SetActive(); |
|
10120 DBG( iSession.LogText( _L8("- CImImap4SessionDummyRead::Start()") ) ); |
|
10121 } |
|
10122 |
|
10123 void CImImap4SessionDummyRead::DoCancel() |
|
10124 { |
|
10125 DBG( iSession.LogText( _L8("+ CImImap4SessionDummyRead::DoCancel()") ) ); |
|
10126 iImapIO.Cancel(); |
|
10127 DBG( iSession.LogText( _L8("- CImImap4SessionDummyRead::DoCancel()") ) ); |
|
10128 } |
|
10129 |
|
10130 void CImImap4SessionDummyRead::RunL() |
|
10131 { |
|
10132 //Inform iOperation that the dummy read has completed. |
|
10133 |
|
10134 //Under normal conditions the dummy read should never complete. |
|
10135 //However, if it does complete it is likely that iStatus != KErrNone. |
|
10136 DBG( iSession.LogText( _L8("+ CImImap4SessionDummyRead::RunL()") ) ); |
|
10137 iSession.DummyComplete(iStatus.Int()); |
|
10138 if (iStatus.Int() >= KErrNone) |
|
10139 { |
|
10140 DBG( iSession.LogText(_L8(" CImImap4SessionDummyRead::RunL() There might be more data coming, issue dummy reading again.")) ); |
|
10141 iSession.ReissueDummy(); |
|
10142 } |
|
10143 DBG( iSession.LogText( _L8("- CImImap4SessionDummyRead::RunL()") ) ); |
|
10144 } |
|
10145 |
|
10146 |
|
10147 CImImap4SessionIdleRead* CImImap4SessionIdleRead::NewL(CImImap4Session& aOperation, TInt aPriority) |
|
10148 { |
|
10149 CImImap4SessionIdleRead* self = new (ELeave) CImImap4SessionIdleRead(aOperation, aPriority); |
|
10150 return self; |
|
10151 } |
|
10152 |
|
10153 CImImap4SessionIdleRead::CImImap4SessionIdleRead(CImImap4Session& aOperation, TInt aPriority) |
|
10154 : CActive(aPriority), iOperation(aOperation) |
|
10155 { |
|
10156 CActiveScheduler::Add(this); |
|
10157 } |
|
10158 |
|
10159 CImImap4SessionIdleRead::~CImImap4SessionIdleRead() |
|
10160 { |
|
10161 Cancel(); |
|
10162 } |
|
10163 |
|
10164 void CImImap4SessionIdleRead::Start(TRequestStatus& aStatus) |
|
10165 { |
|
10166 // Store the status for completion later |
|
10167 iOperationStatus = &aStatus; |
|
10168 *iOperationStatus = KRequestPending; |
|
10169 |
|
10170 // Issue the read with *our* status |
|
10171 iOperation.DoIdleRead(iStatus); |
|
10172 DBG((iOperation.LogText(_L8("*******************************************************")))); |
|
10173 DBG((iOperation.LogText(_L8("CImap4SessionIdleRead::Start(): waiting for iOperation to wake me")))); |
|
10174 DBG((iOperation.LogText(_L8("*******************************************************")))); |
|
10175 |
|
10176 SetActive(); |
|
10177 } |
|
10178 |
|
10179 void CImImap4SessionIdleRead::DoCancel() |
|
10180 { |
|
10181 DBG(iOperation.LogText(_L8("CImImap4SessionIdleRead::DoCancel()"))); |
|
10182 |
|
10183 // Cancel the idle read we started |
|
10184 iOperation.CancelIdleRead(); |
|
10185 DBG((iOperation.LogText(_L8("-----------------------------------------------------------")))); |
|
10186 DBG((iOperation.LogText(_L8("CImap4SessionIdleRead::DoCancel(): calling request complete")))); |
|
10187 DBG((iOperation.LogText(_L8("-----------------------------------------------------------")))); |
|
10188 |
|
10189 User::RequestComplete(iOperationStatus, KErrCancel); |
|
10190 } |
|
10191 |
|
10192 void CImImap4SessionIdleRead::RunL() |
|
10193 { |
|
10194 TInt err = iStatus.Int(); |
|
10195 DBG(iOperation.LogText(_L8("CImImap4SessionIdleRead::RunL(iStatus=%d)"),err)); |
|
10196 |
|
10197 if(err < KErrNone) |
|
10198 { // Special case for error |
|
10199 // Need to do this because if read completed with an error, |
|
10200 // the RequestComplete would not handle this by itself as |
|
10201 // the RunL would not be called and neither would the |
|
10202 // DoComplete (because the session's iReport is NULL...) |
|
10203 iOperation.IdleReadError(err); |
|
10204 } |
|
10205 DBG((iOperation.LogText(_L8("-----------------------------------------------------------")))); |
|
10206 DBG((iOperation.LogText(_L8("CImap4SessionIdleRead::RunL(): calling request complete")))); |
|
10207 DBG((iOperation.LogText(_L8("-----------------------------------------------------------")))); |
|
10208 |
|
10209 User::RequestComplete(iOperationStatus, err); |
|
10210 } |