You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1019 lines
29 KiB
1019 lines
29 KiB
/* 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 kde-pim@kde.org.
|
|
*/
|
|
|
|
|
|
// 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);
|
|
}
|
|
|