kpilot

hotSync.cc

Go to the documentation of this file.
00001 /* KPilot
00002 **
00003 ** Copyright (C) 2001 by Dan Pilone
00004 ** Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
00005 ** Copyright (C) 2006 Adriaan de Groot <groot@kde.org>
00006 **
00007 ** This file defines SyncActions, which are used to perform some specific
00008 ** task during a HotSync. Conduits are not included here, nor are
00009 ** sync actions requiring user interaction. Those can be found in the
00010 ** conduits subdirectory or interactiveSync.h.
00011 */
00012 
00013 /*
00014 ** This program is free software; you can redistribute it and/or modify
00015 ** it under the terms of the GNU General Public License as published by
00016 ** the Free Software Foundation; either version 2 of the License, or
00017 ** (at your option) any later version.
00018 **
00019 ** This program is distributed in the hope that it will be useful,
00020 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
00021 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00022 ** GNU General Public License for more details.
00023 **
00024 ** You should have received a copy of the GNU General Public License
00025 ** along with this program in a file called COPYING; if not, write to
00026 ** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
00027 ** MA 02110-1301, USA.
00028 */
00029 
00030 /*
00031 ** Bug reports and questions can be sent to kde-pim@kde.org.
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 #include <qthread.h>
00052 
00053 #include <kglobal.h>
00054 #include <kstandarddirs.h>
00055 #include <kapplication.h>
00056 #include <kmessagebox.h>
00057 
00058 #include "pilotUser.h"
00059 #include "pilotRecord.h"
00060 #include "actionQueue.h"
00061 #include "pilotSerialDatabase.h"
00062 #include "pilotLocalDatabase.h"
00063 #include "pilotDatabase.h"
00064 #include "kpilotSettings.h"
00065 
00066 #include "hotSync.moc"
00067 
00068 class BackupAction::Thread : public QThread
00069 {
00070 public:
00071     Thread( BackupAction *parent,
00072         KPilotLink *link,
00073         const QString &filename,
00074         const DBInfo *info );
00075 
00076     enum {
00077         TerminateOK = QEvent::User,
00078         TerminateFailure
00079     } ;
00080 
00081 protected:
00082     virtual void run();
00083 private:
00084     BackupAction *fParent;
00085     KPilotLink *fLink;
00086     QString fFilename;
00087     struct DBInfo fDBInfo;
00088 } ;
00089 
00090 class BackupAction::Private
00091 {
00092 public:
00093     bool fFullBackup; 
00094     QStringList fNoBackupDBs;
00095     QValueList<unsigned long> fNoBackupCreators;
00096     QStringList fDeviceDBs;
00097 
00098     QString fPreferBackupDir; 
00099 
00100     // Remainder is used to hand around info during sync
00101 
00102     int fDBIndex;       
00103     QString fBackupDir; 
00104 
00109     void addDBInfo( const DBInfo *info )
00110     {
00111         FUNCTIONSETUP;
00112         fDBIndex = info->index + 1;
00113 
00114         // Each character of buff[] is written to
00115         char buff[7];
00116         buff[0] = '[';
00117         set_long( &buff[1], info->creator );
00118         buff[5] = ']';
00119         buff[6] = '\0';
00120         QString creator = QString::fromLatin1( buff );
00121 
00122         QString dbname = Pilot::fromPilot( info->name, 32 );
00123 
00124         if ( !fDeviceDBs.contains( creator ) )
00125         {
00126             fDeviceDBs << creator;
00127         }
00128         if ( !fDeviceDBs.contains( dbname ) )
00129         {
00130             fDeviceDBs << dbname;
00131         }
00132 
00133         DEBUGKPILOT << fname << ": Added <" << dbname
00134             << "> " << creator << endl;
00135     }
00136 
00137 
00145     bool allowBackup( const DBInfo *info ) const
00146     {
00147         // Special case - skip database Unsaved Preferences
00148         if (   (info->creator == pi_mktag('p','s','y','s'))  &&
00149             (info->type == pi_mktag('p','r','e','f')) )
00150         {
00151             return false;
00152         }
00153 
00154         if (fNoBackupCreators.findIndex(info->creator) != -1)
00155         {
00156             return false;
00157         }
00158 
00159         // Now take wildcards into account
00160         QString db = Pilot::fromPilot(info->name);
00161         for (QStringList::const_iterator i = fNoBackupDBs.begin();
00162             i != fNoBackupDBs.end(); ++i)
00163         {
00164             QRegExp re(*i,true,true); // Wildcard match
00165             if (re.exactMatch(db))
00166             {
00167                 return false;
00168             }
00169         }
00170         return true;
00171     }
00172 
00173 } ;
00174 
00175 BackupAction::BackupAction(KPilotLink * p, bool full) :
00176     SyncAction(p, "backupAction"),
00177     fP( new Private ),
00178     fBackupThread( 0L )
00179 {
00180     FUNCTIONSETUP;
00181 
00182     fP->fFullBackup = full;
00183 }
00184 
00185 /* virtual */ QString BackupAction::statusString() const
00186 {
00187     FUNCTIONSETUP;
00188     QString s(CSL1("BackupAction="));
00189 
00190     switch (status())
00191     {
00192     case Init:
00193         s.append(CSL1("Init"));
00194         break;
00195     case Error:
00196         s.append(CSL1("Error"));
00197         break;
00198     case FullBackup:
00199         s.append(CSL1("FullBackup"));
00200         break;
00201     case FastBackup:
00202         s.append(CSL1("FastBackup"));
00203         break;
00204     case BackupEnded:
00205         s.append(CSL1("BackupEnded"));
00206         break;
00207     case BackupIncomplete:
00208         s.append(CSL1("BackupIncomplete"));
00209         break;
00210     case BackupComplete:
00211         s.append(CSL1("BackupComplete"));
00212         break;
00213     default:
00214         s.append(CSL1("(unknown "));
00215         s.append(QString::number(status()));
00216         s.append(CSL1(")"));
00217     }
00218 
00219     return s;
00220 }
00221 
00222 void BackupAction::setDirectory( const QString &p )
00223 {
00224     fP->fPreferBackupDir = p;
00225     if (!p.endsWith(CSL1("/")))
00226     {
00227         fP->fPreferBackupDir.append(CSL1("/"));
00228     }
00229 }
00230 
00231 static inline void initNoBackup(QStringList &dbnames,
00232     QValueList<unsigned long> &dbcreators)
00233 {
00234     FUNCTIONSETUP;
00235     dbnames.clear();
00236     dbcreators.clear();
00237 
00238     QStringList configuredSkip = KPilotSettings::skipBackupDB();
00239     QStringList::const_iterator e = configuredSkip.end();
00240     for (QStringList::const_iterator i = configuredSkip.begin();
00241         i!= e; ++i)
00242     {
00243         QString s = *i;
00244         if (s.startsWith(CSL1("[")) && s.endsWith(CSL1("]")))
00245         {
00246             if (s.length() != 6)
00247             {
00248                 WARNINGKPILOT << "Creator ID " << s << " is malformed." << endl;
00249             }
00250             else
00251             {
00252                 QCString data =  s.mid(1,4).latin1();
00253                 unsigned long creator = pi_mktag(data[0],data[1],data[2],data[3]);
00254                 dbcreators.append(creator);
00255             }
00256         }
00257         else
00258         {
00259             dbnames.append(s);
00260         }
00261     }
00262 
00263     DEBUGKPILOT << fname << ": Will skip databases "
00264         << dbnames.join(CSL1(",")) << endl;
00265     QString creatorids;
00266     char buf[5];
00267     for (QValueList<unsigned long>::const_iterator i = dbcreators.begin();
00268         i != dbcreators.end(); ++i)
00269     {
00270         unsigned long tag = *i;
00271         pi_untag(buf,tag);
00272         buf[4]=0;
00273         creatorids.append(CSL1("[%1]").arg(buf));
00274     }
00275     DEBUGKPILOT << fname << ": Will skip creators " << creatorids << endl;
00276 }
00277 
00283 static inline bool checkBackupDirectory( const QString &backupDir )
00284 {
00285     FUNCTIONSETUP;
00286     QFileInfo fi(backupDir);
00287 
00288     if (fi.exists() && fi.isDir())
00289     {
00290         return true;
00291     }
00292 
00293     if (fi.exists() && !fi.isDir())
00294     {
00295         WARNINGKPILOT << "Requested backup directory "
00296             << backupDir
00297             << " exists but is not a directory."
00298             << endl;
00299         return false;
00300     }
00301 
00302     if ( !backupDir.endsWith("/") )
00303     {
00304         WARNINGKPILOT << "Backup dir does not end with a / "
00305             << endl;
00306         return false;
00307     }
00308 
00309     Q_ASSERT(!fi.exists());
00310 
00311     DEBUGKPILOT << fname
00312         << ": Creating directory " << backupDir << endl;
00313 
00314     KStandardDirs::makeDir( backupDir );
00315 
00316     fi = QFileInfo(backupDir);
00317 
00318     return fi.exists() && fi.isDir();
00319 }
00320 
00321 
00322 /* virtual */ bool BackupAction::exec()
00323 {
00324     FUNCTIONSETUP;
00325 
00326     fP->fDeviceDBs = KPilotSettings::deviceDBs();
00327 
00328     if (fP->fPreferBackupDir.isEmpty())
00329     {
00330         fP->fBackupDir =
00331             KGlobal::dirs()->saveLocation("data",CSL1("kpilot/DBBackup/")) +
00332                 deviceLink()->getPilotUser().name() + '/';
00333     }
00334     else
00335     {
00336         fP->fBackupDir = fP->fPreferBackupDir;
00337     }
00338 
00339     logMessage(i18n("Backup directory: %1.").arg(fP->fBackupDir));
00340 
00341     DEBUGKPILOT << fname
00342         << ": This Pilot user's name is \""
00343         << deviceLink()->getPilotUser().name() << "\"" << endl;
00344     DEBUGKPILOT << fname
00345         << ": Using backup dir: " << fP->fBackupDir << endl;
00346     DEBUGKPILOT << fname
00347         << ": Full Backup? " << fP->fFullBackup << endl;
00348 
00349 
00350     if (fP->fFullBackup)
00351     {
00352         fActionStatus = FullBackup;
00353         addSyncLogEntry(i18n("Full backup started."));
00354     }
00355     else
00356     {
00357         fActionStatus = FastBackup;
00358         addSyncLogEntry(i18n("Fast backup started"));
00359     }
00360 
00361     if (!checkBackupDirectory(fP->fBackupDir))
00362     {
00363         fActionStatus=BackupIncomplete;
00364         // Don't issue an error message, checkBackupDirectory
00365         // did this already...
00366         return false;
00367     }
00368 
00369     initNoBackup( fP->fNoBackupDBs, fP->fNoBackupCreators );
00370 
00371     fP->fDBIndex = 0;
00372     QTimer::singleShot(0,this,SLOT(backupOneDB()));
00373     return true;
00374 }
00375 
00376 /* slot */ void BackupAction::backupOneDB()
00377 {
00378     FUNCTIONSETUP;
00379 
00380     struct DBInfo info;
00381 
00382     // TODO: make the progress reporting more accurate
00383     emit logProgress(QString::null, fP->fDBIndex);
00384 
00385     if (openConduit() < 0)
00386     {
00387         addSyncLogEntry(i18n("Exiting on cancel."));
00388         endBackup();
00389         fActionStatus = BackupIncomplete;
00390         return;
00391     }
00392 
00393     // TODO: Is there a way to skip unchanged databases?
00394     int res = deviceLink()->getNextDatabase( fP->fDBIndex, &info );
00395     if (res < 0)
00396     {
00397         if ( fP->fFullBackup )
00398         {
00399             addSyncLogEntry( i18n("Full backup complete.") );
00400         }
00401         else
00402         {
00403             addSyncLogEntry( i18n("Fast backup complete.") );
00404         }
00405         endBackup();
00406         fActionStatus = BackupComplete;
00407         return;
00408     }
00409 
00410     fP->addDBInfo( &info );
00411 
00412     // see if user told us not to back this creator or database up...
00413     if (fP->allowBackup(&info))
00414     {
00415         // back up DB if this is a full backup *or* in non-full backups,
00416         // only backup data, not applications.
00417         if ( (fP->fFullBackup) || !PilotDatabase::isResource(&info) )
00418         {
00419             addSyncLogEntry(i18n("Backing up: %1").arg(Pilot::fromPilot(info.name)));
00420 
00421             if (!startBackupThread(&info))
00422             {
00423                 WARNINGKPILOT << "Could not create local database for <"
00424                     << info.name << ">" << endl;
00425             }
00426             else
00427             {
00428                 // The thread has started, so we will be woken
00429                 // up by it eventually when it is done. Do *NOT*
00430                 // fall through to the single-shot timer below,
00431                 // because that would return us to the backup
00432                 // function too soon.
00433                 return;
00434             }
00435         }
00436         else
00437         {
00438             // Just skip resource DBs during an update hotsync.
00439             DEBUGKPILOT << fname << ": Skipping database <" << info.name
00440                 << "> (resource database)" << endl;
00441         }
00442     }
00443     else
00444     {
00445         DEBUGKPILOT << fname << ": Skipping database <" << info.name
00446             << "> (no-backup list)" << endl;
00447         QString s = i18n("Skipping %1")
00448             .arg(Pilot::fromPilot(info.name));
00449         addSyncLogEntry(s);
00450     }
00451     QTimer::singleShot(0,this,SLOT(backupOneDB()));
00452 }
00453 
00467 bool BackupAction::startBackupThread(DBInfo *info)
00468 {
00469     FUNCTIONSETUP;
00470 
00471     // now we look to see if the database on the pilot has at least one changed record
00472     // in it.  we do this so that we don't waste time backing up a database that has
00473     // not changed.  note: don't bother with this check if we're doing a full backup.
00474     if (!fP->fFullBackup)
00475     {
00476         // Check if this DB has modified records.
00477         PilotDatabase *serial=deviceLink()->database(info);
00478         if (!serial->isOpen())
00479         {
00480             WARNINGKPILOT << "Unable to open database <" << info->name << ">" << endl;
00481             KPILOT_DELETE(serial);
00482             addSyncLogEntry(i18n("Backup of %1 failed.\n")
00483                 .arg(Pilot::fromPilot(info->name)));
00484             return false;
00485         }
00486 
00487         int index=0;
00488         PilotRecord*rec=serial->readNextModifiedRec(&index);
00489         if (!rec)
00490         {
00491             DEBUGKPILOT << fname << ": No modified records." << endl;
00492             KPILOT_DELETE(serial);
00493             return false;
00494         }
00495         // Exists, with modified records.
00496         KPILOT_DELETE(rec);
00497         KPILOT_DELETE(serial);
00498     }
00499 
00500 
00501     // if we're here then we are going to back this database up.  do some basic sanity
00502     // checks and proceed....
00503     QString databaseName(Pilot::fromPilot(info->name));
00504     databaseName.replace('/', '_');
00505 
00506     QString fullBackupName = fP->fBackupDir + databaseName;
00507 
00508     if (PilotDatabase::isResource(info))
00509     {
00510         fullBackupName.append(CSL1(".prc"));
00511     }
00512     else
00513     {
00514         fullBackupName.append(CSL1(".pdb"));
00515     }
00516 
00517     DEBUGKPILOT << fname
00518         << ": Backing up database to: [" << fullBackupName << "]" << endl;
00519 
00520     /* Ensure that DB-open flag is not kept */
00521     info->flags &= ~dlpDBFlagOpen;
00522 
00523     if (fBackupThread)
00524     {
00525         WARNINGKPILOT << "Starting new backup thread before the old one is done." << endl;
00526         return false;
00527     }
00528 
00529     fBackupThread  = new Thread(this,deviceLink(),fullBackupName,info);
00530     fBackupThread->start();
00531     return true;
00532 }
00533 
00534 /* virtual */ bool BackupAction::event( QEvent *e )
00535 {
00536     if (e->type() == (QEvent::Type)Thread::TerminateOK)
00537     {
00538         KPILOT_DELETE(fBackupThread);
00539         // This was a successful termination.
00540         addSyncLogEntry( i18n("... OK.\n"), false );
00541         QTimer::singleShot(0,this,SLOT(backupOneDB()));
00542         return true;
00543     }
00544     if (e->type() == (QEvent::Type)Thread::TerminateFailure)
00545     {
00546         KPILOT_DELETE(fBackupThread);
00547         // Unsuccessful termination.
00548         addSyncLogEntry( i18n("Backup failed.") );
00549         QTimer::singleShot(0,this,SLOT(backupOneDB()));
00550         return true;
00551     }
00552     return SyncAction::event(e);
00553 }
00554 
00555 void BackupAction::endBackup()
00556 {
00557     FUNCTIONSETUP;
00558 
00559     fP->fDBIndex = (-1);
00560     fActionStatus = BackupEnded;
00561     fP->fDeviceDBs.sort();
00562     QString old( QString::null );
00563     QStringList::Iterator itr = fP->fDeviceDBs.begin();
00564     while ( itr != fP->fDeviceDBs.end() ) {
00565         if ( old == *itr ) {
00566             itr = fP->fDeviceDBs.remove( itr );
00567         } else {
00568             old = *itr;
00569             ++itr;
00570         }
00571     }
00572     KPilotSettings::setDeviceDBs( fP->fDeviceDBs );
00573 
00574     emit syncDone(this);
00575 }
00576 
00577 FileInstallAction::FileInstallAction(KPilotLink * p,
00578     const QString & d) :
00579     SyncAction(p, "fileInstall"),
00580     fDBIndex(-1),
00581     fTimer(0L),
00582     fDir(d)
00583 {
00584     FUNCTIONSETUP;
00585 }
00586 
00587 FileInstallAction::~FileInstallAction()
00588 {
00589     FUNCTIONSETUP;
00590 
00591     KPILOT_DELETE(fTimer);
00592 }
00593 
00594 /* virtual */ bool FileInstallAction::exec()
00595 {
00596     FUNCTIONSETUP;
00597 
00598     QDir installDir(fDir);
00599     fList = installDir.entryList(QDir::Files |
00600         QDir::NoSymLinks | QDir::Readable);
00601 #ifdef DEBUG
00602     DEBUGKPILOT << fname
00603         << ": Installing " << fList.count() << " files" << endl;
00604 #endif
00605 
00606     fDBIndex = 0;
00607     emit logMessage(i18n("[File Installer]"));
00608 
00609     // Possibly no files to install?
00610     if (!fList.count())
00611     {
00612         emit logMessage(i18n("No Files to install"));
00613         delayDone();
00614         return true;
00615     }
00616 
00617     fTimer = new QTimer(this);
00618     QObject::connect(fTimer, SIGNAL(timeout()),
00619         this, SLOT(installNextFile()));
00620 
00621     fTimer->start(0, false);
00622 
00623     emit logProgress(i18n("Installing one file",
00624         "Installing %n Files",fList.count()), 0);
00625     return true;
00626 }
00627 
00628 /* slot */ void FileInstallAction::installNextFile()
00629 {
00630     FUNCTIONSETUP;
00631 
00632     Q_ASSERT(fDBIndex >= 0);
00633     Q_ASSERT((unsigned) fDBIndex <= fList.count());
00634 
00635 #ifdef DEBUG
00636     DEBUGKPILOT << fname
00637         << ": Installing file index "
00638         << fDBIndex << " (of " << fList.count() << ")" << endl;
00639 #endif
00640 
00641     if ((!fList.count()) || ((unsigned) fDBIndex >= fList.count()))
00642     {
00643 #ifdef DEBUG
00644         DEBUGKPILOT << fname
00645             << ": Peculiar file index, bailing out." << endl;
00646 #endif
00647         KPILOT_DELETE(fTimer);
00648         fDBIndex = (-1);
00649         emit logProgress(i18n("Done Installing Files"), 100);
00650         delayDone();
00651         return;
00652     }
00653 
00654     const QString filePath = fDir + fList[fDBIndex];
00655     const QString fileName = fList[fDBIndex];
00656 
00657     fDBIndex++;
00658 
00659 #ifdef DEBUG
00660     DEBUGKPILOT << fname << ": Installing file " << filePath << endl;
00661 #endif
00662 
00663     QString m = i18n("Installing %1").arg(fileName);
00664     emit logProgress(m,(100 * fDBIndex) / (fList.count()+1));
00665     m+=CSL1("\n");
00666     emit addSyncLogEntry(m,false /* Don't print in KPilot's log. */ );
00667 
00668     struct pi_file *f = 0L;
00669 
00670     // Check DB is ok, return false after warning user
00671     if (!resourceOK(fileName,filePath)) goto nextFile;
00672 
00673     f = pi_file_open(const_cast <char *>
00674         ((const char *) QFile::encodeName(filePath)));
00675 
00676 
00677 #if PILOT_LINK_NUMBER < PILOT_LINK_0_12_0
00678     if (pi_file_install(f, pilotSocket(), 0) < 0)
00679 #else
00680     if (pi_file_install(f, pilotSocket(), 0, NULL) < 0)
00681 #endif
00682     {
00683         WARNINGKPILOT << "Failed to install." << endl;
00684 
00685 
00686         emit logError(i18n("Cannot install file &quot;%1&quot;.").
00687             arg(fileName));
00688     }
00689     else
00690     {
00691         QFile::remove(filePath);
00692     }
00693 
00694 
00695 nextFile:
00696     if (f) pi_file_close(f);
00697     if (fDBIndex == -1)
00698     {
00699         fTimer->stop();
00700         delayDone();
00701         // emit syncDone(this);
00702     }
00703 }
00704 
00705 // Check that the given file path is a good resource
00706 // file - in particular that the resource name is ok.
00707 bool FileInstallAction::resourceOK(const QString &fileName, const QString &filePath)
00708 {
00709     FUNCTIONSETUP;
00710 
00711     if (!QFile::exists(filePath))
00712     {
00713         emit logError(i18n("Unable to open file &quot;%1&quot;.").
00714             arg(fileName));
00715         return false;
00716     }
00717 
00718     struct pi_file *f = pi_file_open(const_cast <char *>
00719         ((const char *) QFile::encodeName(filePath)));
00720 
00721     if (!f)
00722     {
00723         emit logError(i18n("Unable to open file &quot;%1&quot;.").
00724             arg(fileName));
00725         return false;
00726     }
00727 
00728     struct DBInfo info;
00729 #if PILOT_LINK_NUMBER < PILOT_LINK_0_12_0
00730     if (pi_file_get_info(f,&info) < 0)
00731     {
00732         emit logError(i18n("Unable to read file &quot;%1&quot;.").
00733             arg(fileName));
00734         return false;
00735     }
00736 #else
00737     pi_file_get_info(f,&info);
00738 #endif
00739 
00740     // Looks like strlen, but we can't be sure of a NUL
00741     // termination.
00742     info.name[sizeof(info.name)-1]=0;
00743     bool r = (strlen(info.name) < 32);
00744     pi_file_close(f);
00745 
00746     if (!r)
00747     {
00748         emit logError(i18n("The database in &quot;%1&quot; has a "
00749             "resource name that is longer than 31 characters. "
00750             "This suggests a bug in the tool used to create the database. "
00751             "KPilot cannot install this database.").arg(fileName));
00752     }
00753 
00754     return r;
00755 }
00756 
00757 /* virtual */ QString FileInstallAction::statusString() const
00758 {
00759     FUNCTIONSETUP;
00760     if (fDBIndex < 0)
00761     {
00762         return QString(CSL1("Idle"));
00763     }
00764     else
00765     {
00766         if ((unsigned) fDBIndex >= fList.count())
00767         {
00768             return QString(CSL1("Index out of range"));
00769         }
00770         else
00771         {
00772             return QString(CSL1("Installing %1")).arg(fList[fDBIndex]);
00773         }
00774     }
00775 }
00776 
00777 CheckUser::CheckUser(KPilotLink * p, QWidget * vp):
00778     SyncAction(p, vp, "userCheck")
00779 {
00780     FUNCTIONSETUP;
00781 
00782 }
00783 
00784 CheckUser::~CheckUser()
00785 {
00786     FUNCTIONSETUP;
00787 }
00788 
00789 /* virtual */ bool CheckUser::exec()
00790 {
00791     FUNCTIONSETUP;
00792 
00793     QString guiUserName = KPilotSettings::userName();
00794     QString pilotUserName = fHandle->getPilotUser().name();
00795     bool pilotUserEmpty = pilotUserName.isEmpty();
00796     // 4 cases to handle:
00797     //    guiUserName empty / not empty
00798     //    pilotUserName empty / not empty
00799     //
00800     //
00801     if (guiUserName.isEmpty())
00802     {
00803         if (pilotUserEmpty)
00804         {
00805             QString defaultUserName =
00806                 i18n("A common name", "John Doe");
00807 
00808             QString q = i18n("<qt>Neither KPilot nor the "
00809                 "handheld have a username set. "
00810                 "They <i>should</i> be set. "
00811                 "Should KPilot set them to a default value "
00812                 "(<i>%1</i>)?</qt>").arg(defaultUserName);
00813 
00814             if (questionYesNo(q, i18n("User Unknown") /* ,"askUserNone" */) ==
00815                 KMessageBox::Yes)
00816             {
00817                 KPilotSettings::setUserName(defaultUserName);
00818                 fHandle->getPilotUser().setName(defaultUserName);
00819                 guiUserName=defaultUserName;
00820                 pilotUserName=defaultUserName;
00821             }
00822 
00823         }
00824         else
00825         {
00826             QString q = i18n("<qt>The handheld has a username set "
00827                 "(<i>%1</i>) but KPilot does not. Should "
00828                 "KPilot use this username in future?</qt>").
00829                 arg(pilotUserName);
00830 
00831             if (questionYesNo(q, i18n("User Unknown") /* ,"askUserSome" */ ) ==
00832                 KMessageBox::Yes)
00833             {
00834                 KPilotSettings::setUserName(pilotUserName);
00835                 guiUserName=pilotUserName;
00836             }
00837         }
00838     }
00839     else
00840     {
00841         if (pilotUserEmpty)
00842         {
00843             QString q = CSL1("<qt>");
00844             q += i18n("KPilot has a username set "
00845                 "(<i>%1</i>) but the handheld does not. "
00846                 "Should KPilot's username be set in the "
00847                 "handheld as well?").arg(guiUserName);
00848             q += i18n("<br/>(<i>Note:</i> If your handheld "
00849                 "has been reset to factory defaults, you "
00850                 "should use <i>Restore</i> instead of a "
00851                 "regular HotSync. Click on Cancel to "
00852                 "stop this sync.)");
00853             q += CSL1("</qt>");
00854 
00855             int r = questionYesNoCancel(q, i18n("User Unknown"));
00856             switch (r)
00857             {
00858             case KMessageBox::Yes:
00859                 DEBUGKPILOT << fname
00860                     << ": Setting user name in pilot to "
00861                     << guiUserName << endl;
00862                 fHandle->getPilotUser().setName(guiUserName);
00863                 pilotUserName=guiUserName;
00864                 break;
00865             case KMessageBox::No:
00866                 // Nothing to do .. continue with sync
00867                 break;
00868             case KMessageBox::Cancel:
00869             default:
00870                 return false;
00871             }
00872         }
00873         else
00874         {
00875             if (guiUserName != pilotUserName)
00876             {
00877                 QString q = i18n("<qt>The handheld thinks that "
00878                     "the username is %1; "
00879                     "however, KPilot says you are %2."
00880                     "Which of these is the correct name?\n"
00881                     "If you click on Cancel, the sync will proceed, "
00882                     "but the usernames will not be changed.</qt>").
00883                     arg(pilotUserName).
00884                     arg(guiUserName);
00885 
00886                 int r = questionYesNoCancel(q,
00887                     i18n("User Mismatch"),
00888                     QString::null,
00889                     20,
00890                     i18n("Use KPilot Name"),
00891                     i18n("Use Handheld Name"));
00892                 switch (r)
00893                 {
00894                 case KMessageBox::Yes:
00895                     fHandle->getPilotUser().setName(guiUserName);
00896                     pilotUserName=guiUserName;
00897                     break;
00898                 case KMessageBox::No:
00899                     KPilotSettings::setUserName(pilotUserName);
00900                     guiUserName=pilotUserName;
00901                     break;
00902                 case KMessageBox::Cancel:
00903                 default:
00904                     // TODO: cancel the sync... Or just don't change any user name?
00905                     break;
00906                 }
00907             }
00908         }
00909     }
00910 
00911 
00912 #ifdef DEBUG
00913     DEBUGKPILOT << fname
00914         << ": User name set to pc <"
00915         << guiUserName
00916         << "> hh <"
00917         << fHandle->getPilotUser().name() << ">" << endl;
00918 #endif
00919 
00920     KPilotSettings::writeConfig();
00921 
00922     // Now we've established which user will be used,
00923     // fix the database location for local databases.
00924     //
00925     //
00926     QString pathName = KGlobal::dirs()->saveLocation("data",
00927         CSL1("kpilot/DBBackup/"));
00928     if (!guiUserName.isEmpty())
00929     {
00930         pathName.append(guiUserName);
00931         pathName.append(CSL1("/"));
00932     }
00933     PilotLocalDatabase::setDBPath(pathName);
00934 
00935     delayDone();
00936     return true;
00937 }
00938 
00939 class RestoreInfo
00940 {
00941 public:
00942     struct DBInfo DBInfo;
00943     QString path;
00944 } ;
00945 
00946 class RestoreAction::Private
00947 {
00948 public:
00949     QString fPreferRestoreDir; 
00951     QValueList<RestoreInfo> fDBList;
00952     QTimer fTimer;
00953     QValueList<RestoreInfo>::ConstIterator fDBIterator;
00954     int fDBIndex;
00955 };
00956 
00957 
00958 RestoreAction::RestoreAction(KPilotLink * p, QWidget * visible ) :
00959     SyncAction(p, visible, "restoreAction")
00960 {
00961     FUNCTIONSETUP;
00962 
00963     fP = new Private;
00964 }
00965 
00966 void RestoreAction::setDirectory( const QString &path )
00967 {
00968     fP->fPreferRestoreDir = path;
00969 }
00970 
00971 /* virtual */ bool RestoreAction::exec()
00972 {
00973     FUNCTIONSETUP;
00974 
00975     QString dirname;
00976     if (fP->fPreferRestoreDir.isEmpty())
00977     {
00978         dirname = PilotLocalDatabase::getDBPath();
00979     }
00980     else
00981     {
00982         dirname = fP->fPreferRestoreDir;
00983     }
00984 
00985 #ifdef DEBUG
00986     DEBUGKPILOT << fname << ": Restoring user " << dirname << endl;
00987 #endif
00988 
00989     QDir dir(dirname, QString::null, QDir::Name,
00990         QDir::Files | QDir::Readable | QDir::NoSymLinks);
00991 
00992     if (!dir.exists())
00993     {
00994         WARNINGKPILOT << "Restore directory "
00995             << dirname << " does not exist." << endl;
00996         fActionStatus = Error;
00997         addSyncLogEntry(i18n("Restore directory does not exist.") +
00998             CSL1(" ") + i18n("Restore not performed."));
00999         return false;
01000     }
01001 
01002     dirname = dir.absPath();
01003     if (questionYesNo(i18n("<qt>Are you sure you want to completely "
01004                 "restore your Pilot from the backup directory "
01005                 "(<i>%1</i>)? This will erase any information "
01006                 "you currently have on your Pilot.</qt>").
01007             arg(dirname),
01008             i18n("Restore Pilot")) != KMessageBox::Yes)
01009     {
01010         emit logError(i18n("Restore <i>not</i> performed."));
01011 
01012         addSyncLogEntry(i18n("Canceled by user.") + CSL1(" ") +
01013             i18n("Restore not performed."));
01014 
01015         // You might call this an error, but that causes
01016         // a frightening message in the log .. and the
01017         // user already _knows_ the restore didn't happen.
01018         // So instead, act as if everything was ok.
01019         delayDone();
01020         return true;
01021     }
01022 
01023 
01024     emit logProgress(i18n("Restoring %1...").arg(QString::null),1);
01025 
01026     for (unsigned int i = 0; i < dir.count(); i++)
01027     {
01028         QString s;
01029         RestoreInfo info;
01030 
01031         s = dirname + QDir::separator() + dir[i];
01032 
01033         DEBUGKPILOT << fname
01034             << ": Adding " << s << " to restore list." << endl;
01035 
01036         if ( PilotLocalDatabase::infoFromFile( s, &info.DBInfo ) )
01037         {
01038             info.path = s;
01039             fP->fDBList.append(info);
01040         }
01041         else
01042         {
01043             WARNINGKPILOT << "Can't open " << s << endl;
01044             logMessage(i18n("File '%1' cannot be read.").arg(s));
01045         }
01046     }
01047 
01048     fP->fDBIndex = 0;
01049     fP->fDBIterator = fP->fDBList.begin();
01050     fActionStatus = InstallingFiles;
01051 
01052     QObject::connect(&(fP->fTimer), SIGNAL(timeout()),
01053         this, SLOT(installNextFile()));
01054 
01055     fP->fTimer.start(0, false);
01056     return true;
01057 }
01058 
01059 /* slot */ void RestoreAction::installNextFile()
01060 {
01061     FUNCTIONSETUP;
01062 
01063     Q_ASSERT(fActionStatus == InstallingFiles);
01064 
01065 
01066     if (fP->fDBIterator == fP->fDBList.end())
01067     {
01068         fP->fTimer.stop();
01069 
01070         fActionStatus = Done;
01071         addSyncLogEntry(i18n("OK."));
01072         delayDone();
01073         return;
01074     }
01075 
01076     const RestoreInfo dbi = *(fP->fDBIterator);
01077     ++(fP->fDBIterator);
01078     ++(fP->fDBIndex);
01079 
01080     DEBUGKPILOT << fname << ": Trying to install " << dbi.path << endl;
01081 
01082     if (openConduit() < 0)
01083     {
01084         WARNINGKPILOT << "Restore apparently canceled." << endl;
01085         logMessage(i18n("Restore incomplete."));
01086         fActionStatus = Done;
01087         emit syncDone(this);
01088 
01089         return;
01090     }
01091 
01092     QFileInfo databaseInfo(dbi.path);
01093     addSyncLogEntry(databaseInfo.fileName());
01094     emit logProgress(i18n("Restoring %1...").arg(databaseInfo.fileName()),
01095         (100*fP->fDBIndex) / (fP->fDBList.count()+1)) ;
01096 
01097     if ( !deviceLink()->installFiles( dbi.path, false /* don't delete */ ) )
01098     {
01099         WARNINGKPILOT << "Couldn't  restore " << dbi.path << endl;
01100         logError(i18n("Cannot restore file `%1'.")
01101             .arg(databaseInfo.fileName()));
01102     }
01103 }
01104 
01105 /* virtual */ QString RestoreAction::statusString() const
01106 {
01107     FUNCTIONSETUP;
01108     QString s;
01109 
01110     switch (status())
01111     {
01112     case InstallingFiles:
01113         s.append(CSL1("Installing Files ("));
01114         s.append(QString::number(fP->fDBIndex));
01115         s.append(CSL1(")"));
01116         break;
01117     case GettingFileInfo:
01118         s.append(CSL1("Getting File Info ("));
01119         s.append(QString::number(fP->fDBIndex));
01120         s.append(CSL1(")"));
01121         break;
01122     default:
01123         return SyncAction::statusString();
01124     }
01125 
01126     return s;
01127 }
01128 
01129 
01130 
01131 BackupAction::Thread::Thread( BackupAction *parent,
01132     KPilotLink *link,
01133     const QString &filename,
01134     const DBInfo *info )
01135 {
01136     fParent = parent;
01137     fLink = link;
01138     fFilename = filename;
01139     memcpy(&fDBInfo,info,sizeof(DBInfo));
01140 }
01141 
01142 void BackupAction::Thread::run()
01143 {
01144     if (fLink->retrieveDatabase(fFilename,&fDBInfo))
01145     {
01146         // Successful.
01147         QApplication::postEvent( fParent, new QEvent( (QEvent::Type)TerminateOK ) );
01148     }
01149     else
01150     {
01151         // Failed
01152         QApplication::postEvent( fParent, new QEvent( (QEvent::Type)TerminateFailure ) );
01153     }
01154 }
01155 
01156