From 106a3b296e37178ed6ee227120771116732ef6eb Mon Sep 17 00:00:00 2001
From: Kevin Smith <git@kismith.co.uk>
Date: Wed, 18 May 2011 10:23:43 +0100
Subject: Eagle mode.

Disables password persistence.

diff --git a/Swift/Controllers/MainController.cpp b/Swift/Controllers/MainController.cpp
index 6cc862e..1e388d5 100644
--- a/Swift/Controllers/MainController.cpp
+++ b/Swift/Controllers/MainController.cpp
@@ -14,6 +14,7 @@
 #include <stdlib.h>
 
 #include <Swiften/Base/format.h>
+#include <Swiften/Base/Algorithm.h>
 #include <Swift/Controllers/Intl.h>
 #include <Swift/Controllers/UIInterfaces/UIFactory.h>
 #include "Swiften/Network/TimerFactory.h"
@@ -86,7 +87,8 @@ MainController::MainController(
 		Dock* dock,
 		Notifier* notifier,
 		URIHandler* uriHandler,
-		bool useDelayForLatency) :
+		bool useDelayForLatency,
+		bool eagleMode) :
 			eventLoop_(eventLoop),
 			networkFactories_(networkFactories),
 			uiFactory_(uiFactories),
@@ -96,7 +98,8 @@ MainController::MainController(
 			settings_(settings),
 			uriHandler_(uriHandler),
 			loginWindow_(NULL) ,
-			useDelayForLatency_(useDelayForLatency) {
+			useDelayForLatency_(useDelayForLatency),
+			eagleMode_(eagleMode) {
 	storages_ = NULL;
 	certificateStorage_ = NULL;
 	statusTracker_ = NULL;
@@ -130,19 +133,25 @@ MainController::MainController(
 	bool loginAutomatically = settings_->getBoolSetting("loginAutomatically", false);
 	std::string cachedPassword;
 	std::string cachedCertificate;
-	foreach (std::string profile, settings->getAvailableProfiles()) {
-		ProfileSettingsProvider profileSettings(profile, settings);
-		std::string password = profileSettings.getStringSetting("pass");
-		std::string certificate = profileSettings.getStringSetting("certificate");
-		std::string jid = profileSettings.getStringSetting("jid");
-		loginWindow_->addAvailableAccount(jid, password, certificate);
-		if (jid == selectedLoginJID) {
-			cachedPassword = password;
-			cachedCertificate = certificate;
+	if (!eagleMode_) {
+		foreach (std::string profile, settings->getAvailableProfiles()) {
+			ProfileSettingsProvider profileSettings(profile, settings);
+			std::string password = eagleMode ? "" : profileSettings.getStringSetting("pass");
+			std::string certificate = profileSettings.getStringSetting("certificate");
+			std::string jid = profileSettings.getStringSetting("jid");
+			loginWindow_->addAvailableAccount(jid, password, certificate);
+			if (jid == selectedLoginJID) {
+				cachedPassword = password;
+				cachedCertificate = certificate;
+			}
 		}
+		loginWindow_->selectUser(selectedLoginJID);
+		loginWindow_->setLoginAutomatically(loginAutomatically);
+	} else {
+		loginWindow_->setRememberingAllowed(false);
 	}
-	loginWindow_->selectUser(selectedLoginJID);
-	loginWindow_->setLoginAutomatically(loginAutomatically);
+
+
 	loginWindow_->onLoginRequest.connect(boost::bind(&MainController::handleLoginRequest, this, _1, _2, _3, _4, _5));
 	loginWindow_->onPurgeSavedLoginRequest.connect(boost::bind(&MainController::handlePurgeSavedLoginRequest, this, _1));
 	loginWindow_->onCancelLoginRequest.connect(boost::bind(&MainController::handleCancelLoginRequest, this));
@@ -166,6 +175,7 @@ MainController::MainController(
 }
 
 MainController::~MainController() {
+	purgeCachedCredentials();
 	//setManagersOffline();
 	eventController_->disconnectAll();
 
@@ -180,7 +190,12 @@ MainController::~MainController() {
 	delete uiEventStream_;
 }
 
+void MainController::purgeCachedCredentials() {
+	safeClear(password_);
+}
+
 void MainController::resetClient() {
+	purgeCachedCredentials();
 	resetCurrentError();
 	resetPendingReconnects();
 	vCardPhotoHash_.clear();
@@ -243,6 +258,9 @@ void MainController::handleConnected() {
 	loginWindow_->setIsLoggingIn(false);
 	resetCurrentError();
 	resetPendingReconnects();
+	if (eagleMode_) {
+		purgeCachedCredentials();
+	}
 	bool freshLogin = rosterController_ == NULL;
 	myStatusLooksOnline_ = true;
 	if (freshLogin) {
@@ -370,12 +388,14 @@ void MainController::handleLoginRequest(const std::string &username, const std::
 		loginWindow_->setMessage("");
 		loginWindow_->setIsLoggingIn(true);
 		profileSettings_ = new ProfileSettingsProvider(username, settings_);
-		profileSettings_->storeString("jid", username);
-		profileSettings_->storeString("certificate", certificateFile);
-		profileSettings_->storeString("pass", (remember || loginAutomatically) ? password : "");
-		settings_->storeString("lastLoginJID", username);
-		settings_->storeBool("loginAutomatically", loginAutomatically);
-		loginWindow_->addAvailableAccount(profileSettings_->getStringSetting("jid"), profileSettings_->getStringSetting("pass"), profileSettings_->getStringSetting("certificate"));
+		if (!eagleMode_) {
+			profileSettings_->storeString("jid", username);
+			profileSettings_->storeString("certificate", certificateFile);
+			profileSettings_->storeString("pass", (remember || loginAutomatically) ? password : "");
+			settings_->storeString("lastLoginJID", username);
+			settings_->storeBool("loginAutomatically", loginAutomatically);
+			loginWindow_->addAvailableAccount(profileSettings_->getStringSetting("jid"), profileSettings_->getStringSetting("pass"), profileSettings_->getStringSetting("certificate"));
+		}
 
 		password_ = password;
 		certificateFile_ = certificateFile;
@@ -389,6 +409,10 @@ void MainController::handlePurgeSavedLoginRequest(const std::string& username) {
 }
 
 void MainController::performLoginFromCachedCredentials() {
+	if (eagleMode_ && password_.empty()) {
+		/* Then we can't try to login again. */
+		return;
+	}
 	/* If we logged in with a bare JID, and we have a full bound JID, re-login with the
 	 * bound JID to try and keep dynamically assigned resources */
 	JID clientJID = jid_;
@@ -434,11 +458,15 @@ void MainController::performLoginFromCachedCredentials() {
 	if (rosterController_) {
 		rosterController_->getWindow()->setConnecting();
 	}
-
-	client_->connect();
+	ClientOptions clientOptions;
+	clientOptions.forgetPassword = eagleMode_;
+	client_->connect(clientOptions);
 }
 
 void MainController::handleDisconnected(const boost::optional<ClientError>& error) {
+	if (eagleMode_) {
+		purgeCachedCredentials();
+	}
 	if (quitRequested_) {
 		resetClient();
 		loginWindow_->quit();
@@ -498,15 +526,19 @@ void MainController::handleDisconnected(const boost::optional<ClientError>& erro
 			loginWindow_->setIsLoggingIn(false);
 		} else {
 			logout();
-			setReconnectTimer();
-			if (lastDisconnectError_) {
-				message = str(format(QT_TRANSLATE_NOOP("", "Reconnect to %1% failed: %2%. Will retry in %3% seconds.")) % jid_.getDomain() % message % boost::lexical_cast<std::string>(timeBeforeNextReconnect_));
-				lastDisconnectError_->conclude();
+			if (eagleMode_) {
+				message = str(format(QT_TRANSLATE_NOOP("", "Disconnected from %1%: %2%. To reconnect, Sign Out and provide your password again.")) % jid_.getDomain() % message);
 			} else {
-				message = str(format(QT_TRANSLATE_NOOP("", "Disconnected from %1%: %2%.")) % jid_.getDomain() % message);
+				setReconnectTimer();
+				if (lastDisconnectError_) {
+					message = str(format(QT_TRANSLATE_NOOP("", "Reconnect to %1% failed: %2%. Will retry in %3% seconds.")) % jid_.getDomain() % message % boost::lexical_cast<std::string>(timeBeforeNextReconnect_));
+					lastDisconnectError_->conclude();
+				} else {
+					message = str(format(QT_TRANSLATE_NOOP("", "Disconnected from %1%: %2%.")) % jid_.getDomain() % message);
+				}
+				lastDisconnectError_ = boost::shared_ptr<ErrorEvent>(new ErrorEvent(JID(jid_.getDomain()), message));
+				eventController_->handleIncomingEvent(lastDisconnectError_);
 			}
-			lastDisconnectError_ = boost::shared_ptr<ErrorEvent>(new ErrorEvent(JID(jid_.getDomain()), message));
-			eventController_->handleIncomingEvent(lastDisconnectError_);
 		}
 	}
 	else if (!rosterController_) { //hasn't been logged in yet
@@ -533,6 +565,9 @@ void MainController::handleCancelLoginRequest() {
 }
 
 void MainController::signOut() {
+	if (eagleMode_) {
+		purgeCachedCredentials();
+	}
 	eventController_->clear();
 	logout();
 	loginWindow_->loggedOut();
@@ -540,6 +575,9 @@ void MainController::signOut() {
 }
 
 void MainController::logout() {
+	if (eagleMode_) {
+		purgeCachedCredentials();
+	}
 	systemTrayController_->setMyStatusType(StatusShow::None);
 	if (clientInitialized_ /*&& client_->isAvailable()*/) {
 		client_->disconnect();
diff --git a/Swift/Controllers/MainController.h b/Swift/Controllers/MainController.h
index 757abb8..21460ec 100644
--- a/Swift/Controllers/MainController.h
+++ b/Swift/Controllers/MainController.h
@@ -81,7 +81,8 @@ namespace Swift {
 					Dock* dock,
 					Notifier* notifier,
 					URIHandler* uriHandler,
-					bool useDelayForLatency);
+					bool useDelayForLatency,
+					bool eagleMode);
 			~MainController();
 
 
@@ -111,6 +112,7 @@ namespace Swift {
 			void setManagersOffline();
 			void handleNotificationClicked(const JID& jid);
 			void handleForceQuit();
+			void purgeCachedCredentials();
 
 		private:
 			EventLoop* eventLoop_;
@@ -160,5 +162,6 @@ namespace Swift {
 			bool myStatusLooksOnline_;
 			bool quitRequested_;
 			static const int SecondsToWaitBeforeForceQuitting;
+			bool eagleMode_;
 	};
 }
diff --git a/Swift/Controllers/UIInterfaces/LoginWindow.h b/Swift/Controllers/UIInterfaces/LoginWindow.h
index 61fcaa1..fcaeb42 100644
--- a/Swift/Controllers/UIInterfaces/LoginWindow.h
+++ b/Swift/Controllers/UIInterfaces/LoginWindow.h
@@ -27,6 +27,8 @@ namespace Swift {
 			boost::signal<void (const std::string&, const std::string&, const std::string& /* certificateFile */, bool /* remember password*/, bool /* login automatically */)> onLoginRequest;
 			virtual void setLoginAutomatically(bool loginAutomatically) = 0;
 			virtual void quit() = 0;
+			/** Disable any GUI elements that suggest saving of passwords. */
+			virtual void setRememberingAllowed(bool allowed) = 0;
 			/** Blocking request whether a cert should be permanently trusted.*/
 			virtual bool askUserToTrustCertificatePermanently(const std::string& message, Certificate::ref) = 0;
 
diff --git a/Swift/QtUI/QtLoginWindow.cpp b/Swift/QtUI/QtLoginWindow.cpp
index 4302c10..45d7c7e 100644
--- a/Swift/QtUI/QtLoginWindow.cpp
+++ b/Swift/QtUI/QtLoginWindow.cpp
@@ -197,6 +197,15 @@ QtLoginWindow::QtLoginWindow(UIEventStream* uiEventStream) : QMainWindow() {
 	this->show();
 }
 
+void QtLoginWindow::setRememberingAllowed(bool allowed) {
+	remember_->setEnabled(allowed);
+	loginAutomatically_->setEnabled(allowed);
+	if (!allowed) {
+		remember_->setChecked(false);
+		loginAutomatically_->setChecked(false);
+	}
+}
+
 bool QtLoginWindow::eventFilter(QObject *obj, QEvent *event) {
 	if (obj == username_->view() && event->type() == QEvent::KeyPress) {
 		QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
@@ -304,6 +313,10 @@ void QtLoginWindow::setIsLoggingIn(bool loggingIn) {
 void QtLoginWindow::loginClicked() {
 	if (username_->isEnabled()) {
 		onLoginRequest(Q2PSTRING(username_->currentText()), Q2PSTRING(password_->text()), Q2PSTRING(certificateFile_), remember_->isChecked(), loginAutomatically_->isChecked());
+		if (!remember_->isEnabled()) { /* Mustn't remember logins */
+			username_->clearEditText();
+			password_->setText("");
+		}
 	} else {
 		onCancelLoginRequest();
 	}
diff --git a/Swift/QtUI/QtLoginWindow.h b/Swift/QtUI/QtLoginWindow.h
index b667a4b..410ed0a 100644
--- a/Swift/QtUI/QtLoginWindow.h
+++ b/Swift/QtUI/QtLoginWindow.h
@@ -36,6 +36,7 @@ namespace Swift {
 			virtual void removeAvailableAccount(const std::string& jid);
 			virtual void setLoginAutomatically(bool loginAutomatically);
 			virtual void setIsLoggingIn(bool loggingIn);
+			virtual void setRememberingAllowed(bool allowed);
 			void selectUser(const std::string& user);
 			bool askUserToTrustCertificatePermanently(const std::string& message, Certificate::ref certificate);
 			void hide();
diff --git a/Swift/QtUI/QtSwift.cpp b/Swift/QtUI/QtSwift.cpp
index d7a1f78..f0c876c 100644
--- a/Swift/QtUI/QtSwift.cpp
+++ b/Swift/QtUI/QtSwift.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010 Kevin Smith
+ * Copyright (c) 2010-2011 Kevin Smith
  * Licensed under the GNU General Public License v3.
  * See Documentation/Licenses/GPLv3.txt for more information.
  */
@@ -73,6 +73,7 @@ po::options_description QtSwift::getOptionsDescription() {
 		("latency-debug", "use latency debugging (unsupported)")
 		("multi-account", po::value<int>()->default_value(1), "number of accounts to open windows for (unsupported)")
 		("start-minimized", "don't show the login/roster window at startup")
+		("eagle-mode", "Settings more suitable for military/secure deployments")
 		;
 	return result;
 }
@@ -103,6 +104,7 @@ QtSwift::QtSwift(const po::variables_map& options) : networkFactories_(&clientMa
 
 	tabs_ = options.count("no-tabs") && !(splitter_ > 0) ? NULL : new QtChatTabs();
 	bool startMinimized = options.count("start-minimized") > 0;
+	bool eagleMode = options.count("eagle-mode") > 0;
 	settings_ = new QtSettingsProvider();
 	applicationPathProvider_ = new PlatformApplicationPathProvider(SWIFT_APPLICATION_NAME);
 	storagesFactory_ = new FileStoragesFactory(applicationPathProvider_->getDataDir());
@@ -162,7 +164,8 @@ QtSwift::QtSwift(const po::variables_map& options) : networkFactories_(&clientMa
 				dock_,
 				notifier_,
 				uriHandler_,
-				options.count("latency-debug") > 0);
+				options.count("latency-debug") > 0,
+				eagleMode);
 		mainControllers_.push_back(mainController);
 	}
 
diff --git a/Swiften/Base/Algorithm.h b/Swiften/Base/Algorithm.h
index cd6f9dc..b7459ed 100644
--- a/Swiften/Base/Algorithm.h
+++ b/Swiften/Base/Algorithm.h
@@ -12,6 +12,7 @@
 #include <algorithm>
 
 namespace Swift {
+
 	/*
 	 * Generic erase()
 	 */
diff --git a/Swiften/Client/ClientOptions.h b/Swiften/Client/ClientOptions.h
index 1155b46..cc80dc2 100644
--- a/Swiften/Client/ClientOptions.h
+++ b/Swiften/Client/ClientOptions.h
@@ -12,7 +12,7 @@ struct ClientOptions {
 		UseTLSWhenAvailable
 	};
 
-	ClientOptions() : useStreamCompression(true), useTLS(UseTLSWhenAvailable), useStreamResumption(false) {
+	ClientOptions() : useStreamCompression(true), useTLS(UseTLSWhenAvailable), useStreamResumption(false), forgetPassword(false) {
 	}
 
 	/**
@@ -35,5 +35,15 @@ struct ClientOptions {
 	 * Default: false
 	 */
 	bool useStreamResumption;
+
+	/**
+	 * Forget the password once it's used.
+	 * This makes the Client useless after the first login attempt.
+	 *
+	 * FIXME: This is a temporary workaround.
+	 *
+	 * Default: false
+	 */
+	bool forgetPassword;
 };
 
diff --git a/Swiften/Client/CoreClient.cpp b/Swiften/Client/CoreClient.cpp
index 9521bd9..8684de2 100644
--- a/Swiften/Client/CoreClient.cpp
+++ b/Swiften/Client/CoreClient.cpp
@@ -9,6 +9,10 @@
 #include <boost/bind.hpp>
 #include <boost/smart_ptr/make_shared.hpp>
 
+#include <Swiften/Base/IDGenerator.h>
+#include <Swiften/Base/Log.h>
+#include <Swiften/Base/foreach.h>
+#include <Swiften/Base/Algorithm.h>
 #include <Swiften/Client/ClientSession.h>
 #include <Swiften/TLS/PlatformTLSFactories.h>
 #include <Swiften/TLS/CertificateVerificationError.h>
@@ -17,10 +21,7 @@
 #include <Swiften/TLS/PKCS12Certificate.h>
 #include <Swiften/Session/BasicSessionStream.h>
 #include <Swiften/Queries/IQRouter.h>
-#include <Swiften/Base/IDGenerator.h>
 #include <Swiften/Client/ClientSessionStanzaChannel.h>
-#include <Swiften/Base/Log.h>
-#include <Swiften/Base/foreach.h>
 #include <Swiften/Network/PlatformProxyProvider.h>
 #include <Swiften/Network/SOCKS5ProxiedConnectionFactory.h>
 #include <Swiften/Network/HTTPConnectProxiedConnectionFactory.h>
@@ -50,6 +51,7 @@ CoreClient::~CoreClient() {
 	stanzaChannel_->onPresenceReceived.disconnect(boost::bind(&CoreClient::handlePresenceReceived, this, _1));
 	stanzaChannel_->onStanzaAcked.disconnect(boost::bind(&CoreClient::handleStanzaAcked, this, _1));
 	delete stanzaChannel_;
+	purgePassword();
 }
 
 void CoreClient::connect(const ClientOptions& o) {
@@ -88,6 +90,9 @@ void CoreClient::handleConnectorFinished(boost::shared_ptr<Connection> connectio
 	}
 
 	if (!connection) {
+		if (options.forgetPassword) {
+			purgePassword();
+		}
 		onDisconnected(disconnectRequested_ ? boost::optional<ClientError>() : boost::optional<ClientError>(ClientError::ConnectionError));
 	}
 	else {
@@ -121,6 +126,9 @@ void CoreClient::handleConnectorFinished(boost::shared_ptr<Connection> connectio
 }
 
 void CoreClient::disconnect() {
+	if (options.forgetPassword) {
+			purgePassword();
+		}
 	// FIXME: We should be able to do without this boolean. We just have to make sure we can tell the difference between
 	// connector finishing without a connection due to an error or because of a disconnect.
 	disconnectRequested_ = true;
@@ -137,6 +145,9 @@ void CoreClient::setCertificate(const std::string& certificate) {
 }
 
 void CoreClient::handleSessionFinished(boost::shared_ptr<Error> error) {
+	if (options.forgetPassword) {
+		purgePassword();
+	}
 	session_->onFinished.disconnect(boost::bind(&CoreClient::handleSessionFinished, this, _1));
 	session_->onNeedCredentials.disconnect(boost::bind(&CoreClient::handleNeedCredentials, this));
 
@@ -248,6 +259,9 @@ void CoreClient::handleSessionFinished(boost::shared_ptr<Error> error) {
 void CoreClient::handleNeedCredentials() {
 	assert(session_);
 	session_->sendCredentials(password_);
+	if (options.forgetPassword) {
+		purgePassword();
+	}
 }
 
 void CoreClient::handleDataRead(const std::string& data) {
@@ -318,4 +332,8 @@ const JID& CoreClient::getJID() const {
 	}
 }
 
+void CoreClient::purgePassword() {
+	safeClear(password_);
+}
+
 }
diff --git a/Swiften/Client/CoreClient.h b/Swiften/Client/CoreClient.h
index 6dc8392..9806d35 100644
--- a/Swiften/Client/CoreClient.h
+++ b/Swiften/Client/CoreClient.h
@@ -199,6 +199,7 @@ namespace Swift {
 			void handlePresenceReceived(boost::shared_ptr<Presence>);
 			void handleMessageReceived(boost::shared_ptr<Message>);
 			void handleStanzaAcked(boost::shared_ptr<Stanza>);
+			void purgePassword();
 
 		private:
 			JID jid_;
diff --git a/Swiften/SASL/ClientAuthenticator.cpp b/Swiften/SASL/ClientAuthenticator.cpp
index b882d04..12636e2 100644
--- a/Swiften/SASL/ClientAuthenticator.cpp
+++ b/Swiften/SASL/ClientAuthenticator.cpp
@@ -1,17 +1,20 @@
 /*
- * Copyright (c) 2010 Remko Tronçon
+ * Copyright (c) 2010-2011 Remko Tronçon
  * Licensed under the GNU General Public License v3.
  * See Documentation/Licenses/GPLv3.txt for more information.
  */
 
 #include <Swiften/SASL/ClientAuthenticator.h>
 
+#include <Swiften/Base/Algorithm.h>
+
 namespace Swift {
 
 ClientAuthenticator::ClientAuthenticator(const std::string& name) : name(name) {
 }
 
 ClientAuthenticator::~ClientAuthenticator() {
+	safeClear(password);
 }
 
 }
-- 
cgit v0.10.2-6-g49f6