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