diff options
Diffstat (limited to 'kpilot/conduits/docconduit/doc-conduit.cc')
-rw-r--r-- | kpilot/conduits/docconduit/doc-conduit.cc | 1018 |
1 files changed, 1018 insertions, 0 deletions
diff --git a/kpilot/conduits/docconduit/doc-conduit.cc b/kpilot/conduits/docconduit/doc-conduit.cc new file mode 100644 index 000000000..eee36080c --- /dev/null +++ b/kpilot/conduits/docconduit/doc-conduit.cc @@ -0,0 +1,1018 @@ +/* KPilot +** +** Copyright (C) 2002 by Reinhold Kainhofer +** +** The doc conduit synchronizes text files on the PC with DOC databases on the Palm +*/ + +/* +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program in a file called COPYING; if not, write to +** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +** MA 02110-1301, USA. +*/ + +/* +** Bug reports and questions can be sent to [email protected]. +*/ + + +// naming of the bookmark file: +// PDB->TXT: convert bookmarks to a .bm file +// TXT->PDB: If a .bmk file exists, use it, otherwise use the .bm file (from the PDB->TXT conversion) +// This way, the bookmark file is not overwritten, a manual bookmark file overrides, but the bookmarks from the handheld are still available + + +#include "options.h" +#include "doc-conduit.moc" + +#include <qtimer.h> +#include <qdir.h> + +#include <kconfig.h> +#include <kmdcodec.h> + +#include <pilotLocalDatabase.h> +#include <pilotSerialDatabase.h> + +#include "doc-factory.h" +#include "doc-conflictdialog.h" +#include "DOC-converter.h" +#include "pilotDOCHead.h" +#include "docconduitSettings.h" + + +// Something to allow us to check what revision +// the modules are that make up a binary distribution. +extern "C" +{ +unsigned long version_conduit_doc = Pilot::PLUGIN_API; +} + +QString dirToString(eSyncDirectionEnum dir) { + switch(dir) { +// case eSyncAll: return "eSyncAll"; + case eSyncPDAToPC: return CSL1("eSyncPDAToPC"); + case eSyncPCToPDA: return CSL1("eSyncPCToPDA"); + case eSyncNone: return CSL1("eSyncNone"); + case eSyncConflict: return CSL1("eSyncConflict"); + case eSyncDelete: return CSL1("eSyncDelete"); + default: return CSL1("ERROR"); + } +} + + +/********************************************************************* + C O N S T R U C T O R + *********************************************************************/ + + +DOCConduit::DOCConduit(KPilotLink * o, + const char *n, const QStringList & a):ConduitAction(o, n, a) +{ + FUNCTIONSETUP; + fConduitName=i18n("DOC"); +} + + + +DOCConduit::~DOCConduit() +{ + FUNCTIONSETUP; +} + + +bool DOCConduit::isCorrectDBTypeCreator(DBInfo dbinfo) { + return dbinfo.type == dbtype() && dbinfo.creator == dbcreator(); +} +const unsigned long DOCConduit::dbtype() { + return get_long(DOCConduitFactory::dbDOCtype); +} +const unsigned long DOCConduit::dbcreator() { + return get_long(DOCConduitFactory::dbDOCcreator); +} + + + +/********************************************************************* + L O A D I N G T H E D A T A + *********************************************************************/ + + + +void DOCConduit::readConfig() +{ + FUNCTIONSETUP; + DOCConduitSettings::self()->readConfig(); + + eConflictResolution = (enum eSyncDirectionEnum) (DOCConduitSettings::conflictResolution() ); + fTXTBookmarks = DOCConverter::eBmkNone; + if ( DOCConduitSettings::convertBookmarks() ) + { + if ( DOCConduitSettings::bmkFileBookmarks() ) + fTXTBookmarks |= DOCConverter::eBmkFile; + if ( DOCConduitSettings::inlineBookmarks() ) + fTXTBookmarks |= DOCConverter::eBmkInline; + if ( DOCConduitSettings::endtagBookmarks() ) + fTXTBookmarks |= DOCConverter::eBmkEndtags; + } + + eSyncDirection = (enum eSyncDirectionEnum)(DOCConduitSettings::syncDirection() ); + +#ifdef DEBUG + DEBUGKPILOT << fname + << ": Settings " + << " tXTDirectory=" << DOCConduitSettings::tXTDirectory() + << " pDBDirectory=" << DOCConduitSettings::pDBDirectory() + << " keepPDBLocally=" << DOCConduitSettings::keepPDBsLocally() + << " eConflictResolution=" << eConflictResolution + << " tXTBookmarks=" << fTXTBookmarks + << " pDBBookmarks=" << DOCConduitSettings::bookmarksToPC() + << " compress=" << DOCConduitSettings::compress() + << " eSyncDirection=" << eSyncDirection << endl; +#endif +} + + + +bool DOCConduit::pcTextChanged(QString txtfn) +{ + FUNCTIONSETUP; + // How do I find out if a text file has changed shince we last synced it?? + // Use KMD5 for now. If I realize it is too slow, then I have to go back to comparing modification times + // 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. + QString oldDigest=DOCConduitSettings::self()->config()->readEntry(txtfn); + if (oldDigest.length()<=0) + { + return true; + } +#ifdef DEBUG + DEBUGKPILOT<<"Old digest is "<<oldDigest<<endl; +#endif + + KMD5 docmd5; + QFile txtfile(txtfn); + if (txtfile.open(IO_ReadOnly)){ + docmd5.update(txtfile); + QString thisDigest(docmd5.hexDigest() /* .data() */); +#ifdef DEBUG + DEBUGKPILOT<<"New digest is "<<thisDigest<<endl; +#endif + return (thisDigest.length()<=0) || (thisDigest!=oldDigest); + } else { + // File does not exist. This should actually never happen. Anyways, just return true to indicate it has changed. + // doSync should detect this and delete the doc from the handheld. + return true; + } + return false; +} + + + +bool DOCConduit::hhTextChanged(PilotDatabase*docdb) +{ + FUNCTIONSETUP; + if (!docdb) return false; + + PilotRecord *firstRec = docdb->readRecordByIndex(0); + PilotDOCHead docHeader(firstRec); + KPILOT_DELETE(firstRec); + + int storyRecs = docHeader.numRecords; + + // determine the index of the next modified record (does it lie + // beyond the actual text records?) + int modRecInd=-1; + PilotRecord*modRec=docdb->readNextModifiedRec(&modRecInd); +#ifdef DEBUG + DEBUGKPILOT<<"Index of first changed record: "<<modRecInd<<endl; +#endif + + KPILOT_DELETE(modRec); + // if the header record was changed, find out which is the first changed + // real document record: + if (modRecInd==0) { + modRec=docdb->readNextModifiedRec(&modRecInd); +#ifdef DEBUG + DEBUGKPILOT<<"Reread Index of first changed records: "<<modRecInd<<endl; +#endif + KPILOT_DELETE(modRec); + } + + // The record index starts with 0, so only a negative number means + // no modified record was found + if (modRecInd >= 0) { +#ifdef DEBUG + DEBUGKPILOT<<"Handheld side has changed, condition="<< + ((!DOCConduitSettings::ignoreBmkChanges()) || (modRecInd <= storyRecs))<<endl; +#endif + if ((!DOCConduitSettings::ignoreBmkChanges()) || (modRecInd <= storyRecs)) + return true; + } else { +#ifdef DEBUG + DEBUGKPILOT<<"Handheld side has NOT changed!"<<endl; +#endif + return false; + } + return false; +} + + + +/********************************************************************* + * Helper functions + ********************************************************************/ + +QString DOCConduit::constructPDBFileName(QString name) { + FUNCTIONSETUP; + QString fn; + QDir dr(DOCConduitSettings::pDBDirectory()); + QFileInfo pth(dr, name); + if (!name.isEmpty()) fn=pth.absFilePath()+CSL1(".pdb"); + return fn; +} +QString DOCConduit::constructTXTFileName(QString name) { + FUNCTIONSETUP; + QString fn; + QDir dr( DOCConduitSettings::tXTDirectory() ); + QFileInfo pth(dr, name); + if (!name.isEmpty()) fn=pth.absFilePath()+CSL1(".txt"); + return fn; +} + + + + + +/********************************************************************* + S Y N C S T R U C T U R E + *********************************************************************/ + + + + + +/* virtual */ bool DOCConduit::exec() +{ + FUNCTIONSETUP; + + readConfig(); + dbnr=0; + + emit logMessage(i18n("Searching for texts and databases to synchronize")); + + QTimer::singleShot(0, this, SLOT(syncNextDB())); + return true; +} + + + +bool DOCConduit::doSync(docSyncInfo &sinfo) +{ + FUNCTIONSETUP; + bool res=false; + + if (sinfo.direction==eSyncDelete) { + if (!sinfo.txtfilename.isEmpty()) { + if (!QFile::remove(sinfo.txtfilename)) { + WARNINGKPILOT << "Unable to delete the text file " << sinfo.txtfilename << " on the PC" << endl; + } + QString bmkfilename = sinfo.txtfilename; + if (bmkfilename.endsWith(CSL1(".txt"))){ + bmkfilename.remove(bmkfilename.length()-4, 4); + } + bmkfilename+=CSL1(PDBBMK_SUFFIX); + if (!QFile::remove(bmkfilename)) { +#ifdef DEBUG + DEBUGKPILOT<<"Could not remove bookmarks file "<<bmkfilename<<" for database "<<sinfo.handheldDB<<endl; +#endif + } + } + if (!sinfo.pdbfilename.isEmpty() && DOCConduitSettings::keepPDBsLocally() ) { + PilotLocalDatabase*database=new PilotLocalDatabase(DOCConduitSettings::pDBDirectory(), + QString::fromLatin1(sinfo.dbinfo.name), false); + if (database) { + if ( database->deleteDatabase() !=0 ) { + WARNINGKPILOT << "Unable to delete database " << sinfo.dbinfo.name << " on the PC" << endl; + } + KPILOT_DELETE(database); + } + } + if (!DOCConduitSettings::localSync()) { + PilotDatabase *database=deviceLink()->database( sinfo.dbinfo.name ); + if ( database->deleteDatabase() !=0 ) { + WARNINGKPILOT << "Unable to delete database " << sinfo.dbinfo.name << " from the handheld" << endl; + } + KPILOT_DELETE(database); + } + return true; + } + // preSyncAction should initialize the custom databases/files for the + // specific action chosen for this db and return a pointer to a docDBInfo + // instance which points either to a local database or a database on the handheld. + PilotDatabase *database = preSyncAction(sinfo); + + if (database && ( !database->isOpen() ) ) { +#ifdef DEBUG + DEBUGKPILOT<<"Database "<<sinfo.dbinfo.name<<" does not yet exist. Creating it:"<<endl; +#endif + if (!database->createDatabase(dbcreator(), dbtype()) ) { +#ifdef DEBUG + DEBUGKPILOT<<"Failed"<<endl; + emit logMessage(i18n("Database created.")); +#endif + } + } + + if (database && database->isOpen()) { + DOCConverter docconverter; + connect(&docconverter, SIGNAL(logError(const QString &)), SIGNAL(logError(const QString &))); + connect(&docconverter, SIGNAL(logMessage(const QString &)), SIGNAL(logMessage(const QString &))); + + docconverter.setTXTpath( DOCConduitSettings::tXTDirectory(), sinfo.txtfilename ); + docconverter.setPDB(database); + docconverter.setCompress(DOCConduitSettings::compress()); + + switch (sinfo.direction) { + case eSyncPDAToPC: + docconverter.setBookmarkTypes(DOCConduitSettings::bookmarksToPC()); + res = docconverter.convertPDBtoTXT(); + break; + case eSyncPCToPDA: + docconverter.setBookmarkTypes(fTXTBookmarks); + res = docconverter.convertTXTtoPDB(); + break; + default: + break; + } + + // Now calculate the md5 checksum of the PC text and write it to the config file + if (res) + { + KMD5 docmd5; + QFile txtfile(docconverter.txtFilename()); + if (txtfile.open(IO_ReadOnly)) { + docmd5.update(txtfile); + QString thisDigest(docmd5.hexDigest() /* .data() */); + DOCConduitSettings::self()->config()->writeEntry(docconverter.txtFilename(), thisDigest); + DOCConduitSettings::self()->config()->sync(); +#ifdef DEBUG + DEBUGKPILOT<<"MD5 Checksum of the text "<<sinfo.txtfilename<<" is "<<thisDigest<<endl; +#endif + } else { +#ifdef DEBUG + DEBUGKPILOT<<"couldn't open file "<<docconverter.txtFilename()<<" for reading!!!"<<endl; +#endif + } + } + + if (!postSyncAction(database, sinfo, res)) + emit logError(i18n("Unable to install the locally created PalmDOC %1 to the handheld.") + .arg(QString::fromLatin1(sinfo.dbinfo.name))); + if (!res) + emit logError(i18n("Conversion of PalmDOC \"%1\" failed.") + .arg(QString::fromLatin1(sinfo.dbinfo.name))); +// disconnect(&docconverter, SIGNAL(logError(const QString &)), SIGNAL(logError(const QString &))); +// disconnect(&docconverter, SIGNAL(logMessage(const QString &)), SIGNAL(logMessage(const QString &))); +// KPILOT_DELETE(database); + } + else + { + emit logError(i18n("Unable to open or create the database %1.") + .arg(QString::fromLatin1(sinfo.dbinfo.name))); + } + return res; +} + + +/** syncNextDB walks through all PalmDoc databases on the handheld and decides if they are supposed to be synced to the PC. + * syncNextDB and syncNextTXT fist build the list of all PalmDoc texts, and then the method syncDatabases does the actual sync. */ +void DOCConduit::syncNextDB() { + FUNCTIONSETUP; + DBInfo dbinfo; + + if (eSyncDirection==eSyncPCToPDA || fHandle->findDatabase(NULL, &dbinfo, dbnr, dbtype(), dbcreator() /*, cardno */ ) < 0) + { + // no more databases available, so check for PC->Palm sync + QTimer::singleShot(0, this, SLOT(syncNextTXT())); + return; + } + dbnr=dbinfo.index+1; +#ifdef DEBUG + DEBUGKPILOT<<"Next Palm database to sync: "<<dbinfo.name<<", Index="<<dbinfo.index<<endl; +#endif + + // if creator and/or type don't match, go to next db + if (!isCorrectDBTypeCreator(dbinfo) || + fDBNames.contains(QString::fromLatin1(dbinfo.name))) + { + QTimer::singleShot(0, this, SLOT(syncNextDB())); + return; + } + + QString txtfilename=constructTXTFileName(QString::fromLatin1(dbinfo.name)); + QString pdbfilename=constructPDBFileName(QString::fromLatin1(dbinfo.name)); + + docSyncInfo syncInfo(QString::fromLatin1(dbinfo.name), + txtfilename, pdbfilename, eSyncNone); + syncInfo.dbinfo=dbinfo; + needsSync(syncInfo); + fSyncInfoList.append(syncInfo); + fDBNames.append(QString::fromLatin1(dbinfo.name)); + + QTimer::singleShot(0, this, SLOT(syncNextDB())); + return; +} + + + +void DOCConduit::syncNextTXT() +{ + FUNCTIONSETUP; + + if (eSyncDirection==eSyncPDAToPC ) + { + // We don't sync from PC to PDB, so start the conflict resolution and then the actual sync process + docnames.clear(); + QTimer::singleShot(0, this, SLOT(checkPDBFiles())); + return; + } + + // if docnames isn't initialized, get a list of all *.txt files in DOCConduitSettings::tXTDirectory() + if (docnames.isEmpty()/* || dociterator==docnames.end() */) { + docnames=QDir( DOCConduitSettings::tXTDirectory(), CSL1("*.txt")).entryList() ; + dociterator=docnames.begin(); + } + if (dociterator==docnames.end()) { + // no more databases available, so start the conflict resolution and then the actual sync proces + docnames.clear(); + QTimer::singleShot(0, this, SLOT(checkPDBFiles())); + return; + } + + QString fn=(*dociterator); + + QDir dr( DOCConduitSettings::tXTDirectory() ); + QFileInfo fl(dr, fn ); + QString txtfilename=fl.absFilePath(); + QString pdbfilename; + ++dociterator; + + DBInfo dbinfo; + // Include all "extensions" except the last. This allows full stops inside the database name (e.g. abbreviations) + // first fill everything with 0, so we won't have a buffer overflow. + memset(&dbinfo.name[0], 0, 33); + strncpy(&dbinfo.name[0], fl.baseName(TRUE).latin1(), 30); + + bool alreadySynced=fDBNames.contains(fl.baseName(TRUE)); + if (!alreadySynced) { + docSyncInfo syncInfo(QString::fromLatin1(dbinfo.name), + txtfilename, pdbfilename, eSyncNone); + syncInfo.dbinfo=dbinfo; + needsSync(syncInfo); + fSyncInfoList.append(syncInfo); + fDBNames.append(QString::fromLatin1(dbinfo.name)); + } else { +#ifdef DEBUG + DEBUGKPILOT<<txtfilename<<" has already been synced, skipping it."<<endl; +#endif + } + + QTimer::singleShot(0, this, SLOT(syncNextTXT())); + return; +} + + + +/** This slot will only be used if DOCConduitSettings::keepPDBsLocally() to check if new doc databases have been copied to the pdb directory. + * If so, install it to the handheld and sync it to the PC */ +void DOCConduit::checkPDBFiles() { + FUNCTIONSETUP; + + if ( DOCConduitSettings::localSync() || !DOCConduitSettings::keepPDBsLocally() || eSyncDirection==eSyncPCToPDA ) + { + // no more databases available, so check for PC->Palm sync + QTimer::singleShot(0, this, SLOT(checkDeletedDocs())); + return; + } + + // Walk through all files in the pdb directory and check if it has already been synced. + // if docnames isn't initialized, get a list of all *.pdb files in DOCConduitSettings::pDBDirectory() + if (docnames.isEmpty()/* || dociterator==docnames.end() */) { + docnames=QDir(DOCConduitSettings::pDBDirectory(), CSL1("*.pdb")).entryList() ; + dociterator=docnames.begin(); + } + if (dociterator==docnames.end()) { + // no more databases available, so start the conflict resolution and then the actual sync proces + docnames.clear(); + QTimer::singleShot(0, this, SLOT(checkDeletedDocs())); + return; + } + + QString fn=(*dociterator); + + QDir dr(DOCConduitSettings::pDBDirectory()); + QFileInfo fl(dr, fn ); + QString pdbfilename=fl.absFilePath(); + ++dociterator; + + // Get the doc title and check if it has already been synced (in the synced docs list of in fDBNames to be synced) + // 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. + QString dbname=fl.baseName(TRUE).left(30); + if (!fDBNames.contains(dbname) && !fDBListSynced.contains(dbname)) { + if (fHandle->installFiles(pdbfilename, false)) { + DBInfo dbinfo; + // Include all "extensions" except the last. This allows full stops inside the database name (e.g. abbreviations) + // first fill everything with 0, so we won't have a buffer overflow. + memset(&dbinfo.name[0], 0, 33); + strncpy(&dbinfo.name[0], dbname.latin1(), 30); + + docSyncInfo syncInfo(dbname, constructTXTFileName(dbname), pdbfilename, eSyncNone); + syncInfo.dbinfo=dbinfo; + needsSync(syncInfo); + fSyncInfoList.append(syncInfo); + fDBNames.append(dbname); + } else { +#ifdef DEBUG + DEBUGKPILOT<<"Could not install database "<<dbname<<" ("<<pdbfilename<<") to the handheld"<<endl; +#endif + } + } + + QTimer::singleShot(0, this, SLOT(checkPDBFiles())); +} + + + +void DOCConduit::checkDeletedDocs() +{ + FUNCTIONSETUP; + + for (QStringList::Iterator it=fDBListSynced.begin(); it!=fDBListSynced.end(); ++it ) { + if (!fDBNames.contains(*it)) { + // We need to delete this doc: + QString dbname(*it); + QString txtfilename=constructTXTFileName(dbname); + QString pdbfilename=constructPDBFileName(dbname); + docSyncInfo syncInfo(dbname, txtfilename, pdbfilename, eSyncDelete); + + DBInfo dbinfo; + memset(&dbinfo.name[0], 0, 33); + strncpy(&dbinfo.name[0], dbname.latin1(), 30); + syncInfo.dbinfo=dbinfo; + + fSyncInfoList.append(syncInfo); + } + } + QTimer::singleShot(0, this, SLOT(resolve())); + return; +} + + + +void DOCConduit::resolve() { + FUNCTIONSETUP; + + for (fSyncInfoListIterator=fSyncInfoList.begin(); fSyncInfoListIterator!=fSyncInfoList.end(); ++fSyncInfoListIterator) { + // Walk through each database and apply the conflictResolution option. + // the remaining conflicts will be resolved in the resolution dialog + if ((*fSyncInfoListIterator).direction==eSyncConflict){ +#ifdef DEBUG + DEBUGKPILOT<<"We have a conflict for "<<(*fSyncInfoListIterator).handheldDB<<", default="<<eConflictResolution<<endl; +#endif + switch (eConflictResolution) + { + case eSyncPDAToPC: +#ifdef DEBUG + DEBUGKPILOT<<"PDA overrides for database "<<(*fSyncInfoListIterator).handheldDB<<endl; +#endif + (*fSyncInfoListIterator).direction = eSyncPDAToPC; + break; + case eSyncPCToPDA: +#ifdef DEBUG + DEBUGKPILOT<<"PC overrides for database "<<(*fSyncInfoListIterator).handheldDB<<endl; +#endif + (*fSyncInfoListIterator).direction = eSyncPCToPDA; + break; + case eSyncNone: +#ifdef DEBUG + DEBUGKPILOT<<"No sync for database "<<(*fSyncInfoListIterator).handheldDB<<endl; +#endif + (*fSyncInfoListIterator).direction = eSyncNone; + break; + case eSyncDelete: + case eSyncConflict: + default: +#ifdef DEBUG + DEBUGKPILOT<<"Conflict remains due to default resolution setting for database "<<(*fSyncInfoListIterator).handheldDB<<endl; +#endif + break; + } + } + } + + // Show the conflict resolution dialog and ask for the action for each database + ResolutionDialog*dlg=new ResolutionDialog( 0, i18n("Conflict Resolution"), &fSyncInfoList , fHandle); + bool show=DOCConduitSettings::alwaysShowResolutionDialog() || (dlg && dlg->hasConflicts); + if (show) { + if (!dlg || !dlg->exec() ) { + KPILOT_DELETE(dlg) + emit logMessage(i18n("Sync aborted by user.")); + QTimer::singleShot(0, this, SLOT(cleanup())); + return; + } + } + KPILOT_DELETE(dlg) + + + // 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 + fDBNames.clear(); + fSyncInfoListIterator=fSyncInfoList.begin(); + QTimer::singleShot(0,this, SLOT(syncDatabases())); + return; +} + + + +void DOCConduit::syncDatabases() { + FUNCTIONSETUP; + if (fSyncInfoListIterator==fSyncInfoList.end()) { + // We're done, so clean up + QTimer::singleShot(0, this, SLOT(cleanup())); + return; + } + + docSyncInfo sinfo=(*fSyncInfoListIterator); + ++fSyncInfoListIterator; + + switch (sinfo.direction) { + case eSyncConflict: +#ifdef DEBUG + DEBUGKPILOT<<"Entry "<<sinfo.handheldDB<<"( txtfilename: "<<sinfo.txtfilename<< + ", pdbfilename: "<<sinfo.pdbfilename<<") had sync direction eSyncConflict!!!"<<endl; +#endif + break; + case eSyncDelete: + case eSyncPDAToPC: + case eSyncPCToPDA: + emit logMessage(i18n("Synchronizing text \"%1\"").arg(sinfo.handheldDB)); + if (!doSync(sinfo)) { + // The sync could not be done, so inform the user (the error message should probably issued inside doSync) +#ifdef DEBUG + DEBUGKPILOT<<"There was some error syncing the text \""<<sinfo.handheldDB<<"\" with the file "<<sinfo.txtfilename<<endl; +#endif + } + break; + case eSyncNone: +// case eSyncAll: + break; + } + if (sinfo.direction != eSyncDelete) fDBNames.append(sinfo.handheldDB); + + QTimer::singleShot(0,this, SLOT(syncDatabases())); + return; +} + + +PilotDatabase*DOCConduit::openDOCDatabase(const QString &dbname) { + if (DOCConduitSettings::localSync()) + { + return new PilotLocalDatabase(DOCConduitSettings::pDBDirectory(), dbname, false); + } + else + { + return deviceLink()->database( dbname ); + } +} + + +bool DOCConduit::needsSync(docSyncInfo &sinfo) +{ + FUNCTIONSETUP; + sinfo.direction = eSyncNone; + + PilotDatabase*docdb=openDOCDatabase(QString::fromLatin1(sinfo.dbinfo.name)); + if (!fDBListSynced.contains(sinfo.handheldDB)) { + // the database wasn't included on last sync, so it has to be new. +#ifdef DEBUG + DEBUGKPILOT<<"Database "<<sinfo.dbinfo.name<<" wasn't included in the previous sync!"<<endl; +#endif + + /* Resolution Table: + PC HH | normal PC->HH HH->PC + ----------------------------------------- + N - | P P D + - N | H D H + N N | C P H + */ + + if (QFile::exists(sinfo.txtfilename)) sinfo.fPCStatus=eStatNew; + else sinfo.fPCStatus=eStatDoesntExist; + if (docdb && docdb->isOpen()) sinfo.fPalmStatus=eStatNew; + else sinfo.fPalmStatus=eStatDoesntExist; + KPILOT_DELETE(docdb); + + switch (eSyncDirection) { + case eSyncPDAToPC: + if (sinfo.fPalmStatus==eStatDoesntExist) + sinfo.direction=eSyncDelete; + else sinfo.direction=eSyncPDAToPC; + break; + case eSyncPCToPDA: + if (sinfo.fPCStatus==eStatDoesntExist) + sinfo.direction=eSyncDelete; + else sinfo.direction=eSyncPCToPDA; + break; + case eSyncNone: // means actually both directions! + if (sinfo.fPCStatus==eStatNew) { + if (sinfo.fPalmStatus==eStatNew) sinfo.direction=eSyncConflict; + else sinfo.direction=eSyncPCToPDA; + } else { + if (sinfo.fPalmStatus==eStatNew) sinfo.direction=eSyncPDAToPC; + else { + sinfo.direction=eSyncNone; +#ifdef DEBUG + DEBUGKPILOT<<"I'm supposed to find a sync direction, but the "<< + " text "<<sinfo.dbinfo.name<<" doesn't exist on either "<< + " the handheld or the PC"<<endl; +#endif + } + } + break; + default: + break; + } + return true; + } + + // Text was included in the last sync + if (!QFile::exists(sinfo.txtfilename)) sinfo.fPCStatus=eStatDeleted; + else if(pcTextChanged(sinfo.txtfilename)) { + sinfo.fPCStatus=eStatChanged; +#ifdef DEBUG + DEBUGKPILOT<<"PC side has changed!"<<endl; +#endif + // TODO: Check for changed bookmarks on the PC side +#ifdef DEBUG + } else { + DEBUGKPILOT<<"PC side has NOT changed!"<<endl; +#endif + } + + if (!docdb || !docdb->isOpen()) sinfo.fPalmStatus=eStatDeleted; + else if (hhTextChanged(docdb)) { +#ifdef DEBUG + DEBUGKPILOT<<"Handheld side has changed!"<<endl; +#endif + sinfo.fPalmStatus=eStatChanged; +#ifdef DEBUG + } else { + DEBUGKPILOT<<"Handheld side has NOT changed!"<<endl; +#endif + } + KPILOT_DELETE(docdb); + + + // Now that we know the status of both sides, determine what to do. + /* Resolution Table: + PC HH | normal PC->HH HH->PC + ----------------------------------------- + - - | - - - + C - | P P H + - C | H P H + C C | C P H + D - | D D H + - D | D P D + D D | D D D + ----------------------------------------- + C D | C P D + D C | C D H + */ + + + if (sinfo.fPCStatus == eStatNone && sinfo.fPalmStatus==eStatNone) { +#ifdef DEBUG + DEBUGKPILOT<<"Nothing has changed, not need for a sync."<<endl; +#endif + sinfo.direction=eSyncNone; + return false; + } + + // In all other cases, if only one direction (PC->HH or HH->PC) + // should be done, check if the DB was deleted or if we are supposed + // to sync that direction + + if (eSyncDirection==eSyncPCToPDA) { + if (sinfo.fPCStatus==eStatDeleted) sinfo.direction=eSyncDelete; + else sinfo.direction=eSyncPCToPDA; + return true; + } + if (eSyncDirection==eSyncPDAToPC) { + if (sinfo.fPalmStatus==eStatDeleted) sinfo.direction=eSyncDelete; + else sinfo.direction=eSyncPDAToPC; + return true; + } + + + // --------------------------------------------------------------- + // Finally, do the normal case, where both directions are possible + // --------------------------------------------------------------- + + + // if either is deleted, and the other is not changed, delete + if ( ((sinfo.fPCStatus==eStatDeleted) && (sinfo.fPalmStatus!=eStatChanged)) || + ((sinfo.fPalmStatus==eStatDeleted) && (sinfo.fPCStatus!=eStatChanged)) ) + { +#ifdef DEBUG + DEBUGKPILOT<<"DB was deleted on one side and not changed on " + "the other -> Delete it."<<endl; +#endif + sinfo.direction=eSyncDelete; + return true; + } + + // eStatDeleted (and both not changed) have already been treated, for all + // other values in combination with eStatNone, just copy the texts. + if (sinfo.fPCStatus==eStatNone) { +#ifdef DEBUG + DEBUGKPILOT<<"PC side has changed!"<<endl; +#endif + sinfo.direction=eSyncPDAToPC; + return true; + } + + if (sinfo.fPalmStatus==eStatNone) { + sinfo.direction=eSyncPCToPDA; + return true; + } + + // All other cases + // (deleted,changed), (changed, deleted), (changed,changed) + // create a conflict: + sinfo.direction=eSyncConflict; + return true; +} + + + +PilotDatabase *DOCConduit::preSyncAction(docSyncInfo &sinfo) const +{ + FUNCTIONSETUP; + + { + // make sure the dir for the local texts really exists! + QDir dir( DOCConduitSettings::tXTDirectory() ); + if (!dir.exists()) + { + dir.mkdir(dir.absPath()); + } + } + + DBInfo dbinfo=sinfo.dbinfo; + switch (sinfo.direction) + { + case eSyncPDAToPC: + if (DOCConduitSettings::keepPDBsLocally()) + { + // make sure the dir for the local db really exists! + QDir dir(DOCConduitSettings::pDBDirectory()); + + if (!dir.exists()) + { + dir.mkdir(dir.absPath()); + } +#ifdef DEBUG + DEBUGKPILOT<<"Need to fetch database "<<dbinfo.name<< + " to the directory "<<dir.absPath()<<endl; +#endif + dbinfo.flags &= ~dlpDBFlagOpen; + + if (!fHandle->retrieveDatabase(sinfo.pdbfilename, &dbinfo) ) + { + WARNINGKPILOT << "Unable to retrieve database " << dbinfo.name << + " from the handheld into " << sinfo.pdbfilename << "." << endl; + return 0L; + } + } + break; + case eSyncPCToPDA: + if (DOCConduitSettings::keepPDBsLocally()) + { + // make sure the dir for the local db really exists! + QDir dir(DOCConduitSettings::pDBDirectory()); + if (!dir.exists()) + { + dir.mkdir(dir.absPath()); + } + } + break; + default: + break; + } + if (DOCConduitSettings::keepPDBsLocally()) + { + return new PilotLocalDatabase(DOCConduitSettings::pDBDirectory(), + QString::fromLatin1(dbinfo.name), false); + } + else + { + return deviceLink()->database(QString::fromLatin1(dbinfo.name)); + } +} + + +// res gives us information whether the sync worked and the db might need to be +// transferred to the handheld or not (and we just need to clean up the mess) +bool DOCConduit::postSyncAction(PilotDatabase * database, + docSyncInfo &sinfo, bool res) +{ + FUNCTIONSETUP; + bool rs = true; + + switch (sinfo.direction) + { + case eSyncPDAToPC: + // also reset the sync flags on the handheld +#ifdef DEBUG + DEBUGKPILOT<<"Resetting sync flags for database " + <<sinfo.dbinfo.name<<endl; +#endif + if (DOCConduitSettings::keepPDBsLocally() && !DOCConduitSettings::localSync()) + { + PilotDatabase*db=deviceLink()->database( + QString::fromLatin1(sinfo.dbinfo.name)); +#ifdef DEBUG + DEBUGKPILOT<<"Middle 1 Resetting sync flags for database " + <<sinfo.dbinfo.name<<endl; +#endif + if (db) + { + db->resetSyncFlags(); + KPILOT_DELETE(db); + } + } +#ifdef DEBUG + DEBUGKPILOT<<"End Resetting sync flags for database " + <<sinfo.dbinfo.name<<endl; +#endif + break; + case eSyncPCToPDA: + if (DOCConduitSettings::keepPDBsLocally() && !DOCConduitSettings::localSync() && res) + { + // Copy the database to the palm + PilotLocalDatabase*localdb=dynamic_cast<PilotLocalDatabase*>(database); + if (localdb) + { +#ifdef DEBUG + DEBUGKPILOT<<"Installing file "<<localdb->dbPathName()<<" (" + <<sinfo.handheldDB<<") to the handheld"<<endl; +#endif + QString dbpathname=localdb->dbPathName(); + // This deletes localdb as well, which is just a cast from database + KPILOT_DELETE(database); + if (!fHandle->installFiles(dbpathname, false)) + { + rs = false; +#ifdef DEBUG + DEBUGKPILOT<<"Could not install the database "<<dbpathname<<" (" + <<sinfo.handheldDB<<")"<<endl; +#endif + } + } + } + default: + break; + } + +#ifdef DEBUG + DEBUGKPILOT<<"Vor KPILOT_DELETE(database)"<<endl; +#endif + + KPILOT_DELETE(database); +#ifdef DEBUG + DEBUGKPILOT<<"End postSyncAction"<<endl; +#endif + return rs; +} + + + +void DOCConduit::cleanup() +{ + FUNCTIONSETUP; + DOCConduitSettings::setConvertedDOCfiles( fDBNames ); + DOCConduitSettings::self()->writeConfig(); + + emit syncDone(this); +} + |