kpilot Library API Documentation

doc-conduit.cc

00001 /* doc-conduit.cc                           KPilot
00002 **
00003 ** Copyright (C) 2002 by Reinhold Kainhofer
00004 **
00005 ** The doc conduit synchronizes text files on the PC with DOC databases on the Palm
00006 */
00007 
00008 /*
00009 ** This program is free software; you can redistribute it and/or modify
00010 ** it under the terms of the GNU General Public License as published by
00011 ** the Free Software Foundation; either version 2 of the License, or
00012 ** (at your option) any later version.
00013 **
00014 ** This program is distributed in the hope that it will be useful,
00015 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
00016 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00017 ** GNU General Public License for more details.
00018 **
00019 ** You should have received a copy of the GNU General Public License
00020 ** along with this program in a file called COPYING; if not, write to
00021 ** the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
00022 ** MA 02111-1307, USA.
00023 */
00024 
00025 /*
00026 ** Bug reports and questions can be sent to kde-pim@kde.org.
00027 */
00028 
00029 
00030 // naming of the bookmark file:
00031 // PDB->TXT:    convert bookmarks to a .bm file
00032 // TXT->PDB:    If a .bmk file exists, use it, otherwise use the .bm file (from the PDB->TXT conversion)
00033 //          This way, the bookmark file is not overwritten, a manual bookmark file overrides, but the bookmarks from the handheld are still available
00034 
00035 
00036 #include "options.h"
00037 #include "doc-conduit.moc"
00038 
00039 #include <qtimer.h>
00040 #include <qdir.h>
00041 
00042 #include <kconfig.h>
00043 #include <kmdcodec.h> 
00044 
00045 #include <pilotLocalDatabase.h>
00046 #include <pilotSerialDatabase.h>
00047 
00048 #include "doc-factory.h"
00049 #include "doc-conflictdialog.h"
00050 #include "DOC-converter.h"
00051 #include "pilotDOCHead.h"
00052 
00053 
00054 // Something to allow us to check what revision
00055 // the modules are that make up a binary distribution.
00056 const char *doc_conduit_id = "$Id: doc-conduit.cc,v 1.20 2003/09/20 17:37:35 binner Exp $";
00057 
00058 QString dirToString(eSyncDirectionEnum dir) {
00059     switch(dir) {
00060 //      case eSyncAll: return "eSyncAll";
00061         case eSyncPDAToPC: return CSL1("eSyncPDAToPC");
00062         case eSyncPCToPDA: return CSL1("eSyncPCToPDA");
00063         case eSyncNone: return CSL1("eSyncNone");
00064         case eSyncConflict: return CSL1("eSyncConflict");
00065         case eSyncDelete: return CSL1("eSyncDelete");
00066         default: return CSL1("ERROR");
00067     }
00068 }
00069 
00070 
00071 /*********************************************************************
00072                         C O N S T R U C T O R
00073  *********************************************************************/
00074 
00075 
00076 DOCConduit::DOCConduit(KPilotDeviceLink * o,
00077     const char *n, const QStringList & a):ConduitAction(o, n, a)
00078 {
00079     FUNCTIONSETUP;
00080 #ifdef DEBUG
00081     DEBUGCONDUIT<<doc_conduit_id<<endl;
00082 #endif
00083     fConduitName=i18n("DOC");
00084 }
00085 
00086 
00087 
00088 DOCConduit::~DOCConduit()
00089 {
00090     FUNCTIONSETUP;
00091 }
00092 
00093 
00094 bool DOCConduit::isCorrectDBTypeCreator(DBInfo dbinfo) {
00095     return dbinfo.type == dbtype() && dbinfo.creator == dbcreator();
00096 }
00097 const unsigned long DOCConduit::dbtype() {
00098     return get_long(DOCConduitFactory::dbDOCtype);
00099 }
00100 const unsigned long DOCConduit::dbcreator() {
00101     return get_long(DOCConduitFactory::dbDOCcreator);
00102 }
00103 
00104 
00105 
00106 /*********************************************************************
00107                 L O A D I N G   T H E   D A T A
00108  *********************************************************************/
00109 
00110 
00111 
00112 void DOCConduit::readConfig()
00113 {
00114     FUNCTIONSETUP;
00115 
00116     KConfigGroupSaver g(fConfig, DOCConduitFactory::fGroup);
00117 
00118     fTXTDir = fConfig->readEntry(DOCConduitFactory::fTXTDir);
00119     fPDBDir = fConfig->readEntry(DOCConduitFactory::fPDBDir);
00120     fKeepPDBLocally =
00121         fConfig->readBoolEntry(DOCConduitFactory::fKeepPDBLocally, true);
00122     eConflictResolution =
00123         (enum eSyncDirectionEnum) (fConfig->
00124         readNumEntry(DOCConduitFactory::fConflictResolution, 0));
00125     fTXTBookmarks = DOCConverter::eBmkNone;
00126     if (fConfig->readBoolEntry(DOCConduitFactory::fConvertBookmarks, true))
00127     {
00128         if (fConfig->readBoolEntry(DOCConduitFactory::fBookmarksBmk, true))
00129             fTXTBookmarks |= DOCConverter::eBmkFile;
00130         if (fConfig->readBoolEntry(DOCConduitFactory::fBookmarksInline, true))
00131             fTXTBookmarks |= DOCConverter::eBmkInline;
00132         if (fConfig->readBoolEntry(DOCConduitFactory::fBookmarksEndtags, true))
00133             fTXTBookmarks |= DOCConverter::eBmkEndtags;
00134     }
00135     fPDBBookmarks = fConfig->readNumEntry(DOCConduitFactory::fPCBookmarks, DOCConverter::eBmkNone);
00136 
00137 
00138     fCompress = fConfig->readBoolEntry(DOCConduitFactory::fCompress, true);
00139     eSyncDirection =
00140         (enum eSyncDirectionEnum) (fConfig->
00141         readNumEntry(DOCConduitFactory::fSyncDirection, 1));
00142 
00143     fIgnoreBmkChangesOnly = fConfig->readBoolEntry(DOCConduitFactory::fIgnoreBmkChanges, false);
00144     fLocalSync = fConfig->readBoolEntry(DOCConduitFactory::fLocalSync, false);
00145     fAlwaysUseResolution = fConfig->readBoolEntry(DOCConduitFactory::fAlwaysUseResolution, false);
00146 
00147     fDBListSynced=fConfig->readListEntry(DOCConduitFactory::fDOCList);
00148 
00149 #ifdef DEBUG
00150     DEBUGCONDUIT << fname
00151         << ": Settings "
00152         << " fTXTDir=" << fTXTDir
00153         << " fPDBDir=" << fPDBDir
00154         << " fkeepPDBLocally=" << fKeepPDBLocally
00155         << " eConflictResolution=" << eConflictResolution
00156         << " fTXTBookmarks=" << fTXTBookmarks
00157         << " fPDBBookmarks=" << fPDBBookmarks
00158         << " fCompress=" << fCompress
00159         << " eSyncDirection=" << eSyncDirection << endl;
00160 #endif
00161 }
00162 
00163 
00164 
00165 bool DOCConduit::pcTextChanged(QString txtfn)
00166 {
00167     KConfigGroupSaver g(fConfig, DOCConduitFactory::fGroup);
00168 
00169     // How do I find out if a text file has changed shince we last synced it??
00170     // Use KMD5 for now. If I realize it is too slow, then I have to go back to comparing modification times
00171     // if there is no config setting yet, assume the file has been changed. the md5 sum will be written to the config file after the sync.
00172     QString oldDigest=fConfig->readEntry(txtfn);
00173     if (oldDigest.length()<=0)
00174     {
00175         return true;
00176     }
00177 #ifdef DEBUG
00178     DEBUGCONDUIT<<"Old digest is "<<oldDigest<<endl;
00179 #endif
00180 
00181     KMD5 docmd5;
00182     QFile txtfile(txtfn);
00183     if (txtfile.open(IO_ReadOnly)){
00184         docmd5.update(txtfile);
00185         QString thisDigest(docmd5.hexDigest() /* .data() */);
00186 #ifdef DEBUG
00187         DEBUGCONDUIT<<"New digest is "<<thisDigest<<endl;
00188 #endif
00189         return (thisDigest.length()<=0) || (thisDigest!=oldDigest);
00190     } else {
00191         // File does not exist. This should actually never happen. Anyways, just return true to indicate it has changed. 
00192         // doSync should detect this and delete the doc from the handheld.
00193         return true;
00194     }
00195     return false;
00196 }
00197 
00198 
00199 
00200 bool DOCConduit::hhTextChanged(PilotDatabase*docdb) 
00201 {
00202     if (!docdb) return false;
00203     
00204     PilotRecord *firstRec = docdb->readRecordByIndex(0);
00205     PilotDOCHead docHeader(firstRec);
00206     KPILOT_DELETE(firstRec);
00207 
00208     int storyRecs = docHeader.numRecords;
00209 
00210     // determine the index of the next modified record (does it lie 
00211     // beyond the actual text records?)
00212     int modRecInd=-1;
00213     PilotRecord*modRec=docdb->readNextModifiedRec(&modRecInd);
00214 #ifdef DEBUG
00215     DEBUGCONDUIT<<"Index of first changed record: "<<modRecInd<<endl;
00216 #endif
00217         
00218     KPILOT_DELETE(modRec);
00219     // if the header record was changed, find out which is the first changed 
00220     // real document record:
00221     if (modRecInd==0) {
00222         modRec=docdb->readNextModifiedRec(&modRecInd);
00223 #ifdef DEBUG
00224         DEBUGCONDUIT<<"Reread Index of first changed records: "<<modRecInd<<endl;
00225 #endif
00226         KPILOT_DELETE(modRec);
00227     }
00228     
00229     // The record index starts with 0, so only a negative number means 
00230     // no modified record was found
00231     if (modRecInd >= 0) {
00232 #ifdef DEBUG
00233         DEBUGCONDUIT<<"Handheld side has changed, condition="<<
00234             ((!fIgnoreBmkChangesOnly) || (modRecInd <= storyRecs))<<endl;
00235 #endif
00236         if ((!fIgnoreBmkChangesOnly) || (modRecInd <= storyRecs)) 
00237             return true;
00238     } else {
00239 #ifdef DEBUG
00240         DEBUGCONDUIT<<"Handheld side has NOT changed!"<<endl;
00241 #endif
00242         return false;
00243     }
00244     return false;
00245 }
00246 
00247 
00248 
00249 /*********************************************************************
00250  *     Helper functions
00251  ********************************************************************/
00252 
00253 QString DOCConduit::constructPDBFileName(QString name) {
00254     FUNCTIONSETUP;
00255     QString fn;
00256     QDir dr(fPDBDir);
00257     QFileInfo pth(dr, name);
00258     if (!name.isEmpty()) fn=pth.absFilePath()+CSL1(".pdb");
00259     return fn;
00260 }
00261 QString DOCConduit::constructTXTFileName(QString name) {
00262     FUNCTIONSETUP;
00263     QString fn;
00264     QDir dr(fTXTDir);
00265     QFileInfo pth(dr, name);
00266     if (!name.isEmpty()) fn=pth.absFilePath()+CSL1(".txt");
00267     return fn;
00268 }
00269 
00270 
00271 
00272 
00273 
00274 /*********************************************************************
00275                 S Y N C   S T R U C T U R E
00276  *********************************************************************/
00277 
00278 
00279 
00280 
00281 
00282 /* virtual */ bool DOCConduit::exec()
00283 {
00284     FUNCTIONSETUP;
00285 #ifdef DEBUG
00286     DEBUGCONDUIT<<"Conduit version: "<<doc_conduit_id<<endl;
00287 #endif
00288 
00289     if (!fConfig) {
00290         kdWarning() << k_funcinfo << ": No config file was set!" << endl;
00291         return false;
00292     }
00293     readConfig();
00294     dbnr=0;
00295     
00296     emit logMessage(i18n("Searching for texts and databases to synchronize"));
00297 
00298     QTimer::singleShot(0, this, SLOT(syncNextDB()));
00299     return true;
00300 }
00301 
00302 
00303 
00304 bool DOCConduit::doSync(docSyncInfo &sinfo) {
00305     bool res=false;
00306     
00307     if (sinfo.direction==eSyncDelete) {
00308         if (!sinfo.txtfilename.isEmpty()) {
00309             if (!QFile::remove(sinfo.txtfilename)) {
00310                 kdWarning()<<"Unable to delete the text file "<<sinfo.txtfilename<<" on the PC"<<endl;
00311             }
00312             QString bmkfilename = sinfo.txtfilename;
00313             if (bmkfilename.endsWith(CSL1(".txt"))){
00314                 bmkfilename.remove(bmkfilename.length()-4, 4);
00315             }
00316             bmkfilename+=CSL1(PDBBMK_SUFFIX);
00317             if (!QFile::remove(bmkfilename)) {
00318 #ifdef DEBUG
00319                 DEBUGCONDUIT<<"Could not remove bookmarks file "<<bmkfilename<<" for database "<<sinfo.handheldDB<<endl;
00320 #endif
00321             }
00322         }
00323         if (!sinfo.pdbfilename.isEmpty() && fKeepPDBLocally) {
00324             PilotLocalDatabase*database=new PilotLocalDatabase(fPDBDir, 
00325                 QString::fromLatin1(sinfo.dbinfo.name), false);
00326             if (database) {
00327                 if ( database->deleteDatabase() !=0 ) {
00328                     kdWarning()<<"Unable to delete database "<<sinfo.dbinfo.name<<" on the PC"<<endl;
00329                 }
00330                 KPILOT_DELETE(database);
00331             }
00332         }
00333         if (!fLocalSync) {
00334             PilotDatabase *database=new PilotSerialDatabase(pilotSocket(), 
00335                 QString::fromLatin1(sinfo.dbinfo.name));
00336             if ( database->deleteDatabase() !=0 ) {
00337                     kdWarning()<<"Unable to delete database "<<sinfo.dbinfo.name<<" from the handheld"<<endl;
00338             }
00339             KPILOT_DELETE(database);
00340         }
00341         return true;
00342     }
00343     // preSyncAction should initialize the custom databases/files for the
00344     // specific action chosen for this db and return a pointer to a docDBInfo
00345     // instance which points either to a local database or a database on the handheld.
00346     PilotDatabase *database = preSyncAction(sinfo);
00347 
00348     if (database && ( !database->isDBOpen() ) ) {
00349 #ifdef DEBUG
00350         DEBUGCONDUIT<<"Database "<<sinfo.dbinfo.name<<" does not yet exist. Creating it:"<<endl;
00351 #endif
00352         if (!database->createDatabase(dbcreator(), dbtype()) ) {
00353 #ifdef DEBUG
00354             DEBUGCONDUIT<<"Failed"<<endl;
00355 #endif
00356         }
00357     }
00358 
00359     if (database && database->isDBOpen()) {
00360         DOCConverter docconverter;
00361         connect(&docconverter, SIGNAL(logError(const QString &)), SIGNAL(logError(const QString &)));
00362         connect(&docconverter, SIGNAL(logMessage(const QString &)), SIGNAL(logMessage(const QString &)));
00363 
00364         docconverter.setTXTpath(fTXTDir, sinfo.txtfilename);
00365         docconverter.setPDB(database);
00366         docconverter.setCompress(fCompress);
00367 
00368         switch (sinfo.direction) {
00369             case eSyncPDAToPC:
00370                 docconverter.setBookmarkTypes(fPDBBookmarks);
00371                 res = docconverter.convertPDBtoTXT();
00372                 break;
00373             case eSyncPCToPDA:
00374                 docconverter.setBookmarkTypes(fTXTBookmarks);
00375                 res = docconverter.convertTXTtoPDB();
00376                 break;
00377             default:
00378                 break;
00379         }
00380         
00381         // Now calculate the md5 checksum of the PC text and write it to the config file
00382         {
00383             KConfigGroupSaver g(fConfig, DOCConduitFactory::fGroup);
00384             KMD5 docmd5;
00385             QFile txtfile(docconverter.txtFilename());
00386             if (txtfile.open(IO_ReadOnly)) {
00387                 docmd5.update(txtfile);
00388                 QString thisDigest(docmd5.hexDigest() /* .data() */);
00389                 fConfig->writeEntry(docconverter.txtFilename(), thisDigest);
00390                 fConfig->sync();
00391 #ifdef DEBUG
00392                 DEBUGCONDUIT<<"MD5 Checksum of the text "<<sinfo.txtfilename<<" is "<<thisDigest<<endl;
00393 #endif
00394             } else {
00395 #ifdef DEBUG
00396                 DEBUGCONDUIT<<"couldn't open file "<<docconverter.txtFilename()<<" for reading!!!"<<endl;
00397 #endif
00398             }
00399         }
00400         
00401         if (!postSyncAction(database, sinfo, res)) 
00402             emit logError(i18n("Unable to install the locally created PalmDOC %1 to the handheld.")
00403                 .arg(QString::fromLatin1(sinfo.dbinfo.name)));
00404         if (!res)
00405             emit logError(i18n("Conversion of PalmDOC \"%1\" failed.")
00406                 .arg(QString::fromLatin1(sinfo.dbinfo.name)));
00407 //      disconnect(&docconverter, SIGNAL(logError(const QString &)), SIGNAL(logError(const QString &)));
00408 //      disconnect(&docconverter, SIGNAL(logMessage(const QString &)), SIGNAL(logMessage(const QString &)));
00409 //      KPILOT_DELETE(database);
00410     }
00411     else
00412     {
00413         emit logError(i18n("Unable to open or create the database %1")
00414             .arg(QString::fromLatin1(sinfo.dbinfo.name)));
00415     }
00416     return res;
00417 }
00418 
00419 
00422 void DOCConduit::syncNextDB() {
00423     FUNCTIONSETUP;
00424     DBInfo dbinfo;
00425 
00426     if (eSyncDirection==eSyncPCToPDA || fHandle->findDatabase(NULL, &dbinfo, dbnr, dbtype(), dbcreator() /*, cardno */ ) < 0)
00427     {
00428         // no more databases available, so check for PC->Palm sync
00429         QTimer::singleShot(0, this, SLOT(syncNextTXT()));
00430         return;
00431     }
00432     dbnr=dbinfo.index+1;
00433 #ifdef DEBUG
00434     DEBUGCONDUIT<<"Next Palm database to sync: "<<dbinfo.name<<", Index="<<dbinfo.index<<endl;
00435 #endif
00436 
00437     // if creator and/or type don't match, go to next db
00438     if (!isCorrectDBTypeCreator(dbinfo) ||
00439         fDBNames.contains(QString::fromLatin1(dbinfo.name)))
00440     {
00441         QTimer::singleShot(0, this, SLOT(syncNextDB()));
00442         return;
00443     }
00444 
00445     QString txtfilename=constructTXTFileName(QString::fromLatin1(dbinfo.name));
00446     QString pdbfilename=constructPDBFileName(QString::fromLatin1(dbinfo.name));
00447 
00448     docSyncInfo syncInfo(QString::fromLatin1(dbinfo.name),
00449         txtfilename, pdbfilename, eSyncNone);
00450     syncInfo.dbinfo=dbinfo;
00451     needsSync(syncInfo);
00452     fSyncInfoList.append(syncInfo);
00453     fDBNames.append(QString::fromLatin1(dbinfo.name));
00454 
00455     QTimer::singleShot(0, this, SLOT(syncNextDB()));
00456     return;
00457 }
00458 
00459 
00460 
00461 void DOCConduit::syncNextTXT()
00462 {
00463     FUNCTIONSETUP;
00464     
00465     if (eSyncDirection==eSyncPDAToPC  )
00466     {
00467         // We don't sync from PC to PDB, so start the conflict resolution and then the actual sync process
00468         docnames.clear();
00469         QTimer::singleShot(0, this, SLOT(checkPDBFiles()));
00470         return;
00471     }
00472 
00473     // if docnames isn't initialized, get a list of all *.txt files in fTXTDir
00474     if (docnames.isEmpty()/* || dociterator==docnames.end() */) {
00475         docnames=QDir(fTXTDir, CSL1("*.txt")).entryList() ;
00476         dociterator=docnames.begin();
00477     }
00478     if (dociterator==docnames.end()) {
00479         // no more databases available, so start the conflict resolution and then the actual sync proces
00480         docnames.clear();
00481         QTimer::singleShot(0, this, SLOT(checkPDBFiles()));
00482         return;
00483     }
00484 
00485     QString fn=(*dociterator);
00486 
00487     QDir dr(fTXTDir);
00488     QFileInfo fl(dr, fn );
00489     QString txtfilename=fl.absFilePath();
00490     QString pdbfilename;
00491     dociterator++;
00492 
00493     DBInfo dbinfo;
00494     // Include all "extensions" except the last. This allows full stops inside the database name (e.g. abbreviations)
00495     // first fill everything with 0, so we won't have a buffer overflow.
00496     memset(&dbinfo.name[0], 0, 33);
00497     strncpy(&dbinfo.name[0], fl.baseName(TRUE).latin1(), 30);
00498 
00499     bool alreadySynced=fDBNames.contains(fl.baseName(TRUE));
00500     if (!alreadySynced) {
00501         docSyncInfo syncInfo(QString::fromLatin1(dbinfo.name), 
00502             txtfilename, pdbfilename, eSyncNone);
00503         syncInfo.dbinfo=dbinfo;
00504         needsSync(syncInfo);
00505         fSyncInfoList.append(syncInfo);
00506         fDBNames.append(QString::fromLatin1(dbinfo.name));
00507     } else {
00508 #ifdef DEBUG
00509         DEBUGCONDUIT<<txtfilename<<" has already been synced, skipping it."<<endl;
00510 #endif
00511     }
00512 
00513     QTimer::singleShot(0, this, SLOT(syncNextTXT()));
00514     return;
00515 }
00516 
00517 
00518 
00521 void DOCConduit::checkPDBFiles() {
00522     FUNCTIONSETUP;
00523 
00524     if (fLocalSync || !fKeepPDBLocally || eSyncDirection==eSyncPCToPDA )
00525     {
00526         // no more databases available, so check for PC->Palm sync
00527         QTimer::singleShot(0, this, SLOT(checkDeletedDocs()));
00528         return;
00529     }
00530 
00531     // Walk through all files in the pdb directory and check if it has already been synced.
00532     // if docnames isn't initialized, get a list of all *.pdb files in fPDBDir
00533     if (docnames.isEmpty()/* || dociterator==docnames.end() */) {
00534         docnames=QDir(fPDBDir, CSL1("*.pdb")).entryList() ;
00535         dociterator=docnames.begin();
00536     }
00537     if (dociterator==docnames.end()) {
00538         // no more databases available, so start the conflict resolution and then the actual sync proces
00539         docnames.clear();
00540         QTimer::singleShot(0, this, SLOT(checkDeletedDocs()));
00541         return;
00542     }
00543 
00544     QString fn=(*dociterator);
00545 
00546     QDir dr(fPDBDir);
00547     QFileInfo fl(dr, fn );
00548     QString pdbfilename=fl.absFilePath();
00549     dociterator++;
00550     
00551     //  Get the doc title and check if it has already been synced (in the synced docs list of in fDBNames to be synced)
00552     // If the doc title doesn't appear in either list, install it to the Handheld, and add it to the list of dbs to be synced.
00553     QString dbname=fl.baseName(TRUE).left(30);
00554     if (!fDBNames.contains(dbname) && !fDBListSynced.contains(dbname)) {
00555         if (fHandle->installFiles(pdbfilename, false)) {
00556             DBInfo dbinfo;
00557             // Include all "extensions" except the last. This allows full stops inside the database name (e.g. abbreviations)
00558             // first fill everything with 0, so we won't have a buffer overflow.
00559             memset(&dbinfo.name[0], 0, 33);
00560             strncpy(&dbinfo.name[0], dbname.latin1(), 30);
00561 
00562             docSyncInfo syncInfo(dbname, constructTXTFileName(dbname), pdbfilename, eSyncNone);
00563             syncInfo.dbinfo=dbinfo;
00564             needsSync(syncInfo);
00565             fSyncInfoList.append(syncInfo);
00566             fDBNames.append(dbname);
00567         } else {
00568 #ifdef DEBUG
00569             DEBUGCONDUIT<<"Could not install database "<<dbname<<" ("<<pdbfilename<<") to the handheld"<<endl;
00570 #endif
00571         }
00572     }
00573     
00574     QTimer::singleShot(0, this, SLOT(checkPDBFiles()));
00575 }
00576 
00577 
00578 
00579 void DOCConduit::checkDeletedDocs()
00580 {
00581     FUNCTIONSETUP;
00582 
00583     for (QStringList::Iterator it=fDBListSynced.begin(); it!=fDBListSynced.end(); ++it ) {
00584         if (!fDBNames.contains(*it)) {
00585             // We need to delete this doc:
00586             QString dbname(*it);
00587             QString txtfilename=constructTXTFileName(dbname);
00588             QString pdbfilename=constructPDBFileName(dbname);
00589             docSyncInfo syncInfo(dbname, txtfilename, pdbfilename, eSyncDelete);
00590 
00591             DBInfo dbinfo;
00592             memset(&dbinfo.name[0], 0, 33);
00593             strncpy(&dbinfo.name[0], dbname.latin1(), 30);
00594             syncInfo.dbinfo=dbinfo;
00595 
00596             fSyncInfoList.append(syncInfo);
00597         }
00598     }
00599     QTimer::singleShot(0, this, SLOT(resolve()));
00600     return;
00601 }
00602 
00603 
00604 
00605 void DOCConduit::resolve() {
00606     FUNCTIONSETUP;
00607     
00608     for (fSyncInfoListIterator=fSyncInfoList.begin(); fSyncInfoListIterator!=fSyncInfoList.end(); fSyncInfoListIterator++) {
00609         // Walk through each database and apply the conflictResolution option. 
00610         // the remaining conflicts will be resolved in the resolution dialog
00611         if ((*fSyncInfoListIterator).direction==eSyncConflict){
00612 #ifdef DEBUG
00613             DEBUGCONDUIT<<"We have a conflict for "<<(*fSyncInfoListIterator).handheldDB<<", default="<<eConflictResolution<<endl;
00614 #endif
00615             switch (eConflictResolution)
00616             {
00617                 case eSyncPDAToPC:
00618 #ifdef DEBUG
00619                     DEBUGCONDUIT<<"PDA overrides for database "<<(*fSyncInfoListIterator).handheldDB<<endl;
00620 #endif
00621                     (*fSyncInfoListIterator).direction = eSyncPDAToPC;
00622                     break;
00623                 case eSyncPCToPDA:
00624 #ifdef DEBUG
00625                     DEBUGCONDUIT<<"PC overrides for database "<<(*fSyncInfoListIterator).handheldDB<<endl;
00626 #endif
00627                     (*fSyncInfoListIterator).direction = eSyncPCToPDA;
00628                     break;
00629                 case eSyncNone:
00630 #ifdef DEBUG
00631                     DEBUGCONDUIT<<"No sync for database "<<(*fSyncInfoListIterator).handheldDB<<endl;
00632 #endif
00633                     (*fSyncInfoListIterator).direction = eSyncNone;
00634                     break;
00635                 case eSyncDelete:
00636                 case eSyncConflict:
00637                 default:
00638 #ifdef DEBUG
00639                     DEBUGCONDUIT<<"Conflict remains due to default resolution setting for database "<<(*fSyncInfoListIterator).handheldDB<<endl;
00640 #endif
00641                     break;
00642             }
00643         }
00644     }
00645     
00646     // Show the conflict resolution dialog and ask for the action for each database
00647     ResolutionDialog*dlg=new ResolutionDialog( 0,  i18n("Conflict Resolution"), &fSyncInfoList , fHandle);
00648     bool show=fAlwaysUseResolution || (dlg && dlg->hasConflicts);
00649     if (show) {
00650         if (!dlg || !dlg->exec() ) {
00651             KPILOT_DELETE(dlg)
00652             emit logMessage(i18n("Sync aborted by user."));
00653             QTimer::singleShot(0, this, SLOT(cleanup()));
00654             return;
00655         }
00656     }
00657     KPILOT_DELETE(dlg)
00658     
00659 
00660     // fDBNames will be filled with the names of the databases that are actually synced (not deleted), so I can write the list to the config file
00661     fDBNames.clear();
00662     fSyncInfoListIterator=fSyncInfoList.begin();
00663     QTimer::singleShot(0,this, SLOT(syncDatabases()));
00664     return;
00665 }
00666 
00667 
00668 
00669 void DOCConduit::syncDatabases() {
00670     FUNCTIONSETUP;
00671     if (fSyncInfoListIterator==fSyncInfoList.end()) {
00672         // We're done, so clean up
00673         QTimer::singleShot(0, this, SLOT(cleanup()));
00674         return;
00675     }
00676     
00677     docSyncInfo sinfo=(*fSyncInfoListIterator);
00678     fSyncInfoListIterator++;
00679     
00680     switch (sinfo.direction) {
00681         case eSyncConflict:
00682 #ifdef DEBUG
00683             DEBUGCONDUIT<<"Entry "<<sinfo.handheldDB<<"( txtfilename: "<<sinfo.txtfilename<<
00684                 ", pdbfilename: "<<sinfo.pdbfilename<<") had sync direction eSyncConflict!!!"<<endl;
00685 #endif
00686             break;
00687         case eSyncDelete:
00688         case eSyncPDAToPC:
00689         case eSyncPCToPDA:
00690             emit logMessage(i18n("Synchronizing text \"%1\"").arg(sinfo.handheldDB));
00691             if (!doSync(sinfo)) {
00692                 // The sync could not be done, so inform the user (the error message should probably issued inside doSync)
00693 #ifdef DEBUG
00694                 DEBUGCONDUIT<<"There was some error syncing the text \""<<sinfo.handheldDB<<"\" with the file "<<sinfo.txtfilename<<endl;
00695 #endif
00696             }
00697             break;
00698         case eSyncNone:
00699 //      case eSyncAll:
00700             break;
00701     }
00702     if (sinfo.direction != eSyncDelete) fDBNames.append(sinfo.handheldDB);
00703     
00704     QTimer::singleShot(0,this, SLOT(syncDatabases()));
00705     return;
00706 }
00707 
00708 
00709 PilotDatabase*DOCConduit::openDOCDatabase(QString dbname) {
00710     if (fLocalSync) return new PilotLocalDatabase(fPDBDir, dbname, false);
00711     else return new PilotSerialDatabase(pilotSocket(), dbname);
00712 }
00713 
00714 
00715 bool DOCConduit::needsSync(docSyncInfo &sinfo)
00716 {
00717     FUNCTIONSETUP;
00718     sinfo.direction = eSyncNone;
00719     
00720     PilotDatabase*docdb=openDOCDatabase(QString::fromLatin1(sinfo.dbinfo.name));
00721     if (!fDBListSynced.contains(sinfo.handheldDB)) {
00722         // the database wasn't included on last sync, so it has to be new.
00723 #ifdef DEBUG
00724         DEBUGCONDUIT<<"Database "<<sinfo.dbinfo.name<<" wasn't included in the previous sync!"<<endl;
00725 #endif
00726 
00727         /* Resolution Table:
00728             PC  HH  |  normal      PC->HH      HH->PC
00729             -----------------------------------------
00730             N   -   |     P          P           D
00731             -   N   |     H          D           H
00732             N   N   |     C          P           H
00733         */
00734 
00735         if (QFile::exists(sinfo.txtfilename)) sinfo.fPCStatus=eStatNew;
00736         else sinfo.fPCStatus=eStatDoesntExist;
00737         if (docdb && docdb->isDBOpen()) sinfo.fPalmStatus=eStatNew;
00738         else sinfo.fPalmStatus=eStatDoesntExist;
00739         KPILOT_DELETE(docdb);
00740         
00741         switch (eSyncDirection) {
00742             case eSyncPDAToPC:
00743                 if (sinfo.fPalmStatus==eStatDoesntExist)
00744                     sinfo.direction=eSyncDelete;
00745                 else sinfo.direction=eSyncPDAToPC;
00746                 break;
00747             case eSyncPCToPDA:
00748                 if (sinfo.fPCStatus==eStatDoesntExist) 
00749                     sinfo.direction=eSyncDelete;
00750                 else sinfo.direction=eSyncPCToPDA;
00751                 break;
00752             case eSyncNone: // means actually both directions!
00753                 if (sinfo.fPCStatus==eStatNew) {
00754                     if (sinfo.fPalmStatus==eStatNew) sinfo.direction=eSyncConflict;
00755                     else sinfo.direction=eSyncPCToPDA;
00756                 } else {
00757                     if (sinfo.fPalmStatus==eStatNew) sinfo.direction=eSyncPDAToPC;
00758                     else {
00759                         sinfo.direction=eSyncNone;
00760 #ifdef DEBUG                    
00761                         DEBUGCONDUIT<<"I'm supposed to find a sync direction, but the "<<
00762                         " text "<<sinfo.dbinfo.name<<" doesn't exist on either "<<
00763                         " the handheld or the PC"<<endl;
00764 #endif
00765                     }
00766                 }
00767                 break;
00768             default:
00769                 break;
00770         }
00771         return true;
00772     }
00773     
00774     // Text was included in the last sync
00775     if (!QFile::exists(sinfo.txtfilename)) sinfo.fPCStatus=eStatDeleted;
00776     else if(pcTextChanged(sinfo.txtfilename)) {
00777         sinfo.fPCStatus=eStatChanged;
00778 #ifdef DEBUG
00779         DEBUGCONDUIT<<"PC side has changed!"<<endl;
00780 #endif
00781         // TODO: Check for changed bookmarks on the PC side
00782 #ifdef DEBUG
00783     } else {
00784         DEBUGCONDUIT<<"PC side has NOT changed!"<<endl;
00785 #endif
00786     }
00787     
00788     if (!docdb || !docdb->isDBOpen()) sinfo.fPalmStatus=eStatDeleted;
00789     else if (hhTextChanged(docdb)) {
00790 #ifdef DEBUG
00791         DEBUGCONDUIT<<"Handheld side has changed!"<<endl;
00792 #endif
00793         sinfo.fPalmStatus=eStatChanged;
00794 #ifdef DEBUG
00795     } else {
00796         DEBUGCONDUIT<<"Handheld side has NOT changed!"<<endl;
00797 #endif
00798     }
00799     KPILOT_DELETE(docdb);
00800 
00801 
00802     // Now that we know the status of both sides, determine what to do.
00803         /* Resolution Table:
00804             PC  HH  |  normal      PC->HH      HH->PC
00805             -----------------------------------------
00806             -   -   |     -          -           -
00807             C   -   |     P          P           H
00808             -   C   |     H          P           H
00809             C   C   |     C          P           H
00810             D   -   |     D          D           H
00811             -   D   |     D          P           D
00812             D   D   |     D          D           D
00813             -----------------------------------------
00814             C   D   |     C          P           D
00815             D   C   |     C          D           H
00816         */
00817 
00818     
00819     if (sinfo.fPCStatus == eStatNone && sinfo.fPalmStatus==eStatNone) {
00820 #ifdef DEBUG
00821         DEBUGCONDUIT<<"Nothing has changed, not need for a sync."<<endl;
00822 #endif
00823         sinfo.direction=eSyncNone;
00824         return false;
00825     }
00826     
00827     // In all other cases, if only one direction (PC->HH or HH->PC) 
00828     // should be done, check if the DB was deleted or if we are supposed 
00829     // to sync that direction
00830     
00831     if (eSyncDirection==eSyncPCToPDA) {
00832         if (sinfo.fPCStatus==eStatDeleted) sinfo.direction=eSyncDelete;
00833         else sinfo.direction=eSyncPCToPDA;
00834         return true;
00835     }
00836     if (eSyncDirection==eSyncPDAToPC) {
00837         if (sinfo.fPalmStatus==eStatDeleted) sinfo.direction=eSyncDelete;
00838         else sinfo.direction=eSyncPDAToPC;
00839         return true;
00840     }
00841     
00842     
00843     // ---------------------------------------------------------------
00844     // Finally, do the normal case, where both directions are possible
00845     // ---------------------------------------------------------------
00846     
00847     
00848     // if either is deleted, and the other is not changed, delete
00849     if ( ((sinfo.fPCStatus==eStatDeleted) && (sinfo.fPalmStatus!=eStatChanged)) ||
00850          ((sinfo.fPalmStatus==eStatDeleted) && (sinfo.fPCStatus!=eStatChanged)) ) 
00851     {
00852 #ifdef DEBUG
00853         DEBUGCONDUIT<<"DB was deleted on one side and not changed on "
00854             "the other -> Delete it."<<endl;
00855 #endif
00856         sinfo.direction=eSyncDelete;
00857         return true;
00858     }
00859     
00860     // eStatDeleted (and both not changed) have already been treated, for all 
00861     // other values in combination with eStatNone, just copy the texts.
00862     if (sinfo.fPCStatus==eStatNone) {
00863 #ifdef DEBUG
00864         DEBUGCONDUIT<<"PC side has changed!"<<endl;
00865 #endif
00866         sinfo.direction=eSyncPDAToPC;
00867         return true;
00868     }
00869 
00870     if (sinfo.fPalmStatus==eStatNone) {
00871         sinfo.direction=eSyncPCToPDA;
00872         return true;
00873     }
00874     
00875     // All other cases 
00876     //    (deleted,changed), (changed, deleted), (changed,changed) 
00877     // create a conflict:
00878     sinfo.direction=eSyncConflict;
00879     return true;
00880 }
00881 
00882 
00883 
00884 PilotDatabase *DOCConduit::preSyncAction(docSyncInfo &sinfo) const
00885 {
00886     FUNCTIONSETUP;
00887 
00888     {
00889         // make sure the dir for the local texts really exists!
00890         QDir dir(fTXTDir);
00891         if (!dir.exists())
00892         {
00893             dir.mkdir(dir.absPath());
00894         }
00895     }
00896 
00897     DBInfo dbinfo=sinfo.dbinfo;
00898     switch (sinfo.direction)
00899     {
00900         case eSyncPDAToPC:
00901             if (fKeepPDBLocally)
00902             {
00903                 // make sure the dir for the local db really exists!
00904                 QDir dir(fPDBDir);
00905 
00906                 if (!dir.exists())
00907                 {
00908                     dir.mkdir(dir.absPath());
00909                 }
00910 #ifdef DEBUG
00911                 DEBUGCONDUIT<<"Need to fetch database "<<dbinfo.name<<
00912                     " to the directory "<<dir.absPath()<<endl;
00913 #endif
00914                 dbinfo.flags &= ~dlpDBFlagOpen;
00915 
00916                 if (!fHandle->retrieveDatabase(sinfo.pdbfilename, &dbinfo) )
00917                 {
00918                     kdWarning(0)<<"Unable to retrieve database "<<dbinfo.name<<
00919                         " from the handheld into "<<sinfo.pdbfilename<<"."<<endl;
00920                     return 0L;
00921                 }
00922             }
00923             break;
00924         case eSyncPCToPDA:
00925             if (fKeepPDBLocally)
00926             {
00927                 // make sure the dir for the local db really exists!
00928                 QDir dir(fPDBDir);
00929                 if (!dir.exists())
00930                 {
00931                     dir.mkdir(dir.absPath());
00932                 }
00933             }
00934             break;
00935         default:
00936             break;
00937     }
00938     if (fKeepPDBLocally)
00939     {
00940         return new PilotLocalDatabase(fPDBDir, 
00941             QString::fromLatin1(dbinfo.name), false);
00942     }
00943     else
00944     {
00945         return new PilotSerialDatabase(pilotSocket(), 
00946             QString::fromLatin1(dbinfo.name));
00947     }
00948 }
00949 
00950 
00951 // res gives us information whether the sync worked and the db might need to be
00952 // transferred to the handheld or not (and we just need to clean up the mess)
00953 bool DOCConduit::postSyncAction(PilotDatabase * database, 
00954                                 docSyncInfo &sinfo, bool res)
00955 {
00956     FUNCTIONSETUP;
00957     bool rs = true;
00958 
00959     switch (sinfo.direction)
00960     {
00961         case eSyncPDAToPC:
00962             // also reset the sync flags on the handheld
00963 #ifdef DEBUG
00964                 DEBUGCONDUIT<<"Resetting sync flags for database "
00965                     <<sinfo.dbinfo.name<<endl;
00966 #endif
00967             if (fKeepPDBLocally && !fLocalSync) {
00968                 PilotSerialDatabase*db=new PilotSerialDatabase(pilotSocket(), 
00969                     QString::fromLatin1(sinfo.dbinfo.name));
00970 #ifdef DEBUG
00971                 DEBUGCONDUIT<<"Middle 1 Resetting sync flags for database "
00972                     <<sinfo.dbinfo.name<<endl;
00973 #endif
00974                 if (db) {
00975                     db->resetSyncFlags();
00976                     KPILOT_DELETE(db);
00977                 }
00978 #ifdef DEBUG
00979                 DEBUGCONDUIT<<"Middle2 Resetting sync flags for database "
00980                     <<sinfo.dbinfo.name<<endl;
00981 #endif
00982             }
00983 #ifdef DEBUG
00984                 DEBUGCONDUIT<<"End Resetting sync flags for database "
00985                     <<sinfo.dbinfo.name<<endl;
00986 #endif
00987             break;
00988         case eSyncPCToPDA:
00989             if (fKeepPDBLocally && !fLocalSync && res)
00990             {
00991                 // Copy the database to the palm
00992                 PilotLocalDatabase*localdb=dynamic_cast<PilotLocalDatabase*>(database);
00993                 if (localdb)
00994                 {
00995 #ifdef DEBUG
00996                     DEBUGCONDUIT<<"Installing file "<<localdb->dbPathName()<<" ("
00997                         <<sinfo.handheldDB<<") to the handheld"<<endl;
00998 #endif
00999                     QString dbpathname=localdb->dbPathName();
01000                     // This deletes localdb as well, which is just a cast from database
01001                     KPILOT_DELETE(database);
01002                     if (!fHandle->installFiles(dbpathname, false))
01003                     {
01004                         rs = false;
01005 #ifdef DEBUG
01006                         DEBUGCONDUIT<<"Could not install the database "<<dbpathname<<" ("
01007                             <<sinfo.handheldDB<<")"<<endl;
01008 #endif
01009                     }
01010                 }
01011             }
01012         default:
01013             break;
01014     }
01015     
01016 #ifdef DEBUG
01017     DEBUGCONDUIT<<"Vor KPILOT_DELETE(database)"<<endl;
01018 #endif
01019 
01020     KPILOT_DELETE(database);
01021 #ifdef DEBUG
01022     DEBUGCONDUIT<<"End postSyncAction"<<endl;
01023 #endif
01024     return rs;
01025 }
01026 
01027 
01028 
01029 void DOCConduit::cleanup()
01030 {
01031     FUNCTIONSETUP;
01032     
01033     KConfigGroupSaver g(fConfig, DOCConduitFactory::fGroup);
01034     fConfig->writeEntry(DOCConduitFactory::fDOCList, fDBNames);
01035     fConfig->sync();
01036 
01037     emit syncDone(this);
01038 }
01039 
KDE Logo
This file is part of the documentation for kpilot Library Version 3.2.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat May 1 11:36:46 2004 by doxygen 1.2.15 written by Dimitri van Heesch, © 1997-2003