email/pop3andsmtpmtm/imapservermtm/src/IMAPSESS.CPP
changeset 25 84d9eb65b26f
equal deleted inserted replaced
23:238255e8b033 25:84d9eb65b26f
       
     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"),&param,&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 	}