You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1898 lines
51 KiB
1898 lines
51 KiB
14 years ago
|
/* KPilot
|
||
|
**
|
||
|
** Copyright (C) 2000,2001 by Dan Pilone
|
||
|
** Copyright (C) 2002-2003 by Reinhold Kainhofer
|
||
|
** Copyright (C) 2007 by Adriaan de Groot <groot@kde.org>
|
||
|
**
|
||
|
** The abbrowser conduit copies addresses from the Pilot's address book to
|
||
|
** the KDE addressbook maintained via the kabc library.
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
** This program is free software; you can redistribute it and/or modify
|
||
|
** it under the terms of the GNU General Public License as published by
|
||
|
** the Free Software Foundation; either version 2 of the License, or
|
||
|
** (at your option) any later version.
|
||
|
**
|
||
|
** This program is distributed in the hope that it will be useful,
|
||
|
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
** GNU General Public License for more details.
|
||
|
**
|
||
|
** You should have received a copy of the GNU General Public License
|
||
|
** along with this program in a file called COPYING; if not, write to
|
||
|
** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||
|
** MA 02110-1301, USA.
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
** Bug reports and questions can be sent to kde-pim@kde.org.
|
||
|
*/
|
||
|
|
||
|
|
||
|
|
||
|
#include "options.h"
|
||
|
|
||
|
#include <tqtimer.h>
|
||
|
#include <tqtextcodec.h>
|
||
|
#include <tqfile.h>
|
||
|
#include <tqregexp.h>
|
||
|
|
||
|
#include <kabc/stdaddressbook.h>
|
||
|
#include <kabc/resourcefile.h>
|
||
|
#include <kio/netaccess.h>
|
||
|
#include <ksavefile.h>
|
||
|
|
||
|
#include <pilotSerialDatabase.h>
|
||
|
#include <pilotLocalDatabase.h>
|
||
|
|
||
|
#include "resolutionDialog.h"
|
||
|
#include "resolutionTable.h"
|
||
|
#include "abbrowserSettings.h"
|
||
|
#include "kabcRecord.h"
|
||
|
|
||
|
#include "abbrowser-conduit.moc"
|
||
|
|
||
|
// Something to allow us to check what revision
|
||
|
// the modules are that make up a binary distribution.
|
||
|
//
|
||
|
//
|
||
|
extern "C"
|
||
|
{
|
||
|
unsigned long version_conduit_address = Pilot::PLUGIN_API;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* This is partly stolen from the boost libraries, partly from
|
||
|
* "Modern C++ design" for doing compile time checks; we need
|
||
|
* to make sure that the enum values in KABCSync:: and in the
|
||
|
* AbbrowserSettings class are the same so that both interpret
|
||
|
* configuration values the same way.
|
||
|
*/
|
||
|
template<bool> struct EnumerationMismatch;
|
||
|
template<> struct EnumerationMismatch<true>{};
|
||
|
|
||
|
#define CHECK_ENUM(a) (void)sizeof(EnumerationMismatch<((int)KABCSync::a)==((int)AbbrowserSettings::a)>)
|
||
|
|
||
|
static inline void compile_time_check()
|
||
|
{
|
||
|
// Mappings for other phone
|
||
|
CHECK_ENUM(eOtherPhone);
|
||
|
CHECK_ENUM(eOtherPhone);
|
||
|
CHECK_ENUM(eAssistant);
|
||
|
CHECK_ENUM(eBusinessFax);
|
||
|
CHECK_ENUM(eCarPhone);
|
||
|
CHECK_ENUM(eEmail2);
|
||
|
CHECK_ENUM(eHomeFax);
|
||
|
CHECK_ENUM(eTelex);
|
||
|
CHECK_ENUM(eTTYTTDPhone);
|
||
|
|
||
|
// Mappings for custom fields
|
||
|
CHECK_ENUM(eCustomField);
|
||
|
CHECK_ENUM(eCustomBirthdate);
|
||
|
CHECK_ENUM(eCustomURL);
|
||
|
CHECK_ENUM(eCustomIM);
|
||
|
}
|
||
|
|
||
|
inline int faxTypeOnPC()
|
||
|
{
|
||
|
return KABC::PhoneNumber::Fax |
|
||
|
( (AbbrowserSettings::pilotFax()==0) ?
|
||
|
KABC::PhoneNumber::Home :
|
||
|
KABC::PhoneNumber::Work );
|
||
|
}
|
||
|
|
||
|
|
||
|
using namespace KABC;
|
||
|
|
||
|
/*********************************************************************
|
||
|
C O N S T R U C T O R
|
||
|
*********************************************************************/
|
||
|
|
||
|
|
||
|
AbbrowserConduit::AbbrowserConduit(KPilotLink * o, const char *n, const TQStringList & a):
|
||
|
ConduitAction(o, n, a),
|
||
|
aBook(0L),
|
||
|
fAddressAppInfo(0L),
|
||
|
addresseeMap(),
|
||
|
syncedIds(),
|
||
|
abiter(),
|
||
|
fTicket(0L),
|
||
|
fCreatedBook(false),
|
||
|
fBookResource(0L)
|
||
|
{
|
||
|
FUNCTIONSETUP;
|
||
|
fConduitName=i18n("Addressbook");
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
AbbrowserConduit::~AbbrowserConduit()
|
||
|
{
|
||
|
FUNCTIONSETUP;
|
||
|
|
||
|
if (fTicket)
|
||
|
{
|
||
|
DEBUGKPILOT << fname << ": Releasing ticket" << endl;
|
||
|
aBook->releaseSaveTicket(fTicket);
|
||
|
fTicket=0L;
|
||
|
}
|
||
|
|
||
|
_cleanupAddressBookPointer();
|
||
|
// unused function warnings.
|
||
|
compile_time_check();
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*********************************************************************
|
||
|
L O A D I N G T H E D A T A
|
||
|
*********************************************************************/
|
||
|
|
||
|
|
||
|
|
||
|
/* Builds the map which links record ids to uid's of Addressee
|
||
|
*/
|
||
|
void AbbrowserConduit::_mapContactsToPilot(TQMap < recordid_t, TQString > &idContactMap)
|
||
|
{
|
||
|
FUNCTIONSETUP;
|
||
|
|
||
|
idContactMap.clear();
|
||
|
|
||
|
for(AddressBook::Iterator contactIter = aBook->begin();
|
||
|
contactIter != aBook->end(); ++contactIter)
|
||
|
{
|
||
|
Addressee aContact = *contactIter;
|
||
|
TQString recid = aContact.custom(KABCSync::appString, KABCSync::idString);
|
||
|
if(!recid.isEmpty())
|
||
|
{
|
||
|
recordid_t id = recid.toULong();
|
||
|
// safety check: make sure that we don't already have a map for this pilot id.
|
||
|
// if we do (this can come from a copy/paste in kaddressbook, etc.), then we need
|
||
|
// to reset our Addressee so that we can assign him a new pilot Id later and sync
|
||
|
// him properly. if we don't do this, we'll lose one of these on the pilot.
|
||
|
if (!idContactMap.tqcontains(id))
|
||
|
{
|
||
|
idContactMap.insert(id, aContact.uid());
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DEBUGKPILOT << fname << ": found duplicate pilot key: ["
|
||
|
<< id << "], removing pilot id from addressee: ["
|
||
|
<< aContact.realName() << "]" << endl;
|
||
|
aContact.removeCustom(KABCSync::appString, KABCSync::idString);
|
||
|
aBook->insertAddressee(aContact);
|
||
|
abChanged = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
DEBUGKPILOT << fname << ": Loaded " << idContactMap.size() <<
|
||
|
" addresses from the addressbook. " << endl;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
bool AbbrowserConduit::_prepare()
|
||
|
{
|
||
|
FUNCTIONSETUP;
|
||
|
|
||
|
readConfig();
|
||
|
syncedIds.clear();
|
||
|
pilotindex = 0;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
void AbbrowserConduit::readConfig()
|
||
|
{
|
||
|
FUNCTIONSETUP;
|
||
|
AbbrowserSettings::self()->readConfig();
|
||
|
|
||
|
// Conflict page
|
||
|
SyncAction::ConflictResolution res = (SyncAction::ConflictResolution)AbbrowserSettings::conflictResolution();
|
||
|
setConflictResolution(res);
|
||
|
|
||
|
DEBUGKPILOT << fname
|
||
|
<< ": Reading addressbook "
|
||
|
<< ( AbbrowserSettings::addressbookType() == AbbrowserSettings::eAbookFile ?
|
||
|
AbbrowserSettings::fileName() : CSL1("Standard") )
|
||
|
<< endl;
|
||
|
DEBUGKPILOT << fname
|
||
|
<< ": "
|
||
|
<< " fConflictResolution=" << getConflictResolution()
|
||
|
<< " fArchive=" << AbbrowserSettings::archiveDeleted()
|
||
|
<< " fFirstTime=" << isFirstSync()
|
||
|
<< endl;
|
||
|
DEBUGKPILOT << fname
|
||
|
<< ": "
|
||
|
<< " fPilotStreetHome=" << AbbrowserSettings::pilotStreet()
|
||
|
<< " fPilotFaxHome=" << AbbrowserSettings::pilotFax()
|
||
|
<< " eCustom[0]=" << AbbrowserSettings::custom0()
|
||
|
<< " eCustom[1]=" << AbbrowserSettings::custom1()
|
||
|
<< " eCustom[2]=" << AbbrowserSettings::custom2()
|
||
|
<< " eCustom[3]=" << AbbrowserSettings::custom3()
|
||
|
<< endl;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
bool isDeleted(const PilotAddress *addr)
|
||
|
{
|
||
|
if (!addr)
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
if (addr->isDeleted() && !addr->isArchived())
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
if (addr->isArchived())
|
||
|
{
|
||
|
return !AbbrowserSettings::archiveDeleted();
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool isArchived(const PilotAddress *addr)
|
||
|
{
|
||
|
if (addr && addr->isArchived())
|
||
|
{
|
||
|
return AbbrowserSettings::archiveDeleted();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
bool AbbrowserConduit::_loadAddressBook()
|
||
|
{
|
||
|
FUNCTIONSETUP;
|
||
|
|
||
|
startTickle();
|
||
|
switch ( AbbrowserSettings::addressbookType() )
|
||
|
{
|
||
|
case AbbrowserSettings::eAbookResource:
|
||
|
DEBUGKPILOT<<"Loading standard addressbook"<<endl;
|
||
|
aBook = StdAddressBook::self( true );
|
||
|
fCreatedBook=false;
|
||
|
break;
|
||
|
case AbbrowserSettings::eAbookFile:
|
||
|
{ // initialize the abook with the given file
|
||
|
DEBUGKPILOT<<"Loading custom addressbook"<<endl;
|
||
|
KURL kurl(AbbrowserSettings::fileName());
|
||
|
if(!KIO::NetAccess::download(AbbrowserSettings::fileName(), fABookFile, 0L) &&
|
||
|
!kurl.isLocalFile())
|
||
|
{
|
||
|
emit logError(i18n("You chose to sync with the file \"%1\", which "
|
||
|
"cannot be opened. Please make sure to supply a "
|
||
|
"valid file name in the conduit's configuration dialog. "
|
||
|
"Aborting the conduit.").arg(AbbrowserSettings::fileName()));
|
||
|
KIO::NetAccess::removeTempFile(fABookFile);
|
||
|
stopTickle();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
aBook = new AddressBook();
|
||
|
if (!aBook)
|
||
|
{
|
||
|
stopTickle();
|
||
|
return false;
|
||
|
}
|
||
|
fBookResource = new ResourceFile(fABookFile, CSL1("vcard") );
|
||
|
|
||
|
bool r = aBook->addResource( fBookResource );
|
||
|
if ( !r )
|
||
|
{
|
||
|
DEBUGKPILOT << "Unable to open resource for file " << fABookFile << endl;
|
||
|
KPILOT_DELETE( aBook );
|
||
|
stopTickle();
|
||
|
return false;
|
||
|
}
|
||
|
fCreatedBook=true;
|
||
|
break;
|
||
|
}
|
||
|
default: break;
|
||
|
}
|
||
|
// find out if this can fail for reasons other than a non-existent
|
||
|
// vcf file. If so, how can I determine if the missing file was the problem
|
||
|
// or something more serious:
|
||
|
if ( !aBook || !aBook->load() )
|
||
|
{
|
||
|
// Something went wrong, so tell the user and return false to exit the conduit
|
||
|
emit logError(i18n("Unable to initialize and load the addressbook for the sync.") );
|
||
|
addSyncLogEntry(i18n("Unable to initialize and load the addressbook for the sync.") );
|
||
|
WARNINGKPILOT << "Unable to initialize the addressbook for the sync." << endl;
|
||
|
_cleanupAddressBookPointer();
|
||
|
stopTickle();
|
||
|
return false;
|
||
|
}
|
||
|
abChanged = false;
|
||
|
|
||
|
fTicket=aBook->requestSaveTicket();
|
||
|
if (!fTicket)
|
||
|
{
|
||
|
WARNINGKPILOT << "Unable to lock addressbook for writing " << endl;
|
||
|
emit logError(i18n("Unable to lock addressbook for writing. Can't sync!"));
|
||
|
addSyncLogEntry(i18n("Unable to lock addressbook for writing. Can't sync!"));
|
||
|
_cleanupAddressBookPointer();
|
||
|
stopTickle();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
fCtrPC->setStartCount(aBook->allAddressees().count());
|
||
|
|
||
|
// get the addresseMap which maps Pilot unique record(address) id's to
|
||
|
// a Abbrowser Addressee; allows for easy lookup and comparisons
|
||
|
if(aBook->begin() == aBook->end())
|
||
|
{
|
||
|
setFirstSync( true );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
_mapContactsToPilot(addresseeMap);
|
||
|
}
|
||
|
stopTickle();
|
||
|
return(aBook != 0L);
|
||
|
}
|
||
|
|
||
|
bool AbbrowserConduit::_saveAddressBook()
|
||
|
{
|
||
|
FUNCTIONSETUP;
|
||
|
|
||
|
bool saveSuccessful = false;
|
||
|
|
||
|
fCtrPC->setEndCount(aBook->allAddressees().count());
|
||
|
|
||
|
Q_ASSERT(fTicket);
|
||
|
|
||
|
if (abChanged)
|
||
|
{
|
||
|
saveSuccessful = aBook->save(fTicket);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DEBUGKPILOT << fname
|
||
|
<< "Addressbook not changed, no need to save it" << endl;
|
||
|
}
|
||
|
// XXX: KDE4: release ticket in all cases (save no longer releases it)
|
||
|
if ( !saveSuccessful ) // didn't save, delete ticket manually
|
||
|
{
|
||
|
aBook->releaseSaveTicket(fTicket);
|
||
|
}
|
||
|
fTicket=0L;
|
||
|
|
||
|
if ( AbbrowserSettings::addressbookType()!= AbbrowserSettings::eAbookResource )
|
||
|
{
|
||
|
KURL kurl(AbbrowserSettings::fileName());
|
||
|
if(!kurl.isLocalFile())
|
||
|
{
|
||
|
DEBUGKPILOT << fname << "Deleting local addressbook tempfile" << endl;
|
||
|
if(!KIO::NetAccess::upload(fABookFile, AbbrowserSettings::fileName(), 0L)) {
|
||
|
emit logError(i18n("An error occurred while uploading \"%1\". You can try to upload "
|
||
|
"the temporary local file \"%2\" manually")
|
||
|
.arg(AbbrowserSettings::fileName()).arg(fABookFile));
|
||
|
}
|
||
|
else {
|
||
|
KIO::NetAccess::removeTempFile(fABookFile);
|
||
|
}
|
||
|
TQFile backup(fABookFile + CSL1("~"));
|
||
|
backup.remove();
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
// now try to remove the resource from the addressbook...
|
||
|
if (fBookResource)
|
||
|
{
|
||
|
bool r = aBook->removeResource( fBookResource );
|
||
|
if ( !r )
|
||
|
{
|
||
|
DEBUGKPILOT << fname <<": Unable to close resource." << endl;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return saveSuccessful;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
void AbbrowserConduit::_getAppInfo()
|
||
|
{
|
||
|
FUNCTIONSETUP;
|
||
|
|
||
|
delete fAddressAppInfo;
|
||
|
fAddressAppInfo = new PilotAddressInfo(fDatabase);
|
||
|
fAddressAppInfo->dump();
|
||
|
}
|
||
|
|
||
|
void AbbrowserConduit::_setAppInfo()
|
||
|
{
|
||
|
FUNCTIONSETUP;
|
||
|
if (fDatabase) fAddressAppInfo->writeTo(fDatabase);
|
||
|
if (fLocalDatabase) fAddressAppInfo->writeTo(fLocalDatabase);
|
||
|
}
|
||
|
|
||
|
|
||
|
void AbbrowserConduit::_cleanupAddressBookPointer()
|
||
|
{
|
||
|
if (fCreatedBook)
|
||
|
{
|
||
|
KPILOT_DELETE(aBook);
|
||
|
fCreatedBook=false;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
aBook=0L;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/*********************************************************************
|
||
|
D E B U G O U T P U T
|
||
|
*********************************************************************/
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
void AbbrowserConduit::showPilotAddress(const PilotAddress *pilotAddress)
|
||
|
{
|
||
|
FUNCTIONSETUPL(3);
|
||
|
if (debug_level < 3)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
if (!pilotAddress)
|
||
|
{
|
||
|
DEBUGKPILOT<< fname << "| EMPTY"<<endl;
|
||
|
return;
|
||
|
}
|
||
|
DEBUGKPILOT << fname << "\n"
|
||
|
<< pilotAddress->getTextRepresentation(
|
||
|
fAddressAppInfo,Qt::PlainText) << endl;
|
||
|
}
|
||
|
|
||
|
|
||
|
void AbbrowserConduit::showAddresses(
|
||
|
const Addressee &pcAddr,
|
||
|
const PilotAddress *backupAddr,
|
||
|
const PilotAddress *palmAddr)
|
||
|
{
|
||
|
FUNCTIONSETUPL(3);
|
||
|
if (debug_level >= 3)
|
||
|
{
|
||
|
DEBUGKPILOT << fname << "abEntry:" << endl;
|
||
|
KABCSync::showAddressee(pcAddr);
|
||
|
DEBUGKPILOT << fname << "pilotAddress:" << endl;
|
||
|
showPilotAddress(palmAddr);
|
||
|
DEBUGKPILOT << fname << "backupAddress:" << endl;
|
||
|
showPilotAddress(backupAddr);
|
||
|
DEBUGKPILOT << fname << "------------------------------------------------" << endl;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*********************************************************************
|
||
|
S Y N C S T R U C T U R E
|
||
|
*********************************************************************/
|
||
|
|
||
|
|
||
|
|
||
|
/* virtual */ bool AbbrowserConduit::exec()
|
||
|
{
|
||
|
FUNCTIONSETUP;
|
||
|
|
||
|
_prepare();
|
||
|
|
||
|
bool retrieved = false;
|
||
|
if(!openDatabases(CSL1("AddressDB"), &retrieved))
|
||
|
{
|
||
|
emit logError(i18n("Unable to open the addressbook databases on the handheld."));
|
||
|
return false;
|
||
|
}
|
||
|
setFirstSync( retrieved );
|
||
|
|
||
|
_getAppInfo();
|
||
|
|
||
|
// Local block
|
||
|
{
|
||
|
TQString dbpath = fLocalDatabase->dbPathName();
|
||
|
DEBUGKPILOT << fname << ": Local database path " << dbpath << endl;
|
||
|
}
|
||
|
|
||
|
if ( syncMode().isTest() )
|
||
|
{
|
||
|
TQTimer::singleShot(0, this, TQT_SLOT(slotTestRecord()));
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if(!_loadAddressBook())
|
||
|
{
|
||
|
emit logError(i18n("Unable to open the addressbook."));
|
||
|
return false;
|
||
|
}
|
||
|
setFirstSync( isFirstSync() || (aBook->begin() == aBook->end()) );
|
||
|
|
||
|
DEBUGKPILOT << fname << ": First sync now " << isFirstSync()
|
||
|
<< " and addressbook is "
|
||
|
<< ((aBook->begin() == aBook->end()) ? "" : "non-")
|
||
|
<< "empty." << endl;
|
||
|
|
||
|
// perform syncing from palm to abbrowser
|
||
|
// iterate through all records in palm pilot
|
||
|
|
||
|
DEBUGKPILOT << fname << ": fullsync=" << isFullSync() << ", firstSync=" << isFirstSync() << endl;
|
||
|
DEBUGKPILOT << fname << ": "
|
||
|
<< "syncDirection=" << syncMode().name() << ", "
|
||
|
<< "archive = " << AbbrowserSettings::archiveDeleted() << endl;
|
||
|
DEBUGKPILOT << fname << ": conflictRes="<< getConflictResolution() << endl;
|
||
|
DEBUGKPILOT << fname << ": PilotStreetHome=" << AbbrowserSettings::pilotStreet() << ", PilotFaxHOme" << AbbrowserSettings::pilotFax() << endl;
|
||
|
|
||
|
if (!isFirstSync())
|
||
|
{
|
||
|
allIds=fDatabase->idList();
|
||
|
}
|
||
|
|
||
|
TQValueVector<int> v(4);
|
||
|
v[0] = AbbrowserSettings::custom0();
|
||
|
v[1] = AbbrowserSettings::custom1();
|
||
|
v[2] = AbbrowserSettings::custom2();
|
||
|
v[3] = AbbrowserSettings::custom3();
|
||
|
|
||
|
fSyncSettings.setCustomMapping(v);
|
||
|
fSyncSettings.setFieldForOtherPhone(AbbrowserSettings::pilotOther());
|
||
|
fSyncSettings.setDateFormat(AbbrowserSettings::customDateFormat());
|
||
|
fSyncSettings.setPreferHome(AbbrowserSettings::pilotStreet()==0);
|
||
|
fSyncSettings.setFaxTypeOnPC(faxTypeOnPC());
|
||
|
|
||
|
/* Note:
|
||
|
if eCopyPCToHH or eCopyHHToPC, first sync everything, then lookup
|
||
|
those entries on the receiving side that are not yet syncced and delete
|
||
|
them. Use slotDeleteUnsyncedPCRecords and slotDeleteUnsyncedHHRecords
|
||
|
for this, and no longer purge the whole addressbook before the sync to
|
||
|
prevent data loss in case of connection loss. */
|
||
|
|
||
|
TQTimer::singleShot(0, this, TQT_SLOT(slotPalmRecToPC()));
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
void AbbrowserConduit::slotPalmRecToPC()
|
||
|
{
|
||
|
FUNCTIONSETUP;
|
||
|
PilotRecord *palmRec = 0L, *backupRec = 0L;
|
||
|
|
||
|
if ( syncMode() == SyncMode::eCopyPCToHH )
|
||
|
{
|
||
|
DEBUGKPILOT << fname << ": Done; change to PCtoHH phase." << endl;
|
||
|
abiter = aBook->begin();
|
||
|
TQTimer::singleShot(0, this, TQT_SLOT(slotPCRecToPalm()));
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if(isFullSync())
|
||
|
{
|
||
|
palmRec = fDatabase->readRecordByIndex(pilotindex++);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
palmRec = fDatabase->readNextModifiedRec();
|
||
|
}
|
||
|
|
||
|
// no record means we're done going in this direction, so switch to
|
||
|
// PC->Palm
|
||
|
if(!palmRec)
|
||
|
{
|
||
|
abiter = aBook->begin();
|
||
|
TQTimer::singleShot(0, this, TQT_SLOT(slotPCRecToPalm()));
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// already synced, so skip:
|
||
|
if(syncedIds.tqcontains(palmRec->id()))
|
||
|
{
|
||
|
KPILOT_DELETE(palmRec);
|
||
|
TQTimer::singleShot(0, this, TQT_SLOT(slotPalmRecToPC()));
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
backupRec = fLocalDatabase->readRecordById(palmRec->id());
|
||
|
PilotRecord*compareRec=(backupRec)?(backupRec):(palmRec);
|
||
|
Addressee e = _findMatch(PilotAddress(compareRec));
|
||
|
|
||
|
PilotAddress*backupAddr=0L;
|
||
|
if (backupRec)
|
||
|
{
|
||
|
backupAddr=new PilotAddress(backupRec);
|
||
|
}
|
||
|
|
||
|
PilotAddress*palmAddr=0L;
|
||
|
if (palmRec)
|
||
|
{
|
||
|
palmAddr=new PilotAddress(palmRec);
|
||
|
}
|
||
|
|
||
|
syncAddressee(e, backupAddr, palmAddr);
|
||
|
|
||
|
syncedIds.append(palmRec->id());
|
||
|
KPILOT_DELETE(palmAddr);
|
||
|
KPILOT_DELETE(backupAddr);
|
||
|
KPILOT_DELETE(palmRec);
|
||
|
KPILOT_DELETE(backupRec);
|
||
|
|
||
|
TQTimer::singleShot(0, this, TQT_SLOT(slotPalmRecToPC()));
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
void AbbrowserConduit::slotPCRecToPalm()
|
||
|
{
|
||
|
FUNCTIONSETUP;
|
||
|
|
||
|
if ( (syncMode()==SyncMode::eCopyHHToPC) ||
|
||
|
abiter == aBook->end() || (*abiter).isEmpty() )
|
||
|
{
|
||
|
DEBUGKPILOT << fname << ": Done; change to delete records." << endl;
|
||
|
pilotindex = 0;
|
||
|
TQTimer::singleShot(0, this, TQT_SLOT(slotDeletedRecord()));
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
PilotRecord *palmRec=0L, *backupRec=0L;
|
||
|
Addressee ad = *abiter;
|
||
|
|
||
|
abiter++;
|
||
|
|
||
|
// If marked as archived, don't sync!
|
||
|
if (KABCSync::isArchived(ad))
|
||
|
{
|
||
|
DEBUGKPILOT << fname << ": address with id " << ad.uid() <<
|
||
|
" marked archived, so don't sync." << endl;
|
||
|
TQTimer::singleShot(0, this, TQT_SLOT(slotPCRecToPalm()));
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
|
||
|
TQString recID(ad.custom(KABCSync::appString, KABCSync::idString));
|
||
|
bool ok;
|
||
|
recordid_t rid = recID.toLong(&ok);
|
||
|
if (recID.isEmpty() || !ok || !rid)
|
||
|
{
|
||
|
DEBUGKPILOT << fname << ": This is a new record." << endl;
|
||
|
// it's a new item(no record ID and not inserted by the Palm -> PC sync), so add it
|
||
|
syncAddressee(ad, 0L, 0L);
|
||
|
TQTimer::singleShot(0, this, TQT_SLOT(slotPCRecToPalm()));
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// look into the list of already synced record ids to see if the addressee hasn't already been synced
|
||
|
if (syncedIds.tqcontains(rid))
|
||
|
{
|
||
|
DEBUGKPILOT << ": address with id " << rid << " already synced." << endl;
|
||
|
TQTimer::singleShot(0, this, TQT_SLOT(slotPCRecToPalm()));
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
|
||
|
backupRec = fLocalDatabase->readRecordById(rid);
|
||
|
// only update if no backup record or the backup record is not equal to the addressee
|
||
|
|
||
|
PilotAddress*backupAddr=0L;
|
||
|
if (backupRec)
|
||
|
{
|
||
|
backupAddr=new PilotAddress(backupRec);
|
||
|
}
|
||
|
if(!backupRec || isFirstSync() || !_equal(backupAddr, ad) )
|
||
|
{
|
||
|
DEBUGKPILOT << fname << ": Updating entry." << endl;
|
||
|
palmRec = fDatabase->readRecordById(rid);
|
||
|
PilotAddress *palmAddr = 0L;
|
||
|
if (palmRec)
|
||
|
{
|
||
|
palmAddr = new PilotAddress(palmRec);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DEBUGKPILOT << fname << ": No HH record with id " << rid << endl;
|
||
|
}
|
||
|
syncAddressee(ad, backupAddr, palmAddr);
|
||
|
// update the id just in case it changed
|
||
|
if (palmRec) rid=palmRec->id();
|
||
|
KPILOT_DELETE(palmRec);
|
||
|
KPILOT_DELETE(palmAddr);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DEBUGKPILOT << fname << ": Entry not updated." << endl;
|
||
|
}
|
||
|
KPILOT_DELETE(backupAddr);
|
||
|
KPILOT_DELETE(backupRec);
|
||
|
|
||
|
DEBUGKPILOT << fname << ": adding id:["<< rid << "] to syncedIds." << endl;
|
||
|
|
||
|
syncedIds.append(rid);
|
||
|
// done with the sync process, go on with the next one:
|
||
|
TQTimer::singleShot(0, this, TQT_SLOT(slotPCRecToPalm()));
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
void AbbrowserConduit::slotDeletedRecord()
|
||
|
{
|
||
|
FUNCTIONSETUP;
|
||
|
|
||
|
PilotRecord *backupRec = fLocalDatabase->readRecordByIndex(pilotindex++);
|
||
|
if(!backupRec || isFirstSync() )
|
||
|
{
|
||
|
KPILOT_DELETE(backupRec);
|
||
|
TQTimer::singleShot(0, this, TQT_SLOT(slotDeleteUnsyncedPCRecords()));
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
recordid_t id = backupRec->id();
|
||
|
|
||
|
TQString uid = addresseeMap[id];
|
||
|
Addressee e = aBook->tqfindByUid(uid);
|
||
|
|
||
|
DEBUGKPILOT << fname << ": now looking at palm id: ["
|
||
|
<< id << "], kabc uid: [" << uid << "]." << endl;
|
||
|
|
||
|
PilotAddress*backupAddr=0L;
|
||
|
if (backupRec)
|
||
|
{
|
||
|
backupAddr=new PilotAddress(backupRec);
|
||
|
}
|
||
|
PilotRecord*palmRec=fDatabase->readRecordById(id);
|
||
|
|
||
|
if ( e.isEmpty() )
|
||
|
{
|
||
|
DEBUGKPILOT << fname << ": no Addressee found for this id." << endl;
|
||
|
DEBUGKPILOT << fname << "\n"
|
||
|
<< backupAddr->getTextRepresentation(
|
||
|
fAddressAppInfo,Qt::PlainText) << endl;
|
||
|
|
||
|
if (palmRec) {
|
||
|
DEBUGKPILOT << fname << ": deleting from database on palm." << endl;
|
||
|
fDatabase->deleteRecord(id);
|
||
|
fCtrHH->deleted();
|
||
|
}
|
||
|
DEBUGKPILOT << fname << ": deleting from backup database." << endl;
|
||
|
fLocalDatabase->deleteRecord(id);
|
||
|
|
||
|
// because we just deleted a record, we need to go back one
|
||
|
pilotindex--;
|
||
|
}
|
||
|
|
||
|
KPILOT_DELETE(palmRec);
|
||
|
KPILOT_DELETE(backupAddr);
|
||
|
KPILOT_DELETE(backupRec);
|
||
|
TQTimer::singleShot(0, this, TQT_SLOT(slotDeletedRecord()));
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
void AbbrowserConduit::slotDeleteUnsyncedPCRecords()
|
||
|
{
|
||
|
FUNCTIONSETUP;
|
||
|
if ( syncMode()==SyncMode::eCopyHHToPC )
|
||
|
{
|
||
|
TQStringList uids;
|
||
|
RecordIDList::iterator it;
|
||
|
TQString uid;
|
||
|
for ( it = syncedIds.begin(); it != syncedIds.end(); ++it)
|
||
|
{
|
||
|
uid=addresseeMap[*it];
|
||
|
if (!uid.isEmpty()) uids.append(uid);
|
||
|
}
|
||
|
// TODO: Does this speed up anything?
|
||
|
// qHeapSort( uids );
|
||
|
AddressBook::Iterator abit;
|
||
|
for (abit = aBook->begin(); abit != aBook->end(); ++abit)
|
||
|
{
|
||
|
if (!uids.tqcontains((*abit).uid()))
|
||
|
{
|
||
|
DEBUGKPILOT<<"Deleting addressee "<<(*abit).realName()<<" from PC (is not on HH, and syncing with HH->PC direction)"<<endl;
|
||
|
abChanged = true;
|
||
|
// TODO: Can I really remove the current iterator???
|
||
|
aBook->removeAddressee(*abit);
|
||
|
fCtrPC->deleted();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
TQTimer::singleShot(0, this, TQT_SLOT(slotDeleteUnsyncedHHRecords()));
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
void AbbrowserConduit::slotDeleteUnsyncedHHRecords()
|
||
|
{
|
||
|
FUNCTIONSETUP;
|
||
|
if ( syncMode()==SyncMode::eCopyPCToHH )
|
||
|
{
|
||
|
RecordIDList ids=fDatabase->idList();
|
||
|
RecordIDList::iterator it;
|
||
|
for ( it = ids.begin(); it != ids.end(); ++it )
|
||
|
{
|
||
|
if (!syncedIds.tqcontains(*it))
|
||
|
{
|
||
|
DEBUGKPILOT<<"Deleting record with ID "<<*it<<" from handheld (is not on PC, and syncing with PC->HH direction)"<<endl;
|
||
|
fDatabase->deleteRecord(*it);
|
||
|
fCtrHH->deleted();
|
||
|
fLocalDatabase->deleteRecord(*it);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
TQTimer::singleShot(0, this, TQT_SLOT(slotCleanup()));
|
||
|
}
|
||
|
|
||
|
|
||
|
void AbbrowserConduit::slotCleanup()
|
||
|
{
|
||
|
FUNCTIONSETUP;
|
||
|
|
||
|
// Set the appInfoBlock, just in case the category labels changed
|
||
|
_setAppInfo();
|
||
|
if(fDatabase)
|
||
|
{
|
||
|
fDatabase->resetSyncFlags();
|
||
|
fDatabase->cleanup();
|
||
|
}
|
||
|
if(fLocalDatabase)
|
||
|
{
|
||
|
fLocalDatabase->resetSyncFlags();
|
||
|
fLocalDatabase->cleanup();
|
||
|
}
|
||
|
|
||
|
// Write out the sync maps
|
||
|
TQString syncFile = fLocalDatabase->dbPathName() + CSL1(".sync");
|
||
|
DEBUGKPILOT << fname << ": Writing sync map to " << syncFile << endl;
|
||
|
KSaveFile map( syncFile );
|
||
|
if ( map.status() == 0 )
|
||
|
{
|
||
|
DEBUGKPILOT << fname << ": Writing sync map ..." << endl;
|
||
|
(*map.dataStream()) << addresseeMap ;
|
||
|
map.close();
|
||
|
}
|
||
|
// This also picks up errors from map.close()
|
||
|
if ( map.status() != 0 )
|
||
|
{
|
||
|
WARNINGKPILOT << "Could not make backup of sync map." << endl;
|
||
|
}
|
||
|
|
||
|
_saveAddressBook();
|
||
|
delayDone();
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*********************************************************************
|
||
|
G E N E R A L S Y N C F U N C T I O N
|
||
|
These functions modify the Handheld and the addressbook
|
||
|
*********************************************************************/
|
||
|
|
||
|
|
||
|
|
||
|
bool AbbrowserConduit::syncAddressee(Addressee &pcAddr, PilotAddress*backupAddr,
|
||
|
PilotAddress*palmAddr)
|
||
|
{
|
||
|
FUNCTIONSETUP;
|
||
|
showAddresses(pcAddr, backupAddr, palmAddr);
|
||
|
|
||
|
if ( syncMode() == SyncMode::eCopyPCToHH )
|
||
|
{
|
||
|
if (pcAddr.isEmpty())
|
||
|
{
|
||
|
return _deleteAddressee(pcAddr, backupAddr, palmAddr);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return _copyToHH(pcAddr, backupAddr, palmAddr);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( syncMode() == SyncMode::eCopyHHToPC )
|
||
|
{
|
||
|
if (!palmAddr)
|
||
|
{
|
||
|
return _deleteAddressee(pcAddr, backupAddr, palmAddr);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return _copyToPC(pcAddr, backupAddr, palmAddr);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( !backupAddr || isFirstSync() )
|
||
|
{
|
||
|
DEBUGKPILOT<< fname << ": Special case: no backup." << endl;
|
||
|
/*
|
||
|
Resolution matrix (0..does not exist, E..exists, D..deleted flag set, A..archived):
|
||
|
HH PC | Resolution
|
||
|
------------------------------------------------------------
|
||
|
0 A | -
|
||
|
0 E | PC -> HH, reset ID if not set correctly
|
||
|
D 0 | delete (error, should never occur!!!)
|
||
|
D E | CR (ERROR)
|
||
|
E/A 0 | HH -> PC
|
||
|
E/A E/A| merge/CR
|
||
|
*/
|
||
|
if (!palmAddr && KABCSync::isArchived(pcAddr) )
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
else if (!palmAddr && !pcAddr.isEmpty())
|
||
|
{
|
||
|
DEBUGKPILOT << fname << ": case: 1a"<<endl;
|
||
|
// PC->HH
|
||
|
bool res=_copyToHH(pcAddr, 0L, 0L);
|
||
|
return res;
|
||
|
}
|
||
|
else if (!palmAddr && pcAddr.isEmpty())
|
||
|
{
|
||
|
DEBUGKPILOT << fname << ": case: 1b"<<endl;
|
||
|
// everything's empty -> ERROR
|
||
|
return false;
|
||
|
}
|
||
|
else if ( (isDeleted(palmAddr) || isArchived(palmAddr)) && pcAddr.isEmpty())
|
||
|
{
|
||
|
DEBUGKPILOT << fname << ": case: 1c"<<endl;
|
||
|
if (isArchived(palmAddr))
|
||
|
return _copyToPC(pcAddr, 0L, palmAddr);
|
||
|
else
|
||
|
// this happens if you add a record on the handheld and delete it again before you do the next sync
|
||
|
return _deleteAddressee(pcAddr, 0L, palmAddr);
|
||
|
}
|
||
|
else if ((isDeleted(palmAddr)||isArchived(palmAddr)) && !pcAddr.isEmpty())
|
||
|
{
|
||
|
DEBUGKPILOT << fname << ": case: 1d"<<endl;
|
||
|
// CR (ERROR)
|
||
|
return _smartMergeAddressee(pcAddr, 0L, palmAddr);
|
||
|
}
|
||
|
else if (pcAddr.isEmpty())
|
||
|
{
|
||
|
DEBUGKPILOT << fname << ": case: 1e"<<endl;
|
||
|
// HH->PC
|
||
|
return _copyToPC(pcAddr, 0L, palmAddr);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DEBUGKPILOT << fname << ": case: 1f"<<endl;
|
||
|
// Conflict Resolution
|
||
|
return _smartMergeAddressee(pcAddr, 0L, palmAddr);
|
||
|
}
|
||
|
} // !backupAddr
|
||
|
else
|
||
|
{
|
||
|
DEBUGKPILOT << fname << ": case: 2"<<endl;
|
||
|
/*
|
||
|
Resolution matrix:
|
||
|
1) if HH.(empty| (deleted &! archived) ) -> { if (PC==B) -> delete, else -> CR }
|
||
|
if HH.archied -> {if (PC==B) -> copyToPC, else -> CR }
|
||
|
if PC.empty -> { if (HH==B) -> delete, else -> CR }
|
||
|
if PC.archived -> {if (HH==B) -> delete on HH, else CR }
|
||
|
2) if PC==HH -> { update B, update ID of PC if needed }
|
||
|
3) if PC==B -> { HH!=PC, thus HH modified, so copy HH->PC }
|
||
|
if HH==B -> { PC!=HH, thus PC modified, so copy PC->HH }
|
||
|
4) else: all three addressees are different -> CR
|
||
|
*/
|
||
|
|
||
|
if (!palmAddr || isDeleted(palmAddr) )
|
||
|
{
|
||
|
DEBUGKPILOT << fname << ": case: 2a"<<endl;
|
||
|
if (_equal(backupAddr, pcAddr) || pcAddr.isEmpty())
|
||
|
{
|
||
|
return _deleteAddressee(pcAddr, backupAddr, 0L);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return _smartMergeAddressee(pcAddr, backupAddr, 0L);
|
||
|
}
|
||
|
}
|
||
|
else if (pcAddr.isEmpty())
|
||
|
{
|
||
|
DEBUGKPILOT << fname << ": case: 2b"<<endl;
|
||
|
if (*palmAddr == *backupAddr)
|
||
|
{
|
||
|
return _deleteAddressee(pcAddr, backupAddr, palmAddr);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return _smartMergeAddressee(pcAddr, backupAddr, palmAddr);
|
||
|
}
|
||
|
}
|
||
|
else if (_equal(palmAddr, pcAddr))
|
||
|
{
|
||
|
DEBUGKPILOT << fname << ": case: 2c"<<endl;
|
||
|
// update Backup, update ID of PC if neededd
|
||
|
return _writeBackup(palmAddr);
|
||
|
}
|
||
|
else if (_equal(backupAddr, pcAddr))
|
||
|
{
|
||
|
DEBUGKPILOT << fname << ": case: 2d"<<endl;
|
||
|
DEBUGKPILOT << fname << ": Flags: "<<palmAddr->attributes()<<", isDeleted="<<
|
||
|
isDeleted(palmAddr)<<", isArchived="<<isArchived(palmAddr)<<endl;
|
||
|
if (isDeleted(palmAddr))
|
||
|
return _deleteAddressee(pcAddr, backupAddr, palmAddr);
|
||
|
else
|
||
|
return _copyToPC(pcAddr, backupAddr, palmAddr);
|
||
|
}
|
||
|
else if (*palmAddr == *backupAddr)
|
||
|
{
|
||
|
DEBUGKPILOT << fname << ": case: 2e"<<endl;
|
||
|
return _copyToHH(pcAddr, backupAddr, palmAddr);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DEBUGKPILOT << fname << ": case: 2f"<<endl;
|
||
|
// CR, since all are different
|
||
|
return _smartMergeAddressee(pcAddr, backupAddr, palmAddr);
|
||
|
}
|
||
|
} // backupAddr
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
bool AbbrowserConduit::_copyToHH(Addressee &pcAddr, PilotAddress*backupAddr,
|
||
|
PilotAddress*palmAddr)
|
||
|
{
|
||
|
FUNCTIONSETUP;
|
||
|
|
||
|
if (pcAddr.isEmpty()) return false;
|
||
|
PilotAddress*paddr=palmAddr;
|
||
|
bool paddrcreated=false;
|
||
|
if (!paddr)
|
||
|
{
|
||
|
paddr=new PilotAddress();
|
||
|
paddrcreated=true;
|
||
|
fCtrHH->created();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
fCtrHH->updated();
|
||
|
}
|
||
|
KABCSync::copy(*paddr, pcAddr, *fAddressAppInfo, fSyncSettings);
|
||
|
|
||
|
DEBUGKPILOT << fname << "palmAddr->id=" << paddr->id()
|
||
|
<< ", pcAddr.ID=" << pcAddr.custom(KABCSync::appString, KABCSync::idString) << endl;
|
||
|
|
||
|
if(_savePalmAddr(paddr, pcAddr))
|
||
|
{
|
||
|
_savePCAddr(pcAddr, backupAddr, paddr);
|
||
|
}
|
||
|
if (paddrcreated) KPILOT_DELETE(paddr);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
bool AbbrowserConduit::_copyToPC(Addressee &pcAddr, PilotAddress*backupAddr,
|
||
|
PilotAddress*palmAddr)
|
||
|
{
|
||
|
FUNCTIONSETUP;
|
||
|
if (!palmAddr)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
// keep track of CUD's...
|
||
|
if (pcAddr.isEmpty())
|
||
|
{
|
||
|
fCtrPC->created();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
fCtrPC->updated();
|
||
|
}
|
||
|
showPilotAddress(palmAddr);
|
||
|
|
||
|
KABCSync::copy(pcAddr, *palmAddr, *fAddressAppInfo, fSyncSettings);
|
||
|
if (isArchived(palmAddr))
|
||
|
{
|
||
|
KABCSync::makeArchived(pcAddr);
|
||
|
}
|
||
|
|
||
|
_savePCAddr(pcAddr, backupAddr, palmAddr);
|
||
|
_writeBackup(palmAddr);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
bool AbbrowserConduit::_writeBackup(PilotAddress *backup)
|
||
|
{
|
||
|
FUNCTIONSETUP;
|
||
|
if (!backup) return false;
|
||
|
|
||
|
showPilotAddress(backup);
|
||
|
|
||
|
PilotRecord *pilotRec = backup->pack();
|
||
|
fLocalDatabase->writeRecord(pilotRec);
|
||
|
KPILOT_DELETE(pilotRec);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
bool AbbrowserConduit::_deleteAddressee(Addressee &pcAddr, PilotAddress*backupAddr,
|
||
|
PilotAddress*palmAddr)
|
||
|
{
|
||
|
FUNCTIONSETUP;
|
||
|
|
||
|
if (palmAddr)
|
||
|
{
|
||
|
if (!syncedIds.tqcontains(palmAddr->id())) {
|
||
|
DEBUGKPILOT << fname << ": adding id:["<< palmAddr->id() << "] to syncedIds." << endl;
|
||
|
syncedIds.append(palmAddr->id());
|
||
|
}
|
||
|
fDatabase->deleteRecord(palmAddr->id());
|
||
|
fCtrHH->deleted();
|
||
|
fLocalDatabase->deleteRecord(palmAddr->id());
|
||
|
}
|
||
|
else if (backupAddr)
|
||
|
{
|
||
|
if (!syncedIds.tqcontains(backupAddr->id())) {
|
||
|
DEBUGKPILOT << fname << ": adding id:["<< backupAddr->id() << "] to syncedIds." << endl;
|
||
|
syncedIds.append(backupAddr->id());
|
||
|
}
|
||
|
fLocalDatabase->deleteRecord(backupAddr->id());
|
||
|
}
|
||
|
if (!pcAddr.isEmpty())
|
||
|
{
|
||
|
DEBUGKPILOT << fname << " removing " << pcAddr.formattedName() << endl;
|
||
|
abChanged = true;
|
||
|
aBook->removeAddressee(pcAddr);
|
||
|
fCtrPC->deleted();
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*********************************************************************
|
||
|
l o w - l e v e l f u n c t i o n s f o r
|
||
|
adding / removing palm/pc records
|
||
|
*********************************************************************/
|
||
|
|
||
|
|
||
|
|
||
|
bool AbbrowserConduit::_savePalmAddr(PilotAddress *palmAddr, Addressee &pcAddr)
|
||
|
{
|
||
|
FUNCTIONSETUP;
|
||
|
|
||
|
DEBUGKPILOT << fname << ": Saving to pilot " << palmAddr->id()
|
||
|
<< " " << palmAddr->getField(entryFirstname)
|
||
|
<< " " << palmAddr->getField(entryLastname)<< endl;
|
||
|
|
||
|
PilotRecord *pilotRec = palmAddr->pack();
|
||
|
DEBUGKPILOT << fname << ": record with id=" << pilotRec->id()
|
||
|
<< " len=" << pilotRec->size() << endl;
|
||
|
recordid_t pilotId = fDatabase->writeRecord(pilotRec);
|
||
|
DEBUGKPILOT << fname << ": Wrote "<<pilotId<<": ID="<<pilotRec->id()<<endl;
|
||
|
fLocalDatabase->writeRecord(pilotRec);
|
||
|
KPILOT_DELETE(pilotRec);
|
||
|
|
||
|
// pilotId == 0 if using local db, so don't overwrite the valid id
|
||
|
if(pilotId != 0)
|
||
|
{
|
||
|
palmAddr->setID(pilotId);
|
||
|
if (!syncedIds.tqcontains(pilotId)) {
|
||
|
DEBUGKPILOT << fname << ": adding id:["<< pilotId << "] to syncedIds." << endl;
|
||
|
syncedIds.append(pilotId);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
recordid_t abId = 0;
|
||
|
abId = pcAddr.custom(KABCSync::appString, KABCSync::idString).toUInt();
|
||
|
if(abId != pilotId)
|
||
|
{
|
||
|
pcAddr.insertCustom(KABCSync::appString, KABCSync::idString, TQString::number(pilotId));
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
bool AbbrowserConduit::_savePCAddr(Addressee &pcAddr, PilotAddress*,
|
||
|
PilotAddress*)
|
||
|
{
|
||
|
FUNCTIONSETUP;
|
||
|
|
||
|
DEBUGKPILOT<<"Before _savePCAddr, pcAddr.custom="<<pcAddr.custom(KABCSync::appString, KABCSync::idString)<<endl;
|
||
|
TQString pilotId = pcAddr.custom(KABCSync::appString, KABCSync::idString);
|
||
|
long pilotIdL = pilotId.toLong();
|
||
|
if(!pilotId.isEmpty())
|
||
|
{
|
||
|
// because we maintain a mapping between pilotId -> kabc uid, whenever we add
|
||
|
// a new relationship, we have to remove any old mapping that would tie a different
|
||
|
// pilot id -> this kabc uid
|
||
|
TQMap < recordid_t, TQString>::iterator it;
|
||
|
for ( it = addresseeMap.begin(); it != addresseeMap.end(); ++it ) {
|
||
|
TQString kabcUid = it.data();
|
||
|
if (kabcUid == pcAddr.uid()) {
|
||
|
addresseeMap.remove(it);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// now put the new mapping in
|
||
|
addresseeMap.insert(pilotIdL, pcAddr.uid());
|
||
|
}
|
||
|
|
||
|
aBook->insertAddressee(pcAddr);
|
||
|
|
||
|
abChanged = true;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/*********************************************************************
|
||
|
C O P Y R E C O R D S
|
||
|
*********************************************************************/
|
||
|
|
||
|
|
||
|
|
||
|
bool AbbrowserConduit::_equal(const PilotAddress *piAddress, const Addressee &abEntry,
|
||
|
enum eqFlagsType flags) const
|
||
|
{
|
||
|
FUNCTIONSETUP;
|
||
|
|
||
|
// empty records are never equal!
|
||
|
if (!piAddress) {
|
||
|
DEBUGKPILOT << fname << ": no pilot address passed" << endl;
|
||
|
return false;
|
||
|
}
|
||
|
if (abEntry.isEmpty()) {
|
||
|
DEBUGKPILOT << fname << ":abEntry.isEmpty()" << endl;
|
||
|
return false;
|
||
|
}
|
||
|
// Archived records match anything so they won't be copied to the HH again
|
||
|
if (flags & eqFlagsFlags)
|
||
|
if (isArchived(piAddress) && KABCSync::isArchived(abEntry) ) return true;
|
||
|
|
||
|
if (flags & eqFlagsName)
|
||
|
{
|
||
|
if(!_equal(abEntry.familyName(), piAddress->getField(entryLastname)))
|
||
|
{
|
||
|
DEBUGKPILOT << fname << ": last name not equal" << endl;
|
||
|
return false;
|
||
|
}
|
||
|
if(!_equal(abEntry.givenName(), piAddress->getField(entryFirstname)))
|
||
|
{
|
||
|
DEBUGKPILOT << fname << ": first name not equal" << endl;
|
||
|
return false;
|
||
|
}
|
||
|
if(!_equal(abEntry.prefix(), piAddress->getField(entryTitle)))
|
||
|
{
|
||
|
DEBUGKPILOT << fname << ": title/prefix not equal" << endl;
|
||
|
return false;
|
||
|
}
|
||
|
if(!_equal(abEntry.organization(), piAddress->getField(entryCompany)))
|
||
|
{
|
||
|
DEBUGKPILOT << fname << ": company/organization not equal" << endl;
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
if (flags & eqFlagsNote)
|
||
|
if(!_equal(abEntry.note(), piAddress->getField(entryNote)))
|
||
|
{
|
||
|
DEBUGKPILOT << fname << ": note not equal" << endl;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (flags & eqFlagsCategory)
|
||
|
{
|
||
|
// Check that the name of the category of the HH record
|
||
|
// is one matching the PC record.
|
||
|
TQString addressCategoryLabel = fAddressAppInfo->categoryName(piAddress->category());
|
||
|
TQString cat = KABCSync::bestMatchedCategoryName(abEntry.categories(),
|
||
|
*fAddressAppInfo, piAddress->category());
|
||
|
if(!_equal(cat, addressCategoryLabel))
|
||
|
{
|
||
|
DEBUGKPILOT << fname << ": category not equal" << endl;
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (flags & eqFlagsPhones)
|
||
|
{
|
||
|
// first, look for missing e-mail addresses on either side
|
||
|
TQStringList abEmails(abEntry.emails());
|
||
|
TQStringList piEmails(piAddress->getEmails());
|
||
|
|
||
|
if (abEmails.count() != piEmails.count())
|
||
|
{
|
||
|
DEBUGKPILOT << fname << ": email count not equal" << endl;
|
||
|
return false;
|
||
|
}
|
||
|
for (TQStringList::Iterator it = abEmails.begin(); it != abEmails.end(); it++) {
|
||
|
if (!piEmails.tqcontains(*it))
|
||
|
{
|
||
|
DEBUGKPILOT << fname << ": pilot e-mail missing" << endl;
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
for (TQStringList::Iterator it = piEmails.begin(); it != piEmails.end(); it++) {
|
||
|
if (!abEmails.tqcontains(*it))
|
||
|
{
|
||
|
DEBUGKPILOT << fname << ": kabc e-mail missing" << endl;
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// now look for differences in phone numbers. Note: we can't just compare one
|
||
|
// of each kind of phone number, because there's no guarantee that if the user
|
||
|
// has more than one of a given type, we're comparing the correct two.
|
||
|
|
||
|
PhoneNumber::List abPhones(abEntry.phoneNumbers());
|
||
|
PhoneNumber::List piPhones = KABCSync::getPhoneNumbers(*piAddress);
|
||
|
// first make sure that all of the pilot phone numbers are in kabc
|
||
|
for (PhoneNumber::List::Iterator it = piPhones.begin(); it != piPhones.end(); it++) {
|
||
|
PhoneNumber piPhone = *it;
|
||
|
bool found=false;
|
||
|
for (PhoneNumber::List::Iterator it = abPhones.begin(); it != abPhones.end(); it++) {
|
||
|
PhoneNumber abPhone = *it;
|
||
|
// see if we have the same number here...
|
||
|
// * Note * We used to check for preferred number matching, but
|
||
|
// this seems to have broke in kdepim 3.5 and I don't have time to
|
||
|
// figure out why, so we won't check to see if preferred number match
|
||
|
if ( _equal(piPhone.number(), abPhone.number()) ) {
|
||
|
found = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (!found) {
|
||
|
DEBUGKPILOT << fname << ": not equal because kabc phone not found." << endl;
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
// now the other way. *cringe* kabc has the capacity to store way more addresses
|
||
|
// than the Pilot, so this might give false positives more than we'd want....
|
||
|
for (PhoneNumber::List::Iterator it = abPhones.begin(); it != abPhones.end(); it++) {
|
||
|
PhoneNumber abPhone = *it;
|
||
|
bool found=false;
|
||
|
for (PhoneNumber::List::Iterator it = piPhones.begin(); it != piPhones.end(); it++) {
|
||
|
PhoneNumber piPhone = *it;
|
||
|
if ( _equal(piPhone.number(), abPhone.number()) ) {
|
||
|
found = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (!found)
|
||
|
{
|
||
|
DEBUGKPILOT << fname << ": not equal because pilot phone not found." << endl;
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(!_equal(KABCSync::getFieldForHHOtherPhone(abEntry,fSyncSettings),
|
||
|
piAddress->getPhoneField(PilotAddressInfo::eOther)))
|
||
|
{
|
||
|
DEBUGKPILOT << fname << ": not equal because of other phone field." << endl;
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (flags & eqFlagsAdress)
|
||
|
{
|
||
|
KABC::Address address = KABCSync::getAddress(abEntry,fSyncSettings);
|
||
|
if(!_equal(address.street(), piAddress->getField(entryAddress)))
|
||
|
{
|
||
|
DEBUGKPILOT << fname << ": address not equal" << endl;
|
||
|
return false;
|
||
|
}
|
||
|
if(!_equal(address.locality(), piAddress->getField(entryCity)))
|
||
|
{
|
||
|
DEBUGKPILOT << fname << ": city not equal" << endl;
|
||
|
return false;
|
||
|
}
|
||
|
if(!_equal(address.region(), piAddress->getField(entryState)))
|
||
|
{
|
||
|
DEBUGKPILOT << fname << ": state not equal" << endl;
|
||
|
return false;
|
||
|
}
|
||
|
if(!_equal(address.postalCode(), piAddress->getField(entryZip)))
|
||
|
{
|
||
|
DEBUGKPILOT << fname << ": zip not equal" << endl;
|
||
|
return false;
|
||
|
}
|
||
|
if(!_equal(address.country(), piAddress->getField(entryCountry)))
|
||
|
{
|
||
|
DEBUGKPILOT << fname << ": country not equal" << endl;
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (flags & eqFlagsCustom)
|
||
|
{
|
||
|
unsigned int customIndex = 0;
|
||
|
unsigned int hhField = entryCustom1;
|
||
|
|
||
|
for ( ; customIndex<4; ++customIndex,++hhField )
|
||
|
{
|
||
|
if (!_equal(KABCSync::getFieldForHHCustom(customIndex, abEntry, fSyncSettings),
|
||
|
piAddress->getField(hhField)))
|
||
|
{
|
||
|
DEBUGKPILOT << fname << ": Custom field " << customIndex
|
||
|
<< " (HH field " << hhField << ") differs." << endl;
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// if any side is marked archived, but the other is not, the two
|
||
|
// are not equal.
|
||
|
if ( (flags & eqFlagsFlags) && (isArchived(piAddress) || KABCSync::isArchived(abEntry) ) )
|
||
|
{
|
||
|
DEBUGKPILOT << fname << ": archived flags don't match" << endl;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/*********************************************************************
|
||
|
C O N F L I C T R E S O L U T I O N a n d M E R G I N G
|
||
|
*********************************************************************/
|
||
|
|
||
|
|
||
|
|
||
|
/** smartly merge the given field for the given entry. use the backup record to determine which record has been modified
|
||
|
@pc, @backup, @palm ... entries of the according databases
|
||
|
@returns string of the merged entries.
|
||
|
*/
|
||
|
TQString AbbrowserConduit::_smartMergeString(const TQString &pc, const TQString & backup,
|
||
|
const TQString & palm, ConflictResolution confRes)
|
||
|
{
|
||
|
FUNCTIONSETUP;
|
||
|
|
||
|
// if both entries are already the same, no need to do anything
|
||
|
if(pc == palm) return pc;
|
||
|
|
||
|
// If this is a first sync, we don't have a backup record, so
|
||
|
if(isFirstSync() || backup.isEmpty()) {
|
||
|
if (pc.isEmpty() && palm.isEmpty() ) return TQString::null;
|
||
|
if(pc.isEmpty()) return palm;
|
||
|
if(palm.isEmpty()) return pc;
|
||
|
} else {
|
||
|
// only one side modified, so return that string, no conflict
|
||
|
if(palm == backup) return pc;
|
||
|
if(pc == backup) return palm;
|
||
|
}
|
||
|
|
||
|
DEBUGKPILOT<<"pc="<<pc<<", backup="<<backup<<", palm="<<
|
||
|
palm<<", ConfRes="<<confRes<<endl;
|
||
|
DEBUGKPILOT<<"Use conflict resolution :"<<confRes<<
|
||
|
", PC="<<SyncAction::ePCOverrides<<endl;
|
||
|
switch(confRes) {
|
||
|
case SyncAction::ePCOverrides: return pc; break;
|
||
|
case SyncAction::eHHOverrides: return palm; break;
|
||
|
case SyncAction::ePreviousSyncOverrides: return backup; break;
|
||
|
default: break;
|
||
|
}
|
||
|
return TQString::null;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
bool AbbrowserConduit::_buildResolutionTable(ResolutionTable*tab, const Addressee &pcAddr,
|
||
|
PilotAddress *backupAddr, PilotAddress *palmAddr)
|
||
|
{
|
||
|
FUNCTIONSETUP;
|
||
|
if (!tab) return false;
|
||
|
tab->setAutoDelete( TRUE );
|
||
|
tab->labels[0]=i18n("Item on PC");
|
||
|
tab->labels[1]=i18n("Handheld");
|
||
|
tab->labels[2]=i18n("Last sync");
|
||
|
if (!pcAddr.isEmpty())
|
||
|
tab->fExistItems=(eExistItems)(tab->fExistItems|eExistsPC);
|
||
|
if (backupAddr)
|
||
|
tab->fExistItems=(eExistItems)(tab->fExistItems|eExistsBackup);
|
||
|
if (palmAddr)
|
||
|
tab->fExistItems=(eExistItems)(tab->fExistItems|eExistsPalm);
|
||
|
|
||
|
#define appendGen(desc, abfield, palmfield) \
|
||
|
tab->append(new ResolutionItem(desc, tab->fExistItems, \
|
||
|
(!pcAddr.isEmpty())?(abfield):(TQString::null), \
|
||
|
(palmAddr)?(palmAddr->palmfield):(TQString::null), \
|
||
|
(backupAddr)?(backupAddr->palmfield):(TQString::null) ))
|
||
|
#define appendAddr(desc, abfield, palmfield) \
|
||
|
appendGen(desc, abfield, getField(palmfield))
|
||
|
#define appendGenPhone(desc, abfield, palmfield) \
|
||
|
appendGen(desc, abfield, getPhoneField(PilotAddressInfo::palmfield))
|
||
|
#define appendPhone(desc, abfield, palmfield) \
|
||
|
appendGenPhone(desc, pcAddr.phoneNumber(PhoneNumber::abfield).number(), palmfield)
|
||
|
|
||
|
|
||
|
appendAddr(i18n("Last name"), pcAddr.familyName(), entryLastname);
|
||
|
appendAddr(i18n("First name"), pcAddr.givenName(), entryFirstname);
|
||
|
appendAddr(i18n("Organization"), pcAddr.organization(), entryCompany);
|
||
|
appendAddr(i18n("Title"), pcAddr.prefix(), entryTitle);
|
||
|
appendAddr(i18n("Note"), pcAddr.note(), entryNote);
|
||
|
|
||
|
appendAddr(i18n("Custom 1"), KABCSync::getFieldForHHCustom(0, pcAddr, fSyncSettings), entryCustom1);
|
||
|
appendAddr(i18n("Custom 2"), KABCSync::getFieldForHHCustom(1, pcAddr, fSyncSettings), entryCustom2);
|
||
|
appendAddr(i18n("Custom 3"), KABCSync::getFieldForHHCustom(2, pcAddr, fSyncSettings), entryCustom3);
|
||
|
appendAddr(i18n("Custom 4"), KABCSync::getFieldForHHCustom(3, pcAddr, fSyncSettings), entryCustom4);
|
||
|
|
||
|
appendPhone(i18n("Work Phone"), Work, eWork);
|
||
|
appendPhone(i18n("Home Phone"), Home, eHome);
|
||
|
appendPhone(i18n("Mobile Phone"), Cell, eMobile);
|
||
|
appendGenPhone(i18n("Fax"), pcAddr.phoneNumber(faxTypeOnPC()).number(), eFax);
|
||
|
appendPhone(i18n("Pager"), Pager, ePager);
|
||
|
appendGenPhone(i18n("Other"), KABCSync::getFieldForHHOtherPhone(pcAddr,fSyncSettings), eOther);
|
||
|
appendGenPhone(i18n("Email"), pcAddr.preferredEmail(), eEmail);
|
||
|
|
||
|
KABC::Address abAddress = KABCSync::getAddress(pcAddr,fSyncSettings);
|
||
|
appendAddr(i18n("Address"), abAddress.street(), entryAddress);
|
||
|
appendAddr(i18n("City"), abAddress.locality(), entryCity);
|
||
|
appendAddr(i18n("Region"), abAddress.region(), entryState);
|
||
|
appendAddr(i18n("Postal code"), abAddress.postalCode(), entryZip);
|
||
|
appendAddr(i18n("Country"), abAddress.country(), entryCountry);
|
||
|
|
||
|
TQString palmAddrCategoryLabel;
|
||
|
if (palmAddr)
|
||
|
{
|
||
|
palmAddrCategoryLabel = fAddressAppInfo->categoryName(palmAddr->category());
|
||
|
}
|
||
|
TQString backupAddrCategoryLabel;
|
||
|
if (backupAddr)
|
||
|
{
|
||
|
backupAddrCategoryLabel = fAddressAppInfo->categoryName(backupAddr->category());
|
||
|
}
|
||
|
int category = palmAddr ? palmAddr->category() : 0;
|
||
|
tab->append(new ResolutionItem(
|
||
|
i18n("Category"),
|
||
|
tab->fExistItems,
|
||
|
!pcAddr.isEmpty() ?
|
||
|
KABCSync::bestMatchedCategoryName(pcAddr.categories(), *fAddressAppInfo, category) :
|
||
|
TQString::null,
|
||
|
palmAddrCategoryLabel,
|
||
|
backupAddrCategoryLabel));
|
||
|
#undef appendGen
|
||
|
#undef appendAddr
|
||
|
#undef appendGenPhone
|
||
|
#undef appendPhone
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
/// This function just sets the phone number of type "type" to "phone"
|
||
|
static inline void setPhoneNumber(Addressee &abEntry, int type, const TQString &nr)
|
||
|
{
|
||
|
PhoneNumber phone = abEntry.phoneNumber(type);
|
||
|
phone.setNumber(nr);
|
||
|
abEntry.insertPhoneNumber(phone);
|
||
|
}
|
||
|
|
||
|
|
||
|
bool AbbrowserConduit::_applyResolutionTable(ResolutionTable*tab, Addressee &pcAddr,
|
||
|
PilotAddress *backupAddr, PilotAddress *palmAddr)
|
||
|
{
|
||
|
FUNCTIONSETUP;
|
||
|
if (!tab) return false;
|
||
|
if (!palmAddr) {
|
||
|
WARNINGKPILOT << "Empty palmAddr after conflict resolution." << endl;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
ResolutionItem*item=tab->first();
|
||
|
#define SETGENFIELD(abfield, palmfield) \
|
||
|
if (item) {\
|
||
|
abfield; \
|
||
|
palmAddr->setField(palmfield, item->fResolved); \
|
||
|
}\
|
||
|
item=tab->next();
|
||
|
#define SETFIELD(abfield, palmfield) \
|
||
|
SETGENFIELD(pcAddr.set##abfield(item->fResolved), palmfield)
|
||
|
#define SETCUSTOMFIELD(abfield, palmfield) \
|
||
|
SETGENFIELD(KABCSync::setFieldFromHHCustom(abfield, pcAddr, item->fResolved, fSyncSettings), palmfield)
|
||
|
#define SETGENPHONE(abfield, palmfield) \
|
||
|
if (item) { \
|
||
|
abfield; \
|
||
|
palmAddr->setPhoneField(PilotAddressInfo::palmfield, item->fResolved, PilotAddress::Replace); \
|
||
|
}\
|
||
|
item=tab->next();
|
||
|
#define SETPHONEFIELD(abfield, palmfield) \
|
||
|
SETGENPHONE(setPhoneNumber(pcAddr, PhoneNumber::abfield, item->fResolved), palmfield)
|
||
|
#define SETADDRESSFIELD(abfield, palmfield) \
|
||
|
SETGENFIELD(abAddress.abfield(item->fResolved), palmfield)
|
||
|
|
||
|
SETFIELD(FamilyName, entryLastname);
|
||
|
SETFIELD(GivenName, entryFirstname);
|
||
|
SETFIELD(Organization, entryCompany);
|
||
|
SETFIELD(Prefix, entryTitle);
|
||
|
SETFIELD(Note, entryNote);
|
||
|
|
||
|
SETCUSTOMFIELD(0, entryCustom1);
|
||
|
SETCUSTOMFIELD(1, entryCustom2);
|
||
|
SETCUSTOMFIELD(2, entryCustom3);
|
||
|
SETCUSTOMFIELD(3, entryCustom4);
|
||
|
|
||
|
SETPHONEFIELD(Work, eWork);
|
||
|
SETPHONEFIELD(Home, eHome);
|
||
|
SETPHONEFIELD(Cell, eMobile);
|
||
|
SETGENPHONE(setPhoneNumber(pcAddr, faxTypeOnPC(), item->fResolved), eFax);
|
||
|
SETPHONEFIELD(Pager, ePager);
|
||
|
SETGENPHONE(KABCSync::setFieldFromHHOtherPhone(pcAddr, item->fResolved, fSyncSettings), eOther);
|
||
|
|
||
|
// TODO: fix email
|
||
|
if (item)
|
||
|
{
|
||
|
palmAddr->setPhoneField(PilotAddressInfo::eEmail, item->fResolved, PilotAddress::Replace);
|
||
|
if (backupAddr)
|
||
|
{
|
||
|
pcAddr.removeEmail(backupAddr->getPhoneField(PilotAddressInfo::eEmail));
|
||
|
}
|
||
|
pcAddr.removeEmail(palmAddr->getPhoneField(PilotAddressInfo::eEmail));
|
||
|
pcAddr.insertEmail(item->fResolved, true);
|
||
|
}
|
||
|
item=tab->next();
|
||
|
|
||
|
KABC::Address abAddress = KABCSync::getAddress(pcAddr, fSyncSettings);
|
||
|
SETADDRESSFIELD(setStreet, entryAddress);
|
||
|
SETADDRESSFIELD(setLocality, entryCity);
|
||
|
SETADDRESSFIELD(setRegion, entryState);
|
||
|
SETADDRESSFIELD(setPostalCode, entryZip);
|
||
|
SETADDRESSFIELD(setCountry, entryCountry);
|
||
|
pcAddr.insertAddress(abAddress);
|
||
|
|
||
|
// TODO: Is this correct?
|
||
|
if (item)
|
||
|
{
|
||
|
palmAddr->setCategory( fAddressAppInfo->findCategory(item->fResolved) );
|
||
|
KABCSync::setCategory(pcAddr, item->fResolved);
|
||
|
}
|
||
|
|
||
|
|
||
|
#undef SETGENFIELD
|
||
|
#undef SETFIELD
|
||
|
#undef SETCUSTOMFIELD
|
||
|
#undef SETGENPHONE
|
||
|
#undef SETPHONEFIELD
|
||
|
#undef SETADDRESSFIELD
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
bool AbbrowserConduit::_smartMergeTable(ResolutionTable*tab)
|
||
|
{
|
||
|
FUNCTIONSETUP;
|
||
|
if (!tab) return false;
|
||
|
bool noconflict=true;
|
||
|
ResolutionItem*item;
|
||
|
for ( item = tab->first(); item; item = tab->next() )
|
||
|
{
|
||
|
// try to merge the three strings
|
||
|
item->fResolved=_smartMergeString(item->fEntries[0],
|
||
|
item->fEntries[2], item->fEntries[1], getConflictResolution());
|
||
|
// if a conflict occurred, set the default to something sensitive:
|
||
|
if (item->fResolved.isNull() && !(item->fEntries[0].isEmpty() &&
|
||
|
item->fEntries[1].isEmpty() && item->fEntries[2].isEmpty() ) )
|
||
|
{
|
||
|
item->fResolved=item->fEntries[0];
|
||
|
noconflict=false;
|
||
|
}
|
||
|
if (item->fResolved.isNull()) item->fResolved=item->fEntries[1];
|
||
|
if (item->fResolved.isNull()) item->fResolved=item->fEntries[2];
|
||
|
}
|
||
|
return noconflict;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/** Merge the palm and the pc entries with the additional information of
|
||
|
* the backup.
|
||
|
* return value: no meaning yet
|
||
|
*/
|
||
|
bool AbbrowserConduit::_smartMergeAddressee(Addressee &pcAddr,
|
||
|
PilotAddress *backupAddr, PilotAddress *palmAddr)
|
||
|
{
|
||
|
FUNCTIONSETUP;
|
||
|
|
||
|
// Merge them, then look which records have to be written to device or abook
|
||
|
int res = SyncAction::eAskUser;
|
||
|
bool result=true;
|
||
|
ResolutionTable tab;
|
||
|
|
||
|
result &= _buildResolutionTable(&tab, pcAddr, backupAddr, palmAddr);
|
||
|
// Now attempt a smart merge. If that fails, let conflict resolution do the job
|
||
|
bool mergeOk=_smartMergeTable(&tab);
|
||
|
|
||
|
if (!mergeOk)
|
||
|
{
|
||
|
TQString dlgText;
|
||
|
if (!palmAddr)
|
||
|
{
|
||
|
dlgText=i18n("The following address entry was changed, but does no longer exist on the handheld. Please resolve this conflict:");
|
||
|
}
|
||
|
else if (pcAddr.isEmpty())
|
||
|
{
|
||
|
dlgText=i18n("The following address entry was changed, but does no longer exist on the PC. Please resolve this conflict:");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
dlgText=i18n("The following address entry was changed on the handheld as well as on the PC side. The changes could not be merged automatically, so please resolve the conflict yourself:");
|
||
|
}
|
||
|
ResolutionDlg*resdlg=new ResolutionDlg(0L, fHandle, i18n("Address conflict"), dlgText, &tab);
|
||
|
resdlg->exec();
|
||
|
KPILOT_DELETE(resdlg);
|
||
|
}
|
||
|
res=tab.fResolution;
|
||
|
|
||
|
// Disallow some resolution under certain conditions, fix wrong values:
|
||
|
switch (res) {
|
||
|
case SyncAction::eHHOverrides:
|
||
|
if (!palmAddr) res=SyncAction::eDelete;
|
||
|
break;
|
||
|
case SyncAction::ePCOverrides:
|
||
|
if (pcAddr.isEmpty()) res=SyncAction::eDelete;
|
||
|
break;
|
||
|
case SyncAction::ePreviousSyncOverrides:
|
||
|
if (!backupAddr) res=SyncAction::eDoNothing;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
PilotAddress*pAddr=palmAddr;
|
||
|
bool pAddrCreated=false;
|
||
|
// Now that we have done a possible conflict resolution, apply the changes
|
||
|
switch (res) {
|
||
|
case SyncAction::eDuplicate:
|
||
|
// Set the Palm ID to 0 so we don't overwrite the existing record.
|
||
|
pcAddr.removeCustom(KABCSync::appString, KABCSync::idString);
|
||
|
result &= _copyToHH(pcAddr, 0L, 0L);
|
||
|
{
|
||
|
Addressee pcadr;
|
||
|
result &= _copyToPC(pcadr, backupAddr, palmAddr);
|
||
|
}
|
||
|
break;
|
||
|
case SyncAction::eDoNothing:
|
||
|
break;
|
||
|
case SyncAction::eHHOverrides:
|
||
|
result &= _copyToPC(pcAddr, backupAddr, palmAddr);
|
||
|
break;
|
||
|
case SyncAction::ePCOverrides:
|
||
|
result &= _copyToHH(pcAddr, backupAddr, pAddr);
|
||
|
break;
|
||
|
case SyncAction::ePreviousSyncOverrides:
|
||
|
KABCSync::copy(pcAddr, *backupAddr, *fAddressAppInfo, fSyncSettings);
|
||
|
if (palmAddr && backupAddr) *palmAddr=*backupAddr;
|
||
|
result &= _savePalmAddr(backupAddr, pcAddr);
|
||
|
result &= _savePCAddr(pcAddr, backupAddr, backupAddr);
|
||
|
break;
|
||
|
case SyncAction::eDelete:
|
||
|
result &= _deleteAddressee(pcAddr, backupAddr, palmAddr);
|
||
|
break;
|
||
|
case SyncAction::eAskUser:
|
||
|
default:
|
||
|
if (!pAddr)
|
||
|
{
|
||
|
pAddr=new PilotAddress();
|
||
|
pAddrCreated=true;
|
||
|
}
|
||
|
result &= _applyResolutionTable(&tab, pcAddr, backupAddr, pAddr);
|
||
|
showAddresses(pcAddr, backupAddr, pAddr);
|
||
|
// savePalmAddr sets the RecordID custom field already
|
||
|
result &= _savePalmAddr(pAddr, pcAddr);
|
||
|
result &= _savePCAddr(pcAddr, backupAddr, pAddr);
|
||
|
if (pAddrCreated) KPILOT_DELETE(pAddr);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// TODO: right now entries are equal if both first/last name and organization are
|
||
|
// equal. This rules out two entries for the same person(e.g. real home and weekend home)
|
||
|
// or two persons with the same name where you don't know the organization.!!!
|
||
|
Addressee AbbrowserConduit::_findMatch(const PilotAddress & pilotAddress) const
|
||
|
{
|
||
|
FUNCTIONSETUP;
|
||
|
// TODO: also search with the pilotID
|
||
|
// first, use the pilotID to UID map to find the appropriate record
|
||
|
if( !isFirstSync() && (pilotAddress.id() > 0) )
|
||
|
{
|
||
|
TQString id(addresseeMap[pilotAddress.id()]);
|
||
|
DEBUGKPILOT << fname << ": PilotRecord has id " << pilotAddress.id() << ", mapped to " << id << endl;
|
||
|
if(!id.isEmpty())
|
||
|
{
|
||
|
Addressee res(aBook->tqfindByUid(id));
|
||
|
if(!res.isEmpty()) return res;
|
||
|
DEBUGKPILOT << fname << ": PilotRecord has id " << pilotAddress.id() << ", but could not be found in the addressbook" << endl;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for(AddressBook::Iterator iter = aBook->begin(); iter != aBook->end(); ++iter)
|
||
|
{
|
||
|
Addressee abEntry = *iter;
|
||
|
TQString recID(abEntry.custom(KABCSync::appString, KABCSync::idString));
|
||
|
bool ok;
|
||
|
if (!recID.isEmpty() )
|
||
|
{
|
||
|
recordid_t rid = recID.toLong(&ok);
|
||
|
if (ok && rid)
|
||
|
{
|
||
|
if (rid==pilotAddress.id()) return abEntry;// yes, we found it
|
||
|
// skip this addressee, as it can an other corresponding address on the handheld
|
||
|
if (allIds.tqcontains(rid)) continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (_equal(&pilotAddress, abEntry, eqFlagsAlmostAll))
|
||
|
{
|
||
|
return abEntry;
|
||
|
}
|
||
|
}
|
||
|
DEBUGKPILOT << fname << ": Could not find any addressbook enty matching " << pilotAddress.getField(entryLastname) << endl;
|
||
|
return Addressee();
|
||
|
}
|
||
|
|
||
|
void AbbrowserConduit::slotTestRecord()
|
||
|
{
|
||
|
FUNCTIONSETUP;
|
||
|
|
||
|
// Get a record and interpret it as an address.
|
||
|
PilotRecord *r = fDatabase->readRecordByIndex( pilotindex );
|
||
|
if (!r)
|
||
|
{
|
||
|
delayDone();
|
||
|
return;
|
||
|
}
|
||
|
PilotAddress a(r);
|
||
|
KPILOT_DELETE(r);
|
||
|
|
||
|
// Process this record.
|
||
|
showPilotAddress(&a);
|
||
|
|
||
|
// Schedule more work.
|
||
|
++pilotindex;
|
||
|
TQTimer::singleShot(0, this, TQT_SLOT(slotTestRecord()));
|
||
|
}
|