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