email/imap4mtm/imapsession/src/cimapselect.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 "cimapselect.h"
       
    18 #include "moutputstream.h"
       
    19 #include "cimapfolderinfo.h"
       
    20 #include "cimapsessionconsts.h"
       
    21 #include "cimaplogger.h"
       
    22 
       
    23 _LIT8(KTxtSelectFormat, "%d SELECT %S\r\n");
       
    24 const TInt KSelectFormatEscapeCharCount = 4; // for "%d" and %S
       
    25 _LIT8(KReadWriteTxt,"READ-WRITE");
       
    26 _LIT8(KReadOnlyTxt,"READ-ONLY");
       
    27 
       
    28 /**
       
    29 The factory constructor. Part of two phased construction.
       
    30 */
       
    31 CImapSelect* CImapSelect::NewL(CImapFolderInfo* aSelectedFolderData, TInt aLogId)
       
    32 	{
       
    33 	CImapSelect* self = new(ELeave) CImapSelect(aSelectedFolderData, aLogId);
       
    34 	CleanupStack::PushL(self);
       
    35 	self->ConstructL();
       
    36 	CleanupStack::Pop(self);
       
    37 	return self;
       
    38 	}
       
    39 
       
    40 /**
       
    41 Second phase constructor.
       
    42 */
       
    43 void CImapSelect::ConstructL()
       
    44 	{
       
    45 	}
       
    46 /**
       
    47 Constructor.
       
    48 */
       
    49 CImapSelect::CImapSelect(CImapFolderInfo* aSelectedFolderData, TInt aLogId)
       
    50 	: CImapCommandEx(aSelectedFolderData, aLogId)
       
    51 	{
       
    52 	__ASSERT_DEBUG(aSelectedFolderData != NULL, TImapServerPanic::ImapPanic(TImapServerPanic::ESelectFolderDataIsNull));
       
    53 	__ASSERT_DEBUG(aSelectedFolderData->Name().Length() > 0, TImapServerPanic::ImapPanic(TImapServerPanic::ESelectMailboxNameIsEmpty));
       
    54 	
       
    55 	// Default to "read-write"
       
    56 	aSelectedFolderData->SetReadWrite(ETrue);
       
    57 	}
       
    58 
       
    59 /**
       
    60 Destructor.
       
    61 */
       
    62 CImapSelect::~CImapSelect()
       
    63 	{
       
    64 	}
       
    65 
       
    66 /**
       
    67 Responsible for sending the IMAP select command to the remote server to
       
    68 perform the desired action the IMAP client wishes. The data will be sent
       
    69 to the remote server on the output stream provided.
       
    70 It is assumed the output stream has already been set up and ready to use.
       
    71 
       
    72 @param aTagId	Used to generate the IMAP tag that identifies the message.
       
    73 @param aStream	The output stream on which the message will be sent.
       
    74 */
       
    75 void CImapSelect::SendMessageL(TInt aTagId, MOutputStream& aStream)
       
    76 	{
       
    77 	HBufC8* mailbox = EncodeMailboxNameForSendL(iSelectedFolderData->Name());
       
    78 	CleanupStack::PushL(mailbox);
       
    79 	
       
    80 	iTagId = aTagId;
       
    81 	TInt bufferLength = KTxtSelectFormat().Length() - KSelectFormatEscapeCharCount + TagLength(aTagId) + mailbox->Length();
       
    82 	
       
    83 	__ASSERT_DEBUG(iOutputBuffer==NULL, TImapServerPanic::ImapPanic(TImapServerPanic::ECommandOutputBufferNotNull));
       
    84 	iOutputBuffer = HBufC8::NewL(bufferLength);
       
    85 	iOutputBuffer->Des().Format(KTxtSelectFormat, iTagId, mailbox);
       
    86 	
       
    87 	CleanupStack::PopAndDestroy(mailbox);
       
    88 	
       
    89 	// Send the data on the output stream
       
    90 	aStream.SendDataReq(*iOutputBuffer);
       
    91 	}
       
    92 
       
    93 /**
       
    94 Parses the tagged response, looking for response text data.
       
    95 Records the response status code (OK/NO/BAD)
       
    96 And checks that the incoming tag id matches the sent tag id.
       
    97 @param aTagId The value of the incoming tag.
       
    98 @return ETrue - indicating that the command is completed by this tagged response.
       
    99 */
       
   100 TBool CImapSelect::ParseTaggedResponseL(TInt aTagId)
       
   101 	{
       
   102 	// Check the tag id and fetche the response code
       
   103 	BaseParseTaggedResponseL(aTagId);
       
   104 
       
   105 	// Check the response code
       
   106 	if (iResponseCode == EImapResponseNone)
       
   107 		{
       
   108 		// Was expecting one of OK/NO/BAD, but didn't get it.  This is a parse error.
       
   109 		CorruptDataL();
       
   110 		}
       
   111 
       
   112 	if (iResponseCode == EImapResponseOk)
       
   113 		{
       
   114 		// [READ-ONLY] or [READ-WRITE] are always sent in the tagged OK response (see p33 of RFC3501)
       
   115 		// The server SHOULD or MUST send this information depending on circumstances.
       
   116 		// This means it is possible that the server might not send this information.
       
   117 		// So the default value is set in the constructor of the Select and Examine command object.
       
   118 		
       
   119 		TPtrC8 responseTextCode = GetResponseTextCodeL(); //  e.g. [READ-ONLY]
       
   120 		
       
   121 		if(responseTextCode.CompareF(KReadWriteTxt) == 0)
       
   122 			{
       
   123 			iSelectedFolderData->SetReadWrite(ETrue);
       
   124 			}
       
   125 		else if(responseTextCode.CompareF(KReadOnlyTxt) == 0)
       
   126 			{
       
   127 			iSelectedFolderData->SetReadWrite(EFalse);
       
   128 			}
       
   129 		else if (responseTextCode.Length() > 0)
       
   130 			{
       
   131 			// Not expecting alternative response code here.
       
   132 			__LOG_TEXT(iLogId, "CImapSelect - ignoring unknown response text code");
       
   133 			}
       
   134 		}
       
   135 		
       
   136 	return ETrue;
       
   137 	}
       
   138 	
       
   139 CImapCommand::TParseBlockResult CImapSelect::ParseUntaggedResponseL()
       
   140 	{
       
   141 	TParseBlockResult result = ENotRecognised;
       
   142 	
       
   143 	TResponseCode untaggedResponseCode = GetResponseStateCode();
       
   144 	
       
   145 	if (untaggedResponseCode == EImapResponseOk)
       
   146 		{
       
   147 		ParseUntaggedOkL();
       
   148 		result = ECompleteUntagged;
       
   149 		}
       
   150 	else if (untaggedResponseCode == EImapResponseNone)
       
   151 		{
       
   152 		TPtrC8 nextPart = GetNextPart();
       
   153 		if (nextPart.CompareF(KImapTxtFlags) == 0)
       
   154 			{
       
   155 			TPtrC8 remainder = Remainder();
       
   156 			ParseFlagsL(remainder);
       
   157 			result = ECompleteUntagged;
       
   158 			}
       
   159 		else
       
   160 			{
       
   161 			result = ENotRecognised;
       
   162 			}
       
   163 		}
       
   164 	
       
   165 	// Untagged NO or untagged BAD are ignored (i.e. ENotRecognised)
       
   166 	// There is no need to treat these as errors.
       
   167 	//
       
   168 	// Servers that support IMAP Extensions may return these responses even if we 
       
   169 	// have not asked for them.
       
   170 	// For example, RFC 4315 defines the "UIDPLUS" extension.
       
   171 	// This allows a server to return...
       
   172 	//
       
   173 	//     * NO [UIDNOTSTICKY] Non-persistent UIDs
       
   174 	//
       
   175 	// ... as one of the untagged responses to a SELECT command.
       
   176 	// 
       
   177 	// We don't currently support UIDPLUS, so we can safely ignore this response.
       
   178 	// But if we did support UIDPLUS, then the untagged NO would not need to be 
       
   179 	// treated as an error (as is the case with tagged NO), but would merely cause
       
   180 	// the code to store a flag to record that certain features of UIDPLUS are 
       
   181 	// not available on this mailbox.
       
   182 		
       
   183 	return result;
       
   184 	}
       
   185 	
       
   186 /**
       
   187 Looks for and parses the response text code that should be found
       
   188 in an untagged OK response.
       
   189 */
       
   190 void CImapSelect::ParseUntaggedOkL()
       
   191 	{
       
   192 	TPtrC8 responseTextCode = GetResponseTextCodeL(); //  e.g. [UIDNEXT 1234]
       
   193 
       
   194 	if (responseTextCode.Length() > 1)
       
   195 		{
       
   196 		TInt offset = responseTextCode.Locate(' ');
       
   197 		if (offset > 0)
       
   198 			{
       
   199 			TPtrC8 textCode(responseTextCode.Left(offset));
       
   200 			
       
   201 			responseTextCode.Set(responseTextCode.Mid(offset + 1));
       
   202 			
       
   203 		 	if (textCode == KImapTxtUidNext) 
       
   204 				{
       
   205 				TLex8 desToInt(responseTextCode);
       
   206 				TInt uidNext=0;
       
   207 				TInt err = desToInt.Val(uidNext);
       
   208 				if(err != KErrNone)
       
   209 					{
       
   210 					CorruptDataL();
       
   211 					}
       
   212 				iSelectedFolderData->SetUidNext(uidNext);
       
   213 				}
       
   214 			else if (textCode == KImapTxtUnseen)
       
   215 				{
       
   216 				TLex8 desToInt(responseTextCode);
       
   217 				TInt unseen=0;
       
   218 				TInt err = desToInt.Val(unseen);
       
   219 				if(err != KErrNone)
       
   220 					{
       
   221 					CorruptDataL();
       
   222 					}
       
   223 				iSelectedFolderData->SetUnseen(unseen);
       
   224 				}
       
   225 				
       
   226 			else if (textCode == KImapTxtUidValidity)
       
   227 				{
       
   228 				TLex8 desToInt(responseTextCode);
       
   229 				TUint uidValidity=0;
       
   230 				TInt err = desToInt.Val(uidValidity);
       
   231 				if(err != KErrNone)
       
   232 					{
       
   233 					CorruptDataL();
       
   234 					}
       
   235 				iSelectedFolderData->SetUidValidity(uidValidity);
       
   236 				}
       
   237 			else
       
   238 				{
       
   239 				__LOG_TEXT(iLogId, "CImapSelect::ParseUntaggedOkL() - ignoring response text code");
       
   240 				}
       
   241 			}
       
   242 		else
       
   243 			{
       
   244 			__LOG_TEXT(iLogId, "CImapSelect::ParseUntaggedOkL() - ignoring response text code (no spaces)");
       
   245 			}			
       
   246 		}
       
   247 	else
       
   248 		{
       
   249 		// was expecting a response text code after the untagged OK
       
   250 		CorruptDataL();	
       
   251 		}
       
   252 	}
       
   253 	
       
   254 void CImapSelect::ParseFlagsL(TDesC8& aFlags)
       
   255 	{
       
   256 	// formal definition of the FLAGS response is
       
   257 	//
       
   258 	// mailbox-data = "FLAGS" SP flag-list
       
   259 	//
       
   260 	// flag-list = "(" [flag *(SP flag)] ")"
       
   261 	//
       
   262 	// flag = "\Answered" / "\Flagged" / "\Deleted" / "\Seen" / "\Draft" / flag-keyword / flag-extension
       
   263 	//
       
   264 	// flag-extension = "\" atom
       
   265 	// flag-keyword = atom
       
   266 	//
       
   267 	
       
   268 	RDesParts flags;
       
   269 	CleanupClosePushL(flags);
       
   270 	TPtrC8 flagsString(aFlags);
       
   271 	
       
   272 	TInt start = aFlags.Locate('(');
       
   273 	TInt end = aFlags.Locate(')');
       
   274 	
       
   275 	if(start == KErrNotFound || end == KErrNotFound)
       
   276 		{
       
   277 		CorruptDataL();	
       
   278 		}
       
   279 	else if (start > end)
       
   280 		{
       
   281 		CorruptDataL();
       
   282 		}
       
   283 	else
       
   284 		{
       
   285 		// lose the brackets
       
   286 		flagsString.Set(aFlags.Mid(start+1, end-start-1));
       
   287 		}
       
   288 	
       
   289 	GetDelimitedPartsL(' ', flagsString, flags);
       
   290 	TInt flagsCount = flags.Count();
       
   291 	for (TInt i = 0; i < flagsCount; ++i)
       
   292 		{
       
   293 		TPtrC8 flag = flags[i];
       
   294 
       
   295 		if(flag.CompareF(KImapTxtFlagDeleted) == 0)
       
   296 			{
       
   297 			iSelectedFolderData->SetFlag(CImapFolderInfo::EDeleted, ETrue);				
       
   298 			}
       
   299 		else if (flag.CompareF(KImapTxtFlagSeen) == 0)
       
   300 			{
       
   301 			iSelectedFolderData->SetFlag(CImapFolderInfo::ESeen, ETrue);	
       
   302 			}
       
   303 		else if(flag.CompareF(KImapTxtFlagFlagged) == 0)
       
   304 			{
       
   305 			iSelectedFolderData->SetFlag(CImapFolderInfo::EFlagged, ETrue);
       
   306 			}
       
   307 		else if(flag.CompareF(KImapTxtFlagAnswered) == 0)
       
   308 			{
       
   309 			iSelectedFolderData->SetFlag(CImapFolderInfo::EAnswered, ETrue);	
       
   310 			}
       
   311 		else if(flag.CompareF(KImapTxtFlagDraft) == 0)
       
   312 			{
       
   313 			iSelectedFolderData->SetFlag(CImapFolderInfo::EDraft, ETrue);	
       
   314 			}
       
   315 		}
       
   316 
       
   317 	CleanupStack::PopAndDestroy(&flags);
       
   318 	}