00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033 static const char *hotsync_id =
00034 "$Id: hotSync.cc 449697 2005-08-16 13:19:47Z adridg $";
00035
00036 #include "options.h"
00037
00038 #include <time.h>
00039 #include <unistd.h>
00040 #include <stdio.h>
00041
00042 #include <pi-file.h>
00043
00044 #include <qtimer.h>
00045 #include <qfile.h>
00046 #include <qfileinfo.h>
00047 #include <qdir.h>
00048 #include <qvaluelist.h>
00049 #include <qregexp.h>
00050 #include <qtextcodec.h>
00051 #include <qstringlist.h>
00052
00053 #include <kglobal.h>
00054 #include <kstandarddirs.h>
00055 #include <kapplication.h>
00056
00057 #include "pilotUser.h"
00058 #include "pilotAppCategory.h"
00059 #include "syncStack.h"
00060 #include "pilotSerialDatabase.h"
00061 #include "pilotLocalDatabase.h"
00062 #include "pilotDatabase.h"
00063 #include "kpilotSettings.h"
00064
00065 #include "hotSync.moc"
00066
00067 TestLink::TestLink(KPilotDeviceLink * p) :
00068 SyncAction(p, "testLink")
00069 {
00070 FUNCTIONSETUP;
00071
00072 (void) hotsync_id;
00073 }
00074
00075 bool TestLink::exec()
00076 {
00077 FUNCTIONSETUP;
00078
00079 int i;
00080 int dbindex = 0;
00081 int count = 0;
00082 struct DBInfo db;
00083
00084 addSyncLogEntry(i18n("Testing.\n"));
00085
00086 #ifdef BRUTE_FORCE
00087 for (i=0; i<32; i++)
00088 #else
00089 while ((i = fHandle->getNextDatabase(dbindex,&db)) > 0)
00090 #endif
00091 {
00092 #ifdef BRUTE_FORCE
00093 if (fHandle->getNextDatabase(i,&db) < 1)
00094 {
00095 DEBUGCONDUIT << fname << ": No database index " << i << endl;
00096 continue;
00097 }
00098 #endif
00099
00100 count++;
00101 dbindex = db.index + 1;
00102
00103 #ifdef DEBUG
00104 DEBUGCONDUIT << fname << ": Read database " << db.name << endl;
00105 #endif
00106
00107
00108 openConduit();
00109
00110
00111 emit logMessage(i18n("Syncing database %1...")
00112 .arg(PilotAppCategory::codec()->toUnicode(db.name)));
00113
00114 kapp->processEvents();
00115 }
00116
00117 emit logMessage(i18n("HotSync finished."));
00118 emit syncDone(this);
00119 return true;
00120 }
00121
00122 BackupAction::BackupAction(KPilotDeviceLink * p, bool full) :
00123 SyncAction(p, "backupAction"),
00124 fFullBackup(full)
00125 {
00126 FUNCTIONSETUP;
00127
00128 fDatabaseDir = KGlobal::dirs()->saveLocation("data",
00129 CSL1("kpilot/DBBackup/"));
00130
00131 #ifdef DEBUG
00132 DEBUGCONDUIT << fname << ": Will write to " << fDatabaseDir << endl;
00133 DEBUGCONDUIT << fname << ": Full sync? " << full << endl;
00134 #endif
00135 }
00136
00137 QString BackupAction::statusString() const
00138 {
00139 FUNCTIONSETUP;
00140 QString s(CSL1("BackupAction="));
00141
00142 switch (status())
00143 {
00144 case Init:
00145 s.append(CSL1("Init"));
00146 break;
00147 case Error:
00148 s.append(CSL1("Error"));
00149 break;
00150 case FullBackup:
00151 s.append(CSL1("FullBackup"));
00152 break;
00153 case FastBackup:
00154 s.append(CSL1("FastBackup"));
00155 break;
00156 case BackupEnded:
00157 s.append(CSL1("BackupEnded"));
00158 break;
00159 case BackupIncomplete:
00160 s.append(CSL1("BackupIncomplete"));
00161 break;
00162 case BackupComplete:
00163 s.append(CSL1("BackupComplete"));
00164 break;
00165 default:
00166 s.append(CSL1("(unknown "));
00167 s.append(QString::number(status()));
00168 s.append(CSL1(")"));
00169 }
00170
00171 return s;
00172 }
00173
00174 static inline bool dontBackup(struct DBInfo *info,
00175 const QStringList &dbnames,
00176 const QValueList<unsigned long> &dbcreators)
00177 {
00178
00179 if ( (info->creator == pi_mktag('p','s','y','s')) &&
00180 (info->type == pi_mktag('p','r','e','f')) ) return true;
00181
00182 if (dbcreators.findIndex(info->creator) != -1) return true;
00183
00184
00185 QString db = PilotAppCategory::codec()->toUnicode(info->name);
00186 for (QStringList::const_iterator i = dbnames.begin(); i != dbnames.end(); ++i)
00187 {
00188 QRegExp re(*i,true,true);
00189 if (re.exactMatch(db)) return true;
00190 }
00191 return false;
00192 }
00193
00194 static inline void initNoBackup(QStringList &dbnames,
00195 QValueList<unsigned long> &dbcreators)
00196 {
00197 FUNCTIONSETUP;
00198 dbnames.clear();
00199 dbcreators.clear();
00200
00201 QStringList configuredSkip = KPilotSettings::skipBackupDB();
00202 QStringList::const_iterator e = configuredSkip.end();
00203 for (QStringList::const_iterator i = configuredSkip.begin();
00204 i!= e; ++i)
00205 {
00206 QString s = *i;
00207 if (s.startsWith(CSL1("[")) && s.endsWith(CSL1("]")))
00208 {
00209 if (s.length() != 6)
00210 {
00211 kdWarning() << k_funcinfo << ": Creator ID " << s << " is malformed." << endl;
00212 }
00213 else
00214 {
00215 QCString data = s.mid(1,4).latin1();
00216 unsigned long creator = pi_mktag(data[0],data[1],data[2],data[3]);
00217 dbcreators.append(creator);
00218 }
00219 }
00220 else
00221 {
00222 dbnames.append(s);
00223 }
00224 }
00225
00226 #ifdef DEBUG
00227 DEBUGCONDUIT << fname << ": Will skip databases "
00228 << dbnames.join(CSL1(",")) << endl;
00229 QString creatorids;
00230 for (QValueList<unsigned long>::const_iterator i = dbcreators.begin();
00231 i != dbcreators.end(); ++i)
00232 {
00233 creatorids.append(CSL1("[%1]").arg(*i,0,16));
00234 }
00235 DEBUGCONDUIT << fname << ": Will skip creators " << creatorids << endl;
00236 #endif
00237 }
00238
00239 bool BackupAction::exec()
00240 {
00241 FUNCTIONSETUP;
00242
00243 mDeviceDBs = KPilotSettings::deviceDBs();
00244
00245 fBackupDir =
00246 fDatabaseDir +
00247 PilotAppCategory::codec()->toUnicode(fHandle->getPilotUser()->getUserName()) +
00248 CSL1("/");
00249
00250 logMessage(i18n("Backup directory: %1.").arg(fBackupDir));
00251
00252 #ifdef DEBUG
00253 DEBUGCONDUIT << fname
00254 << ": This Pilot user's name is \""
00255 << fHandle->getPilotUser()->getUserName() << "\"" << endl;
00256 DEBUGCONDUIT << fname
00257 << ": Using backup dir: " << fBackupDir << endl;
00258 DEBUGCONDUIT << fname
00259 << ": Full Backup? " << fFullBackup << endl;
00260 #endif
00261
00262
00263 if (fFullBackup)
00264 {
00265 fActionStatus = FullBackup;
00266 addSyncLogEntry(i18n("Full backup started."));
00267 }
00268 else
00269 {
00270 fActionStatus = FastBackup;
00271 addSyncLogEntry(i18n("Fast backup started"));
00272 }
00273
00274 if (!checkBackupDirectory(fBackupDir))
00275 {
00276 fActionStatus=BackupIncomplete;
00277
00278
00279 return false;
00280 }
00281
00282 initNoBackup( fNoBackupDBs, fNoBackupCreators );
00283
00284 fTimer = new QTimer( this );
00285 QObject::connect( fTimer, SIGNAL( timeout() ),
00286 this, SLOT( backupOneDB() ) );
00287
00288 fDBIndex = 0;
00289
00290 fTimer->start( 0, false );
00291 return true;
00292 }
00293
00294 bool BackupAction::checkBackupDirectory(QString backupDir)
00295 {
00296 FUNCTIONSETUP;
00297 QFileInfo fi(backupDir);
00298
00299 if (!(fi.exists() && fi.isDir()))
00300 {
00301 #ifdef DEBUG
00302 DEBUGCONDUIT << fname
00303 << ": Need to create backup directory for user "
00304 << fHandle->getPilotUser()->getUserName() << endl;
00305 #endif
00306
00307 fi = QFileInfo(fDatabaseDir);
00308 if (!(fi.exists() && fi.isDir()))
00309 {
00310 kdError() << k_funcinfo
00311 << ": Database backup directory "
00312 << "doesn't exist."
00313 << endl;
00314 return false;
00315 }
00316
00317 QDir databaseDir(backupDir);
00318
00319 if (!databaseDir.mkdir(backupDir, true))
00320 {
00321 kdError() << k_funcinfo
00322 << ": Can't create backup directory." << endl;
00323 return false;
00324 }
00325 }
00326 return true;
00327 }
00328
00329 void BackupAction::backupOneDB()
00330 {
00331 FUNCTIONSETUP;
00332
00333 struct DBInfo info;
00334
00335 emit logProgress(QString::null, fDBIndex);
00336
00337 if (openConduit() < 0)
00338 {
00339 #ifdef DEBUG
00340 DEBUGCONDUIT << fname
00341 << ": openConduit failed. User cancel?" << endl;
00342 #endif
00343
00344 addSyncLogEntry(i18n("Exiting on cancel."));
00345 endBackup();
00346 fActionStatus = BackupIncomplete;
00347 return;
00348 }
00349
00350
00351 int res = fHandle->getNextDatabase( fDBIndex, &info );
00352 if (res < 0)
00353 {
00354 #ifdef DEBUG
00355 DEBUGCONDUIT << fname << ": Backup complete." << endl;
00356 #endif
00357
00358 if ( fFullBackup )
00359 addSyncLogEntry( i18n("Full backup complete.") );
00360 else
00361 addSyncLogEntry( i18n("Fast backup complete.") );
00362 endBackup();
00363 fActionStatus = BackupComplete;
00364 return;
00365 }
00366
00367 fDBIndex = info.index + 1;
00368
00369 char buff[8];
00370 memset(buff, 0, 8);
00371 buff[0] = '[';
00372 set_long( &buff[1], info.creator );
00373 buff[5] = ']';
00374 buff[6] = '\0';
00375 QString creator = QString::fromLatin1( buff );
00376 info.name[33]='\0';
00377 QString dbname = QString::fromLatin1( info.name );
00378 if ( !mDeviceDBs.contains( creator ) )
00379 mDeviceDBs << creator;
00380 if ( !mDeviceDBs.contains( dbname ) )
00381 mDeviceDBs << dbname;
00382
00383
00384 #ifdef DEBUG
00385 DEBUGCONDUIT << fname << ": Checking to see if we should backup database " << info.name
00386 << " [" << QString::number(info.creator,16) << "]" << endl;
00387 #endif
00388
00389
00390 if (dontBackup(&info,fNoBackupDBs,fNoBackupCreators))
00391 {
00392 #ifdef DEBUG
00393 DEBUGCONDUIT << fname << ": Skipping database " << info.name
00394 << " (database in no-backup list)" << endl;
00395 #endif
00396 QString s = i18n("Skipping %1")
00397 .arg(PilotAppCategory::codec()->toUnicode(info.name));
00398 addSyncLogEntry(s);
00399 return;
00400 }
00401
00402
00403 if ( (!fFullBackup) && PilotDatabase::isResource(&info))
00404 {
00405 #ifdef DEBUG
00406 DEBUGCONDUIT << fname << ": Skipping database " << info.name
00407 << " (resource database)" << endl;
00408 #endif
00409
00410 return;
00411 }
00412
00413 QString s = i18n("Backing up: %1")
00414 .arg(PilotAppCategory::codec()->toUnicode(info.name));
00415 addSyncLogEntry(s);
00416
00417 if (!createLocalDatabase(&info))
00418 {
00419 kdError() << k_funcinfo
00420 << ": Couldn't create local database for "
00421 << info.name << endl;
00422 addSyncLogEntry(i18n("Backup of %1 failed.\n")
00423 .arg(PilotAppCategory::codec()->toUnicode(info.name)));
00424 }
00425 else
00426 {
00427 addSyncLogEntry(i18n(" .. OK\n"),false);
00428 }
00429 }
00430
00440 bool BackupAction::createLocalDatabase(DBInfo * info)
00441 {
00442 FUNCTIONSETUP;
00443
00444 QString databaseName(PilotAppCategory::codec()->toUnicode(info->name));
00445
00446
00447 bool doBackup = true;
00448
00449
00450 if (!checkBackupDirectory(fBackupDir)) return false;
00451
00452
00453
00454 PilotSerialDatabase*serial=new PilotSerialDatabase(pilotSocket(), databaseName);
00455 if (!serial->isDBOpen())
00456 {
00457 #ifdef DEBUG
00458 DEBUGCONDUIT<<"Unable to open database "<<info->name<<" to check for modified records and reset sync flags."<<endl;
00459 #endif
00460 }
00461
00462
00463
00464
00465 if (!fFullBackup && serial->isDBOpen())
00466 {
00467 int index=0;
00468 PilotRecord*rec=serial->readNextModifiedRec(&index);
00469 if (!rec)
00470 {
00471 doBackup = false;
00472 }
00473 KPILOT_DELETE(rec);
00474 }
00475
00476
00477
00478 KPILOT_DELETE(serial);
00479
00480
00481
00482 if (!doBackup)
00483 {
00484 #ifdef DEBUG
00485 DEBUGDB << fname
00486 << ": don't need to backup this database (no changes)." << endl;
00487 #endif
00488 return true;
00489 }
00490
00491
00492
00493 databaseName.replace('/', CSL1("_"));
00494
00495 QString fullBackupName = fBackupDir + databaseName;
00496
00497 if (PilotDatabase::isResource(info))
00498 {
00499 fullBackupName.append(CSL1(".prc"));
00500 }
00501 else
00502 {
00503 fullBackupName.append(CSL1(".pdb"));
00504 }
00505
00506 #ifdef DEBUG
00507 DEBUGDB << fname
00508 << ": Backing up database to: [" << fullBackupName << "]" << endl;
00509 #endif
00510
00511
00512 info->flags &= ~dlpDBFlagOpen;
00513
00514 bool backedUp = fHandle->retrieveDatabase(fullBackupName,info);
00515
00516
00517
00518 serial=new PilotSerialDatabase(pilotSocket(), databaseName);
00519 if (backedUp && serial->isDBOpen())
00520 {
00521 serial->cleanup();
00522 serial->resetSyncFlags();
00523 }
00524 KPILOT_DELETE(serial);
00525
00526 return backedUp;
00527 }
00528
00529 void BackupAction::endBackup()
00530 {
00531 FUNCTIONSETUP;
00532
00533 KPILOT_DELETE(fTimer);
00534 fDBIndex = (-1);
00535 fActionStatus = BackupEnded;
00536 mDeviceDBs.sort();
00537 QString old( QString::null );
00538 QStringList::Iterator itr = mDeviceDBs.begin();
00539 while ( itr != mDeviceDBs.end() ) {
00540 if ( old == *itr ) {
00541 itr = mDeviceDBs.remove( itr );
00542 } else {
00543 old = *itr;
00544 ++itr;
00545 }
00546 }
00547 KPilotSettings::setDeviceDBs( mDeviceDBs );
00548
00549 emit syncDone(this);
00550 }
00551
00552 FileInstallAction::FileInstallAction(KPilotDeviceLink * p,
00553 const QString & d) :
00554 SyncAction(p, "fileInstall"),
00555 fDBIndex(-1),
00556 fTimer(0L),
00557 fDir(d)
00558 {
00559 FUNCTIONSETUP;
00560 }
00561
00562 FileInstallAction::~FileInstallAction()
00563 {
00564 FUNCTIONSETUP;
00565
00566 KPILOT_DELETE(fTimer);
00567 }
00568
00569 bool FileInstallAction::exec()
00570 {
00571 FUNCTIONSETUP;
00572
00573 QDir installDir(fDir);
00574 fList = installDir.entryList(QDir::Files |
00575 QDir::NoSymLinks | QDir::Readable);
00576 #ifdef DEBUG
00577 DEBUGCONDUIT << fname
00578 << ": Installing " << fList.count() << " files" << endl;
00579 #endif
00580
00581 fDBIndex = 0;
00582 emit logMessage(i18n("[File Installer]"));
00583
00584
00585 if (!fList.count())
00586 {
00587 emit logMessage(i18n("No Files to install"));
00588 delayDone();
00589 return true;
00590 }
00591
00592 fTimer = new QTimer(this);
00593 QObject::connect(fTimer, SIGNAL(timeout()),
00594 this, SLOT(installNextFile()));
00595
00596 fTimer->start(0, false);
00597
00598 emit logProgress(i18n("Installing one file",
00599 "Installing %n Files",fList.count()), 0);
00600 return true;
00601 }
00602
00603 void FileInstallAction::installNextFile()
00604 {
00605 FUNCTIONSETUP;
00606
00607 Q_ASSERT(fDBIndex >= 0);
00608 Q_ASSERT((unsigned) fDBIndex <= fList.count());
00609
00610 #ifdef DEBUG
00611 DEBUGCONDUIT << fname
00612 << ": Installing file index "
00613 << fDBIndex << " (of " << fList.count() << ")" << endl;
00614 #endif
00615
00616 if ((!fList.count()) || ((unsigned) fDBIndex >= fList.count()))
00617 {
00618 #ifdef DEBUG
00619 DEBUGCONDUIT << fname
00620 << ": Peculiar file index, bailing out." << endl;
00621 #endif
00622 KPILOT_DELETE(fTimer);
00623 fDBIndex = (-1);
00624 emit logProgress(i18n("Done Installing Files"), 100);
00625 delayDone();
00626 return;
00627 }
00628
00629 const QString filePath = fDir + fList[fDBIndex];
00630 const QString fileName = fList[fDBIndex];
00631
00632 fDBIndex++;
00633
00634 #ifdef DEBUG
00635 DEBUGCONDUIT << fname << ": Installing file " << filePath << endl;
00636 #endif
00637
00638 QString m = i18n("Installing %1").arg(fileName);
00639 emit logProgress(m,(100 * fDBIndex) / (fList.count()+1));
00640 m+=CSL1("\n");
00641 emit addSyncLogEntry(m,false );
00642
00643 struct pi_file *f = 0L;
00644
00645
00646 if (!resourceOK(fileName,filePath)) goto nextFile;
00647
00648 f = pi_file_open(const_cast <char *>
00649 ((const char *) QFile::encodeName(filePath)));
00650
00651
00652 #if PILOT_LINK_NUMBER < PILOT_LINK_0_12_0
00653 if (pi_file_install(f, pilotSocket(), 0) < 0)
00654 #else
00655 if (pi_file_install(f, pilotSocket(), 0, NULL) < 0)
00656 #endif
00657 {
00658 kdWarning() << k_funcinfo << ": failed to install." << endl;
00659
00660
00661 emit logError(i18n("Cannot install file "%1".").
00662 arg(fileName));
00663 }
00664 else
00665 {
00666 QFile::remove(filePath);
00667 }
00668
00669
00670 nextFile:
00671 if (f) pi_file_close(f);
00672 if (fDBIndex == -1)
00673 {
00674 fTimer->stop();
00675 delayDone();
00676
00677 }
00678 }
00679
00680
00681
00682 bool FileInstallAction::resourceOK(const QString &fileName, const QString &filePath)
00683 {
00684 FUNCTIONSETUP;
00685
00686 if (!QFile::exists(filePath))
00687 {
00688 emit logError(i18n("Unable to open file "%1".").
00689 arg(fileName));
00690 return false;
00691 }
00692
00693 struct pi_file *f = pi_file_open(const_cast <char *>
00694 ((const char *) QFile::encodeName(filePath)));
00695
00696 if (!f)
00697 {
00698 emit logError(i18n("Unable to open file "%1".").
00699 arg(fileName));
00700 return false;
00701 }
00702
00703 struct DBInfo info;
00704 #if PILOT_LINK_NUMBER < PILOT_LINK_0_12_0
00705 if (pi_file_get_info(f,&info) < 0)
00706 {
00707 emit logError(i18n("Unable to read file "%1".").
00708 arg(fileName));
00709 return false;
00710 }
00711 #else
00712 pi_file_get_info(f,&info);
00713 #endif
00714
00715
00716
00717 info.name[sizeof(info.name)-1]=0;
00718 bool r = (strlen(info.name) < 32);
00719 pi_file_close(f);
00720
00721 if (!r)
00722 {
00723 emit logError(i18n("The database in "%1" has a "
00724 "resource name that is longer than 31 characters. "
00725 "This suggests a bug in the tool used to create the database. "
00726 "KPilot cannot install this database.").arg(fileName));
00727 }
00728
00729 return r;
00730 }
00731
00732 QString FileInstallAction::statusString() const
00733 {
00734 FUNCTIONSETUP;
00735 if (fDBIndex < 0)
00736 {
00737 return QString(CSL1("Idle"));
00738 }
00739 else
00740 {
00741 if ((unsigned) fDBIndex >= fList.count())
00742 {
00743 return QString(CSL1("Index out of range"));
00744 }
00745 else
00746 {
00747 return QString(CSL1("Installing %1")).arg(fList[fDBIndex]);
00748 }
00749 }
00750 }
00751
00752 CleanupAction::CleanupAction(KPilotDeviceLink *p) : SyncAction(p,"cleanupAction")
00753 {
00754 FUNCTIONSETUP;
00755 }
00756
00757 CleanupAction::~CleanupAction()
00758 {
00759 #ifdef DEBUG
00760 FUNCTIONSETUP;
00761 DEBUGCONDUIT << fname
00762 << ": Deleting @" << (long)this << endl;
00763 #endif
00764 }
00765
00766 bool CleanupAction::exec()
00767 {
00768 FUNCTIONSETUP;
00769
00770 if (deviceLink())
00771 {
00772 deviceLink()->finishSync();
00773 }
00774 emit syncDone(this);
00775 return true;
00776 }
00777
00778