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 Wed Jul 28 23:57:48 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003