From 159e773b156f531575d0d7e241e2d20c85ee6d7c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Remko=20Tron=C3=A7on?= <git@el-tramo.be>
Date: Sat, 12 May 2012 20:09:25 +0200
Subject: Show Certificate dialog from certificate error window.


diff --git a/Swift/Controllers/MainController.cpp b/Swift/Controllers/MainController.cpp
index 40b4ded..cb43057 100644
--- a/Swift/Controllers/MainController.cpp
+++ b/Swift/Controllers/MainController.cpp
@@ -559,9 +559,9 @@ void MainController::handleDisconnected(const boost::optional<ClientError>& erro
 		}
 		bool forceReconnectAfterCertificateTrust = false;
 		if (!certificateErrorMessage.empty()) {
-			Certificate::ref certificate = certificateTrustChecker_->getLastCertificate();
-			if (loginWindow_->askUserToTrustCertificatePermanently(certificateErrorMessage, certificate)) {
-				certificateStorage_->addCertificate(certificate);
+			std::vector<Certificate::ref> certificates = certificateTrustChecker_->getLastCertificateChain();
+			if (!certificates.empty() && loginWindow_->askUserToTrustCertificatePermanently(certificateErrorMessage, certificates)) {
+				certificateStorage_->addCertificate(certificates[0]);
 				forceReconnectAfterCertificateTrust = true;
 			}
 			else {
diff --git a/Swift/Controllers/Storages/CertificateStorageTrustChecker.h b/Swift/Controllers/Storages/CertificateStorageTrustChecker.h
index 40838dd..a73590a 100644
--- a/Swift/Controllers/Storages/CertificateStorageTrustChecker.h
+++ b/Swift/Controllers/Storages/CertificateStorageTrustChecker.h
@@ -18,17 +18,17 @@ namespace Swift {
 			CertificateStorageTrustChecker(CertificateStorage* storage) : storage(storage) {
 			}
 
-			virtual bool isCertificateTrusted(Certificate::ref certificate) {
-				lastCertificate = certificate;
-				return storage->hasCertificate(certificate);
+			virtual bool isCertificateTrusted(Certificate::ref, const std::vector<Certificate::ref>& certificateChain) {
+				lastCertificateChain = std::vector<Certificate::ref>(certificateChain.begin(), certificateChain.end());
+				return certificateChain.empty() ? false : storage->hasCertificate(certificateChain[0]);
 			}
 
-			Certificate::ref getLastCertificate() const {
-				return lastCertificate;
+			const std::vector<Certificate::ref>& getLastCertificateChain() const {
+				return lastCertificateChain;
 			}
 
 		private:
 			CertificateStorage* storage;
-			Certificate::ref lastCertificate;
+			std::vector<Certificate::ref> lastCertificateChain;
 	};
 }
diff --git a/Swift/Controllers/UIInterfaces/LoginWindow.h b/Swift/Controllers/UIInterfaces/LoginWindow.h
index bbbbe35..f1c2ce1 100644
--- a/Swift/Controllers/UIInterfaces/LoginWindow.h
+++ b/Swift/Controllers/UIInterfaces/LoginWindow.h
@@ -31,7 +31,7 @@ namespace Swift {
 			virtual void setLoginAutomatically(bool loginAutomatically) = 0;
 			virtual void quit() = 0;
 			/** Blocking request whether a cert should be permanently trusted.*/
-			virtual bool askUserToTrustCertificatePermanently(const std::string& message, Certificate::ref) = 0;
+			virtual bool askUserToTrustCertificatePermanently(const std::string& message, const std::vector<Certificate::ref>& certificateChain) = 0;
 
 			boost::signal<void ()> onCancelLoginRequest;
 			boost::signal<void ()> onQuitRequest;
diff --git a/Swift/QtUI/QtLoginWindow.cpp b/Swift/QtUI/QtLoginWindow.cpp
index 094f96c..42ebe39 100644
--- a/Swift/QtUI/QtLoginWindow.cpp
+++ b/Swift/QtUI/QtLoginWindow.cpp
@@ -506,20 +506,25 @@ void QtLoginWindow::moveEvent(QMoveEvent*) {
 	emit geometryChanged();
 }
 
-bool QtLoginWindow::askUserToTrustCertificatePermanently(const std::string& message, Certificate::ref certificate) {
+bool QtLoginWindow::askUserToTrustCertificatePermanently(const std::string& message, const std::vector<Certificate::ref>& certificates) {
 	QMessageBox dialog(this);
 
 	dialog.setText(tr("The certificate presented by the server is not valid."));
 	dialog.setInformativeText(P2QSTRING(message) + "\n\n" + tr("Would you like to permanently trust this certificate? This must only be done if you know it is correct."));
 
-	QString detailedText = tr("Subject: %1").arg(P2QSTRING(certificate->getSubjectName())) + "\n";
-	detailedText += tr("SHA-1 Fingerprint: %1").arg(P2QSTRING(certificate->getSHA1Fingerprint()));
-	dialog.setDetailedText(detailedText);
-
-	dialog.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
+	dialog.addButton("Show Certificate", QMessageBox::HelpRole);
+	dialog.addButton(QMessageBox::Yes);
+	dialog.addButton(QMessageBox::No);
 	dialog.setDefaultButton(QMessageBox::No);
-
-	return dialog.exec() == QMessageBox::Yes;
+	while (true) {
+		int result = dialog.exec();
+		if (result == QMessageBox::Yes || result == QMessageBox::No) {
+			return result == QMessageBox::Yes;
+		}
+		// FIXME: This isn't very nice, because the dialog disappears every time. We actually need a real
+		// dialog with a custom button.
+		QtMainWindow::openCertificateDialog(certificates, &dialog);
+	}
 }
 
 }
diff --git a/Swift/QtUI/QtLoginWindow.h b/Swift/QtUI/QtLoginWindow.h
index 8f50a35..572902e 100644
--- a/Swift/QtUI/QtLoginWindow.h
+++ b/Swift/QtUI/QtLoginWindow.h
@@ -48,7 +48,7 @@ namespace Swift {
 			virtual void setLoginAutomatically(bool loginAutomatically);
 			virtual void setIsLoggingIn(bool loggingIn);
 			void selectUser(const std::string& user);
-			bool askUserToTrustCertificatePermanently(const std::string& message, Certificate::ref certificate);
+			bool askUserToTrustCertificatePermanently(const std::string& message, const std::vector<Certificate::ref>& certificate);
 			void hide();
 			QtMenus getMenus() const;
 			virtual void quit();
diff --git a/Swift/QtUI/QtMainWindow.cpp b/Swift/QtUI/QtMainWindow.cpp
index 547f22b..ec05684 100644
--- a/Swift/QtUI/QtMainWindow.cpp
+++ b/Swift/QtUI/QtMainWindow.cpp
@@ -273,10 +273,14 @@ void QtMainWindow::setStreamEncryptionStatus(bool tlsInPlaceAndValid) {
 }
 
 void QtMainWindow::openCertificateDialog(const std::vector<Certificate::ref>& chain) {
+	openCertificateDialog(chain, this);
+}
+
+void QtMainWindow::openCertificateDialog(const std::vector<Certificate::ref>& chain, QWidget* parent) {
 #if defined(SWIFTEN_PLATFORM_MACOSX)
-	CocoaUIHelpers::displayCertificateChainAsSheet(this, chain);
+	CocoaUIHelpers::displayCertificateChainAsSheet(parent, chain);
 #elif defined(SWIFTEN_PLATFORM_WINDOWS)
-	WinUIHelpers::displayCertificateChainAsSheet(this,chain);
+	WinUIHelpers::displayCertificateChainAsSheet(parent, chain);
 #else
 #pragma message ("No certificate dialog available on this platform.")
 #endif
diff --git a/Swift/QtUI/QtMainWindow.h b/Swift/QtUI/QtMainWindow.h
index c725d08..25d9ace 100644
--- a/Swift/QtUI/QtMainWindow.h
+++ b/Swift/QtUI/QtMainWindow.h
@@ -47,6 +47,7 @@ namespace Swift {
 			void setConnecting();
 			void setStreamEncryptionStatus(bool tlsInPlaceAndValid);
 			void openCertificateDialog(const std::vector<Certificate::ref>& chain);
+			static void openCertificateDialog(const std::vector<Certificate::ref>& chain, QWidget* parent);
 			QtEventWindow* getEventWindow();
 			QtChatListWindow* getChatListWindow();
 			void setRosterModel(Roster* roster);
diff --git a/Swiften/Client/ClientSession.cpp b/Swiften/Client/ClientSession.cpp
index 8be8a8c..c2dc3ae 100644
--- a/Swiften/Client/ClientSession.cpp
+++ b/Swiften/Client/ClientSession.cpp
@@ -371,9 +371,10 @@ void ClientSession::handleTLSEncrypted() {
 	checkState(Encrypting);
 
 	Certificate::ref certificate = stream->getPeerCertificate();
+	std::vector<Certificate::ref> certificateChain = stream->getPeerCertificateChain();
 	boost::shared_ptr<CertificateVerificationError> verificationError = stream->getPeerCertificateVerificationError();
 	if (verificationError) {
-		checkTrustOrFinish(certificate, verificationError);
+		checkTrustOrFinish(certificate, certificateChain, verificationError);
 	}
 	else {
 		ServerIdentityVerifier identityVerifier(localJID);
@@ -381,13 +382,13 @@ void ClientSession::handleTLSEncrypted() {
 			continueAfterTLSEncrypted();
 		}
 		else {
-			checkTrustOrFinish(certificate, boost::make_shared<CertificateVerificationError>(CertificateVerificationError::InvalidServerIdentity));
+			checkTrustOrFinish(certificate, certificateChain, boost::make_shared<CertificateVerificationError>(CertificateVerificationError::InvalidServerIdentity));
 		}
 	}
 }
 
-void ClientSession::checkTrustOrFinish(Certificate::ref certificate, boost::shared_ptr<CertificateVerificationError> error) {
-	if (certificateTrustChecker && certificateTrustChecker->isCertificateTrusted(certificate)) {
+void ClientSession::checkTrustOrFinish(Certificate::ref certificate, const std::vector<Certificate::ref>& certificateChain, boost::shared_ptr<CertificateVerificationError> error) {
+	if (certificateTrustChecker && certificateTrustChecker->isCertificateTrusted(certificate, certificateChain)) {
 		continueAfterTLSEncrypted();
 	}
 	else {
diff --git a/Swiften/Client/ClientSession.h b/Swiften/Client/ClientSession.h
index b67b23d..9c4b980 100644
--- a/Swiften/Client/ClientSession.h
+++ b/Swiften/Client/ClientSession.h
@@ -154,7 +154,7 @@ namespace Swift {
 			void handleStanzaAcked(boost::shared_ptr<Stanza> stanza);
 			void ack(unsigned int handledStanzasCount);
 			void continueAfterTLSEncrypted();
-			void checkTrustOrFinish(Certificate::ref certificate, boost::shared_ptr<CertificateVerificationError> error);
+			void checkTrustOrFinish(Certificate::ref certificate, const std::vector<Certificate::ref>& certificateChain, boost::shared_ptr<CertificateVerificationError> error);
 
 		private:
 			JID localJID;
diff --git a/Swiften/TLS/BlindCertificateTrustChecker.h b/Swiften/TLS/BlindCertificateTrustChecker.h
index 3177322..9ed7ff2 100644
--- a/Swiften/TLS/BlindCertificateTrustChecker.h
+++ b/Swiften/TLS/BlindCertificateTrustChecker.h
@@ -19,7 +19,7 @@ namespace Swift {
 	 */
 	class BlindCertificateTrustChecker : public CertificateTrustChecker {
 		public:
-			virtual bool isCertificateTrusted(Certificate::ref) {
+			virtual bool isCertificateTrusted(Certificate::ref, const std::vector<Certificate::ref>&) {
 				return true;
 			}
 	};
diff --git a/Swiften/TLS/CertificateTrustChecker.h b/Swiften/TLS/CertificateTrustChecker.h
index 06c0c32..91cc530 100644
--- a/Swiften/TLS/CertificateTrustChecker.h
+++ b/Swiften/TLS/CertificateTrustChecker.h
@@ -10,6 +10,7 @@
 
 #include <string>
 #include <Swiften/TLS/Certificate.h>
+#include <vector>
 
 namespace Swift {
 	/**
@@ -23,7 +24,10 @@ namespace Swift {
 			 * This method is called to find out whether a certificate is
 			 * trusted. This usually happens when a certificate's validation
 			 * fails, to check whether to proceed with the connection or not.
+			 *
+			 * certificateChain contains the chain of certificates, if available.
+			 * This chain includes certificate.
 			 */
-			virtual bool isCertificateTrusted(Certificate::ref certificate) = 0;
+			virtual bool isCertificateTrusted(Certificate::ref certificate, const std::vector<Certificate::ref>& certificateChain) = 0;
 	};
 }
-- 
cgit v0.10.2-6-g49f6