diff -r 238255e8b033 -r 84d9eb65b26f email/pop3andsmtpmtm/imapservermtm/src/IMAPIO.CPP --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/email/pop3andsmtpmtm/imapservermtm/src/IMAPIO.CPP Mon May 03 12:29:07 2010 +0300 @@ -0,0 +1,1128 @@ +// Copyright (c) 1998-2009 Nokia Corporation and/or its subsidiary(-ies). +// All rights reserved. +// This component and the accompanying materials are made available +// under the terms of "Eclipse Public License v1.0" +// which accompanies this distribution, and is available +// at the URL "http://www.eclipse.org/legal/epl-v10.html". +// +// Initial Contributors: +// Nokia Corporation - initial contribution. +// +// Contributors: +// +// Description: +// This deals with connection to the server, sending commands, and parsing the +// returned server data. The data returned is buffered in iBuffer: this is +// simply back-to-back data, with a tree which describes the structure +// containing TPtrC's which identify the data belonging to each atom. +// This class owns both the buffer and the tree, and deals with destroying them +// as necessary. The client can request the base of the tree for it to parse +// when a line has been received. +// 980708 - modified to store flavour of ([ in parent atom +// 980802 - partial read behavior modified (see release.txt) +// 990112 - added cancel debug printing +// 990304 - Tweaks to parsing of escape characters +// 990415 - Now happy with ('s in the middle on non-quoted atoms. +// This only generally happens with non-parsable text +// (eg, after an OK response). +// +// + +#include "imapio.h" +#include "impspan.h" +#include +#include +#include +#include "imapsess.h" + +#define IMAPLOG + +#ifdef _DEBUG +#define DBG(a) a +#else +#define DBG(a) +#endif + +// Initial parser buffer size & granularity +const TInt KIOBufferSize=1280; +const TInt KIOBufferGranularity=256; + +// Specifies how long a socket is allowed to be inactive before we close it +// down. This handles the situation where the mail server closes down the +// connection, but we don't receive any indication of that. It has been seen +// when connected using GPRS, and a long telephone call is then made. +const TInt KImapSendInactivityTimeMinutes = 30; +const TInt KImapReceiveInactivityTimeMinutes = 30; + +// The CImapAtom class contains a TPtrC which points to the data 'owned' by +// this atom in the buffer. It also contains next (sibling) and child pointers, +// with which the tree is constructed. +CImapAtom::CImapAtom() + { + __DECLARE_NAME(_S("CImapAtom")); + } + +CImapAtom::~CImapAtom() + { + } + +void CImapAtom::Set(const TDesC8& aAtom) + { + // Save this atom in here + iAtom.Set(aAtom); + } + +void CImapAtom::AddChild(CImapAtom *aNewChild) + { + // Set child pointer + iChild=aNewChild; + } + +void CImapAtom::AddNext(CImapAtom *aNewNext) + { + // Set next pointer + iNext=aNewNext; + } + +CImapAtom *CImapAtom::Child() + { + return(iChild); + } + +CImapAtom *CImapAtom::Next() + { + return(iNext); + } + +// ToChildL and ToNextL provide clients with a neat way of traversing the tree +// in a direction that *should* exist: if it doesn't, a Leave occurs. +CImapAtom *CImapAtom::ToChildL() + { + if (!iChild) + User::Leave(KErrNotFound); + return(iChild); + } + +CImapAtom *CImapAtom::ToNextL() + { + if (!iNext) + User::Leave(KErrNotFound); + return(iNext); + } + +// Makes things look neater: do a CompareF +TBool CImapAtom::Compare(const TDesC8& aVal) + { + // Compare and return result + return(iAtom.CompareF(aVal)==0); + } + +// Compare the right hand side of the atom with the match string. +TBool CImapAtom::CompareTail(const TDesC8& aVal) + { + if (aVal.Length() > iAtom.Length()) + return EFalse; + TPtrC8 ptr = iAtom.Right(aVal.Length()); + return(ptr.CompareF(aVal)==0); + } + +// Makes things look neater: lex a decimal atom to a TUint +TInt CImapAtom::Value(TUint& aVal) + { + // Turn it into a value + TLex8 lex(iAtom); + + return(lex.Val(aVal)); + } + +// Makes things look neater: lex a decimal atom to a TInt +TInt CImapAtom::Value(TInt& aVal) + { + // Turn it into a value + TLex8 lex(iAtom); + + return(lex.Val(aVal)); + } + +// Makes things look neater: lex a decimal atom to a TInt32 +TInt CImapAtom::Value(TInt32& aVal) + { + // Turn it into a value + TLex8 lex(iAtom); + + return(lex.Val(aVal)); + } + +// Return descriptor +TPtrC8 CImapAtom::Atom() + { + return(iAtom); + } + +// Fixup descriptor pointers +void CImapAtom::FixupL(const HBufC8 *aNewBuffer, const TText8 *aOldBuffer) + { + // Fixup descriptor pointers + CArrayFixFlat* atomStack = new (ELeave) CArrayFixFlat(10); + CleanupStack::PushL(atomStack); + + atomStack->AppendL(this); + CImapAtom* currentAtom; + while (atomStack->Count() != 0) + { + // Pop the top atom off of the stack + currentAtom = (*atomStack)[atomStack->Count() - 1]; + atomStack->ResizeL(atomStack->Count() - 1); + + // Fix up the current atom + if (currentAtom->Atom().Length()>0) + { + // Find offset from start of old buffer + TInt start=(currentAtom->Atom().Ptr()-aOldBuffer); + + // Make new descriptor & assign it + TPtrC8 bufptr(aNewBuffer->Ptr()+start,currentAtom->Atom().Length()); + currentAtom->iAtom.Set(bufptr); // Note that we are setting the real iAtom not the copy returned by Atom() + } + + // Add the first sibling to the stack, + // subsequent siblings are added when this sibling is visited + CImapAtom* siblingAtom = currentAtom->Next(); + if (siblingAtom) + atomStack->AppendL(siblingAtom); + + + // Add child to the stack + CImapAtom* childAtom = currentAtom->Child(); + if (childAtom) + atomStack->AppendL(childAtom); + } + + CleanupStack::PopAndDestroy(atomStack); + } + +// The IO processing class +CImapIO::CImapIO(TInt aId) : CMsgActive(1), iBufferSize(KIOBufferSize), iAtomStack(8), iID(aId),iPrimaryTextServerSession(NULL) + { + __DECLARE_NAME(_S("CImapIO")); + } + +CImapIO::~CImapIO() + { + // Make sure we're cancelled + Disconnect(); + + delete iSession; + + // Dispose of buffer + delete iBuffer; + + // Delete any atom tree that may still exist + delete iRootAtom; + + // Get rid of log + delete iImapLog; + // delete the atom array + iAtomArray.ResetAndDestroy(); + } + +// Create and call non-trivial contructor +CImapIO *CImapIO::NewLC(TInt aId) + { + CImapIO* self=new (ELeave) CImapIO(aId); + CleanupStack::PushL(self); + self->ConstructL(); + return self; + } + +CImapIO *CImapIO::NewL(TInt aId) + { + CImapIO* self=NewLC(aId); + CleanupStack::Pop(); + return self; + } + +// The non-trivial constructor +void CImapIO::ConstructL() + { + // Get initial buffer + iBuffer=HBufC8::NewL(iBufferSize); + + // We're an active object... + CActiveScheduler::Add(this); + } + +// Called when an asynchronous child exits with status>=0 +void CImapIO::DoRunL() + { + DBG((LogText(_L8("CImapIO::DoRunL(state=%d, status=%d)"),iState,iStatus.Int()))); + + switch(iState) + { + case EIOStateConnectQueued: + // Connect has finished: OK? + if (iStatus.Int()==KErrNone) + { + // Done! We're connected + iState=EIOStateConnected; + } + else + { + // Close connection and report fail + iSession->Disconnect(); + } + break; + + case EIOStateReadQueued: + // Process it + iState=EIOStateConnected; + iRXbytes+=iReceive.Length(); + ProcessBufferL(); + break; + + case EIOStateWriteQueued: + // Sent! Return error code directly + iState=EIOStateConnected; + break; + + case EIOStateConnected: + case EIOStateDisconnected: + case EIOStateDummyQueued: + // Never happens, but pleases the compiler + break; + } + } + +// Called when an asynchronous child exits with status Cancel(); + iState=EIOStateDisconnected; + break; + + case EIOStateReadQueued: + case EIOStateWriteQueued: + // Cancel it at the session level: this will return the cancelled error + // upwards, which should cause the above DoComplete to return + iSession->Cancel(); + iState=EIOStateConnected; + break; + + case EIOStateConnected: + case EIOStateDisconnected: + // Never happens, but pleases the compiler + break; + case EIOStateDummyQueued: + break; + } + } + else + iState=EIOStateDisconnected; + + // Get msgactive to finish up + CMsgActive::DoCancel(); + + DBG((LogText(_L8("CImapIO::DoCancel done")))); + + } + +// Return the root atom +CImapAtom *CImapIO::RootAtom() + { + // Return it + return(iRootAtom); + } + +// Deal with the atom stack: this is used when building the atom tree +void CImapIO::PushL(CImapAtom *aAtom) + { + iAtomStack.AppendL(aAtom); + } + +CImapAtom *CImapIO::PopL() + { + // Check it isn't empty + if (iAtomStack.Count()==0) + User::Leave(KErrUnderflow); + + TInt count = iAtomStack.Count(); + CImapAtom* atom = iAtomStack[count-1]; + iAtomStack.Delete(count-1); + return(atom); + } + +// Add to the parsed buffer +void CImapIO::BufferAppendL(const TChar aChar) + { + // Does buffer need extending? + if (iBuffer->Length()==iBufferSize) + { + HBufC8 *oldbuffer=iBuffer; + const TText8 *oldbufptr=iBuffer->Ptr(); + + // Extend by granularity amount + iBufferSize+=KIOBufferGranularity; + iBuffer=iBuffer->ReAllocL(iBufferSize); + + // Buffer moved? + if (iBuffer!=oldbuffer) + { + // Fixup buffer tree pointers + iRootAtom->FixupL(iBuffer,oldbufptr); + } + } + + // Append the data + iBuffer->Des().Append(aChar); + } + +// Add the last atom appended to the buffer to the tree. +void CImapIO::AddAtomL() + { + // Add it + AddAtomL(iBuffer->Length()-iAtomStart); + } + +// Add the last atom, given a length +void CImapIO::AddAtomL(const TInt aLength) + { + // Note buffer position in an atom + TPtrC8 bufptr(iBuffer->Ptr()+iAtomStart,aLength); + + // Make a new current atom + CImapAtom *newAtom=new (ELeave) CImapAtom(); + + iAtomArray.AppendL(newAtom); + // Set pointers in it + newAtom->Set(bufptr); + + // Add it as a child/sibling to the current atom + if (iNextIsChild) + iAtom->AddChild(newAtom); + else + iAtom->AddNext(newAtom); + + // The next item should be a sibling + iNextIsChild=EFalse; + + // Make new current + iAtom=newAtom; + } + +// Process the received buffer, creating atoms as we go +void CImapIO::ProcessBufferL() + { + // Process the buffer + TChar byte; + + DBG((LogText(_L8("CImapIO::ProcessBuffer(iParserState=%d, Length=%d)"),iParserState,iReceive.Length()))); + + for(TInt pos=0;posAddChild(newAtom); + else + iAtom->AddNext(newAtom); + + // The next item should be a child + iNextIsChild=ETrue; + + // Push current atom onto atom stack, make new current + iAtom=newAtom; + PushL(iAtom); + + // Store the open bracket in the buffer, so we can tell what it is + TPtrC8 bufptr(iBuffer->Ptr()+iBuffer->Length(),1); + BufferAppendL(byte); + iAtom->Set(bufptr); + + break; + } + + case ')': + case ']': + // End of this nesting level: pop last atom off stack and + // make it the new current one + iAtom=PopL(); + + // Any new atoms will be siblings, not children + iNextIsChild=EFalse; + + break; + + case '{': + // Start of a literal length + iLiteralLength=0; + iParserState=EIOStateLiteralLength; + break; + + case ' ': + // Whitespace. Ignore! This state only happens with whitespace + // after a close )] or a endquote " + break; + + case '\r': + // Newline after a close )] or endquote " + iParserState=EIOStateWaitLF; + break; + + case '\"': + // Quotes: we don't keep them, so the atom starts at the next + // character. + iAtomStart=iBuffer->Length(); + iParserState=EIOStateInAtom; + iParserQuoted=ETrue; + iGotEscape=EFalse; + break; + + default: + // Start new atom in buffer + iAtomStart=iBuffer->Length(); + BufferAppendL(byte); + iParserState=EIOStateInAtom; + iParserQuoted=EFalse; + break; + } + break; + + case EIOStateInAtom: + if (iParserQuoted) + { + // Look for another quote + if (byte=='\"') + { + // Just had an escape character? + if (iGotEscape) + { + // Add the character + BufferAppendL(byte); + iGotEscape=EFalse; + } + else + { + // It's the terminator: Add the atom, minus the quotes + AddAtomL(); + iParserState=EIOStateAtomWait; + } + } + // fix for INC51597 and DEF053082:if a " has been missed out by the server, this will end the atom at a \r + else if(!iGotEscape && byte == '\r') + { + AddAtomL(); + iParserState = EIOStateWaitLF; + } + else + { + // Escape character? + if (!iGotEscape && byte=='\\') + { + // Got one + iGotEscape=ETrue; + } + else + { + // Add to buffer + BufferAppendL(byte); + iGotEscape=EFalse; + } + } + } + else + { + if (byte==' ' || byte=='\r') + { + AddAtomL(); + + // Either go back to looking for an atom, or a LF + iParserState=(byte=='\r')?EIOStateWaitLF:EIOStateAtomWait; + } + else if (byte=='(' || byte=='[') + { + // Add this atom + AddAtomL(); + + // Make a new current atom + CImapAtom *newAtom=new (ELeave) CImapAtom(); + iAtomArray.AppendL(newAtom); + + // Add it as a sibling to the current atom + if (iNextIsChild) + iAtom->AddChild(newAtom); + else + iAtom->AddNext(newAtom); + + // The next item should be a child + iNextIsChild=ETrue; + + // Push current atom onto atom stack, make new current + iAtom=newAtom; + PushL(iAtom); + + // Store the open bracket in the buffer, so we can tell what it is + TPtrC8 bufptr(iBuffer->Ptr()+iBuffer->Length(),1); + BufferAppendL(byte); + iAtom->Set(bufptr); + + iParserState=EIOStateAtomWait; + } + else if (byte==')' || byte==']' || byte == '>') + { + // Although these bytes usually indicate the end of an atom, + // they can also legitimately appear in a text field. + // If this is the end of an atom, then it must be a child or + // sibling atom in which case there will be an entry on the atom + // stack. If there is no entry on the atom stack, then this must + // be a text field so just add the byte to the buffer. + if (iAtomStack.Count() > 0) + { + // Add this atom + AddAtomL(); + + // End of this nesting level: pop last atom off stack and + // make it the new current one + iAtom=PopL(); + + // Any new atoms will be siblings, not children + iNextIsChild=EFalse; + + iParserState=EIOStateAtomWait; + } + else + { + BufferAppendL(byte); + } + } + else + { + // Add to buffer + BufferAppendL(byte); + } + } + break; + + case EIOStateWaitLF: + // After LF, this is end of line, finish! + if (byte=='\n') + { + // Remove everything from the buffer, we've finished + iReceive.Delete(0,pos+1); + + // Reset bytes read count & complete + iBytesRead=0; + + // Complete with KErrFoundEOL + DBG((LogText(_L8("CImapIO::ProcessBuffer: Complete in EIOStateWaitLF")))); + Complete(KErrFoundEOL); + return; + } + break; + + case EIOStateLiteralLength: + // Digit? + if (byte.IsDigit()) + { + // Add it to the total + iLiteralLength=(iLiteralLength*10)+(byte-(TChar)'0'); + if (iLiteralLength <0) + User::Leave(KErrCorrupt); + } + else if (byte=='}') + { + // Need to skip CR, LF + iLiteralSkip=2; + iParserState=EIOStateLiteralSkip; + + // Add the atom (with the length we know, but no data) to the + // structure now, so that the partial structure can be parsed. + iAtomStart=iBuffer->Length(); + AddAtomL(iLiteralLength); + } + break; + + case EIOStateLiteralSkip: + // Skipping... + if (--iLiteralSkip==0) + { + // Is literal 0 bytes long? + if (iLiteralLength==0) + { + // Nothing to follow + iParserState=EIOStateAtomWait; + } + else + { + // Non-empty literal: go into fetch state + iParserState=EIOStateLiteralFetch; + } + } + break; + + case EIOStateLiteralFetch: + // Fetching + + TInt fetchLength(0); + if(KReceiveBuffer iReceive.Length()-pos) + { + fetchLength = iReceive.Length()-pos; + } +// need to extend buffer ? + DBG((LogText(_L8(">>> CImapIO::ProcessBufferL(1):buflen=%d:buffsize=%d:litlen=%d:fetchlen=%d"), iBuffer->Length(),iBufferSize, iLiteralLength,fetchLength))); + if (iBuffer->Length() + iLiteralLength > iBufferSize) + { + HBufC8 *oldbuffer=iBuffer; + const TText8 *oldbufptr=iBuffer->Ptr(); + + // Extend by extra amount + round up by KIOBufferGranularity + iBufferSize += iLiteralLength; + iBufferSize += (KIOBufferGranularity - (iBufferSize % KIOBufferGranularity)); + iBuffer=iBuffer->ReAllocL(iBufferSize); + + // Buffer moved? + if (iBuffer!=oldbuffer) + { + // Fixup buffer tree pointers + iRootAtom->FixupL(iBuffer,oldbufptr); + } + DBG((LogText(_L8(">>> CImapIO::ProcessBufferL(2):buflen=%d:buffsize=%d:litlen=%d:fetchlen=%d"), iBuffer->Length(),iBufferSize, iLiteralLength,fetchLength))); + } + iBuffer->Des().Append(iReceive.Mid(pos,fetchLength)); + // adjust loop to account for data copy + pos+=fetchLength-1; + iLiteralLength-=fetchLength; + DBG((LogText(_L8(">>> CImapIO::ProcessBufferL(3):buflen=%d:buffsize=%d:litlen=%d:fetchlen=%d"), iBuffer->Length(),iBufferSize, iLiteralLength,fetchLength))); + if (iLiteralLength==0) + { + // Atom is already saved (we add literal atoms before they + // are stored) + iParserState=EIOStateAtomWait; + } + break; + } + } + + iReceive.Zero(); + // At start of line, or if we're not doing a partial return, we need to + // queue another read here + if (iBytesRead==0 || !iReturnPartialLine) + { + // We've processed this buffer: queue a read. We only complete (above) + // when we've had a whole reply, including terminating CRLF. + DBG((LogText(_L8("CImapIO::ProcessBufferL(): queuing read, iBytesRead=%d, iReturnPartialLine is %d"), iBytesRead, iReturnPartialLine))); + QueueRead(); + } + else + { + // Have we got 'enough' of the partial line? + if (iBytesReadReceiveBinaryData(iStatus,iReceive,iLen); + iState=EIOStateReadQueued; + SetActive(); + } + +// Close connection +void CImapIO::Disconnect() + { + DBG((LogText(_L8("CImapIO::Disconnect(state=%d)"),iState))); + + // Reset state + iState=EIOStateDisconnected; + + // Delete session to stop timeouts (this will disconnect too) + delete iSession; + iSession=NULL; + } + +void CImapIO::SetTLSResponseL() + { + DBG((LogText(_L8("CImapIO::SetTLSResponseL")))); + + iSession->SetSSLTLSResponseL(KIMAP_OK); + } + +/** + @fn TInt GetIAPValue(TUint32& aIap) + Intended Usage : Gets the value of the currently connecting/connected IAP + @param aIap will be value of the currently connecting/connected IAP or KErrNotFound if an error occurs + @return KErrNone if succesful, KErrNotFound is the session does not exist or a system-wide error code. + + */ +TInt CImapIO::GetIAPValue(TUint32& aIap) + { + return (iSession) ? (iSession->GetIAPValue(aIap)) : (KErrNotFound); + } + +/** + @fn TInt GetRConnectionName(TName &aName) + Intended Usage : On return, the unique name of the RConnection. + @since 9.1 + @return KErrNone if succesful, or another of the system-wide error codes. + */ +TInt CImapIO::GetRConnectionName(TName &aName) + { + return (iSession) ? (iSession->GetRConnectionName(aName)) : (KErrNotFound); + } + +// Returns the last socket activity timeout value for the session connection +TInt CImapIO::GetLastSocketActivityTimeout(TUint32& aTimeout) + { + return (iSession) ? (iSession->GetLastSocketActivityTimeout(aTimeout)) : (KErrNotFound); + } + +/** + @fn TInt GetConnectionStage() + Intended Usage : Gets the stage of the connection process as defined in nifvar.h and csdprog.h + @since 7.0s + @return The current connection stage, KErrNotFound is the session does not exist or a system-wide error code + + */ +TInt CImapIO::GetConnectionStage() + { + return (iSession) ? (iSession->GetConnectionStage()) : (KErrNotFound); + } + + +// Parent wants to queue a connection +void CImapIO::ConnectL(TRequestStatus& aStatus, const TDesC& aHost, const TUint aPortNum, const CImIAPPreferences& aPrefs, TBool aSSLWrappedSocket) + { + // Have to be disconnected to connect... + __ASSERT_DEBUG(iState==EIOStateDisconnected,gPanic(EConnectWhenConnected)); + if(!(iState==EIOStateDisconnected)) + { + User::LeaveIfError(KErrInUse);// Connect when connected + } + // Queue request + Queue(aStatus); + + // Delete current session (there shouldn't be one really) + delete iSession; + iSession=NULL; + + // Reconstruct it + iSession=CImTextServerSession::NewL(KImapSendInactivityTimeMinutes, KImapReceiveInactivityTimeMinutes); + + // check local textseverssion is active. + if(iPrimaryTextServerSession) + { + // Providing primarysession's textserversession + // Going to be set on the secondary session + iSession->SetPrimaryTextServerSession(iPrimaryTextServerSession); + } + + // Ask session to connect (it does the resolving bit) + if(aSSLWrappedSocket) + { + iSession->SSLQueueConnectL(iStatus,aHost,aPortNum,aPrefs); + } + else + { + iSession->QueueConnectL(iStatus,aHost,aPortNum,aPrefs); + } + + SetActive(); + iState=EIOStateConnectQueued; + } + +// Parent wants the next line, all nicely parsed. When this completes, the +// parent calls RootAtom() to get the root of the parse tree. +TInt CImapIO::GetReply(TRequestStatus& aStatus) + { + // Call with maximum buffer size: no partial returns + return(GetReply(aStatus,KReceiveBuffer,EFalse)); + } + +TInt CImapIO::GetReply(TRequestStatus& aStatus, const TInt aFetchSize, const TBool aPartialReturn) + { + DBG((LogText(_L8("CImapIO::GetReply (aFetchSize=%d, aPartialReturn=%d)"),aFetchSize,aPartialReturn))); + + // Disconnected? + if (iState==EIOStateDisconnected) + return(KErrDisconnected); + + // Have to be connected & not busy + __ASSERT_ALWAYS(iState==EIOStateConnected,gPanic(EIOWhenNotReady)); + + // Queue request + Queue(aStatus); + + // Save max number of bytes to read in next request + iBytesToRead=aFetchSize; + + // Partial return required? (ie return tree in as-is state when read + // completes) + iReturnPartialLine=aPartialReturn; + + // Still busy (last request cancelled?), or still building tree? + if (iBytesRead) + { + // Carry on with this one, return it when ready + } + else + { + // New line + + // Blank output buffer + iBuffer->Des().Zero(); + + // Delete existing tree + if (iRootAtom!=NULL) + { + + // remove atoms from atom tree + iAtomArray.ResetAndDestroy(); + + delete iRootAtom; + iRootAtom=NULL; + } + + // Reset (empty) atom stack + iAtomStack.Reset(); + + // Reset state + iParserState=EIOStateAtomWait; + + // First created atom will be child of root + iRootAtom=new CImapAtom(); + iAtom=iRootAtom; + iNextIsChild=ETrue; + } + + // Anything in receive buffer? Deal with it if we can + TRAPD(err,ProcessBufferL()); + if (err!=KErrNone) + { + // An error has occurred: return this + DBG((LogText(_L8("CImapIO::ProcessBuffer: Complete in GetReply err %d"),err))); + Complete(KErrGeneral); + } + + return(KErrNone); + } + +// Send with structure +TInt CImapIO::Send(TRequestStatus &aStatus, TRefByValue aFmt,...) + { + VA_LIST list; + VA_START(list,aFmt); + iTransmit.Zero(); + iTransmit.AppendFormatList(aFmt,list); + + return(Send(aStatus, iTransmit)); + } + +// Send with structure +void CImapIO::SendL(TRequestStatus &aStatus, TRefByValue aFmt,...) + { + VA_LIST list; + VA_START(list,aFmt); + iTransmit.Zero(); + iTransmit.AppendFormatList(aFmt,list); + + User::LeaveIfError(Send(aStatus, iTransmit)); + } + +// Send with structure, specifying the transport-idle timeout to be used. +void CImapIO::SendWithTimeoutL(TRequestStatus &aStatus, TInt aTimeout, TRefByValue aFmt,...) + { + VA_LIST list; + VA_START(list,aFmt); + iTransmit.Zero(); + iTransmit.AppendFormatList(aFmt,list); + + User::LeaveIfError(SendWithTimeout(aStatus, aTimeout, iTransmit)); + } + +// Send a string +TInt CImapIO::Send(TRequestStatus& aStatus, const TDesC8& aLine) + { + // Disconnected? + if (iState==EIOStateDisconnected) + return(KErrDisconnected); + + if (iState!=EIOStateConnected) + { + return(KErrNotReady); + } + + // We're queuing a send + iState=EIOStateWriteQueued; + + Queue(aStatus); + iTXbytes+=aLine.Length(); + iSession->Send(iStatus,aLine); + SetActive(); + + return(KErrNone); + } + +// Send a string, specifying the transport-idle timeout to be used +TInt CImapIO::SendWithTimeout(TRequestStatus& aStatus, TInt aTimeout, const TDesC8& aLine) + { + // Disconnected? + if (iState==EIOStateDisconnected) + return(KErrDisconnected); + + if (iState!=EIOStateConnected) + { + return(KErrNotReady); + } + + // We're queuing a send + iState=EIOStateWriteQueued; + + Queue(aStatus); + iTXbytes+=aLine.Length(); + iSession->SendWithTimeout(iStatus, aTimeout, aLine); + SetActive(); + + return(KErrNone); + } + +// Logging calls: passed through to IMSK +void CImapIO::LogText(const TDesC8& aString) + { + // Log the text + if (iSession) + iSession->LogText(aString); + else + { +#if defined(IMAPLOG) + // Log it to secondary log file (so that we get logging after + // connection close) + if (!iImapLog) + { + TBuf<64> buf; + buf.AppendFormat(_L("c:\\logs\\Email\\imaplog%d.txt"),iID); + TRAP_IGNORE(iImapLog=CImLog::NewL(buf, EAppend)); + } + + if (iImapLog) + iImapLog->AppendComment(aString); +#endif + } + } + +void CImapIO::LogText(TRefByValue aFmt,...) + { + VA_LIST list; + VA_START(list,aFmt); + TBuf8<1024> aBuf; + + aBuf.AppendFormatList(aFmt,list); + LogText(aBuf); + } + +// Pass Logging onto the Session +void CImapIO::PerformLogging(TBool aLogging) + { + iSession->PerformLogging(aLogging); + } + +// Bytes in/out +TInt CImapIO::RXbytes(const TBool aReset) + { + TInt b=iRXbytes; + if (aReset) iRXbytes=0; + return(b); + } + +TInt CImapIO::TXbytes(const TBool aReset) + { + TInt b=iTXbytes; + if (aReset) iTXbytes=0; + return(b); + } + +// Setting of current primaryTextServerSession, going to be use on the secondary session +void CImapIO::SetPrimaryTextServerSession(CImTextServerSession* aPrimaryTextServerSession) + { + iPrimaryTextServerSession=aPrimaryTextServerSession; + } + +// Retruns the current textserversession +CImTextServerSession* CImapIO::GetTextServerSession() + { + return iSession; + } + +// Return descriptor, without any enclosing brackets < > +TPtrC8 CImapAtom::AtomNoAngleBrackets() + { + TPtrC8 atom = iAtom; + TInt len = atom.Length(); + + if (len>2 && atom[0]==KImcvLeftChevron && atom[len-1]==KImcvRightChevron) + { + atom.Set(atom.Mid(1,len-2)); + } + return(atom); + } + + +