plugins/contacts/symbian/contactsmodel/cntvcard/cntvcardconverter.cpp
changeset 0 876b1a06bc25
equal deleted inserted replaced
-1:000000000000 0:876b1a06bc25
       
     1 /*
       
     2 * Copyright (c) 1997-2009 Nokia Corporation and/or its subsidiary(-ies).
       
     3 * All rights reserved.
       
     4 * This component and the accompanying materials are made available
       
     5 * under the terms of "Eclipse Public License v1.0"
       
     6 * which accompanies this distribution, and is available
       
     7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 *
       
     9 * Initial Contributors:
       
    10 * Nokia Corporation - initial contribution.
       
    11 *
       
    12 * Contributors:
       
    13 *
       
    14 * Description: 
       
    15 *
       
    16 */
       
    17 
       
    18 
       
    19 #include <cntvcard.h>
       
    20 #include "cntvcardutils.h"
       
    21 
       
    22 // System includes
       
    23 #include <badesca.h>
       
    24 #include <s32std.h>
       
    25 #include <s32mem.h>
       
    26 #include <ecom/implementationproxy.h>
       
    27 
       
    28 // User includes
       
    29 #include "cntprof.h"
       
    30 #include <cntfldst.h>
       
    31 #include <cntfield.h>
       
    32 #include <cntdef.h>
       
    33 #include <cntitem.h>
       
    34 
       
    35 #ifdef SYMBIAN_ENABLE_SPLIT_HEADERS
       
    36 #include "cntconvertercallback.h"
       
    37 #endif
       
    38 
       
    39 
       
    40 
       
    41 /** 
       
    42  * Imports one or more vCards from a read stream. The vCards are converted 
       
    43  * into contact items, and added to the database. If at least one contact
       
    44  * item was successfully imported, aImportSuccessful is set to ETrue. 
       
    45  * 
       
    46  * @param aDb Contacts database
       
    47  * @param aReadStream The stream to read from.
       
    48  * @param aImportSuccessful On return, ETrue if at least one contact was successfully imported. EFalse if not
       
    49  * @param aOption Import preferences (available options defined in CContactDatabase::TOptions)
       
    50  * @param aImportSingleContact Import a single vCard entity
       
    51  * @return Array of CContactItem objects
       
    52  */
       
    53 CArrayPtr<CContactItem>* CContactVCardConverter::ImportL(CContactDatabase& aDb,RReadStream& aReadStream,TBool& aImportSuccessful,TInt aOption,TBool aImportSingleContact)
       
    54 	{
       
    55 __START_PROFILE(23);
       
    56 	TBool increaseAccessCount=aOption & (EIncreaseAccessCount);
       
    57 	TBool decreaseAccessCount=aOption & (EDecreaseAccessCount);
       
    58 	aImportSuccessful=EFalse;	
       
    59 	CVCardToContactsAppConverter* converter=new(ELeave)	CVCardToContactsAppConverter;
       
    60 	CleanupStack::PushL(converter);
       
    61 	CArrayPtr<CContactItem>* contactItems=new(ELeave) CArrayPtrFlat<CContactItem>(4);
       
    62 	CleanupStack::PushL(TCleanupItem(CleanUpResetAndDestroy,contactItems));
       
    63 	TInt err=KErrNone;
       
    64 	if (!aImportSingleContact)
       
    65 		{
       
    66 		aDb.DatabaseBeginLC(EFalse);
       
    67 		}
       
    68 
       
    69 	while (err!=KErrEof)
       
    70 		{
       
    71 		CParserVCard* vCard= CParserVCard::NewL();
       
    72 		CleanupStack::PushL(vCard);
       
    73 __START_PROFILE(20);
       
    74 		TRAP(err,vCard->InternalizeL(aReadStream));
       
    75 __END_PROFILE(20);
       
    76 		if ((err != KErrNotFound) && (err != KErrEof))
       
    77 	  		{
       
    78 			User::LeaveIfError(err);
       
    79 				
       
    80 				TContactItemId pos = IsVCardMergeNeededL(*converter, *vCard, aDb, aOption);
       
    81 				//Check if we have been asked to replaced rather than merge the
       
    82 				//contact item.		
       
    83 				TContactItemId posId = KErrNotFound;
       
    84 				if ((aOption & EReplaceIfExists) && (pos != KErrNotFound))
       
    85 					{
       
    86 					posId = pos;
       
    87 					pos = KErrNotFound;						
       
    88 					}
       
    89 					
       
    90 				if (pos == KErrNotFound)
       
    91 					{
       
    92 					//this is a vCard previously exported but now deleted from the database
       
    93 					//treat as a new card
       
    94 					//or an existing contact item which is to be
       
    95 					//updated (replaced) with the given vCard.
       
    96 					doImportL(*converter, *vCard, aDb, aOption, increaseAccessCount, decreaseAccessCount, aImportSuccessful, contactItems, !aImportSingleContact, posId);
       
    97 					}
       
    98 				else
       
    99 					{
       
   100 					//this is a existing contact to update
       
   101 					CContactItem* item=aDb.OpenContactLX(pos);
       
   102 					CleanupStack::PushL(item);
       
   103 					
       
   104 					ModifyAccessCountL(*item, increaseAccessCount, decreaseAccessCount);
       
   105 
       
   106 					TUid type = item->Type();
       
   107 					if (type != KUidContactCard && type != KUidContactOwnCard)
       
   108 						{
       
   109 						User::Leave(KErrNotFound);
       
   110 						}
       
   111 
       
   112 					CArrayPtr<CParserProperty>* agentPropertyList = NULL;
       
   113 					
       
   114 					agentPropertyList = vCard->PropertyL(KVersitTokenAGENT, TUid::Uid(KVCardPropertyAgentUid));
       
   115 					
       
   116 					CleanupStack::PushL(TCleanupItem(CleanUpResetAndDestroy, agentPropertyList));
       
   117 					if(agentPropertyList && agentPropertyList->Count())
       
   118 						{
       
   119 						//Add agents to contact item and if needed, merge agentcards with existing contacts
       
   120 						HandleAgentsInVCardL(*converter, agentPropertyList, *item, aDb, aOption, increaseAccessCount, decreaseAccessCount, contactItems,ETrue);
       
   121 						}		
       
   122 					CleanupStack::PopAndDestroy(agentPropertyList);
       
   123 
       
   124 					TBool deleteItem = converter->MergeVCardWithContactItemL(*item, *vCard, CVCardToContactsAppConverter::EPreserveAllProperties, aOption);
       
   125 		
       
   126 					aDb.doCommitContactL(*item, ETrue, ETrue);  
       
   127 					
       
   128 					if (deleteItem)
       
   129 						{
       
   130 						CleanupStack::PopAndDestroy(2); // item, item close
       
   131 						aDb.doDeleteContactL(pos, ETrue, ETrue, decreaseAccessCount);
       
   132 						}
       
   133 					else
       
   134 						{
       
   135 						contactItems->AppendL(item);
       
   136 						CleanupStack::Pop(); // item
       
   137 						CleanupStack::PopAndDestroy(); // item close
       
   138 						}
       
   139 						
       
   140 					aImportSuccessful = ETrue;
       
   141  				}
       
   142 			}	
       
   143 		CleanupStack::PopAndDestroy();	// vCard
       
   144 		if (aImportSingleContact)
       
   145 			{
       
   146 			break;
       
   147 			}
       
   148 		}
       
   149 
       
   150 	if (!aImportSingleContact)
       
   151 		{
       
   152 		aDb.DatabaseCommitLP(EFalse);
       
   153 		}
       
   154 
       
   155 	CleanupStack::Pop();	// contactItems
       
   156 	CleanupStack::PopAndDestroy(); //converter
       
   157 __END_PROFILE(23);
       
   158 	return contactItems;
       
   159 	}
       
   160 
       
   161 
       
   162 /** 
       
   163  * Import a vCard object
       
   164  * 
       
   165  * @param aConverter Import converter
       
   166  * @param aVCard vCard object to import
       
   167  * @param aDb Contacts database reference
       
   168  * @param aOption Import preferences (available options defined in CContactDatabase::TOptions)
       
   169  * @param aIncAccessCount Increment the access count of contact items imported
       
   170  * @param aDecAccessCount Decrement the access count of contact items imported
       
   171  * @param aImportSuccessful vCard object was imported successfully
       
   172  * @param aContactItems Array of imported contact items
       
   173  * @param aIdForUpdate  contact ID which is to be updated / replaced with the imported vCrad.
       
   174  */
       
   175 void CContactVCardConverter::doImportL(CVCardToContactsAppConverter& aConverter, CParserVCard& aVCard, CContactDatabase& aDb, TInt aOption, TBool aIncAccessCount, TBool aDecAccessCount, TBool& aImportSuccessful, CArrayPtr<CContactItem>* aContactItems, TBool aIsInTransaction, TContactItemId aIdForUpdate)
       
   176 	{
       
   177 	CArrayPtr<CParserProperty>* agentPropertyList = NULL;
       
   178 	
       
   179 	agentPropertyList = aVCard.PropertyL(KVersitTokenAGENT, TUid::Uid(KVCardPropertyAgentUid));
       
   180 	CleanupStack::PushL(TCleanupItem(CleanUpResetAndDestroy, agentPropertyList));
       
   181 
       
   182 	CContactItem* mainItem = aConverter.GetVCardAsContactItemLC(aVCard, CVCardToContactsAppConverter::EPreserveAllProperties, aOption);
       
   183 
       
   184 	if (agentPropertyList && agentPropertyList->Count())
       
   185 		{
       
   186 		//Add agents to contact item and if needed, merge agentcards with existing contacts
       
   187 		HandleAgentsInVCardL(aConverter, agentPropertyList, *mainItem, aDb, aOption, aIncAccessCount, aDecAccessCount, aContactItems, EFalse);
       
   188 		}
       
   189 	
       
   190 	if	(mainItem->CardFields().Count()) // checks vcard is not empty
       
   191 		{
       
   192 		ModifyAccessCountL(*mainItem, aIncAccessCount, aDecAccessCount);
       
   193 		if	(aOption & ENullTemplateId)
       
   194 			{
       
   195 			mainItem->SetTemplateRefId(KNullContactId);
       
   196 			}
       
   197 		else
       
   198 			{
       
   199 			mainItem->SetTemplateRefId(KGoldenTemplateId);
       
   200 			}
       
   201 
       
   202 		//Check if we have been asked to replace (update) rather than add 
       
   203 		//the contact item.
       
   204 		if ((aOption & EReplaceIfExists) && (aIdForUpdate != KErrNotFound))
       
   205 			{
       
   206 			CContactItem* oldItem;
       
   207 			oldItem = aDb.OpenContactL(aIdForUpdate);
       
   208 			CleanupStack::PushL(oldItem);								
       
   209 			CContactItemFieldSet& fieldSet = oldItem->CardFields();			
       
   210 			fieldSet.Reset();			
       
   211 			aDb.CommitContactL(*oldItem);
       
   212 			CleanupStack::PopAndDestroy(oldItem);
       
   213 			CContactItem* updateItem = NULL;
       
   214 			updateItem = aDb.UpdateContactLC(aIdForUpdate, mainItem);
       
   215 			aContactItems->AppendL(updateItem); 
       
   216 			CleanupStack::Pop(updateItem);
       
   217 			CleanupStack::PopAndDestroy(mainItem);		
       
   218 			}
       
   219 		else
       
   220 			{			
       
   221 			aDb.doAddNewContactL(*mainItem, EFalse, aIsInTransaction);
       
   222 			aContactItems->AppendL(mainItem);
       
   223 			CleanupStack::Pop(mainItem);
       
   224 			}
       
   225 		aImportSuccessful = ETrue;
       
   226 		}
       
   227 	else
       
   228 		{
       
   229 		// Just cleanup
       
   230 		CleanupStack::PopAndDestroy(mainItem);
       
   231 		}
       
   232 	CleanupStack::PopAndDestroy(agentPropertyList);
       
   233 	}
       
   234 
       
   235 
       
   236 /** 
       
   237  * Export a contact as vCard.
       
   238  * 
       
   239  * @param aDb Contact database
       
   240  * @param aSelectedContactIds Array of contact items IDs to export
       
   241  * @param aWriteStream Stream to externalize vCard data to 
       
   242  * @param aOption Export preferences (available options defined in CContactDatabase::TOptions)
       
   243  * @param aCharSet Default character set to pass to Versit parser component
       
   244  * @param aExportPrivateFields Specify whether private fields are included
       
   245  * @param aCommitNumber Number of contacts to be exported before commit of database
       
   246  */
       
   247 void CContactVCardConverter::ExportL(CContactDatabase& aDb,const CContactIdArray& aSelectedContactIds,RWriteStream& aWriteStream,TInt aOption,const Versit::TVersitCharSet aCharSet, TBool aExportPrivateFields, TInt aCommitNumber=10)
       
   248 	{
       
   249 	const TBool increaseAccessCount=aOption & (EIncreaseAccessCount);
       
   250 	const TBool decreaseAccessCount=aOption & (EDecreaseAccessCount);
       
   251 	const TBool accessCountChanges=decreaseAccessCount || increaseAccessCount;
       
   252 	CContactsAppToVCardConverter* converter = new(ELeave)CContactsAppToVCardConverter(aDb.MachineId(), aCharSet, EVCard21);
       
   253 	CleanupStack::PushL(converter);
       
   254 
       
   255 	const TInt count=aSelectedContactIds.Count();
       
   256 	for (TInt ii=0; ii<count; ii++)
       
   257 		{
       
   258 		CContactItem* contactItem=NULL;
       
   259 		TContactItemId id=aSelectedContactIds[ii];
       
   260 		if (accessCountChanges)
       
   261 			{
       
   262 			if (ii % aCommitNumber == 0)	// 1st item of N
       
   263 				{
       
   264 				aDb.DatabaseBeginLC(EFalse);	
       
   265 				}
       
   266 			contactItem=aDb.OpenContactLX(id);
       
   267 			CleanupStack::PushL(contactItem);
       
   268 			}
       
   269 		else
       
   270 			{
       
   271 			contactItem=aDb.ReadContactLC(id,*aDb.AllFieldsView());
       
   272 			}
       
   273 		
       
   274 		CContactItemFieldSet& fields=contactItem->CardFields(); 
       
   275 		TInt agentPos = KContactFieldSetSearchAll; 
       
   276 
       
   277 		CContactItem* agentItem = NULL;
       
   278 		CArrayPtr<CContactItem>* agentItemArray = new(ELeave) CArrayPtrFlat<CContactItem>(4);
       
   279 		CleanupStack::PushL(TCleanupItem(CleanUpResetAndDestroy, agentItemArray));
       
   280 
       
   281 		while(1)
       
   282 			{
       
   283 			agentPos = fields.FindNext(KUidContactFieldVCardMapAGENT, agentPos);
       
   284 			TContactItemId agentId;
       
   285 			
       
   286 			if (agentPos != KErrNotFound)
       
   287 				{
       
   288 		  		agentId = fields[agentPos].AgentStorage()->Value();		
       
   289 			  	TRAPD(err, agentItem = aDb.ReadContactL(agentId, *aDb.AllFieldsView()));
       
   290 
       
   291 				if(err != KErrNotFound) 
       
   292 	 				{
       
   293 	 				User::LeaveIfError(err);
       
   294 					agentItemArray->AppendL(agentItem);
       
   295 				  	}
       
   296 				++agentPos;
       
   297 				agentItem = NULL;
       
   298 				}
       
   299 				
       
   300 			else
       
   301 				{
       
   302 				break;
       
   303 				}
       
   304 		}//while
       
   305 		CParserVCard* vCard=converter->GetContactItemAsVCardL(contactItem, agentItemArray, aOption, aExportPrivateFields);
       
   306 	
       
   307  		CleanupStack::PushL(vCard);
       
   308 		vCard->SetDefaultCharSet(aCharSet);
       
   309 		vCard->ExternalizeL(aWriteStream); 
       
   310 		CleanupStack::PopAndDestroy(vCard);
       
   311 
       
   312 		CleanupStack::PopAndDestroy(agentItemArray);
       
   313 			
       
   314 		if (accessCountChanges)
       
   315 			{
       
   316 			if (decreaseAccessCount)
       
   317 				{
       
   318 				contactItem->DecAccessCount();
       
   319 				}
       
   320 			if (increaseAccessCount)
       
   321 				{
       
   322 				contactItem->IncAccessCount();
       
   323 				}
       
   324 			aDb.doCommitContactL(*contactItem,ETrue,EFalse);	// commit every 10 by default
       
   325 			CleanupStack::PopAndDestroy(); // contactItem
       
   326 			CleanupStack::PopAndDestroy(); // contact close from OpenContactLX
       
   327 			// Nth item or last item
       
   328 			if (((ii + 1) % aCommitNumber == 0) || ii == (count - 1))
       
   329 				{
       
   330 				aDb.DatabaseCommitLP(EFalse);
       
   331 				}
       
   332 			}
       
   333 		else	// access count does not change
       
   334 			{
       
   335 			CleanupStack::PopAndDestroy(); // contactItem
       
   336 			}
       
   337 		}
       
   338 		
       
   339 	CleanupStack::PopAndDestroy(converter); 
       
   340 	}
       
   341 
       
   342 
       
   343 TBool CContactVCardConverter::ContainsExportableData(const TDesC& aText)
       
   344 /**  
       
   345  * Two different export rules:
       
   346  * 
       
   347  * RULE 1: Single property values, e.g. TEL, LABEL, FN, NOTE etc etc
       
   348  * For this type of property value, whitespace is not exported to the PIM, i.e this property
       
   349  * value should not be sent to the PIM for merging.
       
   350  * 
       
   351  * RULE 2: Multi property values, e.g. ADR, N, ORG etc etc
       
   352  * 
       
   353  * a) NULL means that the field is not supported by the device. It does NOT mean that the
       
   354  * field should be deleted from the PIM. Therefore, when a NULL value is sent by the device to the PC
       
   355  * the existing PC-PIM contact field should be preserved.
       
   356  * 
       
   357  * b) a SINGLE SPACE means that the field is supported by the device, but currently has an empty value (i.e.
       
   358  * the field has been deleted or is currently empty).
       
   359  * 
       
   360  * =====> This implementation only current obeys RULE1.
       
   361  * 
       
   362  * @param aText The text to be analyzed
       
   363  */
       
   364 	{
       
   365 	return ContainsData(aText);
       
   366 	}
       
   367 
       
   368 TBool CContactVCardConverter::ContainsImportableData(const TDesC& aText, TVersitPropertyType aType, TCntVCardImportType aImportType)
       
   369 /**
       
   370  * Two different import rules:
       
   371  * 
       
   372  * RULE 1: Single property values, e.g. TEL, LABEL, FN, NOTE etc etc
       
   373  * For this type of property value, a NULL value (i.e aText.Length() == 0) means that
       
   374  * the specified field should be deleted from the contact card. Any whitespace will be
       
   375  * ignored and not imported into the Contacts Database.
       
   376  * 
       
   377  * RULE 2: Multi property values, e.g. ADR, N, ORG etc etc
       
   378  * PC-based PIM's have varying support for the different sub-fields within a multi-property
       
   379  * object. Therefore, in a multi-property value object, the following semantics are observed
       
   380  * 
       
   381  * a) NULL means that the field is not supported by the PIM software. It does NOT mean that the
       
   382  * field should be deleted. Therefore, when a NULL value is sent by the PC Sync Engine to the
       
   383  * device no changes should be made to the corresponding field in the contact card.
       
   384  * 
       
   385  * b) a SINGLE SPACE means that the field is supported by the PC PIM, but has an empty value (i.e.
       
   386  * the field has been deleted or is currently empty).
       
   387  * 
       
   388  * A further complication is that during a non-merge sync, its entirely possible for the PC to
       
   389  * send vCard data which is effectively empty. One such example is TimeIS data extracted from
       
   390  * MS Outlook which then sent to the device. In this case, even if the PIM shows an address
       
   391  * as being empty, TimeIS sync drivers still send this empty address, e.g.:
       
   392  * 
       
   393  * ADR;HOME: ;; ; ; ; ; 
       
   394  * 
       
   395  * In this instance, we must ensure that we do not import a completely empty property. Therefore
       
   396  * the SPACE/NULL syntax is only used explicitly during a MERGE operation. An INITIAL SYNC
       
   397  * doesn't need to understand the SPACE/NULL syntax.
       
   398  * 
       
   399  * =====> This implementation obeys both rules completely.
       
   400  * @param aText The text to be analyzed.
       
   401  * @param aType Specify if the property is a single or multi-fielded type
       
   402  * @param aImportType Specify if this is the first time the card is being imported or if its a merge.
       
   403 */
       
   404 	{
       
   405 	TBool validDataForImport = EFalse;
       
   406  
       
   407 	const TInt length = aText.Length();
       
   408 	if	(aType == EPropertyValueComposite)
       
   409 		{
       
   410 		if	(aImportType == ECntVCardImportTypeFirstSync)
       
   411 			{
       
   412 			if	(!length || (length == 1 && aText[0] == KContactVCardSpaceCharacter))
       
   413 				{
       
   414 				// We do not import either
       
   415 				//
       
   416 				// - empty (but supported) PIM fields 
       
   417 				// - empty (non supported) PIM fields
       
   418 				//
       
   419 				// during initial sync since these would lead to fields in the contact card
       
   420 				// which contain spaces.
       
   421 				validDataForImport = EFalse;
       
   422 				}
       
   423 			else
       
   424 				{
       
   425 				// If its non-whitespace, we'll allow it for import
       
   426 				validDataForImport = ContainsData(aText);
       
   427 				}
       
   428 			}
       
   429 		else if (aImportType == ECntVCardImportTypeMerge)
       
   430 			{
       
   431 			if	(!length)
       
   432 				{
       
   433 				// Don't process (i.e. delete) fields which are not supported by the PC PIM.
       
   434 				validDataForImport = EFalse;
       
   435 				}
       
   436 			else if	(length == 1 && aText[0] == KContactVCardSpaceCharacter)
       
   437 				{
       
   438 				// This field is empty in the PIM and therefore we must 'import it' (which in fact,
       
   439 				// means we must delete the corresponding device-side field).
       
   440 				validDataForImport = ETrue;
       
   441 				}
       
   442 			else 
       
   443 				{
       
   444 				// Otherwise check each character to weed out the whitespace
       
   445 				validDataForImport = ContainsData(aText);
       
   446 				}
       
   447 			}
       
   448 		else
       
   449 			Panic(ECntVPanicInvalidImportType);
       
   450 		}
       
   451 	else if (aType == EPropertyValueSingle)
       
   452 		{
       
   453 		// We must import (process) NULL values, since they effectively mean 'DELETE'
       
   454 		// We do not import fields containing only whitespace
       
   455 		validDataForImport = (!length || ContainsData(aText));
       
   456 		}
       
   457 	return validDataForImport;
       
   458 	}
       
   459 
       
   460 
       
   461 TBool CContactVCardConverter::ContainsData(const TDesC& aText)
       
   462 /**
       
   463  * Determine if the text contains any data that can be imported
       
   464  * @param aText The text to be analyzed.
       
   465 */
       
   466 	{
       
   467 	const TInt length = aText.Length();
       
   468 	TInt whiteSpaceCount = 0;
       
   469 	TChar character;
       
   470 
       
   471 	for(TInt i=0; i<length; i++)
       
   472 		{
       
   473 		// If the character is whitespace, then check the next character until
       
   474 		// all characters have been reached. If its not-whitespace, then this field
       
   475 		// contains data which can be exported.
       
   476 		character = aText[i];
       
   477 		if	(character.IsSpace())
       
   478 			{
       
   479 			++whiteSpaceCount;
       
   480 			}
       
   481 		else
       
   482 			{
       
   483 			return ETrue;
       
   484 			}
       
   485 		}
       
   486 	return (whiteSpaceCount < length);
       
   487 	}
       
   488 
       
   489 CContactVCardConverter* CContactVCardConverter::NewL()
       
   490 	{
       
   491 	return new(ELeave) CContactVCardConverter();
       
   492 	}
       
   493 	
       
   494 // Export the implementation collection function
       
   495 const TImplementationProxy ImplementationTable[] = 
       
   496     {
       
   497     IMPLEMENTATION_PROXY_ENTRY(0x102035F9, 	CContactVCardConverter::NewL),
       
   498     IMPLEMENTATION_PROXY_ENTRY(0xA00015C1, 	CPBAPContactVCardConverter::NewL)
       
   499     };
       
   500 
       
   501 EXPORT_C const TImplementationProxy* ImplementationGroupProxy(TInt& aTableCount)
       
   502     {
       
   503     aTableCount = sizeof(ImplementationTable) / sizeof(TImplementationProxy);
       
   504 
       
   505     return ImplementationTable;
       
   506     }
       
   507     
       
   508 void CPBAPContactVCardConverter::ExportL(CContactDatabase& aDb, const CContactIdArray& aSelectedContactIds, RWriteStream& aWriteStream, TInt aOption, const Versit::TVersitCharSet aCharSet, TBool aExportPrivateFields, TInt /*aCommitNumber = 10*/)
       
   509 	{
       
   510 	CContactsAppToVCardConverter* converter = new(ELeave)CContactsAppToVCardConverter(aDb.MachineId(), aCharSet, GetVersion());
       
   511 	CleanupStack::PushL(converter);
       
   512 
       
   513 	const TInt count = aSelectedContactIds.Count();
       
   514 
       
   515 	converter->SetFilter(PrepareFilterAndOption(aOption));
       
   516 	
       
   517 	for (TInt ii = 0; ii < count; ii++)
       
   518 		{
       
   519 		CContactItem* contactItem = NULL;
       
   520 		TContactItemId id = aSelectedContactIds[ii];
       
   521 		contactItem = aDb.ReadContactLC(id, *aDb.AllFieldsView());
       
   522 	
       
   523 		CParserVCard* vCard = converter->GetContactItemAsVCardL(contactItem, NULL, aOption, aExportPrivateFields);
       
   524  		CleanupStack::PushL(vCard);
       
   525 		vCard->SetDefaultCharSet(aCharSet);
       
   526 		//Intra-Contact Properties start
       
   527 		MConverterCallBack* clientCallback = GetCallback();
       
   528 		if(clientCallback)
       
   529 			{
       
   530 			CArrayPtr<CParserProperty>* intraPropertyList = NULL;
       
   531 			intraPropertyList = new(ELeave) CArrayPtrFlat<CParserProperty>(5);
       
   532 			CleanupStack::PushL(TCleanupItem(CVersitParser::ResetAndDestroyArrayOfProperties, intraPropertyList));
       
   533 			clientCallback->AddIntraContactPropertiesL(id, intraPropertyList);	
       
   534 			TInt count = intraPropertyList->Count();
       
   535 			for(TInt loop = 0; loop < count; ++loop)
       
   536 				{
       
   537 				CParserProperty* parserProperty = (*intraPropertyList)[loop];
       
   538 				(*intraPropertyList)[loop] = NULL;
       
   539 				//AddpropertyL takes ownership
       
   540 				vCard->AddPropertyL(parserProperty, ETrue);
       
   541 				}
       
   542 			CleanupStack::PopAndDestroy(intraPropertyList);
       
   543 			}
       
   544 		//Intra-Contact Properties ends
       
   545 		vCard->ExternalizeL(aWriteStream);
       
   546 		CleanupStack::PopAndDestroy(vCard); 
       
   547 		CleanupStack::PopAndDestroy(); // contactItem
       
   548 		}
       
   549 	CleanupStack::PopAndDestroy(converter); 
       
   550 	}
       
   551 
       
   552 TInt64 CPBAPContactVCardConverter::PrepareFilterAndOption(TInt& aOption)
       
   553 	{
       
   554 	//These fields are not required for PBAP
       
   555 	if(aOption & CContactVCardConverter::EIncludeX)
       
   556 		{
       
   557 		aOption ^= CContactVCardConverter::EIncludeX;
       
   558 		}
       
   559 	if(aOption & CContactVCardConverter::ETTFormat) 
       
   560 		{
       
   561 		aOption ^= CContactVCardConverter::ETTFormat;
       
   562 		}
       
   563 	if(aOption & CContactVCardConverter::EConnectWhitespace)
       
   564 		{
       
   565 		aOption ^= CContactVCardConverter::EConnectWhitespace;
       
   566 		}
       
   567 		
       
   568 	//Filter starts
       
   569 	TInt64 pbapFilter = GetFilter();
       
   570 	
       
   571 	//If bit filter is unset (0x0000000), then export all supported fields.
       
   572 	//0xFFFFFFF sets all the property bit fields specified by PBAP spec.
       
   573 	if(pbapFilter == 0)
       
   574 		{
       
   575 		pbapFilter = EAllProperties;
       
   576 		}
       
   577 	//if iExportTel is TRUE then TEL will be exported (even if empty)
       
   578 	if(IsExportTel())
       
   579 		{
       
   580 		pbapFilter |= EPropertyTEL;
       
   581 		}
       
   582 	//if iExportTel is FALSE then TEL will not be exported.
       
   583 	else
       
   584 		{ 
       
   585 		if(pbapFilter & EPropertyTEL)
       
   586 			{
       
   587 			pbapFilter ^= EPropertyTEL;
       
   588 			}
       
   589 		}			
       
   590 	pbapFilter |= EPropertyN; // Mandatory for both 2.1 and 3.0
       
   591 	//Export of FN property is mandatory for vCard 3.0 but for vCard 2.1 only if provided in filter.
       
   592 	if(GetVersion() == EPBAPVCard30) 
       
   593 		{
       
   594 		pbapFilter |= EPropertyFN;
       
   595 		//these properties are not supported for vCard3.0
       
   596 		if(pbapFilter & EPropertyAGENT)
       
   597 			{
       
   598 			pbapFilter ^= EPropertyAGENT;
       
   599 			}
       
   600 		if(pbapFilter & EPropertyTZ)
       
   601 			{
       
   602 			pbapFilter ^= EPropertyTZ;
       
   603 			}
       
   604 		if(pbapFilter & EPropertyGEO)
       
   605 			{
       
   606 			pbapFilter ^= EPropertyGEO;
       
   607 			}
       
   608 		if(pbapFilter & EPropertySOUND)
       
   609 			{
       
   610 			pbapFilter ^= EPropertySOUND;
       
   611 			}
       
   612 		if(pbapFilter & EPropertyCLASS)
       
   613 			{
       
   614 			pbapFilter ^= EPropertyCLASS;
       
   615 			}																			
       
   616 		}
       
   617 	//If UID is not in filter, it should not be exported,
       
   618 	if(!(pbapFilter & EPropertyUID))
       
   619 		{
       
   620 		aOption |= CContactVCardConverter::EExcludeUid;
       
   621 		}
       
   622 	return pbapFilter;
       
   623 	}
       
   624 
       
   625 CArrayPtr<CContactItem>* CPBAPContactVCardConverter::ImportL(CContactDatabase& /*aDb*/, RReadStream& /*aReadStream*/, TBool& /*aImportSuccessful*/, TInt /*aOption*/, TBool /*aImportSingleContact*/)
       
   626 	{
       
   627 	User::Leave(KErrNotSupported);
       
   628 	return NULL;
       
   629 	}
       
   630 
       
   631 CPBAPContactVCardConverter::CPBAPContactVCardConverter(TInt64 aFilter, MConverterCallBack* aCallback, TVCardVersion aVersion, TBool aExportTel):
       
   632 	iFilter(aFilter),
       
   633 	iCallback(aCallback),
       
   634 	iVersion(aVersion),
       
   635 	iExportTel(aExportTel)
       
   636 	{
       
   637 	}
       
   638 
       
   639 CPBAPContactVCardConverter* CPBAPContactVCardConverter::NewL(TAny* param)
       
   640 	{
       
   641 	TPluginParameters* ptr = static_cast<TPluginParameters*>(param);
       
   642 	CPBAPContactVCardConverter* self = new(ELeave) CPBAPContactVCardConverter(ptr->GetFilter(), ptr->GetCallback(), ptr->GetExportVersion(), ptr->IsExportTel());
       
   643 	return self;
       
   644 	}
       
   645 	
       
   646 TInt64 CPBAPContactVCardConverter::GetFilter()const
       
   647 	{
       
   648 	return iFilter;
       
   649 	}
       
   650 
       
   651 MConverterCallBack* CPBAPContactVCardConverter::GetCallback()const
       
   652 	{
       
   653 	return iCallback;
       
   654 	}
       
   655 
       
   656 TVCardVersion CPBAPContactVCardConverter::GetVersion ()const
       
   657 	{
       
   658 	return iVersion;
       
   659 	}
       
   660 
       
   661 TBool CPBAPContactVCardConverter::IsExportTel()const
       
   662 	{
       
   663 	return iExportTel;
       
   664 	}
       
   665 
       
   666 /** 
       
   667  * Checks if an incoming vCard should be merged to an existing contact in database or added as a new contact.
       
   668  * 
       
   669  * @param aConverter Import converter
       
   670  * @param aVCard vCard object of contact being imported. 
       
   671  * @param aDb Contacts database reference
       
   672  * @param aOption Import preferences (available options defined in CContactDatabase::TOptions)
       
   673  * @return  If incoming vCard has to be merged then contactItem Id of a contact, otherwise KErrNotFound.
       
   674  */
       
   675 TContactItemId CContactVCardConverter::IsVCardMergeNeededL(CVCardToContactsAppConverter& aConverter, CParserVCard& aVCard, CContactDatabase& aDb, TInt aOption)
       
   676 	{
       
   677 	TContactItemId pos = KErrNotFound;
       
   678 	CContactItem* contact = NULL;
       
   679 	TBuf<KUidStringLength> uidString;
       
   680 
       
   681 	//
       
   682 	// Have we been asked to ignore the UID property value?  If not,
       
   683 	// we must extract the contact ID from the UID.  Otherwise, assume
       
   684 	// that client which provided the EIgnoreUid option knows that this
       
   685 	// contact does not already exist in the database.
       
   686 	//
       
   687 
       
   688 	if (!(aOption & (EIgnoreUid)))
       
   689 		{
       
   690 		aConverter.GetVCardUidStringL(aVCard, uidString);	    				
       
   691 
       
   692 		//
       
   693 		// Is there a UID property value?  If so, check whether or not
       
   694 		// there is a Contact Item with the associated UID already
       
   695 		// present in the database.  If there is, the Contact Item will
       
   696 		// be updated rather than added.
       
   697 		//
       
   698 	
       
   699 		if (uidString.Length())
       
   700 			{
       
   701 			//
       
   702 			// Parse the UID property value to see if the Machine ID
       
   703 			// field matches this database's Machine ID (i.e. this vCard
       
   704 			// was originally created in this database and is being
       
   705 			// updated).  If the Machine IDs match then return the
       
   706 			// Contact ID field from the UID property value.
       
   707 			//
       
   708 
       
   709 			pos = ContactGuid::IsLocalContactUidString(uidString, aDb.MachineId());
       
   710 			//vCard was created from a contact in current database.
       
   711 			if (pos != KErrNotFound)
       
   712 				{
       
   713 				TRAPD(err, contact = aDb.ReadContactL(pos));
       
   714 				if (err == KErrNotFound)
       
   715 					{
       
   716 					pos = KErrNotFound;	
       
   717 					}
       
   718 				else
       
   719 					{
       
   720 					User::LeaveIfError(err);					
       
   721 					if (contact->IsDeleted())
       
   722 						{
       
   723 						//we dont want to update this contact as it has already been deleted.
       
   724 						//so assign it a value other than KErrNotFound, this will avoid execution of "if" condition below
       
   725 						//as we dont have to make use of ContactIdByGuidL since contact already exists in database.
       
   726 						pos = KErrGeneral;
       
   727 						}
       
   728 					delete contact;
       
   729 					}
       
   730 				}
       
   731 			//No Positon found in UID, so check using Guid of VCard.
       
   732 			if (pos == KErrNotFound)
       
   733 				{
       
   734 				//
       
   735 				// If the Contact item represented by ID extracted from the UID
       
   736 				// does not exist, then this vCard was either created elsewhere or the original
       
   737 				// contact has been deleted from the database.
       
   738 				// (Contacts Model on another device, PC application, etc).
       
   739 				// In this case we need to attempt to find the "foreign" UID
       
   740 				// in the database since it may have been imported
       
   741 				// previously and if so should be updated.
       
   742 				//
       
   743 
       
   744 				pos = aDb.ContactIdByGuidL(uidString);
       
   745 				if (pos != KNullContactId)
       
   746 					{
       
   747 					TRAPD(err, contact = aDb.ReadContactL(pos));
       
   748 					if (err != KErrNotFound)	
       
   749 						{
       
   750 						User::LeaveIfError(err);
       
   751 						if (contact->IsDeleted())
       
   752 							{
       
   753 							pos = KErrNotFound;
       
   754 							}
       
   755 						}
       
   756 					delete contact;
       
   757 					}
       
   758 				}						
       
   759 			}
       
   760 		}
       
   761 		
       
   762 		if (pos == KErrGeneral)
       
   763 			{
       
   764 			pos = KErrNotFound;	
       
   765 			}
       
   766 	return pos;
       
   767 	}
       
   768 
       
   769 /** 
       
   770  * Modifies access count of any contact.
       
   771  * 
       
   772  * @param aContact Contact item whose access count has to be modified.
       
   773  * @param aIncAccessCount Increment the access count of contact items imported
       
   774  * @param aDecAccessCount Decrement the access count of contact items imported
       
   775  */
       
   776 void CContactVCardConverter::ModifyAccessCountL(CContactItem& aContact, TBool aIncAccessCount, TBool aDecAccessCount)
       
   777 	{				
       
   778 	if (aIncAccessCount)
       
   779 		{
       
   780 		aContact.IncAccessCount();
       
   781 		}
       
   782 	if (aDecAccessCount)
       
   783 		{
       
   784 		aContact.DecAccessCount();
       
   785 		}
       
   786 	}
       
   787 
       
   788 /** 
       
   789  * Imports one or more agent vCard objects present in the vCard.
       
   790  * 
       
   791  * @param aConverter Import converter
       
   792  * @param aAgentProperties Array of agent properties extracted from parent vCard object.
       
   793  * @param aContact Contact item to which the agents will be added.
       
   794  * @param aDb Contacts database reference
       
   795  * @param aOption Import preferences (available options defined in CContactDatabase::TOptions)
       
   796  * @param aIncAccessCount Increment the access count of contact items imported
       
   797  * @param aDecAccessCount Decrement the access count of contact items imported
       
   798  * @param aImportSuccessful vCard object was imported successfully
       
   799  * @param aContactItems Array of imported contact items
       
   800  * @param aMerge  aContact is being merged or added as a new contact.
       
   801  */
       
   802 void CContactVCardConverter::HandleAgentsInVCardL(CVCardToContactsAppConverter& aConverter, CArrayPtr<CParserProperty>* aAgentProperties, CContactItem& aContact, CContactDatabase& aDb, TInt aOption, TBool aIncAccessCount, TBool aDecAccessCount, CArrayPtr<CContactItem>* aContactItemArray, TBool aMerge)
       
   803 	{
       
   804 	CContactItem* contact = NULL;
       
   805 	CContactItem* agentItem = NULL;
       
   806 	
       
   807 	__ASSERT_DEBUG(aAgentProperties->Count() != 0, User::Leave(KErrArgument));
       
   808 
       
   809 	RArray <TContactItemId> contactIdArray;
       
   810 	CleanupClosePushL(contactIdArray);
       
   811 
       
   812 	TContactItemId pos = KErrNotFound;
       
   813 	const TInt count = aAgentProperties->Count();
       
   814 	for(TInt loop = 0;loop < count;++loop)
       
   815 		{
       
   816 		CParserVCard* agentcard = static_cast<CParserPropertyValueAgent*>((*aAgentProperties)[loop]->Value())->Value();
       
   817 		pos = IsVCardMergeNeededL(aConverter, *agentcard, aDb, aOption);
       
   818 		if(pos != KErrNotFound) //contact similar to Agent exists in database, so merge both.
       
   819 			{
       
   820 			contact = aDb.OpenContactL(pos); 
       
   821 			CleanupStack::PushL(contact);
       
   822 			TBool deleteItem = aConverter.MergeVCardWithContactItemL(*contact, *agentcard, CVCardToContactsAppConverter::EPreserveAllProperties, aOption);
       
   823 			ModifyAccessCountL(*contact, aIncAccessCount, aDecAccessCount);
       
   824 			aDb.CommitContactL(*contact);
       
   825 			CleanupStack::PopAndDestroy(contact);				
       
   826 			if(deleteItem)
       
   827 				{
       
   828 				aDb.DeleteContactL(pos);	
       
   829 				}				
       
   830 			}
       
   831 		else //Agent present in vCard but should be added as a new contact
       
   832 			{
       
   833 			agentItem = aConverter.GetVCardAsContactItemLC(*agentcard, CVCardToContactsAppConverter::EPreserveAllProperties, aOption);
       
   834 			ModifyAccessCountL(*agentItem, aIncAccessCount, aDecAccessCount);
       
   835 			agentItem->SetLastModified(aContact.LastModified());
       
   836 			pos = aDb.doAddNewContactL(*agentItem, EFalse, ETrue);
       
   837 			aContactItemArray->AppendL(agentItem);
       
   838 			CleanupStack::Pop(agentItem);
       
   839 			}	
       
   840 		contactIdArray.Append(pos);
       
   841 		}
       
   842 		
       
   843 	
       
   844 	const TInt idCount = contactIdArray.Count();
       
   845 	if (idCount)
       
   846 		{
       
   847 		for(TInt idLoop = 0;idLoop < idCount;++idLoop)	
       
   848 			{
       
   849 			// Include agendid in a field in maincontact
       
   850 			CContactItemField* field = CContactItemField::NewLC(KStorageTypeContactItemId);
       
   851 			field->SetMapping(KUidContactFieldVCardMapAGENT);
       
   852 			field->AgentStorage()->SetAgentId(contactIdArray[idLoop]);
       
   853 			if (aMerge)
       
   854 				{
       
   855 				aContact.CardFields().UpdateFieldSyncL(*field, idLoop+1);
       
   856 				CleanupStack::PopAndDestroy(); // field
       
   857 				}
       
   858 			else
       
   859 				{
       
   860 				aContact.AddFieldL(*field);				
       
   861 				CleanupStack::Pop(); // takes ownership of field.			
       
   862 				}
       
   863 			}
       
   864 		}
       
   865 	CleanupStack::PopAndDestroy(&contactIdArray);
       
   866 	}
       
   867  
       
   868     
       
   869