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