email/imap4mtm/imapmailstore/src/cstoreutilities.cpp
changeset 80 8b14b30db193
equal deleted inserted replaced
79:2981cb3aa489 80:8b14b30db193
       
     1 // Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies).
       
     2 // All rights reserved.
       
     3 // This component and the accompanying materials are made available
       
     4 // under the terms of "Eclipse Public License v1.0"
       
     5 // which accompanies this distribution, and is available
       
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     7 //
       
     8 // Initial Contributors:
       
     9 // Nokia Corporation - initial contribution.
       
    10 //
       
    11 // Contributors:
       
    12 //
       
    13 // Description:
       
    14 //
       
    15 
       
    16 #include "cstoreutilities.h"
       
    17 #include <miuthdr.h>
       
    18 #include <barsread.h>
       
    19 
       
    20 // The maximum number of octets to be uuencoded on each line is 45
       
    21 const TInt KUuDecodedLineLength = 45;	
       
    22 // Initial size of buffer in which the "Real name <user@host>" strings
       
    23 // are built before they're put into the CImHeader and the size by
       
    24 // which to increment the buffer when necessary
       
    25 const TInt KImapAddressSizeInc=256;
       
    26 const TInt KKiloByteSize = 1024;
       
    27 const TInt KBufferExtension = 4;
       
    28 /**
       
    29 Constructor.
       
    30 */
       
    31 CStoreUtilities::CStoreUtilities(TImEncodingType aEncodingType,TUint aCharsetId,RFs& aFs)
       
    32 	:iEncodingType(aEncodingType),iCharsetId(aCharsetId),iFs(aFs)
       
    33 	{
       
    34 	}
       
    35 	
       
    36 /**
       
    37 Factory constructors.
       
    38 Ownership aHeader is taken.
       
    39 */	
       
    40 CStoreUtilities* CStoreUtilities::NewL(TImEncodingType aEncodingType,TUint aCharsetId,RFs& aFs)
       
    41 	{
       
    42 	CStoreUtilities* self=new(ELeave) CStoreUtilities(aEncodingType,aCharsetId,aFs);
       
    43 	CleanupStack::PushL(self);
       
    44 	self->ConstructL();
       
    45 	CleanupStack::Pop();
       
    46 	return self;
       
    47 	}
       
    48 	
       
    49 void CStoreUtilities::ConstructL()
       
    50 	{
       
    51 	// Create converter objects
       
    52 	iCharacterConverter=CCnvCharacterSetConverter::NewL();
       
    53 	iCharConv=CImConvertCharconv::NewL(*iCharacterConverter, iFs);	
       
    54 	}
       
    55 
       
    56 /**
       
    57 Destructor.
       
    58 */	
       
    59 CStoreUtilities::~CStoreUtilities()
       
    60 	{
       
    61 	delete iCharConv;
       
    62 	delete iCharacterConverter;
       
    63 	delete iFooterString;
       
    64 	delete iPartialLine;
       
    65 	}
       
    66 
       
    67 /**
       
    68 Checks if the descriptor contains the UUE begin header
       
    69 Extracts the file name if it is
       
    70 */	
       
    71 TBool CStoreUtilities::CheckUUEStartL(const TDesC8& aSourceLine)
       
    72 	{
       
    73 	
       
    74 	TInt sourceLength = aSourceLine.Length();
       
    75 	if(sourceLength < KImcvUueStart().Length()+3) // can't be "begin ###", it's not long enough; 3=length of ###
       
    76 		return EFalse;
       
    77 
       
    78 	if(!aSourceLine.Left(KImcvUueStart().Length()).CompareF(KImcvUueStart)) // start of line might be UUE boundary
       
    79 		{
       
    80 		// we also need to check that the next three chars are numbers - Unix file access code
       
    81 		const TUint8* sourceLinePtr = aSourceLine.Ptr();
       
    82 		TInt length=KImcvUueStart().Length();// this defines length as 6 ie. "b e g i n  " 
       
    83 		if( TChar(sourceLinePtr[length]).IsDigit() && 
       
    84 			TChar(sourceLinePtr[length+1]).IsDigit() && 
       
    85 			TChar(sourceLinePtr[length+2]).IsDigit() )
       
    86 			{
       
    87 			// Found 'begin ###' at the start of a line - assume this is a UUencode header
       
    88 			// The attachment name in this header is ignored. We use the value from the MIME header
       
    89 			return ETrue;
       
    90 			}
       
    91 		}
       
    92 		
       
    93 	return EFalse;
       
    94 	}
       
    95 	
       
    96 /**
       
    97 Appends data to partial line object extening the destination buffer as appropirate 
       
    98 @param aBufferPtr the destination bufer
       
    99 @param aText the data to be appended
       
   100 */	
       
   101 	
       
   102 void CStoreUtilities::AppendExtendL(HBufC8** aBufferPtr, const TDesC8& aText)
       
   103 	{
       
   104 	HBufC8 *buffer = *aBufferPtr;
       
   105 	TInt32 space = buffer->Des().MaxLength() - (buffer->Des().Length() + aText.Length());
       
   106 	if (space < 0)
       
   107 		{
       
   108 		TInt32 inc = (-space) + KImapAddressSizeInc - ((-space) % KImapAddressSizeInc);
       
   109 		TInt32 newSize = buffer->Des().MaxLength() + inc;
       
   110 		HBufC8 *newBuf = buffer->ReAllocL(newSize);
       
   111 		*aBufferPtr = buffer = newBuf;
       
   112 		}
       
   113 	buffer->Des().Append(aText);
       
   114 	}
       
   115 	
       
   116 	
       
   117 void CStoreUtilities::AttachFooterInfoL(TInt32 aBodyPartRemainingSize,CImapSettings& aImapSettings,HBufC8*& aDecodedData)
       
   118 	{
       
   119 	const HBufC8* buf= aImapSettings.DefaultPartialMessageFooter();
       
   120 
       
   121 	_LIT(KIntegerDirective,"%d");
       
   122 	const TInt KNoOfDigitsInMailSize = 10; // maximum length for no of digits for remaining body parts size
       
   123 	TResourceReader reader;
       
   124 	reader.SetBuffer(buf);
       
   125 	// Check if %d is not found in the resource string (To avoid problems due to localisation)
       
   126 	if(buf->Find((TDesC8&)KIntegerDirective) == KErrNotFound)
       
   127 		{
       
   128 		iFooterString = (reader.ReadTPtrC()).AllocL();
       
   129 		}
       
   130 	else
       
   131 		{
       
   132 		HBufC* resourceBuf = (reader.ReadTPtrC()).AllocL();
       
   133 		CleanupStack::PushL(resourceBuf);
       
   134 		iFooterString = HBufC::NewL(resourceBuf->Length()+KNoOfDigitsInMailSize);
       
   135 		iFooterString->Des().Format(*resourceBuf,(aBodyPartRemainingSize / KKiloByteSize));
       
   136 		CleanupStack::PopAndDestroy(resourceBuf);
       
   137 		}	
       
   138 	//caller will have left aDecodedData on the cleanupstack
       
   139 	CleanupStack::Pop(aDecodedData); 
       
   140 	TInt newSize = aDecodedData->Size() + iFooterString->Size();
       
   141 	aDecodedData = aDecodedData->ReAlloc(newSize);
       
   142 	CleanupStack::PushL(aDecodedData);
       
   143 	aDecodedData->Des().Append(*iFooterString);
       
   144 	delete iFooterString;
       
   145 	iFooterString = NULL;
       
   146 			
       
   147 	}
       
   148 	
       
   149 
       
   150 // convert text from its charset and write to richtext store. aText
       
   151 // can span multiple and partial lines
       
   152 void CStoreUtilities::WriteToBodyL(const TDesC8& aText,CRichText* aMessageBody)
       
   153 	{
       
   154 	TInt pos = aMessageBody->DocumentLength();
       
   155 
       
   156 	// Add bits of body text, converting along the way, till no characters left
       
   157 	// .. to convert.
       
   158 
       
   159 	// Convert text before writing to body.
       
   160 	TInt rem = 0;
       
   161 
       
   162 	// there will be a max of one output char per input byte
       
   163 	HBufC16* text16=HBufC16::NewLC(aText.Length());
       
   164 	TPtr16 ptr16=text16->Des();
       
   165 	
       
   166 	TBool preparedToConvert = iCharConv->PrepareToConvertToFromOurCharsetL(iCharsetId);
       
   167 	
       
   168 	if (!preparedToConvert)
       
   169 		{
       
   170 		ptr16.Copy(aText);
       
   171 		aMessageBody->InsertL(pos, ptr16);
       
   172 		}
       
   173 	else
       
   174 		{
       
   175 		TInt unconvertedChars, firstPos; // not used 
       
   176 		rem = iCharConv->ConvertToOurCharsetL(aText, ptr16, 
       
   177 											  unconvertedChars, firstPos);
       
   178 		if (rem < 0) // error
       
   179 			{
       
   180 			// Copy unconverted characters.
       
   181 			ptr16.Copy(aText);
       
   182 			aMessageBody->InsertL(pos, ptr16);
       
   183 			}
       
   184 		else if (rem && rem < iLeftOver.MaxLength())
       
   185 			{
       
   186 			iLeftOver.Copy(aText.Right(rem));	
       
   187 			}
       
   188 
       
   189 		// convert CRLF to ELineBreak
       
   190 		TInt start = 0;
       
   191 		TInt length = ptr16.Length();
       
   192 		
       
   193 		if (length>0)
       
   194 			{
       
   195 			TInt i;
       
   196 			for (i=1; i<length; i++)
       
   197 				{
       
   198 				if (ptr16[i-1] == KImcvCR && ptr16[i] == KImcvLF)
       
   199 					{
       
   200 					ptr16[i-1] = CEditableText::ELineBreak;
       
   201 
       
   202 					// write this line to body
       
   203 					TPtrC ptr = ptr16.Mid(start, i-start);
       
   204 					aMessageBody->InsertL(pos, ptr);
       
   205 					pos += ptr.Length();
       
   206 					start = i+1;
       
   207 					}
       
   208 				}
       
   209 
       
   210 			if (start != i)
       
   211 				{
       
   212 				TPtrC ptr = ptr16.Mid(start, i-start);
       
   213 				aMessageBody->InsertL(pos, ptr);
       
   214 				}
       
   215 			}
       
   216 		}
       
   217 
       
   218 	CleanupStack::PopAndDestroy(text16);
       
   219 	}
       
   220 	
       
   221 // Decode and store received data
       
   222 HBufC8* CStoreUtilities::DecodeL(const TPtrC8& aBodyData, const TBool aEndOfStream)
       
   223 	{
       
   224 	// Somewhere to store decoded data, at least as long as source (plus anything we have left
       
   225 	// in the partial line buffer which may now get consumed)
       
   226 	TInt outputbuffersize=aBodyData.Length()+KBufferExtension;
       
   227 	if (iPartialLine)
       
   228 		outputbuffersize+=iPartialLine->Des().Length();
       
   229 
       
   230 	HBufC8* decoded=HBufC8::NewL(outputbuffersize);
       
   231 	TPtr8 decoded_ptr=decoded->Des();
       
   232 
       
   233 	// Which decoder are we using?
       
   234 	switch(iEncodingType)
       
   235 		{
       
   236 		case EEncodingTypeNone:
       
   237 		case EEncodingType7Bit:
       
   238 		case EEncodingType8Bit:
       
   239 		case EEncodingTypeBinary:
       
   240 		case EEncodingTypeUnknown:
       
   241 			// Nothing to do, just copy data
       
   242 			decoded->Des().Append(aBodyData);
       
   243 			break;
       
   244 
       
   245 		case EEncodingTypeBASE64:
       
   246 			// Decode Base64 data: just filter it through decoder, it
       
   247 			// ignores line breaks anyway.
       
   248 			iB64Decoder.Decode(aBodyData,decoded_ptr);
       
   249 			break;
       
   250 
       
   251 		case EEncodingTypeUU:
       
   252 			{
       
   253 			TPtrC8 bodydata=aBodyData;
       
   254 
       
   255 			// Got a partial buffer?
       
   256 			if (!iPartialLine)
       
   257 				{
       
   258 				// Allocate buffer
       
   259 				iPartialLine=HBufC8::NewL(KUuDecodedLineLength);
       
   260 				iUUDecoding = EFalse;
       
   261 				}
       
   262 			
       
   263 			// Decode UUEncoded data: line by line
       
   264 			TBool decodeEnded = EFalse;
       
   265 			TInt position=0;
       
   266 			while ( bodydata.Length() && !decodeEnded )
       
   267 				{
       
   268 				// Find() returns the start of "\r\n". The decoding algorithm
       
   269 				// requires that the encoded line contains the "\r\n".
       
   270 				TInt lineEnd = bodydata.Find( _L8("\r\n") );
       
   271 				if (lineEnd != KErrNotFound)
       
   272 					{
       
   273 					lineEnd = lineEnd + 2;
       
   274 					AppendExtendL( &iPartialLine, bodydata.Left( lineEnd ));
       
   275 				
       
   276 					bodydata.Set( bodydata.Mid( lineEnd ) );
       
   277 				
       
   278 					// Check for a well-formated  begin-tag
       
   279 					if ( CheckUUEStartL( iPartialLine->Des() ) )
       
   280 						{
       
   281 						iUUDecoding = ETrue;
       
   282 						}
       
   283 					else if ( iPartialLine->Compare( KImcvUueEnd ) != 0 && iUUDecoding )
       
   284 						{
       
   285 						// Every malformatted string is decoded as an empty string 
       
   286 						// with length 0. Appending such a string is harmless.
       
   287 						TPtr8 destination((unsigned char*)decoded_ptr.Ptr()+position,0,outputbuffersize-position);
       
   288 						iUUDecoder.Decode(*iPartialLine,destination);
       
   289 						position+=destination.Length();			
       
   290 						}
       
   291 					else if ( iUUDecoding )
       
   292 						{
       
   293 						decodeEnded = ETrue;
       
   294 						iUUDecoding = EFalse;	
       
   295 						}
       
   296 					
       
   297 					iPartialLine->Des().Zero();
       
   298 					}
       
   299 				else
       
   300 					{
       
   301 					AppendExtendL( &iPartialLine, bodydata);
       
   302 					
       
   303 					// advance to end of bodydata
       
   304   					bodydata.Set(bodydata.Ptr()+bodydata.Length(), 0);
       
   305 					}
       
   306 				}
       
   307 			decoded->Des().SetLength(position);	
       
   308 			break;
       
   309 			}
       
   310 
       
   311 		case EEncodingTypeQP:
       
   312 			{
       
   313 			TPtrC8 bodydata=aBodyData;
       
   314 
       
   315 			// Got a partial buffer?
       
   316 			if (!iPartialLine)
       
   317 				{
       
   318 				// Allocate buffer
       
   319 				iPartialLine=HBufC8::NewL(256);
       
   320 				}
       
   321 
       
   322 			// Build buffer to decode: basically, QP decoder wants CRLF terminated
       
   323 			// lines, so we build them in the iPartialLine buffer. There may be
       
   324 			// stuff already there from previous data packet - so we just append.
       
   325 			TInt position=0;
       
   326 			while(bodydata.Length())
       
   327 				{
       
   328 				// Find a line break
       
   329 				TInt lineend=bodydata.Find(_L8("\r\n"));
       
   330 
       
   331 				// No break?
       
   332 				if (lineend==KErrNotFound && !aEndOfStream)
       
   333 					{
       
   334 					// Stick it all in the partialline buffer, we should get a CRLF
       
   335 					// soon...
       
   336 					AppendExtendL( &iPartialLine,bodydata);
       
   337 					break;
       
   338 					}
       
   339 				else
       
   340 					{
       
   341 					if (lineend==KErrNotFound)
       
   342 						{
       
   343 						// Append whole thing left to buffer
       
   344 						AppendExtendL( &iPartialLine,bodydata);
       
   345 
       
   346 						// advance to end of bodydata
       
   347 						bodydata.Set(bodydata.Ptr()+bodydata.Length(), 0);
       
   348 						}
       
   349 					else
       
   350 						{
       
   351 						// Append to buffer up to that point (including the \r\n)
       
   352 						AppendExtendL( &iPartialLine,bodydata.Left(lineend+2));
       
   353 
       
   354 						// Remove from the buffer we're working on (including the \r\n)
       
   355 						bodydata.Set(bodydata.Ptr()+lineend+2,bodydata.Length()-lineend-2);
       
   356 						}
       
   357 
       
   358 					// Decode & skip on in buffer
       
   359 					TPtr8 destination((unsigned char*)decoded_ptr.Ptr()+position,0,outputbuffersize-position);
       
   360 					iQPDecoder.Decode(*iPartialLine,destination);
       
   361 					position+=destination.Length();
       
   362 					iPartialLine->Des().Zero();
       
   363 					}
       
   364 				}
       
   365 
       
   366 			// Update decoded
       
   367 			decoded->Des().SetLength(position);
       
   368 			break;
       
   369 			}
       
   370 		}
       
   371 
       
   372 	// put back any partially converted data
       
   373 	if (iLeftOver.Length())
       
   374 		{
       
   375 		decoded->Des().Insert(0, iLeftOver);
       
   376 		iLeftOver.SetLength(0);
       
   377 		}
       
   378 		
       
   379 	return decoded;
       
   380 	}
       
   381