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