kpilot Library API Documentation

abbrowser-conduit.cc

00001 /* abbrowser-conduit.cc                           KPilot
00002 **
00003 ** Copyright (C) 2000,2001 by Dan Pilone
00004 ** Copyright (C) 2002-2003 by Reinhold Kainhofer
00005 **
00006 ** The abbrowser conduit copies addresses from the Pilot's address book to
00007 ** the KDE addressbook maintained via the kabc library.
00008 */
00009 
00010 /*
00011 ** This program is free software; you can redistribute it and/or modify
00012 ** it under the terms of the GNU General Public License as published by
00013 ** the Free Software Foundation; either version 2 of the License, or
00014 ** (at your option) any later version.
00015 **
00016 ** This program is distributed in the hope that it will be useful,
00017 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
00018 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00019 ** GNU General Public License for more details.
00020 **
00021 ** You should have received a copy of the GNU General Public License
00022 ** along with this program in a file called COPYING; if not, write to
00023 ** the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
00024 ** MA 02111-1307, USA.
00025 */
00026 
00027 /*
00028 ** Bug reports and questions can be sent to kde-pim@kde.org.
00029 */
00030 
00031 
00032 
00033 #include "options.h"
00034 #include "abbrowser-conduit.moc"
00035 
00036 #include <unistd.h>
00037 
00038 #include <qtimer.h>
00039 #include <qvbuttongroup.h>
00040 #include <qcheckbox.h>
00041 #include <qtextcodec.h>
00042 #include <time.h>
00043 
00044 
00045 #include <kglobal.h>
00046 #include <kdebug.h>
00047 #include <kconfig.h>
00048 #include <kabc/addressbook.h>
00049 #include <kabc/stdaddressbook.h>
00050 #include <kabc/resourcefile.h>
00051 
00052 #include <pilotUser.h>
00053 #include <pilotSerialDatabase.h>
00054 
00055 #include "abbrowser-factory.h"
00056 #include "resolutionDialog.h"
00057 #include "resolutionTable.h"
00058 
00059 // Something to allow us to check what revision
00060 // the modules are that make up a binary distribution.
00061 //
00062 //
00063 const char *abbrowser_conduit_id="$Id: abbrowser-conduit.cc,v 1.88 2003/10/24 13:50:43 kainhofe Exp $";
00064 
00065 using namespace KABC;
00066 
00067 const QString AbbrowserConduit::appString=CSL1("KPILOT");
00068 const QString AbbrowserConduit::flagString=CSL1("Flag");
00069 const QString AbbrowserConduit::idString=CSL1("RecordID");
00070 
00071 bool AbbrowserConduit::fPilotStreetHome=true;
00072 bool AbbrowserConduit::fPilotFaxHome=true;
00073 bool AbbrowserConduit::fArchive=true;
00074 enum AbbrowserConduit::ePilotOtherEnum AbbrowserConduit::ePilotOther=AbbrowserConduit::eOtherPhone;
00075 AddressBook*AbbrowserConduit::aBook=0L;
00076 
00077 enum AbbrowserConduit::eCustomEnum AbbrowserConduit::eCustom[4] = {
00078     AbbrowserConduit::eCustomField,
00079     AbbrowserConduit::eCustomField,
00080     AbbrowserConduit::eCustomField,
00081     AbbrowserConduit::eCustomField
00082     } ;
00083 QString AbbrowserConduit::fCustomFmt=QString::null;
00084 
00088 #define _setPhoneNumber(abEntry, type, nr) \
00089     { PhoneNumber phone = abEntry.phoneNumber(type); \
00090     phone.setNumber(nr); \
00091     abEntry.insertPhoneNumber(phone); }
00092 
00093 
00094 /*********************************************************************
00095                         C O N S T R U C T O R
00096  *********************************************************************/
00097 
00098 
00099 
00100 
00101 
00102 AbbrowserConduit::AbbrowserConduit(KPilotDeviceLink * o, const char *n, const QStringList & a):
00103         ConduitAction(o, n, a),
00104         addresseeMap(),
00105         syncedIds(),
00106         abiter(),
00107         ticket(0L)
00108 {
00109     FUNCTIONSETUP;
00110 #ifdef DEBUG
00111     DEBUGCONDUIT<<abbrowser_conduit_id<<endl;
00112 #endif
00113     fConduitName=i18n("Addressbook");
00114 }
00115 
00116 
00117 
00118 AbbrowserConduit::~AbbrowserConduit()
00119 {
00120 }
00121 
00122 
00123 
00124 /*********************************************************************
00125                 L O A D I N G   T H E   D A T A
00126  *********************************************************************/
00127 
00128 
00129 
00130 /* Builds the map which links record ids to uid's of Addressee
00131 */
00132 void AbbrowserConduit::_mapContactsToPilot(QMap < recordid_t, QString > &idContactMap) const
00133 {
00134     FUNCTIONSETUP;
00135 
00136     idContactMap.clear();
00137 
00138     for(AddressBook::Iterator contactIter = aBook->begin();
00139         contactIter != aBook->end(); ++contactIter)
00140     {
00141         Addressee aContact = *contactIter;
00142         QString recid = aContact.custom(appString, idString);
00143         if(!recid.isEmpty())
00144         {
00145             recordid_t id = recid.toULong();
00146             idContactMap.insert(id, aContact.uid());
00147         }
00148     }
00149 #ifdef DEBUG
00150     DEBUGCONDUIT << fname << ": Loaded " << idContactMap.size() <<
00151         " addresses from the addressbook. " << endl;
00152 #endif
00153 }
00154 
00155 
00156 
00157 bool AbbrowserConduit::_prepare()
00158 {
00159     FUNCTIONSETUP;
00160 
00161     readConfig();
00162     syncedIds.clear();
00163 
00164     return true;
00165 }
00166 
00167 
00168 
00169 void AbbrowserConduit::readConfig()
00170 {
00171     FUNCTIONSETUP;
00172 
00173     KConfigGroupSaver g(fConfig, AbbrowserConduitFactory::group());
00174 
00175     // General page
00176     fAbookType = (eAbookTypeEnum)fConfig->readNumEntry(
00177         AbbrowserConduitFactory::fAbookType, 0);
00178     fAbookFile = fConfig->readEntry(
00179         AbbrowserConduitFactory::fAbookFile);
00180     fArchive=fConfig->readBoolEntry(
00181         AbbrowserConduitFactory::fArchive, true);
00182 
00183     // Conflict page
00184     SyncAction::eConflictResolution res=(SyncAction::eConflictResolution)fConfig->readNumEntry(
00185         AbbrowserConduitFactory::fResolution, SyncAction::eUseGlobalSetting);
00186     if (res!=SyncAction::eUseGlobalSetting) fConflictResolution=res;
00187 
00188     // Fields page
00189     fPilotStreetHome=!fConfig->readBoolEntry(
00190         AbbrowserConduitFactory::fStreetType, true);
00191     fPilotFaxHome=!fConfig->readBoolEntry(
00192         AbbrowserConduitFactory::fFaxType, true);
00193     ePilotOther=(ePilotOtherEnum)(fConfig->readNumEntry(
00194         AbbrowserConduitFactory::fOtherField, eOtherPhone));
00195 
00196     // Custom fields page
00197     for (int i=0; i<4; i++)
00198     {
00199         eCustom[i]=(eCustomEnum)(fConfig->readNumEntry(
00200             AbbrowserConduitFactory::custom(i), eCustomField) );
00201     }
00202     fCustomFmt=fConfig->readEntry(AbbrowserConduitFactory::fCustomFmt, QString::null);
00203 
00204 #ifdef DEBUG
00205     DEBUGCONDUIT << fname
00206         << ": Settings "
00207         << " fConflictResolution=" << fConflictResolution
00208         << " fPilotStreetHome=" << fPilotStreetHome
00209         << " fPilotFaxHome=" << fPilotFaxHome
00210         << " fArchive=" << fArchive
00211         << " eCustom[0]=" << eCustom[0]
00212         << " eCustom[1]=" << eCustom[1]
00213         << " eCustom[2]=" << eCustom[2]
00214         << " eCustom[3]=" << eCustom[3]
00215         << " fFirstTime=" << isFirstSync()
00216         << endl;
00217 #endif
00218 }
00219 
00220 
00221 
00222 bool AbbrowserConduit::isDeleted(const PilotAddress*addr)
00223 {
00224     if (!addr) return true;
00225     if (addr->isDeleted() && !addr->isArchived()) return true;
00226     if (addr->isArchived()) return !fArchive;
00227     return false;
00228 }
00229 bool AbbrowserConduit::isArchived(const PilotAddress*addr)
00230 {
00231     if (addr && addr->isArchived()) return fArchive;
00232     else return false;
00233 }
00234 bool AbbrowserConduit::isArchived(const Addressee &addr)
00235 {
00236     return addr.custom(appString, flagString) == QString::number(SYNCDEL);
00237 }
00238 bool AbbrowserConduit::makeArchived(Addressee &addr)
00239 {
00240     FUNCTIONSETUP;
00241     addr.insertCustom(appString, flagString, QString::number(SYNCDEL));
00242     addr.removeCustom(appString, idString);
00243     return true;
00244 }
00245 
00246 
00247 
00248 bool AbbrowserConduit::_loadAddressBook()
00249 {
00250     FUNCTIONSETUP;
00251     KConfigGroupSaver g(fConfig, AbbrowserConduitFactory::group());
00252     switch (fAbookType)
00253     {
00254         case eAbookResource:
00255             DEBUGCONDUIT<<"Loading standard addressbook"<<endl;
00256             aBook = StdAddressBook::self();
00257             break;
00258         case eAbookLocal: { // initialize the abook with the given file
00259             DEBUGCONDUIT<<"Loading custom addressbook"<<endl;
00260             aBook = new AddressBook();
00261             if (!aBook) return false;
00262             KABC::Resource *res = new ResourceFile( fAbookFile, "vcard" );
00263             if ( !aBook->addResource( res ) ) {
00264                 DEBUGCONDUIT << "Unable to open resource for file " << fAbookFile << endl;
00265                 KPILOT_DELETE( aBook );
00266                 return false;
00267             }
00268             break;}
00269         default: break;
00270     }
00271     // find out if this can fail for reasons other than a non-existent
00272     // vcf file. If so, how can I determine if the missing file was the problem
00273     // or something more serious:
00274     if ( !aBook || !aBook->load() )
00275     {
00276         // Something went wrong, so tell the user and return false to exit the conduit
00277         emit logError(i18n("Unable to initialize and load the addressbook for the sync.") );
00278         kdWarning()<<k_funcinfo<<": Unable to initialize the addressbook for the sync."<<endl;
00279         KPILOT_DELETE(aBook);
00280         return false;
00281     }
00282     abChanged = false;
00283     ticket=aBook->requestSaveTicket();
00284     if (!ticket)
00285     {
00286         kdWarning()<<k_funcinfo<<": Unable to lock addressbook for writing "<<endl;
00287         KPILOT_DELETE(aBook);
00288         return false;
00289     }
00290     // get the addresseMap which maps Pilot unique record(address) id's to
00291     // a Abbrowser Addressee; allows for easy lookup and comparisons
00292     if(aBook->begin() == aBook->end())
00293     {
00294         fFirstSync = true;
00295     }
00296     else
00297     {
00298         _mapContactsToPilot(addresseeMap);
00299     }
00300     return(aBook != 0L);
00301 }
00302 bool AbbrowserConduit::_saveAddressBook()
00303 {
00304     FUNCTIONSETUP;
00305 #ifdef DEBUG
00306             DEBUGCONDUIT<<"Addressbook not changed, freeing ticket"<<endl;
00307 #endif
00308 
00309     bool res=false;
00310 
00311     if (ticket)
00312     {
00313         if (abChanged) 
00314         {
00315             res=aBook->save(ticket);
00316         }
00317         else
00318         {
00319 #ifdef DEBUG
00320             DEBUGCONDUIT<<"Addressbook not changed, no need to save it"<<endl;
00321 #endif
00322         }
00323         // XXX: KDE4: release ticket in all cases (save no longer releases it)
00324         if ( !res ) // didn't save, delete ticket manually
00325         {
00326             aBook->releaseSaveTicket(ticket);
00327         }
00328         ticket=0;
00329     }
00330     else
00331     {
00332         kdWarning()<<k_funcinfo<<": No ticket available to save the "
00333         <<"addressbook."<<endl;
00334     }
00335     if (fAbookType!=eAbookResource)
00336     {
00337 #ifdef DEBUG
00338         DEBUGCONDUIT<<"Deleting addressbook"<<endl;
00339 #endif
00340         KPILOT_DELETE(aBook);
00341     }
00342 
00343     return res;
00344 }
00345 
00346 
00347 
00348 void AbbrowserConduit::_getAppInfo()
00349 {
00350     FUNCTIONSETUP;
00351     // get the address application header information
00352     unsigned char *buffer = new unsigned char[PilotAddress::APP_BUFFER_SIZE];
00353     int appLen=fDatabase->readAppBlock(buffer, PilotAddress::APP_BUFFER_SIZE);
00354 
00355     unpack_AddressAppInfo(&fAddressAppInfo, buffer, appLen);
00356     delete[]buffer;
00357     buffer = NULL;
00358 
00359 #ifdef DEBUG
00360     DEBUGCONDUIT << fname << " lastUniqueId" << fAddressAppInfo.category.lastUniqueID << endl;
00361     for(int i = 0; i < 16; i++)
00362     {
00363         DEBUGCONDUIT << fname << " cat " << i << " =" << fAddressAppInfo.category.name[i] << endl;
00364     }
00365 
00366     for(int x = 0; x < 8; x++)
00367     {
00368         DEBUGCONDUIT << fname << " phone[" << x << "] = " << fAddressAppInfo.phoneLabels[x] << endl;
00369     }
00370 #endif
00371 }
00372 void AbbrowserConduit::_setAppInfo()
00373 {
00374     FUNCTIONSETUP;
00375     // get the address application header information
00376     int appLen = pack_AddressAppInfo(&fAddressAppInfo, 0, 0);
00377     unsigned char *buffer = new unsigned char[appLen];
00378     pack_AddressAppInfo(&fAddressAppInfo, buffer, appLen);
00379     if (fDatabase) fDatabase->writeAppBlock(buffer, appLen);
00380     if (fLocalDatabase) fLocalDatabase->writeAppBlock(buffer, appLen);
00381     delete[] buffer;
00382 }
00383 
00384 
00385 
00386 QString AbbrowserConduit::getCustomField(const Addressee &abEntry, const int index)
00387 {
00388     FUNCTIONSETUP;
00389 
00390     switch (eCustom[index]) {
00391         case eCustomBirthdate: {
00392             QDateTime bdate(abEntry.birthday().date());
00393             if (!bdate.isValid()) return abEntry.custom(appString, CSL1("CUSTOM")+QString::number(index));
00394             QString tmpfmt(KGlobal::locale()->dateFormat());
00395             if (!fCustomFmt.isEmpty()) KGlobal::locale()->setDateFormat(fCustomFmt);
00396 #ifdef DEBUG
00397             DEBUGCONDUIT<<"Birthdate: "<<KGlobal::locale()->formatDate(bdate.date())<<" (QDate: "<<bdate.toString()<<endl;
00398 #endif
00399             QString ret(KGlobal::locale()->formatDate(bdate.date()));
00400             KGlobal::locale()->setDateFormat(tmpfmt);
00401             return ret;
00402         }
00403         case eCustomURL:
00404             return abEntry.url().url();
00405             break;
00406         case eCustomIM:
00407             return abEntry.custom(CSL1("KADDRESSBOOK"), CSL1("X-IMAddress"));
00408             break;
00409         case eCustomField:
00410         default:
00411             return abEntry.custom(appString, CSL1("CUSTOM")+QString::number(index));
00412             break;
00413     }
00414 }
00415 void AbbrowserConduit::setCustomField(Addressee &abEntry,  int index, QString cust)
00416 {
00417     FUNCTIONSETUP;
00418 
00419     switch (eCustom[index]) {
00420         case eCustomBirthdate: {
00421             QDate bdate;
00422             bool ok=false;
00423             if (!fCustomFmt.isEmpty())
00424             {
00425                 // empty format means use locale setting
00426                 bdate=KGlobal::locale()->readDate(cust, &ok);
00427             }
00428             else
00429             {
00430                 // use given format
00431                 bdate=KGlobal::locale()->readDate(cust, fCustomFmt, &ok);
00432             }
00433 #ifdef DEBUG
00434             DEBUGCONDUIT<<"Birthdate from "<<index<<"-th custom field: "<<bdate.toString()<<endl;
00435             DEBUGCONDUIT<<"Is Valid: "<<bdate.isValid()<<endl;
00436 #endif
00437             if (bdate.isValid())
00438                 return abEntry.setBirthday(bdate);
00439             else
00440                 return abEntry.insertCustom(CSL1("KADDRESSBOOK"), CSL1("X-IMAddress"), cust);
00441             break; }
00442         case eCustomURL: {
00443             return abEntry.setUrl(cust);
00444             break;}
00445         case eCustomIM: {
00446             return abEntry.insertCustom(CSL1("KADDRESSBOOK"), CSL1("X-IMAddress"), cust);
00447             break;}
00448         case eCustomField:
00449         default: {
00450             return abEntry.insertCustom(appString, CSL1("CUSTOM")+QString::number(index), cust);
00451             break;}
00452     }
00453     return;
00454 }
00455 
00456 
00457 
00458 QString AbbrowserConduit::getOtherField(const Addressee & abEntry)
00459 {
00460     switch(ePilotOther)
00461     {
00462         case eOtherPhone:
00463             return abEntry.phoneNumber(0).number();
00464         case eAssistant:
00465             return abEntry.custom(CSL1("KADDRESSBOOK"), CSL1("AssistantsName"));
00466         case eBusinessFax:
00467             return abEntry.phoneNumber(PhoneNumber::Fax | PhoneNumber::Work).number();
00468         case eCarPhone:
00469             return abEntry.phoneNumber(PhoneNumber::Car).number();
00470         case eEmail2:
00471             return abEntry.emails().first();
00472         case eHomeFax:
00473             return abEntry.phoneNumber(PhoneNumber::Fax | PhoneNumber::Home).number();
00474         case eTelex:
00475             return abEntry.phoneNumber(PhoneNumber::Bbs).number();
00476         case eTTYTTDPhone:
00477             return abEntry.phoneNumber(PhoneNumber::Pcs).number();
00478         default:
00479             return QString::null;
00480     }
00481 }
00482 void AbbrowserConduit::setOtherField(Addressee & abEntry, QString nr)
00483 {
00484 //  PhoneNumber phone;
00485     switch(ePilotOther)
00486     {
00487         case eOtherPhone:
00488             _setPhoneNumber(abEntry, 0, nr)
00489             break;
00490         case eAssistant:
00491             abEntry.insertCustom(CSL1("KADDRESSBOOK"), CSL1("AssistantsName"), nr);
00492             break;
00493         case eBusinessFax:
00494             _setPhoneNumber(abEntry, PhoneNumber::Fax | PhoneNumber::Work, nr)
00495             break;
00496         case eCarPhone:
00497             _setPhoneNumber(abEntry, PhoneNumber::Car, nr)
00498             break;
00499         case eEmail2:
00500             return abEntry.insertEmail(nr);
00501         case eHomeFax:
00502             _setPhoneNumber(abEntry, PhoneNumber::Fax|PhoneNumber::Home, nr)
00503             break;
00504         case eTelex:
00505             _setPhoneNumber(abEntry, PhoneNumber::Bbs, nr)
00506             break;
00507         case eTTYTTDPhone:
00508             _setPhoneNumber(abEntry, PhoneNumber::Pcs, nr)
00509             break;
00510     }
00511 }
00512 
00513 
00514 
00515 PhoneNumber AbbrowserConduit::getFax(const Addressee & abEntry)
00516 {
00517     return abEntry.phoneNumber(PhoneNumber::Fax |
00518         ( (fPilotFaxHome) ?(PhoneNumber::Home) :(PhoneNumber::Work)));
00519 }
00520 void AbbrowserConduit::setFax(Addressee & abEntry, QString fax)
00521 {
00522     _setPhoneNumber(abEntry, PhoneNumber::Fax | (fPilotFaxHome ? PhoneNumber::Home : PhoneNumber::Work ), fax);
00523 }
00524 
00525 
00530 KABC::Address AbbrowserConduit::getAddress(const Addressee & abEntry)
00531 {
00532     int type=(fPilotStreetHome)?(KABC::Address::Home):(KABC::Address::Work);
00533     KABC::Address ad(abEntry.address(KABC::Address::Pref));
00534     if (!ad.isEmpty()) return ad;
00535     ad=abEntry.address(type);
00536     if (!ad.isEmpty()) return ad;
00537     ad=abEntry.address((fPilotStreetHome) ?(KABC::Address::Work):(KABC::Address::Home));
00538     if (!ad.isEmpty()) return ad;
00539 
00540     return abEntry.address(type | KABC::Address::Pref);
00541 }
00542 
00543 
00544 
00552 QString AbbrowserConduit::_getCatForHH(const QStringList cats, const QString curr) const
00553 {
00554     FUNCTIONSETUP;
00555     int j;
00556     if (cats.size()<1) return QString::null;
00557     if (cats.contains(curr)) return curr;
00558     for(QStringList::ConstIterator it = cats.begin(); it != cats.end(); ++it)
00559     {
00560         for(j = 0; j <= 15; j++)
00561         {
00562             QString catName = PilotAppCategory::codec()->
00563                 toUnicode(fAddressAppInfo.category.name[j]);
00564             if(!(*it).isEmpty() && !_compare(*it, catName))
00565             {
00566                 return catName;
00567             }
00568         }
00569     }
00570     // If we have a free label, return the first possible cat
00571     QString lastCat(fAddressAppInfo.category.name[15]);
00572     if (lastCat.isEmpty()) return cats.first();
00573     return QString::null;
00574 }
00575 void AbbrowserConduit::_setCategory(Addressee & abEntry, QString cat)
00576 {
00577     if ( (!cat.isEmpty()))
00578     // &&  (cat!=QString(fAddressAppInfo.category.name[0])) )
00579         abEntry.insertCategory(cat);
00580 }
00581 
00582 
00583 
00584 /*********************************************************************
00585                      D E B U G   O U T P U T
00586  *********************************************************************/
00587 
00588 
00589 
00590 #ifdef DEBUG
00591 void AbbrowserConduit::showAddressee(const Addressee & abAddress)
00592 {
00593     FUNCTIONSETUP;
00594     DEBUGCONDUIT << "\tAbbrowser Contact Entry" << endl;
00595     if (abAddress.isEmpty()) {
00596         DEBUGCONDUIT<< "\t\tEMPTY"<<endl;
00597         return;
00598     }
00599     DEBUGCONDUIT << "\t\tLast name = " << abAddress.familyName() << endl;
00600     DEBUGCONDUIT << "\t\tFirst name = " << abAddress.givenName() << endl;
00601     DEBUGCONDUIT << "\t\tCompany = " << abAddress.organization() << endl;
00602     DEBUGCONDUIT << "\t\tJob Title = " << abAddress.title() << endl;
00603     DEBUGCONDUIT << "\t\tNote = " << abAddress.note() << endl;
00604     DEBUGCONDUIT << "\t\tHome phone = " << abAddress.phoneNumber(PhoneNumber::Home).number() << endl;
00605     DEBUGCONDUIT << "\t\tWork phone = " << abAddress.phoneNumber(PhoneNumber::Work).number() << endl;
00606     DEBUGCONDUIT << "\t\tMobile phone = " << abAddress.phoneNumber(PhoneNumber::Cell).number() << endl;
00607     DEBUGCONDUIT << "\t\tEmail = " << abAddress.preferredEmail() << endl;
00608     DEBUGCONDUIT << "\t\tFax = " << getFax(abAddress).number() << endl;
00609     DEBUGCONDUIT << "\t\tPager = " << abAddress.phoneNumber(PhoneNumber::Pager).number() << endl;
00610     DEBUGCONDUIT << "\t\tCategory = " << abAddress.categories().first() << endl;
00611 }
00612 
00613 
00614 
00615 void AbbrowserConduit::showPilotAddress(PilotAddress *pilotAddress)
00616 {
00617     FUNCTIONSETUP;
00618     DEBUGCONDUIT << "\tPilot Address" << endl;
00619     if (!pilotAddress) {
00620         DEBUGCONDUIT<< "\t\tEMPTY"<<endl;
00621         return;
00622     }
00623     DEBUGCONDUIT << "\t\tLast name = " << pilotAddress->getField(entryLastname) << endl;
00624     DEBUGCONDUIT << "\t\tFirst name = " << pilotAddress->getField(entryFirstname) << endl;
00625     DEBUGCONDUIT << "\t\tCompany = " << pilotAddress->getField(entryCompany) << endl;
00626     DEBUGCONDUIT << "\t\tJob Title = " << pilotAddress->getField(entryTitle) << endl;
00627     DEBUGCONDUIT << "\t\tNote = " << pilotAddress->getField(entryNote) << endl;
00628     DEBUGCONDUIT << "\t\tHome phone = " << pilotAddress->getPhoneField(PilotAddress::eHome, false) << endl;
00629     DEBUGCONDUIT << "\t\tWork phone = " << pilotAddress->getPhoneField(PilotAddress::eWork, false) << endl;
00630     DEBUGCONDUIT << "\t\tMobile phone = " << pilotAddress->getPhoneField(PilotAddress::eMobile, false) << endl;
00631     DEBUGCONDUIT << "\t\tEmail = " << pilotAddress->getPhoneField(PilotAddress::eEmail, false) << endl;
00632     DEBUGCONDUIT << "\t\tFax = " << pilotAddress->getPhoneField(PilotAddress::eFax, false) << endl;
00633     DEBUGCONDUIT << "\t\tPager = " << pilotAddress->getPhoneField(PilotAddress::ePager, false) << endl;
00634     DEBUGCONDUIT << "\t\tOther = " << pilotAddress->getPhoneField(PilotAddress::eOther, false) << endl;
00635     DEBUGCONDUIT << "\t\tCategory = " << pilotAddress->getCategoryLabel() << endl;
00636 }
00637 #endif
00638 
00639 
00640 void AbbrowserConduit::showAdresses(Addressee &pcAddr, PilotAddress *backupAddr,
00641     PilotAddress *palmAddr)
00642 {
00643 #ifdef DEBUG
00644     DEBUGCONDUIT << "abEntry:" << endl;
00645     showAddressee(pcAddr);
00646     DEBUGCONDUIT << "pilotAddress:" << endl;
00647     showPilotAddress(palmAddr);
00648     DEBUGCONDUIT << "backupAddress:" << endl;
00649     showPilotAddress(backupAddr);
00650     DEBUGCONDUIT << "------------------------------------------------" << endl;
00651 #endif
00652 }
00653 
00654 
00655 
00656 /*********************************************************************
00657                 S Y N C   S T R U C T U R E
00658  *********************************************************************/
00659 
00660 
00661 
00662 /* virtual */ bool AbbrowserConduit::exec()
00663 {
00664     FUNCTIONSETUP;
00665     DEBUGCONDUIT<<abbrowser_conduit_id<<endl;
00666 
00667     if(!fConfig)
00668     {
00669         kdWarning() << k_funcinfo << ": No config file was set!" << endl;
00670         emit logError(i18n("Unable to load configuration of the addressbook conduit."));
00671         return false;
00672     }
00673 
00674     _prepare();
00675 
00676     fFirstSync = false;
00677     // Database names probably in latin1.
00678     if(!openDatabases(QString::fromLatin1("AddressDB"), &fFirstSync))
00679     {
00680         emit logError(i18n("Unable to open the addressbook databases on the handheld."));
00681         return false;
00682     }
00683     _getAppInfo();
00684     if(!_loadAddressBook())
00685     {
00686         emit logError(i18n("Unable to open the addressbook."));
00687         return false;
00688     }
00689     fFirstSync = fFirstSync || (aBook->begin() == aBook->end());
00690 
00691     // perform syncing from palm to abbrowser
00692     // iterate through all records in palm pilot
00693     pilotindex = 0;
00694 
00695 #ifdef DEBUG
00696     DEBUGCONDUIT << fname << ": fullsync=" << isFullSync() << ", firstSync=" <<    isFirstSync() << endl;
00697     DEBUGCONDUIT << fname << ": "
00698         << "syncDirection=" << fSyncDirection << ", "
00699         << "archive = " << fArchive << endl;
00700     DEBUGCONDUIT << fname << ": conflictRes="<< fConflictResolution << endl;
00701     DEBUGCONDUIT << fname << ": PilotStreetHome=" << fPilotStreetHome << ", PilotFaxHOme" << fPilotFaxHome << endl;
00702 #endif
00703 
00704     if (!isFirstSync())
00705         allIds=fDatabase->idList();
00706 
00707     /* Note:
00708        if eCopyPCToHH or eCopyHHToPC, first sync everything, then lookup
00709        those entries on the receiving side that are not yet syncced and delete
00710        them. Use slotDeleteUnsyncedPCRecords and slotDeleteUnsyncedHHRecords
00711        for this, and no longer purge the whole addressbook before the sync to
00712        prevent data loss in case of connection loss. */
00713 
00714     QTimer::singleShot(0, this, SLOT(slotPalmRecToPC()));
00715 
00716     return true;
00717 }
00718 
00719 
00720 
00721 void AbbrowserConduit::slotPalmRecToPC()
00722 {
00723     FUNCTIONSETUP;
00724     PilotRecord *palmRec = 0L, *backupRec = 0L;
00725 
00726     if (fSyncDirection==SyncAction::eCopyPCToHH)
00727     {
00728         abiter = aBook->begin();
00729         QTimer::singleShot(0, this, SLOT(slotPCRecToPalm()));
00730         return;
00731     }
00732 
00733     if(isFullSync())
00734         palmRec = fDatabase->readRecordByIndex(pilotindex++);
00735     else
00736         palmRec = dynamic_cast <PilotSerialDatabase * >(fDatabase)->readNextModifiedRec();
00737 
00738     if(!palmRec)
00739     {
00740         abiter = aBook->begin();
00741         QTimer::singleShot(0, this, SLOT(slotPCRecToPalm()));
00742         return;
00743     }
00744 
00745     // already synced, so skip:
00746     if(syncedIds.contains(palmRec->getID()))
00747     {
00748         KPILOT_DELETE(palmRec);
00749         QTimer::singleShot(0, this, SLOT(slotPalmRecToPC()));
00750         return;
00751     }
00752 
00753     backupRec = fLocalDatabase->readRecordById(palmRec->getID());
00754     PilotRecord*compareRec=(backupRec)?(backupRec):(palmRec);
00755     Addressee e = _findMatch(PilotAddress(fAddressAppInfo, compareRec));
00756 
00757     PilotAddress*backupAddr=0L;
00758     if (backupRec) backupAddr=new PilotAddress(fAddressAppInfo, backupRec);
00759     PilotAddress*palmAddr=0L;
00760     if (palmRec) palmAddr=new PilotAddress(fAddressAppInfo, palmRec);
00761 
00762     syncAddressee(e, backupAddr, palmAddr);
00763 
00764     syncedIds.append(palmRec->getID());
00765     KPILOT_DELETE(palmAddr);
00766     KPILOT_DELETE(backupAddr);
00767     KPILOT_DELETE(palmRec);
00768     KPILOT_DELETE(backupRec);
00769 
00770     QTimer::singleShot(0, this, SLOT(slotPalmRecToPC()));
00771 }
00772 
00773 
00774 
00775 void AbbrowserConduit::slotPCRecToPalm()
00776 {
00777     FUNCTIONSETUP;
00778 
00779     if ( (fSyncDirection==SyncAction::eCopyHHToPC) ||
00780         abiter == aBook->end() || (*abiter).isEmpty() )
00781     {
00782         pilotindex = 0;
00783         QTimer::singleShot(0, this, SLOT(slotDeletedRecord()));
00784         return;
00785     }
00786 
00787     PilotRecord *palmRec=0L, *backupRec=0L;
00788     Addressee ad = *abiter;
00789 
00790     abiter++;
00791 
00792     // If marked as archived, don't sync!
00793     if (isArchived(ad))
00794     {
00795 #ifdef DEBUG
00796         DEBUGCONDUIT << fname << ": address with id " << ad.uid() <<
00797             " marked archived, so don't sync." << endl;
00798 #endif
00799         QTimer::singleShot(0, this, SLOT(slotPCRecToPalm()));
00800         return;
00801     }
00802 
00803 
00804     QString recID(ad.custom(appString, idString));
00805     bool ok;
00806     recordid_t rid = recID.toLong(&ok);
00807     if (recID.isEmpty() || !ok || !rid)
00808     {
00809         // it's a new item(no record ID and not inserted by the Palm -> PC sync), so add it
00810         syncAddressee(ad, 0L, 0L);
00811         QTimer::singleShot(0, this, SLOT(slotPCRecToPalm()));
00812         return;
00813     }
00814 
00815     // look into the list of already synced record ids to see if the addressee hasn't already been synced
00816     if (syncedIds.contains(rid))
00817     {
00818 #ifdef DEBUG
00819         DEBUGCONDUIT << ": address with id " << rid << " already synced." << endl;
00820 #endif
00821         QTimer::singleShot(0, this, SLOT(slotPCRecToPalm()));
00822         return;
00823     }
00824 
00825 
00826     backupRec = fLocalDatabase->readRecordById(rid);
00827     // only update if no backup record or the backup record is not equal to the addressee
00828 
00829     PilotAddress*backupAddr=0L;
00830     if (backupRec) backupAddr=new PilotAddress(fAddressAppInfo, backupRec);
00831     if(!backupRec || isFirstSync() || !_equal(backupAddr, ad)  )
00832     {
00833         palmRec = fDatabase->readRecordById(rid);
00834         PilotAddress*palmAddr=0L;
00835         if (palmRec) palmAddr= new PilotAddress(fAddressAppInfo, palmRec);
00836         syncAddressee(ad, backupAddr, palmAddr);
00837         // update the id just in case it changed
00838         if (palmRec) rid=palmRec->getID();
00839         KPILOT_DELETE(palmRec);
00840         KPILOT_DELETE(palmAddr);
00841     }
00842     KPILOT_DELETE(backupAddr);
00843     KPILOT_DELETE(backupRec);
00844     syncedIds.append(rid);
00845     // done with the sync process, go on with the next one:
00846     QTimer::singleShot(0, this, SLOT(slotPCRecToPalm()));
00847 }
00848 
00849 
00850 
00851 void AbbrowserConduit::slotDeletedRecord()
00852 {
00853     FUNCTIONSETUP;
00854 
00855     PilotRecord *backupRec = fLocalDatabase->readRecordByIndex(pilotindex++);
00856     if(!backupRec || isFirstSync() )
00857     {
00858         KPILOT_DELETE(backupRec);
00859         QTimer::singleShot(0, this, SLOT(slotDeleteUnsyncedPCRecords()));
00860         return;
00861     }
00862 
00863     // already synced, so skip this record:
00864     if(syncedIds.contains(backupRec->getID()))
00865     {
00866         KPILOT_DELETE(backupRec);
00867         QTimer::singleShot(0, this, SLOT(slotDeletedRecord()));
00868         return;
00869     }
00870 
00871     QString uid = addresseeMap[backupRec->getID()];
00872     Addressee e = aBook->findByUid(uid);
00873     PilotRecord*palmRec=fDatabase->readRecordById(backupRec->getID());
00874     PilotAddress*backupAddr=0L;
00875     if (backupRec) backupAddr=new PilotAddress(fAddressAppInfo, backupRec);
00876     PilotAddress*palmAddr=0L;
00877     if (palmRec) palmAddr=new PilotAddress(fAddressAppInfo, palmRec);
00878 
00879     syncedIds.append(backupRec->getID());
00880     syncAddressee(e, backupAddr, palmAddr);
00881 
00882     KPILOT_DELETE(palmAddr);
00883     KPILOT_DELETE(backupAddr);
00884     KPILOT_DELETE(palmRec);
00885     KPILOT_DELETE(backupRec);
00886     QTimer::singleShot(0, this, SLOT(slotDeletedRecord()));
00887 }
00888 
00889 
00890 
00891 void AbbrowserConduit::slotDeleteUnsyncedPCRecords()
00892 {
00893     FUNCTIONSETUP;
00894     if (fSyncDirection==SyncAction::eCopyHHToPC)
00895     {
00896         QStringList uids;
00897         RecordIDList::iterator it;
00898         QString uid;
00899         for ( it = syncedIds.begin(); it != syncedIds.end(); ++it)
00900         {
00901             uid=addresseeMap[*it];
00902             if (!uid.isEmpty()) uids.append(uid);
00903         }
00904         // TODO: Does this speed up anything?
00905         // qHeapSort( uids );
00906         AddressBook::Iterator abit;
00907         for (abit = aBook->begin(); abit != aBook->end(); ++abit)
00908         {
00909             if (!uids.contains((*abit).uid()))
00910             {
00911 #ifdef DEBUG
00912                 DEBUGCONDUIT<<"Deleting addressee "<<(*abit).realName()<<" from PC (is not on HH, and syncing with HH->PC direction)"<<endl;
00913 #endif
00914                 abChanged = true;
00915                 // TODO: Can I really remove the current iterator???
00916                 aBook->removeAddressee(*abit);
00917             }
00918         }
00919     }
00920     QTimer::singleShot(0, this, SLOT(slotDeleteUnsyncedHHRecords()));
00921 }
00922 
00923 
00924 
00925 void AbbrowserConduit::slotDeleteUnsyncedHHRecords()
00926 {
00927     FUNCTIONSETUP;
00928     if (fSyncDirection==SyncAction::eCopyPCToHH)
00929     {
00930         RecordIDList ids=fDatabase->idList();
00931         RecordIDList::iterator it;
00932         for ( it = ids.begin(); it != ids.end(); ++it )
00933         {
00934             if (!syncedIds.contains(*it))
00935             {
00936 #ifdef DEBUG
00937                 DEBUGCONDUIT<<"Deleting record with ID "<<*it<<" from handheld (is not on PC, and syncing with PC->HH direction)"<<endl;
00938 #endif
00939                 fDatabase->deleteRecord(*it);
00940                 fLocalDatabase->deleteRecord(*it);
00941             }
00942         }
00943     }
00944     QTimer::singleShot(0, this, SLOT(slotCleanup()));
00945 }
00946 
00947 
00948 void AbbrowserConduit::slotCleanup()
00949 {
00950     FUNCTIONSETUP;
00951 
00952     // Set the appInfoBlock, just in case the category labels changed
00953     _setAppInfo();
00954     if(fDatabase)
00955     {
00956         fDatabase->resetSyncFlags();
00957         fDatabase->cleanup();
00958     }
00959     if(fLocalDatabase)
00960     {
00961         fLocalDatabase->resetSyncFlags();
00962         fLocalDatabase->cleanup();
00963     }
00964     KPILOT_DELETE(fDatabase);
00965     KPILOT_DELETE(fLocalDatabase);
00966     _saveAddressBook();
00967     emit syncDone(this);
00968 }
00969 
00970 
00971 
00972 /*********************************************************************
00973               G E N E R A L   S Y N C   F U N C T I O N
00974          These functions modify the Handheld and the addressbook
00975  *********************************************************************/
00976 
00977 
00978 
00979 bool AbbrowserConduit::syncAddressee(Addressee &pcAddr, PilotAddress*backupAddr,
00980         PilotAddress*palmAddr)
00981 {
00982     FUNCTIONSETUP;
00983 showAdresses(pcAddr, backupAddr, palmAddr);
00984 
00985     if (fSyncDirection==SyncAction::eCopyPCToHH)
00986     {
00987         if (pcAddr.isEmpty())
00988         {
00989 #ifdef DEBUG
00990             DEBUGCONDUIT<<"0a "<<endl;
00991 #endif
00992             return _deleteAddressee(pcAddr, backupAddr, palmAddr);
00993         }
00994         else
00995         {
00996 #ifdef DEBUG
00997             DEBUGCONDUIT<<"0b "<<endl;
00998 #endif
00999             return _copyToHH(pcAddr, backupAddr, palmAddr);
01000         }
01001     }
01002 
01003     if (fSyncDirection==SyncAction::eCopyHHToPC)
01004     {
01005 #ifdef DEBUG
01006             DEBUGCONDUIT<<"0c "<<endl;
01007 #endif
01008         if (!palmAddr)
01009             return _deleteAddressee(pcAddr, backupAddr, palmAddr);
01010         else
01011             return _copyToPC(pcAddr, backupAddr, palmAddr);
01012     }
01013 
01014     if ( !backupAddr || isFirstSync() )
01015     {
01016 #ifdef DEBUG
01017             DEBUGCONDUIT<<"1"<<endl;
01018 #endif
01019         /*
01020         Resolution matrix (0..does not exist, E..exists, D..deleted flag set, A..archived):
01021           HH    PC  | Resolution
01022           ------------------------------------------------------------
01023            0     A  |  -
01024            0     E  |  PC -> HH, reset ID if not set correctly
01025            D     0  |  delete (error, should never occur!!!)
01026            D     E  |  CR (ERROR)
01027            E/A   0  |  HH -> PC
01028            E/A   E/A|  merge/CR
01029          */
01030         if  (!palmAddr && isArchived(pcAddr) )
01031         {
01032             return true;
01033         }
01034         else if (!palmAddr && !pcAddr.isEmpty())
01035         {
01036 #ifdef DEBUG
01037             DEBUGCONDUIT<<"1a"<<endl;
01038 #endif
01039             // PC->HH
01040             bool res=_copyToHH(pcAddr, 0L, 0L);
01041             return res;
01042         }
01043         else if (!palmAddr && pcAddr.isEmpty())
01044         {
01045 #ifdef DEBUG
01046             DEBUGCONDUIT<<"1b"<<endl;
01047 #endif
01048             // everything's empty -> ERROR
01049             return false;
01050         }
01051         else if ( (isDeleted(palmAddr) || isArchived(palmAddr)) && pcAddr.isEmpty())
01052         {
01053 #ifdef DEBUG
01054             DEBUGCONDUIT<<"1c"<<endl;
01055 #endif
01056             if (isArchived(palmAddr))
01057                 return _copyToPC(pcAddr, 0L, palmAddr);
01058             else
01059                 // this happens if you add a record on the handheld and delete it again before you do the next sync
01060                 return _deleteAddressee(pcAddr, 0L, palmAddr);
01061         }
01062         else if ((isDeleted(palmAddr)||isArchived(palmAddr)) && !pcAddr.isEmpty())
01063         {
01064 #ifdef DEBUG
01065             DEBUGCONDUIT<<"1d"<<endl;
01066 #endif
01067             // CR (ERROR)
01068             return _smartMergeAddressee(pcAddr, 0L, palmAddr);
01069         }
01070         else if (pcAddr.isEmpty())
01071         {
01072 #ifdef DEBUG
01073             DEBUGCONDUIT<<"1e"<<endl;
01074 #endif
01075             // HH->PC
01076             return _copyToPC(pcAddr, 0L, palmAddr);
01077         }
01078         else
01079         {
01080 #ifdef DEBUG
01081             DEBUGCONDUIT<<"1f"<<endl;
01082 #endif
01083             // Conflict Resolution
01084             return _smartMergeAddressee(pcAddr, 0L, palmAddr);
01085         }
01086     } // !backupAddr
01087     else
01088     {
01089 #ifdef DEBUG
01090             DEBUGCONDUIT<<"2"<<endl;
01091 #endif
01092         /*
01093         Resolution matrix:
01094           1) if HH.(empty| (deleted &! archived) ) -> { if (PC==B) -> delete, else -> CR }
01095              if HH.archied -> {if (PC==B) -> copyToPC, else -> CR }
01096              if PC.empty -> { if (HH==B) -> delete, else -> CR }
01097              if PC.archived -> {if (HH==B) -> delete on HH, else CR }
01098           2) if PC==HH -> { update B, update ID of PC if needed }
01099           3) if PC==B -> { HH!=PC, thus HH modified, so copy HH->PC }
01100              if HH==B -> { PC!=HH, thus PC modified, so copy PC->HH }
01101           4) else: all three addressees are different -> CR
01102         */
01103 
01104         if (!palmAddr || isDeleted(palmAddr) )
01105         {
01106 #ifdef DEBUG
01107             DEBUGCONDUIT<<"2a"<<endl;
01108 #endif
01109             if (_equal(backupAddr, pcAddr) || pcAddr.isEmpty())
01110             {
01111                 return _deleteAddressee(pcAddr, backupAddr, 0L);
01112             }
01113             else
01114             {
01115                 return _smartMergeAddressee(pcAddr, backupAddr, 0L);
01116             }
01117         }
01118         else if (pcAddr.isEmpty())
01119         {
01120 #ifdef DEBUG
01121             DEBUGCONDUIT<<"2b"<<endl;
01122 #endif
01123             if (*palmAddr == *backupAddr)
01124             {
01125                 return _deleteAddressee(pcAddr, backupAddr, palmAddr);
01126             }
01127             else
01128             {
01129                 return _smartMergeAddressee(pcAddr, backupAddr, palmAddr);
01130             }
01131         }
01132         else if (_equal(palmAddr, pcAddr))
01133         {
01134 #ifdef DEBUG
01135             DEBUGCONDUIT<<"2c"<<endl;
01136 #endif
01137             // update Backup, update ID of PC if neededd
01138             return _writeBackup(palmAddr);
01139         }
01140         else if (_equal(backupAddr, pcAddr))
01141         {
01142 #ifdef DEBUG
01143             DEBUGCONDUIT<<"2d"<<endl;
01144             DEBUGCONDUIT<<"Flags: "<<palmAddr->getAttrib()<<", isDeleted="<<
01145                 isDeleted(palmAddr)<<", isArchived="<<isArchived(palmAddr)<<endl;
01146 #endif
01147             if (isDeleted(palmAddr))
01148                 return _deleteAddressee(pcAddr, backupAddr, palmAddr);
01149             else
01150                 return _copyToPC(pcAddr, backupAddr, palmAddr);
01151         }
01152         else if (*palmAddr == *backupAddr)
01153         {
01154 #ifdef DEBUG
01155             DEBUGCONDUIT<<"2e"<<endl;
01156 #endif
01157             return _copyToHH(pcAddr, backupAddr, palmAddr);
01158         }
01159         else
01160         {
01161 #ifdef DEBUG
01162             DEBUGCONDUIT<<"2f"<<endl;
01163 #endif
01164             // CR, since all are different
01165             return _smartMergeAddressee(pcAddr, backupAddr, palmAddr);
01166         }
01167     } // backupAddr
01168     return false;
01169 }
01170 
01171 
01172 
01173 bool AbbrowserConduit::_copyToHH(Addressee &pcAddr, PilotAddress*backupAddr,
01174         PilotAddress*palmAddr)
01175 {
01176     FUNCTIONSETUP;
01177 
01178     if (pcAddr.isEmpty()) return false;
01179     PilotAddress*paddr=palmAddr;
01180     bool paddrcreated=false;
01181     if (!paddr)
01182     {
01183         paddr=new PilotAddress(fAddressAppInfo);
01184         paddrcreated=true;
01185     }
01186     _copy(paddr, pcAddr);
01187 #ifdef DEBUG
01188     DEBUGCONDUIT<<"palmAddr->id="<<paddr->getID()<<", pcAddr.ID="<<
01189         pcAddr.custom(appString, idString)<<endl;
01190 #endif
01191 
01192     if(_savePalmAddr(paddr, pcAddr))
01193     {
01194 #ifdef DEBUG
01195         DEBUGCONDUIT<<"Vor _saveAbEntry, palmAddr->id="<<
01196         paddr->getID()<<", pcAddr.ID="<<pcAddr.custom(appString, idString)<<endl;
01197 #endif
01198         _savePCAddr(pcAddr, backupAddr, paddr);
01199     }
01200     if (paddrcreated) KPILOT_DELETE(paddr);
01201     return true;
01202 }
01203 
01204 
01205 
01206 bool AbbrowserConduit::_copyToPC(Addressee &pcAddr, PilotAddress*backupAddr,
01207         PilotAddress*palmAddr)
01208 {
01209     FUNCTIONSETUP;
01210     if (!palmAddr)
01211     {
01212         return false;
01213     }
01214 #ifdef DEBUG
01215     showPilotAddress(palmAddr);
01216 #endif
01217     _copy(pcAddr, palmAddr);
01218     _savePCAddr(pcAddr, backupAddr, palmAddr);
01219     _writeBackup(palmAddr);
01220     return true;
01221 }
01222 
01223 
01224 
01225 bool AbbrowserConduit::_writeBackup(PilotAddress *backup)
01226 {
01227     FUNCTIONSETUP;
01228     if (!backup) return false;
01229 
01230 
01231 #ifdef DEBUG
01232     showPilotAddress(backup);
01233 #endif
01234     PilotRecord *pilotRec = backup->pack();
01235     fLocalDatabase->writeRecord(pilotRec);
01236     KPILOT_DELETE(pilotRec);
01237     return true;
01238 }
01239 
01240 
01241 
01242 bool AbbrowserConduit::_deleteAddressee(Addressee &pcAddr, PilotAddress*backupAddr,
01243         PilotAddress*palmAddr)
01244 {
01245     FUNCTIONSETUP;
01246 
01247     if (palmAddr)
01248     {
01249         if (!syncedIds.contains(palmAddr->getID())) syncedIds.append(palmAddr->getID());
01250         palmAddr->makeDeleted();
01251         PilotRecord *pilotRec = palmAddr->pack();
01252         pilotRec->makeDeleted();
01253         pilotindex--;
01254         fDatabase->writeRecord(pilotRec);
01255         fLocalDatabase->writeRecord(pilotRec);
01256         syncedIds.append(pilotRec->getID());
01257         KPILOT_DELETE(pilotRec);
01258     }
01259     else if (backupAddr)
01260     {
01261         if (!syncedIds.contains(backupAddr->getID())) syncedIds.append(backupAddr->getID());
01262         backupAddr->makeDeleted();
01263         PilotRecord *pilotRec = backupAddr->pack();
01264         pilotRec->makeDeleted();
01265         pilotindex--;
01266         fLocalDatabase->writeRecord(pilotRec);
01267         syncedIds.append(pilotRec->getID());
01268         KPILOT_DELETE(pilotRec);
01269     }
01270     if (!pcAddr.isEmpty())
01271     {
01272 #ifdef DEBUG
01273         DEBUGCONDUIT << fname << " removing " << pcAddr.formattedName() << endl;
01274 #endif
01275         abChanged = true;
01276         aBook->removeAddressee(pcAddr);
01277     }
01278     return true;
01279 }
01280 
01281 
01282 
01283 /*********************************************************************
01284                  l o w - l e v e l   f u n c t i o n s   f o r
01285                    adding / removing palm/pc records
01286  *********************************************************************/
01287 
01288 
01289 
01290 bool AbbrowserConduit::_savePalmAddr(PilotAddress *palmAddr, Addressee &pcAddr)
01291 {
01292     FUNCTIONSETUP;
01293 
01294 #ifdef DEBUG
01295     DEBUGCONDUIT << "Saving to pilot " << palmAddr->id()
01296         << " " << palmAddr->getField(entryFirstname)
01297         << " " << palmAddr->getField(entryLastname)<< endl;
01298 #endif
01299 
01300     PilotRecord *pilotRec = palmAddr->pack();
01301     recordid_t pilotId = fDatabase->writeRecord(pilotRec);
01302 #ifdef DEBUG
01303     DEBUGCONDUIT<<"PilotRec nach writeRecord ("<<pilotId<<": ID="<<pilotRec->getID()<<endl;
01304 #endif
01305     fLocalDatabase->writeRecord(pilotRec);
01306     KPILOT_DELETE(pilotRec);
01307 
01308     // pilotId == 0 if using local db, so don't overwrite the valid id
01309     if(pilotId != 0)
01310     {
01311         palmAddr->setID(pilotId);
01312         if (!syncedIds.contains(pilotId)) syncedIds.append(pilotId);
01313     }
01314 
01315     recordid_t abId = 0;
01316     abId = pcAddr.custom(appString, idString).toUInt();
01317     if(abId != pilotId)
01318     {
01319         pcAddr.insertCustom(appString, idString, QString::number(pilotId));
01320         return true;
01321     }
01322 
01323     return false;
01324 }
01325 
01326 
01327 
01328 bool AbbrowserConduit::_savePCAddr(Addressee &pcAddr, PilotAddress*,
01329     PilotAddress*)
01330 {
01331     FUNCTIONSETUP;
01332 
01333 #ifdef DEBUG
01334     DEBUGCONDUIT<<"Before _savePCAddr, pcAddr.custom="<<pcAddr.custom(appString, idString)<<endl;
01335 #endif
01336     if(!pcAddr.custom(appString, idString).isEmpty())
01337     {
01338         addresseeMap.insert(pcAddr.custom(appString, idString).toLong(), pcAddr.uid());
01339     }
01340 
01341     aBook->insertAddressee(pcAddr);
01342 
01343     abChanged = true;
01344     return true;
01345 }
01346 
01347 
01348 
01349 /*********************************************************************
01350                    C O P Y   R E C O R D S
01351  *********************************************************************/
01352 
01353 
01354 int AbbrowserConduit::_compare(const QString & str1, const QString & str2) const
01355 {
01356 //  FUNCTIONSETUP;
01357     if(str1.isEmpty() && str2.isEmpty()) return 0;
01358     else return str1.compare(str2);
01359 }
01360 
01361 
01362 bool AbbrowserConduit::_equal(const PilotAddress *piAddress, const Addressee &abEntry,
01363     enum eqFlagsType flags) const
01364 {
01365     FUNCTIONSETUP;
01366     // empty records are never equal!
01367     if (!piAddress) return false;
01368     if (abEntry.isEmpty()) return false;
01369     //  Archived records match anything so they won't be copied to the HH again
01370     if (flags & eqFlagsFlags)
01371         if (isArchived(piAddress) && isArchived(abEntry) ) return true;
01372 
01373     if (flags & eqFlagsName)
01374     {
01375         if(_compare(abEntry.familyName(), piAddress->getField(entryLastname)))
01376             return false;
01377         if(_compare(abEntry.givenName(), piAddress->getField(entryFirstname)))
01378             return false;
01379         if(_compare(abEntry.title(), piAddress->getField(entryTitle)))
01380             return false;
01381         if(_compare(abEntry.organization(), piAddress->getField(entryCompany)))
01382             return false;
01383     }
01384     if (flags & eqFlagsNote)
01385         if(_compare(abEntry.note(), piAddress->getField(entryNote)))
01386             return false;
01387 
01388     if (flags & eqFlagsNote)
01389     {
01390         QString cat = _getCatForHH(abEntry.categories(), piAddress->getCategoryLabel());
01391         if(_compare(cat, piAddress->getCategoryLabel())) return false;
01392     }
01393 
01394     if (flags & eqFlagsPhones)
01395     {
01396         if(_compare(abEntry.phoneNumber(PhoneNumber::Work).number(),
01397             piAddress->getPhoneField(PilotAddress::eWork, false))) return false;
01398         if(_compare(abEntry.phoneNumber(PhoneNumber::Home).number(),
01399             piAddress->getPhoneField(PilotAddress::eHome, false))) return false;
01400         if(_compare(getOtherField(abEntry),
01401             piAddress->getPhoneField(PilotAddress::eOther, false))) return false;
01402         if(_compare(abEntry.preferredEmail(),
01403             piAddress->getPhoneField(PilotAddress::eEmail, false))) return false;
01404         if(_compare(getFax(abEntry).number(),
01405             piAddress->getPhoneField(PilotAddress::eFax, false))) return false;
01406         if(_compare(abEntry.phoneNumber(PhoneNumber::Cell).number(),
01407             piAddress->getPhoneField(PilotAddress::eMobile, false))) return false;
01408     }
01409 
01410     if (flags & eqFlagsAdress)
01411     {
01412         KABC::Address address = getAddress(abEntry);
01413         if(_compare(address.street(), piAddress->getField(entryAddress)))
01414             return false;
01415         if(_compare(address.locality(), piAddress->getField(entryCity)))
01416             return false;
01417         if(_compare(address.region(), piAddress->getField(entryState)))
01418             return false;
01419         if(_compare(address.postalCode(), piAddress->getField(entryZip)))
01420             return false;
01421         if(_compare(address.country(), piAddress->getField(entryCountry)))
01422             return false;
01423     }
01424 
01425     if (flags & eqFlagsCustom)
01426     {
01427         if(_compare(getCustomField(abEntry, 0),
01428             piAddress->getField(entryCustom1))) return false;
01429         if(_compare(getCustomField(abEntry, 1),
01430             piAddress->getField(entryCustom2))) return false;
01431         if(_compare(getCustomField(abEntry, 2),
01432             piAddress->getField(entryCustom3))) return false;
01433         if(_compare(getCustomField(abEntry, 3),
01434             piAddress->getField(entryCustom4))) return false;
01435     }
01436 
01437     // if any side is marked archived, but the other is not, the two
01438     // are not equal.
01439     if (flags & eqFlagsFlags)
01440         if (isArchived(piAddress) || isArchived(abEntry) ) return false;
01441 
01442     return true;
01443 }
01444 
01445 
01446 
01447 void AbbrowserConduit::_copy(PilotAddress *toPilotAddr, Addressee &fromAbEntry)
01448 {
01449     FUNCTIONSETUP;
01450     if (!toPilotAddr) return;
01451 
01452     toPilotAddr->setAttrib(toPilotAddr->getAttrib() & ~(dlpRecAttrDeleted));
01453 
01454     // don't do a reset since this could wipe out non copied info
01455     //toPilotAddr->reset();
01456     toPilotAddr->setField(entryLastname, fromAbEntry.familyName());
01457     QString firstAndMiddle = fromAbEntry.givenName();
01458     if(!fromAbEntry.additionalName().isEmpty()) firstAndMiddle += CSL1(" ") + fromAbEntry.additionalName();
01459     toPilotAddr->setField(entryFirstname, firstAndMiddle);
01460     toPilotAddr->setField(entryCompany, fromAbEntry.organization());
01461     toPilotAddr->setField(entryTitle, fromAbEntry.title());
01462     toPilotAddr->setField(entryNote, fromAbEntry.note());
01463 
01464     // do email first, to ensure its gets stored
01465     toPilotAddr->setPhoneField(PilotAddress::eEmail, fromAbEntry.preferredEmail(), false);
01466     toPilotAddr->setPhoneField(PilotAddress::eWork,
01467         fromAbEntry.phoneNumber(PhoneNumber::Work).number(), false);
01468     toPilotAddr->setPhoneField(PilotAddress::eHome,
01469         fromAbEntry.phoneNumber(PhoneNumber::Home).number(), false);
01470     toPilotAddr->setPhoneField(PilotAddress::eMobile,
01471         fromAbEntry.phoneNumber(PhoneNumber::Cell).number(), false);
01472     toPilotAddr->setPhoneField(PilotAddress::eFax, getFax(fromAbEntry).number(), false);
01473     toPilotAddr->setPhoneField(PilotAddress::ePager,
01474         fromAbEntry.phoneNumber(PhoneNumber::Pager).number(), false);
01475     toPilotAddr->setPhoneField(PilotAddress::eOther, getOtherField(fromAbEntry), false);
01476     toPilotAddr->setShownPhone(PilotAddress::eMobile);
01477 
01478     KABC::Address homeAddress = getAddress(fromAbEntry);
01479     _setPilotAddress(toPilotAddr, homeAddress);
01480 
01481     // Process the additional entries from the Palm(the palm database app block tells us the name of the fields)
01482     toPilotAddr->setField(entryCustom1, getCustomField(fromAbEntry, 0));
01483     toPilotAddr->setField(entryCustom2, getCustomField(fromAbEntry, 1));
01484     toPilotAddr->setField(entryCustom3, getCustomField(fromAbEntry, 2));
01485     toPilotAddr->setField(entryCustom4, getCustomField(fromAbEntry, 3));
01486 
01487     toPilotAddr->setCategory(_getCatForHH(fromAbEntry.categories(), toPilotAddr->getCategoryLabel()));
01488 
01489     if (isArchived(fromAbEntry))
01490         toPilotAddr->makeArchived();
01491     else
01492         toPilotAddr->setAttrib(toPilotAddr->getAttrib() & ~(dlpRecAttrArchived));
01493 }
01494 
01495 
01496 
01497 void AbbrowserConduit::_setPilotAddress(PilotAddress *toPilotAddr, const KABC::Address & abAddress)
01498 {
01499     toPilotAddr->setField(entryAddress, abAddress.street());
01500     toPilotAddr->setField(entryCity, abAddress.locality());
01501     toPilotAddr->setField(entryState, abAddress.region());
01502     toPilotAddr->setField(entryZip, abAddress.postalCode());
01503     toPilotAddr->setField(entryCountry, abAddress.country());
01504 }
01505 
01506 
01507 
01508 void AbbrowserConduit::_copyPhone(Addressee &toAbEntry,
01509                   PhoneNumber phone, QString palmphone)
01510 {
01511     if(!palmphone.isEmpty())
01512     {
01513         phone.setNumber(palmphone);
01514         toAbEntry.insertPhoneNumber(phone);
01515     }
01516     else
01517     {
01518         toAbEntry.removePhoneNumber(phone);
01519     }
01520 }
01521 
01522 
01523 
01524 void AbbrowserConduit::_copy(Addressee &toAbEntry, PilotAddress *fromPiAddr)
01525 {
01526     FUNCTIONSETUP;
01527     if (!fromPiAddr) return;
01528     // copy straight forward values
01529     toAbEntry.setFamilyName(fromPiAddr->getField(entryLastname));
01530     toAbEntry.setGivenName(fromPiAddr->getField(entryFirstname));
01531     toAbEntry.setOrganization(fromPiAddr->getField(entryCompany));
01532     toAbEntry.setTitle(fromPiAddr->getField(entryTitle));
01533     toAbEntry.setNote(fromPiAddr->getField(entryNote));
01534 
01535     // copy the phone stuff
01536     toAbEntry.removeEmail(toAbEntry.preferredEmail());
01537     toAbEntry.insertEmail(fromPiAddr->getPhoneField(PilotAddress::eEmail, false), true);
01538 
01539     _copyPhone(toAbEntry,
01540         toAbEntry.phoneNumber(PhoneNumber::Home),
01541         fromPiAddr->getPhoneField(PilotAddress::eHome, false));
01542     _copyPhone(toAbEntry,
01543         toAbEntry.phoneNumber(PhoneNumber::Work),
01544         fromPiAddr->getPhoneField(PilotAddress::eWork, false));
01545     _copyPhone(toAbEntry,
01546         toAbEntry.phoneNumber(PhoneNumber::Cell),
01547         fromPiAddr->getPhoneField(PilotAddress::eMobile, false));
01548     _copyPhone(toAbEntry,
01549         getFax(toAbEntry),
01550         fromPiAddr->getPhoneField(PilotAddress::eFax, false));
01551     _copyPhone(toAbEntry,
01552         toAbEntry.phoneNumber(PhoneNumber::Pager),
01553         fromPiAddr->getPhoneField(PilotAddress::ePager, false));
01554     setOtherField(toAbEntry, fromPiAddr->getPhoneField(PilotAddress::eOther, false));
01555 
01556     KABC::Address homeAddress = getAddress(toAbEntry);
01557     homeAddress.setStreet(fromPiAddr->getField(entryAddress));
01558     homeAddress.setLocality(fromPiAddr->getField(entryCity));
01559     homeAddress.setRegion(fromPiAddr->getField(entryState));
01560     homeAddress.setPostalCode(fromPiAddr->getField(entryZip));
01561     homeAddress.setCountry(fromPiAddr->getField(entryCountry));
01562     toAbEntry.insertAddress(homeAddress);
01563 
01564     setCustomField(toAbEntry, 0, fromPiAddr->getField(entryCustom1));
01565     setCustomField(toAbEntry, 1, fromPiAddr->getField(entryCustom2));
01566     setCustomField(toAbEntry, 2, fromPiAddr->getField(entryCustom3));
01567     setCustomField(toAbEntry, 3, fromPiAddr->getField(entryCustom4));
01568 
01569     // copy the fromPiAddr pilot id to the custom field KPilot_Id;
01570     // pilot id may be zero(since it could be new) but couldn't hurt
01571     // to even assign it to zero; let's us know what state the
01572     // toAbEntry is in
01573     toAbEntry.insertCustom(appString, idString, QString::number(fromPiAddr->getID()));
01574 
01575 
01576     int cat = fromPiAddr->getCat();
01577     QString category;
01578     if (0 < cat && cat <= 15) category = fAddressAppInfo.category.name[cat];
01579     _setCategory(toAbEntry, category);
01580 #ifdef DEBUG
01581     showAddressee(toAbEntry);
01582 #endif
01583     if (isArchived(fromPiAddr))
01584         makeArchived(toAbEntry);
01585 }
01586 
01587 
01588 
01589 /*********************************************************************
01590  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
01591  *********************************************************************/
01592 
01593 
01594 
01599 QString AbbrowserConduit::_smartMergeString(const QString &pc, const QString & backup,
01600     const QString & palm, eConflictResolution confRes)
01601 {
01602     FUNCTIONSETUP;
01603 
01604     // if both entries are already the same, no need to do anything
01605     if(pc == palm) return pc;
01606 
01607     // If this is a first sync, we don't have a backup record, so
01608     if(isFirstSync() || backup.isEmpty()) {
01609         if (pc.isEmpty() && palm.isEmpty() ) return QString::null;
01610         if(pc.isEmpty()) return palm;
01611         if(palm.isEmpty()) return pc;
01612     } else {
01613         // only one side modified, so return that string, no conflict
01614         if(palm == backup) return pc;
01615         if(pc == backup) return palm;
01616     }
01617 
01618 #ifdef DEBUG
01619     DEBUGCONDUIT<<"pc="<<pc<<", backup="<<backup<<", palm="<<
01620         palm<<", ConfRes="<<confRes<<endl;
01621     DEBUGCONDUIT<<"Use conflict resolution :"<<confRes<<
01622         ", PC="<<SyncAction::ePCOverrides<<endl;
01623 #endif
01624     switch(confRes) {
01625         case SyncAction::ePCOverrides: return pc; break;
01626         case SyncAction::eHHOverrides: return palm; break;
01627         case SyncAction::ePreviousSyncOverrides: return backup; break;
01628         default: break;
01629     }
01630     return QString::null;
01631 }
01632 
01633 
01634 
01635 bool AbbrowserConduit::_buildResolutionTable(ResolutionTable*tab, const Addressee &pcAddr,
01636     PilotAddress *backupAddr, PilotAddress *palmAddr)
01637 {
01638     FUNCTIONSETUP;
01639     if (!tab) return false;
01640     tab->setAutoDelete( TRUE );
01641     tab->labels[0]=i18n("Item on PC");
01642     tab->labels[1]=i18n("Handheld");
01643     tab->labels[2]=i18n("Last sync");
01644     if (!pcAddr.isEmpty())
01645         tab->fExistItems=(eExistItems)(tab->fExistItems|eExistsPC);
01646     if (backupAddr)
01647         tab->fExistItems=(eExistItems)(tab->fExistItems|eExistsBackup);
01648     if (palmAddr)
01649         tab->fExistItems=(eExistItems)(tab->fExistItems|eExistsPalm);
01650 
01651 #define appendGen(desc, abfield, palmfield) \
01652     tab->append(new ResolutionItem(desc, tab->fExistItems, \
01653         (!pcAddr.isEmpty())?(abfield):(QString::null), \
01654         (palmAddr)?(palmAddr->palmfield):(QString::null), \
01655         (backupAddr)?(backupAddr->palmfield):(QString::null) ))
01656 #define appendAddr(desc, abfield, palmfield) \
01657     appendGen(desc, abfield, getField(palmfield))
01658 #define appendGenPhone(desc, abfield, palmfield) \
01659     appendGen(desc, abfield, getPhoneField(PilotAddress::palmfield, false))
01660 #define appendPhone(desc, abfield, palmfield) \
01661     appendGenPhone(desc, pcAddr.phoneNumber(PhoneNumber::abfield).number(), palmfield)
01662 
01663 
01664     appendAddr(i18n("Last name"), pcAddr.familyName(), entryLastname);
01665     appendAddr(i18n("First name"), pcAddr.givenName(), entryFirstname);
01666     appendAddr(i18n("Organization"), pcAddr.organization(), entryCompany);
01667     appendAddr(i18n("Title"), pcAddr.title(), entryTitle);
01668     appendAddr(i18n("Note"), pcAddr.note(), entryNote);
01669     appendAddr(i18n("Custom 1"), getCustomField(pcAddr, 0), entryCustom1);
01670     appendAddr(i18n("Custom 2"), getCustomField(pcAddr, 1), entryCustom2);
01671     appendAddr(i18n("Custom 3"), getCustomField(pcAddr, 2), entryCustom3);
01672     appendAddr(i18n("Custom 4"), getCustomField(pcAddr, 3), entryCustom4);
01673     appendPhone(i18n("Work Phone"), Work, eWork);
01674     appendPhone(i18n("Home Phone"), Home, eHome);
01675     appendPhone(i18n("Mobile Phone"), Cell, eMobile);
01676     appendGenPhone(i18n("Fax"), getFax(pcAddr).number(), eFax);
01677     appendPhone(i18n("Pager"), Pager, ePager);
01678     appendGenPhone(i18n("Other"), getOtherField(pcAddr), eOther);
01679     appendGenPhone(i18n("Email"), pcAddr.preferredEmail(), eEmail);
01680 
01681     KABC::Address abAddress = getAddress(pcAddr);
01682     appendAddr(i18n("Address"), abAddress.street(), entryAddress);
01683     appendAddr(i18n("City"), abAddress.locality(), entryCity);
01684     appendAddr(i18n("Region"), abAddress.region(), entryState);
01685     appendAddr(i18n("Postal code"), abAddress.postalCode(), entryZip);
01686     appendAddr(i18n("Country"), abAddress.country(), entryCountry);
01687 
01688     appendGen(i18n("Category"),
01689         _getCatForHH(pcAddr.categories(), (palmAddr)?(palmAddr->getCategoryLabel()):(QString::null)),
01690         getCategoryLabel());
01691 
01692 #undef appendGen
01693 #undef appendAddr
01694 #undef appendGenPhone
01695 #undef appendPhone
01696 
01697     return true;
01698 }
01699 
01700 
01701 
01702 bool AbbrowserConduit::_applyResolutionTable(ResolutionTable*tab, Addressee &pcAddr,
01703     PilotAddress *backupAddr, PilotAddress *palmAddr)
01704 {
01705     FUNCTIONSETUP;
01706     if (!tab) return false;
01707     if (!palmAddr) {
01708 #ifdef DEBUG
01709         DEBUGCONDUIT<<"Empty palmAddr after conf res. ERROR!!!!"<<endl;
01710 #endif
01711         kdWarning()<<"Empty palmAddr after conf res. ERROR!!!!"<<endl;
01712         return false;
01713     }
01714 
01715     ResolutionItem*item=tab->first();
01716 #define SETGENFIELD(abfield, palmfield) \
01717     if (item) {\
01718         abfield; \
01719         palmAddr->setField(palmfield, item->fResolved); \
01720     }\
01721     item=tab->next();
01722 #define SETFIELD(abfield, palmfield) \
01723     SETGENFIELD(pcAddr.set##abfield(item->fResolved), palmfield)
01724 #define SETCUSTOMFIELD(abfield, palmfield) \
01725     SETGENFIELD(setCustomField(pcAddr, abfield, item->fResolved), palmfield)
01726 #define SETGENPHONE(abfield, palmfield) \
01727     if (item) { \
01728         abfield; \
01729         palmAddr->setPhoneField(PilotAddress::palmfield, item->fResolved, false); \
01730     }\
01731     item=tab->next();
01732 #define SETPHONEFIELD(abfield, palmfield) \
01733     SETGENPHONE(_setPhoneNumber(pcAddr, PhoneNumber::abfield, item->fResolved), palmfield)
01734 #define SETADDRESSFIELD(abfield, palmfield) \
01735     SETGENFIELD(abAddress.abfield(item->fResolved), palmfield)
01736 
01737     SETFIELD(FamilyName, entryLastname);
01738     SETFIELD(GivenName, entryFirstname);
01739     SETFIELD(Organization, entryCompany);
01740     SETFIELD(Title, entryTitle);
01741     SETFIELD(Note, entryNote);
01742     SETCUSTOMFIELD(0, entryCustom1);
01743     SETCUSTOMFIELD(1, entryCustom2);
01744     SETCUSTOMFIELD(2, entryCustom3);
01745     SETCUSTOMFIELD(3, entryCustom4);
01746     SETPHONEFIELD(Work, eWork);
01747     SETPHONEFIELD(Home, eHome);
01748     SETPHONEFIELD(Cell, eMobile);
01749     SETGENPHONE(setFax(pcAddr, item->fResolved), eFax);
01750     SETPHONEFIELD(Pager, ePager);
01751     SETGENPHONE(setOtherField(pcAddr, item->fResolved), eOther);
01752 
01753     // TODO: fix email
01754     if (item) {
01755         palmAddr->setPhoneField(PilotAddress::eEmail, item->fResolved, false);
01756         if (backupAddr)
01757             pcAddr.removeEmail(backupAddr->getPhoneField(PilotAddress::eEmail, false));
01758         pcAddr.removeEmail(palmAddr->getPhoneField(PilotAddress::eEmail, false));
01759         pcAddr.insertEmail(item->fResolved, true);
01760     }
01761     item=tab->next();
01762 
01763     KABC::Address abAddress = getAddress(pcAddr);
01764     SETADDRESSFIELD(setStreet, entryAddress);
01765     SETADDRESSFIELD(setLocality, entryCity);
01766     SETADDRESSFIELD(setRegion, entryState);
01767     SETADDRESSFIELD(setPostalCode, entryZip);
01768     SETADDRESSFIELD(setCountry, entryCountry);
01769     pcAddr.insertAddress(abAddress);
01770 
01771     // TODO: Is this correct?
01772     if (item) {
01773         palmAddr->setCategory(item->fResolved);
01774         _setCategory(pcAddr, item->fResolved);
01775     }
01776 
01777 
01778 #undef SETGENFIELD
01779 #undef SETFIELD
01780 #undef SETCUSTOMFIELD
01781 #undef SETGENPHONE
01782 #undef SETPHONEFIELD
01783 #undef SETADDRESSFIELD
01784 
01785     return true;
01786 }
01787 
01788 
01789 
01790 bool AbbrowserConduit::_smartMergeTable(ResolutionTable*tab)
01791 {
01792     FUNCTIONSETUP;
01793     if (!tab) return false;
01794     bool noconflict=true;
01795     ResolutionItem*item;
01796     for ( item = tab->first(); item; item = tab->next() )
01797     {
01798         // try to merge the three strings
01799         item->fResolved=_smartMergeString(item->fEntries[0],
01800             item->fEntries[2], item->fEntries[1], fConflictResolution);
01801         // if a conflict occurred, set the default to something sensitive:
01802         if (item->fResolved.isNull() && !(item->fEntries[0].isEmpty() &&
01803             item->fEntries[1].isEmpty() && item->fEntries[2].isEmpty() ) )
01804         {
01805             item->fResolved=item->fEntries[0];
01806             noconflict=false;
01807         }
01808         if (item->fResolved.isNull()) item->fResolved=item->fEntries[1];
01809         if (item->fResolved.isNull()) item->fResolved=item->fEntries[2];
01810     }
01811     return  noconflict;
01812 }
01813 
01814 
01815 
01820 bool AbbrowserConduit::_smartMergeAddressee(Addressee &pcAddr,
01821     PilotAddress *backupAddr, PilotAddress *palmAddr)
01822 {
01823     FUNCTIONSETUP;
01824 
01825     // Merge them, then look which records have to be written to device or abook
01826     int res = SyncAction::eAskUser;
01827     bool result=true;
01828     ResolutionTable tab;
01829 
01830     result &= _buildResolutionTable(&tab, pcAddr, backupAddr, palmAddr);
01831     // Now attempt a smart merge. If that fails, let conflict resolution do the job
01832     bool mergeOk=_smartMergeTable(&tab);
01833 
01834     if (!mergeOk)
01835     {
01836         QString dlgText;
01837         if (!palmAddr)
01838         {
01839             dlgText=i18n("The following address entry was changed, but does no longer exist on the handheld. Please resolve this conflict:");
01840         }
01841         else if (pcAddr.isEmpty())
01842         {
01843             dlgText=i18n("The following address entry was changed, but does no longer exist on the PC. Please resolve this conflict:");
01844         }
01845         else
01846         {
01847             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:");
01848         }
01849         ResolutionDlg*resdlg=new ResolutionDlg(0L, fHandle, i18n("Address conflict"), dlgText, &tab);
01850         resdlg->exec();
01851         KPILOT_DELETE(resdlg);
01852     }
01853     res=tab.fResolution;
01854 
01855     // Disallow some resolution under certain conditions, fix wrong values:
01856     switch (res) {
01857         case SyncAction::eHHOverrides:
01858             if (!palmAddr) res=SyncAction::eDelete;
01859             break;
01860         case SyncAction::ePCOverrides:
01861             if (pcAddr.isEmpty()) res=SyncAction::eDelete;
01862             break;
01863         case SyncAction::ePreviousSyncOverrides:
01864             if (!backupAddr) res=SyncAction::eDoNothing;
01865             break;
01866     }
01867 
01868     PilotAddress*pAddr=palmAddr;
01869     bool pAddrCreated=false;
01870     // Now that we have done a possible conflict resolution, apply the changes
01871     switch (res) {
01872         case SyncAction::eDuplicate:
01873             // Set the Palm ID to 0 so we don't overwrite the existing record.
01874             pcAddr.removeCustom(appString, idString);
01875             result &= _copyToHH(pcAddr, 0L, 0L);
01876             {
01877             Addressee pcadr;
01878             result &= _copyToPC(pcadr, backupAddr, palmAddr);
01879             }
01880             break;
01881         case SyncAction::eDoNothing:
01882             break;
01883         case SyncAction::eHHOverrides:
01884                 result &= _copyToPC(pcAddr, backupAddr, palmAddr);
01885                 break;
01886         case SyncAction::ePCOverrides:
01887             result &= _copyToHH(pcAddr, backupAddr, pAddr);
01888             break;
01889         case SyncAction::ePreviousSyncOverrides:
01890             _copy(pcAddr, backupAddr);
01891             if (palmAddr && backupAddr) *palmAddr=*backupAddr;
01892             result &= _savePalmAddr(backupAddr, pcAddr);
01893             result &= _savePCAddr(pcAddr, backupAddr, backupAddr);
01894             break;
01895         case SyncAction::eDelete:
01896             result &= _deleteAddressee(pcAddr, backupAddr, palmAddr);
01897             break;
01898         case SyncAction::eAskUser:
01899         default:
01900             if (!pAddr)
01901             {
01902                 pAddr=new PilotAddress(fAddressAppInfo);
01903                 pAddrCreated=true;
01904             }
01905             result &= _applyResolutionTable(&tab, pcAddr, backupAddr, pAddr);
01906 showAdresses(pcAddr, backupAddr, pAddr);
01907             // savePalmAddr sets the RecordID custom field already
01908             result &= _savePalmAddr(pAddr, pcAddr);
01909             result &= _savePCAddr(pcAddr, backupAddr, pAddr);
01910             if (pAddrCreated) KPILOT_DELETE(pAddr);
01911             break;
01912     }
01913 
01914     return result;
01915 }
01916 
01917 
01918 
01919 // TODO: right now entries are equal if both first/last name and organization are
01920 //  equal. This rules out two entries for the same person(e.g. real home and weekend home)
01921 //  or two persons with the same name where you don't know the organization.!!!
01922 Addressee AbbrowserConduit::_findMatch(const PilotAddress & pilotAddress) const
01923 {
01924     FUNCTIONSETUP;
01925     // TODO: also search with the pilotID
01926     // first, use the pilotID to UID map to find the appropriate record
01927     if( !isFirstSync() && (pilotAddress.id() > 0) )
01928     {
01929         QString id(addresseeMap[pilotAddress.id()]);
01930 #ifdef DEBUG
01931         DEBUGCONDUIT << fname << ": PilotRecord has id " << pilotAddress.id() << ", mapped to " << id << endl;
01932 #endif
01933         if(!id.isEmpty())
01934         {
01935             Addressee res(aBook->findByUid(id));
01936             if(!res.isEmpty()) return res;
01937 #ifdef DEBUG
01938             DEBUGCONDUIT << fname << ": PilotRecord has id " << pilotAddress.id() << ", but could not be found in the addressbook" << endl;
01939 #endif
01940         }
01941     }
01942 
01943     for(AddressBook::Iterator iter = aBook->begin(); iter != aBook->end(); ++iter)
01944     {
01945         Addressee abEntry = *iter;
01946         QString recID(abEntry.custom(appString, idString));
01947         bool ok;
01948         if (!recID.isEmpty() )
01949         {
01950             recordid_t rid = recID.toLong(&ok);
01951             if (ok && rid)
01952             {
01953                 if (rid==pilotAddress.id()) return abEntry;// yes, we found it
01954                 // skip this addressee, as it can an other corresponding address on the handheld
01955                 if (allIds.contains(rid)) continue;
01956             }
01957         }
01958 
01959         if (_equal(&pilotAddress, abEntry, eqFlagsAlmostAll))
01960         {
01961             return abEntry;
01962         }
01963     }
01964 #ifdef DEBUG
01965     DEBUGCONDUIT << fname << ": Could not find any addressbook enty matching " << pilotAddress.getField(entryLastname) << endl;
01966 #endif
01967     return Addressee();
01968 }
01969 
KDE Logo
This file is part of the documentation for kpilot Library Version 3.2.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat May 1 11:36:46 2004 by doxygen 1.2.15 written by Dimitri van Heesch, © 1997-2003