email/imap4mtm/imapsession/src/cimaplistbase.cpp
changeset 0 72b543305e3a
equal deleted inserted replaced
-1:000000000000 0:72b543305e3a
       
     1 // Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies).
       
     2 // All rights reserved.
       
     3 // This component and the accompanying materials are made available
       
     4 // under the terms of "Eclipse Public License v1.0"
       
     5 // which accompanies this distribution, and is available
       
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     7 //
       
     8 // Initial Contributors:
       
     9 // Nokia Corporation - initial contribution.
       
    10 //
       
    11 // Contributors:
       
    12 //
       
    13 // Description:
       
    14 //
       
    15 
       
    16 
       
    17 #include "cimaplistbase.h"
       
    18 #include "moutputstream.h"
       
    19 #include "cimapsessionconsts.h"
       
    20 #include "cimapatomparser.h"
       
    21 #include "cimapatomwalker.h"
       
    22 #include "cimaputils.h"
       
    23 #include "cimapcharconv.h"
       
    24 #include "cimapsettings.h"
       
    25 
       
    26 // Inbox mailbox name
       
    27 _LIT16(KIMAP_INBOX, "INBOX");
       
    28 
       
    29 const TUint KNullTerminator = '\0';
       
    30 
       
    31 void CImapListBase::ConstructL(const TDesC& aReferenceName, const TDesC& aMailboxName)
       
    32 	{
       
    33 	iReferenceName = EncodeMailboxNameForSendL(aReferenceName);
       
    34 	iMailboxName = EncodeMailboxNameForSendL(aMailboxName);
       
    35 	iAtomWalker = CImapAtomWalker::NewL(iLogId);
       
    36 	}
       
    37 
       
    38 CImapListBase::CImapListBase(CImapFolderInfo* aSelectedFolderData, TInt aLogId, RArrayImapListFolderInfo& aFolderList, const CImapSettings& aImapSettings, const TDesC8& aSendMessageFormat, const TDesC8& aResponseIdentifier)
       
    39 	: CImapCommandEx(aSelectedFolderData, aLogId)
       
    40 	, iFolderList(aFolderList)
       
    41 	, iSendMessageFormat(aSendMessageFormat)
       
    42 	, iResponseIdentifier(aResponseIdentifier)
       
    43 	, iListParseState(EWaitingForResponse)
       
    44 	, iImapSettings(aImapSettings)
       
    45 	{		
       
    46 	}
       
    47 	
       
    48 CImapListBase::~CImapListBase()
       
    49 	{
       
    50 	delete iAtomWalker;
       
    51 	delete iAtomParser;
       
    52 	delete iReferenceName;
       
    53 	delete iMailboxName;
       
    54 	}
       
    55 
       
    56 /** 
       
    57 Formats and sends the IMAP LIST command.
       
    58 @param aTagId Command sequence id which will be send along with the IMAP command.
       
    59 @param aStream A wrapper for the outbound stream of a connected socket, using which 
       
    60 the command will be send to the server
       
    61 */
       
    62 void CImapListBase::SendMessageL(TInt aTagId, MOutputStream& aStream)
       
    63 	{
       
    64 	iTagId = aTagId;
       
    65 	
       
    66 	TInt bufLength = iSendMessageFormat.Length();	
       
    67 	bufLength += TagLength(aTagId);
       
    68 	bufLength += iReferenceName->Length();
       
    69 	bufLength += iMailboxName->Length();
       
    70 	
       
    71 	__ASSERT_DEBUG(iOutputBuffer==NULL, TImapServerPanic::ImapPanic(TImapServerPanic::ECommandOutputBufferNotNull));
       
    72 	iOutputBuffer = HBufC8::NewL(bufLength);	
       
    73 	iOutputBuffer->Des().Format(iSendMessageFormat, aTagId, iReferenceName, iMailboxName);
       
    74 	
       
    75 	//send the command to the server
       
    76 	aStream.SendDataReq(*iOutputBuffer);	
       
    77 	}
       
    78 
       
    79 /**
       
    80 If this is a LIST/LSUB response, then begin parsing it.
       
    81 Parsing of LIST/LSUB responses is a two phase process.
       
    82 First, build an atom tree using the atom parser.  
       
    83 The atom parser determines whether extra literal data is expected on the stream
       
    84 Once the atom parser has enough data (either here or in ParseLineFollowingLiteralL()) 
       
    85 then the second phase is to process the atom tree by calling ProcessMailboxListL().
       
    86 Otherwise return not recognised
       
    87 @return ENotRecognised (if not a LIST/LSUB response)
       
    88 		ECompleteUntagged (if a LIST/LSUB response with no literal data)
       
    89 		EResponseIncomplete (if a LIST/LSUB response wiht literal data expected)		
       
    90 */
       
    91 CImapCommand::TParseBlockResult CImapListBase::ParseUntaggedResponseL()
       
    92 	{
       
    93 	TParseBlockResult result = ENotRecognised;
       
    94 		
       
    95 	// Is this a list response?
       
    96 	TPtrC8 part = GetNextPart(); // returns KNullDesC8 if there is no part available
       
    97 				
       
    98 	if(part.CompareF(iResponseIdentifier) == 0)
       
    99 		{
       
   100 		__ASSERT_DEBUG(iAtomParser == NULL, TImapServerPanic::ImapPanic(TImapServerPanic::EListBaseAtomParserAlreadyExists));
       
   101 		iAtomParser = CImapAtomParser::NewL(ETrue, iLogId);
       
   102 		
       
   103 		TBool wantMore = iAtomParser->ProcessLineL(iUnparsedData);
       
   104 		if (wantMore)
       
   105 			{
       
   106 			result = EResponseIncomplete;
       
   107 			}
       
   108 		else
       
   109 			{
       
   110 			// All the data was received in the fist line.  Process it now.
       
   111 			ProcessMailboxListL();
       
   112 			result = ECompleteUntagged;
       
   113 			}
       
   114 		}
       
   115 	else
       
   116 		{
       
   117 		result = ENotRecognised;
       
   118 		}			
       
   119    		
       
   120 	return result;
       
   121 	}
       
   122 
       
   123 /**
       
   124 Delegates parsing of incoming literal data to the atom parser.
       
   125 */
       
   126 void CImapListBase::ParseLiteralBlockL()
       
   127 	{
       
   128 	__ASSERT_DEBUG(iAtomParser != NULL, TImapServerPanic::ImapPanic(TImapServerPanic::EListBaseAtomParserIsNull));
       
   129 	iAtomParser->ProcessLiteralBlockL(iUnparsedData);
       
   130 	}
       
   131 	
       
   132 /**
       
   133 Delegates parsing of the incoming line of data to the atom parser.
       
   134 If the atom parser has all the data it needs, then processing of the mailbox data
       
   135 can begin.
       
   136 @return whether further literal data is expected
       
   137 */
       
   138 TBool CImapListBase::ParseLineFollowingLiteralL()
       
   139 	{
       
   140 	__ASSERT_DEBUG(iAtomParser != NULL, TImapServerPanic::ImapPanic(TImapServerPanic::EListBaseAtomParserIsNull));
       
   141 	
       
   142 	TBool wantMoreData = iAtomParser->ProcessLineL(iUnparsedData);
       
   143  	if(!wantMoreData)
       
   144  		{
       
   145  		// All the data for this LIST/LSUB response has been recieved.  Process it now.
       
   146 		ProcessMailboxListL();
       
   147 		}
       
   148 		
       
   149 	return wantMoreData;
       
   150 	}
       
   151 
       
   152 /**
       
   153 Having received all the data for a LIST/LSUB response, this method processes the atom tree
       
   154 representation of the response.
       
   155 A CImapListFolderInfo object representing the response is populated and then inserted 
       
   156 in alphabetic order (based on the mailbox name) into the output array of CImapListFolderInfo objects
       
   157 that was passed into this object on construction.
       
   158 */
       
   159 void CImapListBase::ProcessMailboxListL()
       
   160 	{
       
   161 	// mailbox-list = "(" [mbx-list-flags] ")" SP (DQUOTE QUOTED-CHAR DQUOTE / nil) SP mailbox
       
   162 	//
       
   163 	// mailbox = "INBOX" / astring
       
   164 
       
   165 	__ASSERT_DEBUG(iAtomParser != NULL, TImapServerPanic::ImapPanic(TImapServerPanic::EListBaseAtomParserIsNull));
       
   166 	
       
   167 	iAtomWalker->SetRootL(iAtomParser->RootAtom());
       
   168 	iAtomWalker->WalkDownL();
       
   169 	
       
   170 	CImapListFolderInfo* folderInfo = CImapListFolderInfo::NewL();
       
   171 	CleanupStack::PushL(folderInfo);
       
   172 		
       
   173 	ProcessMailboxListFlagsL(*folderInfo);
       
   174 	ProcessHeirarchySeparatorL(*folderInfo);	
       
   175 	ProcessMailboxL(*folderInfo);
       
   176 
       
   177 	TLinearOrder<CImapListFolderInfo> sorter(CImapListFolderInfo::CompareByFolderName);
       
   178 	iFolderList.InsertInOrderL(folderInfo, sorter);
       
   179 
       
   180 	// Finished with the atom tree for this response.
       
   181 	delete iAtomParser;
       
   182 	iAtomParser = NULL;
       
   183 
       
   184 	CleanupStack::Pop(folderInfo);
       
   185 	}
       
   186 
       
   187 /**
       
   188 Process the mbx-list-flags portion of the LIST/LSUB response
       
   189 @param aFolderInfo recieves the flags data
       
   190 */
       
   191 void CImapListBase::ProcessMailboxListFlagsL(CImapListFolderInfo& aFolderInfo)
       
   192 	{
       
   193 	// mbx-list-flags =	*(mbx-list-oflag SP) mbx-list-sflag *(SP mbx-list-oflag) /
       
   194 	//					mbx-list-oflag *(SP mbx-list-oflag)
       
   195 	//
       
   196 	// mbx-list-oflag = "\Noinferiors" / flag-extension
       
   197 	//		; Other flags; multiple possible per LIST response
       
   198 	//
       
   199 	// flag-extension = "\" atom
       
   200 	//
       
   201 	// mbx-list-sflag = "\Noselect" / "\Marked" / "\Unmarked"
       
   202 	//		; Selectability flags; only one per LIST response
       
   203 
       
   204 	// mbx-list-flags cannot contain brackets, so it is safe to search for its delimiting brackets
       
   205 
       
   206 	// There may be no flags, so check for this first
       
   207 	if (iAtomWalker->PeekDown())
       
   208 		{
       
   209 		iAtomWalker->WalkDownL();
       
   210 		do
       
   211 			{
       
   212 			// mbx-list-oflag = "\Noinferiors" / flag-extension
       
   213 			if (iAtomWalker->CurrentMatch(KImapTxtNoInferiors))
       
   214 				{
       
   215 				aFolderInfo.SetFlag(CImapListFolderInfo::ENoinferiors, ETrue);
       
   216 				}
       
   217 		
       
   218 			// mbx-list-sflag = "\Noselect" / "\Marked" / "\Unmarked"
       
   219 			// Server should only send one of these (but must send one)
       
   220 			else if (iAtomWalker->CurrentMatch(KImapTxtNoSelect))
       
   221 				{
       
   222 				aFolderInfo.SetFlag(CImapListFolderInfo::ENoselect, ETrue);
       
   223 				}
       
   224 			else if (iAtomWalker->CurrentMatch(KImapTxtMarked))
       
   225 				{
       
   226 				aFolderInfo.SetFlag(CImapListFolderInfo::EMarked, ETrue);
       
   227 				}
       
   228 			else if (iAtomWalker->CurrentMatch(KImapTxtUnmarked))
       
   229 				{
       
   230 				aFolderInfo.SetFlag(CImapListFolderInfo::EUnmarked, ETrue);
       
   231 				}
       
   232 		
       
   233 			} while (iAtomWalker->WalkAcrossL(EFalse));
       
   234 	
       
   235 		iAtomWalker->WalkUpL();
       
   236 		}
       
   237 	}
       
   238 
       
   239 /**
       
   240 Processes the heirarchy separator portion of the LIST/LSUB response.
       
   241 @param aFolderInfo receives the heirarchy character
       
   242 */
       
   243 void CImapListBase::ProcessHeirarchySeparatorL(CImapListFolderInfo& aFolderInfo)
       
   244 	{
       
   245 	// now look for heirarchy separator
       
   246 	// (DQUOTE QUOTED-CHAR DQUOTE / nil)
       
   247 	
       
   248 	iAtomWalker->WalkAcrossL(ETrue);
       
   249 	TPtrC8 heirarchySeparator = iAtomWalker->CurrentDes(ETrue); // this is effectively an nstring
       
   250 		
       
   251 	if (heirarchySeparator.Length() == 1)
       
   252 		{
       
   253 		// this is the most likely scenario so start here.
       
   254 		aFolderInfo.iHierarchySeperator = heirarchySeparator[0];
       
   255 		}
       
   256 	else if (heirarchySeparator.Length() == 0)
       
   257 		{
       
   258 		// The separator was NIL
       
   259 		aFolderInfo.iHierarchySeperator = KNullTerminator;
       
   260 		}
       
   261 	else
       
   262 		{
       
   263 		CorruptDataL(); // we were returned a string that was too long for a heirarchySeparator.
       
   264 		}
       
   265 	}
       
   266 
       
   267 /**
       
   268 Extracts the mailbox name from the LIST/LSUB response.
       
   269 @param aFolderInfo receives the mailbox name
       
   270 */
       
   271 void CImapListBase::ProcessMailboxL(CImapListFolderInfo& aFolderInfo)
       
   272 	{
       
   273 	iAtomWalker->WalkAcrossL(ETrue);
       
   274 	TPtrC8 mailboxNameUtf7 = iAtomWalker->CurrentDes(EFalse); // mailbox not an nstring.
       
   275 	
       
   276 	// Convert into ImapUtf7.
       
   277 	HBufC16* mailboxNameUnicode = CImapUtils::GetRef().Charconv().ConvertToUnicodeFromImapUtf7L(mailboxNameUtf7);
       
   278 	TPtr16 namePtr = mailboxNameUnicode->Des();
       
   279 
       
   280 	// Strip leading hierarchy separators
       
   281 	TPtrC16 stripName(namePtr);
       
   282 	while ((stripName.Length() > 0) && (stripName.Locate(aFolderInfo.iHierarchySeperator) == 0))
       
   283 		{
       
   284 		stripName.Set(stripName.Mid(1));
       
   285 		}
       
   286 
       
   287 	TInt numFound = namePtr.Length() - stripName.Length();
       
   288 	if (numFound > 0)
       
   289 		{
       
   290 		namePtr.Delete(0, numFound);
       
   291 		}
       
   292 
       
   293 	// Strip trailing hierarchy separators
       
   294 	while ((namePtr.Length() > 0) && (namePtr.LocateReverse(aFolderInfo.iHierarchySeperator) == namePtr.Length() - 1))
       
   295 		{
       
   296 		namePtr.Delete(namePtr.Length() - 1, 1);
       
   297 		}
       
   298 
       
   299 	// If the first part of the path name is the inbox, make sure we use
       
   300 	// the uppercase version INBOX. This allows for case sensitive searches
       
   301 	// to be performed later.
       
   302 	TInt inboxLength = KIMAP_INBOX().Length();
       
   303 
       
   304 	// Check that the input folder name is long enough to contain INBOX and
       
   305 	// the hierarchy separator.
       
   306 	if (namePtr.Length() >= inboxLength + 1)
       
   307 		{
       
   308 		TPtrC16 inboxPart = namePtr.Left(inboxLength);
       
   309 
       
   310 		if (inboxPart.CompareF(KIMAP_INBOX) == 0 && inboxPart.Compare(KIMAP_INBOX) != 0)
       
   311 			{
       
   312 			TPtrC16 separatorPart = namePtr.Mid(inboxLength, 1);
       
   313 
       
   314 			if (separatorPart.Compare(iImapSettings.PathSeparator()) == 0)
       
   315 				{
       
   316 				namePtr.Replace(0, inboxLength, KIMAP_INBOX);
       
   317 				}
       
   318 			}
       
   319 		}
       
   320 
       
   321 	aFolderInfo.SetFolderNameL(mailboxNameUnicode);
       
   322 	}