diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2011-02-16 20:17:18 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2011-02-16 20:17:18 +0000 |
commit | cb7eddb91455a69cf66fcd717e91a51ca5e2cfef (patch) | |
tree | cf5546e4d7c44370fbe9ca2be937bd254f30ebaa /kpilot/hotSync.cc | |
download | kpilot-cb7eddb91455a69cf66fcd717e91a51ca5e2cfef.tar.gz kpilot-cb7eddb91455a69cf66fcd717e91a51ca5e2cfef.zip |
Moved kpilot from kdepim to applications, as the core Trinity libraries should not contain hardware-dependent software
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/kpilot@1221127 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kpilot/hotSync.cc')
-rw-r--r-- | kpilot/hotSync.cc | 1156 |
1 files changed, 1156 insertions, 0 deletions
diff --git a/kpilot/hotSync.cc b/kpilot/hotSync.cc new file mode 100644 index 0000000..e0bc7e3 --- /dev/null +++ b/kpilot/hotSync.cc @@ -0,0 +1,1156 @@ +/* KPilot +** +** Copyright (C) 2001 by Dan Pilone +** Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com> +** Copyright (C) 2006 Adriaan de Groot <groot@kde.org> +** +** This file defines SyncActions, which are used to perform some specific +** task during a HotSync. Conduits are not included here, nor are +** sync actions requiring user interaction. Those can be found in the +** conduits subdirectory or interactiveSync.h. +*/ + +/* +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program in a file called COPYING; if not, write to +** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +** MA 02110-1301, USA. +*/ + +/* +** Bug reports and questions can be sent to kde-pim@kde.org. +*/ + + +#include "options.h" + +#include <time.h> +#include <unistd.h> +#include <stdio.h> + +#include <pi-file.h> +#include <pi-util.h> + +#include <tqtimer.h> +#include <tqfile.h> +#include <tqfileinfo.h> +#include <tqdir.h> +#include <tqvaluelist.h> +#include <tqregexp.h> +#include <tqstringlist.h> +#include <tqthread.h> + +#include <kglobal.h> +#include <kstandarddirs.h> +#include <kapplication.h> +#include <kmessagebox.h> + +#include "pilotUser.h" +#include "pilotRecord.h" +#include "actionQueue.h" +#include "pilotSerialDatabase.h" +#include "pilotLocalDatabase.h" +#include "pilotDatabase.h" +#include "kpilotSettings.h" + +#include "hotSync.moc" + +class BackupAction::Thread : public QThread +{ +public: + Thread( BackupAction *parent, + KPilotLink *link, + const TQString &filename, + const DBInfo *info ); + + enum { + TerminateOK = TQEvent::User, + TerminateFailure + } ; + +protected: + virtual void run(); +private: + BackupAction *fParent; + KPilotLink *fLink; + TQString fFilename; + struct DBInfo fDBInfo; +} ; + +class BackupAction::Private +{ +public: + bool fFullBackup; ///< Is this a full backup (all DBs, not just changed ones)? + TQStringList fNoBackupDBs; + TQValueList<unsigned long> fNoBackupCreators; + TQStringList fDeviceDBs; + + TQString fPreferBackupDir; ///< Directory to write backup in, overrides default + + // Remainder is used to hand around info during sync + + int fDBIndex; ///< Database number we're now doing + TQString fBackupDir; ///< Directory to write backup in. + + /** + * Add the database described by the info block to the list of + * databases definitely found on the handheld. + */ + void addDBInfo( const DBInfo *info ) + { + FUNCTIONSETUP; + fDBIndex = info->index + 1; + + // Each character of buff[] is written to + char buff[7]; + buff[0] = '['; + set_long( &buff[1], info->creator ); + buff[5] = ']'; + buff[6] = '\0'; + TQString creator = TQString::tqfromLatin1( buff ); + + TQString dbname = Pilot::fromPilot( info->name, 32 ); + + if ( !fDeviceDBs.tqcontains( creator ) ) + { + fDeviceDBs << creator; + } + if ( !fDeviceDBs.tqcontains( dbname ) ) + { + fDeviceDBs << dbname; + } + + DEBUGKPILOT << fname << ": Added <" << dbname + << "> " << creator << endl; + } + + + /** + * Check if this database, described by @p info , should + * be backed up (i.e. is allowed to be backed up by the + * user settings for no-backup DBs). + * + * @return @c true if the database may be backed up. + */ + bool allowBackup( const DBInfo *info ) const + { + // Special case - skip database Unsaved Preferences + if ( (info->creator == pi_mktag('p','s','y','s')) && + (info->type == pi_mktag('p','r','e','f')) ) + { + return false; + } + + if (fNoBackupCreators.findIndex(info->creator) != -1) + { + return false; + } + + // Now take wildcards into account + TQString db = Pilot::fromPilot(info->name); + for (TQStringList::const_iterator i = fNoBackupDBs.begin(); + i != fNoBackupDBs.end(); ++i) + { + TQRegExp re(*i,true,true); // Wildcard match + if (re.exactMatch(db)) + { + return false; + } + } + return true; + } + +} ; + +BackupAction::BackupAction(KPilotLink * p, bool full) : + SyncAction(p, "backupAction"), + fP( new Private ), + fBackupThread( 0L ) +{ + FUNCTIONSETUP; + + fP->fFullBackup = full; +} + +/* virtual */ TQString BackupAction::statusString() const +{ + FUNCTIONSETUP; + TQString s(CSL1("BackupAction=")); + + switch (status()) + { + case Init: + s.append(CSL1("Init")); + break; + case Error: + s.append(CSL1("Error")); + break; + case FullBackup: + s.append(CSL1("FullBackup")); + break; + case FastBackup: + s.append(CSL1("FastBackup")); + break; + case BackupEnded: + s.append(CSL1("BackupEnded")); + break; + case BackupIncomplete: + s.append(CSL1("BackupIncomplete")); + break; + case BackupComplete: + s.append(CSL1("BackupComplete")); + break; + default: + s.append(CSL1("(unknown ")); + s.append(TQString::number(status())); + s.append(CSL1(")")); + } + + return s; +} + +void BackupAction::setDirectory( const TQString &p ) +{ + fP->fPreferBackupDir = p; + if (!p.endsWith(CSL1("/"))) + { + fP->fPreferBackupDir.append(CSL1("/")); + } +} + +static inline void initNoBackup(TQStringList &dbnames, + TQValueList<unsigned long> &dbcreators) +{ + FUNCTIONSETUP; + dbnames.clear(); + dbcreators.clear(); + + TQStringList configuredSkip = KPilotSettings::skipBackupDB(); + TQStringList::const_iterator e = configuredSkip.end(); + for (TQStringList::const_iterator i = configuredSkip.begin(); + i!= e; ++i) + { + TQString s = *i; + if (s.startsWith(CSL1("[")) && s.endsWith(CSL1("]"))) + { + if (s.length() != 6) + { + WARNINGKPILOT << "Creator ID " << s << " is malformed." << endl; + } + else + { + TQCString data = s.mid(1,4).latin1(); + unsigned long creator = pi_mktag(data[0],data[1],data[2],data[3]); + dbcreators.append(creator); + } + } + else + { + dbnames.append(s); + } + } + + DEBUGKPILOT << fname << ": Will skip databases " + << dbnames.join(CSL1(",")) << endl; + TQString creatorids; + char buf[5]; + for (TQValueList<unsigned long>::const_iterator i = dbcreators.begin(); + i != dbcreators.end(); ++i) + { + unsigned long tag = *i; + pi_untag(buf,tag); + buf[4]=0; + creatorids.append(CSL1("[%1]").arg(buf)); + } + DEBUGKPILOT << fname << ": Will skip creators " << creatorids << endl; +} + +/** Make sure that the backup directory @p backupDir +* exists and is a directory; returns @c false +* if this is not the case. This method will try +* to create the directory if it doesn't exist yet. +*/ +static inline bool checkBackupDirectory( const TQString &backupDir ) +{ + FUNCTIONSETUP; + TQFileInfo fi(backupDir); + + if (fi.exists() && fi.isDir()) + { + return true; + } + + if (fi.exists() && !fi.isDir()) + { + WARNINGKPILOT << "Requested backup directory " + << backupDir + << " exists but is not a directory." + << endl; + return false; + } + + if ( !backupDir.endsWith("/") ) + { + WARNINGKPILOT << "Backup dir does not end with a / " + << endl; + return false; + } + + Q_ASSERT(!fi.exists()); + + DEBUGKPILOT << fname + << ": Creating directory " << backupDir << endl; + + KStandardDirs::makeDir( backupDir ); + + fi = TQFileInfo(backupDir); + + return fi.exists() && fi.isDir(); +} + + +/* virtual */ bool BackupAction::exec() +{ + FUNCTIONSETUP; + + fP->fDeviceDBs = KPilotSettings::deviceDBs(); + + if (fP->fPreferBackupDir.isEmpty()) + { + fP->fBackupDir = + KGlobal::dirs()->saveLocation("data",CSL1("kpilot/DBBackup/")) + + deviceLink()->getPilotUser().name() + '/'; + } + else + { + fP->fBackupDir = fP->fPreferBackupDir; + } + + logMessage(i18n("Backup directory: %1.").arg(fP->fBackupDir)); + + DEBUGKPILOT << fname + << ": This Pilot user's name is \"" + << deviceLink()->getPilotUser().name() << "\"" << endl; + DEBUGKPILOT << fname + << ": Using backup dir: " << fP->fBackupDir << endl; + DEBUGKPILOT << fname + << ": Full Backup? " << fP->fFullBackup << endl; + + + if (fP->fFullBackup) + { + fActionStatus = FullBackup; + addSyncLogEntry(i18n("Full backup started.")); + } + else + { + fActionStatus = FastBackup; + addSyncLogEntry(i18n("Fast backup started")); + } + + if (!checkBackupDirectory(fP->fBackupDir)) + { + fActionStatus=BackupIncomplete; + // Don't issue an error message, checkBackupDirectory + // did this already... + return false; + } + + initNoBackup( fP->fNoBackupDBs, fP->fNoBackupCreators ); + + fP->fDBIndex = 0; + TQTimer::singleShot(0,this,TQT_SLOT(backupOneDB())); + return true; +} + +/* slot */ void BackupAction::backupOneDB() +{ + FUNCTIONSETUP; + + struct DBInfo info; + + // TODO: make the progress reporting more accurate + emit logProgress(TQString::null, fP->fDBIndex); + + if (openConduit() < 0) + { + addSyncLogEntry(i18n("Exiting on cancel.")); + endBackup(); + fActionStatus = BackupIncomplete; + return; + } + + // TODO: Is there a way to skip unchanged databases? + int res = deviceLink()->getNextDatabase( fP->fDBIndex, &info ); + if (res < 0) + { + if ( fP->fFullBackup ) + { + addSyncLogEntry( i18n("Full backup complete.") ); + } + else + { + addSyncLogEntry( i18n("Fast backup complete.") ); + } + endBackup(); + fActionStatus = BackupComplete; + return; + } + + fP->addDBInfo( &info ); + + // see if user told us not to back this creator or database up... + if (fP->allowBackup(&info)) + { + // back up DB if this is a full backup *or* in non-full backups, + // only backup data, not applications. + if ( (fP->fFullBackup) || !PilotDatabase::isResource(&info) ) + { + addSyncLogEntry(i18n("Backing up: %1").arg(Pilot::fromPilot(info.name))); + + if (!startBackupThread(&info)) + { + WARNINGKPILOT << "Could not create local database for <" + << info.name << ">" << endl; + } + else + { + // The thread has started, so we will be woken + // up by it eventually when it is done. Do *NOT* + // fall through to the single-shot timer below, + // because that would return us to the backup + // function too soon. + return; + } + } + else + { + // Just skip resource DBs during an update hotsync. + DEBUGKPILOT << fname << ": Skipping database <" << info.name + << "> (resource database)" << endl; + } + } + else + { + DEBUGKPILOT << fname << ": Skipping database <" << info.name + << "> (no-backup list)" << endl; + TQString s = i18n("Skipping %1") + .arg(Pilot::fromPilot(info.name)); + addSyncLogEntry(s); + } + TQTimer::singleShot(0,this,TQT_SLOT(backupOneDB())); +} + +/** + * This method will back up a single database from the Pilot to a directory on + * our filesystem. If our user asks us to do a full backup, then we will unconditionally + * copy the database file from the Pilot into the backup directory. Otherwise, we will + * check to see if the database has any modified records in it on the pilot. If the + * database has not changed on the Pilot, then there's nothing to backup and we return. + * + * @return @c true if the backup has started (in another thread). + * You must wait on the thread to end with a User or User+1 + * type event and not start another backup thread. + * @return @c false if there is no backup to do. Diagnostic messages + * will already have been printed. + */ +bool BackupAction::startBackupThread(DBInfo *info) +{ + FUNCTIONSETUP; + + // now we look to see if the database on the pilot has at least one changed record + // in it. we do this so that we don't waste time backing up a database that has + // not changed. note: don't bother with this check if we're doing a full backup. + if (!fP->fFullBackup) + { + // Check if this DB has modified records. + PilotDatabase *serial=deviceLink()->database(info); + if (!serial->isOpen()) + { + WARNINGKPILOT << "Unable to open database <" << info->name << ">" << endl; + KPILOT_DELETE(serial); + addSyncLogEntry(i18n("Backup of %1 failed.\n") + .arg(Pilot::fromPilot(info->name))); + return false; + } + + int index=0; + PilotRecord*rec=serial->readNextModifiedRec(&index); + if (!rec) + { + DEBUGKPILOT << fname << ": No modified records." << endl; + KPILOT_DELETE(serial); + return false; + } + // Exists, with modified records. + KPILOT_DELETE(rec); + KPILOT_DELETE(serial); + } + + + // if we're here then we are going to back this database up. do some basic sanity + // checks and proceed.... + TQString databaseName(Pilot::fromPilot(info->name)); + databaseName.tqreplace('/', '_'); + + TQString fullBackupName = fP->fBackupDir + databaseName; + + if (PilotDatabase::isResource(info)) + { + fullBackupName.append(CSL1(".prc")); + } + else + { + fullBackupName.append(CSL1(".pdb")); + } + + DEBUGKPILOT << fname + << ": Backing up database to: [" << fullBackupName << "]" << endl; + + /* Ensure that DB-open flag is not kept */ + info->flags &= ~dlpDBFlagOpen; + + if (fBackupThread) + { + WARNINGKPILOT << "Starting new backup thread before the old one is done." << endl; + return false; + } + + fBackupThread = new Thread(this,deviceLink(),fullBackupName,info); + fBackupThread->start(); + return true; +} + +/* virtual */ bool BackupAction::event( TQEvent *e ) +{ + if (e->type() == (TQEvent::Type)Thread::TerminateOK) + { + KPILOT_DELETE(fBackupThread); + // This was a successful termination. + addSyncLogEntry( i18n("... OK.\n"), false ); + TQTimer::singleShot(0,this,TQT_SLOT(backupOneDB())); + return true; + } + if (e->type() == (TQEvent::Type)Thread::TerminateFailure) + { + KPILOT_DELETE(fBackupThread); + // Unsuccessful termination. + addSyncLogEntry( i18n("Backup failed.") ); + TQTimer::singleShot(0,this,TQT_SLOT(backupOneDB())); + return true; + } + return SyncAction::event(e); +} + +void BackupAction::endBackup() +{ + FUNCTIONSETUP; + + fP->fDBIndex = (-1); + fActionStatus = BackupEnded; + fP->fDeviceDBs.sort(); + TQString old( TQString::null ); + TQStringList::Iterator itr = fP->fDeviceDBs.begin(); + while ( itr != fP->fDeviceDBs.end() ) { + if ( old == *itr ) { + itr = fP->fDeviceDBs.remove( itr ); + } else { + old = *itr; + ++itr; + } + } + KPilotSettings::setDeviceDBs( fP->fDeviceDBs ); + + emit syncDone(this); +} + +FileInstallAction::FileInstallAction(KPilotLink * p, + const TQString & d) : + SyncAction(p, "fileInstall"), + fDBIndex(-1), + fTimer(0L), + fDir(d) +{ + FUNCTIONSETUP; +} + +FileInstallAction::~FileInstallAction() +{ + FUNCTIONSETUP; + + KPILOT_DELETE(fTimer); +} + +/* virtual */ bool FileInstallAction::exec() +{ + FUNCTIONSETUP; + + TQDir installDir(fDir); + fList = installDir.entryList(TQDir::Files | + TQDir::NoSymLinks | TQDir::Readable); +#ifdef DEBUG + DEBUGKPILOT << fname + << ": Installing " << fList.count() << " files" << endl; +#endif + + fDBIndex = 0; + emit logMessage(i18n("[File Installer]")); + + // Possibly no files to install? + if (!fList.count()) + { + emit logMessage(i18n("No Files to install")); + delayDone(); + return true; + } + + fTimer = new TQTimer(this); + TQObject::connect(fTimer, TQT_SIGNAL(timeout()), + this, TQT_SLOT(installNextFile())); + + fTimer->start(0, false); + + emit logProgress(i18n("Installing one file", + "Installing %n Files",fList.count()), 0); + return true; +} + +/* slot */ void FileInstallAction::installNextFile() +{ + FUNCTIONSETUP; + + Q_ASSERT(fDBIndex >= 0); + Q_ASSERT((unsigned) fDBIndex <= fList.count()); + +#ifdef DEBUG + DEBUGKPILOT << fname + << ": Installing file index " + << fDBIndex << " (of " << fList.count() << ")" << endl; +#endif + + if ((!fList.count()) || ((unsigned) fDBIndex >= fList.count())) + { +#ifdef DEBUG + DEBUGKPILOT << fname + << ": Peculiar file index, bailing out." << endl; +#endif + KPILOT_DELETE(fTimer); + fDBIndex = (-1); + emit logProgress(i18n("Done Installing Files"), 100); + delayDone(); + return; + } + + const TQString filePath = fDir + fList[fDBIndex]; + const TQString fileName = fList[fDBIndex]; + + fDBIndex++; + +#ifdef DEBUG + DEBUGKPILOT << fname << ": Installing file " << filePath << endl; +#endif + + TQString m = i18n("Installing %1").arg(fileName); + emit logProgress(m,(100 * fDBIndex) / (fList.count()+1)); + m+=CSL1("\n"); + emit addSyncLogEntry(m,false /* Don't print in KPilot's log. */ ); + + struct pi_file *f = 0L; + + // Check DB is ok, return false after warning user + if (!resourceOK(fileName,filePath)) goto nextFile; + + f = pi_file_open(const_cast <char *> + ((const char *) TQFile::encodeName(filePath))); + + +#if PILOT_LINK_NUMBER < PILOT_LINK_0_12_0 + if (pi_file_install(f, pilotSocket(), 0) < 0) +#else + if (pi_file_install(f, pilotSocket(), 0, NULL) < 0) +#endif + { + WARNINGKPILOT << "Failed to install." << endl; + + + emit logError(i18n("Cannot install file "%1"."). + arg(fileName)); + } + else + { + TQFile::remove(filePath); + } + + +nextFile: + if (f) pi_file_close(f); + if (fDBIndex == -1) + { + fTimer->stop(); + delayDone(); + // emit syncDone(this); + } +} + +// Check that the given file path is a good resource +// file - in particular that the resource name is ok. +bool FileInstallAction::resourceOK(const TQString &fileName, const TQString &filePath) +{ + FUNCTIONSETUP; + + if (!TQFile::exists(filePath)) + { + emit logError(i18n("Unable to open file "%1"."). + arg(fileName)); + return false; + } + + struct pi_file *f = pi_file_open(const_cast <char *> + ((const char *) TQFile::encodeName(filePath))); + + if (!f) + { + emit logError(i18n("Unable to open file "%1"."). + arg(fileName)); + return false; + } + + struct DBInfo info; +#if PILOT_LINK_NUMBER < PILOT_LINK_0_12_0 + if (pi_file_get_info(f,&info) < 0) + { + emit logError(i18n("Unable to read file "%1"."). + arg(fileName)); + return false; + } +#else + pi_file_get_info(f,&info); +#endif + + // Looks like strlen, but we can't be sure of a NUL + // termination. + info.name[sizeof(info.name)-1]=0; + bool r = (strlen(info.name) < 32); + pi_file_close(f); + + if (!r) + { + emit logError(i18n("The database in "%1" has a " + "resource name that is longer than 31 characters. " + "This suggests a bug in the tool used to create the database. " + "KPilot cannot install this database.").arg(fileName)); + } + + return r; +} + +/* virtual */ TQString FileInstallAction::statusString() const +{ + FUNCTIONSETUP; + if (fDBIndex < 0) + { + return TQString(CSL1("Idle")); + } + else + { + if ((unsigned) fDBIndex >= fList.count()) + { + return TQString(CSL1("Index out of range")); + } + else + { + return TQString(CSL1("Installing %1")).arg(fList[fDBIndex]); + } + } +} + +CheckUser::CheckUser(KPilotLink * p, TQWidget * vp): + SyncAction(p, vp, "userCheck") +{ + FUNCTIONSETUP; + +} + +CheckUser::~CheckUser() +{ + FUNCTIONSETUP; +} + +/* virtual */ bool CheckUser::exec() +{ + FUNCTIONSETUP; + + TQString guiUserName = KPilotSettings::userName(); + TQString pilotUserName = fHandle->getPilotUser().name(); + bool pilotUserEmpty = pilotUserName.isEmpty(); + // 4 cases to handle: + // guiUserName empty / not empty + // pilotUserName empty / not empty + // + // + if (guiUserName.isEmpty()) + { + if (pilotUserEmpty) + { + TQString defaultUserName = + i18n("A common name", "John Doe"); + + TQString q = i18n("<qt>Neither KPilot nor the " + "handheld have a username set. " + "They <i>should</i> be set. " + "Should KPilot set them to a default value " + "(<i>%1</i>)?</qt>").arg(defaultUserName); + + if (questionYesNo(q, i18n("User Unknown") /* ,"askUserNone" */) == + KMessageBox::Yes) + { + KPilotSettings::setUserName(defaultUserName); + fHandle->getPilotUser().setName(defaultUserName); + guiUserName=defaultUserName; + pilotUserName=defaultUserName; + } + + } + else + { + TQString q = i18n("<qt>The handheld has a username set " + "(<i>%1</i>) but KPilot does not. Should " + "KPilot use this username in future?</qt>"). + arg(pilotUserName); + + if (questionYesNo(q, i18n("User Unknown") /* ,"askUserSome" */ ) == + KMessageBox::Yes) + { + KPilotSettings::setUserName(pilotUserName); + guiUserName=pilotUserName; + } + } + } + else + { + if (pilotUserEmpty) + { + TQString q = CSL1("<qt>"); + q += i18n("KPilot has a username set " + "(<i>%1</i>) but the handheld does not. " + "Should KPilot's username be set in the " + "handheld as well?").arg(guiUserName); + q += i18n("<br/>(<i>Note:</i> If your handheld " + "has been reset to factory defaults, you " + "should use <i>Restore</i> instead of a " + "regular HotSync. Click on Cancel to " + "stop this sync.)"); + q += CSL1("</qt>"); + + int r = questionYesNoCancel(q, i18n("User Unknown")); + switch (r) + { + case KMessageBox::Yes: + DEBUGKPILOT << fname + << ": Setting user name in pilot to " + << guiUserName << endl; + fHandle->getPilotUser().setName(guiUserName); + pilotUserName=guiUserName; + break; + case KMessageBox::No: + // Nothing to do .. continue with sync + break; + case KMessageBox::Cancel: + default: + return false; + } + } + else + { + if (guiUserName != pilotUserName) + { + TQString q = i18n("<qt>The handheld thinks that " + "the username is %1; " + "however, KPilot says you are %2." + "Which of these is the correct name?\n" + "If you click on Cancel, the sync will proceed, " + "but the usernames will not be changed.</qt>"). + arg(pilotUserName). + arg(guiUserName); + + int r = questionYesNoCancel(q, + i18n("User Mismatch"), + TQString::null, + 20, + i18n("Use KPilot Name"), + i18n("Use Handheld Name")); + switch (r) + { + case KMessageBox::Yes: + fHandle->getPilotUser().setName(guiUserName); + pilotUserName=guiUserName; + break; + case KMessageBox::No: + KPilotSettings::setUserName(pilotUserName); + guiUserName=pilotUserName; + break; + case KMessageBox::Cancel: + default: + // TODO: cancel the sync... Or just don't change any user name? + break; + } + } + } + } + + +#ifdef DEBUG + DEBUGKPILOT << fname + << ": User name set to pc <" + << guiUserName + << "> hh <" + << fHandle->getPilotUser().name() << ">" << endl; +#endif + + KPilotSettings::writeConfig(); + + // Now we've established which user will be used, + // fix the database location for local databases. + // + // + TQString pathName = KGlobal::dirs()->saveLocation("data", + CSL1("kpilot/DBBackup/")); + if (!guiUserName.isEmpty()) + { + pathName.append(guiUserName); + pathName.append(CSL1("/")); + } + PilotLocalDatabase::setDBPath(pathName); + + delayDone(); + return true; +} + +class RestoreInfo +{ +public: + struct DBInfo DBInfo; + TQString path; +} ; + +class RestoreAction::Private +{ +public: + TQString fPreferRestoreDir; /**< Preference setting where to get data from. */ + + TQValueList<RestoreInfo> fDBList; + TQTimer fTimer; + TQValueList<RestoreInfo>::ConstIterator fDBIterator; + int fDBIndex; +}; + + +RestoreAction::RestoreAction(KPilotLink * p, TQWidget * visible ) : + SyncAction(p, visible, "restoreAction") +{ + FUNCTIONSETUP; + + fP = new Private; +} + +void RestoreAction::setDirectory( const TQString &path ) +{ + fP->fPreferRestoreDir = path; +} + +/* virtual */ bool RestoreAction::exec() +{ + FUNCTIONSETUP; + + TQString dirname; + if (fP->fPreferRestoreDir.isEmpty()) + { + dirname = PilotLocalDatabase::getDBPath(); + } + else + { + dirname = fP->fPreferRestoreDir; + } + +#ifdef DEBUG + DEBUGKPILOT << fname << ": Restoring user " << dirname << endl; +#endif + + TQDir dir(dirname, TQString::null, TQDir::Name, + TQDir::Files | TQDir::Readable | TQDir::NoSymLinks); + + if (!dir.exists()) + { + WARNINGKPILOT << "Restore directory " + << dirname << " does not exist." << endl; + fActionStatus = Error; + addSyncLogEntry(i18n("Restore directory does not exist.") + + CSL1(" ") + i18n("Restore not performed.")); + return false; + } + + dirname = dir.absPath(); + if (questionYesNo(i18n("<qt>Are you sure you want to completely " + "restore your Pilot from the backup directory " + "(<i>%1</i>)? This will erase any information " + "you currently have on your Pilot.</qt>"). + arg(dirname), + i18n("Restore Pilot")) != KMessageBox::Yes) + { + emit logError(i18n("Restore <i>not</i> performed.")); + + addSyncLogEntry(i18n("Canceled by user.") + CSL1(" ") + + i18n("Restore not performed.")); + + // You might call this an error, but that causes + // a frightening message in the log .. and the + // user already _knows_ the restore didn't happen. + // So instead, act as if everything was ok. + delayDone(); + return true; + } + + + emit logProgress(i18n("Restoring %1...").arg(TQString::null),1); + + for (unsigned int i = 0; i < dir.count(); i++) + { + TQString s; + RestoreInfo info; + + s = dirname + TQDir::separator() + dir[i]; + + DEBUGKPILOT << fname + << ": Adding " << s << " to restore list." << endl; + + if ( PilotLocalDatabase::infoFromFile( s, &info.DBInfo ) ) + { + info.path = s; + fP->fDBList.append(info); + } + else + { + WARNINGKPILOT << "Can't open " << s << endl; + logMessage(i18n("File '%1' cannot be read.").arg(s)); + } + } + + fP->fDBIndex = 0; + fP->fDBIterator = fP->fDBList.begin(); + fActionStatus = InstallingFiles; + + TQObject::connect(&(fP->fTimer), TQT_SIGNAL(timeout()), + this, TQT_SLOT(installNextFile())); + + fP->fTimer.start(0, false); + return true; +} + +/* slot */ void RestoreAction::installNextFile() +{ + FUNCTIONSETUP; + + Q_ASSERT(fActionStatus == InstallingFiles); + + + if (fP->fDBIterator == fP->fDBList.end()) + { + fP->fTimer.stop(); + + fActionStatus = Done; + addSyncLogEntry(i18n("OK.")); + delayDone(); + return; + } + + const RestoreInfo dbi = *(fP->fDBIterator); + ++(fP->fDBIterator); + ++(fP->fDBIndex); + + DEBUGKPILOT << fname << ": Trying to install " << dbi.path << endl; + + if (openConduit() < 0) + { + WARNINGKPILOT << "Restore apparently canceled." << endl; + logMessage(i18n("Restore incomplete.")); + fActionStatus = Done; + emit syncDone(this); + + return; + } + + TQFileInfo databaseInfo(dbi.path); + addSyncLogEntry(databaseInfo.fileName()); + emit logProgress(i18n("Restoring %1...").arg(databaseInfo.fileName()), + (100*fP->fDBIndex) / (fP->fDBList.count()+1)) ; + + if ( !deviceLink()->installFiles( dbi.path, false /* don't delete */ ) ) + { + WARNINGKPILOT << "Couldn't restore " << dbi.path << endl; + logError(i18n("Cannot restore file `%1'.") + .arg(databaseInfo.fileName())); + } +} + +/* virtual */ TQString RestoreAction::statusString() const +{ + FUNCTIONSETUP; + TQString s; + + switch (status()) + { + case InstallingFiles: + s.append(CSL1("Installing Files (")); + s.append(TQString::number(fP->fDBIndex)); + s.append(CSL1(")")); + break; + case GettingFileInfo: + s.append(CSL1("Getting File Info (")); + s.append(TQString::number(fP->fDBIndex)); + s.append(CSL1(")")); + break; + default: + return SyncAction::statusString(); + } + + return s; +} + + + +BackupAction::Thread::Thread( BackupAction *parent, + KPilotLink *link, + const TQString &filename, + const DBInfo *info ) +{ + fParent = parent; + fLink = link; + fFilename = filename; + memcpy(&fDBInfo,info,sizeof(DBInfo)); +} + +void BackupAction::Thread::run() +{ + if (fLink->retrieveDatabase(fFilename,&fDBInfo)) + { + // Successful. + TQApplication::postEvent( fParent, new TQEvent( (TQEvent::Type)TerminateOK ) ); + } + else + { + // Failed + TQApplication::postEvent( fParent, new TQEvent( (TQEvent::Type)TerminateFailure ) ); + } +} + + |