|
1 // Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
2 // All rights reserved. |
|
3 // This component and the accompanying materials are made available |
|
4 // under the terms of "Eclipse Public License v1.0" |
|
5 // which accompanies this distribution, and is available |
|
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
7 // |
|
8 // Initial Contributors: |
|
9 // Nokia Corporation - initial contribution. |
|
10 // |
|
11 // Contributors: |
|
12 // |
|
13 // Description: |
|
14 // |
|
15 |
|
16 #include "cimapcompoundrename.h" |
|
17 #include "cimapsession.h" |
|
18 #include "cimapsyncmanager.h" |
|
19 #include "cimapfolder.h" |
|
20 #include "cimaplogger.h" |
|
21 #include "imappaniccodes.h" |
|
22 |
|
23 #include "mobilitytestmtmapi.h" |
|
24 |
|
25 _LIT16(KFullPathFormat, "%S%S%S"); |
|
26 |
|
27 CImapCompoundRename::CImapCompoundRename(CImapSyncManager& aSyncManager, |
|
28 CMsvServerEntry& aServerEntry, |
|
29 CImapSettings& aImapSettings, |
|
30 const TMsvId aTarget) |
|
31 : CImapCompoundBase(aSyncManager, aServerEntry, aImapSettings), |
|
32 iFolderId(aTarget) |
|
33 { |
|
34 } |
|
35 |
|
36 CImapCompoundRename::~CImapCompoundRename() |
|
37 { |
|
38 delete iNewLeafName; |
|
39 delete iMsgselection; |
|
40 } |
|
41 |
|
42 CImapCompoundRename* CImapCompoundRename::NewL(CImapSyncManager& aSyncManager, |
|
43 CMsvServerEntry& aServerEntry, |
|
44 CImapSettings& aImapSettings, |
|
45 const TMsvId aTarget, |
|
46 const TDesC& aNewName) |
|
47 { |
|
48 CImapCompoundRename* self = new (ELeave) CImapCompoundRename(aSyncManager, |
|
49 aServerEntry, |
|
50 aImapSettings, |
|
51 aTarget); |
|
52 CleanupStack::PushL(self); |
|
53 self->ConstructL(aNewName); |
|
54 CleanupStack::Pop(self); |
|
55 return self; |
|
56 } |
|
57 |
|
58 /** |
|
59 Secondary construction |
|
60 */ |
|
61 void CImapCompoundRename::ConstructL(const TDesC& aNewName) |
|
62 { |
|
63 // Store the new folder name |
|
64 iNewLeafName = HBufC16::NewL(aNewName.Length()); |
|
65 TPtr16 ptr(iNewLeafName->Des()); |
|
66 ptr.Copy(aNewName); |
|
67 |
|
68 CActiveScheduler::Add(this); |
|
69 } |
|
70 |
|
71 /** |
|
72 Starts the folder rename operation. |
|
73 |
|
74 @param aStatus client object's TRequestStatus, to be notified on completion |
|
75 @param aTarget the local id of the folder to be renamed |
|
76 @param aNewName the new name for the specified folder |
|
77 */ |
|
78 void CImapCompoundRename::StartOperation(TRequestStatus& aStatus, CImapSession& aSession) |
|
79 { |
|
80 iSession = &aSession; |
|
81 __LOG_TEXT(iSession->LogId(), "CImapCompoundRename::StartOperation()"); |
|
82 iNextStep = ERename; |
|
83 Queue(aStatus); |
|
84 CompleteSelf(); |
|
85 } |
|
86 |
|
87 /** |
|
88 Handles the compound operation state machine |
|
89 |
|
90 @return ETrue if compound operation is completed, |
|
91 EFalse otherwise (will be called again, unless active) |
|
92 */ |
|
93 TBool CImapCompoundRename::DoRunLoopL() |
|
94 { |
|
95 SetCurrentStep(); |
|
96 switch (iCurrentStep) |
|
97 { |
|
98 |
|
99 case ERename: // asynchronous |
|
100 { |
|
101 MOBILITY_TEST_MTM_STATE(iImapSettings.ServiceId(), KMobilityTestMtmStateImapRename1); // RENAME issued |
|
102 // Issue the rename command to the remote server |
|
103 // IssueRenameL() calls SetActive() |
|
104 IssueRenameL(); |
|
105 break; |
|
106 } |
|
107 |
|
108 case EProcessRenameResponse: // asynchronous if renaming inbox, else synchronous |
|
109 { |
|
110 SetEntryL(iFolderId); |
|
111 TMsvEmailEntry entry = iServerEntry.Entry(); |
|
112 TBool remotelySubscribed = entry.Subscribed(); |
|
113 iNextStep = remotelySubscribed?ESyncSubscriptions:EFinished; |
|
114 |
|
115 // Rename the folder locally. |
|
116 if (iFolderId == iSyncManager.Inbox()->MailboxId()) |
|
117 { |
|
118 RenameInboxL(); // calls SetActive() |
|
119 } |
|
120 else |
|
121 { |
|
122 // Sync Manager updates affected CImapFolder objects. |
|
123 iSyncManager.RenameLocalL(iFolderId, iNewLeafName->Des()); |
|
124 } |
|
125 break; |
|
126 } |
|
127 |
|
128 case ESyncSubscriptions: // asynchronous |
|
129 { |
|
130 MOBILITY_TEST_MTM_STATE(iImapSettings.ServiceId(), KMobilityTestMtmStateImapRename2); // SUBSCRIBE issued |
|
131 // The folder was previously remotely subscribed. |
|
132 // IMAP RFC does not specify that a folder must retain its subscribed |
|
133 // status following a rename, therefore we issue an explicit subscribe |
|
134 // command to ensure remote subscription status is maintained. |
|
135 CImapFolder* folder = iSyncManager.GetFolderL(iFolderId); |
|
136 iSession->SubscribeL(iStatus, folder->FullFolderPathL()); |
|
137 SetActive(); |
|
138 iNextStep = EFinished; |
|
139 break; |
|
140 } |
|
141 |
|
142 case EFinished: // finished |
|
143 { |
|
144 __LOG_TEXT(iSession->LogId(), "CImapCompoundRename::Completing OK"); |
|
145 iProgressState = TImap4GenericProgress::EIdle; |
|
146 Complete(KErrNone); |
|
147 return ETrue; |
|
148 } |
|
149 |
|
150 default : |
|
151 { |
|
152 __ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::ERenameCompoundUnexpectedState)); |
|
153 // unexpected state - complete the request |
|
154 iProgressState = TImap4GenericProgress::EIdle; |
|
155 return ETrue; |
|
156 } |
|
157 |
|
158 } // end of switch (iCurrentStep) |
|
159 return EFalse; |
|
160 } |
|
161 |
|
162 /** |
|
163 May be called in case of a genuine cancel or a cancel for migrate. |
|
164 Following a genuine cancel, the compound operation will be deleted. |
|
165 Following a cancel for migrate, the compound operation will be resumed, |
|
166 so the iNextState is updated here to ensure the operation is |
|
167 correctly restarted. |
|
168 |
|
169 In either case, CMsgActive::DoCancel() is called to complete the |
|
170 user request with KErrCancel. |
|
171 |
|
172 Note that if the default CMsgActive::DoComplete() is overridden, |
|
173 then that must also be modified to handle either case described above. |
|
174 */ |
|
175 void CImapCompoundRename::DoCancel() |
|
176 { |
|
177 switch (iCurrentStep) |
|
178 { |
|
179 case ERename: |
|
180 { |
|
181 // outstanding request on the session |
|
182 iSession->Cancel(); |
|
183 // Re-issue the rename after migration. There is a race condition |
|
184 // - it is possible that the rename occurred on the server but the |
|
185 // response was not received. This is not handled. The next folder |
|
186 // sync will sort it out. |
|
187 iNextStep=ERename; |
|
188 break; |
|
189 } |
|
190 case ESyncSubscriptions: |
|
191 { |
|
192 iSession->Cancel(); |
|
193 iNextStep=ESyncSubscriptions; |
|
194 break; |
|
195 } |
|
196 case EProcessRenameResponse: |
|
197 { |
|
198 // moving contents of inbox |
|
199 iServerEntry.Cancel(); |
|
200 iNextStep=EProcessRenameResponse; |
|
201 break; |
|
202 } |
|
203 case EFinished: |
|
204 case ESuspendedForMigrate: |
|
205 default: |
|
206 { |
|
207 __ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::ERenameCompoundCancelUnexpectedState)); |
|
208 iNextStep = ERename; |
|
209 break; |
|
210 } |
|
211 } // end switch (iCurrentStep) |
|
212 |
|
213 if (!iCancelForMigrate) |
|
214 { |
|
215 // genuine cancel - update progress |
|
216 iProgressErrorCode = KErrCancel; |
|
217 } |
|
218 CMsgActive::DoCancel(); |
|
219 } |
|
220 |
|
221 |
|
222 /** |
|
223 Builds up the full path name for the renamed folder and issues the |
|
224 RENAME IMAP command via the imap session. |
|
225 |
|
226 @leave system wide error codes |
|
227 */ |
|
228 void CImapCompoundRename::IssueRenameL() |
|
229 { |
|
230 CImapFolder* folder = iSyncManager.GetFolderL(iFolderId); |
|
231 if(folder == NULL) |
|
232 { |
|
233 // Sync manager hasn't found the folder under the imap service. |
|
234 // exit with progress error code Not Found |
|
235 iProgressErrorCode = KErrNotFound; |
|
236 iNextStep = EFinished; |
|
237 CompleteSelf(); |
|
238 return; |
|
239 } |
|
240 |
|
241 // build the new full path name for the folder |
|
242 HBufC16* newPath = folder->MakePathL(EFalse); |
|
243 CleanupStack::PushL(newPath); |
|
244 |
|
245 // build up the full path |
|
246 HBufC16* newFullPath = NULL; |
|
247 if (newPath->Length() > 0) |
|
248 { |
|
249 // folder is a subdirectory |
|
250 TInt buffLength = newPath->Length() + iNewLeafName->Length() + 1; // +1 for heirarchy separator |
|
251 newFullPath = HBufC16::NewL(buffLength); |
|
252 newFullPath->Des().Format( |
|
253 KFullPathFormat, |
|
254 newPath, |
|
255 &(iImapSettings.PathSeparator()), |
|
256 iNewLeafName); |
|
257 CleanupStack::PopAndDestroy(newPath); |
|
258 CleanupStack::PushL(newFullPath); |
|
259 } |
|
260 else |
|
261 { |
|
262 // folder is at root level |
|
263 CleanupStack::PopAndDestroy(newPath); |
|
264 newFullPath = iNewLeafName->AllocLC(); |
|
265 } |
|
266 |
|
267 // Get the full path name for the folder's current name. |
|
268 TDesC& oldPath = folder->FullFolderPathL(); |
|
269 |
|
270 // issue the command to the remote server. |
|
271 iSession->RenameL(iStatus, oldPath, newFullPath->Des()); |
|
272 SetActive(); |
|
273 CleanupStack::PopAndDestroy(newFullPath); |
|
274 iProgressState = TImap4GenericProgress::EBusy; |
|
275 iNextStep = EProcessRenameResponse; |
|
276 } |
|
277 |
|
278 /* |
|
279 Special handling of rename is required for the INBOX as this has special |
|
280 behaviour as specified in RFC 3501: |
|
281 Renaming INBOX is permitted, and has special behavior. It moves all |
|
282 messages in INBOX to a new mailbox with the given name, leaving INBOX empty. |
|
283 |
|
284 */ |
|
285 void CImapCompoundRename::RenameInboxL() |
|
286 { |
|
287 // create a new folder with the new name, a copy of the inbox |
|
288 SetEntryL(iFolderId); |
|
289 |
|
290 // Selection of messages |
|
291 delete iMsgselection; |
|
292 iMsgselection = NULL; |
|
293 CMsvEntrySelection* iMsgselection = new (ELeave) CMsvEntrySelection; |
|
294 |
|
295 // Children of original inbox |
|
296 CMsvEntrySelection* children=new (ELeave) CMsvEntrySelection; |
|
297 CleanupStack::PushL(children); |
|
298 |
|
299 User::LeaveIfError(iServerEntry.GetChildren(*children)); |
|
300 |
|
301 // create the new mailbox |
|
302 TMsvEmailEntry entry = iServerEntry.Entry(); |
|
303 SetEntryL(entry.Parent()); |
|
304 entry.iDetails.Set(*iNewLeafName); |
|
305 iServerEntry.CreateEntry(entry); |
|
306 iNewFolderId = entry.Id(); |
|
307 |
|
308 // create a reduced children list - just the messages in Inbox |
|
309 TInt count = children->Count(); |
|
310 for (TInt child = 0; child < count; ++child) |
|
311 { |
|
312 SetEntryL((*children)[child]); |
|
313 TMsvEntry entry = iServerEntry.Entry(); |
|
314 if (entry.iType==KUidMsvMessageEntry) |
|
315 { |
|
316 iMsgselection->AppendL(entry.Id()); |
|
317 } |
|
318 } |
|
319 CleanupStack::PopAndDestroy(children); |
|
320 |
|
321 // reset the server entry context |
|
322 SetEntryL(iFolderId); |
|
323 if (iMsgselection->Count()>0) |
|
324 { |
|
325 // move children messages to the newly created folder |
|
326 iServerEntry.MoveEntriesL(*iMsgselection, iNewFolderId, iStatus); |
|
327 SetActive(); |
|
328 } |
|
329 else |
|
330 { |
|
331 // inbox was empty - nothing to move |
|
332 CompleteSelf(); |
|
333 } |
|
334 } |
|
335 |
|
336 |
|
337 /** |
|
338 Populates the compound progress object with progress information for the |
|
339 rename compound operation. |
|
340 |
|
341 @param aCompoundProgress the compound progress object to be populated. |
|
342 */ |
|
343 void CImapCompoundRename::Progress(TImap4CompoundProgress& aCompoundProgress) |
|
344 { |
|
345 // rename does not set iOperation, it just sets iState to EBusy |
|
346 // when doing the renaming |
|
347 aCompoundProgress.iGenericProgress.iState = iProgressState; |
|
348 |
|
349 // Put error into progress buffer |
|
350 if( aCompoundProgress.iGenericProgress.iErrorCode == KErrNone ) |
|
351 { |
|
352 aCompoundProgress.iGenericProgress.iErrorCode = iProgressErrorCode; |
|
353 } |
|
354 } |
|
355 |
|
356 /** |
|
357 Handles server error responses according to current step |
|
358 |
|
359 @return TInt error code for completion (if error fatal) |
|
360 */ |
|
361 TInt CImapCompoundRename::ProcessNegativeServerResponse() |
|
362 { |
|
363 switch(iCurrentStep) |
|
364 { |
|
365 case ERename: |
|
366 { |
|
367 iProgressErrorCode = KErrNotSupported; |
|
368 iNextStep = EFinished; |
|
369 break; |
|
370 } |
|
371 case ESyncSubscriptions: |
|
372 { |
|
373 // subscribe failing is not serious - silently ignore it |
|
374 iNextStep = EFinished; |
|
375 break; |
|
376 } |
|
377 case EFinished: |
|
378 case ESuspendedForMigrate: |
|
379 default: |
|
380 { |
|
381 // positive error code not expected, |
|
382 // self-completed states or no outstanding request. |
|
383 TImapServerPanic::ImapPanic(TImapServerPanic::ERenameCompoundUnexpectedState); |
|
384 break; |
|
385 } |
|
386 } |
|
387 return KErrNone; |
|
388 } |
|
389 |
|
390 |
|
391 /** |
|
392 Resumes the operation following a migration. |
|
393 */ |
|
394 void CImapCompoundRename::ResumeOperationL(TRequestStatus& aStatus, CImapSession& aSession) |
|
395 { |
|
396 iSession = &aSession; |
|
397 __LOG_TEXT(iSession->LogId(), "CImapCompoundRename::Resuming"); |
|
398 __ASSERT_DEBUG(iCurrentStep==ESuspendedForMigrate, TImapServerPanic::ImapPanic(TImapServerPanic::ERenameCompoundUnexpectedState)); |
|
399 iStopForMigrate = EFalse; |
|
400 |
|
401 switch (iNextStep) |
|
402 { |
|
403 case ERename: |
|
404 case ESyncSubscriptions: |
|
405 { |
|
406 // restart the state machine. |
|
407 CompleteSelf(); |
|
408 break; |
|
409 } |
|
410 case EProcessRenameResponse: |
|
411 { |
|
412 // resume move of messages to renamed inbox.. |
|
413 // 1st, renew selection of children of original inbox |
|
414 SetEntryL(iFolderId); |
|
415 CMsvEntrySelection* children=new (ELeave) CMsvEntrySelection; |
|
416 CleanupStack::PushL(children); |
|
417 User::LeaveIfError(iServerEntry.GetChildren(*children)); |
|
418 |
|
419 // 2nd, create a reduced list - just the messages entries |
|
420 delete iMsgselection; |
|
421 iMsgselection = NULL; |
|
422 CMsvEntrySelection* iMsgselection = new (ELeave) CMsvEntrySelection; |
|
423 TInt count = children->Count(); |
|
424 for (TInt child = 0; child < count; ++child) |
|
425 { |
|
426 SetEntryL((*children)[child]); |
|
427 TMsvEntry entry = iServerEntry.Entry(); |
|
428 if (entry.iType==KUidMsvMessageEntry) |
|
429 { |
|
430 iMsgselection->AppendL(entry.Id()); |
|
431 } |
|
432 } |
|
433 CleanupStack::PopAndDestroy(children); |
|
434 |
|
435 // reset the server entry context |
|
436 SetEntryL(iFolderId); |
|
437 if (iMsgselection->Count()>0) |
|
438 { |
|
439 // move children messages to the newly created folder |
|
440 iServerEntry.MoveEntriesL(*iMsgselection, iNewFolderId, iStatus); |
|
441 SetActive(); |
|
442 } |
|
443 else |
|
444 { |
|
445 // inbox was empty - nothing left to move |
|
446 CompleteSelf(); |
|
447 } |
|
448 break; |
|
449 } |
|
450 case EFinished: |
|
451 default: |
|
452 { |
|
453 __ASSERT_DEBUG(EFalse, TImapServerPanic::ImapPanic(TImapServerPanic::ERenameCompoundUnexpectedState)); |
|
454 // abandon the compound operation |
|
455 iNextStep=EFinished; |
|
456 CompleteSelf(); |
|
457 break; |
|
458 } |
|
459 } // end switch (iNextStep) |
|
460 Queue(aStatus); |
|
461 } |