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