/*
* 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 "cntvcardutils.h"

// System includes
#include <badesca.h>
#include <conlist.h>
#include <s32mem.h> 


// User includes
#include <cntfldst.h>
#include <cntfield.h>
#include <cntdef.h>
#include <cntitem.h>


// Constants
const TInt KVCardImportAddressArrayGranularity = 4;
const TInt KContactGivenName = 1;
const TInt KContactFamilyName = 0;
const TInt KContactAdditionalName = 2;
const TInt KContactPrefixName = 3;
const TInt KContactSuffixName = 4;
const TInt KContactPostOffice = 0;
const TInt KContactExtendedAddress = 1;
const TInt KContactAddress = 2;
const TInt KContactLocality = 3;
const TInt KContactRegion = 4;
const TInt KContactPostcode = 5;
const TInt KContactCountry = 6;

#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS
const TInt KContactMaxFieldNumber = 32;
#endif

/**
 * Delete name fields from a contact
 *
 * @param aContact Contact item
 */
void CVCardToContactsAppConverter::DeleteNameFields(CContactItem& aContact)
	{
	const CContactItemFieldSet& fieldSet = aContact.CardFields();
	//
	DeleteField(aContact, fieldSet, KUidContactFieldGivenName, KUidContactFieldVCardMapUnusedN);
	DeleteField(aContact, fieldSet, KUidContactFieldFamilyName, KUidContactFieldVCardMapUnusedN);
	DeleteField(aContact, fieldSet, KUidContactFieldAdditionalName, KUidContactFieldVCardMapUnusedN);
	DeleteField(aContact, fieldSet, KUidContactFieldPrefixName, KUidContactFieldVCardMapUnusedN);
	DeleteField(aContact, fieldSet, KUidContactFieldSuffixName, KUidContactFieldVCardMapUnusedN);
	}


/**
 * Delete a specific field from the contact card
 *
 * @param aContact The contact
 * @param aFieldSet The contact's field set
 * @param aFieldType The type of field to delete from the field set
 * @param aMapping The additional mapping which the field must contain for it to be deleted
 */
void CVCardToContactsAppConverter::DeleteField(CContactItem& aContact, const CContactItemFieldSet& aFieldSet, TFieldType aFieldType, TUid aMapping)
	{
	const TInt pos = aFieldSet.Find(aFieldType, aMapping);
	if	(pos != KErrNotFound)
		aContact.RemoveField(pos);
	}


void CVCardToContactsAppConverter::MergeSpecifiedNameFieldL(CContactItem& aContact, TFieldType aFieldType, CVCardItemAndLabel& aNames, TInt aOption, TInt aNameIndex)
/**
 * Merge specific name fields from a contact
 *
 * @param aContact Contact item to add fields to
 * @param aFieldType Field type of field to add (TFieldType)
 * @param aNames An object containing the name and labels for 'N' property fields
 * @param aOption Import preferences (available options defined in CContactDatabase::TOptions)
 * @param aNameIndex Index into aNames
 */
	{
	CContactItemFieldSet& oldfieldset=aContact.CardFields();
	const TInt pos = oldfieldset.Find(aFieldType, KUidContactFieldVCardMapUnusedN);
	const TBool processWhitespace = (aOption & CContactVCardConverter::EConnectWhitespace);

	// First check whether the field is present in the contact card
	// Also verify that the array of address sub-values actually contains a specific
	// value for the requested index.
	if (aNames.ItemCount() > aNameIndex)
		{
		const TPtrC pValue = aNames.Item(aNameIndex);
		const TInt length = pValue.Length();
		if (processWhitespace)
			{
			TBool isSingleSpace = EFalse;
			if (length == 1)
				{
				isSingleSpace = (pValue[0] == KContactVCardSpaceCharacter);
				}
			if	((pos != KErrNotFound) && (length || isSingleSpace))
				{
				// This means the PC side field is empty, so delete the corresponding device-side field.
				aContact.RemoveField(pos);
				}
			if	(length && !isSingleSpace)
				{
				// This means the PC side field is unsupported, so ignore the corresponding contents.
				TInt insertPos = 0;
				SetNameFieldL(aNames, aContact, aOption, aNameIndex, aFieldType, insertPos);
				}
			}
		else
			{
			if (pos != KErrNotFound)
				{
				// This means the PC side field is empty, so delete the corresponding device-side field.
				aContact.RemoveField(pos);
				}
			if (length)
				{
				// This means the PC side field is not empty, so add the corresponding contents.
				TInt insertPos = 0;
				SetNameFieldL(aNames, aContact, aOption, aNameIndex, aFieldType, insertPos);
				}
			}
		}
	}


void CVCardToContactsAppConverter::MergeNameFieldsL(CContactItem& aContact, CVCardItemAndLabel& aNames, TInt aOption, TBool aTreatAsPrn)
/**
 * Merge name fields from a contact
 *
 * @param aContact Contact item to add fields to
 * @param aNames An object containing the name and labels for 'N' property fields
 * @param aOption Import preferences (available options defined in CContactDatabase::TOptions)
 */
	{
	if(aTreatAsPrn)
		{
		MergeSpecifiedNameFieldL(aContact, KUidContactFieldGivenNamePronunciation,	aNames, aOption, KContactGivenName);
		MergeSpecifiedNameFieldL(aContact, KUidContactFieldFamilyNamePronunciation,	aNames, aOption, KContactFamilyName);
		}
	else
		{
		MergeSpecifiedNameFieldL(aContact, KUidContactFieldPrefixName,		aNames, aOption, KContactPrefixName);
		MergeSpecifiedNameFieldL(aContact, KUidContactFieldGivenName,		aNames, aOption, KContactGivenName);
		MergeSpecifiedNameFieldL(aContact, KUidContactFieldAdditionalName,	aNames, aOption, KContactAdditionalName);
		MergeSpecifiedNameFieldL(aContact, KUidContactFieldFamilyName,		aNames, aOption, KContactFamilyName);
		MergeSpecifiedNameFieldL(aContact, KUidContactFieldSuffixName,		aNames, aOption, KContactSuffixName);
		}
	}


void CVCardToContactsAppConverter::MergeSpecifiedAddressFieldL(CContactItem& aContact, const CVCardAddress& aAddress, const TUid& aFieldUid, const TUid& aMappingUid, TInt aAddressIndex, TInt aOption)
/**
 * Merge specific address fields from a contact
 *
 * @param aContact Contact item to add fields to
 * @param aUid The Uid of the contact
 * @param aFieldUid Contacts database Field ID
 * @param aMappingUid VCard Mapping ID
 * @param aAddress An object containing the name and labels for 'ADR' property fields
 * @param aAddressIndex Index into aAddress
 * @param aOption Import preferences (available options defined in CContactDatabase::TOptions)
 */
	{
	CContactItemFieldSet& oldfieldset = aContact.CardFields();
	TInt pos = 0;
	const TBool processWhitespace = (aOption & CContactVCardConverter::EConnectWhitespace);

	if (aAddress.Mapping() == KNullUid)
		{
		TBool fieldFound = EFalse;
		TInt startPos = KContactFieldSetSearchAll;

		while(!fieldFound && !(pos == KErrNotFound))
			{
			pos = oldfieldset.FindNext(aMappingUid, startPos);
			startPos = pos + 1;
			if (pos != KErrNotFound )
				{
				CContactItemField& tempField = oldfieldset[pos];
				const CContentType& tempContentType = tempField.ContentType();
				TBool additionalMapFound = EFalse;
				additionalMapFound |= tempContentType.ContainsFieldType(KUidContactFieldVCardMapHOME);
				additionalMapFound |= tempContentType.ContainsFieldType(KUidContactFieldVCardMapWORK);
				additionalMapFound |= tempContentType.ContainsFieldType(KUidContactFieldVCardMapPREF);
				if (!additionalMapFound)
					{
					fieldFound = ETrue;
					}
				}
			}
		}
	else
		{
		pos = oldfieldset.Find(aAddress.Mapping(), aMappingUid);
		}

	// First check whether the field is present in the contact card
	// Also verify that the array of address sub-values actually contains a specific
	// value for the requested index.
	if (aAddress.ItemCount() > aAddressIndex)
		{
		const TPtrC pValue = aAddress.Item(aAddressIndex);
		const TInt length = pValue.Length();
		if (processWhitespace)
			{
			TBool isSingleSpace = EFalse;
			if (length == 1)
				{
				isSingleSpace = (pValue[0] == KContactVCardSpaceCharacter);
				}
			if	((length || isSingleSpace) && (pos != KErrNotFound))
				{
				// This means the PC side field is empty, so delete the corresponding device-side field.
				aContact.RemoveField(pos);
				}
			if	(length && !isSingleSpace)
				{
				TInt insertPos = 0;
				SetAddressFieldL(aAddress, aContact, aOption, aAddressIndex, aFieldUid, insertPos, aMappingUid);
				}
			}
		else
			{
			if (pos != KErrNotFound)
				{
				// This means the PC side field is empty, so delete the corresponding device-side field.
				aContact.RemoveField(pos);
				}
			if (length)
				{
				// This means the PC side field is not empty, so add the corresponding contents.
				TInt insertPos = 0;
				SetAddressFieldL(aAddress, aContact, aOption, aAddressIndex, aFieldUid, insertPos, aMappingUid);
				}
			}
		}
	}


void CVCardToContactsAppConverter::MergeAddressFieldsL(CContactItem& aContact, const CVCardAddress& aAddress, TInt aOption)
/**
 * Merge a specific field from the contact card
 *
 * @param aContact Contact item to add fields to
 * @param aUid The Uid of the contact
 * @param aAddresses Address of locally stored contact
 * @param aOption Import preferences (available options defined in CContactDatabase::TOptions)
 */
	{
	MergeSpecifiedAddressFieldL(aContact, aAddress, KUidContactFieldPostOffice, KUidContactFieldVCardMapPOSTOFFICE, KContactPostOffice, aOption);
	MergeSpecifiedAddressFieldL(aContact, aAddress, KUidContactFieldExtendedAddress, KUidContactFieldVCardMapEXTENDEDADR, KContactExtendedAddress, aOption);
	MergeSpecifiedAddressFieldL(aContact, aAddress, KUidContactFieldAddress, KUidContactFieldVCardMapADR, KContactAddress, aOption);
	MergeSpecifiedAddressFieldL(aContact, aAddress, KUidContactFieldLocality, KUidContactFieldVCardMapLOCALITY, KContactLocality, aOption);
	MergeSpecifiedAddressFieldL(aContact, aAddress, KUidContactFieldRegion, KUidContactFieldVCardMapREGION, KContactRegion, aOption);
	MergeSpecifiedAddressFieldL(aContact, aAddress, KUidContactFieldPostcode, KUidContactFieldVCardMapPOSTCODE, KContactPostcode, aOption);
	MergeSpecifiedAddressFieldL(aContact, aAddress, KUidContactFieldCountry, KUidContactFieldVCardMapCOUNTRY, KContactCountry, aOption);
	}


TBool CVCardToContactsAppConverter::MergeVCardWithContactItemL(CContactItem &aContact, CParserVCard& aVCard, TUnknownPropertyBehaviour aUnknownPropertyBehaviour, TInt aOption)
/**
 * Merge a vCard with an existing contact item
 *
 * @param aContact Contact item to add fields to
 * @param aVCard vCard parser object
 * @param aUnknownPropertyBehaviour Specifies how extension properties are handled
 * @param aOption Import preferences (available options defined in CContactDatabase::TOptions)
 * @return ETrue if contact item should be deleted, EFalse otherwise
 */
	{
	TInt ii = 0;
	TInt count = 0;
	//
	TBool deleteContact = ETrue;
	iUnknownPropertyBehaviour = aUnknownPropertyBehaviour;
	TTime lastModified;
	GetVCardModifiedTimeL(aVCard, lastModified);

	// We're performing a merge
	SetImportType(ECntVCardImportTypeMerge);

	// Get Name
	CVCardItemAndLabel* names = GetContactNameLC(aVCard, aOption);
	if	(names && names->ItemCount())
		{
		MergeNameFieldsL(aContact, *names, aOption);
		deleteContact = EFalse;
		}
	CleanupStack::PopAndDestroy(names);
	// Get Name pronunciation
 	names = GetContactNamePrnLC(aVCard, aOption);
 	if (names && names->ItemCount())
 		{
 		MergeNameFieldsL(aContact, *names, aOption, ETrue);
 		}
 	CleanupStack::PopAndDestroy(names);

	// Create address container
	RPointerArray<CVCardAddress> addresses(KVCardImportAddressArrayGranularity);
	CleanupStack::PushL(TCleanupItem(CVCardItemAndLabel::CleanUpResetDestroyAndCloseArray, &addresses));

	// Get addresses from the vCard. This actually only retrieves four types of addresses:
	// HOME, WORK, PREF and now additionally, 'general' addresses
	GetAddressesL(aVCard, aOption, addresses);

	// Import each of the located address field into the contact card
	count = addresses.Count();
    for (ii=0; ii<count; ii++)
		{
		const CVCardAddress* address = addresses[ii];
		MergeAddressFieldsL(aContact, *address, aOption);
		deleteContact = EFalse;
		}

	// Finished with addresses now, so clean up
	CleanupStack::PopAndDestroy(&addresses);

	// Get Organization related information from the vCard. This actually only retrieves the Company and the Department Name
	CDesCArrayFlat* orgList = new (ELeave)CDesCArrayFlat(4);
	CleanupStack::PushL(orgList);
	TInt orgCount = GetVCardPropertyAsArrayOfValuesL(aVCard, KVersitTokenORG, *orgList);
	if(orgCount)
		{
		MergeOrgDetailsL(aContact,*orgList,aOption);
		deleteContact = EFalse;
		}
	CleanupStack::PopAndDestroy(orgList); // orgList
	
	// Get Single Instance of Class Field from the vCard.
	HBufC* singleClass = NULL;
	singleClass = HBufC::NewLC(256);
   	TPtr ptr(singleClass->Des());

	TInt classCount = GetSingleInstanceL(aVCard, KVersitTokenClass, ptr);
	if(classCount)
		{
		MergeSingleInstanceL(aContact,ptr,KUidContactFieldClass, KUidContactFieldVCardMapClass,aOption);
		deleteContact = EFalse;
		}
	CleanupStack::PopAndDestroy(singleClass);

	// Get other properties
	CArrayPtr<CParserProperty>* arrayOfProperties = aVCard.ArrayOfProperties();
	CleanupStack::PushL(TCleanupItem(CleanUpResetAndDestroy, arrayOfProperties));
	count = arrayOfProperties->Count();

	//Within this loop we store the content type of the last field updated.
	//If the content type of the current field matches, we update fieldCount
	//This holds the position of the field to update, to prevent us from
	//overwriting the field we just updated.

	TInt fieldCount = 0;
	CContactItemField* oldField = NULL;
	CContactItemField* newField = NULL;

	for (ii = 0; ii < count; ii++)
 	  	{
        if (((*arrayOfProperties)[ii]->Name() != KVersitTokenADR) && ((*arrayOfProperties)[ii]->Name() != KVersitTokenORG) && ((*arrayOfProperties)[ii]->Name() != KVersitTokenClass))
		 {
		  TBool unsupportedProperty=EFalse;
		 
			newField = GetVCardPropertyAsContactFieldLC((*arrayOfProperties)[ii], aOption,unsupportedProperty);
			if (newField)
				{
				TInt ttnumber;
				if (!oldField)
					{
					fieldCount = 1;
					ttnumber = 1;
					if (aOption & CContactVCardConverter::ETTFormat)
						{
						ttnumber = GetVCardPropertyTTNumber((*arrayOfProperties)[ii]);
						}
					}
				else if (newField->ContentType().IsEqualForSyncUpdate(oldField->ContentType()))
					{
					ttnumber = ++fieldCount;
					}
				else
					{
					fieldCount = 1;
					ttnumber = 1;
					if (aOption & CContactVCardConverter::ETTFormat)
						{
						ttnumber = GetVCardPropertyTTNumber((*arrayOfProperties)[ii]);
						}
					}
				if (oldField)
					{
					CleanupStack::Pop(); //newField
					CleanupStack::PopAndDestroy(oldField);
					oldField = NULL;
					CleanupStack::PushL(newField);
					}
				oldField = newField;
				aContact.CardFields().UpdateFieldSyncL(*newField, ttnumber);
				if(newField->Storage()->IsFull())
					{
					deleteContact = EFalse;
					}
				newField = NULL;	
				}
			else if (unsupportedProperty)
				{
				CleanupStack::Pop();
				deleteContact = EFalse;
				}
			else{
				CleanupStack::Pop();
				}
			}
    	}
    //Remove all empty fields after merge
    CContactItemFieldSet &fieldset = aContact.CardFields();
    TInt i = 0;
    for(; i < fieldset.Count(); ++i)
    	{
    	if( !fieldset[i].Storage()->IsFull() )
    		{
    		fieldset.Remove( i-- );
    		}
    	}

	if (newField)
		//coverity [dead_error_begin]
		{	
		CleanupStack::PopAndDestroy(newField);
		newField = NULL;
		}

	if (oldField)
		{
		CleanupStack::PopAndDestroy(oldField);
		oldField = NULL;
		}
	
	if (lastModified != Time::NullTTime())
		{
    	aContact.SetLastModified(lastModified);
		}
    CleanupStack::PopAndDestroy(arrayOfProperties);
	return(deleteContact);
	}
/**
 * Set a name field. Only creates a field if the specified name text is not empty.
 *
 * @param aNames An object containing the name and labels for 'N' property fields
 * @param aContact Contact item to add fields to
 * @param aOption Import preferences (available options defined in CContactDatabase::TOptions)
 * @param aIndex Array position of field to use
 * @param aFieldType Field type of field to add (TFieldType)
 * @param aInsertPos Position in contact field set to add field
*/
void CVCardToContactsAppConverter::SetNameFieldL(const CVCardItemAndLabel& aNames, CContactItem& aContact, TInt aOption, TInt aIndex, TFieldType aFieldType, TInt& aInsertPos) const
	{
	const TInt count = aNames.ItemCount();
	const TInt labelCount = aNames.LabelCount();
	//
	if	(aIndex >= 0 && aIndex < count)
		{
		const TPtrC pFieldText(aNames.Item(aIndex));
		TBool addField = EFalse;

		if (CContactVCardConverter::EConnectWhitespace & aOption)
			{
			addField = CContactVCardConverter::ContainsImportableData(pFieldText, CContactVCardConverter::EPropertyValueComposite, ImportType());
			}
		else
			{
			addField = (pFieldText.Length());
			}
		// Only add the field if it contains some data
		if	(addField)
			{
			CContactItemField* contactItemField = CContactItemField::NewLC(KStorageTypeText, aFieldType);
			contactItemField->SetMapping(KUidContactFieldVCardMapUnusedN);
			if ((aOption & CContactVCardConverter::EIncludeX) && (aIndex >= 0 && aIndex < labelCount))
				contactItemField->SetLabelL(aNames.Label(aIndex));
			//
			HBufC* encodedText = EncodeL(pFieldText, ETrue);
			contactItemField->TextStorage()->SetText(encodedText); // takes ownership
			
			aContact.InsertFieldL(*contactItemField, aInsertPos++);
			CleanupStack::Pop(contactItemField);
			}
		}
	}

HBufC* CVCardToContactsAppConverter::EncodeL(const TDesC& aText, TBool aTextTobeTruncated) const
	{
	const TUid KUidTextToEtextNoTrim={0x10281B4C};
	
	// Make a copy of aText and truncate if necessary.
	TPtr truncText(const_cast<TUint16*>(aText.Ptr()),aText.Length());
	
	if(aTextTobeTruncated)
		truncText.SetLength(aText.Length()>KCntMaxTextFieldLength ? KCntMaxTextFieldLength : aText.Length());
	else
		truncText.SetLength(aText.Length());
			
	HBufC8* text=HBufC8::NewLC(truncText.Length()*2);
	TPtr8 ptr = text->Des();
	TInt i;
	for (i=0; i < truncText.Length(); i++)
		{
		ptr.Append(truncText[i] & 0x00FF);
		ptr.Append((truncText[i] >> 8) & 0x00FF);
		}
	CCnaConverterList* convList=CCnaConverterList::NewLC(); 
	CConverterBase* conv = convList->NewConverterL(KUidTextToEtextNoTrim); 
	if (!conv)
		{
		CleanupStack::PopAndDestroy();          // convList 
		User::Leave(KErrNotSupported);
		}
	CleanupStack::PushL(conv);
	CBufFlat* decodeBuffer = CBufFlat::NewL(256); 
	CleanupStack::PushL(decodeBuffer);
	CBufFlat* encodedBuffer = CBufFlat::NewL(256); 
	CleanupStack::PushL(encodedBuffer);
	decodeBuffer->InsertL(0,ptr);
	RBufReadStream readStream;
	RBufWriteStream writeStream;
	readStream.Open(*decodeBuffer);
	writeStream.Open(*encodedBuffer);
	conv->ConvertObjectL(readStream, writeStream); 
	readStream.Close();
	TInt size=encodedBuffer->Size();
	HBufC* writeBuf=HBufC::NewLC(size); 
	TPtr resulttext = writeBuf->Des();
	for(i = 0; i < (size - 1); i += 2)
		{
		resulttext.Append((encodedBuffer->Ptr(0)[i + 1] << 8) | 
		encodedBuffer->Ptr(0)[i]);
		}

	writeStream.CommitL();
	writeStream.Close();
	CleanupStack::Pop(); // writebuf
	CleanupStack::PopAndDestroy(2); // buffers 
	CleanupStack::PopAndDestroy(2); //conv+convList 
	CleanupStack::PopAndDestroy();  //text
	return writeBuf;
	}

/**
 * Set name fields
 *
 * @param aNames An object containing the name and labels for 'N' property fields
 * @param aContact Contact item to add fields to
 * @param aOption Import preferences (available options defined in CContactDatabase::TOptions)
 * @param aTreatAsPronunciation If true, save name data in pronunciation fields, otherwise save as an actual name (the default)
 */
void CVCardToContactsAppConverter::SetNameFieldsL(const CVCardItemAndLabel& aNames, CContactItem& aContact, TInt aOption, TBool aTreatAsPronunciation) const
	{
	TInt insertPos=0;
	if( aTreatAsPronunciation )
		{
		SetNameFieldL(aNames, aContact, aOption, KContactGivenName, KUidContactFieldGivenNamePronunciation, insertPos);
		SetNameFieldL(aNames, aContact, aOption, KContactFamilyName, KUidContactFieldFamilyNamePronunciation, insertPos);
		}
	else
		{
		SetNameFieldL(aNames, aContact, aOption, KContactPrefixName, KUidContactFieldPrefixName, insertPos);
		SetNameFieldL(aNames, aContact, aOption, KContactGivenName, KUidContactFieldGivenName, insertPos);
		SetNameFieldL(aNames, aContact, aOption, KContactAdditionalName, KUidContactFieldAdditionalName, insertPos);
		SetNameFieldL(aNames, aContact, aOption, KContactFamilyName, KUidContactFieldFamilyName, insertPos);
		SetNameFieldL(aNames, aContact, aOption, KContactSuffixName, KUidContactFieldSuffixName, insertPos);
		}
	}



void CVCardToContactsAppConverter::SetAddressFieldL(const CVCardAddress& aAddress, CContactItem& aContact, TInt aOption, TInt aIndex, TFieldType aFieldType, TInt& aInsertPos, TUid aMapping) const
/**
 * Set an address field
 *
 * @param aAddress An object containing the name and labels for 'ADR' property fields
 * @param aContact Contact item to add fields to
 * @param aOption Import preferences (available options defined in CContactVCardConverter::TOptions)
 * @param aIndex Array position of field to use
 * @param aFieldType Contact field type for address field
 * @param aInsertPos Position in contact field set to add field
 * @param aMapping vCard field mapping (eg. KUidContactFieldVCardMapPOSTCODE)
 */
	{
	const TInt count = aAddress.ItemCount();
	if	(aIndex >= 0 && aIndex < count)
		{
		const TPtrC pFieldText(aAddress.Item(aIndex));
		TBool doInsert = ETrue; // By default we process the data, but if the TimeIS flag is set, then we need to check the content first.

		if (CContactVCardConverter::EConnectWhitespace & aOption)
			{
			doInsert = (CContactVCardConverter::ContainsImportableData(pFieldText, CContactVCardConverter::EPropertyValueComposite, ImportType()));
			}
		if (doInsert)
			{
			CContentType* content = CContentType::NewL(aFieldType, aMapping);
			CleanupStack::PushL(content);

			if	(aAddress.Mapping() != KNullUid) // KNullUid corresponds to general address
				{
				content->AddFieldTypeL(aAddress.Mapping());
				}

			CContactItemField* contactItemField = CContactItemField::NewLC(KStorageTypeText, *content);
			if ((aOption & CContactVCardConverter::EIncludeX) && (aIndex >= 0 && aIndex < aAddress.LabelCount()))
				{
				// get the correct label
				TBuf<25> labelName(KContactVarVCardXDashEPOCCNTMODEL);
				labelName.AppendFormat(KContactVarVCardLABELn, aIndex);
				TInt position=KErrNotFound;
				TInt err = aAddress.FindLabel(labelName, position);
				if	(err==KErrNone && position!=KErrNotFound)
					contactItemField->SetLabelL(aAddress.Label(position));
				}
			
			HBufC* encodedText = EncodeL(pFieldText, ETrue);
			contactItemField->TextStorage()->SetText(encodedText); // takes ownership
			
			// The contact takes ownership of the field.
			aContact.InsertFieldL(*contactItemField, aInsertPos++);
			CleanupStack::Pop(contactItemField);
			CleanupStack::PopAndDestroy(content);
			}
		}
	}


void CVCardToContactsAppConverter::SetAddressFieldsL(const CVCardAddress& aAddress, CContactItem& aContact, TInt aOption) const
/**
 * Set the address fields
 *
 * @param aAddressses An object containing the name and labels for 'ADR' property fields
 * @param aContact Contact item to add fields to
 * @param aOption Import preferences (available options defined in CContactDatabase::TOptions)
 */
	{
	TInt insertPos = 0;
	SetAddressFieldL(aAddress, aContact, aOption, KContactPostOffice, KUidContactFieldPostOffice, insertPos, KUidContactFieldVCardMapPOSTOFFICE);
	SetAddressFieldL(aAddress, aContact, aOption, KContactExtendedAddress, KUidContactFieldExtendedAddress, insertPos, KUidContactFieldVCardMapEXTENDEDADR);
	SetAddressFieldL(aAddress, aContact, aOption, KContactAddress, KUidContactFieldAddress, insertPos, KUidContactFieldVCardMapADR);
	SetAddressFieldL(aAddress, aContact, aOption, KContactLocality, KUidContactFieldLocality, insertPos, KUidContactFieldVCardMapLOCALITY);
	SetAddressFieldL(aAddress, aContact, aOption, KContactRegion, KUidContactFieldRegion, insertPos, KUidContactFieldVCardMapREGION);
	SetAddressFieldL(aAddress, aContact, aOption, KContactPostcode, KUidContactFieldPostcode, insertPos, KUidContactFieldVCardMapPOSTCODE);
	SetAddressFieldL(aAddress, aContact, aOption, KContactCountry, KUidContactFieldCountry, insertPos, KUidContactFieldVCardMapCOUNTRY);
	}


/**
 * Convert aVCard into a ContactItem. This method leaves a CContactItem instance on the cleanup stack upon exit.
 *
 * @param aVCard					vCard parser object
 * @param aAgentContact             Agent contact item. Note that this object is passed by reference but is not
 *                                  left on the cleanup stack upon exit from this function. Clients shoul re-Push
 *									this object if needed.
 * @param aUnknownPropertyBehaviour Specifies how extension properties are handled
 * @param aOption                   Import preferences (available options defined in CContactDatabase::TOptions)
 *
 * @return                          A CContactItem instance (on the cleanup stack) that has been constructed from
 *                                  the vCard data.
 */
CContactItem* CVCardToContactsAppConverter::GetVCardAsContactItemLC(CParserVCard& aVCard, TUnknownPropertyBehaviour aUnknownPropertyBehaviour, TInt aOption)
	{
	TInt ii=0;
	TInt count = 0;
	//
	iUnknownPropertyBehaviour = aUnknownPropertyBehaviour;
	TTime lastModified;
	TBuf<KUidStringLength> uidstring;
	GetVCardModifiedTimeL(aVCard, lastModified);
	GetVCardUidStringL(aVCard, uidstring);

	//
	CContactCard* mainContact = CContactCard::NewLC();
	//
	// We're performing an initial import
	SetImportType(ECntVCardImportTypeFirstSync);

	// Get Name
	CVCardItemAndLabel* names = GetContactNameLC(aVCard, aOption);
	if (names && names->ItemCount())
		SetNameFieldsL(*names, *mainContact, aOption);
	CleanupStack::PopAndDestroy(names);

	// Get Name pronunciation
	names = GetContactNamePrnLC(aVCard, aOption);
	if (names && names->ItemCount())
		{
		SetNameFieldsL(*names, *mainContact, aOption, ETrue);
		}
	CleanupStack::PopAndDestroy(names);

	// Create address container
	RPointerArray<CVCardAddress> addresses(KVCardImportAddressArrayGranularity);
	CleanupStack::PushL(TCleanupItem(CVCardItemAndLabel::CleanUpResetDestroyAndCloseArray, &addresses));

	// Get addresses
	GetAddressesL(aVCard, aOption, addresses);

	// Import each address field into the contact card
	count = addresses.Count();
    for (ii=0; ii<count; ii++)
		{
		const CVCardAddress* address = addresses[ii];
		SetAddressFieldsL(*address, *mainContact, aOption);
		}

	// Finished with addresses now, so clean up
	CleanupStack::PopAndDestroy(&addresses);

// Get Organization related information from the vCard. This actually only retrieves the Company and the Department Name
	CDesCArrayFlat* orgList = new (ELeave)CDesCArrayFlat(4);
	CleanupStack::PushL(orgList);
	TInt orgCount = GetVCardPropertyAsArrayOfValuesL(aVCard, KVersitTokenORG, *orgList);
	if(orgCount)
		{
		SetOrgDetailsL(*mainContact, *orgList, aOption);
		}
	CleanupStack::PopAndDestroy(orgList); // orgList

// Get Single Instance of Class Field from the vCard.
	HBufC* singleClass = NULL;
	singleClass = HBufC::NewLC(256);
	TPtr ptr(singleClass->Des());

	TInt classCount = GetSingleInstanceL(aVCard, KVersitTokenClass, ptr);
	if(classCount)
		{
		SetSingleInstanceL(*mainContact, ptr, KUidContactFieldClass, KUidContactFieldVCardMapClass,aOption);
		}
	CleanupStack::PopAndDestroy(singleClass);

	// Get other properties
    CArrayPtr<CParserProperty>* arrayOfProperties = aVCard.ArrayOfProperties();
	CleanupStack::PushL(TCleanupItem(CleanUpResetAndDestroy,arrayOfProperties));
	//
	if	(arrayOfProperties)
		{
		count = arrayOfProperties->Count();
		for (ii=0; ii<count; ii++)
			{
			CParserProperty* property = arrayOfProperties->At(ii);
			// Address fields are handled in the above sections
			if	((property->Name() != KVersitTokenADR) && (property->Name() != KVersitTokenORG) && (property->Name() != KVersitTokenClass))
				{
				// NOTE: This method can return a NULL object pushed onto the cleanup stack, so must
				// always be popped!
				TBool unsupportedProperty=EFalse;
				CContactItemField* field = GetVCardPropertyAsContactFieldLC(property, aOption,unsupportedProperty);
				if	(field)
					{
					mainContact->AddFieldL(*field);
					}
				CleanupStack::Pop(field);
				}
			}
		// Email and Tel properties should only have a single Pref parameter
		AdjustForPrefRule(*mainContact, KUidContactFieldEMail, KUidContactFieldVCardMapEMAILINTERNET);
		AdjustForPrefRule(*mainContact, KUidContactFieldPhoneNumber, KUidContactFieldVCardMapTEL);
		}
	//
    if	(lastModified != Time::NullTTime())
    	{
    	mainContact->SetLastModified(lastModified);
    	}
	//
	mainContact->SetUidStringL(uidstring);
	//
    CleanupStack::PopAndDestroy(); // arrayOfProperties->ResetAndDestroy()
	//
	return mainContact;
    }


/**
 * Extract the last modified date of the vCard.
 * If there is no 'REV' property the last modified time is returned as as NULL TTime value.
 *
 * @param aVCard vCard parser object
 * @param aLastModified Last modified time
 */
void CVCardToContactsAppConverter::GetVCardModifiedTimeL(CParserVCard& aVCard,TTime& aLastModified)
	{
	CArrayPtr<CParserProperty>* arrayOfRevisions=aVCard.PropertyL(KVersitTokenREV,TUid::Uid(KVersitPropertyDateTimeUid),EFalse);
	if (arrayOfRevisions && arrayOfRevisions->Count())
		{
		CleanupStack::PushL(arrayOfRevisions);
   		CParserPropertyValueDateTime* revision=(static_cast<CParserPropertyValueDateTime*>((*arrayOfRevisions)[0]->Value()));

   		aLastModified=revision->Value()->iDateTime;
		if (revision->Value()->iRelativeTime != TVersitDateTime::EIsUTC )
			// The REV property isn't in UTC time, so let's (try to) convert it
			{
			CArrayPtr<CParserProperty>* arrayOfTimeZones=aVCard.PropertyL(KVersitTokenTZ, TUid::Uid(KVersitPropertyTimeZoneUid),EFalse);
			if (arrayOfTimeZones && arrayOfTimeZones->Count())
				// If we have the TZ property, adjust the machine local timestamp.
				// If we don't have the TZ property, we'll just pretend that the REV
				// property was already a UTC value.
				{
				CleanupStack::PushL(arrayOfTimeZones);
				CParserPropertyValueTimeZone* timeZone =
						static_cast<CParserPropertyValueTimeZone*> ((*arrayOfTimeZones)[0]->Value());
				// Subtract the offset: UTC + Offset = Local => UTC = Local - Offset.
				TTimeIntervalSeconds utcOffset = timeZone->Value().Int();
				aLastModified -= utcOffset;
				CleanupStack::PopAndDestroy(arrayOfTimeZones);
				}
			}
		CleanupStack::PopAndDestroy(arrayOfRevisions);
   		}
   	else
		{
   		aLastModified=Time::NullTTime();
		}
    }


/**
 * Extract the vCard 'UID' property from the vCard.
 *
 * @param aVCard vCard parser object
 * @param aUidString UID property value
 */
void CVCardToContactsAppConverter::GetVCardUidStringL(CParserVCard& aVCard,TDes& aUidString) const
	{
	const CArrayPtr<CParserProperty>* arrayOfProperties=aVCard.PropertyL(KVersitTokenUID,TUid::Uid(KVersitPropertyHBufCUid),EFalse);
    if (arrayOfProperties)
    	{
    	TInt count=arrayOfProperties->Count();
    	if (count)
    		{
    		CParserPropertyValueHBufC* uidstring=(STATIC_CAST(CParserPropertyValueHBufC*,(*arrayOfProperties)[0]->Value()));
			if (uidstring->Value().Length() > KUidStringLength )
				aUidString=uidstring->Value().Left(KUidStringLength) ; // truncate
			else
				aUidString=uidstring->Value();
    		}
    	delete arrayOfProperties;
    	}
    }


/**
 * Convert aVCardProperty into contact field.
 *
 * @param aProperty vCard property
 * @param aOption Import preferences (available options defined in CContactDatabase::TOptions)
 * @param aUnsupportedProperty Return whether or not the field is supported by the PC PIM, only relevant when using the EConnectWhitespace flag
 * @return Contact field or a NULL pointer (pushed onto the cleanup stack) in the case where
 *         it wasn't possible to create a contact field for this vCard property.
 */
CContactItemField* CVCardToContactsAppConverter::GetVCardPropertyAsContactFieldLC(CParserProperty* aProperty,TInt aOption,TBool& aUnsupportedProperty)
 	{
	TUid mappingUid;
	TStorageType storageType;
	CContentType* content=MapVCardPropertyToContentAndStorageTypeL(*aProperty,storageType);
	CleanupStack::PushL(content);
	mappingUid=content->Mapping();
	TBool noConversion=EFalse;
	aUnsupportedProperty=EFalse;

	if (mappingUid==KUidContactFieldVCardMapNotRequired)
		{
		noConversion=ETrue;
		}
	else
		{	/*Could preserve unknown non X- if VCard version no>2.1*/
		switch (iUnknownPropertyBehaviour)
			{
			case EDiscardAllUnknownProperties:
				if (mappingUid==KUidContactFieldVCardMapUnknownXDash)
					noConversion=ETrue;
					/* fall through */
			case EDiscardNonXDashUnknownProperties:
				if (mappingUid==KUidContactFieldVCardMapUnknown)
					noConversion=ETrue;
					/* fall through */
			default:
				break;
			}
		}

	CContactItemField* contactItemField=NULL;
	if (!noConversion)
		{
		TBool validImportableDataFound = ETrue;
		contactItemField=CContactItemField::NewLC(storageType,*content);
		if ((mappingUid==KUidContactFieldVCardMapUnknown) ||(mappingUid==KUidContactFieldVCardMapUnknownXDash))
	    		{
	    		TBool propertyTobeTruncated = PropertyTobeTruncated(aProperty->Name());
	    		HBufC* encodedText = EncodeL(STATIC_CAST(CParserPropertyValueHBufC*,aProperty->Value())->Value(), propertyTobeTruncated);
	    		contactItemField->TextStorage()->SetText(encodedText); // takes ownership
				
	    		contactItemField->SetHidden(ETrue);
	    		}
		else
			{
	    		switch (contactItemField->StorageType())
	    			{
	    			case KStorageTypeText:
	    				{
					TInt id = aProperty->Value()->Uid().iUid;
					if(mappingUid == KUidContactFieldVCardMapORGPronunciation)
						{ // this should be treated as an array instead of a single string
						id = KVersitPropertyCDesCArrayUid;
						}
	    				switch (id)
	    					{
	    					case KVersitPropertyCDesCArrayUid:
	    						{
								TBool doConvert = ETrue;
								 TBool pushed = EFalse;
								CParserPropertyValueCDesCArray* propertyValueWrapper=NULL;
								if(mappingUid == KUidContactFieldVCardMapORGPronunciation)
									{ // Treat a SOUND with X-IRMC-ORG param as an array
									const CParserPropertyValueHBufC* valueScalar = static_cast<CParserPropertyValueHBufC*>(aProperty->Value());
									if(valueScalar)
										{
										propertyValueWrapper = valueScalar->TreatAsArrayPropertyLC(*aProperty);
										pushed= ETrue;
										}
									}
								else
									{
									propertyValueWrapper = static_cast<CParserPropertyValueCDesCArray*>(aProperty->Value());
									}
								CDesCArray* propertyValue = NULL;
								if	(propertyValueWrapper)
									{
									propertyValue = propertyValueWrapper->Value();
									}

								if ((CContactVCardConverter::EConnectWhitespace & aOption) && propertyValue)
									{
									doConvert = TextArrayContainsImportableData(*propertyValue);
									if (doConvert)
										{
										RemoveWhitespaceFromCompositePropertyL(*propertyValue);
										}
									}
								if (doConvert && propertyValue)
									{
	   								TInt count = propertyValue->MdcaCount();
								    HBufC *txt=HBufC::NewL(0);
									CleanupStack::PushL(txt);
									// concatenate the array
								    for (TInt ii=0;ii<count;ii++)
								    	{
										TInt mdcaLen=propertyValue->MdcaPoint(ii).Size();
										if (mdcaLen>0)
											{
								    			if (txt->Length()>0)
												{
												txt->Des().Append(TChar('\n'));
												}
											txt=txt->ReAllocL(txt->Length()+mdcaLen+1);	// +1 In case we add '\n'
											CleanupStack::Pop();	// txt(old value)
											CleanupStack::PushL(txt);
											TPtrC temp(propertyValue->MdcaPoint(ii));
											txt->Des().Append(temp);
											}
								    	}
			   						TBool propertyTobeTruncated = PropertyTobeTruncated(aProperty->Name());
			   						HBufC* encodedText = EncodeL(*txt, propertyTobeTruncated);
			   						CleanupStack::PopAndDestroy(txt);
			   						contactItemField->TextStorage()->SetText(encodedText); // takes ownership
									}
								else
									{
									validImportableDataFound = EFalse;
									}
								if(pushed)
									{
									CleanupStack::PopAndDestroy(propertyValueWrapper);
									}
	    						}
			    				break;
	   					case KVersitPropertyHBufCUid:
							{
							TBool doConvert = ETrue;

							CParserPropertyValueHBufC* propertyValueWrapper = static_cast<CParserPropertyValueHBufC*>(aProperty->Value());
							const TPtrC propertyValue(propertyValueWrapper->Value());

							if (CContactVCardConverter::EConnectWhitespace & aOption)
								{
								if (propertyValue==KSingleSpacePropertyValue)
									{
									aUnsupportedProperty=ETrue;
									}
								doConvert = (CContactVCardConverter::ContainsImportableData(propertyValue, CContactVCardConverter::EPropertyValueSingle, ImportType()));
								}
							if (doConvert)
								{
								TBool propertyTobeTruncated = PropertyTobeTruncated(aProperty->Name());
								HBufC* encodedText = EncodeL(propertyValue, propertyTobeTruncated);
								contactItemField->TextStorage()->SetText(encodedText); // takes ownership
								}
							else
								{
								validImportableDataFound = EFalse;
								}
							}
			    				break;
		   				default:;
	    					}
	    				}
		    			break;
		    		case KStorageTypeDateTime:
	    				{
		    			switch (aProperty->Value()->Uid().iUid)
		    				{
		    				case KVersitPropertyDateTimeUid:
		    					STATIC_CAST(CContactDateField*,contactItemField->Storage())->SetTime(STATIC_CAST(CParserPropertyValueDateTime*,aProperty->Value())->Value()->iDateTime);
		    					break;
						case KVersitPropertyDateUid:
							STATIC_CAST(CContactDateField*,contactItemField->Storage())->SetTime(STATIC_CAST(CParserPropertyValueDate*,aProperty->Value())->Value()->iDateTime);
							break;
		    				default:
			    				break;
		    				}
		    			}
	    				break;
		    		case KStorageTypeStore:
		    			{
		    			STATIC_CAST(CContactStoreField*,contactItemField->Storage())->SetThingL(STATIC_CAST(CParserPropertyValueBinary*,aProperty->Value())->Value());
		    			}
		    			break;
		    		default:
		    			break;
		    		}
	    		}
		if (aOption & CContactVCardConverter::EIncludeX)
			{
			TBuf8<KContactMaxVCardPropertyNameLength> paramName;
			paramName=KContactVCardXDashEPOCCNTMODEL;
			paramName.Append(KContactVCardFIELDHIDDEN);
			if (aProperty->Param(paramName))
				contactItemField->SetHidden(ETrue);
			paramName.Zero();
			paramName=KContactVCardXDashEPOCCNTMODEL;
			paramName.Append(KContactVCardFIELDREADONLY);
			if (aProperty->Param(paramName))
				contactItemField->SetReadOnly(ETrue);
			paramName.Zero();
			paramName=KContactVCardXDashEPOCCNTMODEL;
			paramName.Append(KContactVCardFIELDLABEL);
			if (aProperty->Param(paramName))
				contactItemField->SetLabel(aProperty->Param(paramName)->ValueL());
			}
		CleanupStack::Pop(contactItemField);
	// The field is only added to the contact card if it was found to contain
	// legitimate importable data (i.e. has content that does not simply consist of LWSP characters).
	// This is important, since TimeIS sync drivers can create vCard objects (pushed over to the device)
	// that contain only space characters for their Versit 'Property Parameter Values'.
		if (!validImportableDataFound)
			{
			delete contactItemField;
			contactItemField = NULL;
			}
		}
	CleanupStack::PopAndDestroy(content);
	// We may be pushing a NULL pointer back onto the cleanup stack, but clients of this
	// method understand this feature.
	CleanupStack::PushL(contactItemField);
	return(contactItemField);
    }


/**
 * Find a vCard property parameter in a vCard property
 *
 * @param aVCardProperty vCard property
 * @return Property parameter number
 */
TInt CVCardToContactsAppConverter::GetVCardPropertyTTNumber(CParserProperty* aVCardProperty)
    {
    TInt ii;
	TBuf8<KContactMaxVCardPropertyNameLength> paramName;
	for (ii=1;ii<=KContactMaxFieldNumber;ii++)
		{
		paramName.Format(_L8("%d"),ii);
		if (aVCardProperty->Param(paramName))
			{
			return (ii);
			}
		}
	return 1;	// Default to 1 if no number found
	}


/**
 * Extract the contact name fields from the vCard 'N' property.
 * This method takes values from the 'N' vCard property and returns the family name,
 * given name, middle name, prefix and suffix.
 *
 * Only the first instance of the 'N' property is used.
 *
 * @param aVCard vCard parser object
 * @param aOption Import preferences (available options defined in CContactDatabase::TOptions)
 * @return An instance of CVCardItemAndLabel which encapsulates the names and their associated
 *         labels contained within the vCard or a NULL pointer pushed onto the cleanup stack
 *         if it wasn't possible to construct the object.
 */
CVCardItemAndLabel* CVCardToContactsAppConverter::GetContactNameLC(CParserVCard& aVCard, TInt aOption)
    {
	CVCardItemAndLabel* names = NULL;

	CArrayPtr<CParserProperty>* arrayOfProperties=aVCard.PropertyL(KVersitTokenN,TUid::Uid(KVersitPropertyCDesCArrayUid));
  	if (arrayOfProperties && arrayOfProperties->Count())
		{
		CleanupStack::PushL(TCleanupItem(CleanUpResetAndDestroy,arrayOfProperties));
		//
		//
		CParserProperty* property = arrayOfProperties->At(0);
		CDesCArray& items = *static_cast<CParserPropertyValueCDesCArray*>(property->Value())->Value();
		names = MakeNamesFromItemsL(items, aOption, property );
		CleanupStack::PopAndDestroy(arrayOfProperties);
    	}

	// Clients of this method assume that something is pushed onto the Cleanup Stack, so
	// this might potentially be NULL.
	CleanupStack::PushL(names);
	return names;
    }



/**
 * Extract the contact name fields from the vCard 'N' property.
 * This method takes values from the 'N' vCard property and returns the family name,
 * given name, middle name, prefix and suffix.
 *
 * Only the first instance of the 'N' property is used.
 *
 * @param aVCard vCard parser object
 * @param aOption Import preferences (available options defined in CContactDatabase::TOptions)
 * @return An instance of CVCardItemAndLabel which encapsulates the names and their associated
 *         labels contained within the vCard or a NULL pointer pushed onto the cleanup stack
 *         if it wasn't possible to construct the object.
 */
CVCardItemAndLabel* CVCardToContactsAppConverter::GetContactNamePrnLC(CParserVCard& aVCard, TInt aOption)
	{
	CVCardItemAndLabel* names = NULL;

	CArrayPtr<CParserProperty>* arrayOfProperties=aVCard.PropertyL(KVersitTokenSOUND,TUid::Uid(KVersitPropertyHBufCUid), EFalse);

	if (arrayOfProperties )
		{
		CleanupStack::PushL(arrayOfProperties);
		TInt count = arrayOfProperties->Count();
		const CParserProperty* property=NULL;
		for (TInt index=0;index<count;index++)
			{
			property = arrayOfProperties->At(index);
			if(property->Param(KVersitParam8NamePrn))
				{
				index=count;
				}
			else
				{
				property=NULL;
				}
			}
		if (property) // property is the 1st SOUND property with an X-IRMC-N parameter
			{
			const CParserPropertyValueHBufC* valueScalar = static_cast<CParserPropertyValueHBufC*>(property->Value());
			if(valueScalar)
				{
				CParserPropertyValueCDesCArray* valueArray = valueScalar->TreatAsArrayPropertyLC(*property);
				names = MakeNamesFromItemsL( *valueArray->Value(), aOption, property );// valueArray will never be NULL
				CleanupStack::PopAndDestroy(valueArray);
				}
			}
		CleanupStack::PopAndDestroy(arrayOfProperties);
    		}

	// Clients of this method assume that something is pushed onto the Cleanup Stack, so
	// this might potentially be NULL.
	CleanupStack::PushL(names);
	return names;
	}

CVCardItemAndLabel* CVCardToContactsAppConverter::MakeNamesFromItemsL(const CDesCArray& aItems, TInt aOption, const CParserProperty* aProperty )
	{
	CVCardItemAndLabel* names = CVCardItemAndLabel::NewLC();

	const TInt fieldcount = aItems.Count();
	//
    	for(TInt ii=0; ii<fieldcount; ii++)
    		{
		const TPtrC pField(aItems.MdcaPoint(ii));
    		names->AddItemL(pField);
		if (aOption & CContactVCardConverter::EIncludeX)
			{
			TBuf8<KContactMaxVCardPropertyNameLength> paramName(KContactVCardXDashEPOCCNTMODEL);
			paramName.AppendFormat(KContactVCardLABELn,ii);
			//
			if(aProperty->Param(paramName))
				{
				HBufC* value = aProperty->Param(paramName)->ValueL();
				CleanupStack::PushL(value);
				names->AddLabelL(*value);
				CleanupStack::PopAndDestroy(value);
				}
    			}
		}
	CleanupStack::Pop(names);
	return names;
	}

/**
 * Extract specific address properties

 * @param aProperties An array of vCard properties which should be searched
 * @param aOption Import preferences (available options defined in CContactDatabase::TOptions)
 * @param aParamMustMatch A property parameter which must be found in the specified vCard property. If this
 *                        has a NULL (KNullDesC) value then the property is treated as satisfying this condition
 * @param aParamMustNotMatch1 A property parameter which must not be found in the specified vCard property. If this
 *                        has a NULL (KNullDesC) value then the property is treated as satisfying this condition
 * @param aParamMustNotMatch2 Another property parameter which must not be found in the specified vCard property. If this
 *                        has a NULL (KNullDesC) value then the property is treated as satisfying this condition
 * @param aMapping The address mapping which is being searched for (e.g. HOME, WORK, etc)
 * @return An address object which contains the name and labels (optionally) located in aProperties for this particular mapping
 */
CVCardAddress* CVCardToContactsAppConverter::GetSpecifiedAddressLC(const CArrayPtr<CParserProperty>& aProperties, TInt aOption, const TDesC8& aParamMustMatch, const TDesC8& aParamMustNotMatch1, const TDesC8& aParamMustNotMatch2, TUid aMapping)
	{
	CVCardAddress* address = NULL;
	const TInt propertyCount = aProperties.Count();
	//
	for (TInt ii=0; ii<propertyCount; ii++)
		{
		CParserProperty* property = aProperties[ii];
		
		// DEF084708, also get and compare for ADDR;TYPE=HOME etc in addition to ADDR;HOME. 
		// The param to be matched (or not matched) can be found either as the param name (ADDR;HOME)
		// or in the param value of param name - TYPE (case ADDR;TYPE=HOME)
		CParserParam* paramType = property->Param(KVersitParam8Type);
		if (
			(property->Name() == KVersitTokenADR) &&
			(property->Value()->Uid() == TUid::Uid(KVersitPropertyCDesCArrayUid)) &&
			
			(!aParamMustMatch.Length()     || property->Param(aParamMustMatch) != NULL ||
			(paramType != NULL && paramType->Value().CompareF(aParamMustMatch) == 0)) &&
			
			(!aParamMustNotMatch1.Length() || (property->Param(aParamMustNotMatch1) == NULL &&
			(paramType == NULL || paramType->Value().CompareF(aParamMustNotMatch1) != 0))) &&
			
			(!aParamMustNotMatch2.Length() || (property->Param(aParamMustNotMatch2) == NULL &&
			(paramType == NULL || paramType->Value().CompareF(aParamMustNotMatch2) != 0)))
			)
			{
			
			CDesCArray& items = *static_cast<CParserPropertyValueCDesCArray*>(property->Value())->Value();
			const TInt fieldcount = items.Count();

			// Create the address
			address = CVCardAddress::NewLC(aMapping);

			for(TInt jj = 0; jj < fieldcount; jj++)
    			{
				const TPtrC pField(items.MdcaPoint(jj));
    			address->AddItemL(pField);
				//
				if	(aOption & CContactVCardConverter::EIncludeX)
					{
					TBuf8<KContactMaxVCardPropertyNameLength> paramName(KContactVCardXDashEPOCCNTMODEL);
					paramName.AppendFormat(KContactVCardLABELn, jj);
					//
					if	(property->Param(paramName))
						{
						HBufC* value = property->Param(paramName)->ValueL();
						CleanupStack::PushL(value);
						address->AddLabelL(*value);
						CleanupStack::PopAndDestroy(value);
						}
    				}
				}

			// End for-loop now we've found the specified item
			break;
			}
		}

	// Have to push something to ensure cleanup stack is balanced
	if	(!address)
		CleanupStack::PushL((TAny*) NULL);
	return address;
	}


/**
 * Extract home and work address properties from the vCard
 *
 * @param aVCard vCard parser object
 * @param aOption Import preferences (available options defined in CContactDatabase::TOptions)
 * @param aAddresses An array reference which contains any located address fields upon exit from this method
 */
void CVCardToContactsAppConverter::GetAddressesL(CParserVCard& aVCard, TInt aOption, RPointerArray<CVCardAddress>& aAddresses)
    {
	CArrayPtr<CParserProperty>* arrayOfProperties = aVCard.ArrayOfProperties(EFalse);
	if	(arrayOfProperties)
		{
		// Find ADR;HOME
		CVCardAddress* home = GetSpecifiedAddressLC(*arrayOfProperties,
													aOption,
													KVersitParam8Home, // Must be ADR;HOME and not ADR;HOME;WORK
													KVersitParam8Work,
													KNullDesC8,
													KUidContactFieldVCardMapHOME
													);
		if	(home)
			User::LeaveIfError(aAddresses.Append(home));
		CleanupStack::Pop(home);

		// Find ADR;WORK
		CVCardAddress* work = GetSpecifiedAddressLC(*arrayOfProperties,
													aOption,
													KVersitParam8Work, // Must be ADR;WORK
													KNullDesC8,
													KNullDesC8,
													KUidContactFieldVCardMapWORK
													);
		if	(work)
			User::LeaveIfError(aAddresses.Append(work));
		CleanupStack::Pop(work);

		// Find ADR;PREF
		CVCardAddress* pref = GetSpecifiedAddressLC(*arrayOfProperties,
													aOption,
													KVersitParam8Pref, // Must be ADR;PREF and not ADR;HOME or ADR;WORK
													KVersitParam8Home,
													KVersitParam8Work,
													KUidContactFieldVCardMapPREF
													);
		if	(pref)
			User::LeaveIfError(aAddresses.Append(pref));
		CleanupStack::Pop(pref);

		// Find general ADR: address
		CVCardAddress* general = GetSpecifiedAddressLC(*arrayOfProperties,
													aOption,
													KNullDesC8, // Just has to be ADR
													KVersitParam8Home,
													KVersitParam8Work,
													KNullUid
													);
		if	(general)
			User::LeaveIfError(aAddresses.Append(general));
		CleanupStack::Pop(general);
		}
	}



/**
 * Map vCard property to contacts field content and storage type
 *
 * @param aVCardProperty vCard property
 * @param aStorageType Type of contact field storage (TStorageType)
 * @return Content type for vCard property
 */
CContentType* CVCardToContactsAppConverter::MapVCardPropertyToContentAndStorageTypeL(const CParserProperty& aVCardProperty, TStorageType& aStorageType)
    {
	TUid mapping=KUidContactFieldNone;
	TFieldType fieldType=KUidContactFieldNone;
	TPtrC8 vpropNameConst = aVCardProperty.Name();
    aStorageType=KStorageTypeStore;

	switch (aVCardProperty.Value()->Uid().iUid)
    	{
    case KVersitPropertyCDesCArrayUid:
    	{
    	aStorageType=KStorageTypeText;
    	if (vpropNameConst.CompareF(KVersitTokenN)==0)
			{
    		fieldType=KUidContactFieldFamilyName;
			mapping=KUidContactFieldVCardMapUnusedN;
			}
    	else if (vpropNameConst.CompareF(KVersitTokenADR)==0)
			{
    		fieldType=KUidContactFieldAddress;
			mapping=KUidContactFieldVCardMapADR;
			}
 		else if (vpropNameConst.CompareF(KVersitTokenChildren) == 0)
 			{
 			mapping = KUidContactFieldVCardMapChildren;
 			fieldType = KUidContactFieldChildren;
 			}
    	}
    	break;
    case KVersitPropertyDateTimeUid:
    	aStorageType=KStorageTypeDateTime;
    	if (vpropNameConst.CompareF(KVersitTokenREV)==0)
    		mapping=KUidContactFieldVCardMapNotRequired;
    	break;
    case KVersitPropertyDateUid:
    	aStorageType=KStorageTypeDateTime;
    	if (vpropNameConst.CompareF(KVersitTokenBDAY)==0)
			{
    		mapping=KUidContactFieldVCardMapBDAY;
			fieldType=KUidContactFieldBirthday;
			}
 		else if (vpropNameConst.CompareF(KVersitTokenAnniversary) == 0)
 			{
 			mapping = KUidContactFieldVCardMapAnniversary;
 			fieldType = KUidContactFieldAnniversary;
 			}
    	break;
    case KVCardPropertyAgentUid:
		aStorageType = KStorageTypeContactItemId ; 
		if (vpropNameConst.CompareF(KVersitTokenAGENT) == 0)
			{
			mapping = KUidContactFieldVCardMapAGENT;
			}
    	break;

    case KVersitPropertyTimeZoneUid:
    	aStorageType=KStorageTypeStore;

    	if(vpropNameConst.CompareF(KVersitTokenTZ) == 0)
    		{
    		mapping=KUidContactFieldVCardMapNotRequired;
    		}
    	break;
//////////////
    default:	// aka case KVersitPropertyHBufCUid:
    	aStorageType=KStorageTypeText;
		mapping = KUidContactFieldVCardMapUnknown; // Initialize to something sensible
		switch (vpropNameConst[0])
			{
		case 'B':
		case 'b':
			if (vpropNameConst.CompareF(KVersitTokenBDAY)==0)
				{
    			mapping=KUidContactFieldVCardMapBDAY;
				fieldType=KUidContactFieldBirthday;
				}
			break;
    	case 'E':
    	case 'e':
			if (vpropNameConst.CompareF(KVersitTokenEMAIL)==0)
				{
    			fieldType=KUidContactFieldEMail;
    			mapping=KUidContactFieldVCardMapEMAILINTERNET;
				}
			break;
    	case 'F':
    	case 'f':
			if (vpropNameConst.CompareF(KVersitTokenFN)==0)
    			mapping=KUidContactFieldVCardMapUnusedFN;
			break;
    	case 'G':
    	case 'g':
			if (vpropNameConst.CompareF(KVersitTokenGEO)==0)
				{
				fieldType=KUidContactFieldGEO;
    			mapping=KUidContactFieldVCardMapGEO;
				}
			break;
    	case 'K':
    	case 'k':
			if (vpropNameConst.CompareF(KVersitTokenKEY)==0)
				{
				if(aVCardProperty.Value()->Uid().iUid == KVersitPropertyBinaryUid)
					{					
					aStorageType = KStorageTypeStore;
			 		}
    			mapping=KUidContactFieldVCardMapKEY;
				}
			break;
    	case 'L':
    	case 'l':
			if (vpropNameConst.CompareF(KVersitTokenLABEL)==0)
    			mapping=KUidContactFieldVCardMapLABEL;
			else if (vpropNameConst.CompareF(KVersitTokenLOGO)==0)
				{
				aStorageType=KStorageTypeStore;
    			mapping=KUidContactFieldVCardMapLOGO;
				}
    		break;
    	case 'M':
    	case 'm':
			if  (vpropNameConst.CompareF(KVersitTokenMAILER)==0)
    			mapping=KUidContactFieldVCardMapMAILER;
			break;
    	case 'N':
    	case 'n':
			if (vpropNameConst.CompareF(KVersitTokenNOTE)==0)
				{
				fieldType=KUidContactFieldNote;
    				mapping=KUidContactFieldVCardMapNOTE;
				}
			break;
    	case 'P':
    	case 'p':
			if (vpropNameConst.CompareF(KVersitTokenPHOTO)==0)
				{
    			mapping=KUidContactFieldVCardMapPHOTO;
				aStorageType=KStorageTypeStore;
				}
			break;
    	case 'R':
    	case 'r':
			if (vpropNameConst.CompareF(KVersitTokenROLE)==0)
    			mapping=KUidContactFieldVCardMapROLE;
			break;
    	case 'S':
    	case 's':
			if (vpropNameConst.CompareF(KVersitTokenSOUND)==0)
				{
				if(aVCardProperty.Param(KVersitParam8CompanyPrn))
					{
					mapping = KUidContactFieldVCardMapORGPronunciation;
					fieldType=KUidContactFieldCompanyNamePronunciation;
					}
				else if(aVCardProperty.Param(KVersitParam8NamePrn))
					{// this is handled elsewhere
					mapping = KUidContactFieldVCardMapNotRequired;
					}
				else // if we ever support any of the other pronunication extensions we'd have to add the checking here
					{
					mapping=KUidContactFieldVCardMapSOUND;
					}
				}
			break;
    	case 'T':
    	case 't':    	
			 if (vpropNameConst.CompareF(KVersitTokenTEL)==0)
    			{
    			fieldType=KUidContactFieldPhoneNumber;
				mapping=KUidContactFieldVCardMapTEL;
    			}
			 else if (vpropNameConst.CompareF(KVersitTokenTITLE)==0)
				{
				fieldType=KUidContactFieldJobTitle;
				mapping=KUidContactFieldVCardMapTITLE;
				}
			 else if (vpropNameConst.CompareF(KVersitTokenTZ)==0)
				{
				mapping=KUidContactFieldVCardMapNotRequired;
				}
			break;
    	case 'U':
    	case 'u':    	
			if (vpropNameConst.CompareF(KVersitTokenURL)==0)
				{
    			mapping=KUidContactFieldVCardMapURL;
				fieldType=KUidContactFieldUrl;
				}
			else if (vpropNameConst.CompareF(KVersitTokenUID)==0)
				{
				mapping=KUidContactFieldVCardMapNotRequired;
				}
			break;
    	case 'V':
    	case 'v':    	
			if (vpropNameConst.CompareF(KVersitTokenVERSION)==0)
				{
    			mapping=KUidContactFieldVCardMapNotRequired;
				}
			break;
    	case 'X':
    	case 'x':    	
			if (vpropNameConst.CompareF(KVersitTokenSECONDNAME)==0)
				{
				mapping=KUidContactFieldVCardMapSECONDNAME;
				fieldType=KUidContactFieldSecondName;
				}
			else if (vpropNameConst.CompareF(KVersitTokenSIPID)==0)
				{
				mapping=KUidContactFieldVCardMapSIPID;
				fieldType=KUidContactFieldSIPID;
				}
			else if (vpropNameConst.CompareF(KVersitTokenWVID)==0)
				{
				mapping=KUidContactFieldVCardMapWV;
				fieldType=KUidContactFieldIMAddress;
				}
			else if (vpropNameConst.CompareF(KVersitTokenAssistant) == 0)
				{
				mapping = KUidContactFieldVCardMapAssistant;
				fieldType = KUidContactFieldAssistant;
				}
			else if (vpropNameConst.CompareF(KVersitTokenAssistantTel) == 0)
				{
				mapping = KUidContactFieldVCardMapAssistantTel;
				fieldType = KUidContactFieldPhoneNumber;
				}
			else if (vpropNameConst.CompareF(KVersitTokenAnniversary) == 0)
				{
				mapping = KUidContactFieldVCardMapAnniversary;
				fieldType = KUidContactFieldAnniversary;
				}
			else if (vpropNameConst.CompareF(KVersitTokenSpouse) == 0)
				{
				mapping = KUidContactFieldVCardMapSpouse;
				fieldType = KUidContactFieldSpouse;
				}
			else if (vpropNameConst.Length()>=2)
    			{
    			if (vpropNameConst.Left(2).CompareF(KVersitTokenXDash)==0)
    				mapping=KUidContactFieldVCardMapUnknownXDash;
				else
 					mapping=KUidContactFieldVCardMapUnknown;
				}
			break;
    	default:
			break; // Leave mapping set to KUidContactFieldVCardMapUnknown
			};
		};

	CContentType* type=CContentType::NewL();
	CleanupStack::PushL( type );
	type->SetMapping(mapping);
	if (mapping==KUidContactFieldVCardMapPHOTO || mapping==KUidContactFieldVCardMapLOGO)
		{
		type->AddFieldTypeL(KUidContactFieldPicture);
        TFieldType bitmapFormat;
        GetBitMapFormat(aVCardProperty, bitmapFormat);
           
        if (bitmapFormat.iUid!=KUidContactFieldNone.iUid)
			{
            type->AddFieldTypeL(bitmapFormat);
			}
		}
	if(fieldType!=KUidContactFieldNone)
		{
		type->AddFieldTypeL(fieldType);
		}

	CArrayPtr<CParserParam>* paramArray = NULL ;
	paramArray = aVCardProperty.ParamArray();

	// parameters
	if (IsParameterValuePresent(paramArray, KVersitTokenTYPE, KVersitParam8Internet))
		{
		type->AddFieldTypeL(KUidContactFieldVCardMapINTERNET);
		}
	if  (IsParameterValuePresent(paramArray, KVersitTokenTYPE, KVersitParam8Home))
		{
		type->AddFieldTypeL(KUidContactFieldVCardMapHOME);
		}
    if 	(IsParameterValuePresent(paramArray, KVersitTokenTYPE, KVersitParam8Work))
		{
		type->AddFieldTypeL(KUidContactFieldVCardMapWORK);
		}
    if 	(IsParameterValuePresent(paramArray, KVersitTokenTYPE, KVersitParam8Voice))
		{
		type->AddFieldTypeL(KUidContactFieldVCardMapVOICE);
		}
    if 	(IsParameterValuePresent(paramArray, KVersitTokenTYPE, KVersitParam8Fax))
        {
        type->RemoveFieldType(KUidContactFieldPhoneNumber);
        type->AddFieldTypeL(KUidContactFieldFax);
        type->AddFieldTypeL(KUidContactFieldVCardMapFAX);
        }
    if 	(IsParameterValuePresent(paramArray, KVersitTokenTYPE, KVersitParam8Pref))
		{
		type->AddFieldTypeL(KUidContactFieldVCardMapPREF);
		}
    if 	(IsParameterValuePresent(paramArray, KVersitTokenTYPE, KVersitParam8Cell))
		{
		type->AddFieldTypeL(KUidContactFieldVCardMapCELL);
		}
    if 	(IsParameterValuePresent(paramArray, KVersitTokenTYPE, KVersitParam8Pager))
		{
		type->AddFieldTypeL(KUidContactFieldVCardMapPAGER);
		}
    if 	(IsParameterValuePresent(paramArray, KVersitTokenTYPE, KVersitParam8Bbs))
		{
		type->AddFieldTypeL(KUidContactFieldVCardMapBBS);
		}
    if 	(IsParameterValuePresent(paramArray, KVersitTokenTYPE, KVersitParam8Modem))
		{
		type->AddFieldTypeL(KUidContactFieldVCardMapMODEM);
		}
    if 	(IsParameterValuePresent(paramArray, KVersitTokenTYPE, KVersitParam8Car))
		{
		type->AddFieldTypeL(KUidContactFieldVCardMapCAR);
		}
    if 	(IsParameterValuePresent(paramArray, KVersitTokenTYPE, KVersitParam8Isdn))
		{
		type->AddFieldTypeL(KUidContactFieldVCardMapISDN);
		}
    if 	(IsParameterValuePresent(paramArray, KVersitTokenTYPE, KVersitParam8Video))
		{
		type->AddFieldTypeL(KUidContactFieldVCardMapVIDEO);
		}
	if 	(IsParameterValuePresent(paramArray, KVersitTokenTYPE, KVersitParam8Msg))
		{
		type->AddFieldTypeL(KUidContactFieldVCardMapMSG);
		}
	if 	(IsParameterValuePresent(paramArray, KVersitTokenTYPE, KVersitParam8Dom))
		{
		type->AddFieldTypeL(KUidContactFieldVCardMapDOM);
		}
	if 	(IsParameterValuePresent(paramArray, KVersitTokenTYPE, KVersitParam8X509))
		{
    	type->AddFieldTypeL(KUidContactFieldVCardMapX509);
		}
	if 	(IsParameterValuePresent(paramArray, KVersitTokenTYPE, KVersitParam8PGP))
		{
    	type->AddFieldTypeL(KUidContactFieldVCardMapPGP);
		}
	if (IsParameterValuePresent(paramArray, KVersitTokenTYPE, KVersitParamPOC))
		{
		type->AddFieldTypeL(KUidContactFieldVCardMapPOC);
		}
	if (IsParameterValuePresent(paramArray, KVersitTokenTYPE, KVersitParamSWIS))
		{
		type->AddFieldTypeL(KUidContactFieldVCardMapSWIS);
		}
	if (IsParameterValuePresent(paramArray, KVersitTokenTYPE, KVersitParamVOIP))
		{
		type->AddFieldTypeL(KUidContactFieldVCardMapVOIP);
		}
	

	CleanupStack::Pop(type);
	return(type);
    }

/*
 * Gets the bitmap format. This is done by looking for the name and value items in the parameter eg. TYPE=GIF
 * the if construct assumes that this is  present in the vcf file. It is also possible to just have the format without
 * the TYPE string and this is stored in the name part of the parameter so need to check through all supported formats
 * to find if one is present, this is done in the else construct.
 *
 * @param aVCardProperty and aBitmapFormat
 * @return void.
 */   
void CVCardToContactsAppConverter::GetBitMapFormat(const CParserProperty& aVCardProperty, TFieldType& aBitmapFormat)
    {
    aBitmapFormat.iUid=KUidContactFieldNone.iUid;
   
    CParserParam* param=aVCardProperty.Param(KVersitTokenTYPE);
    if (param)
        {
        TFieldType bitmapFormat=MapVCardPhotoTypeToFieldType(param->Value());
        if (bitmapFormat.iUid!=KUidContactFieldNone.iUid)
            {
            aBitmapFormat.iUid = bitmapFormat.iUid;
            }
        } //if
    else
        {   
            const TInt KNumberOfBitmaps = 16;
            //Set up table of bitmap formats
            const TBufC8<5> bitmapString[] =
            {
                KVersitParam8Gif(),
                KVersitParam8Jpeg(),
                KVersitParam8Bmp(),
                KVersitParam8Tiff(),
                KVersitParam8Pict(),
                KVersitParam8Cgm(),
                KVersitParam8Wmf(),
                KVersitParam8Ps(),
                KVersitParam8Pdf(),
                KVersitParam8Mpeg(),
                KVersitParam8Mpeg2(),
                KVersitParam8Avi(),
                KVersitParam8Qtime(),   
                KVersitParam8Dib(),
                KVersitParam8Pmb(),
                KVersitParam8Met()     
            };
           
            // Loop through the table looking for a param whose name matches the bitmap format
            for (TInt i=0; i < KNumberOfBitmaps - 1; ++i)
                {
                CParserParam* paramNameCheck = aVCardProperty.Param(bitmapString[i]);
                if (paramNameCheck)
                    {//ok found something
                    TFieldType bitmapFormat=MapVCardPhotoTypeToFieldType(paramNameCheck->Name());
                    aBitmapFormat.iUid = bitmapFormat.iUid;
                    break;
                    }
                }   
        } //else
   
    }

TFieldType CVCardToContactsAppConverter::MapVCardPhotoTypeToFieldType(/*const CParserParam& aParserParam*/TPtrC8 aBitmapStringPtr)
	{
    if (aBitmapStringPtr.Length()<2)
		{
		return KUidContactFieldNone; //no bitmap type token are less than 2 characters long
		}

    TChar firstChar(aBitmapStringPtr[0]);
	firstChar=firstChar.GetUpperCase();

	switch (firstChar)
		{
	case 'G':
		return KUidContactFieldVCardMapGIF;
	case 'C':
		return KUidContactFieldVCardMapCGM;
	case 'W':
		return KUidContactFieldVCardMapWMF;
	case 'B':
		return KUidContactFieldVCardMapBMP;
	case 'D':
		return KUidContactFieldVCardMapDIB;
	case 'P':
		{
	    TChar secondChar(aBitmapStringPtr[1]);
		switch (secondChar.GetUpperCase())
			{
		case 'S':
			return KUidContactFieldVCardMapPS;
		case 'M':
			return KUidContactFieldVCardMapPMB;
		case 'D':
			return KUidContactFieldVCardMapPDF;
		case 'I':
			return KUidContactFieldVCardMapPICT;
		default:
			return KUidContactFieldNone;
			}				
		}
	case 'T':
		return KUidContactFieldVCardMapTIFF;
	case 'J':
		return KUidContactFieldVCardMapJPEG;
	case 'M':
        switch (aBitmapStringPtr.Length())
			{
		case 3:
			return KUidContactFieldVCardMapMET;
		case 4:
			return KUidContactFieldVCardMapMPEG;
		case 5:
			return KUidContactFieldVCardMapMPEG2;
		default:
			return KUidContactFieldNone;
			}
	case 'A':
		return KUidContactFieldVCardMapAVI;
	case 'Q':
		return KUidContactFieldVCardMapQTIME;
	default:
		return KUidContactFieldNone;
		}
	}


/**
 * Checks each field in an array to see if one or more contain importable data. Assuming one field within
 * the array does, then the whole array should be imported.
 *
 * @param aArray The array of text fields to be checked for importable data
 * @return Whether the array should be imported.
 */
TBool CVCardToContactsAppConverter::TextArrayContainsImportableData(const CDesCArray& aArray) const
	{
	// Text arrays are implicitly composite in nature
	const TInt count = aArray.Count();
	for(TInt i=0; i<count; i++)
		{
		const TPtrC pItem(aArray.MdcaPoint(i));
		if	(CContactVCardConverter::ContainsImportableData(pItem, CContactVCardConverter::EPropertyValueComposite, ImportType()))
			return ETrue;
		}
	//
	return EFalse;
	}

/**
 * Remove single space character entries from the specified composite property array and replace them
 * with null values.
 *
 * The TimeIS sync engines use spaces and NULLs to indicate special behaviour. Space = supported
 * but empty property. NULL = unsupported property.
 *
 * Contacts model fields do not support composite properties themselves. Instead, the following
 * method is used to converted a composite (array) property to a non composite value:
 *
 * CContactTextField::SetStandardTextArray(...)
 *
 * This method simply concatenates all the array values into one non-composite value. A CRLF
 * is added between each sub-field. Therefore, if a composite property reads thus:
 *
 * {"A","B"}
 *
 * contacts model will store the result as "A\r\n\B".
 *
 * In the case where a composite property from TimeIS contains a supported but empty field value,
 * without special handling, the space character (implicit meaning: "supported but empty")
 * will be interpreted literally and stored within the contacts model field as real data.
 *
 * For example: {"Symbian"," "}
 *
 * The implied meaning of this (in terms of PC sync) is:
 *
 * OrgName: Symbian
 * OrgUnit: (a supported field of the PC PIM, but contains no data at this time).
 *
 * In the above example, without special processing, the resultant contacts model field would read thus:
 *
 * "Symbian\r\n "
 *
 * The trailing space would also be exported during any synchronisation, and therefore introduce
 * unexpected data in the sync chain.
 *
 * This method simply ensures that single space characters are mapped back to their 'real' meaning,
 * i.e. a NULL.
 *
 * @param aArray The composite property which needs fixing up
 */
void CVCardToContactsAppConverter::RemoveWhitespaceFromCompositePropertyL(CDesCArray& aArray)
	{
	const TInt count = aArray.Count();
	for(TInt i=0; i<count; i++)
		{
		TPtrC pItem(aArray[i]);
		if	(pItem == KContactVCardCompositeSupportedButEmptyFieldValue)
			{
			aArray.Delete(i);
			aArray.InsertL(i, KContactVCardEmptyFieldValue);
			}
		}
	}

/**
 * Get a Pointer to the First Instance of the specified Property's value in the VCard Object
 *
 * @param aVCard A vCard Object containing Array of Properties
 * @param aToken A String with desired Property Name
 * @param A buffer descriptor expected to contain the first instance of desired property Value on return
 * @return Count of Class Properties found
 */
TInt CVCardToContactsAppConverter::GetSingleInstanceL(const CParserVCard& aVCard,const TDesC8& aToken, TDes& aClass)
	{
	CArrayPtr<CParserProperty>* arrayOfProp=aVCard.PropertyL(aToken, TUid::Uid(KVersitPropertyHBufCUid), EFalse);
	if(arrayOfProp)
		{
		CleanupStack::PushL(arrayOfProp);
		TInt propCount = arrayOfProp->Count();
		CParserProperty* property = arrayOfProp->At(0);
		CParserPropertyValueHBufC* propertyAsHBufC = static_cast<CParserPropertyValueHBufC*>(property->Value());
		aClass = propertyAsHBufC->Value();
		if( !aClass.Length() )
			{
			propCount = 0;
			}
		CleanupStack::PopAndDestroy(arrayOfProp);
		return 	propCount;
		}
	return 0;
	}

/**
 * Add a specific field into a contact
 *
 * @param aContact Contact item to add fields to
 * @param aValue Reference to first Instance of the Property Value
 * @param aFieldType Field type of field to add (TFieldType)
 * @param aMapping vCard mapping Id of the field to add.
 * @param aNames An object containing the name and labels for 'N' property fields
 * @param aOption Import preferences (available options defined in CContactDatabase::TOptions)
 */
void CVCardToContactsAppConverter::SetSingleInstanceL(CContactItem& aContact,const TDes& aValue,const TFieldType& aFieldType, const TUid& aMapping, TInt aOption)
	{
	//
	TBool addField = EFalse;
	if (CContactVCardConverter::EConnectWhitespace & aOption)
		{
		addField = CContactVCardConverter::ContainsImportableData(aValue, CContactVCardConverter::EPropertyValueSingle, ImportType());
		}
	else
		{
		addField = (aValue.Length());
		}
	// Only add the field if it contains some data
	if	(addField)
		{
		CContactItemField* contactItemField = CContactItemField::NewLC(KStorageTypeText, aFieldType);
		contactItemField->SetMapping(aMapping);
		contactItemField->TextStorage()->SetStandardTextL(aValue);
		aContact.AddFieldL(*contactItemField);
		CleanupStack::Pop(contactItemField);
		}
	}

/**
 * Merge a specific field from a contact
 *
 * @param aContact Contact item to add fields to
 * @param aValue Pointer to first Instance of the Property Value
 * @param aFieldType Field type of field to add (TFieldType)
 * @param aMapping vCard mapping Id of the field to add.
 * @param aNames An object containing the name and labels for 'N' property fields
 * @param aOption Import preferences (available options defined in CContactDatabase::TOptions)
 */
void CVCardToContactsAppConverter::MergeSingleInstanceL(CContactItem& aContact, const TDes& aValue,const TFieldType& aFieldType, const TUid& aMapping, TInt aOption)
	{
	CContactItemFieldSet& oldfieldset = aContact.CardFields();
	const TInt pos = oldfieldset.Find(aFieldType, aMapping);
	const TBool processWhitespace = (aOption & CContactVCardConverter::EConnectWhitespace);

	// First check whether the field is present in the contact card
	// Also verify that the array of address sub-values actually contains a specific
	// value for the requested index.
	const TInt Klength = aValue.Length();
	if (processWhitespace)
		{
		TBool isSingleSpace = EFalse;
		if (Klength == 1)
			{
			isSingleSpace = (aValue[0] == KContactVCardSpaceCharacter);
			}
		if	((pos != KErrNotFound) && (Klength || isSingleSpace))
			{
			// This means the PC side field is empty, so delete the corresponding device-side field.
			aContact.RemoveField(pos);
			}
		if	(Klength && !isSingleSpace)
			{
			// This means the PC side field is unsupported, so ignore the corresponding contents.
			SetSingleInstanceL(aContact, aValue, aFieldType,aMapping,aOption);
			}
		}
	else
		{
		if (pos != KErrNotFound)
			{
			// This means the PC side field is empty, so delete the corresponding device-side field.
			aContact.RemoveField(pos);
			}
		if (Klength)
			{
			// This means the PC side field is not empty, so add the corresponding contents.
			SetSingleInstanceL(aContact, aValue, aFieldType,aMapping,aOption);
			}
		}
	}

/**
 * Get an Array containing Property's values from a VCard
 *
 * @param aVCard A vCard Object containing Array of Properties
 * @param aToken A String with desired Property Name
 * @param A Composite Descriptor array with desired vCard Property's Values on return
 * @return Count of desired properties found.
 */

TInt CVCardToContactsAppConverter::GetVCardPropertyAsArrayOfValuesL(const CParserVCard& aVCard, const TDesC8& aToken, CDesCArray& aItems)
	{
	CArrayPtr<CParserProperty>* arrayOfProp =aVCard.PropertyL(aToken,TUid::Uid(KVersitPropertyCDesCArrayUid),EFalse);
	TInt propCount = 0;
	if(arrayOfProp)
		{
		CleanupStack::PushL(arrayOfProp);
		propCount = arrayOfProp->Count();
		CParserProperty* property = arrayOfProp->At(0);
		CDesCArray& value = *static_cast<CParserPropertyValueCDesCArray*>(property->Value())->Value();
		if(value.Count())
			{
			for(TInt i = 0;i < value.Count();i++)
				{
				aItems.AppendL(value.MdcaPoint(i));
				}
			}
		else
			{
			propCount = 0;
			}
		CleanupStack::PopAndDestroy(arrayOfProp);
		}

	return propCount;
	}

/**
 * Add Organization Information like Company Name and Department Name into contact
 *
 * @param aContact Contact item to add fields to
 * @param aItems A CDesC Array containing the Property's value
 * @param aOption Import preferences (available options defined in CContactDatabase::TOptions)
 */
void CVCardToContactsAppConverter::SetOrgDetailsL(CContactItem& aContact,CDesCArray& aItems,const TInt aOption)
	{
	TInt orgCount = aItems.MdcaCount();
	SetSpecificFieldL(aContact, aItems, KUidContactFieldCompanyName, KUidContactFieldVCardMapORG, aOption, 0, 1);
	SetSpecificFieldL(aContact, aItems, KUidContactFieldDepartmentName,	KUidContactFieldVCardMapDepartment, aOption, 1, orgCount);
	}

/**
 * Add a specific field from the Array of Property's values into contact
 *
 * @param aContact Contact item to add fields to
 * @param aItems A CDesC Array containing the Property's values
 * @param aFieldType Field type of field to add (TFieldType)
 * @param aMapping vCardMapping Id of field to add
 * @param aOption Import preferences (available options defined in CContactDatabase::TOptions)
 * @param aStartIndex Starting Index in the Property Value Array
 * @param aEndIndex   Ending Index in the Property Value Array
 */
void CVCardToContactsAppConverter::SetSpecificFieldL(CContactItem& aContact,CDesCArray& aItems,const TUid& aFieldType,const TUid& aMapping,TInt aOption, TInt aStartIndex, TInt aEndIndex)
	{
	TBool doConvert = ETrue;
	TInt ii = 0;

	CDesCArrayFlat* orgList = new (ELeave)CDesCArrayFlat(4);
	CleanupStack::PushL(orgList);

    for (ii = aStartIndex;ii < aEndIndex;ii++)
    	{
    	orgList->AppendL(aItems.MdcaPoint(ii));
    	}
	if ((CContactVCardConverter::EConnectWhitespace & aOption) && orgList)
		{
		doConvert = TextArrayContainsImportableData(*orgList);
		if (doConvert)
			{
			RemoveWhitespaceFromCompositePropertyL(*orgList);
			}
		}
	if (doConvert)
		{
		CContactItemField* contactItemField = CContactItemField::NewLC(KStorageTypeText, aFieldType);
		contactItemField->SetMapping(aMapping);
		contactItemField->TextStorage()->SetStandardTextArray(orgList);
		aContact.AddFieldL(*contactItemField);
		CleanupStack::Pop(contactItemField);
		}

	CleanupStack::PopAndDestroy(orgList); // orgList
}

/**
 * Merge Organization Information like Company Name and Department Name into contact
 *
 * @param aContact Contact item to add fields to
 * @param aItems A CDesC Array containing the Property's value
 * @param aFieldType Field type of field to add (TFieldType)
 * @param aMapping vCardMapping Id of field to add
 * @param aOption Import preferences (available options defined in CContactDatabase::TOptions)
 * @param aStartIndex Starting Index in the Property Value Array
 * @param aEndIndex   Ending Index in the Property Value Array
 */
void CVCardToContactsAppConverter::MergeOrgDetailsL(CContactItem& aContact, CDesCArray& aItems, TInt aOption)
	{
	TInt orgCount = aItems.MdcaCount();
	MergeSpecificFieldL(aContact, aItems, KUidContactFieldCompanyName,	KUidContactFieldVCardMapORG, aOption, 0, 1);
	MergeSpecificFieldL(aContact, aItems, KUidContactFieldDepartmentName,	KUidContactFieldVCardMapDepartment, aOption, 1, orgCount);
	}

/**
 * Merge specific Field from the array of Property's values into contact
 *
 * @param aContact Contact item to add fields to
 * @param aItems A CDesC Array containing the Property's value
 * @param aFieldType Field type of field to add (TFieldType)
 * @param aMapping vCardMapping Id of field to add
 * @param aOption Import preferences (available options defined in CContactDatabase::TOptions)
 * @param aStartIndex Starting Index in the Property Value Array
 * @param aEndIndex   Ending Index in the Property Value Array
 */
void CVCardToContactsAppConverter::MergeSpecificFieldL(CContactItem& aContact, CDesCArray& aItems, const TFieldType& aFieldType, const TUid& aMapping, TInt aOption, TInt aStartIndex, TInt aEndIndex)
	{
	CContactItemFieldSet& oldfieldset = aContact.CardFields();
	const TInt pos = oldfieldset.Find(aFieldType, aMapping);
	const TBool processWhitespace = (aOption & CContactVCardConverter::EConnectWhitespace);

	// First check whether the field is present in the contact card
	// Also verify that the array of address sub-values actually contains a specific
	// value for the requested index.
	
	if(aItems.MdcaCount() > aStartIndex)
		{	
		const TPtrC pValue = aItems.MdcaPoint(aStartIndex);
		const TInt Klength = pValue.Length();
		if (processWhitespace)
			{
			TBool isSingleSpace = EFalse;
			if (Klength == 1)
				{
				isSingleSpace = (pValue[0] == KContactVCardSpaceCharacter);
				}
			if	((pos != KErrNotFound) && (Klength || isSingleSpace))
				{
				// This means the PC side field is empty, so delete the corresponding device-side field.
				aContact.RemoveField(pos);
				}
			if	(Klength && !isSingleSpace)
				{
				// This means the PC side field is unsupported, so ignore the corresponding contents.
				SetSpecificFieldL(aContact, aItems, aFieldType,	aMapping, aOption, aStartIndex, aEndIndex);
				}
			}
		else
			{
			if (pos != KErrNotFound)
				{
				// This means the PC side field is empty, so delete the corresponding device-side field.
				aContact.RemoveField(pos);
				}
			if (Klength)
				{
				// This means the PC side field is not empty, so add the corresponding contents.
				SetSpecificFieldL(aContact, aItems, aFieldType,	aMapping, aOption, aStartIndex, aEndIndex);
				}
			}
		}
	else if(pos != KErrNotFound)
		{
		aContact.RemoveField(pos);
		}
	}		

/**
 * Check if a specific field from vCard should be truncated or not.
 *
 * @param aPropertyName vCard property's name
 *
 * @return ETrue if the property must be truncated and EFalse otherwise
 *
 */
TBool CVCardToContactsAppConverter::PropertyTobeTruncated(const TPtrC8& aPropertyName) const
	{
	//Test is the field is one that has to be truncated
	if( aPropertyName == KVersitTokenN || aPropertyName == KVersitTokenORG || 
		aPropertyName == KVersitTokenTEL || aPropertyName == KVersitTokenEMAIL )
		return ETrue;
	//	
	return EFalse;
	}

/**
 * Check if a specific parameter value exists in a parameter array.
 *
 * @param aParamArray Array of property parameters in which value has to be searched.
 * @param aParamName A parameter name whose corresponding parameter value present in aParamArray has to be matched with aParamValue.
 * @param aParamValue A parameter value which will be used to search.
 * @return TBool ETrue if value is found in parameter array, otherwise EFalse.
 */
TBool CVCardToContactsAppConverter::IsParameterValuePresent(CArrayPtr<CParserParam>* aParamArray, const TDesC8& aParamName, const TDesC8& aParamValue)
	{
	if(!aParamArray)
		{
		return EFalse;
		}
	const TInt count = aParamArray->Count();
	for (TInt ii = 0; ii < count; ii++)
		{
		if((*aParamArray)[ii]->Name().CompareF(aParamValue) == 0)
			{
			return ETrue;		
			}
		else if((*aParamArray)[ii]->Name().CompareF(aParamName) == 0)
			{
			if((*aParamArray)[ii]->Value().CompareF(aParamValue) == 0)
				{
				return ETrue;		
				}	
			}
		}
	return EFalse;	
	}

/**
 * If there are multiple EMAIL and TEL properties in a vCard, then there should only be a single EMAIL or TEL property 
 * with a PREF parameter. If more than one EMAIL or TEL properties contain a PREF parameter,then only the first property
 * with the PREF parameter is stored and the other properties are stored with their other parameters but without the
 * PREF parameter.
 * 
 * @param aMainContact, Contact item for the vCard
 * @param aFieldType, the fieldtype in conjuction with the mapping is used to search for fields.
 * @param aMapping, the mapping is used for searching a field i.e. EMAIL or TEL fields.
 */

void CVCardToContactsAppConverter::AdjustForPrefRule(CContactItem& aContact, TFieldType aFieldType, TUid aMapping)
	{
	TInt pos = 0;
	TInt prefCount = 0;
	CContactItemFieldSet& contactItemFieldSet = aContact.CardFields();
	pos = contactItemFieldSet.FindNext(aFieldType, aMapping, pos);
	while (pos != KErrNotFound)
		{
		CContactItemField& contactItemField = contactItemFieldSet[pos];
		const CContentType& contentType = contactItemField.ContentType();
		TBool pref = contentType.ContainsFieldType(KUidContactFieldVCardMapPREF);
		if (pref)
			{
			++prefCount;
			if (prefCount > 1)
				{
				contactItemField.RemoveFieldType(KUidContactFieldVCardMapPREF);
				}
			}
		++pos;
		pos = contactItemFieldSet.FindNext(aFieldType, aMapping, pos);
		}
	}
