messagingfw/biomsgfw/ENPSRC/ENP.CPP
changeset 62 db3f5fa34ec7
parent 0 8e480a14352b
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/messagingfw/biomsgfw/ENPSRC/ENP.CPP	Wed Nov 03 22:41:46 2010 +0530
@@ -0,0 +1,629 @@
+// Copyright (c) 1999-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:
+//
+
+#include <msvids.h>
+#include <msvuids.h>
+#include <msvapi.h>
+#include <cemailaccounts.h>
+
+#include "ENP.H"
+#include <pop3set.h>
+#include <imapset.h>
+#include <miutconv.h>
+#include <e32math.h>
+
+#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS 
+#include "tmsvbioinfo.h"
+#endif
+
+// Parse constants
+_LIT(KEmailHeader,"//MLAP11");
+#define KCharColon          ':'
+#define KCharSpace          ' '
+#define KCharTab            '\t'
+#define KCharLineFeed       '\n'
+#define KCharSlash			'/'
+
+// Header fields defined in Nokia Smart Messaging Specification grammar (3.9.2.1).
+// N.B. If new fields are added, (cf. <extension-field>) PrintFieldToRichTextL()
+// will need to be changed accordingly.
+_LIT(KHeaderNumberMessages, "msgcount");
+_LIT(KHeaderFrom,"from");
+_LIT(KHeaderSubject,"subject");
+_LIT(KHeaderSize,"size");
+_LIT(KHeaderUidImap,"iuid");
+_LIT(KHeaderUidPop,"puid");
+_LIT(KHeaderServerId,"sid");
+_LIT(KHeaderAttachments,"att");
+_LIT(KHeaderTo,"to");
+_LIT(KHeaderCc,"cc");
+_LIT(KHeaderDate,"date");
+_LIT(KHeaderFolder,"fldr");
+_LIT(KHeaderSender,"sender");
+_LIT(KHeaderReplyTo,"reply-to");
+_LIT(KHeaderUidValidity,"uidv");
+_LIT(KInitialFieldValue,"");
+
+#define KUidCharsetISO88591         0x10003b10
+#define	KServerIdModVal				512
+
+//
+// Factory methods
+//
+EXPORT_C CEmailNotificationParser* CEmailNotificationParser::NewL(CRegisteredParserDll& aRegisteredParserDll, CMsvEntry& aEntry, RFs& aFs)
+    {
+    CEmailNotificationParser* self = new (ELeave) CEmailNotificationParser(aRegisteredParserDll, aEntry, aFs);
+    CleanupStack::PushL(self);
+    self->ConstructL();
+    CleanupStack::Pop();
+    return self;
+    }
+// end CEmailNotificationParser::NewL()
+
+
+void CEmailNotificationParser::ConstructL()
+    {
+    iParsedFieldArray = new (ELeave) CArrayPtrSeg<CParsedField>(16);
+
+    // Initialise array with required field names
+    // N.B. This dictates the order in which the headers will appear in the message body.
+    AddRequiredFieldL(KHeaderNumberMessages);
+	AddRequiredFieldL(KHeaderFrom);
+    AddRequiredFieldL(KHeaderSubject);
+    AddRequiredFieldL(KHeaderTo);
+    AddRequiredFieldL(KHeaderDate);
+	AddRequiredFieldL(KHeaderAttachments);
+	AddRequiredFieldL(KHeaderCc);
+    AddRequiredFieldL(KHeaderSize);
+    AddRequiredFieldL(KHeaderServerId);
+    AddRequiredFieldL(KHeaderUidImap);
+    AddRequiredFieldL(KHeaderUidPop);
+	//The following fields added to fix defect INC023328 
+	AddRequiredFieldL(KHeaderFolder);
+    AddRequiredFieldL(KHeaderSender);
+    AddRequiredFieldL(KHeaderReplyTo);
+    AddRequiredFieldL(KHeaderUidValidity);
+
+	iFoundServer = EFalse;
+
+    CActiveScheduler::Add(this);
+    }
+// end CEmailNotificationParser::ConstructL()
+
+void CEmailNotificationParser::ParseL(TRequestStatus& aStatus, const TDesC& aSms)
+    {
+    TMsvEntry entry = iEntry.Entry();   //  Get the generic stuff
+    iEntryId = entry.Id();              //  store the TMsvId
+
+    __ASSERT_DEBUG((entry.MtmData3() == 0 || entry.MtmData3() == 1),
+                            User::Panic(_L("ENP-DLL"),KErrCorrupt));
+    //  Already parsed....
+    if(entry.MtmData3() == 1)
+        {
+        iReport = &aStatus;
+        User::RequestComplete(iReport, KErrNone);
+        }
+    //  not parsed
+    else if(entry.MtmData3() == 0)
+        {
+        if (iSmsBuf != NULL)
+            {
+            delete iSmsBuf;
+            iSmsBuf = NULL;
+            }
+        iSmsBuf = aSms.AllocL();            // Allocate new HBufC object
+        ChangeStateL(EUnfoldMessage);       //Set to initial request
+        aStatus = KRequestPending;
+        iReport = &aStatus;
+        }
+    else
+        {
+        User::Leave(KErrNotSupported);
+        }
+    }
+// end CEmailNotificationParser::ParseL(TRequestStatus&, const TDesC&)
+
+
+
+//
+//  ProcessL() --    Email notification parser doesn't actually commit
+//                  any information to persistant store other than the 
+//                  iParsedFieldsArray data as part of the parse method
+//                  .. Complete  KErrNotSupported..
+//
+void CEmailNotificationParser::ProcessL(TRequestStatus& aStatus)
+    {
+    iReport = &aStatus;
+    User::RequestComplete(iReport, KErrNotSupported);
+    }
+// end CEmailNotificationParser::CommitL(TRequestStatus&)
+
+
+void CEmailNotificationParser::DoCancel()
+    {
+	User::RequestComplete(iReport,KErrCancel);
+    }
+// end CEmailNotificationParser::DoCancel()
+
+
+void CEmailNotificationParser::RunL()
+    {
+    iCompleted = iStatus.Int();
+    if (iCompleted != KErrNone)
+        {
+        User::RequestComplete(iReport,iCompleted);
+        return;
+        }
+
+    switch (iState)
+        {
+        case EUnfoldMessage:        // UnfoldSmsL completed
+            TRAPD(err, ChangeStateL(EParseMessage));
+            if (err != KErrNone)
+                User::RequestComplete(iReport, err);
+            break;
+
+        case EParseMessage:
+            TRAP(err, ChangeStateL(ECompleteMessage));
+            if(err != KErrNone)
+                {
+                User::RequestComplete(iReport, err);
+                }
+            break;
+                        
+        case ECompleteMessage:
+            // CompleteMessage completed
+            User::RequestComplete(iReport, KErrNone);
+            break;
+
+        default:
+            break;
+        }
+    }
+// end CEmailNotificationParser::RunL()
+
+
+void CEmailNotificationParser::ChangeStateL(TParseSession aState)
+    {
+    iState = aState;
+    switch (iState)
+        {                    
+        case EUnfoldMessage:
+            UnfoldMessageL();
+            iSms = iSmsBuf->Des();      // initialise Tlex object
+            break;
+
+        case EParseMessage:
+            ParseMessageL();
+            break;
+
+        case ECompleteMessage:
+            CompleteMessageL();
+            break;
+
+        default:
+            break;
+        }
+	RequestComplete(iStatus, KErrNone);
+
+    SetActive();
+    }
+// end CEmailNotificationParser::ChangeStateL(TParseSession)
+
+
+void CEmailNotificationParser::UnfoldMessageL()
+    {
+    // Nokia protocol allows for folding of long fields (see Nokia Smart
+    // Messaging spec 2.0.0pre, 3-34 and RFC822, 3.1.1). This method
+    // unfolds the message by deleting any linefeed characters which are
+    // followed immediately by linear white space.
+    //   Note that the value returned by pBuf.Length() will change if
+    // linefeeds are deleted. Hence this is called for each iteration to
+    // avoid violating buffer bounds.
+
+    TPtr pBuf(iSmsBuf->Des());     // Create modifiable pointer to HBufC
+
+    for (TInt pos = 0; pos < (pBuf.Length() - 1); pos++)
+        {
+        // Find linefeed followed by whitespace
+        if (pBuf[pos] == KCharLineFeed  &&
+            (pBuf[pos+1] == KCharSpace  ||  pBuf[pos+1] == KCharTab))
+            {
+            pBuf.Delete(pos, 1);
+            }
+        }
+
+    // Reallocate iSmsBuf with new size
+    iSmsBuf = iSmsBuf->ReAllocL(pBuf.Length());
+    }
+// end CEmailNotificationParser::UnfoldSmsL()
+
+//
+//  ParseMessageL()
+//
+//  Simple Notification:
+//      Looks for number of messages and "From:" field
+//      writes numMsg into the parsed field array, sets
+//      entry.iDetails to the "From:" 
+//  Complicated Notification:
+//      does the above and writes everything else into
+//      the array....
+//
+void CEmailNotificationParser::ParseMessageL()
+    {
+    TBool isSimpleNotification = EFalse; 
+    TLex  tokenLex;
+    TPtrC fieldName;
+    TPtrC fieldValue;
+
+	// reset parsedfield array
+	for(TInt i = iParsedFieldArray->Count();--i>=0;)
+		(*iParsedFieldArray)[i]->SetFieldValueL(_L(""));
+
+    // Set extraction mark at first character
+    iSms.SkipSpaceAndMark();
+	if(iSms.Peek() == KCharSlash)
+		{
+		// Check first line is <header>
+		iSms.SkipCharacters();
+
+		if (iSms.MarkedToken() != KEmailHeader)
+			{
+			User::Leave(KBspInvalidMessage);
+			}
+		// Get <new-amount> from second line and check for terminating linefeed
+		iSms.SkipSpaceAndMark();
+		}
+
+    // Val() seeks forward from next char position, looking for valid
+    // digits and incrementing next char as it goes.
+    if (!(iSms.Val(iMessageCount) == KErrNone  &&  iMessageCount >= 0))     // If marked token is not a valid positive integer
+        {
+        iMessageCount = 0;
+        User::Leave(KBspInvalidMessage);
+        }
+    else
+        {
+        fieldValue.Set(iSms.MarkedToken());                            //  The message count..
+        AddParsedFieldL(KHeaderNumberMessages, fieldValue, ETrue);
+        }
+
+    // Next character may now be at newline or space after integer.
+    // If at space, advance to newline.
+    while (iSms.Peek() != KCharLineFeed && !iSms.Eos())
+        iSms.Inc();
+
+    iSms.SkipSpaceAndMark();
+
+    // Now parse the rest of the fields, if any.
+    while (!iSms.Eos())
+        {
+        while (iSms.Peek() != KCharLineFeed  &&  !iSms.Eos())
+            iSms.Inc();     //  Skip to next delimiter
+
+        if (iSms.Eos())
+            break;          //  we've finished break out of the function
+
+        if (iSms.TokenLength() == 0)
+            User::Leave(KBspSmartMessageInvalidToken);
+
+        //  Parsing....
+        tokenLex.Assign(iSms.MarkedToken());        // Assign token to a new TLex
+        while (tokenLex.Peek() != KCharColon  &&  !tokenLex.Eos())
+            tokenLex.Inc();     //  Advance to a ':'
+
+        if (tokenLex.Eos()  ||  tokenLex.TokenLength() == 0)
+            User::Leave(KBspSmartMessageInvalidToken);
+
+
+        fieldName.Set(tokenLex.MarkedToken());      // Store (pointer to) field name
+        tokenLex.Inc();
+
+		//fix for DEF017686
+		LeaveIfEmptyFieldsL(fieldName,tokenLex);
+		
+        
+        tokenLex.SkipSpaceAndMark();                // Step past optional spaces
+
+		//fix for DEF017686
+        LeaveIfEmptyFieldsL(fieldName,tokenLex);
+	
+		// if it's the server id field try to extract the id value
+		// and match to an existing email service
+		if(fieldName.CompareF(KHeaderServerId)==KErrNone)
+			{
+			TInt valErr = tokenLex.Val(iServerId);
+			if(valErr != KErrNone)
+				iServerId = 0;
+			else
+				GetEmailServicesL();
+
+			tokenLex.UnGetToMark();
+			}	
+		fieldValue.Set(tokenLex.Remainder());       // Store (pointer to) field value
+
+        if(!isSimpleNotification)
+            {
+            AddParsedFieldL(fieldName, fieldValue, EFalse);
+            }		
+
+        // Successfully parsed a token. Move iSms's next character past the
+        // (linefeed) delimiter, set the extraction mark and reiterate to
+        // look for next token.
+        iSms.SkipSpaceAndMark();
+        }
+    }
+// end CEmailNotificationParser::ParseMessageL()
+
+
+//fix for DEF017686: EMail notification parser leaves when field data is empty  
+void CEmailNotificationParser::LeaveIfEmptyFieldsL(const TDesC& aFieldName,
+												   const TLex& aTokenLex)
+	{
+/*
+ 		The following  checks for an empty field value.
+ 		The only fields allowed to be empty are the subject  and extension fields.
+ 		Hence, the check is done when aFieldName is not  a subject field and an extension field.
+ 		However, it is not possible to explicitly check for an extension field as we don't 
+ 		know which strings to compare. Therefore, a comparison with all the known field names
+ 		must be performed instead. The risk is fairly small as the change is localised and it can 
+ 		be easily tested.
+*/
+
+	if ((aFieldName.CompareF(KHeaderFrom)==KErrNone) ||
+ 		(aFieldName.CompareF(KHeaderSize)==KErrNone) ||
+		(aFieldName.CompareF(KHeaderUidImap)==KErrNone) ||
+ 		(aFieldName.CompareF(KHeaderUidPop)==KErrNone) ||
+ 		(aFieldName.CompareF(KHeaderServerId)==KErrNone) ||
+ 		(aFieldName.CompareF(KHeaderAttachments)==KErrNone) ||
+ 		(aFieldName.CompareF(KHeaderTo)==KErrNone) ||
+ 		(aFieldName.CompareF(KHeaderCc)==KErrNone) ||
+ 		(aFieldName.CompareF(KHeaderDate)==KErrNone) ||
+ 		(aFieldName.CompareF(KHeaderFolder)==KErrNone) ||
+ 		(aFieldName.CompareF(KHeaderSender)==KErrNone) ||
+ 		(aFieldName.CompareF(KHeaderReplyTo)==KErrNone) ||
+ 		(aFieldName.CompareF(KHeaderUidValidity)==KErrNone) //not sure if this actually exists*/
+	   )
+ 		
+ 		if (aTokenLex.Eos())
+ 			User::Leave(KBspSmartMessageInvalidToken);	
+	}
+
+
+//
+//  CompleteMessageL()
+//
+//  -   Function streams the contents of iParsedFieldsArray
+//      as a child entry of the original message. If this
+//      returns OK sets the iMtmData3 flag to 1 (parsed) and
+//      writes a new message body into the SmartMessage
+//
+//  -   If externalization fails the function sets the message
+//      not parsed and does not alter the message body.
+//
+void CEmailNotificationParser::CompleteMessageL()
+    {
+	//Create new stream (containing parsed data) within CMsvStore associated with message entry
+	iEntry.SetEntryL(iEntryId);
+	CMsvStore* store=iEntry.EditStoreL();
+	CleanupStack::PushL(store);
+	StoreL(*store);
+	CleanupStack::PopAndDestroy();
+
+	TMsvEntry entry= iEntry.Entry();
+    entry.SetMtmData3(1);                                //  Indicates that we've parsed it..
+
+	// set iRelated id if we know which server the message corresponds to
+	// iRelatedId is set to the id of the relevant service entry.
+	if(iFoundServer)
+		entry.iRelatedId = iServiceMsvId;
+	iEntry.ChangeL(entry);
+    }
+// end CEmailNotificationParser::CompleteMessageL()
+
+void CEmailNotificationParser::AddRequiredFieldL(const TDesC& aFieldName)
+    {
+    // Create new CParsedField object on heap
+    CParsedField* parsedField = new (ELeave) CParsedField;
+	CleanupStack::PushL(parsedField);
+    // Initialise field name
+    parsedField->SetFieldNameL(aFieldName);
+    parsedField->SetFieldValueL(KInitialFieldValue);
+    parsedField->SetMandatoryField(EFalse);
+
+    // Add pointer to array
+    iParsedFieldArray->AppendL(parsedField);
+    CleanupStack::Pop();
+    // No need to delete the CParsedField object, since it's now owned by the array
+    }
+// end CEmailNotificationParser::AddRequiredFieldL(const TDesC&)
+
+
+void CEmailNotificationParser::AddParsedFieldL(const TDesC& aFieldName,
+                                              const TDesC& aFieldValue,
+                                              TBool aMandatory)
+    {
+    // This method is responsible for adding field values found
+    // by the parser to the array of parsed fields. It also
+    // enforces the "each field once only" rule (see Nokia Smart
+    // Messaging spec, 2.0.0pre, 3-34).
+    for (TInt i = 0; i < iParsedFieldArray->Count(); i++)
+        {
+        // Use CompareF() to perform case-insensitive match
+        if (aFieldName.CompareF(iParsedFieldArray->At(i)->FieldName()) == 0)
+            {
+            // Field name exists, so insert a value
+            iParsedFieldArray->At(i)->SetFieldValueL(aFieldValue);
+            iParsedFieldArray->At(i)->SetMandatoryField(aMandatory);
+            iExtendedNotification = ETrue;
+			return;
+            }
+        }
+
+	// This has been added to fix defect INC38950
+	// If we have got here, then we have a new field, not specified by one of the fields in iParseFieldArray
+    CParsedField* parsedField = new (ELeave) CParsedField;
+	CleanupStack::PushL(parsedField);
+    parsedField->SetFieldNameL(aFieldName);
+    parsedField->SetFieldValueL(aFieldValue);
+    parsedField->SetMandatoryField(EFalse);
+    iParsedFieldArray->AppendL(parsedField);
+    CleanupStack::Pop(parsedField);
+	}
+// end CEmailNotificationParser::AddParsedFieldL(const TDesC&, const TDesC&, TBool)
+
+//
+// Get all email services
+//
+void CEmailNotificationParser::GetEmailServicesL()
+	{
+
+	CCnvCharacterSetConverter* charconv = CCnvCharacterSetConverter::NewL();
+	CleanupStack::PushL(charconv);
+	CArrayFix<CCnvCharacterSetConverter::SCharacterSet> *charsetsAvailable = charconv->CreateArrayOfCharacterSetsAvailableL(iFs);
+	CleanupStack::PushL(charsetsAvailable);
+
+	charconv->PrepareToConvertToOrFromL(KUidCharsetISO88591,*charsetsAvailable,iFs);
+	
+	const TMsvId current = iEntry.Entry().Id();
+	iEntry.SetEntryL(KMsvRootIndexEntryId);
+	// Get all POP3 and Imap services
+	CMsvEntrySelection* rootChildren = iEntry.ChildrenL();
+	CleanupStack::PushL(rootChildren);
+
+	TMsvEntry entry;
+
+	CEmailAccounts* accounts = CEmailAccounts::NewLC();
+
+	const TInt count = rootChildren->Count(); 
+	TInt j =0;
+	while(j < count && iFoundServer ==EFalse)
+		{
+		// set context to service entry
+		iEntry.SetEntryL((*rootChildren)[j]);
+		entry = iEntry.Entry(); 
+		if (entry.iType == KUidMsvServiceEntry && (entry.iMtm == KUidMsgTypePOP3 || entry.iMtm == KUidMsgTypeIMAP4))
+			{
+			TBuf8<8> port;
+
+			if(entry.iMtm == KUidMsgTypePOP3)
+				{
+				// calculate hash value
+				CImPop3Settings* settings = new(ELeave)CImPop3Settings;
+				CleanupStack::PushL(settings);
+ 
+ 				TPopAccount id;
+				accounts->GetPopAccountL(entry.Id(), id);
+				accounts->LoadPopSettingsL(id, *settings);
+
+				port.Num((TUint)KPOP3DefaultPortNumber);
+				iFoundServer = CalcHashValueL( *charconv, settings->LoginName(), settings->ServerAddress(), port);
+				}
+			else // entry.iMtm == KUidMsgTypeIMAP4
+				{
+				CImImap4Settings* settings = new(ELeave)CImImap4Settings;
+				CleanupStack::PushL(settings);
+				
+ 				TImapAccount id;
+				accounts->GetImapAccountL(entry.Id(), id);
+				accounts->LoadImapSettingsL(id, *settings);
+
+				port.Num((TUint)KIMAPDefaultPortNumber);
+				iFoundServer = CalcHashValueL( *charconv, settings->LoginName(), settings->ServerAddress(), port);
+				}
+			CleanupStack::PopAndDestroy(); // settings
+			}
+		j++;
+		}
+
+	if(	iFoundServer )
+		iServiceMsvId = entry.Id();
+
+	CleanupStack::PopAndDestroy(4, charconv); // charconv, charsetsAvailable, rootChildren, accounts
+	
+	iEntry.SetEntryL(current);
+	}
+
+//
+// Calculate a hash value for each server entry
+// 
+TBool CEmailNotificationParser::CalcHashValueL(CCnvCharacterSetConverter& aCharConv, const TDesC8& aUserName, const TDesC& aServerName, const TDesC8& aPortNum)
+	{
+	TInt total = 0;
+	TInt loop = 0;
+	TInt unConvertChar;
+	TInt posOfUnconv;
+
+	delete iServerName;
+	iServerName = NULL;
+	iServerName = HBufC8::NewL(aServerName.Length());
+
+	TPtr8 svr = iServerName->Des();
+	aCharConv.ConvertFromUnicode( svr, aServerName, unConvertChar,posOfUnconv);
+
+	// Fix for DEF044588: the Smart Messaging Specification (Revision 3.0.0) states (page 3-17) that
+	// the value of the first character of S is multiplied by one, second character by two etc.
+	for(loop = 0; loop < aUserName.Length(); loop++)
+		{
+		total += ( (loop+1) * (TUint)aUserName[loop]);
+		}
+
+	TInt pos = aUserName.Length();
+
+	for(loop = 0; loop < svr.Length(); loop++)
+		{
+		total += ( (pos + (loop+1) )* (TUint)svr[loop]);
+		}
+
+	pos += aServerName.Length();
+
+	for(loop = 0; loop < aPortNum.Length(); loop++)
+		{
+		total += ( (pos + (loop+1) )* (TUint)aPortNum[loop]);
+		}
+
+	TReal hashVal;
+	User::LeaveIfError(Math::Mod(hashVal, total, KServerIdModVal));
+	return ((TInt)hashVal == iServerId);
+	}
+
+void CEmailNotificationParser::RequestComplete(TRequestStatus& aStatus, TInt aError)
+    {
+    TRequestStatus* p = &aStatus;
+    User::RequestComplete(p, aError);
+    }
+// end CEmailNotificationParser::RequestComplete(TRequestStatus&, TInt)
+
+//
+// Constructor
+//
+CEmailNotificationParser::CEmailNotificationParser(CRegisteredParserDll& aRegisteredParserDll, CMsvEntry& aEntry, RFs& aFs)
+: CBaseScriptParser2(aRegisteredParserDll, aEntry, aFs), 
+iState(0), iSmsBuf(0), iReport(0), iCompleted(0), iMessageCount(0), iExtendedNotification(EFalse)
+    {
+    }
+// end CEmailNotificationParser::CEmailNotificationParser()
+
+CEmailNotificationParser::~CEmailNotificationParser()
+    {
+	delete iSmsBuf;
+	if(iParsedFieldArray)
+		{
+		iParsedFieldArray->ResetAndDestroy();
+		delete iParsedFieldArray;
+		}
+	delete iServerName;
+    }
+// end CEmailNotificationParser::~CEmailNotificationParser()