/* * Copyright (c) 1997-2009 Nokia Corporation and/or its subsidiary(-ies). * All rights reserved. * This component and the accompanying materials are made available * under the terms of "Eclipse Public License v1.0" * which accompanies this distribution, and is available * at the URL "http://www.eclipse.org/legal/epl-v10.html". * * Initial Contributors: * Nokia Corporation - initial contribution. * * Contributors: * * Description: * */ #include "cntvcardutils.h" // System includes #include #include #include // User includes #include #include #include #include // Constants const TInt KVCardImportAddressArrayGranularity = 4; const TInt KContactGivenName = 1; const TInt KContactFamilyName = 0; const TInt KContactAdditionalName = 2; const TInt KContactPrefixName = 3; const TInt KContactSuffixName = 4; const TInt KContactPostOffice = 0; const TInt KContactExtendedAddress = 1; const TInt KContactAddress = 2; const TInt KContactLocality = 3; const TInt KContactRegion = 4; const TInt KContactPostcode = 5; const TInt KContactCountry = 6; #ifdef SYMBIAN_ENABLE_SPLIT_HEADERS const TInt KContactMaxFieldNumber = 32; #endif /** * Delete name fields from a contact * * @param aContact Contact item */ void CVCardToContactsAppConverter::DeleteNameFields(CContactItem& aContact) { const CContactItemFieldSet& fieldSet = aContact.CardFields(); // DeleteField(aContact, fieldSet, KUidContactFieldGivenName, KUidContactFieldVCardMapUnusedN); DeleteField(aContact, fieldSet, KUidContactFieldFamilyName, KUidContactFieldVCardMapUnusedN); DeleteField(aContact, fieldSet, KUidContactFieldAdditionalName, KUidContactFieldVCardMapUnusedN); DeleteField(aContact, fieldSet, KUidContactFieldPrefixName, KUidContactFieldVCardMapUnusedN); DeleteField(aContact, fieldSet, KUidContactFieldSuffixName, KUidContactFieldVCardMapUnusedN); } /** * Delete a specific field from the contact card * * @param aContact The contact * @param aFieldSet The contact's field set * @param aFieldType The type of field to delete from the field set * @param aMapping The additional mapping which the field must contain for it to be deleted */ void CVCardToContactsAppConverter::DeleteField(CContactItem& aContact, const CContactItemFieldSet& aFieldSet, TFieldType aFieldType, TUid aMapping) { const TInt pos = aFieldSet.Find(aFieldType, aMapping); if (pos != KErrNotFound) aContact.RemoveField(pos); } void CVCardToContactsAppConverter::MergeSpecifiedNameFieldL(CContactItem& aContact, TFieldType aFieldType, CVCardItemAndLabel& aNames, TInt aOption, TInt aNameIndex) /** * Merge specific name fields from a contact * * @param aContact Contact item to add fields to * @param aFieldType Field type of field to add (TFieldType) * @param aNames An object containing the name and labels for 'N' property fields * @param aOption Import preferences (available options defined in CContactDatabase::TOptions) * @param aNameIndex Index into aNames */ { CContactItemFieldSet& oldfieldset=aContact.CardFields(); const TInt pos = oldfieldset.Find(aFieldType, KUidContactFieldVCardMapUnusedN); const TBool processWhitespace = (aOption & CContactVCardConverter::EConnectWhitespace); // First check whether the field is present in the contact card // Also verify that the array of address sub-values actually contains a specific // value for the requested index. if (aNames.ItemCount() > aNameIndex) { const TPtrC pValue = aNames.Item(aNameIndex); const TInt length = pValue.Length(); if (processWhitespace) { TBool isSingleSpace = EFalse; if (length == 1) { isSingleSpace = (pValue[0] == KContactVCardSpaceCharacter); } if ((pos != KErrNotFound) && (length || isSingleSpace)) { // This means the PC side field is empty, so delete the corresponding device-side field. aContact.RemoveField(pos); } if (length && !isSingleSpace) { // This means the PC side field is unsupported, so ignore the corresponding contents. TInt insertPos = 0; SetNameFieldL(aNames, aContact, aOption, aNameIndex, aFieldType, insertPos); } } else { if (pos != KErrNotFound) { // This means the PC side field is empty, so delete the corresponding device-side field. aContact.RemoveField(pos); } if (length) { // This means the PC side field is not empty, so add the corresponding contents. TInt insertPos = 0; SetNameFieldL(aNames, aContact, aOption, aNameIndex, aFieldType, insertPos); } } } } void CVCardToContactsAppConverter::MergeNameFieldsL(CContactItem& aContact, CVCardItemAndLabel& aNames, TInt aOption, TBool aTreatAsPrn) /** * Merge name fields from a contact * * @param aContact Contact item to add fields to * @param aNames An object containing the name and labels for 'N' property fields * @param aOption Import preferences (available options defined in CContactDatabase::TOptions) */ { if(aTreatAsPrn) { MergeSpecifiedNameFieldL(aContact, KUidContactFieldGivenNamePronunciation, aNames, aOption, KContactGivenName); MergeSpecifiedNameFieldL(aContact, KUidContactFieldFamilyNamePronunciation, aNames, aOption, KContactFamilyName); } else { MergeSpecifiedNameFieldL(aContact, KUidContactFieldPrefixName, aNames, aOption, KContactPrefixName); MergeSpecifiedNameFieldL(aContact, KUidContactFieldGivenName, aNames, aOption, KContactGivenName); MergeSpecifiedNameFieldL(aContact, KUidContactFieldAdditionalName, aNames, aOption, KContactAdditionalName); MergeSpecifiedNameFieldL(aContact, KUidContactFieldFamilyName, aNames, aOption, KContactFamilyName); MergeSpecifiedNameFieldL(aContact, KUidContactFieldSuffixName, aNames, aOption, KContactSuffixName); } } void CVCardToContactsAppConverter::MergeSpecifiedAddressFieldL(CContactItem& aContact, const CVCardAddress& aAddress, const TUid& aFieldUid, const TUid& aMappingUid, TInt aAddressIndex, TInt aOption) /** * Merge specific address fields from a contact * * @param aContact Contact item to add fields to * @param aUid The Uid of the contact * @param aFieldUid Contacts database Field ID * @param aMappingUid VCard Mapping ID * @param aAddress An object containing the name and labels for 'ADR' property fields * @param aAddressIndex Index into aAddress * @param aOption Import preferences (available options defined in CContactDatabase::TOptions) */ { CContactItemFieldSet& oldfieldset = aContact.CardFields(); TInt pos = 0; const TBool processWhitespace = (aOption & CContactVCardConverter::EConnectWhitespace); if (aAddress.Mapping() == KNullUid) { TBool fieldFound = EFalse; TInt startPos = KContactFieldSetSearchAll; while(!fieldFound && !(pos == KErrNotFound)) { pos = oldfieldset.FindNext(aMappingUid, startPos); startPos = pos + 1; if (pos != KErrNotFound ) { CContactItemField& tempField = oldfieldset[pos]; const CContentType& tempContentType = tempField.ContentType(); TBool additionalMapFound = EFalse; additionalMapFound |= tempContentType.ContainsFieldType(KUidContactFieldVCardMapHOME); additionalMapFound |= tempContentType.ContainsFieldType(KUidContactFieldVCardMapWORK); additionalMapFound |= tempContentType.ContainsFieldType(KUidContactFieldVCardMapPREF); if (!additionalMapFound) { fieldFound = ETrue; } } } } else { pos = oldfieldset.Find(aAddress.Mapping(), aMappingUid); } // First check whether the field is present in the contact card // Also verify that the array of address sub-values actually contains a specific // value for the requested index. if (aAddress.ItemCount() > aAddressIndex) { const TPtrC pValue = aAddress.Item(aAddressIndex); const TInt length = pValue.Length(); if (processWhitespace) { TBool isSingleSpace = EFalse; if (length == 1) { isSingleSpace = (pValue[0] == KContactVCardSpaceCharacter); } if ((length || isSingleSpace) && (pos != KErrNotFound)) { // This means the PC side field is empty, so delete the corresponding device-side field. aContact.RemoveField(pos); } if (length && !isSingleSpace) { TInt insertPos = 0; SetAddressFieldL(aAddress, aContact, aOption, aAddressIndex, aFieldUid, insertPos, aMappingUid); } } else { if (pos != KErrNotFound) { // This means the PC side field is empty, so delete the corresponding device-side field. aContact.RemoveField(pos); } if (length) { // This means the PC side field is not empty, so add the corresponding contents. TInt insertPos = 0; SetAddressFieldL(aAddress, aContact, aOption, aAddressIndex, aFieldUid, insertPos, aMappingUid); } } } } void CVCardToContactsAppConverter::MergeAddressFieldsL(CContactItem& aContact, const CVCardAddress& aAddress, TInt aOption) /** * Merge a specific field from the contact card * * @param aContact Contact item to add fields to * @param aUid The Uid of the contact * @param aAddresses Address of locally stored contact * @param aOption Import preferences (available options defined in CContactDatabase::TOptions) */ { MergeSpecifiedAddressFieldL(aContact, aAddress, KUidContactFieldPostOffice, KUidContactFieldVCardMapPOSTOFFICE, KContactPostOffice, aOption); MergeSpecifiedAddressFieldL(aContact, aAddress, KUidContactFieldExtendedAddress, KUidContactFieldVCardMapEXTENDEDADR, KContactExtendedAddress, aOption); MergeSpecifiedAddressFieldL(aContact, aAddress, KUidContactFieldAddress, KUidContactFieldVCardMapADR, KContactAddress, aOption); MergeSpecifiedAddressFieldL(aContact, aAddress, KUidContactFieldLocality, KUidContactFieldVCardMapLOCALITY, KContactLocality, aOption); MergeSpecifiedAddressFieldL(aContact, aAddress, KUidContactFieldRegion, KUidContactFieldVCardMapREGION, KContactRegion, aOption); MergeSpecifiedAddressFieldL(aContact, aAddress, KUidContactFieldPostcode, KUidContactFieldVCardMapPOSTCODE, KContactPostcode, aOption); MergeSpecifiedAddressFieldL(aContact, aAddress, KUidContactFieldCountry, KUidContactFieldVCardMapCOUNTRY, KContactCountry, aOption); } TBool CVCardToContactsAppConverter::MergeVCardWithContactItemL(CContactItem &aContact, CParserVCard& aVCard, TUnknownPropertyBehaviour aUnknownPropertyBehaviour, TInt aOption) /** * Merge a vCard with an existing contact item * * @param aContact Contact item to add fields to * @param aVCard vCard parser object * @param aUnknownPropertyBehaviour Specifies how extension properties are handled * @param aOption Import preferences (available options defined in CContactDatabase::TOptions) * @return ETrue if contact item should be deleted, EFalse otherwise */ { TInt ii = 0; TInt count = 0; // TBool deleteContact = ETrue; iUnknownPropertyBehaviour = aUnknownPropertyBehaviour; TTime lastModified; GetVCardModifiedTimeL(aVCard, lastModified); // We're performing a merge SetImportType(ECntVCardImportTypeMerge); // Get Name CVCardItemAndLabel* names = GetContactNameLC(aVCard, aOption); if (names && names->ItemCount()) { MergeNameFieldsL(aContact, *names, aOption); deleteContact = EFalse; } CleanupStack::PopAndDestroy(names); // Get Name pronunciation names = GetContactNamePrnLC(aVCard, aOption); if (names && names->ItemCount()) { MergeNameFieldsL(aContact, *names, aOption, ETrue); } CleanupStack::PopAndDestroy(names); // Create address container RPointerArray addresses(KVCardImportAddressArrayGranularity); CleanupStack::PushL(TCleanupItem(CVCardItemAndLabel::CleanUpResetDestroyAndCloseArray, &addresses)); // Get addresses from the vCard. This actually only retrieves four types of addresses: // HOME, WORK, PREF and now additionally, 'general' addresses GetAddressesL(aVCard, aOption, addresses); // Import each of the located address field into the contact card count = addresses.Count(); for (ii=0; iiDes()); TInt classCount = GetSingleInstanceL(aVCard, KVersitTokenClass, ptr); if(classCount) { MergeSingleInstanceL(aContact,ptr,KUidContactFieldClass, KUidContactFieldVCardMapClass,aOption); deleteContact = EFalse; } CleanupStack::PopAndDestroy(singleClass); // Get other properties CArrayPtr* arrayOfProperties = aVCard.ArrayOfProperties(); CleanupStack::PushL(TCleanupItem(CleanUpResetAndDestroy, arrayOfProperties)); count = arrayOfProperties->Count(); //Within this loop we store the content type of the last field updated. //If the content type of the current field matches, we update fieldCount //This holds the position of the field to update, to prevent us from //overwriting the field we just updated. TInt fieldCount = 0; CContactItemField* oldField = NULL; CContactItemField* newField = NULL; for (ii = 0; ii < count; ii++) { if (((*arrayOfProperties)[ii]->Name() != KVersitTokenADR) && ((*arrayOfProperties)[ii]->Name() != KVersitTokenORG) && ((*arrayOfProperties)[ii]->Name() != KVersitTokenClass)) { TBool unsupportedProperty=EFalse; newField = GetVCardPropertyAsContactFieldLC((*arrayOfProperties)[ii], aOption,unsupportedProperty); if (newField) { TInt ttnumber; if (!oldField) { fieldCount = 1; ttnumber = 1; if (aOption & CContactVCardConverter::ETTFormat) { ttnumber = GetVCardPropertyTTNumber((*arrayOfProperties)[ii]); } } else if (newField->ContentType().IsEqualForSyncUpdate(oldField->ContentType())) { ttnumber = ++fieldCount; } else { fieldCount = 1; ttnumber = 1; if (aOption & CContactVCardConverter::ETTFormat) { ttnumber = GetVCardPropertyTTNumber((*arrayOfProperties)[ii]); } } if (oldField) { CleanupStack::Pop(); //newField CleanupStack::PopAndDestroy(oldField); oldField = NULL; CleanupStack::PushL(newField); } oldField = newField; aContact.CardFields().UpdateFieldSyncL(*newField, ttnumber); if(newField->Storage()->IsFull()) { deleteContact = EFalse; } newField = NULL; } else if (unsupportedProperty) { CleanupStack::Pop(); deleteContact = EFalse; } else{ CleanupStack::Pop(); } } } //Remove all empty fields after merge CContactItemFieldSet &fieldset = aContact.CardFields(); TInt i = 0; for(; i < fieldset.Count(); ++i) { if( !fieldset[i].Storage()->IsFull() ) { fieldset.Remove( i-- ); } } if (newField) //coverity [dead_error_begin] { CleanupStack::PopAndDestroy(newField); newField = NULL; } if (oldField) { CleanupStack::PopAndDestroy(oldField); oldField = NULL; } if (lastModified != Time::NullTTime()) { aContact.SetLastModified(lastModified); } CleanupStack::PopAndDestroy(arrayOfProperties); return(deleteContact); } /** * Set a name field. Only creates a field if the specified name text is not empty. * * @param aNames An object containing the name and labels for 'N' property fields * @param aContact Contact item to add fields to * @param aOption Import preferences (available options defined in CContactDatabase::TOptions) * @param aIndex Array position of field to use * @param aFieldType Field type of field to add (TFieldType) * @param aInsertPos Position in contact field set to add field */ void CVCardToContactsAppConverter::SetNameFieldL(const CVCardItemAndLabel& aNames, CContactItem& aContact, TInt aOption, TInt aIndex, TFieldType aFieldType, TInt& aInsertPos) const { const TInt count = aNames.ItemCount(); const TInt labelCount = aNames.LabelCount(); // if (aIndex >= 0 && aIndex < count) { const TPtrC pFieldText(aNames.Item(aIndex)); TBool addField = EFalse; if (CContactVCardConverter::EConnectWhitespace & aOption) { addField = CContactVCardConverter::ContainsImportableData(pFieldText, CContactVCardConverter::EPropertyValueComposite, ImportType()); } else { addField = (pFieldText.Length()); } // Only add the field if it contains some data if (addField) { CContactItemField* contactItemField = CContactItemField::NewLC(KStorageTypeText, aFieldType); contactItemField->SetMapping(KUidContactFieldVCardMapUnusedN); if ((aOption & CContactVCardConverter::EIncludeX) && (aIndex >= 0 && aIndex < labelCount)) contactItemField->SetLabelL(aNames.Label(aIndex)); // HBufC* encodedText = EncodeL(pFieldText, ETrue); contactItemField->TextStorage()->SetText(encodedText); // takes ownership aContact.InsertFieldL(*contactItemField, aInsertPos++); CleanupStack::Pop(contactItemField); } } } HBufC* CVCardToContactsAppConverter::EncodeL(const TDesC& aText, TBool aTextTobeTruncated) const { const TUid KUidTextToEtextNoTrim={0x10281B4C}; // Make a copy of aText and truncate if necessary. TPtr truncText(const_cast(aText.Ptr()),aText.Length()); if(aTextTobeTruncated) truncText.SetLength(aText.Length()>KCntMaxTextFieldLength ? KCntMaxTextFieldLength : aText.Length()); else truncText.SetLength(aText.Length()); HBufC8* text=HBufC8::NewLC(truncText.Length()*2); TPtr8 ptr = text->Des(); TInt i; for (i=0; i < truncText.Length(); i++) { ptr.Append(truncText[i] & 0x00FF); ptr.Append((truncText[i] >> 8) & 0x00FF); } CCnaConverterList* convList=CCnaConverterList::NewLC(); CConverterBase* conv = convList->NewConverterL(KUidTextToEtextNoTrim); if (!conv) { CleanupStack::PopAndDestroy(); // convList User::Leave(KErrNotSupported); } CleanupStack::PushL(conv); CBufFlat* decodeBuffer = CBufFlat::NewL(256); CleanupStack::PushL(decodeBuffer); CBufFlat* encodedBuffer = CBufFlat::NewL(256); CleanupStack::PushL(encodedBuffer); decodeBuffer->InsertL(0,ptr); RBufReadStream readStream; RBufWriteStream writeStream; readStream.Open(*decodeBuffer); writeStream.Open(*encodedBuffer); conv->ConvertObjectL(readStream, writeStream); readStream.Close(); TInt size=encodedBuffer->Size(); HBufC* writeBuf=HBufC::NewLC(size); TPtr resulttext = writeBuf->Des(); for(i = 0; i < (size - 1); i += 2) { resulttext.Append((encodedBuffer->Ptr(0)[i + 1] << 8) | encodedBuffer->Ptr(0)[i]); } writeStream.CommitL(); writeStream.Close(); CleanupStack::Pop(); // writebuf CleanupStack::PopAndDestroy(2); // buffers CleanupStack::PopAndDestroy(2); //conv+convList CleanupStack::PopAndDestroy(); //text return writeBuf; } /** * Set name fields * * @param aNames An object containing the name and labels for 'N' property fields * @param aContact Contact item to add fields to * @param aOption Import preferences (available options defined in CContactDatabase::TOptions) * @param aTreatAsPronunciation If true, save name data in pronunciation fields, otherwise save as an actual name (the default) */ void CVCardToContactsAppConverter::SetNameFieldsL(const CVCardItemAndLabel& aNames, CContactItem& aContact, TInt aOption, TBool aTreatAsPronunciation) const { TInt insertPos=0; if( aTreatAsPronunciation ) { SetNameFieldL(aNames, aContact, aOption, KContactGivenName, KUidContactFieldGivenNamePronunciation, insertPos); SetNameFieldL(aNames, aContact, aOption, KContactFamilyName, KUidContactFieldFamilyNamePronunciation, insertPos); } else { SetNameFieldL(aNames, aContact, aOption, KContactPrefixName, KUidContactFieldPrefixName, insertPos); SetNameFieldL(aNames, aContact, aOption, KContactGivenName, KUidContactFieldGivenName, insertPos); SetNameFieldL(aNames, aContact, aOption, KContactAdditionalName, KUidContactFieldAdditionalName, insertPos); SetNameFieldL(aNames, aContact, aOption, KContactFamilyName, KUidContactFieldFamilyName, insertPos); SetNameFieldL(aNames, aContact, aOption, KContactSuffixName, KUidContactFieldSuffixName, insertPos); } } void CVCardToContactsAppConverter::SetAddressFieldL(const CVCardAddress& aAddress, CContactItem& aContact, TInt aOption, TInt aIndex, TFieldType aFieldType, TInt& aInsertPos, TUid aMapping) const /** * Set an address field * * @param aAddress An object containing the name and labels for 'ADR' property fields * @param aContact Contact item to add fields to * @param aOption Import preferences (available options defined in CContactVCardConverter::TOptions) * @param aIndex Array position of field to use * @param aFieldType Contact field type for address field * @param aInsertPos Position in contact field set to add field * @param aMapping vCard field mapping (eg. KUidContactFieldVCardMapPOSTCODE) */ { const TInt count = aAddress.ItemCount(); if (aIndex >= 0 && aIndex < count) { const TPtrC pFieldText(aAddress.Item(aIndex)); TBool doInsert = ETrue; // By default we process the data, but if the TimeIS flag is set, then we need to check the content first. if (CContactVCardConverter::EConnectWhitespace & aOption) { doInsert = (CContactVCardConverter::ContainsImportableData(pFieldText, CContactVCardConverter::EPropertyValueComposite, ImportType())); } if (doInsert) { CContentType* content = CContentType::NewL(aFieldType, aMapping); CleanupStack::PushL(content); if (aAddress.Mapping() != KNullUid) // KNullUid corresponds to general address { content->AddFieldTypeL(aAddress.Mapping()); } CContactItemField* contactItemField = CContactItemField::NewLC(KStorageTypeText, *content); if ((aOption & CContactVCardConverter::EIncludeX) && (aIndex >= 0 && aIndex < aAddress.LabelCount())) { // get the correct label TBuf<25> labelName(KContactVarVCardXDashEPOCCNTMODEL); labelName.AppendFormat(KContactVarVCardLABELn, aIndex); TInt position=KErrNotFound; TInt err = aAddress.FindLabel(labelName, position); if (err==KErrNone && position!=KErrNotFound) contactItemField->SetLabelL(aAddress.Label(position)); } HBufC* encodedText = EncodeL(pFieldText, ETrue); contactItemField->TextStorage()->SetText(encodedText); // takes ownership // The contact takes ownership of the field. aContact.InsertFieldL(*contactItemField, aInsertPos++); CleanupStack::Pop(contactItemField); CleanupStack::PopAndDestroy(content); } } } void CVCardToContactsAppConverter::SetAddressFieldsL(const CVCardAddress& aAddress, CContactItem& aContact, TInt aOption) const /** * Set the address fields * * @param aAddressses An object containing the name and labels for 'ADR' property fields * @param aContact Contact item to add fields to * @param aOption Import preferences (available options defined in CContactDatabase::TOptions) */ { TInt insertPos = 0; SetAddressFieldL(aAddress, aContact, aOption, KContactPostOffice, KUidContactFieldPostOffice, insertPos, KUidContactFieldVCardMapPOSTOFFICE); SetAddressFieldL(aAddress, aContact, aOption, KContactExtendedAddress, KUidContactFieldExtendedAddress, insertPos, KUidContactFieldVCardMapEXTENDEDADR); SetAddressFieldL(aAddress, aContact, aOption, KContactAddress, KUidContactFieldAddress, insertPos, KUidContactFieldVCardMapADR); SetAddressFieldL(aAddress, aContact, aOption, KContactLocality, KUidContactFieldLocality, insertPos, KUidContactFieldVCardMapLOCALITY); SetAddressFieldL(aAddress, aContact, aOption, KContactRegion, KUidContactFieldRegion, insertPos, KUidContactFieldVCardMapREGION); SetAddressFieldL(aAddress, aContact, aOption, KContactPostcode, KUidContactFieldPostcode, insertPos, KUidContactFieldVCardMapPOSTCODE); SetAddressFieldL(aAddress, aContact, aOption, KContactCountry, KUidContactFieldCountry, insertPos, KUidContactFieldVCardMapCOUNTRY); } /** * Convert aVCard into a ContactItem. This method leaves a CContactItem instance on the cleanup stack upon exit. * * @param aVCard vCard parser object * @param aAgentContact Agent contact item. Note that this object is passed by reference but is not * left on the cleanup stack upon exit from this function. Clients shoul re-Push * this object if needed. * @param aUnknownPropertyBehaviour Specifies how extension properties are handled * @param aOption Import preferences (available options defined in CContactDatabase::TOptions) * * @return A CContactItem instance (on the cleanup stack) that has been constructed from * the vCard data. */ CContactItem* CVCardToContactsAppConverter::GetVCardAsContactItemLC(CParserVCard& aVCard, TUnknownPropertyBehaviour aUnknownPropertyBehaviour, TInt aOption) { TInt ii=0; TInt count = 0; // iUnknownPropertyBehaviour = aUnknownPropertyBehaviour; TTime lastModified; TBuf uidstring; GetVCardModifiedTimeL(aVCard, lastModified); GetVCardUidStringL(aVCard, uidstring); // CContactCard* mainContact = CContactCard::NewLC(); // // We're performing an initial import SetImportType(ECntVCardImportTypeFirstSync); // Get Name CVCardItemAndLabel* names = GetContactNameLC(aVCard, aOption); if (names && names->ItemCount()) SetNameFieldsL(*names, *mainContact, aOption); CleanupStack::PopAndDestroy(names); // Get Name pronunciation names = GetContactNamePrnLC(aVCard, aOption); if (names && names->ItemCount()) { SetNameFieldsL(*names, *mainContact, aOption, ETrue); } CleanupStack::PopAndDestroy(names); // Create address container RPointerArray addresses(KVCardImportAddressArrayGranularity); CleanupStack::PushL(TCleanupItem(CVCardItemAndLabel::CleanUpResetDestroyAndCloseArray, &addresses)); // Get addresses GetAddressesL(aVCard, aOption, addresses); // Import each address field into the contact card count = addresses.Count(); for (ii=0; iiDes()); TInt classCount = GetSingleInstanceL(aVCard, KVersitTokenClass, ptr); if(classCount) { SetSingleInstanceL(*mainContact, ptr, KUidContactFieldClass, KUidContactFieldVCardMapClass,aOption); } CleanupStack::PopAndDestroy(singleClass); // Get other properties CArrayPtr* arrayOfProperties = aVCard.ArrayOfProperties(); CleanupStack::PushL(TCleanupItem(CleanUpResetAndDestroy,arrayOfProperties)); // if (arrayOfProperties) { count = arrayOfProperties->Count(); for (ii=0; iiAt(ii); // Address fields are handled in the above sections if ((property->Name() != KVersitTokenADR) && (property->Name() != KVersitTokenORG) && (property->Name() != KVersitTokenClass)) { // NOTE: This method can return a NULL object pushed onto the cleanup stack, so must // always be popped! TBool unsupportedProperty=EFalse; CContactItemField* field = GetVCardPropertyAsContactFieldLC(property, aOption,unsupportedProperty); if (field) { mainContact->AddFieldL(*field); } CleanupStack::Pop(field); } } // Email and Tel properties should only have a single Pref parameter AdjustForPrefRule(*mainContact, KUidContactFieldEMail, KUidContactFieldVCardMapEMAILINTERNET); AdjustForPrefRule(*mainContact, KUidContactFieldPhoneNumber, KUidContactFieldVCardMapTEL); } // if (lastModified != Time::NullTTime()) { mainContact->SetLastModified(lastModified); } // mainContact->SetUidStringL(uidstring); // CleanupStack::PopAndDestroy(); // arrayOfProperties->ResetAndDestroy() // return mainContact; } /** * Extract the last modified date of the vCard. * If there is no 'REV' property the last modified time is returned as as NULL TTime value. * * @param aVCard vCard parser object * @param aLastModified Last modified time */ void CVCardToContactsAppConverter::GetVCardModifiedTimeL(CParserVCard& aVCard,TTime& aLastModified) { CArrayPtr* arrayOfRevisions=aVCard.PropertyL(KVersitTokenREV,TUid::Uid(KVersitPropertyDateTimeUid),EFalse); if (arrayOfRevisions && arrayOfRevisions->Count()) { CleanupStack::PushL(arrayOfRevisions); CParserPropertyValueDateTime* revision=(static_cast((*arrayOfRevisions)[0]->Value())); aLastModified=revision->Value()->iDateTime; if (revision->Value()->iRelativeTime != TVersitDateTime::EIsUTC ) // The REV property isn't in UTC time, so let's (try to) convert it { CArrayPtr* arrayOfTimeZones=aVCard.PropertyL(KVersitTokenTZ, TUid::Uid(KVersitPropertyTimeZoneUid),EFalse); if (arrayOfTimeZones && arrayOfTimeZones->Count()) // If we have the TZ property, adjust the machine local timestamp. // If we don't have the TZ property, we'll just pretend that the REV // property was already a UTC value. { CleanupStack::PushL(arrayOfTimeZones); CParserPropertyValueTimeZone* timeZone = static_cast ((*arrayOfTimeZones)[0]->Value()); // Subtract the offset: UTC + Offset = Local => UTC = Local - Offset. TTimeIntervalSeconds utcOffset = timeZone->Value().Int(); aLastModified -= utcOffset; CleanupStack::PopAndDestroy(arrayOfTimeZones); } } CleanupStack::PopAndDestroy(arrayOfRevisions); } else { aLastModified=Time::NullTTime(); } } /** * Extract the vCard 'UID' property from the vCard. * * @param aVCard vCard parser object * @param aUidString UID property value */ void CVCardToContactsAppConverter::GetVCardUidStringL(CParserVCard& aVCard,TDes& aUidString) const { const CArrayPtr* arrayOfProperties=aVCard.PropertyL(KVersitTokenUID,TUid::Uid(KVersitPropertyHBufCUid),EFalse); if (arrayOfProperties) { TInt count=arrayOfProperties->Count(); if (count) { CParserPropertyValueHBufC* uidstring=(STATIC_CAST(CParserPropertyValueHBufC*,(*arrayOfProperties)[0]->Value())); if (uidstring->Value().Length() > KUidStringLength ) aUidString=uidstring->Value().Left(KUidStringLength) ; // truncate else aUidString=uidstring->Value(); } delete arrayOfProperties; } } /** * Convert aVCardProperty into contact field. * * @param aProperty vCard property * @param aOption Import preferences (available options defined in CContactDatabase::TOptions) * @param aUnsupportedProperty Return whether or not the field is supported by the PC PIM, only relevant when using the EConnectWhitespace flag * @return Contact field or a NULL pointer (pushed onto the cleanup stack) in the case where * it wasn't possible to create a contact field for this vCard property. */ CContactItemField* CVCardToContactsAppConverter::GetVCardPropertyAsContactFieldLC(CParserProperty* aProperty,TInt aOption,TBool& aUnsupportedProperty) { TUid mappingUid; TStorageType storageType; CContentType* content=MapVCardPropertyToContentAndStorageTypeL(*aProperty,storageType); CleanupStack::PushL(content); mappingUid=content->Mapping(); TBool noConversion=EFalse; aUnsupportedProperty=EFalse; if (mappingUid==KUidContactFieldVCardMapNotRequired) { noConversion=ETrue; } else { /*Could preserve unknown non X- if VCard version no>2.1*/ switch (iUnknownPropertyBehaviour) { case EDiscardAllUnknownProperties: if (mappingUid==KUidContactFieldVCardMapUnknownXDash) noConversion=ETrue; /* fall through */ case EDiscardNonXDashUnknownProperties: if (mappingUid==KUidContactFieldVCardMapUnknown) noConversion=ETrue; /* fall through */ default: break; } } CContactItemField* contactItemField=NULL; if (!noConversion) { TBool validImportableDataFound = ETrue; contactItemField=CContactItemField::NewLC(storageType,*content); if ((mappingUid==KUidContactFieldVCardMapUnknown) ||(mappingUid==KUidContactFieldVCardMapUnknownXDash)) { TBool propertyTobeTruncated = PropertyTobeTruncated(aProperty->Name()); HBufC* encodedText = EncodeL(STATIC_CAST(CParserPropertyValueHBufC*,aProperty->Value())->Value(), propertyTobeTruncated); contactItemField->TextStorage()->SetText(encodedText); // takes ownership contactItemField->SetHidden(ETrue); } else { switch (contactItemField->StorageType()) { case KStorageTypeText: { TInt id = aProperty->Value()->Uid().iUid; if(mappingUid == KUidContactFieldVCardMapORGPronunciation) { // this should be treated as an array instead of a single string id = KVersitPropertyCDesCArrayUid; } switch (id) { case KVersitPropertyCDesCArrayUid: { TBool doConvert = ETrue; TBool pushed = EFalse; CParserPropertyValueCDesCArray* propertyValueWrapper=NULL; if(mappingUid == KUidContactFieldVCardMapORGPronunciation) { // Treat a SOUND with X-IRMC-ORG param as an array const CParserPropertyValueHBufC* valueScalar = static_cast(aProperty->Value()); if(valueScalar) { propertyValueWrapper = valueScalar->TreatAsArrayPropertyLC(*aProperty); pushed= ETrue; } } else { propertyValueWrapper = static_cast(aProperty->Value()); } CDesCArray* propertyValue = NULL; if (propertyValueWrapper) { propertyValue = propertyValueWrapper->Value(); } if ((CContactVCardConverter::EConnectWhitespace & aOption) && propertyValue) { doConvert = TextArrayContainsImportableData(*propertyValue); if (doConvert) { RemoveWhitespaceFromCompositePropertyL(*propertyValue); } } if (doConvert && propertyValue) { TInt count = propertyValue->MdcaCount(); HBufC *txt=HBufC::NewL(0); CleanupStack::PushL(txt); // concatenate the array for (TInt ii=0;iiMdcaPoint(ii).Size(); if (mdcaLen>0) { if (txt->Length()>0) { txt->Des().Append(TChar('\n')); } txt=txt->ReAllocL(txt->Length()+mdcaLen+1); // +1 In case we add '\n' CleanupStack::Pop(); // txt(old value) CleanupStack::PushL(txt); TPtrC temp(propertyValue->MdcaPoint(ii)); txt->Des().Append(temp); } } TBool propertyTobeTruncated = PropertyTobeTruncated(aProperty->Name()); HBufC* encodedText = EncodeL(*txt, propertyTobeTruncated); CleanupStack::PopAndDestroy(txt); contactItemField->TextStorage()->SetText(encodedText); // takes ownership } else { validImportableDataFound = EFalse; } if(pushed) { CleanupStack::PopAndDestroy(propertyValueWrapper); } } break; case KVersitPropertyHBufCUid: { TBool doConvert = ETrue; CParserPropertyValueHBufC* propertyValueWrapper = static_cast(aProperty->Value()); const TPtrC propertyValue(propertyValueWrapper->Value()); if (CContactVCardConverter::EConnectWhitespace & aOption) { if (propertyValue==KSingleSpacePropertyValue) { aUnsupportedProperty=ETrue; } doConvert = (CContactVCardConverter::ContainsImportableData(propertyValue, CContactVCardConverter::EPropertyValueSingle, ImportType())); } if (doConvert) { TBool propertyTobeTruncated = PropertyTobeTruncated(aProperty->Name()); HBufC* encodedText = EncodeL(propertyValue, propertyTobeTruncated); contactItemField->TextStorage()->SetText(encodedText); // takes ownership } else { validImportableDataFound = EFalse; } } break; default:; } } break; case KStorageTypeDateTime: { switch (aProperty->Value()->Uid().iUid) { case KVersitPropertyDateTimeUid: STATIC_CAST(CContactDateField*,contactItemField->Storage())->SetTime(STATIC_CAST(CParserPropertyValueDateTime*,aProperty->Value())->Value()->iDateTime); break; case KVersitPropertyDateUid: STATIC_CAST(CContactDateField*,contactItemField->Storage())->SetTime(STATIC_CAST(CParserPropertyValueDate*,aProperty->Value())->Value()->iDateTime); break; default: break; } } break; case KStorageTypeStore: { STATIC_CAST(CContactStoreField*,contactItemField->Storage())->SetThingL(STATIC_CAST(CParserPropertyValueBinary*,aProperty->Value())->Value()); } break; default: break; } } if (aOption & CContactVCardConverter::EIncludeX) { TBuf8 paramName; paramName=KContactVCardXDashEPOCCNTMODEL; paramName.Append(KContactVCardFIELDHIDDEN); if (aProperty->Param(paramName)) contactItemField->SetHidden(ETrue); paramName.Zero(); paramName=KContactVCardXDashEPOCCNTMODEL; paramName.Append(KContactVCardFIELDREADONLY); if (aProperty->Param(paramName)) contactItemField->SetReadOnly(ETrue); paramName.Zero(); paramName=KContactVCardXDashEPOCCNTMODEL; paramName.Append(KContactVCardFIELDLABEL); if (aProperty->Param(paramName)) contactItemField->SetLabel(aProperty->Param(paramName)->ValueL()); } CleanupStack::Pop(contactItemField); // The field is only added to the contact card if it was found to contain // legitimate importable data (i.e. has content that does not simply consist of LWSP characters). // This is important, since TimeIS sync drivers can create vCard objects (pushed over to the device) // that contain only space characters for their Versit 'Property Parameter Values'. if (!validImportableDataFound) { delete contactItemField; contactItemField = NULL; } } CleanupStack::PopAndDestroy(content); // We may be pushing a NULL pointer back onto the cleanup stack, but clients of this // method understand this feature. CleanupStack::PushL(contactItemField); return(contactItemField); } /** * Find a vCard property parameter in a vCard property * * @param aVCardProperty vCard property * @return Property parameter number */ TInt CVCardToContactsAppConverter::GetVCardPropertyTTNumber(CParserProperty* aVCardProperty) { TInt ii; TBuf8 paramName; for (ii=1;ii<=KContactMaxFieldNumber;ii++) { paramName.Format(_L8("%d"),ii); if (aVCardProperty->Param(paramName)) { return (ii); } } return 1; // Default to 1 if no number found } /** * Extract the contact name fields from the vCard 'N' property. * This method takes values from the 'N' vCard property and returns the family name, * given name, middle name, prefix and suffix. * * Only the first instance of the 'N' property is used. * * @param aVCard vCard parser object * @param aOption Import preferences (available options defined in CContactDatabase::TOptions) * @return An instance of CVCardItemAndLabel which encapsulates the names and their associated * labels contained within the vCard or a NULL pointer pushed onto the cleanup stack * if it wasn't possible to construct the object. */ CVCardItemAndLabel* CVCardToContactsAppConverter::GetContactNameLC(CParserVCard& aVCard, TInt aOption) { CVCardItemAndLabel* names = NULL; CArrayPtr* arrayOfProperties=aVCard.PropertyL(KVersitTokenN,TUid::Uid(KVersitPropertyCDesCArrayUid)); if (arrayOfProperties && arrayOfProperties->Count()) { CleanupStack::PushL(TCleanupItem(CleanUpResetAndDestroy,arrayOfProperties)); // // CParserProperty* property = arrayOfProperties->At(0); CDesCArray& items = *static_cast(property->Value())->Value(); names = MakeNamesFromItemsL(items, aOption, property ); CleanupStack::PopAndDestroy(arrayOfProperties); } // Clients of this method assume that something is pushed onto the Cleanup Stack, so // this might potentially be NULL. CleanupStack::PushL(names); return names; } /** * Extract the contact name fields from the vCard 'N' property. * This method takes values from the 'N' vCard property and returns the family name, * given name, middle name, prefix and suffix. * * Only the first instance of the 'N' property is used. * * @param aVCard vCard parser object * @param aOption Import preferences (available options defined in CContactDatabase::TOptions) * @return An instance of CVCardItemAndLabel which encapsulates the names and their associated * labels contained within the vCard or a NULL pointer pushed onto the cleanup stack * if it wasn't possible to construct the object. */ CVCardItemAndLabel* CVCardToContactsAppConverter::GetContactNamePrnLC(CParserVCard& aVCard, TInt aOption) { CVCardItemAndLabel* names = NULL; CArrayPtr* arrayOfProperties=aVCard.PropertyL(KVersitTokenSOUND,TUid::Uid(KVersitPropertyHBufCUid), EFalse); if (arrayOfProperties ) { CleanupStack::PushL(arrayOfProperties); TInt count = arrayOfProperties->Count(); const CParserProperty* property=NULL; for (TInt index=0;indexAt(index); if(property->Param(KVersitParam8NamePrn)) { index=count; } else { property=NULL; } } if (property) // property is the 1st SOUND property with an X-IRMC-N parameter { const CParserPropertyValueHBufC* valueScalar = static_cast(property->Value()); if(valueScalar) { CParserPropertyValueCDesCArray* valueArray = valueScalar->TreatAsArrayPropertyLC(*property); names = MakeNamesFromItemsL( *valueArray->Value(), aOption, property );// valueArray will never be NULL CleanupStack::PopAndDestroy(valueArray); } } CleanupStack::PopAndDestroy(arrayOfProperties); } // Clients of this method assume that something is pushed onto the Cleanup Stack, so // this might potentially be NULL. CleanupStack::PushL(names); return names; } CVCardItemAndLabel* CVCardToContactsAppConverter::MakeNamesFromItemsL(const CDesCArray& aItems, TInt aOption, const CParserProperty* aProperty ) { CVCardItemAndLabel* names = CVCardItemAndLabel::NewLC(); const TInt fieldcount = aItems.Count(); // for(TInt ii=0; iiAddItemL(pField); if (aOption & CContactVCardConverter::EIncludeX) { TBuf8 paramName(KContactVCardXDashEPOCCNTMODEL); paramName.AppendFormat(KContactVCardLABELn,ii); // if(aProperty->Param(paramName)) { HBufC* value = aProperty->Param(paramName)->ValueL(); CleanupStack::PushL(value); names->AddLabelL(*value); CleanupStack::PopAndDestroy(value); } } } CleanupStack::Pop(names); return names; } /** * Extract specific address properties * @param aProperties An array of vCard properties which should be searched * @param aOption Import preferences (available options defined in CContactDatabase::TOptions) * @param aParamMustMatch A property parameter which must be found in the specified vCard property. If this * has a NULL (KNullDesC) value then the property is treated as satisfying this condition * @param aParamMustNotMatch1 A property parameter which must not be found in the specified vCard property. If this * has a NULL (KNullDesC) value then the property is treated as satisfying this condition * @param aParamMustNotMatch2 Another property parameter which must not be found in the specified vCard property. If this * has a NULL (KNullDesC) value then the property is treated as satisfying this condition * @param aMapping The address mapping which is being searched for (e.g. HOME, WORK, etc) * @return An address object which contains the name and labels (optionally) located in aProperties for this particular mapping */ CVCardAddress* CVCardToContactsAppConverter::GetSpecifiedAddressLC(const CArrayPtr& aProperties, TInt aOption, const TDesC8& aParamMustMatch, const TDesC8& aParamMustNotMatch1, const TDesC8& aParamMustNotMatch2, TUid aMapping) { CVCardAddress* address = NULL; const TInt propertyCount = aProperties.Count(); // for (TInt ii=0; iiParam(KVersitParam8Type); if ( (property->Name() == KVersitTokenADR) && (property->Value()->Uid() == TUid::Uid(KVersitPropertyCDesCArrayUid)) && (!aParamMustMatch.Length() || property->Param(aParamMustMatch) != NULL || (paramType != NULL && paramType->Value().CompareF(aParamMustMatch) == 0)) && (!aParamMustNotMatch1.Length() || (property->Param(aParamMustNotMatch1) == NULL && (paramType == NULL || paramType->Value().CompareF(aParamMustNotMatch1) != 0))) && (!aParamMustNotMatch2.Length() || (property->Param(aParamMustNotMatch2) == NULL && (paramType == NULL || paramType->Value().CompareF(aParamMustNotMatch2) != 0))) ) { CDesCArray& items = *static_cast(property->Value())->Value(); const TInt fieldcount = items.Count(); // Create the address address = CVCardAddress::NewLC(aMapping); for(TInt jj = 0; jj < fieldcount; jj++) { const TPtrC pField(items.MdcaPoint(jj)); address->AddItemL(pField); // if (aOption & CContactVCardConverter::EIncludeX) { TBuf8 paramName(KContactVCardXDashEPOCCNTMODEL); paramName.AppendFormat(KContactVCardLABELn, jj); // if (property->Param(paramName)) { HBufC* value = property->Param(paramName)->ValueL(); CleanupStack::PushL(value); address->AddLabelL(*value); CleanupStack::PopAndDestroy(value); } } } // End for-loop now we've found the specified item break; } } // Have to push something to ensure cleanup stack is balanced if (!address) CleanupStack::PushL((TAny*) NULL); return address; } /** * Extract home and work address properties from the vCard * * @param aVCard vCard parser object * @param aOption Import preferences (available options defined in CContactDatabase::TOptions) * @param aAddresses An array reference which contains any located address fields upon exit from this method */ void CVCardToContactsAppConverter::GetAddressesL(CParserVCard& aVCard, TInt aOption, RPointerArray& aAddresses) { CArrayPtr* arrayOfProperties = aVCard.ArrayOfProperties(EFalse); if (arrayOfProperties) { // Find ADR;HOME CVCardAddress* home = GetSpecifiedAddressLC(*arrayOfProperties, aOption, KVersitParam8Home, // Must be ADR;HOME and not ADR;HOME;WORK KVersitParam8Work, KNullDesC8, KUidContactFieldVCardMapHOME ); if (home) User::LeaveIfError(aAddresses.Append(home)); CleanupStack::Pop(home); // Find ADR;WORK CVCardAddress* work = GetSpecifiedAddressLC(*arrayOfProperties, aOption, KVersitParam8Work, // Must be ADR;WORK KNullDesC8, KNullDesC8, KUidContactFieldVCardMapWORK ); if (work) User::LeaveIfError(aAddresses.Append(work)); CleanupStack::Pop(work); // Find ADR;PREF CVCardAddress* pref = GetSpecifiedAddressLC(*arrayOfProperties, aOption, KVersitParam8Pref, // Must be ADR;PREF and not ADR;HOME or ADR;WORK KVersitParam8Home, KVersitParam8Work, KUidContactFieldVCardMapPREF ); if (pref) User::LeaveIfError(aAddresses.Append(pref)); CleanupStack::Pop(pref); // Find general ADR: address CVCardAddress* general = GetSpecifiedAddressLC(*arrayOfProperties, aOption, KNullDesC8, // Just has to be ADR KVersitParam8Home, KVersitParam8Work, KNullUid ); if (general) User::LeaveIfError(aAddresses.Append(general)); CleanupStack::Pop(general); } } /** * Map vCard property to contacts field content and storage type * * @param aVCardProperty vCard property * @param aStorageType Type of contact field storage (TStorageType) * @return Content type for vCard property */ CContentType* CVCardToContactsAppConverter::MapVCardPropertyToContentAndStorageTypeL(const CParserProperty& aVCardProperty, TStorageType& aStorageType) { TUid mapping=KUidContactFieldNone; TFieldType fieldType=KUidContactFieldNone; TPtrC8 vpropNameConst = aVCardProperty.Name(); aStorageType=KStorageTypeStore; switch (aVCardProperty.Value()->Uid().iUid) { case KVersitPropertyCDesCArrayUid: { aStorageType=KStorageTypeText; if (vpropNameConst.CompareF(KVersitTokenN)==0) { fieldType=KUidContactFieldFamilyName; mapping=KUidContactFieldVCardMapUnusedN; } else if (vpropNameConst.CompareF(KVersitTokenADR)==0) { fieldType=KUidContactFieldAddress; mapping=KUidContactFieldVCardMapADR; } else if (vpropNameConst.CompareF(KVersitTokenChildren) == 0) { mapping = KUidContactFieldVCardMapChildren; fieldType = KUidContactFieldChildren; } } break; case KVersitPropertyDateTimeUid: aStorageType=KStorageTypeDateTime; if (vpropNameConst.CompareF(KVersitTokenREV)==0) mapping=KUidContactFieldVCardMapNotRequired; break; case KVersitPropertyDateUid: aStorageType=KStorageTypeDateTime; if (vpropNameConst.CompareF(KVersitTokenBDAY)==0) { mapping=KUidContactFieldVCardMapBDAY; fieldType=KUidContactFieldBirthday; } else if (vpropNameConst.CompareF(KVersitTokenAnniversary) == 0) { mapping = KUidContactFieldVCardMapAnniversary; fieldType = KUidContactFieldAnniversary; } break; case KVCardPropertyAgentUid: aStorageType = KStorageTypeContactItemId ; if (vpropNameConst.CompareF(KVersitTokenAGENT) == 0) { mapping = KUidContactFieldVCardMapAGENT; } break; case KVersitPropertyTimeZoneUid: aStorageType=KStorageTypeStore; if(vpropNameConst.CompareF(KVersitTokenTZ) == 0) { mapping=KUidContactFieldVCardMapNotRequired; } break; ////////////// default: // aka case KVersitPropertyHBufCUid: aStorageType=KStorageTypeText; mapping = KUidContactFieldVCardMapUnknown; // Initialize to something sensible switch (vpropNameConst[0]) { case 'B': case 'b': if (vpropNameConst.CompareF(KVersitTokenBDAY)==0) { mapping=KUidContactFieldVCardMapBDAY; fieldType=KUidContactFieldBirthday; } break; case 'E': case 'e': if (vpropNameConst.CompareF(KVersitTokenEMAIL)==0) { fieldType=KUidContactFieldEMail; mapping=KUidContactFieldVCardMapEMAILINTERNET; } break; case 'F': case 'f': if (vpropNameConst.CompareF(KVersitTokenFN)==0) mapping=KUidContactFieldVCardMapUnusedFN; break; case 'G': case 'g': if (vpropNameConst.CompareF(KVersitTokenGEO)==0) { fieldType=KUidContactFieldGEO; mapping=KUidContactFieldVCardMapGEO; } break; case 'K': case 'k': if (vpropNameConst.CompareF(KVersitTokenKEY)==0) { if(aVCardProperty.Value()->Uid().iUid == KVersitPropertyBinaryUid) { aStorageType = KStorageTypeStore; } mapping=KUidContactFieldVCardMapKEY; } break; case 'L': case 'l': if (vpropNameConst.CompareF(KVersitTokenLABEL)==0) mapping=KUidContactFieldVCardMapLABEL; else if (vpropNameConst.CompareF(KVersitTokenLOGO)==0) { aStorageType=KStorageTypeStore; mapping=KUidContactFieldVCardMapLOGO; } break; case 'M': case 'm': if (vpropNameConst.CompareF(KVersitTokenMAILER)==0) mapping=KUidContactFieldVCardMapMAILER; break; case 'N': case 'n': if (vpropNameConst.CompareF(KVersitTokenNOTE)==0) { fieldType=KUidContactFieldNote; mapping=KUidContactFieldVCardMapNOTE; } break; case 'P': case 'p': if (vpropNameConst.CompareF(KVersitTokenPHOTO)==0) { mapping=KUidContactFieldVCardMapPHOTO; aStorageType=KStorageTypeStore; } break; case 'R': case 'r': if (vpropNameConst.CompareF(KVersitTokenROLE)==0) mapping=KUidContactFieldVCardMapROLE; break; case 'S': case 's': if (vpropNameConst.CompareF(KVersitTokenSOUND)==0) { if(aVCardProperty.Param(KVersitParam8CompanyPrn)) { mapping = KUidContactFieldVCardMapORGPronunciation; fieldType=KUidContactFieldCompanyNamePronunciation; } else if(aVCardProperty.Param(KVersitParam8NamePrn)) {// this is handled elsewhere mapping = KUidContactFieldVCardMapNotRequired; } else // if we ever support any of the other pronunication extensions we'd have to add the checking here { mapping=KUidContactFieldVCardMapSOUND; } } break; case 'T': case 't': if (vpropNameConst.CompareF(KVersitTokenTEL)==0) { fieldType=KUidContactFieldPhoneNumber; mapping=KUidContactFieldVCardMapTEL; } else if (vpropNameConst.CompareF(KVersitTokenTITLE)==0) { fieldType=KUidContactFieldJobTitle; mapping=KUidContactFieldVCardMapTITLE; } else if (vpropNameConst.CompareF(KVersitTokenTZ)==0) { mapping=KUidContactFieldVCardMapNotRequired; } break; case 'U': case 'u': if (vpropNameConst.CompareF(KVersitTokenURL)==0) { mapping=KUidContactFieldVCardMapURL; fieldType=KUidContactFieldUrl; } else if (vpropNameConst.CompareF(KVersitTokenUID)==0) { mapping=KUidContactFieldVCardMapNotRequired; } break; case 'V': case 'v': if (vpropNameConst.CompareF(KVersitTokenVERSION)==0) { mapping=KUidContactFieldVCardMapNotRequired; } break; case 'X': case 'x': if (vpropNameConst.CompareF(KVersitTokenSECONDNAME)==0) { mapping=KUidContactFieldVCardMapSECONDNAME; fieldType=KUidContactFieldSecondName; } else if (vpropNameConst.CompareF(KVersitTokenSIPID)==0) { mapping=KUidContactFieldVCardMapSIPID; fieldType=KUidContactFieldSIPID; } else if (vpropNameConst.CompareF(KVersitTokenWVID)==0) { mapping=KUidContactFieldVCardMapWV; fieldType=KUidContactFieldIMAddress; } else if (vpropNameConst.CompareF(KVersitTokenAssistant) == 0) { mapping = KUidContactFieldVCardMapAssistant; fieldType = KUidContactFieldAssistant; } else if (vpropNameConst.CompareF(KVersitTokenAssistantTel) == 0) { mapping = KUidContactFieldVCardMapAssistantTel; fieldType = KUidContactFieldPhoneNumber; } else if (vpropNameConst.CompareF(KVersitTokenAnniversary) == 0) { mapping = KUidContactFieldVCardMapAnniversary; fieldType = KUidContactFieldAnniversary; } else if (vpropNameConst.CompareF(KVersitTokenSpouse) == 0) { mapping = KUidContactFieldVCardMapSpouse; fieldType = KUidContactFieldSpouse; } else if (vpropNameConst.Length()>=2) { if (vpropNameConst.Left(2).CompareF(KVersitTokenXDash)==0) mapping=KUidContactFieldVCardMapUnknownXDash; else mapping=KUidContactFieldVCardMapUnknown; } break; default: break; // Leave mapping set to KUidContactFieldVCardMapUnknown }; }; CContentType* type=CContentType::NewL(); CleanupStack::PushL( type ); type->SetMapping(mapping); if (mapping==KUidContactFieldVCardMapPHOTO || mapping==KUidContactFieldVCardMapLOGO) { type->AddFieldTypeL(KUidContactFieldPicture); TFieldType bitmapFormat; GetBitMapFormat(aVCardProperty, bitmapFormat); if (bitmapFormat.iUid!=KUidContactFieldNone.iUid) { type->AddFieldTypeL(bitmapFormat); } } if(fieldType!=KUidContactFieldNone) { type->AddFieldTypeL(fieldType); } CArrayPtr* paramArray = NULL ; paramArray = aVCardProperty.ParamArray(); // parameters if (IsParameterValuePresent(paramArray, KVersitTokenTYPE, KVersitParam8Internet)) { type->AddFieldTypeL(KUidContactFieldVCardMapINTERNET); } if (IsParameterValuePresent(paramArray, KVersitTokenTYPE, KVersitParam8Home)) { type->AddFieldTypeL(KUidContactFieldVCardMapHOME); } if (IsParameterValuePresent(paramArray, KVersitTokenTYPE, KVersitParam8Work)) { type->AddFieldTypeL(KUidContactFieldVCardMapWORK); } if (IsParameterValuePresent(paramArray, KVersitTokenTYPE, KVersitParam8Voice)) { type->AddFieldTypeL(KUidContactFieldVCardMapVOICE); } if (IsParameterValuePresent(paramArray, KVersitTokenTYPE, KVersitParam8Fax)) { type->RemoveFieldType(KUidContactFieldPhoneNumber); type->AddFieldTypeL(KUidContactFieldFax); type->AddFieldTypeL(KUidContactFieldVCardMapFAX); } if (IsParameterValuePresent(paramArray, KVersitTokenTYPE, KVersitParam8Pref)) { type->AddFieldTypeL(KUidContactFieldVCardMapPREF); } if (IsParameterValuePresent(paramArray, KVersitTokenTYPE, KVersitParam8Cell)) { type->AddFieldTypeL(KUidContactFieldVCardMapCELL); } if (IsParameterValuePresent(paramArray, KVersitTokenTYPE, KVersitParam8Pager)) { type->AddFieldTypeL(KUidContactFieldVCardMapPAGER); } if (IsParameterValuePresent(paramArray, KVersitTokenTYPE, KVersitParam8Bbs)) { type->AddFieldTypeL(KUidContactFieldVCardMapBBS); } if (IsParameterValuePresent(paramArray, KVersitTokenTYPE, KVersitParam8Modem)) { type->AddFieldTypeL(KUidContactFieldVCardMapMODEM); } if (IsParameterValuePresent(paramArray, KVersitTokenTYPE, KVersitParam8Car)) { type->AddFieldTypeL(KUidContactFieldVCardMapCAR); } if (IsParameterValuePresent(paramArray, KVersitTokenTYPE, KVersitParam8Isdn)) { type->AddFieldTypeL(KUidContactFieldVCardMapISDN); } if (IsParameterValuePresent(paramArray, KVersitTokenTYPE, KVersitParam8Video)) { type->AddFieldTypeL(KUidContactFieldVCardMapVIDEO); } if (IsParameterValuePresent(paramArray, KVersitTokenTYPE, KVersitParam8Msg)) { type->AddFieldTypeL(KUidContactFieldVCardMapMSG); } if (IsParameterValuePresent(paramArray, KVersitTokenTYPE, KVersitParam8Dom)) { type->AddFieldTypeL(KUidContactFieldVCardMapDOM); } if (IsParameterValuePresent(paramArray, KVersitTokenTYPE, KVersitParam8X509)) { type->AddFieldTypeL(KUidContactFieldVCardMapX509); } if (IsParameterValuePresent(paramArray, KVersitTokenTYPE, KVersitParam8PGP)) { type->AddFieldTypeL(KUidContactFieldVCardMapPGP); } if (IsParameterValuePresent(paramArray, KVersitTokenTYPE, KVersitParamPOC)) { type->AddFieldTypeL(KUidContactFieldVCardMapPOC); } if (IsParameterValuePresent(paramArray, KVersitTokenTYPE, KVersitParamSWIS)) { type->AddFieldTypeL(KUidContactFieldVCardMapSWIS); } if (IsParameterValuePresent(paramArray, KVersitTokenTYPE, KVersitParamVOIP)) { type->AddFieldTypeL(KUidContactFieldVCardMapVOIP); } CleanupStack::Pop(type); return(type); } /* * Gets the bitmap format. This is done by looking for the name and value items in the parameter eg. TYPE=GIF * the if construct assumes that this is present in the vcf file. It is also possible to just have the format without * the TYPE string and this is stored in the name part of the parameter so need to check through all supported formats * to find if one is present, this is done in the else construct. * * @param aVCardProperty and aBitmapFormat * @return void. */ void CVCardToContactsAppConverter::GetBitMapFormat(const CParserProperty& aVCardProperty, TFieldType& aBitmapFormat) { aBitmapFormat.iUid=KUidContactFieldNone.iUid; CParserParam* param=aVCardProperty.Param(KVersitTokenTYPE); if (param) { TFieldType bitmapFormat=MapVCardPhotoTypeToFieldType(param->Value()); if (bitmapFormat.iUid!=KUidContactFieldNone.iUid) { aBitmapFormat.iUid = bitmapFormat.iUid; } } //if else { const TInt KNumberOfBitmaps = 16; //Set up table of bitmap formats const TBufC8<5> bitmapString[] = { KVersitParam8Gif(), KVersitParam8Jpeg(), KVersitParam8Bmp(), KVersitParam8Tiff(), KVersitParam8Pict(), KVersitParam8Cgm(), KVersitParam8Wmf(), KVersitParam8Ps(), KVersitParam8Pdf(), KVersitParam8Mpeg(), KVersitParam8Mpeg2(), KVersitParam8Avi(), KVersitParam8Qtime(), KVersitParam8Dib(), KVersitParam8Pmb(), KVersitParam8Met() }; // Loop through the table looking for a param whose name matches the bitmap format for (TInt i=0; i < KNumberOfBitmaps - 1; ++i) { CParserParam* paramNameCheck = aVCardProperty.Param(bitmapString[i]); if (paramNameCheck) {//ok found something TFieldType bitmapFormat=MapVCardPhotoTypeToFieldType(paramNameCheck->Name()); aBitmapFormat.iUid = bitmapFormat.iUid; break; } } } //else } TFieldType CVCardToContactsAppConverter::MapVCardPhotoTypeToFieldType(/*const CParserParam& aParserParam*/TPtrC8 aBitmapStringPtr) { if (aBitmapStringPtr.Length()<2) { return KUidContactFieldNone; //no bitmap type token are less than 2 characters long } TChar firstChar(aBitmapStringPtr[0]); firstChar=firstChar.GetUpperCase(); switch (firstChar) { case 'G': return KUidContactFieldVCardMapGIF; case 'C': return KUidContactFieldVCardMapCGM; case 'W': return KUidContactFieldVCardMapWMF; case 'B': return KUidContactFieldVCardMapBMP; case 'D': return KUidContactFieldVCardMapDIB; case 'P': { TChar secondChar(aBitmapStringPtr[1]); switch (secondChar.GetUpperCase()) { case 'S': return KUidContactFieldVCardMapPS; case 'M': return KUidContactFieldVCardMapPMB; case 'D': return KUidContactFieldVCardMapPDF; case 'I': return KUidContactFieldVCardMapPICT; default: return KUidContactFieldNone; } } case 'T': return KUidContactFieldVCardMapTIFF; case 'J': return KUidContactFieldVCardMapJPEG; case 'M': switch (aBitmapStringPtr.Length()) { case 3: return KUidContactFieldVCardMapMET; case 4: return KUidContactFieldVCardMapMPEG; case 5: return KUidContactFieldVCardMapMPEG2; default: return KUidContactFieldNone; } case 'A': return KUidContactFieldVCardMapAVI; case 'Q': return KUidContactFieldVCardMapQTIME; default: return KUidContactFieldNone; } } /** * Checks each field in an array to see if one or more contain importable data. Assuming one field within * the array does, then the whole array should be imported. * * @param aArray The array of text fields to be checked for importable data * @return Whether the array should be imported. */ TBool CVCardToContactsAppConverter::TextArrayContainsImportableData(const CDesCArray& aArray) const { // Text arrays are implicitly composite in nature const TInt count = aArray.Count(); for(TInt i=0; i* arrayOfProp=aVCard.PropertyL(aToken, TUid::Uid(KVersitPropertyHBufCUid), EFalse); if(arrayOfProp) { CleanupStack::PushL(arrayOfProp); TInt propCount = arrayOfProp->Count(); CParserProperty* property = arrayOfProp->At(0); CParserPropertyValueHBufC* propertyAsHBufC = static_cast(property->Value()); aClass = propertyAsHBufC->Value(); if( !aClass.Length() ) { propCount = 0; } CleanupStack::PopAndDestroy(arrayOfProp); return propCount; } return 0; } /** * Add a specific field into a contact * * @param aContact Contact item to add fields to * @param aValue Reference to first Instance of the Property Value * @param aFieldType Field type of field to add (TFieldType) * @param aMapping vCard mapping Id of the field to add. * @param aNames An object containing the name and labels for 'N' property fields * @param aOption Import preferences (available options defined in CContactDatabase::TOptions) */ void CVCardToContactsAppConverter::SetSingleInstanceL(CContactItem& aContact,const TDes& aValue,const TFieldType& aFieldType, const TUid& aMapping, TInt aOption) { // TBool addField = EFalse; if (CContactVCardConverter::EConnectWhitespace & aOption) { addField = CContactVCardConverter::ContainsImportableData(aValue, CContactVCardConverter::EPropertyValueSingle, ImportType()); } else { addField = (aValue.Length()); } // Only add the field if it contains some data if (addField) { CContactItemField* contactItemField = CContactItemField::NewLC(KStorageTypeText, aFieldType); contactItemField->SetMapping(aMapping); contactItemField->TextStorage()->SetStandardTextL(aValue); aContact.AddFieldL(*contactItemField); CleanupStack::Pop(contactItemField); } } /** * Merge a specific field from a contact * * @param aContact Contact item to add fields to * @param aValue Pointer to first Instance of the Property Value * @param aFieldType Field type of field to add (TFieldType) * @param aMapping vCard mapping Id of the field to add. * @param aNames An object containing the name and labels for 'N' property fields * @param aOption Import preferences (available options defined in CContactDatabase::TOptions) */ void CVCardToContactsAppConverter::MergeSingleInstanceL(CContactItem& aContact, const TDes& aValue,const TFieldType& aFieldType, const TUid& aMapping, TInt aOption) { CContactItemFieldSet& oldfieldset = aContact.CardFields(); const TInt pos = oldfieldset.Find(aFieldType, aMapping); const TBool processWhitespace = (aOption & CContactVCardConverter::EConnectWhitespace); // First check whether the field is present in the contact card // Also verify that the array of address sub-values actually contains a specific // value for the requested index. const TInt Klength = aValue.Length(); if (processWhitespace) { TBool isSingleSpace = EFalse; if (Klength == 1) { isSingleSpace = (aValue[0] == KContactVCardSpaceCharacter); } if ((pos != KErrNotFound) && (Klength || isSingleSpace)) { // This means the PC side field is empty, so delete the corresponding device-side field. aContact.RemoveField(pos); } if (Klength && !isSingleSpace) { // This means the PC side field is unsupported, so ignore the corresponding contents. SetSingleInstanceL(aContact, aValue, aFieldType,aMapping,aOption); } } else { if (pos != KErrNotFound) { // This means the PC side field is empty, so delete the corresponding device-side field. aContact.RemoveField(pos); } if (Klength) { // This means the PC side field is not empty, so add the corresponding contents. SetSingleInstanceL(aContact, aValue, aFieldType,aMapping,aOption); } } } /** * Get an Array containing Property's values from a VCard * * @param aVCard A vCard Object containing Array of Properties * @param aToken A String with desired Property Name * @param A Composite Descriptor array with desired vCard Property's Values on return * @return Count of desired properties found. */ TInt CVCardToContactsAppConverter::GetVCardPropertyAsArrayOfValuesL(const CParserVCard& aVCard, const TDesC8& aToken, CDesCArray& aItems) { CArrayPtr* arrayOfProp =aVCard.PropertyL(aToken,TUid::Uid(KVersitPropertyCDesCArrayUid),EFalse); TInt propCount = 0; if(arrayOfProp) { CleanupStack::PushL(arrayOfProp); propCount = arrayOfProp->Count(); CParserProperty* property = arrayOfProp->At(0); CDesCArray& value = *static_cast(property->Value())->Value(); if(value.Count()) { for(TInt i = 0;i < value.Count();i++) { aItems.AppendL(value.MdcaPoint(i)); } } else { propCount = 0; } CleanupStack::PopAndDestroy(arrayOfProp); } return propCount; } /** * Add Organization Information like Company Name and Department Name into contact * * @param aContact Contact item to add fields to * @param aItems A CDesC Array containing the Property's value * @param aOption Import preferences (available options defined in CContactDatabase::TOptions) */ void CVCardToContactsAppConverter::SetOrgDetailsL(CContactItem& aContact,CDesCArray& aItems,const TInt aOption) { TInt orgCount = aItems.MdcaCount(); SetSpecificFieldL(aContact, aItems, KUidContactFieldCompanyName, KUidContactFieldVCardMapORG, aOption, 0, 1); SetSpecificFieldL(aContact, aItems, KUidContactFieldDepartmentName, KUidContactFieldVCardMapDepartment, aOption, 1, orgCount); } /** * Add a specific field from the Array of Property's values into contact * * @param aContact Contact item to add fields to * @param aItems A CDesC Array containing the Property's values * @param aFieldType Field type of field to add (TFieldType) * @param aMapping vCardMapping Id of field to add * @param aOption Import preferences (available options defined in CContactDatabase::TOptions) * @param aStartIndex Starting Index in the Property Value Array * @param aEndIndex Ending Index in the Property Value Array */ void CVCardToContactsAppConverter::SetSpecificFieldL(CContactItem& aContact,CDesCArray& aItems,const TUid& aFieldType,const TUid& aMapping,TInt aOption, TInt aStartIndex, TInt aEndIndex) { TBool doConvert = ETrue; TInt ii = 0; CDesCArrayFlat* orgList = new (ELeave)CDesCArrayFlat(4); CleanupStack::PushL(orgList); for (ii = aStartIndex;ii < aEndIndex;ii++) { orgList->AppendL(aItems.MdcaPoint(ii)); } if ((CContactVCardConverter::EConnectWhitespace & aOption) && orgList) { doConvert = TextArrayContainsImportableData(*orgList); if (doConvert) { RemoveWhitespaceFromCompositePropertyL(*orgList); } } if (doConvert) { CContactItemField* contactItemField = CContactItemField::NewLC(KStorageTypeText, aFieldType); contactItemField->SetMapping(aMapping); contactItemField->TextStorage()->SetStandardTextArray(orgList); aContact.AddFieldL(*contactItemField); CleanupStack::Pop(contactItemField); } CleanupStack::PopAndDestroy(orgList); // orgList } /** * Merge Organization Information like Company Name and Department Name into contact * * @param aContact Contact item to add fields to * @param aItems A CDesC Array containing the Property's value * @param aFieldType Field type of field to add (TFieldType) * @param aMapping vCardMapping Id of field to add * @param aOption Import preferences (available options defined in CContactDatabase::TOptions) * @param aStartIndex Starting Index in the Property Value Array * @param aEndIndex Ending Index in the Property Value Array */ void CVCardToContactsAppConverter::MergeOrgDetailsL(CContactItem& aContact, CDesCArray& aItems, TInt aOption) { TInt orgCount = aItems.MdcaCount(); MergeSpecificFieldL(aContact, aItems, KUidContactFieldCompanyName, KUidContactFieldVCardMapORG, aOption, 0, 1); MergeSpecificFieldL(aContact, aItems, KUidContactFieldDepartmentName, KUidContactFieldVCardMapDepartment, aOption, 1, orgCount); } /** * Merge specific Field from the array of Property's values into contact * * @param aContact Contact item to add fields to * @param aItems A CDesC Array containing the Property's value * @param aFieldType Field type of field to add (TFieldType) * @param aMapping vCardMapping Id of field to add * @param aOption Import preferences (available options defined in CContactDatabase::TOptions) * @param aStartIndex Starting Index in the Property Value Array * @param aEndIndex Ending Index in the Property Value Array */ void CVCardToContactsAppConverter::MergeSpecificFieldL(CContactItem& aContact, CDesCArray& aItems, const TFieldType& aFieldType, const TUid& aMapping, TInt aOption, TInt aStartIndex, TInt aEndIndex) { CContactItemFieldSet& oldfieldset = aContact.CardFields(); const TInt pos = oldfieldset.Find(aFieldType, aMapping); const TBool processWhitespace = (aOption & CContactVCardConverter::EConnectWhitespace); // First check whether the field is present in the contact card // Also verify that the array of address sub-values actually contains a specific // value for the requested index. if(aItems.MdcaCount() > aStartIndex) { const TPtrC pValue = aItems.MdcaPoint(aStartIndex); const TInt Klength = pValue.Length(); if (processWhitespace) { TBool isSingleSpace = EFalse; if (Klength == 1) { isSingleSpace = (pValue[0] == KContactVCardSpaceCharacter); } if ((pos != KErrNotFound) && (Klength || isSingleSpace)) { // This means the PC side field is empty, so delete the corresponding device-side field. aContact.RemoveField(pos); } if (Klength && !isSingleSpace) { // This means the PC side field is unsupported, so ignore the corresponding contents. SetSpecificFieldL(aContact, aItems, aFieldType, aMapping, aOption, aStartIndex, aEndIndex); } } else { if (pos != KErrNotFound) { // This means the PC side field is empty, so delete the corresponding device-side field. aContact.RemoveField(pos); } if (Klength) { // This means the PC side field is not empty, so add the corresponding contents. SetSpecificFieldL(aContact, aItems, aFieldType, aMapping, aOption, aStartIndex, aEndIndex); } } } else if(pos != KErrNotFound) { aContact.RemoveField(pos); } } /** * Check if a specific field from vCard should be truncated or not. * * @param aPropertyName vCard property's name * * @return ETrue if the property must be truncated and EFalse otherwise * */ TBool CVCardToContactsAppConverter::PropertyTobeTruncated(const TPtrC8& aPropertyName) const { //Test is the field is one that has to be truncated if( aPropertyName == KVersitTokenN || aPropertyName == KVersitTokenORG || aPropertyName == KVersitTokenTEL || aPropertyName == KVersitTokenEMAIL ) return ETrue; // return EFalse; } /** * Check if a specific parameter value exists in a parameter array. * * @param aParamArray Array of property parameters in which value has to be searched. * @param aParamName A parameter name whose corresponding parameter value present in aParamArray has to be matched with aParamValue. * @param aParamValue A parameter value which will be used to search. * @return TBool ETrue if value is found in parameter array, otherwise EFalse. */ TBool CVCardToContactsAppConverter::IsParameterValuePresent(CArrayPtr* aParamArray, const TDesC8& aParamName, const TDesC8& aParamValue) { if(!aParamArray) { return EFalse; } const TInt count = aParamArray->Count(); for (TInt ii = 0; ii < count; ii++) { if((*aParamArray)[ii]->Name().CompareF(aParamValue) == 0) { return ETrue; } else if((*aParamArray)[ii]->Name().CompareF(aParamName) == 0) { if((*aParamArray)[ii]->Value().CompareF(aParamValue) == 0) { return ETrue; } } } return EFalse; } /** * If there are multiple EMAIL and TEL properties in a vCard, then there should only be a single EMAIL or TEL property * with a PREF parameter. If more than one EMAIL or TEL properties contain a PREF parameter,then only the first property * with the PREF parameter is stored and the other properties are stored with their other parameters but without the * PREF parameter. * * @param aMainContact, Contact item for the vCard * @param aFieldType, the fieldtype in conjuction with the mapping is used to search for fields. * @param aMapping, the mapping is used for searching a field i.e. EMAIL or TEL fields. */ void CVCardToContactsAppConverter::AdjustForPrefRule(CContactItem& aContact, TFieldType aFieldType, TUid aMapping) { TInt pos = 0; TInt prefCount = 0; CContactItemFieldSet& contactItemFieldSet = aContact.CardFields(); pos = contactItemFieldSet.FindNext(aFieldType, aMapping, pos); while (pos != KErrNotFound) { CContactItemField& contactItemField = contactItemFieldSet[pos]; const CContentType& contentType = contactItemField.ContentType(); TBool pref = contentType.ContainsFieldType(KUidContactFieldVCardMapPREF); if (pref) { ++prefCount; if (prefCount > 1) { contactItemField.RemoveFieldType(KUidContactFieldVCardMapPREF); } } ++pos; pos = contactItemFieldSet.FindNext(aFieldType, aMapping, pos); } }