|
1 // Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
2 // All rights reserved. |
|
3 // This component and the accompanying materials are made available |
|
4 // under the terms of "Eclipse Public License v1.0" |
|
5 // which accompanies this distribution, and is available |
|
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
7 // |
|
8 // Initial Contributors: |
|
9 // Nokia Corporation - initial contribution. |
|
10 // |
|
11 // Contributors: |
|
12 // |
|
13 // Description: |
|
14 // cimapcompoundbase.cpp |
|
15 // |
|
16 |
|
17 #include "cimapcompoundbase.h" |
|
18 #include "cimapsession.h" |
|
19 #include "cimapsettings.h" |
|
20 #include "cimapsessionconsts.h" |
|
21 #include "cimaplogger.h" |
|
22 #include "imappaniccodes.h" |
|
23 |
|
24 #ifdef SYMBIAN_ENABLE_SPLIT_HEADERS |
|
25 #include "timrfc822datefield.h" |
|
26 #endif |
|
27 |
|
28 CImapCompoundBase::CImapCompoundBase( CImapSyncManager& aSyncManager, |
|
29 CMsvServerEntry& aServerEntry, |
|
30 CImapSettings& aImapSettings) : |
|
31 CMsgActive(EPriorityStandard ), |
|
32 iSyncManager(aSyncManager), |
|
33 iServerEntry(aServerEntry), |
|
34 iImapSettings(aImapSettings), |
|
35 iStopForMigrate(EFalse), |
|
36 iCancelForMigrate(EFalse), |
|
37 iProgressState(TImap4GenericProgress::EIdle), |
|
38 iSyncProgressState(TImap4SyncProgress::EIdle) |
|
39 { |
|
40 |
|
41 } |
|
42 |
|
43 CImapCompoundBase::~CImapCompoundBase( ) |
|
44 { |
|
45 } |
|
46 |
|
47 #ifdef __IMAP_LOGGING |
|
48 void CImapCompoundBase::DoComplete(TInt& aErr) |
|
49 #else |
|
50 void CImapCompoundBase::DoComplete(TInt& /*aErr*/) |
|
51 #endif //_IMAP_LOGGING |
|
52 { |
|
53 __LOG_FORMAT((iSession->LogId(), "CImapCompoundBase::DoComplete(aErr = %d) CurrentStep = %d", aErr, iCurrentStep)); |
|
54 } |
|
55 |
|
56 /** |
|
57 Called when asynchronous service requests complete. |
|
58 Handles errors returned by aSynchronous services. |
|
59 |
|
60 The state machine for derived compound commands is managed by |
|
61 the pure-virtual DoRunLoopL() which is called by DoRunL(). |
|
62 |
|
63 This form of DoRunL also allows synchronous steps to be performed |
|
64 sequentially. |
|
65 */ |
|
66 void CImapCompoundBase::DoRunL() |
|
67 { |
|
68 // Handle any server errors |
|
69 if (iStatus.Int()!=KErrNone) |
|
70 { |
|
71 if (TInt err=ProcessSessionError() != KErrNone) |
|
72 { |
|
73 Complete(err); |
|
74 return; |
|
75 } |
|
76 } |
|
77 |
|
78 // Operation state machine handled by DoRunLoopL() |
|
79 while (!IsActive() && !DoRunLoopL()) |
|
80 { |
|
81 // do nothing in the body of this |
|
82 } |
|
83 } |
|
84 |
|
85 |
|
86 /** |
|
87 Base class method for handling errors returned by the session. |
|
88 Called on completion of an asynchronous request on the IMAP session, |
|
89 ie at the start of the DoRunL() method. If this method returns |
|
90 a value other than KErrNone, the compound operation should complete |
|
91 with the return value as the error code. |
|
92 |
|
93 This will catch any positive error codes returned by the imap |
|
94 session These indicate either NO/BAD server responses or a more |
|
95 serious error has occurred, for example a disconnect, or corrupt |
|
96 data received from the remote server. |
|
97 |
|
98 NO/BAD server responses are handled by the derived compound |
|
99 operation classes (these are non-fatal errors, an attempt is |
|
100 made to continue the requested operation) |
|
101 |
|
102 Other errors are more serious - these are passed up to the |
|
103 Protocol Controller to deal with. |
|
104 |
|
105 @return KErrNone if the error has been handled, |
|
106 otherwise the positive error code as returned by |
|
107 the IMAP session. |
|
108 */ |
|
109 TInt CImapCompoundBase::ProcessSessionError() |
|
110 { |
|
111 TInt errCode = iStatus.Int(); |
|
112 if (errCode==KErrImapNo || errCode==KErrImapBad) |
|
113 { |
|
114 return ProcessNegativeServerResponse(); |
|
115 } |
|
116 else |
|
117 { |
|
118 return errCode; |
|
119 } |
|
120 } |
|
121 |
|
122 /** |
|
123 Checks the input selection for entries of specified types. |
|
124 In this context, "Messages" means complete messages, not RFC888 |
|
125 parts of another message. "parts" means attachment or text bodies. |
|
126 |
|
127 @param aSelection - original list of message entry ids |
|
128 @param aLocalCopy - filtered copy of message entry ids. |
|
129 @param aMessages - accept entries of type KUidMsvMessageEntry |
|
130 @param aParts - accept entries of type KUidMsvEmailTextEntry, |
|
131 KUidMsvAttachmentEntry, |
|
132 KUidMsvMessageEntry |
|
133 KUidMsvEmailHtmlEntry |
|
134 KUidMsvEmailExternalBodyEntry |
|
135 KUidMsvEmailRtfEntry |
|
136 @param aFolders - accept entries of type KUidMsvFolderEntry |
|
137 @param aIsInService - only accept entries under the IMAP service entry |
|
138 |
|
139 @return KErrNotSupported if no valid entries, |
|
140 KErrNone otherwise |
|
141 */ |
|
142 TInt CImapCompoundBase::CheckSelectionL(const CMsvEntrySelection& aSelection, |
|
143 CMsvEntrySelection* aLocalCopy, |
|
144 const TBool aMessages, |
|
145 const TBool aParts, |
|
146 const TBool aFolders, |
|
147 const TBool aIsInService) |
|
148 { |
|
149 // reset the local copy |
|
150 aLocalCopy->Reset(); |
|
151 |
|
152 // get the service ID |
|
153 TMsvId serviceId = iImapSettings.ServiceId(); |
|
154 |
|
155 // Check all entries are messages |
|
156 for(TInt a=0;a<aSelection.Count();a++) |
|
157 { |
|
158 // Does entry exist? |
|
159 TBool addIt = EFalse; |
|
160 |
|
161 if (iServerEntry.SetEntry(aSelection[a])==KErrNone) |
|
162 { |
|
163 TUid type = iServerEntry.Entry().iType; |
|
164 if ((aMessages && type==KUidMsvMessageEntry) || |
|
165 (aParts && (type==KUidMsvEmailTextEntry || type==KUidMsvAttachmentEntry || type==KUidMsvMessageEntry || type==KUidMsvEmailHtmlEntry || type==KUidMsvEmailExternalBodyEntry || type==KUidMsvEmailRtfEntry)) || |
|
166 (aFolders && type==KUidMsvFolderEntry)) |
|
167 { |
|
168 TBool inEnclosingMessage=EFalse; |
|
169 |
|
170 // Do we need to check if it's in the local service or |
|
171 // if it is a complete message |
|
172 if (aIsInService || (!aParts && type==KUidMsvMessageEntry)) |
|
173 { |
|
174 // Work up the tree until we get to the service or the root |
|
175 do |
|
176 { |
|
177 SetEntryL(iServerEntry.Entry().Parent()); |
|
178 if (iServerEntry.Entry().iType==KUidMsvMessageEntry) |
|
179 { |
|
180 inEnclosingMessage=ETrue; |
|
181 } |
|
182 } |
|
183 while(iServerEntry.Entry().iType!=KUidMsvServiceEntry && |
|
184 iServerEntry.Entry().Id()!=KMsvRootIndexEntryId); |
|
185 |
|
186 // Are we at the service that this MTM referrs to? |
|
187 // if offline iServiceId==0 so allow all |
|
188 if (!aIsInService || serviceId==0 || iServerEntry.Entry().Id()==serviceId) |
|
189 { |
|
190 // it's OK if it is not a message type (in |
|
191 // which case it has already been checked and |
|
192 // passed) or it is not within an enclosing message |
|
193 if (type!=KUidMsvMessageEntry || !inEnclosingMessage || aParts) |
|
194 { |
|
195 addIt = ETrue; |
|
196 } |
|
197 } |
|
198 } |
|
199 else |
|
200 { |
|
201 // Add to local copy |
|
202 addIt = ETrue; |
|
203 } |
|
204 } |
|
205 } |
|
206 |
|
207 if (addIt) |
|
208 { |
|
209 aLocalCopy->AppendL(aSelection[a]); |
|
210 } |
|
211 #ifdef _DEBUG |
|
212 // UI shouldn't really be giving us bogus items so panic |
|
213 else |
|
214 { |
|
215 TImapServerPanic::ImapPanic(TImapServerPanic::EInvalidMsvTypeToCommand); |
|
216 } |
|
217 #endif |
|
218 } |
|
219 |
|
220 // Anything to do? |
|
221 if (!aLocalCopy->Count()) |
|
222 { |
|
223 // Nothing valid to work with |
|
224 return(KErrNotSupported); |
|
225 } |
|
226 else |
|
227 { |
|
228 // All OK, the selection isn't empty |
|
229 return(KErrNone); |
|
230 } |
|
231 } |
|
232 |
|
233 /** |
|
234 Sums the total size of messages to be copied or moved |
|
235 |
|
236 @param aSelection - selection of messages to be processed |
|
237 @return TInt - the total size of the messages in the selection |
|
238 */ |
|
239 TInt CImapCompoundBase::CalculateDownloadSizeL(const CMsvEntrySelection& aSelection) |
|
240 { |
|
241 TInt totalSize = 0; |
|
242 |
|
243 // Do a quick tally on the size of messages which are to be copied / moved. |
|
244 TInt count=aSelection.Count(); |
|
245 while (count--) |
|
246 { |
|
247 SetEntryL(aSelection.At(count)); |
|
248 // Only add the size up if the message is not complete. |
|
249 if(!iServerEntry.Entry().Complete()) |
|
250 { |
|
251 totalSize += iServerEntry.Entry().iSize; |
|
252 } |
|
253 } |
|
254 return totalSize; |
|
255 } |
|
256 |
|
257 /** |
|
258 Find the folder that contains specified message |
|
259 |
|
260 @param aMessage - selection of messages to be processed |
|
261 @return TMsvId - the parent folder of aMessage, or the service ID. |
|
262 */ |
|
263 TMsvId CImapCompoundBase::FindFolderL(const TMsvId aMessage) |
|
264 { |
|
265 __LOG_FORMAT((iSession->LogId(), "CImapCompoundBase::FindFolderL(%d)", aMessage)); |
|
266 |
|
267 // Find folder that encloses this message (has Mailbox flag set), or service, |
|
268 // whichever we find first |
|
269 SetEntryL(aMessage); |
|
270 TMsvEmailEntry entry; |
|
271 do |
|
272 { |
|
273 // Change context to parent of current entry |
|
274 SetEntryL(iServerEntry.Entry().Parent()); |
|
275 entry=iServerEntry.Entry(); |
|
276 |
|
277 __LOG_FORMAT((iSession->LogId(), " At %x, type=%x, mailbox=%d", entry.Id(),entry.iType,entry.Mailbox())); |
|
278 |
|
279 // A folder & a mailbox, or a service? |
|
280 if (entry.iType==KUidMsvFolderEntry && entry.Mailbox()) |
|
281 { |
|
282 // This'll do! |
|
283 return(entry.Id()); |
|
284 } |
|
285 } |
|
286 while(iServerEntry.Entry().iType!=KUidMsvServiceEntry && entry.Id()!=KMsvRootIndexEntryId); |
|
287 |
|
288 __LOG_TEXT(iSession->LogId(), " FindFolderL() Failed"); |
|
289 |
|
290 return(NULL); |
|
291 } |
|
292 |
|
293 // Do setentry, leave if there is an error |
|
294 void CImapCompoundBase::SetEntryL(const TMsvId aId) |
|
295 { |
|
296 User::LeaveIfError(iServerEntry.SetEntry(aId)); |
|
297 } |
|
298 |
|
299 /** |
|
300 Self completes to allow RunL to be called. |
|
301 */ |
|
302 void CImapCompoundBase::CompleteSelf() |
|
303 { |
|
304 SetActive(); |
|
305 TRequestStatus* status = &iStatus; |
|
306 User::RequestComplete(status, KErrNone); |
|
307 } |
|
308 |
|
309 |
|
310 void CImapCompoundBase::SetCurrentStep() |
|
311 { |
|
312 iCurrentStep = iNextStep; |
|
313 } |
|
314 |
|
315 /** |
|
316 Save error code in a message |
|
317 |
|
318 @param aMessgeId |
|
319 @param aError |
|
320 */ |
|
321 void CImapCompoundBase::MessageErrorL(const TMsvId aMessageId, const TInt aError) |
|
322 { |
|
323 // Save error code: if we can't access this entry, then it's probably something to do |
|
324 // with the error we're trying to report: ignore it silently |
|
325 if (iServerEntry.SetEntry(aMessageId)==KErrNone) |
|
326 { |
|
327 TMsvEntry entry=iServerEntry.Entry(); |
|
328 |
|
329 // Save unnecessary writes... |
|
330 if (entry.iError!=aError) |
|
331 { |
|
332 entry.iError=aError; |
|
333 User::LeaveIfError(iServerEntry.ChangeEntry(entry)); |
|
334 } |
|
335 } |
|
336 } |
|
337 |
|
338 /** |
|
339 Cancels outstanding service request, and recovers the compound operation |
|
340 object to a state in which the operation may be restarted following migration |
|
341 to a new carrier. |
|
342 The operation will be restarted by a call to ResumeOperationL(). |
|
343 |
|
344 Derived classes must implement a DoCancel that sets iNextStep to the step |
|
345 that should be performed following the migration, when ReumeOperationL is called; |
|
346 */ |
|
347 void CImapCompoundBase::CancelForMigrate() |
|
348 { |
|
349 __LOG_TEXT(iSession->LogId(), "CImapCompoundBase::CancelForMigrate()"); |
|
350 iCancelForMigrate = ETrue; |
|
351 Cancel(); |
|
352 iCancelForMigrate = EFalse; |
|
353 // Mark Operation as suspended. |
|
354 iCurrentStep = ESuspendedForMigrate; |
|
355 } |
|
356 |
|
357 /** |
|
358 Indicates that the compound operation should complete as soon as it is possible |
|
359 for the operation to be re-started without requiring a significant amount of |
|
360 repeated communication. |
|
361 The operation will be restarted by a call to ResumeOperationL() |
|
362 */ |
|
363 void CImapCompoundBase::StopForMigrate() |
|
364 { |
|
365 __LOG_TEXT(iSession->LogId(), "CImapCompoundBase::StopForMigrate()"); |
|
366 iStopForMigrate = ETrue; |
|
367 } |
|
368 |
|
369 /** |
|
370 Returns ETrue if the compound operation has been suspended (stopped/cancelled). |
|
371 This would occur in the case of a bearer migration. |
|
372 */ |
|
373 TBool CImapCompoundBase::Suspended() |
|
374 { |
|
375 if (iCurrentStep == ESuspendedForMigrate) |
|
376 { |
|
377 return ETrue; |
|
378 } |
|
379 return EFalse; |
|
380 } |
|
381 |