kpilot/lib

recordConduit.cc

00001 /* KPilot
00002 **
00003 ** Copyright (C) 2004 by Reinhold Kainhofer
00004 ** Based on the addressbook conduit:
00005 ** Copyright (C) 2000,2001 by Dan Pilone
00006 ** Copyright (C) 2000 Gregory Stern
00007 ** Copyright (C) 2002-2003 by Reinhold Kainhofer
00008 **
00009 ** This conduit is the base class for all record-based conduits.
00010 ** all the sync logic is included in this class, and all child classes
00011 ** just have to implement some specific copying and conflict resolution
00012 ** methods.
00013 */
00014 
00015 /*
00016 ** This program is free software; you can redistribute it and/or modify
00017 ** it under the terms of the GNU General Public License as published by
00018 ** the Free Software Foundation; either version 2 of the License, or
00019 ** (at your option) any later version.
00020 **
00021 ** This program is distributed in the hope that it will be useful,
00022 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
00023 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00024 ** GNU General Public License for more details.
00025 **
00026 ** You should have received a copy of the GNU General Public License
00027 ** along with this program in a file called COPYING; if not, write to
00028 ** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
00029 ** MA 02110-1301, USA.
00030 */
00031 
00032 /*
00033 ** Bug reports and questions can be sent to kde-pim@kde.org.
00034 */
00035 
00036 
00037 
00038 #include "options.h"
00039 
00040 #include <qtimer.h>
00041 #include <qtextcodec.h>
00042 #include <qfile.h>
00043 
00044 #include "pilotAppCategory.h"
00045 #include "pilotSerialDatabase.h"
00046 #include "pilotLocalDatabase.h"
00047 #include "recordConduit.h"
00048 
00049 
00050 // Something to allow us to check what revision
00051 // the modules are that make up a binary distribution.
00052 //
00053 //
00054 extern "C"
00055 {
00056 long version_record_conduit = KPILOT_PLUGIN_API;
00057 const char *id_record_conduit="$Id: recordConduit.cc 437980 2005-07-23 19:53:57Z kainhofe $";
00058 }
00059 
00060 
00061 /* virtual */ bool RecordConduitBase::exec()
00062 {
00063     FUNCTIONSETUP;
00064     fState = Initialize;
00065 
00066     setFirstSync(false);
00067 
00068     bool retrieved = false;
00069     if (!openDatabases( fDBName, &retrieved))
00070     {
00071         emit logError(TODO_I18N("Unable to open the %1 database on the handheld.").arg( fDBName ) );
00072         return false;
00073     }
00074     if (retrieved) setFirstSync(true);
00075 
00076     if (isFirstSync()) fIDList=fDatabase->idList();
00077     else fIDList=fDatabase->modifiedIDList();
00078     fIDListIterator = fIDList.begin();
00079 
00080     fTimer = new QTimer(this);
00081     connect(fTimer,SIGNAL(timeout()),this,SLOT(process()));
00082     fTimer->start(0,true); // Fire as often as possible to prompt processing
00083     return true;
00084 }
00085 
00086 /* virtual */ void RecordConduitBase::process()
00087 {
00088     FUNCTIONSETUP;
00089     SyncProgress p = Error;
00090 
00091     switch(fState)
00092     {
00093     case Initialize :
00094         p = loadPC();
00095         break;
00096     case PalmToPC :
00097         p = palmRecToPC();
00098         break;
00099     case Cleanup :
00100         p = cleanup();
00101         break;
00102     }
00103 
00104     switch(p)
00105     {
00106     case Error :
00107         fTimer->stop();
00108         delayDone();
00109         return;
00110     case NotDone :
00111         // Return so we get called again.
00112         return;
00113     case Done :
00114         // Get on with it.
00115         break;
00116     }
00117 
00118     // Here the previous call was done.
00119     switch(fState)
00120     {
00121     case Initialize :
00122         if ( ( syncMode().mode() == SyncMode::eCopyPCToHH ) ||
00123             ( syncMode().mode() == SyncMode::eRestore ) )
00124         {
00125             fState = Cleanup;
00126         }
00127         else
00128         {
00129             fState = PalmToPC;
00130         }
00131         break;
00132     case PalmToPC :
00133         fState = Cleanup;
00134         break;
00135     case Cleanup :
00136         fTimer->stop();
00137         delayDone();
00138         // No change in state, timer stopped and we're done.
00139         break;
00140     }
00141 }
00142 
00143 
00144 #if 0
00145 
00147 bool RecordConduit::PCData::makeArchived( RecordConduit::PCEntry *pcEntry )
00148 {
00149     if ( pcEntry ) {
00150         pcEntry->makeArchived();
00151         setChanged( true );
00152         return true;
00153     } else return false;
00154 }
00155 
00156 
00157 /* Builds the map which links record ids to uid's of PCEntry. This is the slow implementation,
00158  * that should always work. subclasses should reimplement it to speed things up.
00159 */
00160 bool RecordConduit::PCData::mapContactsToPilot( QMap<recordid_t,QString> &idContactMap )
00161 {
00162     FUNCTIONSETUP;
00163 
00164     idContactMap.clear();
00165 
00166     Iterator it = begin();
00167     PCEntry *ent;
00168     while ( !atEnd( it ) ) {
00169         ent = *it;
00170         recordid_t id( ent->recid() );
00171         if ( id != 0 ) {
00172             idContactMap.insert( id, ent->uid() );
00173         }
00174         ++it;
00175     }
00176 #ifdef DEBUG
00177     DEBUGCONDUIT << fname << ": Loaded " << idContactMap.size() <<
00178         " Entries on the pc and mapped them to records on the handheld. " << endl;
00179 #endif
00180     return true;
00181 }
00182 
00183 
00184 
00185 /*********************************************************************
00186                         C O N S T R U C T O R
00187  *********************************************************************/
00188 
00189 
00190 
00191 bool RecordConduit::mArchiveDeleted = false;
00192 
00193 RecordConduit::RecordConduit(QString name, KPilotDeviceLink * o, const char *n, const QStringList & a):
00194         ConduitAction(o, n, a),
00195         mPCData(0), mPalmIndex(0),
00196         mEntryMap(), mSyncedIds(), mAllIds()
00197 {
00198     FUNCTIONSETUP;
00199 #ifdef DEBUG
00200     DEBUGCONDUIT << id_record_conduit << endl;
00201 #endif
00202     fConduitName = name;
00203 }
00204 
00205 
00206 
00207 RecordConduit::~RecordConduit()
00208 {
00209     if ( mPCData ) KPILOT_DELETE(mPCData);
00210 }
00211 
00212 
00213 
00214 
00215 
00216 
00217 /*********************************************************************
00218                 S Y N C   S T R U C T U R E
00219  *********************************************************************/
00220 
00221 
00222 
00223 /* virtual */ bool RecordConduit::exec()
00224 {
00225     FUNCTIONSETUP;
00226 #ifdef DEBUG
00227     DEBUGCONDUIT << id_record_conduit << endl;
00228 #endif
00229 
00230     if ( !_prepare() ) return false;
00231 
00232     fFirstSync = false;
00233     // Database names probably in latin1.
00234     if( !openDatabases( dbName(), &fFirstSync ) )
00235     {
00236         emit logError(i18n("Unable to open the %1 database on the handheld.").arg( dbName() ) );
00237         return false;
00238     }
00239     _getAppInfo();
00240     if( !mPCData->loadData() )
00241     {
00242         emit logError( i18n("Unable to open %1.").arg( mPCData->description() ) );
00243         return false;
00244     }
00245     // get the addresseMap which maps Pilot unique record(address) id's to
00246     // a Abbrowser Addressee; allows for easy lookup and comparisons
00247     if ( mPCData->isEmpty() )
00248         fFirstSync = true;
00249     else
00250         mPCData->mapContactsToPilot( mEntryMap );
00251     fFirstSync = fFirstSync || ( mPCData->isEmpty() );
00252 
00253     // perform syncing from palm to abbrowser
00254     // iterate through all records in palm pilot
00255     mPalmIndex = 0;
00256 
00257 #ifdef DEBUG
00258     DEBUGCONDUIT << fname << ": fullsync=" << isFullSync() << ", firstSync=" << isFirstSync() << endl;
00259     DEBUGCONDUIT << fname << ": "
00260         << "syncDirection=" << getSyncDirection() << ", "
00261 //      << "archive = " << AbbrowserSettings::archiveDeleted()
00262         << endl;
00263     DEBUGCONDUIT << fname << ": conflictRes="<< getConflictResolution() << endl;
00264 //  DEBUGCONDUIT << fname << ": PilotStreetHome=" << AbbrowserSettings::pilotStreet() << ", PilotFaxHOme" << AbbrowserSettings::pilotFax() << endl;
00265 #endif
00266 
00267     if ( !isFirstSync() )
00268         mAllIds=fDatabase->idList();
00269 
00270     /* Note:
00271        if eCopyPCToHH or eCopyHHToPC, first sync everything, then lookup
00272        those entries on the receiving side that are not yet syncced and delete
00273        them. Use slotDeleteUnsyncedPCRecords and slotDeleteUnsyncedHHRecords
00274        for this, and no longer purge the whole addressbook before the sync to
00275        prevent data loss in case of connection loss. */
00276 
00277     QTimer::singleShot(0, this, SLOT(slotPalmRecToPC()));
00278 
00279     return true;
00280 }
00281 
00282 
00283 
00284 void RecordConduit::slotPalmRecToPC()
00285 {
00286     FUNCTIONSETUP;
00287     PilotRecord *palmRec = 0L, *backupRec = 0L;
00288 
00289     if ( getSyncDirection() == SyncAction::eCopyPCToHH )
00290     {
00291         mPCIter = mPCData->begin();
00292         QTimer::singleShot(0, this, SLOT(slotPCRecToPalm()));
00293         return;
00294     }
00295 
00296     if ( isFullSync() )
00297         palmRec = fDatabase->readRecordByIndex( mPalmIndex++ );
00298     else
00299         palmRec = dynamic_cast <PilotSerialDatabase * >(fDatabase)->readNextModifiedRec();
00300 
00301     if ( !palmRec )
00302     {
00303         mPCIter = mPCData->begin();
00304         QTimer::singleShot( 0, this, SLOT( slotPCRecToPalm() ) );
00305         return;
00306     }
00307 
00308     // already synced, so skip:
00309     if ( mSyncedIds.contains( palmRec->id() ) )
00310     {
00311         KPILOT_DELETE( palmRec );
00312         QTimer::singleShot( 0, this, SLOT( slotPalmRecToPC() ) );
00313         return;
00314     }
00315 
00316     backupRec = fLocalDatabase->readRecordById( palmRec->id() );
00317     PilotRecord *compareRec = backupRec ? backupRec : palmRec;
00318     PilotAppCategory *compareEntry = createPalmEntry( compareRec );
00319     PCEntry *pcEntry = findMatch( compareEntry );
00320     KPILOT_DELETE( compareEntry );
00321 
00322     PilotAppCategory *backupEntry=0L;
00323     if ( backupRec )
00324         backupEntry = createPalmEntry( backupRec );
00325     PilotAppCategory *palmEntry=0L;
00326     if ( palmRec )
00327         palmEntry = createPalmEntry( palmRec );
00328 
00329     syncEntry( pcEntry, backupEntry, palmEntry );
00330 
00331     mSyncedIds.append( palmRec->id() );
00332 
00333     KPILOT_DELETE( pcEntry );
00334     KPILOT_DELETE( palmEntry );
00335     KPILOT_DELETE( backupEntry );
00336     KPILOT_DELETE( palmRec );
00337     KPILOT_DELETE( backupRec );
00338 
00339     QTimer::singleShot(0, this, SLOT(slotPalmRecToPC()));
00340 }
00341 
00342 
00343 
00344 void RecordConduit::slotPCRecToPalm()
00345 {
00346     FUNCTIONSETUP;
00347 
00348     if ( ( getSyncDirection()==SyncAction::eCopyHHToPC ) ||
00349         mPCData->atEnd( mPCIter ) )
00350     {
00351         mPalmIndex = 0;
00352         QTimer::singleShot( 0, this, SLOT( slotDeletedRecord() ) );
00353         return;
00354     }
00355 
00356     PilotRecord *backupRec=0L;
00357     PCEntry *pcEntry = *mPCIter;
00358     ++mPCIter;
00359 
00360     // If marked as archived, don't sync!
00361     if ( isArchived( pcEntry ) )
00362     {
00363 #ifdef DEBUG
00364         DEBUGCONDUIT << fname << ": address with id " << pcEntry->uid() <<
00365             " marked archived, so don't sync." << endl;
00366 #endif
00367         KPILOT_DELETE( pcEntry );
00368         QTimer::singleShot( 0, this, SLOT( slotPCRecToPalm() ) );
00369         return;
00370     }
00371 
00372     recordid_t recID( pcEntry->recid() );
00373     if ( recID == 0 )
00374     {
00375         // it's a new item(no record ID and not inserted by the Palm -> PC sync), so add it
00376         syncEntry( pcEntry, 0L, 0L );
00377         KPILOT_DELETE( pcEntry );
00378         QTimer::singleShot( 0, this, SLOT( slotPCRecToPalm() ) );
00379         return;
00380     }
00381 
00382     // look into the list of already synced record ids to see if the PCEntry hasn't already been synced
00383     if ( mSyncedIds.contains( recID ) )
00384     {
00385 #ifdef DEBUG
00386         DEBUGCONDUIT << ": address with id " << recID << " already synced." << endl;
00387 #endif
00388         KPILOT_DELETE( pcEntry );
00389         QTimer::singleShot( 0, this, SLOT( slotPCRecToPalm() ) );
00390         return;
00391     }
00392 
00393 
00394     backupRec = fLocalDatabase->readRecordById( recID );
00395     // only update if no backup record or the backup record is not equal to the PCEntry
00396 
00397     PilotAppCategory*backupEntry=0L;
00398     if ( backupRec )
00399         backupEntry = createPalmEntry( backupRec );
00400     if( !backupRec || isFirstSync() || !_equal( backupEntry, pcEntry ) )
00401     {
00402         PilotRecord *palmRec = fDatabase->readRecordById( recID );
00403         PilotAppCategory *palmEntry=0L;
00404         if (palmRec)
00405             palmEntry = createPalmEntry( palmRec );
00406         syncEntry( pcEntry, backupEntry, palmEntry );
00407         // update the id just in case it changed
00408         if ( palmRec )
00409             recID = palmRec->id();
00410         KPILOT_DELETE( palmRec );
00411         KPILOT_DELETE( palmEntry );
00412     }
00413 
00414     KPILOT_DELETE( pcEntry );
00415     KPILOT_DELETE( backupEntry );
00416     KPILOT_DELETE( backupRec );
00417     mSyncedIds.append( recID );
00418 
00419     // done with the sync process, go on with the next one:
00420     QTimer::singleShot( 0, this, SLOT( slotPCRecToPalm() ) );
00421 }
00422 
00423 
00424 
00425 void RecordConduit::slotDeletedRecord()
00426 {
00427     FUNCTIONSETUP;
00428 
00429     PilotRecord *backupRec = fLocalDatabase->readRecordByIndex( mPalmIndex++ );
00430     if( !backupRec || isFirstSync() )
00431     {
00432         KPILOT_DELETE(backupRec);
00433         QTimer::singleShot( 0, this, SLOT( slotDeleteUnsyncedPCRecords() ) );
00434         return;
00435     }
00436 
00437     // already synced, so skip this record:
00438     if ( mSyncedIds.contains( backupRec->id() ) )
00439     {
00440         KPILOT_DELETE( backupRec );
00441         QTimer::singleShot( 0, this, SLOT( slotDeletedRecord() ) );
00442         return;
00443     }
00444 
00445     QString uid = mEntryMap[ backupRec->id() ];
00446     PCEntry *pcEntry = mPCData->findByUid( uid );
00447     PilotRecord *palmRec = fDatabase->readRecordById( backupRec->id() );
00448     PilotAppCategory *backupEntry = 0L;
00449     if (backupRec)
00450         backupEntry = createPalmEntry( backupRec );
00451     PilotAppCategory*palmEntry=0L;
00452     if (palmRec)
00453         palmEntry = createPalmEntry( palmRec );
00454 
00455     mSyncedIds.append( backupRec->id() );
00456     syncEntry( pcEntry, backupEntry, palmEntry );
00457 
00458     KPILOT_DELETE( pcEntry );
00459     KPILOT_DELETE( palmEntry );
00460     KPILOT_DELETE( backupEntry );
00461     KPILOT_DELETE( palmRec );
00462     KPILOT_DELETE( backupRec );
00463     QTimer::singleShot( 0, this, SLOT( slotDeletedRecord() ) );
00464 }
00465 
00466 
00467 
00468 void RecordConduit::slotDeleteUnsyncedPCRecords()
00469 {
00470     FUNCTIONSETUP;
00471     if ( getSyncDirection() == SyncAction::eCopyHHToPC )
00472     {
00473         QStringList uids;
00474         RecordIDList::iterator it;
00475         QString uid;
00476         for ( it = mSyncedIds.begin(); it != mSyncedIds.end(); ++it)
00477         {
00478             uid = mEntryMap[ *it ];
00479             if ( !uid.isEmpty() ) uids.append( uid );
00480         }
00481         // TODO: Does this speed up anything?
00482         // qHeapSort( uids );
00483         const QStringList alluids( mPCData->uids() );
00484         QStringList::ConstIterator uidit;
00485         for ( uidit = alluids.constBegin(); uidit != alluids.constEnd(); ++uidit )
00486         {
00487             if ( !uids.contains( *uidit ) )
00488             {
00489 #ifdef DEBUG
00490                 DEBUGCONDUIT << "Deleting PCEntry with uid " << (*uidit) << " from PC (is not on HH, and syncing with HH->PC direction)" << endl;
00491 #endif
00492                 mPCData->removeEntry( *uidit );
00493             }
00494         }
00495     }
00496     QTimer::singleShot(0, this, SLOT(slotDeleteUnsyncedHHRecords()));
00497 }
00498 
00499 
00500 
00501 void RecordConduit::slotDeleteUnsyncedHHRecords()
00502 {
00503     FUNCTIONSETUP;
00504     if ( getSyncDirection() == SyncAction::eCopyPCToHH )
00505     {
00506         RecordIDList ids = fDatabase->idList();
00507         RecordIDList::iterator it;
00508         for ( it = ids.begin(); it != ids.end(); ++it )
00509         {
00510             if ( !mSyncedIds.contains(*it) )
00511             {
00512 #ifdef DEBUG
00513                 DEBUGCONDUIT << "Deleting record with ID " << *it << " from handheld (is not on PC, and syncing with PC->HH direction)" << endl;
00514 #endif
00515                 fDatabase->deleteRecord(*it);
00516                 fLocalDatabase->deleteRecord(*it);
00517             }
00518         }
00519     }
00520     QTimer::singleShot( 0, this, SLOT( slotCleanup() ) );
00521 }
00522 
00523 
00524 void RecordConduit::slotCleanup()
00525 {
00526     FUNCTIONSETUP;
00527 
00528     // Set the appInfoBlock, just in case the category labels changed
00529     _setAppInfo();
00530     doPostSync();
00531     if(fDatabase)
00532     {
00533         fDatabase->resetSyncFlags();
00534         fDatabase->cleanup();
00535     }
00536     if(fLocalDatabase)
00537     {
00538         fLocalDatabase->resetSyncFlags();
00539         fLocalDatabase->cleanup();
00540     }
00541     KPILOT_DELETE( fDatabase );
00542     KPILOT_DELETE( fLocalDatabase );
00543     // TODO: do something if saving fails!
00544     mPCData->saveData();
00545     mPCData->cleanup();
00546     emit syncDone(this);
00547 }
00548 
00549 
00552 const QStringList RecordConduit::categories() const
00553 {
00554     QStringList cats;
00555     for ( int j = 0; j < PILOT_CATEGORY_MAX; j++ ) {
00556         QString catName( category( j ) );
00557         if ( !catName.isEmpty() ) cats << catName;
00558     }
00559     return cats;
00560 }
00561 int RecordConduit::findFlags() const
00562 {
00563     return eqFlagsAlmostAll;
00564 }
00565 
00566 
00567 bool RecordConduit::isDeleted( const PilotAppCategory *palmEntry )
00568 {
00569     if ( !palmEntry )
00570         return true;
00571     if ( palmEntry->isDeleted() && !palmEntry->isArchived() )
00572         return true;
00573     if ( palmEntry->isArchived() )
00574         return !archiveDeleted();
00575     return false;
00576 }
00577 bool RecordConduit::isArchived( const PilotAppCategory *palmEntry )
00578 {
00579     if ( palmEntry && palmEntry->isArchived() )
00580         return archiveDeleted();
00581     else
00582         return false;
00583 }
00584 
00585 
00586 
00587 
00588 /*********************************************************************
00589                 L O A D I N G   T H E   D A T A
00590  *********************************************************************/
00591 
00592 
00593 
00594 bool RecordConduit::_prepare()
00595 {
00596     FUNCTIONSETUP;
00597 
00598     readConfig();
00599     mSyncedIds.clear();
00600     mPCData = initializePCData();
00601 
00602     return mPCData && doPrepare();
00603 }
00604 
00605 
00606 void RecordConduit::_getAppInfo()
00607 {
00608     FUNCTIONSETUP;
00609     // get the address application header information
00610     unsigned char *buffer = new unsigned char[PilotRecord::APP_BUFFER_SIZE];
00611     int appLen=fDatabase->readAppBlock(buffer, PilotRecord::APP_BUFFER_SIZE);
00612 
00613     doUnpackAppInfo( buffer, appLen );
00614     delete[] buffer;
00615     buffer = 0;
00616 }
00617 
00618 void RecordConduit::_setAppInfo()
00619 {
00620     FUNCTIONSETUP;
00621     // get the address application header information
00622     int appLen = 0;
00623     unsigned char *buffer = doPackAppInfo( &appLen );
00624     if ( buffer )
00625     {   if (fDatabase)
00626             fDatabase->writeAppBlock( buffer, appLen );
00627         if (fLocalDatabase)
00628             fLocalDatabase->writeAppBlock( buffer, appLen );
00629         delete[] buffer;
00630     }
00631 }
00632 
00633 
00634 int RecordConduit::compareStr( const QString & str1, const QString & str2 )
00635 {
00636 //  FUNCTIONSETUP;
00637     if ( str1.isEmpty() && str2.isEmpty() )
00638         return 0;
00639     else
00640         return str1.compare( str2 );
00641 }
00642 
00643 
00651 QString RecordConduit::getCatForHH( const QStringList cats, const QString curr ) const
00652 {
00653     FUNCTIONSETUP;
00654     int j;
00655     if ( cats.size() < 1 )
00656         return QString::null;
00657     if ( cats.contains( curr ) )
00658         return curr;
00659     for ( QStringList::ConstIterator it = cats.begin(); it != cats.end(); ++it)
00660     {
00661         for ( j = 0; j < PILOT_CATEGORY_MAX; j++ )
00662         {
00663             QString catnm( category( j ) );
00664             if ( !(*it).isEmpty() && ( (*it)==catnm ) )
00665             {
00666                 return catnm;
00667             }
00668         }
00669     }
00670     // If we have a free label, return the first possible cat
00671     QString lastCat( category( PILOT_CATEGORY_MAX-1 ) );
00672     return ( lastCat.isEmpty() ) ? ( cats.first() ) : ( QString::null );
00673 }
00674 
00675 void RecordConduit::setCategory(PCEntry * pcEntry, QString cat)
00676 {
00677     if ( !cat.isEmpty() && cat!=category( 0 ) )
00678         pcEntry->insertCategory(cat);
00679 }
00680 
00681 
00682 
00683 
00684 
00685 
00686 /*********************************************************************
00687               G E N E R A L   S Y N C   F U N C T I O N
00688          These functions modify the Handheld and the addressbook
00689  *********************************************************************/
00690 
00691 
00692 
00693 bool RecordConduit::syncEntry( PCEntry *pcEntry, PilotAppCategory*backupEntry,
00694     PilotAppCategory*palmEntry)
00695 {
00696     FUNCTIONSETUP;
00697 
00698     if ( getSyncDirection() == SyncAction::eCopyPCToHH )
00699     {
00700         if ( pcEntry->isEmpty() )
00701         {
00702             return pcDeleteEntry( pcEntry, backupEntry, palmEntry );
00703         }
00704         else
00705         {
00706             return pcCopyToPalm( pcEntry, backupEntry, palmEntry );
00707         }
00708     }
00709 
00710     if ( getSyncDirection() == SyncAction::eCopyHHToPC )
00711     {
00712         if (!palmEntry)
00713             return pcDeleteEntry(pcEntry, backupEntry, palmEntry);
00714         else
00715             return palmCopyToPC(pcEntry, backupEntry, palmEntry);
00716     }
00717 
00718     if ( !backupEntry || isFirstSync() )
00719     {
00720         /*
00721         Resolution matrix (0..does not exist, E..exists, D..deleted flag set, A..archived):
00722           HH    PC  | Resolution
00723           ------------------------------------------------------------
00724            0     A  |  -
00725            0     E  |  PC -> HH, reset ID if not set correctly
00726            D     0  |  delete (error, should never occur!!!)
00727            D     E  |  CR (ERROR)
00728            E/A   0  |  HH -> PC
00729            E/A   E/A|  merge/CR
00730          */
00731         if  ( !palmEntry && isArchived( pcEntry ) )
00732         {
00733             return true;
00734         }
00735         else if ( !palmEntry && !pcEntry->isEmpty() )
00736         {
00737             // PC->HH
00738             bool res = pcCopyToPalm( pcEntry, 0L, 0L );
00739             return res;
00740         }
00741         else if ( !palmEntry && pcEntry->isEmpty() )
00742         {
00743             // everything's empty -> ERROR
00744             return false;
00745         }
00746         else if ( ( isDeleted( palmEntry ) || isArchived( palmEntry ) ) && pcEntry->isEmpty())
00747         {
00748             if ( isArchived( palmEntry ) )
00749                 return palmCopyToPC( pcEntry, 0L, palmEntry );
00750             else
00751                 // this happens if you add a record on the handheld and delete it again before you do the next sync
00752                 return pcDeleteEntry( pcEntry, 0L, palmEntry );
00753         }
00754         else if ( ( isDeleted(palmEntry) || isArchived( palmEntry ) ) && !pcEntry->isEmpty() )
00755         {
00756             // CR (ERROR)
00757             return smartMergeEntry( pcEntry, 0L, palmEntry );
00758         }
00759         else if ( pcEntry->isEmpty() )
00760         {
00761             // HH->PC
00762             return palmCopyToPC( pcEntry, 0L, palmEntry );
00763         }
00764         else
00765         {
00766             // Conflict Resolution
00767             return smartMergeEntry( pcEntry, 0L, palmEntry );
00768         }
00769     } // !backupEntry
00770     else
00771     {
00772         /*
00773         Resolution matrix:
00774           1) if HH.(empty| (deleted &! archived) ) -> { if (PC==B) -> delete, else -> CR }
00775              if HH.archived -> {if (PC==B) -> copyToPC, else -> CR }
00776              if PC.empty -> { if (HH==B) -> delete, else -> CR }
00777              if PC.archived -> {if (HH==B) -> delete on HH, else CR }
00778           2) if PC==HH -> { update B, update ID of PC if needed }
00779           3) if PC==B -> { HH!=PC, thus HH modified, so copy HH->PC }
00780              if HH==B -> { PC!=HH, thus PC modified, so copy PC->HH }
00781           4) else: all three PCEntrys are different -> CR
00782         */
00783 
00784         if ( !palmEntry || isDeleted(palmEntry) )
00785         {
00786             if ( _equal( backupEntry, pcEntry ) || pcEntry->isEmpty() )
00787             {
00788                 return pcDeleteEntry( pcEntry, backupEntry, 0L );
00789             }
00790             else
00791             {
00792                 return smartMergeEntry( pcEntry, backupEntry, 0L );
00793             }
00794         }
00795         else if ( pcEntry->isEmpty() )
00796         {
00797             if (*palmEntry == *backupEntry)
00798             {
00799                 return pcDeleteEntry( pcEntry, backupEntry, palmEntry );
00800             }
00801             else
00802             {
00803                 return smartMergeEntry( pcEntry, backupEntry, palmEntry );
00804             }
00805         }
00806         else if ( _equal( palmEntry, pcEntry ) )
00807         {
00808             // update Backup, update ID of PC if neededd
00809             return backupSaveEntry( palmEntry );
00810         }
00811         else if ( _equal( backupEntry, pcEntry ) )
00812         {
00813 #ifdef DEBUG
00814             DEBUGCONDUIT << "Flags: " << palmEntry->getAttrib() << ", isDeleted=" <<
00815                 isDeleted( palmEntry ) << ", isArchived=" << isArchived( palmEntry )
00816                 << endl;
00817 #endif
00818             if ( isDeleted( palmEntry ) )
00819             {
00820                 return pcDeleteEntry( pcEntry, backupEntry, palmEntry );
00821             }
00822             else
00823             {
00824                 return palmCopyToPC( pcEntry, backupEntry, palmEntry );
00825             }
00826         }
00827         else if ( *palmEntry == *backupEntry )
00828         {
00829             return pcCopyToPalm( pcEntry, backupEntry, palmEntry );
00830         }
00831         else
00832         {
00833             // CR, since all are different
00834             return smartMergeEntry( pcEntry, backupEntry, palmEntry );
00835         }
00836     } // backupEntry
00837     return false;
00838 }
00839 
00840 bool RecordConduit::pcCopyToPalm( PCEntry *pcEntry, PilotAppCategory *backupEntry,
00841         PilotAppCategory*palmEntry )
00842 {
00843     FUNCTIONSETUP;
00844 
00845     if ( pcEntry->isEmpty() ) return false;
00846     PilotAppCategory *hhEntry = palmEntry;
00847     bool hhEntryCreated = false;
00848     if ( !hhEntry )
00849     {
00850         hhEntry = createPalmEntry( 0 );
00851         hhEntryCreated=true;
00852     }
00853     _copy( hhEntry, pcEntry );
00854 #ifdef DEBUG
00855     DEBUGCONDUIT << "palmEntry->id=" << hhEntry->id() << ", pcEntry.ID=" <<
00856         pcEntry->uid() << endl;
00857 #endif
00858 
00859     if( palmSaveEntry( hhEntry, pcEntry ) )
00860     {
00861 #ifdef DEBUG
00862         DEBUGCONDUIT << "Entry palmEntry->id=" <<
00863         hhEntry->id() << "saved to palm, now updating pcEntry->uid()=" << pcEntry->uid() << endl;
00864 #endif
00865         pcSaveEntry( pcEntry, backupEntry, hhEntry );
00866     }
00867     if ( hhEntryCreated ) KPILOT_DELETE( hhEntry );
00868     return true;
00869 }
00870 
00871 
00872 
00873 
00874 bool RecordConduit::palmCopyToPC( PCEntry *pcEntry, PilotAppCategory *backupEntry,
00875         PilotAppCategory *palmEntry )
00876 {
00877     FUNCTIONSETUP;
00878     if ( !palmEntry )
00879     {
00880         return false;
00881     }
00882     _copy( pcEntry, palmEntry );
00883     pcSaveEntry( pcEntry, backupEntry, palmEntry );
00884     backupSaveEntry( palmEntry );
00885     return true;
00886 }
00887 
00888 
00889 
00890 /*********************************************************************
00891                  l o w - l e v e l   f u n c t i o n s   f o r
00892                    adding / removing palm/pc records
00893  *********************************************************************/
00894 
00895 
00896 
00897 bool RecordConduit::palmSaveEntry( PilotAppCategory *palmEntry, PCEntry *pcEntry )
00898 {
00899     FUNCTIONSETUP;
00900 
00901 #ifdef DEBUG
00902     DEBUGCONDUIT << "Saving to pilot " << palmEntry->id() << endl;
00903 #endif
00904 
00905     PilotRecord *pilotRec = palmEntry->pack();
00906     recordid_t pilotId = fDatabase->writeRecord(pilotRec);
00907 #ifdef DEBUG
00908     DEBUGCONDUIT << "PilotRec nach writeRecord (" << pilotId <<
00909         ": ID=" << pilotRec->id() << endl;
00910 #endif
00911     fLocalDatabase->writeRecord( pilotRec );
00912     KPILOT_DELETE( pilotRec );
00913 
00914     // pilotId == 0 if using local db, so don't overwrite the valid id
00915     if ( pilotId != 0 )
00916     {
00917         palmEntry->setID( pilotId );
00918         if ( !mSyncedIds.contains( pilotId ) )
00919         {
00920             mSyncedIds.append( pilotId );
00921         }
00922     }
00923 
00924     recordid_t hhId( pcEntry->recid() );
00925     if ( hhId != pilotId )
00926     {
00927         pcEntry->setRecid( pilotId );
00928         return true;
00929     }
00930 
00931     return false;
00932 }
00933 
00934 
00935 
00936 bool RecordConduit::backupSaveEntry( PilotAppCategory *backup )
00937 {
00938     FUNCTIONSETUP;
00939     if ( !backup ) return false;
00940 
00941 
00942 #ifdef DEBUG
00943 //  showPilotAppCategory( backup );
00944 #endif
00945     PilotRecord *pilotRec = backup->pack();
00946     fLocalDatabase->writeRecord( pilotRec );
00947     KPILOT_DELETE( pilotRec );
00948     return true;
00949 }
00950 
00951 
00952 
00953 bool RecordConduit::pcSaveEntry( PCEntry *pcEntry, PilotAppCategory *,
00954     PilotAppCategory * )
00955 {
00956     FUNCTIONSETUP;
00957 
00958 #ifdef DEBUG
00959     DEBUGCONDUIT << "Before _savepcEntry, pcEntry->uid()=" <<
00960         pcEntry->uid() << endl;
00961 #endif
00962     if ( pcEntry->recid() != 0 )
00963     {
00964         mEntryMap.insert( pcEntry->recid(), pcEntry->uid() );
00965     }
00966 
00967     mPCData->updateEntry( pcEntry );
00968     return true;
00969 }
00970 
00971 
00972 
00973 bool RecordConduit::pcDeleteEntry( PCEntry *pcEntry, PilotAppCategory *backupEntry,
00974     PilotAppCategory *palmEntry )
00975 {
00976     FUNCTIONSETUP;
00977 
00978     if ( palmEntry )
00979     {
00980         if ( !mSyncedIds.contains( palmEntry->id() ) )
00981         {
00982             mSyncedIds.append(palmEntry->id());
00983         }
00984         palmEntry->makeDeleted();
00985         PilotRecord *pilotRec = palmEntry->pack();
00986         pilotRec->setDeleted();
00987         mPalmIndex--;
00988         fDatabase->writeRecord( pilotRec );
00989         fLocalDatabase->writeRecord( pilotRec );
00990         mSyncedIds.append( pilotRec->id() );
00991         KPILOT_DELETE( pilotRec );
00992     }
00993     else if ( backupEntry )
00994     {
00995         if ( !mSyncedIds.contains( backupEntry->id() ) )
00996         {
00997             mSyncedIds.append( backupEntry->id() );
00998         }
00999         backupEntry->makeDeleted();
01000         PilotRecord *pilotRec = backupEntry->pack();
01001         pilotRec->setDeleted();
01002         mPalmIndex--;
01003         fLocalDatabase->writeRecord( pilotRec );
01004         mSyncedIds.append( pilotRec->id() );
01005         KPILOT_DELETE( pilotRec );
01006     }
01007     if ( !pcEntry->isEmpty() )
01008     {
01009 #ifdef DEBUG
01010         DEBUGCONDUIT << fname << " removing " << pcEntry->uid() << endl;
01011 #endif
01012         mPCData->removeEntry( pcEntry );
01013     }
01014     return true;
01015 }
01016 
01017 
01018 
01019 /*********************************************************************
01020                    C O P Y   R E C O R D S
01021  *********************************************************************/
01022 
01023 
01024 
01025 
01026 
01027 /*********************************************************************
01028  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
01029  *********************************************************************/
01030 
01031 
01032 
01033 
01034 // TODO: right now entries are equal if both first/last name and organization are
01035 //  equal. This rules out two entries for the same person(e.g. real home and weekend home)
01036 //  or two persons with the same name where you don't know the organization.!!!
01037 RecordConduit::PCEntry *RecordConduit::findMatch( PilotAppCategory *palmEntry ) const
01038 {
01039     FUNCTIONSETUP;
01040     if ( !palmEntry )
01041         return 0;
01042 
01043     // TODO: also search with the pilotID
01044     // first, use the pilotID to UID map to find the appropriate record
01045     if( !isFirstSync() && ( palmEntry->id() > 0) )
01046     {
01047         QString id( mEntryMap[palmEntry->id()] );
01048 #ifdef DEBUG
01049         DEBUGCONDUIT << fname << ": PilotRecord has id " << palmEntry->id() << ", mapped to " << id << endl;
01050 #endif
01051         if( !id.isEmpty() )
01052         {
01053             PCEntry *res = mPCData->findByUid( id );
01054             if ( !res && !res->isEmpty() ) return res;
01055             KPILOT_DELETE( res );
01056 #ifdef DEBUG
01057             DEBUGCONDUIT << fname << ": PilotRecord has id " << palmEntry->id() <<
01058                 ", but could not be found on the PC side" << endl;
01059 #endif
01060         }
01061     }
01062 
01063     for ( PCData::Iterator iter = mPCData->begin(); !mPCData->atEnd( iter ); ++iter )
01064     {
01065         PCEntry *abEntry = *iter;
01066         recordid_t rid( abEntry->recid() );
01067         if ( rid>0 )
01068         {
01069             if ( rid == palmEntry->id() )
01070                 return abEntry;// yes, we found it
01071             // skip this PCEntry, as it has a different corresponding address on the handheld
01072             //if ( mAllIds.contains( rid ) ) continue;
01073         }
01074 
01075         if ( _equal( palmEntry, abEntry, eqFlagsAlmostAll ) )
01076         {
01077             return abEntry;
01078         }
01079         KPILOT_DELETE( abEntry );
01080     }
01081 #ifdef DEBUG
01082     DEBUGCONDUIT << fname << ": Could not find any entry matching Palm record with id " << QString::number( palmEntry->id() ) << endl;
01083 #endif
01084     return 0;
01085 }
01086 
01087 #endif
01088 
01089 
01090 
01091 
01092 #include "recordConduit.moc"
01093 
KDE Home | KDE Accessibility Home | Description of Access Keys