phonebookengines/contactsmodel/cntvcard/cntvcardconverter.cpp
changeset 0 e686773b3f54
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/phonebookengines/contactsmodel/cntvcard/cntvcardconverter.cpp	Tue Feb 02 10:12:17 2010 +0200
@@ -0,0 +1,866 @@
+// Copyright (c) 1997-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 <cntvcard.h>
+#include "cntvcardutils.h"
+
+// System includes
+#include <badesca.h>
+#include <s32std.h>
+#include <s32mem.h>
+#include <ecom/implementationproxy.h>
+
+// User includes
+#include "CNTPROF.H"
+#include <cntfldst.h>
+#include <cntfield.h>
+#include <cntdef.h>
+#include <cntitem.h>
+
+#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS
+#include "cntconvertercallback.h"
+#endif
+
+
+
+/** 
+ * Imports one or more vCards from a read stream. The vCards are converted 
+ * into contact items, and added to the database. If at least one contact
+ * item was successfully imported, aImportSuccessful is set to ETrue. 
+ * 
+ * @param aDb Contacts database
+ * @param aReadStream The stream to read from.
+ * @param aImportSuccessful On return, ETrue if at least one contact was successfully imported. EFalse if not
+ * @param aOption Import preferences (available options defined in CContactDatabase::TOptions)
+ * @param aImportSingleContact Import a single vCard entity
+ * @return Array of CContactItem objects
+ */
+CArrayPtr<CContactItem>* CContactVCardConverter::ImportL(CContactDatabase& aDb,RReadStream& aReadStream,TBool& aImportSuccessful,TInt aOption,TBool aImportSingleContact)
+	{
+__START_PROFILE(23);
+	TBool increaseAccessCount=aOption & (EIncreaseAccessCount);
+	TBool decreaseAccessCount=aOption & (EDecreaseAccessCount);
+	aImportSuccessful=EFalse;	
+	CVCardToContactsAppConverter* converter=new(ELeave)	CVCardToContactsAppConverter;
+	CleanupStack::PushL(converter);
+	CArrayPtr<CContactItem>* contactItems=new(ELeave) CArrayPtrFlat<CContactItem>(4);
+	CleanupStack::PushL(TCleanupItem(CleanUpResetAndDestroy,contactItems));
+	TInt err=KErrNone;
+	if (!aImportSingleContact)
+		{
+		aDb.DatabaseBeginLC(EFalse);
+		}
+
+	while (err!=KErrEof)
+		{
+		CParserVCard* vCard= CParserVCard::NewL();
+		CleanupStack::PushL(vCard);
+__START_PROFILE(20);
+		TRAP(err,vCard->InternalizeL(aReadStream));
+__END_PROFILE(20);
+		if ((err != KErrNotFound) && (err != KErrEof))
+	  		{
+			User::LeaveIfError(err);
+				
+				TContactItemId pos = IsVCardMergeNeededL(*converter, *vCard, aDb, aOption);
+				//Check if we have been asked to replaced rather than merge the
+				//contact item.		
+				TContactItemId posId = KErrNotFound;
+				if ((aOption & EReplaceIfExists) && (pos != KErrNotFound))
+					{
+					posId = pos;
+					pos = KErrNotFound;						
+					}
+					
+				if (pos == KErrNotFound)
+					{
+					//this is a vCard previously exported but now deleted from the database
+					//treat as a new card
+					//or an existing contact item which is to be
+					//updated (replaced) with the given vCard.
+					doImportL(*converter, *vCard, aDb, aOption, increaseAccessCount, decreaseAccessCount, aImportSuccessful, contactItems, !aImportSingleContact, posId);
+					}
+				else
+					{
+					//this is a existing contact to update
+					CContactItem* item=aDb.OpenContactLX(pos);
+					CleanupStack::PushL(item);
+					
+					ModifyAccessCountL(*item, increaseAccessCount, decreaseAccessCount);
+
+					TUid type = item->Type();
+					if (type != KUidContactCard && type != KUidContactOwnCard)
+						{
+						User::Leave(KErrNotFound);
+						}
+
+					CArrayPtr<CParserProperty>* agentPropertyList = NULL;
+					
+					agentPropertyList = vCard->PropertyL(KVersitTokenAGENT, TUid::Uid(KVCardPropertyAgentUid));
+					
+					CleanupStack::PushL(TCleanupItem(CleanUpResetAndDestroy, agentPropertyList));
+					if(agentPropertyList && agentPropertyList->Count())
+						{
+						//Add agents to contact item and if needed, merge agentcards with existing contacts
+						HandleAgentsInVCardL(*converter, agentPropertyList, *item, aDb, aOption, increaseAccessCount, decreaseAccessCount, contactItems,ETrue);
+						}		
+					CleanupStack::PopAndDestroy(agentPropertyList);
+
+					TBool deleteItem = converter->MergeVCardWithContactItemL(*item, *vCard, CVCardToContactsAppConverter::EPreserveAllProperties, aOption);
+		
+					aDb.doCommitContactL(*item, ETrue, ETrue);  
+					
+					if (deleteItem)
+						{
+						CleanupStack::PopAndDestroy(2); // item, item close
+						aDb.doDeleteContactL(pos, ETrue, ETrue, decreaseAccessCount);
+						}
+					else
+						{
+						contactItems->AppendL(item);
+						CleanupStack::Pop(); // item
+						CleanupStack::PopAndDestroy(); // item close
+						}
+						
+					aImportSuccessful = ETrue;
+ 				}
+			}	
+		CleanupStack::PopAndDestroy();	// vCard
+		if (aImportSingleContact)
+			{
+			break;
+			}
+		}
+
+	if (!aImportSingleContact)
+		{
+		aDb.DatabaseCommitLP(EFalse);
+		}
+
+	CleanupStack::Pop();	// contactItems
+	CleanupStack::PopAndDestroy(); //converter
+__END_PROFILE(23);
+	return contactItems;
+	}
+
+
+/** 
+ * Import a vCard object
+ * 
+ * @param aConverter Import converter
+ * @param aVCard vCard object to import
+ * @param aDb Contacts database reference
+ * @param aOption Import preferences (available options defined in CContactDatabase::TOptions)
+ * @param aIncAccessCount Increment the access count of contact items imported
+ * @param aDecAccessCount Decrement the access count of contact items imported
+ * @param aImportSuccessful vCard object was imported successfully
+ * @param aContactItems Array of imported contact items
+ * @param aIdForUpdate  contact ID which is to be updated / replaced with the imported vCrad.
+ */
+void CContactVCardConverter::doImportL(CVCardToContactsAppConverter& aConverter, CParserVCard& aVCard, CContactDatabase& aDb, TInt aOption, TBool aIncAccessCount, TBool aDecAccessCount, TBool& aImportSuccessful, CArrayPtr<CContactItem>* aContactItems, TBool aIsInTransaction, TContactItemId aIdForUpdate)
+	{
+	CArrayPtr<CParserProperty>* agentPropertyList = NULL;
+	
+	agentPropertyList = aVCard.PropertyL(KVersitTokenAGENT, TUid::Uid(KVCardPropertyAgentUid));
+	CleanupStack::PushL(TCleanupItem(CleanUpResetAndDestroy, agentPropertyList));
+
+	CContactItem* mainItem = aConverter.GetVCardAsContactItemLC(aVCard, CVCardToContactsAppConverter::EPreserveAllProperties, aOption);
+
+	if (agentPropertyList && agentPropertyList->Count())
+		{
+		//Add agents to contact item and if needed, merge agentcards with existing contacts
+		HandleAgentsInVCardL(aConverter, agentPropertyList, *mainItem, aDb, aOption, aIncAccessCount, aDecAccessCount, aContactItems, EFalse);
+		}
+	
+	if	(mainItem->CardFields().Count()) // checks vcard is not empty
+		{
+		ModifyAccessCountL(*mainItem, aIncAccessCount, aDecAccessCount);
+		if	(aOption & ENullTemplateId)
+			{
+			mainItem->SetTemplateRefId(KNullContactId);
+			}
+		else
+			{
+			mainItem->SetTemplateRefId(KGoldenTemplateId);
+			}
+
+		//Check if we have been asked to replace (update) rather than add 
+		//the contact item.
+		if ((aOption & EReplaceIfExists) && (aIdForUpdate != KErrNotFound))
+			{
+			CContactItem* oldItem;
+			oldItem = aDb.OpenContactL(aIdForUpdate);
+			CleanupStack::PushL(oldItem);								
+			CContactItemFieldSet& fieldSet = oldItem->CardFields();			
+			fieldSet.Reset();			
+			aDb.CommitContactL(*oldItem);
+			CleanupStack::PopAndDestroy(oldItem);
+			CContactItem* updateItem = NULL;
+			updateItem = aDb.UpdateContactLC(aIdForUpdate, mainItem);
+			aContactItems->AppendL(updateItem); 
+			CleanupStack::Pop(updateItem);
+			CleanupStack::PopAndDestroy(mainItem);		
+			}
+		else
+			{			
+			aDb.doAddNewContactL(*mainItem, EFalse, aIsInTransaction);
+			aContactItems->AppendL(mainItem);
+			CleanupStack::Pop(mainItem);
+			}
+		aImportSuccessful = ETrue;
+		}
+	else
+		{
+		// Just cleanup
+		CleanupStack::PopAndDestroy(mainItem);
+		}
+	CleanupStack::PopAndDestroy(agentPropertyList);
+	}
+
+
+/** 
+ * Export a contact as vCard.
+ * 
+ * @param aDb Contact database
+ * @param aSelectedContactIds Array of contact items IDs to export
+ * @param aWriteStream Stream to externalize vCard data to 
+ * @param aOption Export preferences (available options defined in CContactDatabase::TOptions)
+ * @param aCharSet Default character set to pass to Versit parser component
+ * @param aExportPrivateFields Specify whether private fields are included
+ * @param aCommitNumber Number of contacts to be exported before commit of database
+ */
+void CContactVCardConverter::ExportL(CContactDatabase& aDb,const CContactIdArray& aSelectedContactIds,RWriteStream& aWriteStream,TInt aOption,const Versit::TVersitCharSet aCharSet, TBool aExportPrivateFields, TInt aCommitNumber=10)
+	{
+	const TBool increaseAccessCount=aOption & (EIncreaseAccessCount);
+	const TBool decreaseAccessCount=aOption & (EDecreaseAccessCount);
+	const TBool accessCountChanges=decreaseAccessCount || increaseAccessCount;
+	CContactsAppToVCardConverter* converter = new(ELeave)CContactsAppToVCardConverter(aDb.MachineId(), aCharSet, EVCard21);
+	CleanupStack::PushL(converter);
+
+	const TInt count=aSelectedContactIds.Count();
+	for (TInt ii=0; ii<count; ii++)
+		{
+		CContactItem* contactItem=NULL;
+		TContactItemId id=aSelectedContactIds[ii];
+		if (accessCountChanges)
+			{
+			if (ii % aCommitNumber == 0)	// 1st item of N
+				{
+				aDb.DatabaseBeginLC(EFalse);	
+				}
+			contactItem=aDb.OpenContactLX(id);
+			CleanupStack::PushL(contactItem);
+			}
+		else
+			{
+			contactItem=aDb.ReadContactLC(id,*aDb.AllFieldsView());
+			}
+		
+		CContactItemFieldSet& fields=contactItem->CardFields(); 
+		TInt agentPos = KContactFieldSetSearchAll; 
+
+		CContactItem* agentItem = NULL;
+		CArrayPtr<CContactItem>* agentItemArray = new(ELeave) CArrayPtrFlat<CContactItem>(4);
+		CleanupStack::PushL(TCleanupItem(CleanUpResetAndDestroy, agentItemArray));
+
+		while(1)
+			{
+			agentPos = fields.FindNext(KUidContactFieldVCardMapAGENT, agentPos);
+			TContactItemId agentId;
+			
+			if (agentPos != KErrNotFound)
+				{
+		  		agentId = fields[agentPos].AgentStorage()->Value();		
+			  	TRAPD(err, agentItem = aDb.ReadContactL(agentId, *aDb.AllFieldsView()));
+
+				if(err != KErrNotFound) 
+	 				{
+	 				User::LeaveIfError(err);
+					agentItemArray->AppendL(agentItem);
+				  	}
+				++agentPos;
+				agentItem = NULL;
+				}
+				
+			else
+				{
+				break;
+				}
+		}//while
+		CParserVCard* vCard=converter->GetContactItemAsVCardL(contactItem, agentItemArray, aOption, aExportPrivateFields);
+	
+ 		CleanupStack::PushL(vCard);
+		vCard->SetDefaultCharSet(aCharSet);
+		vCard->ExternalizeL(aWriteStream); 
+		CleanupStack::PopAndDestroy(vCard);
+
+		CleanupStack::PopAndDestroy(agentItemArray);
+			
+		if (accessCountChanges)
+			{
+			if (decreaseAccessCount)
+				{
+				contactItem->DecAccessCount();
+				}
+			if (increaseAccessCount)
+				{
+				contactItem->IncAccessCount();
+				}
+			aDb.doCommitContactL(*contactItem,ETrue,EFalse);	// commit every 10 by default
+			CleanupStack::PopAndDestroy(); // contactItem
+			CleanupStack::PopAndDestroy(); // contact close from OpenContactLX
+			// Nth item or last item
+			if (((ii + 1) % aCommitNumber == 0) || ii == (count - 1))
+				{
+				aDb.DatabaseCommitLP(EFalse);
+				}
+			}
+		else	// access count does not change
+			{
+			CleanupStack::PopAndDestroy(); // contactItem
+			}
+		}
+		
+	CleanupStack::PopAndDestroy(converter); 
+	}
+
+
+TBool CContactVCardConverter::ContainsExportableData(const TDesC& aText)
+/**  
+ * Two different export rules:
+ * 
+ * RULE 1: Single property values, e.g. TEL, LABEL, FN, NOTE etc etc
+ * For this type of property value, whitespace is not exported to the PIM, i.e this property
+ * value should not be sent to the PIM for merging.
+ * 
+ * RULE 2: Multi property values, e.g. ADR, N, ORG etc etc
+ * 
+ * a) NULL means that the field is not supported by the device. It does NOT mean that the
+ * field should be deleted from the PIM. Therefore, when a NULL value is sent by the device to the PC
+ * the existing PC-PIM contact field should be preserved.
+ * 
+ * b) a SINGLE SPACE means that the field is supported by the device, but currently has an empty value (i.e.
+ * the field has been deleted or is currently empty).
+ * 
+ * =====> This implementation only current obeys RULE1.
+ * 
+ * @param aText The text to be analyzed
+ */
+	{
+	return ContainsData(aText);
+	}
+
+TBool CContactVCardConverter::ContainsImportableData(const TDesC& aText, TVersitPropertyType aType, TCntVCardImportType aImportType)
+/**
+ * Two different import rules:
+ * 
+ * RULE 1: Single property values, e.g. TEL, LABEL, FN, NOTE etc etc
+ * For this type of property value, a NULL value (i.e aText.Length() == 0) means that
+ * the specified field should be deleted from the contact card. Any whitespace will be
+ * ignored and not imported into the Contacts Database.
+ * 
+ * RULE 2: Multi property values, e.g. ADR, N, ORG etc etc
+ * PC-based PIM's have varying support for the different sub-fields within a multi-property
+ * object. Therefore, in a multi-property value object, the following semantics are observed
+ * 
+ * a) NULL means that the field is not supported by the PIM software. It does NOT mean that the
+ * field should be deleted. Therefore, when a NULL value is sent by the PC Sync Engine to the
+ * device no changes should be made to the corresponding field in the contact card.
+ * 
+ * b) a SINGLE SPACE means that the field is supported by the PC PIM, but has an empty value (i.e.
+ * the field has been deleted or is currently empty).
+ * 
+ * A further complication is that during a non-merge sync, its entirely possible for the PC to
+ * send vCard data which is effectively empty. One such example is TimeIS data extracted from
+ * MS Outlook which then sent to the device. In this case, even if the PIM shows an address
+ * as being empty, TimeIS sync drivers still send this empty address, e.g.:
+ * 
+ * ADR;HOME: ;; ; ; ; ; 
+ * 
+ * In this instance, we must ensure that we do not import a completely empty property. Therefore
+ * the SPACE/NULL syntax is only used explicitly during a MERGE operation. An INITIAL SYNC
+ * doesn't need to understand the SPACE/NULL syntax.
+ * 
+ * =====> This implementation obeys both rules completely.
+ * @param aText The text to be analyzed.
+ * @param aType Specify if the property is a single or multi-fielded type
+ * @param aImportType Specify if this is the first time the card is being imported or if its a merge.
+*/
+	{
+	TBool validDataForImport = EFalse;
+ 
+	const TInt length = aText.Length();
+	if	(aType == EPropertyValueComposite)
+		{
+		if	(aImportType == ECntVCardImportTypeFirstSync)
+			{
+			if	(!length || (length == 1 && aText[0] == KContactVCardSpaceCharacter))
+				{
+				// We do not import either
+				//
+				// - empty (but supported) PIM fields 
+				// - empty (non supported) PIM fields
+				//
+				// during initial sync since these would lead to fields in the contact card
+				// which contain spaces.
+				validDataForImport = EFalse;
+				}
+			else
+				{
+				// If its non-whitespace, we'll allow it for import
+				validDataForImport = ContainsData(aText);
+				}
+			}
+		else if (aImportType == ECntVCardImportTypeMerge)
+			{
+			if	(!length)
+				{
+				// Don't process (i.e. delete) fields which are not supported by the PC PIM.
+				validDataForImport = EFalse;
+				}
+			else if	(length == 1 && aText[0] == KContactVCardSpaceCharacter)
+				{
+				// This field is empty in the PIM and therefore we must 'import it' (which in fact,
+				// means we must delete the corresponding device-side field).
+				validDataForImport = ETrue;
+				}
+			else 
+				{
+				// Otherwise check each character to weed out the whitespace
+				validDataForImport = ContainsData(aText);
+				}
+			}
+		else
+			Panic(ECntVPanicInvalidImportType);
+		}
+	else if (aType == EPropertyValueSingle)
+		{
+		// We must import (process) NULL values, since they effectively mean 'DELETE'
+		// We do not import fields containing only whitespace
+		validDataForImport = (!length || ContainsData(aText));
+		}
+	return validDataForImport;
+	}
+
+
+TBool CContactVCardConverter::ContainsData(const TDesC& aText)
+/**
+ * Determine if the text contains any data that can be imported
+ * @param aText The text to be analyzed.
+*/
+	{
+	const TInt length = aText.Length();
+	TInt whiteSpaceCount = 0;
+	TChar character;
+
+	for(TInt i=0; i<length; i++)
+		{
+		// If the character is whitespace, then check the next character until
+		// all characters have been reached. If its not-whitespace, then this field
+		// contains data which can be exported.
+		character = aText[i];
+		if	(character.IsSpace())
+			{
+			++whiteSpaceCount;
+			}
+		else
+			{
+			return ETrue;
+			}
+		}
+	return (whiteSpaceCount < length);
+	}
+
+CContactVCardConverter* CContactVCardConverter::NewL()
+	{
+	return new(ELeave) CContactVCardConverter();
+	}
+	
+// Export the implementation collection function
+const TImplementationProxy ImplementationTable[] = 
+    {
+    IMPLEMENTATION_PROXY_ENTRY(0x102035F9, 	CContactVCardConverter::NewL),
+    IMPLEMENTATION_PROXY_ENTRY(0xA00015C1, 	CPBAPContactVCardConverter::NewL)
+    };
+
+EXPORT_C const TImplementationProxy* ImplementationGroupProxy(TInt& aTableCount)
+    {
+    aTableCount = sizeof(ImplementationTable) / sizeof(TImplementationProxy);
+
+    return ImplementationTable;
+    }
+    
+void CPBAPContactVCardConverter::ExportL(CContactDatabase& aDb, const CContactIdArray& aSelectedContactIds, RWriteStream& aWriteStream, TInt aOption, const Versit::TVersitCharSet aCharSet, TBool aExportPrivateFields, TInt /*aCommitNumber = 10*/)
+	{
+	CContactsAppToVCardConverter* converter = new(ELeave)CContactsAppToVCardConverter(aDb.MachineId(), aCharSet, GetVersion());
+	CleanupStack::PushL(converter);
+
+	const TInt count = aSelectedContactIds.Count();
+
+	converter->SetFilter(PrepareFilterAndOption(aOption));
+	
+	for (TInt ii = 0; ii < count; ii++)
+		{
+		CContactItem* contactItem = NULL;
+		TContactItemId id = aSelectedContactIds[ii];
+		contactItem = aDb.ReadContactLC(id, *aDb.AllFieldsView());
+	
+		CParserVCard* vCard = converter->GetContactItemAsVCardL(contactItem, NULL, aOption, aExportPrivateFields);
+ 		CleanupStack::PushL(vCard);
+		vCard->SetDefaultCharSet(aCharSet);
+		//Intra-Contact Properties start
+		MConverterCallBack* clientCallback = GetCallback();
+		if(clientCallback)
+			{
+			CArrayPtr<CParserProperty>* intraPropertyList = NULL;
+			intraPropertyList = new(ELeave) CArrayPtrFlat<CParserProperty>(5);
+			CleanupStack::PushL(TCleanupItem(CVersitParser::ResetAndDestroyArrayOfProperties, intraPropertyList));
+			clientCallback->AddIntraContactPropertiesL(id, intraPropertyList);	
+			TInt count = intraPropertyList->Count();
+			for(TInt loop = 0; loop < count; ++loop)
+				{
+				CParserProperty* parserProperty = (*intraPropertyList)[loop];
+				(*intraPropertyList)[loop] = NULL;
+				//AddpropertyL takes ownership
+				vCard->AddPropertyL(parserProperty, ETrue);
+				}
+			CleanupStack::PopAndDestroy(intraPropertyList);
+			}
+		//Intra-Contact Properties ends
+		vCard->ExternalizeL(aWriteStream);
+		CleanupStack::PopAndDestroy(vCard); 
+		CleanupStack::PopAndDestroy(); // contactItem
+		}
+	CleanupStack::PopAndDestroy(converter); 
+	}
+
+TInt64 CPBAPContactVCardConverter::PrepareFilterAndOption(TInt& aOption)
+	{
+	//These fields are not required for PBAP
+	if(aOption & CContactVCardConverter::EIncludeX)
+		{
+		aOption ^= CContactVCardConverter::EIncludeX;
+		}
+	if(aOption & CContactVCardConverter::ETTFormat) 
+		{
+		aOption ^= CContactVCardConverter::ETTFormat;
+		}
+	if(aOption & CContactVCardConverter::EConnectWhitespace)
+		{
+		aOption ^= CContactVCardConverter::EConnectWhitespace;
+		}
+		
+	//Filter starts
+	TInt64 pbapFilter = GetFilter();
+	
+	//If bit filter is unset (0x0000000), then export all supported fields.
+	//0xFFFFFFF sets all the property bit fields specified by PBAP spec.
+	if(pbapFilter == 0)
+		{
+		pbapFilter = EAllProperties;
+		}
+	//if iExportTel is TRUE then TEL will be exported (even if empty)
+	if(IsExportTel())
+		{
+		pbapFilter |= EPropertyTEL;
+		}
+	//if iExportTel is FALSE then TEL will not be exported.
+	else
+		{ 
+		if(pbapFilter & EPropertyTEL)
+			{
+			pbapFilter ^= EPropertyTEL;
+			}
+		}			
+	pbapFilter |= EPropertyN; // Mandatory for both 2.1 and 3.0
+	//Export of FN property is mandatory for vCard 3.0 but for vCard 2.1 only if provided in filter.
+	if(GetVersion() == EPBAPVCard30) 
+		{
+		pbapFilter |= EPropertyFN;
+		//these properties are not supported for vCard3.0
+		if(pbapFilter & EPropertyAGENT)
+			{
+			pbapFilter ^= EPropertyAGENT;
+			}
+		if(pbapFilter & EPropertyTZ)
+			{
+			pbapFilter ^= EPropertyTZ;
+			}
+		if(pbapFilter & EPropertyGEO)
+			{
+			pbapFilter ^= EPropertyGEO;
+			}
+		if(pbapFilter & EPropertySOUND)
+			{
+			pbapFilter ^= EPropertySOUND;
+			}
+		if(pbapFilter & EPropertyCLASS)
+			{
+			pbapFilter ^= EPropertyCLASS;
+			}																			
+		}
+	//If UID is not in filter, it should not be exported,
+	if(!(pbapFilter & EPropertyUID))
+		{
+		aOption |= CContactVCardConverter::EExcludeUid;
+		}
+	return pbapFilter;
+	}
+
+CArrayPtr<CContactItem>* CPBAPContactVCardConverter::ImportL(CContactDatabase& /*aDb*/, RReadStream& /*aReadStream*/, TBool& /*aImportSuccessful*/, TInt /*aOption*/, TBool /*aImportSingleContact*/)
+	{
+	User::Leave(KErrNotSupported);
+	return NULL;
+	}
+
+CPBAPContactVCardConverter::CPBAPContactVCardConverter(TInt64 aFilter, MConverterCallBack* aCallback, TVCardVersion aVersion, TBool aExportTel):
+	iFilter(aFilter),
+	iCallback(aCallback),
+	iVersion(aVersion),
+	iExportTel(aExportTel)
+	{
+	}
+
+CPBAPContactVCardConverter* CPBAPContactVCardConverter::NewL(TAny* param)
+	{
+	TPluginParameters* ptr = static_cast<TPluginParameters*>(param);
+	CPBAPContactVCardConverter* self = new(ELeave) CPBAPContactVCardConverter(ptr->GetFilter(), ptr->GetCallback(), ptr->GetExportVersion(), ptr->IsExportTel());
+	return self;
+	}
+	
+TInt64 CPBAPContactVCardConverter::GetFilter()const
+	{
+	return iFilter;
+	}
+
+MConverterCallBack* CPBAPContactVCardConverter::GetCallback()const
+	{
+	return iCallback;
+	}
+
+TVCardVersion CPBAPContactVCardConverter::GetVersion ()const
+	{
+	return iVersion;
+	}
+
+TBool CPBAPContactVCardConverter::IsExportTel()const
+	{
+	return iExportTel;
+	}
+
+/** 
+ * Checks if an incoming vCard should be merged to an existing contact in database or added as a new contact.
+ * 
+ * @param aConverter Import converter
+ * @param aVCard vCard object of contact being imported. 
+ * @param aDb Contacts database reference
+ * @param aOption Import preferences (available options defined in CContactDatabase::TOptions)
+ * @return  If incoming vCard has to be merged then contactItem Id of a contact, otherwise KErrNotFound.
+ */
+TContactItemId CContactVCardConverter::IsVCardMergeNeededL(CVCardToContactsAppConverter& aConverter, CParserVCard& aVCard, CContactDatabase& aDb, TInt aOption)
+	{
+	TContactItemId pos = KErrNotFound;
+	CContactItem* contact = NULL;
+	TBuf<KUidStringLength> uidString;
+
+	//
+	// Have we been asked to ignore the UID property value?  If not,
+	// we must extract the contact ID from the UID.  Otherwise, assume
+	// that client which provided the EIgnoreUid option knows that this
+	// contact does not already exist in the database.
+	//
+
+	if (!(aOption & (EIgnoreUid)))
+		{
+		aConverter.GetVCardUidStringL(aVCard, uidString);	    				
+
+		//
+		// Is there a UID property value?  If so, check whether or not
+		// there is a Contact Item with the associated UID already
+		// present in the database.  If there is, the Contact Item will
+		// be updated rather than added.
+		//
+	
+		if (uidString.Length())
+			{
+			//
+			// Parse the UID property value to see if the Machine ID
+			// field matches this database's Machine ID (i.e. this vCard
+			// was originally created in this database and is being
+			// updated).  If the Machine IDs match then return the
+			// Contact ID field from the UID property value.
+			//
+
+			pos = ContactGuid::IsLocalContactUidString(uidString, aDb.MachineId());
+			//vCard was created from a contact in current database.
+			if (pos != KErrNotFound)
+				{
+				TRAPD(err, contact = aDb.ReadContactL(pos));
+				if (err == KErrNotFound)
+					{
+					pos = KErrNotFound;	
+					}
+				else
+					{
+					User::LeaveIfError(err);					
+					if (contact->IsDeleted())
+						{
+						//we dont want to update this contact as it has already been deleted.
+						//so assign it a value other than KErrNotFound, this will avoid execution of "if" condition below
+						//as we dont have to make use of ContactIdByGuidL since contact already exists in database.
+						pos = KErrGeneral;
+						}
+					delete contact;
+					}
+				}
+			//No Positon found in UID, so check using Guid of VCard.
+			if (pos == KErrNotFound)
+				{
+				//
+				// If the Contact item represented by ID extracted from the UID
+				// does not exist, then this vCard was either created elsewhere or the original
+				// contact has been deleted from the database.
+				// (Contacts Model on another device, PC application, etc).
+				// In this case we need to attempt to find the "foreign" UID
+				// in the database since it may have been imported
+				// previously and if so should be updated.
+				//
+
+				pos = aDb.ContactIdByGuidL(uidString);
+				if (pos != KNullContactId)
+					{
+					TRAPD(err, contact = aDb.ReadContactL(pos));
+					if (err != KErrNotFound)	
+						{
+						User::LeaveIfError(err);
+						if (contact->IsDeleted())
+							{
+							pos = KErrNotFound;
+							}
+						}
+					delete contact;
+					}
+				}						
+			}
+		}
+		
+		if (pos == KErrGeneral)
+			{
+			pos = KErrNotFound;	
+			}
+	return pos;
+	}
+
+/** 
+ * Modifies access count of any contact.
+ * 
+ * @param aContact Contact item whose access count has to be modified.
+ * @param aIncAccessCount Increment the access count of contact items imported
+ * @param aDecAccessCount Decrement the access count of contact items imported
+ */
+void CContactVCardConverter::ModifyAccessCountL(CContactItem& aContact, TBool aIncAccessCount, TBool aDecAccessCount)
+	{				
+	if (aIncAccessCount)
+		{
+		aContact.IncAccessCount();
+		}
+	if (aDecAccessCount)
+		{
+		aContact.DecAccessCount();
+		}
+	}
+
+/** 
+ * Imports one or more agent vCard objects present in the vCard.
+ * 
+ * @param aConverter Import converter
+ * @param aAgentProperties Array of agent properties extracted from parent vCard object.
+ * @param aContact Contact item to which the agents will be added.
+ * @param aDb Contacts database reference
+ * @param aOption Import preferences (available options defined in CContactDatabase::TOptions)
+ * @param aIncAccessCount Increment the access count of contact items imported
+ * @param aDecAccessCount Decrement the access count of contact items imported
+ * @param aImportSuccessful vCard object was imported successfully
+ * @param aContactItems Array of imported contact items
+ * @param aMerge  aContact is being merged or added as a new contact.
+ */
+void CContactVCardConverter::HandleAgentsInVCardL(CVCardToContactsAppConverter& aConverter, CArrayPtr<CParserProperty>* aAgentProperties, CContactItem& aContact, CContactDatabase& aDb, TInt aOption, TBool aIncAccessCount, TBool aDecAccessCount, CArrayPtr<CContactItem>* aContactItemArray, TBool aMerge)
+	{
+	CContactItem* contact = NULL;
+	CContactItem* agentItem = NULL;
+	
+	__ASSERT_DEBUG(aAgentProperties->Count() != 0, User::Leave(KErrArgument));
+
+	RArray <TContactItemId> contactIdArray;
+	CleanupClosePushL(contactIdArray);
+
+	TContactItemId pos = KErrNotFound;
+	const TInt count = aAgentProperties->Count();
+	for(TInt loop = 0;loop < count;++loop)
+		{
+		CParserVCard* agentcard = static_cast<CParserPropertyValueAgent*>((*aAgentProperties)[loop]->Value())->Value();
+		pos = IsVCardMergeNeededL(aConverter, *agentcard, aDb, aOption);
+		if(pos != KErrNotFound) //contact similar to Agent exists in database, so merge both.
+			{
+			contact = aDb.OpenContactL(pos); 
+			CleanupStack::PushL(contact);
+			TBool deleteItem = aConverter.MergeVCardWithContactItemL(*contact, *agentcard, CVCardToContactsAppConverter::EPreserveAllProperties, aOption);
+			ModifyAccessCountL(*contact, aIncAccessCount, aDecAccessCount);
+			aDb.CommitContactL(*contact);
+			CleanupStack::PopAndDestroy(contact);				
+			if(deleteItem)
+				{
+				aDb.DeleteContactL(pos);	
+				}				
+			}
+		else //Agent present in vCard but should be added as a new contact
+			{
+			agentItem = aConverter.GetVCardAsContactItemLC(*agentcard, CVCardToContactsAppConverter::EPreserveAllProperties, aOption);
+			ModifyAccessCountL(*agentItem, aIncAccessCount, aDecAccessCount);
+			agentItem->SetLastModified(aContact.LastModified());
+			pos = aDb.doAddNewContactL(*agentItem, EFalse, ETrue);
+			aContactItemArray->AppendL(agentItem);
+			CleanupStack::Pop(agentItem);
+			}	
+		contactIdArray.Append(pos);
+		}
+		
+	
+	const TInt idCount = contactIdArray.Count();
+	if (idCount)
+		{
+		for(TInt idLoop = 0;idLoop < idCount;++idLoop)	
+			{
+			// Include agendid in a field in maincontact
+			CContactItemField* field = CContactItemField::NewLC(KStorageTypeContactItemId);
+			field->SetMapping(KUidContactFieldVCardMapAGENT);
+			field->AgentStorage()->SetAgentId(contactIdArray[idLoop]);
+			if (aMerge)
+				{
+				aContact.CardFields().UpdateFieldSyncL(*field, idLoop+1);
+				CleanupStack::PopAndDestroy(); // field
+				}
+			else
+				{
+				aContact.AddFieldL(*field);				
+				CleanupStack::Pop(); // takes ownership of field.			
+				}
+			}
+		}
+	CleanupStack::PopAndDestroy(&contactIdArray);
+	}
+ 
+    
+