From 0f91f88ac69644fb7e7bdbf601b7e098194490fa Mon Sep 17 00:00:00 2001
From: Tobias Markmann <tm@ayena.de>
Date: Fri, 4 May 2012 23:39:30 +0200
Subject: Showing stream encryption status in the roster header. Provide native
 certificate viewers on click.

Native viewers for Windows and Mac OS X are implemented.
Added TODOs to OpenSSL based TLS interface related to CRL and OCSP.

Resolves: #167

License: This patch is BSD-licensed, see http://www.opensource.org/licenses/bsd-license.php

diff --git a/BuildTools/SCons/SConscript.boot b/BuildTools/SCons/SConscript.boot
index a3a5c6d..098df0a 100644
--- a/BuildTools/SCons/SConscript.boot
+++ b/BuildTools/SCons/SConscript.boot
@@ -215,7 +215,7 @@ if env["PLATFORM"] == "win32" :
 		env["SHLINKCOM"] = [env["SHLINKCOM"], 'mt.exe -nologo -manifest ${TARGET}.manifest -outputresource:$TARGET;2']
 
 if env["PLATFORM"] == "darwin" and not env["target"] in ["iphone-device", "iphone-simulator", "xcode"] :
-	env.Append(FRAMEWORKS = ["IOKit", "AppKit", "SystemConfiguration"])
+	env.Append(FRAMEWORKS = ["IOKit", "AppKit", "SystemConfiguration", "Security", "SecurityInterface"])
 
 # Testing
 env["TEST_TYPE"] = env["test"]
diff --git a/Swift/Controllers/MainController.cpp b/Swift/Controllers/MainController.cpp
index 7bd89cb..40b4ded 100644
--- a/Swift/Controllers/MainController.cpp
+++ b/Swift/Controllers/MainController.cpp
@@ -72,6 +72,7 @@
 #include <Swiften/FileTransfer/FileTransferManager.h>
 #include <Swiften/Client/ClientXMLTracer.h>
 #include <Swift/Controllers/SettingConstants.h>
+#include <Swiften/Client/StanzaChannel.h>
 
 namespace Swift {
 
@@ -285,6 +286,7 @@ void MainController::handleConnected() {
 		rosterController_ = new RosterController(jid_, client_->getRoster(), client_->getAvatarManager(), uiFactory_, client_->getNickManager(), client_->getNickResolver(), client_->getPresenceOracle(), client_->getSubscriptionManager(), eventController_, uiEventStream_, client_->getIQRouter(), settings_, client_->getEntityCapsProvider(), ftOverview_);
 		rosterController_->onChangeStatusRequest.connect(boost::bind(&MainController::handleChangeStatusRequest, this, _1, _2));
 		rosterController_->onSignOutRequest.connect(boost::bind(&MainController::signOut, this));
+		rosterController_->getWindow()->onShowCertificateRequest.connect(boost::bind(&MainController::handleShowCertificateRequest, this));
 
 		contactEditController_ = new ContactEditController(rosterController_, client_->getVCardManager(), uiFactory_, uiEventStream_);
 
@@ -327,6 +329,7 @@ void MainController::handleConnected() {
 	client_->getVCardManager()->requestOwnVCard();
 	
 	rosterController_->setEnabled(true);
+	rosterController_->getWindow()->setStreamEncryptionStatus(client_->isStreamEncrypted());
 	profileController_->setAvailable(true);
 	contactEditController_->setAvailable(true);
 	/* Send presence later to catch all the incoming presences. */
@@ -417,6 +420,11 @@ void MainController::handleInputIdleChanged(bool idle) {
 	}
 }
 
+void MainController::handleShowCertificateRequest() {
+	std::vector<Certificate::ref> chain = client_->getStanzaChannel()->getPeerCertificateChain();
+	rosterController_->getWindow()->openCertificateDialog(chain);
+}
+
 void MainController::handleLoginRequest(const std::string &username, const std::string &password, const std::string& certificatePath, CertificateWithKey::ref certificate, bool remember, bool loginAutomatically) {
 	jid_ = JID(username);
 	if (!jid_.isValid() || jid_.getNode().empty()) {
diff --git a/Swift/Controllers/MainController.h b/Swift/Controllers/MainController.h
index 14de4eb..eeba9f3 100644
--- a/Swift/Controllers/MainController.h
+++ b/Swift/Controllers/MainController.h
@@ -103,6 +103,7 @@ namespace Swift {
 			void handlePurgeSavedLoginRequest(const std::string& username);
 			void sendPresence(boost::shared_ptr<Presence> presence);
 			void handleInputIdleChanged(bool);
+			void handleShowCertificateRequest();
 			void logout();
 			void signOut();
 			void setReconnectTimer();
diff --git a/Swift/Controllers/UIInterfaces/MainWindow.h b/Swift/Controllers/UIInterfaces/MainWindow.h
index 93584e7..23328b4 100644
--- a/Swift/Controllers/UIInterfaces/MainWindow.h
+++ b/Swift/Controllers/UIInterfaces/MainWindow.h
@@ -10,7 +10,7 @@
 #include "Swiften/JID/JID.h"
 #include "Swiften/Elements/StatusShow.h"
 #include "Swiften/Elements/DiscoItems.h"
-
+#include "Swiften/TLS/Certificate.h"
 #include "Swiften/Base/boost_bsignals.h"
 #include <boost/shared_ptr.hpp>
 
@@ -35,9 +35,12 @@ namespace Swift {
 			virtual void setRosterModel(Roster* roster) = 0;
 			virtual void setConnecting() = 0;
 			virtual void setAvailableAdHocCommands(const std::vector<DiscoItems::Item>& commands) = 0;
+			virtual void setStreamEncryptionStatus(bool tlsInPlaceAndValid) = 0;
+			virtual void openCertificateDialog(const std::vector<Certificate::ref>& chain) = 0;
 			
 			boost::signal<void (StatusShow::Type, const std::string&)> onChangeStatusRequest;
 			boost::signal<void ()> onSignOutRequest;
+			boost::signal<void ()> onShowCertificateRequest;
 
 		private:
 			bool canDelete_;
diff --git a/Swift/Controllers/UnitTest/MockMainWindow.h b/Swift/Controllers/UnitTest/MockMainWindow.h
index f773062..be1a932 100644
--- a/Swift/Controllers/UnitTest/MockMainWindow.h
+++ b/Swift/Controllers/UnitTest/MockMainWindow.h
@@ -22,6 +22,8 @@ namespace Swift {
 			virtual void setMyStatusType(StatusShow::Type /*type*/) {};
 			virtual void setAvailableAdHocCommands(const std::vector<DiscoItems::Item>& /*commands*/) {};
 			virtual void setConnecting() {};
+			virtual void setStreamEncryptionStatus(bool /*tlsInPlaceAndValid*/) {}
+			virtual void openCertificateDialog(const std::vector<Certificate::ref>& /*chain*/) {}
 			Roster* roster;
 
 	};
diff --git a/Swift/QtUI/CocoaUIHelpers.h b/Swift/QtUI/CocoaUIHelpers.h
new file mode 100644
index 0000000..25da0e3
--- /dev/null
+++ b/Swift/QtUI/CocoaUIHelpers.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2012 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/TLS/Certificate.h>
+#include <QWidget>
+
+namespace Swift {
+
+class CocoaUIHelpers {
+public:
+	static void displayCertificateChainAsSheet(QWidget* parent, const std::vector<Certificate::ref>& chain);
+};
+
+}
+
diff --git a/Swift/QtUI/CocoaUIHelpers.mm b/Swift/QtUI/CocoaUIHelpers.mm
new file mode 100644
index 0000000..3cb62f3
--- /dev/null
+++ b/Swift/QtUI/CocoaUIHelpers.mm
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2012 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "CocoaUIHelpers.h"
+
+#include <boost/shared_ptr.hpp>
+#include <boost/type_traits.hpp>
+
+#include <Cocoa/Cocoa.h>
+
+#include <Security/Security.h>
+#include <SecurityInterface/SFCertificatePanel.h>
+
+#include <Swiften/Base/foreach.h>
+
+#pragma GCC diagnostic ignored "-Wold-style-cast"
+
+namespace Swift {
+
+void CocoaUIHelpers::displayCertificateChainAsSheet(QWidget* parent, const std::vector<Certificate::ref>& chain) {
+	NSWindow* parentWindow = [((NSView*)parent->winId()) window];
+	NSMutableArray* certificates = [[NSMutableArray alloc] init];
+	foreach(Certificate::ref cert, chain) {
+		// convert chain to SecCertificateRef
+		ByteArray certAsDER = cert->toDER();
+		boost::shared_ptr<boost::remove_pointer<CFDataRef>::type> certData(CFDataCreate(NULL, certAsDER.data(), certAsDER.size()), CFRelease);
+		boost::shared_ptr<OpaqueSecCertificateRef> macCert(SecCertificateCreateWithData(NULL, certData.get()), CFRelease);
+
+		// add to NSMutable array
+		[certificates addObject: (id)macCert.get()];
+	}
+
+
+	SFCertificatePanel* panel = [[SFCertificatePanel alloc] init];
+	//[panel setPolicies:(id)policies.get()];
+	[panel beginSheetForWindow:parentWindow modalDelegate:nil didEndSelector:NULL contextInfo:NULL certificates:certificates showGroup:YES];
+	[certificates release];
+}
+
+}
diff --git a/Swift/QtUI/QtMainWindow.cpp b/Swift/QtUI/QtMainWindow.cpp
index 9a3ce6b..547f22b 100644
--- a/Swift/QtUI/QtMainWindow.cpp
+++ b/Swift/QtUI/QtMainWindow.cpp
@@ -34,6 +34,13 @@
 #include <Swift/Controllers/UIEvents/RequestAdHocUIEvent.h>
 #include <Swift/QtUI/QtUISettingConstants.h>
 #include <Swift/Controllers/SettingConstants.h>
+#include <Swiften/Base/Platform.h>
+
+#if defined(SWIFTEN_PLATFORM_MACOSX)
+#include <Swift/QtUI/CocoaUIHelpers.h>
+#elif defined(SWIFTEN_PLATFORM_WINDOWS)
+#include <Swift/QtUI/WinUIHelpers.h>
+#endif
 
 namespace Swift {
 
@@ -48,6 +55,7 @@ QtMainWindow::QtMainWindow(SettingsProvider* settings, UIEventStream* uiEventStr
 	mainLayout->addWidget(meView_);
 	connect(meView_, SIGNAL(onChangeStatusRequest(StatusShow::Type, const QString&)), this, SLOT(handleStatusChanged(StatusShow::Type, const QString&)));
 	connect(meView_, SIGNAL(onEditProfileRequest()), this, SLOT(handleEditProfileRequest()));
+	connect(meView_, SIGNAL(onShowCertificateInfo()), this, SLOT(handleShowCertificateInfo()));
 
 	tabs_ = new QtTabWidget(this);
 #if QT_VERSION >= 0x040500
@@ -153,6 +161,10 @@ void QtMainWindow::handleToggleRequestDeliveryReceipts(bool enabled) {
 	settings_->storeSetting(SettingConstants::REQUEST_DELIVERYRECEIPTS, enabled);
 }
 
+void QtMainWindow::handleShowCertificateInfo() {
+	onShowCertificateRequest();
+}
+
 QtEventWindow* QtMainWindow::getEventWindow() {
 	return eventWindow_;
 }
@@ -256,6 +268,20 @@ void QtMainWindow::setConnecting() {
 	meView_->setConnecting();
 }
 
+void QtMainWindow::setStreamEncryptionStatus(bool tlsInPlaceAndValid) {
+	meView_->setStreamEncryptionStatus(tlsInPlaceAndValid);
+}
+
+void QtMainWindow::openCertificateDialog(const std::vector<Certificate::ref>& chain) {
+#if defined(SWIFTEN_PLATFORM_MACOSX)
+	CocoaUIHelpers::displayCertificateChainAsSheet(this, chain);
+#elif defined(SWIFTEN_PLATFORM_WINDOWS)
+	WinUIHelpers::displayCertificateChainAsSheet(this,chain);
+#else
+#pragma message ("No certificate dialog available on this platform.")
+#endif
+}
+
 void QtMainWindow::handleAdHocActionTriggered(bool /*checked*/) {
 	QAction* action = qobject_cast<QAction*>(sender());
 	assert(action);
diff --git a/Swift/QtUI/QtMainWindow.h b/Swift/QtUI/QtMainWindow.h
index bef483d..c725d08 100644
--- a/Swift/QtUI/QtMainWindow.h
+++ b/Swift/QtUI/QtMainWindow.h
@@ -45,6 +45,8 @@ namespace Swift {
 			void setMyStatusText(const std::string& status);
 			void setMyStatusType(StatusShow::Type type);
 			void setConnecting();
+			void setStreamEncryptionStatus(bool tlsInPlaceAndValid);
+			void openCertificateDialog(const std::vector<Certificate::ref>& chain);
 			QtEventWindow* getEventWindow();
 			QtChatListWindow* getChatListWindow();
 			void setRosterModel(Roster* roster);
@@ -64,6 +66,7 @@ namespace Swift {
 			void handleEditProfileRequest();
 			void handleTabChanged(int index);
 			void handleToggleRequestDeliveryReceipts(bool enabled);
+			void handleShowCertificateInfo();
 
 		private:
 			SettingsProvider* settings_;
diff --git a/Swift/QtUI/QtRosterHeader.cpp b/Swift/QtUI/QtRosterHeader.cpp
index 98e75c2..2bce222 100644
--- a/Swift/QtUI/QtRosterHeader.cpp
+++ b/Swift/QtUI/QtRosterHeader.cpp
@@ -45,14 +45,27 @@ QtRosterHeader::QtRosterHeader(SettingsProvider* settings, QWidget* parent) : QW
 	rightLayout->setContentsMargins(4,0,0,0);
 	topLayout->addLayout(rightLayout);
 
+	QHBoxLayout* nameAndSecurityLayout = new QHBoxLayout();
+	nameAndSecurityLayout->setContentsMargins(4,0,0,0);
+
 	nameWidget_ = new QtNameWidget(settings, this);
 	connect(nameWidget_, SIGNAL(onChangeNickRequest()), this, SIGNAL(onEditProfileRequest()));
-	rightLayout->addWidget(nameWidget_);
+	nameAndSecurityLayout->addWidget(nameWidget_);
+
+	securityInfoButton_ = new QToolButton(this);
+
+	securityInfoButton_->setStyleSheet("border: none; hover: {border: 1px} pressed {border: 1px}");
+	// TODO: replace with a more appropriate icon
+	securityInfoButton_->setIcon(QIcon(":/icons/certificate.png"));
+	connect(securityInfoButton_, SIGNAL(clicked()), this, SIGNAL(onShowCertificateInfo()));
+	nameAndSecurityLayout->addWidget(securityInfoButton_);
 
+	rightLayout->addLayout(nameAndSecurityLayout);
 
 	statusWidget_ = new QtStatusWidget(this);
 	connect(statusWidget_, SIGNAL(onChangeStatusRequest(StatusShow::Type, const QString&)), this, SLOT(handleChangeStatusRequest(StatusShow::Type, const QString&)));
 	rightLayout->addWidget(statusWidget_);
+
 	show();
 }
 
@@ -72,6 +85,10 @@ void QtRosterHeader::setConnecting() {
 	statusWidget_->setConnecting();
 }
 
+void QtRosterHeader::setStreamEncryptionStatus(bool tlsInPlace) {
+	securityInfoButton_->setVisible(tlsInPlace);
+}
+
 void QtRosterHeader::setAvatar(const QString& path) {
 	QString scaledAvatarPath = QtScaledAvatarCache(avatarSize_).getScaledAvatarPath(path);
 	QPixmap avatar;
diff --git a/Swift/QtUI/QtRosterHeader.h b/Swift/QtUI/QtRosterHeader.h
index 050460c..1132ee3 100644
--- a/Swift/QtUI/QtRosterHeader.h
+++ b/Swift/QtUI/QtRosterHeader.h
@@ -11,6 +11,7 @@
 #include <QPixmap>
 #include <QSize>
 #include <QToolBar>
+#include <QToolButton>
 
 #include <string>
 #include "Swiften/Elements/StatusShow.h"
@@ -37,9 +38,11 @@ namespace Swift {
 		void setStatusText(const QString& statusMessage);
 		void setStatusType(StatusShow::Type type);
 		void setConnecting();
+		void setStreamEncryptionStatus(bool tlsInPlace);
 	signals:
 		void onChangeStatusRequest(StatusShow::Type showType, const QString &statusMessage);
 		void onEditProfileRequest();
+		void onShowCertificateInfo();
 
 	private slots:
 		void handleChangeStatusRequest(StatusShow::Type type, const QString &statusMessage);
@@ -50,6 +53,7 @@ namespace Swift {
 		QtTextEdit* statusEdit_;
 		QToolBar* toolBar_;
 		QtStatusWidget* statusWidget_;
+		QToolButton* securityInfoButton_;
 		static const int avatarSize_;
 	};
 }
diff --git a/Swift/QtUI/SConscript b/Swift/QtUI/SConscript
index f944b43..4c53313 100644
--- a/Swift/QtUI/SConscript
+++ b/Swift/QtUI/SConscript
@@ -161,6 +161,7 @@ if env["PLATFORM"] == "win32" :
 	# Adding it explicitly until i figure out why
   myenv.Depends(res, "../Controllers/BuildVersion.h")
   sources += [
+			"WinUIHelpers.cpp",
 			"CAPICertificateSelector.cpp",
 			"WindowsNotifier.cpp",
 			"#/Swift/resources/Windows/Swift.res"
@@ -174,6 +175,7 @@ if env["PLATFORM"] == "posix" :
 
 if env["PLATFORM"] == "darwin" :
 	sources += ["CocoaApplicationActivateHelper.mm"]
+	sources += ["CocoaUIHelpers.mm"]
 
 if env["PLATFORM"] == "darwin" or env["PLATFORM"] == "win32" :
   swiftProgram = myenv.Program("Swift", sources)
diff --git a/Swift/QtUI/WinUIHelpers.cpp b/Swift/QtUI/WinUIHelpers.cpp
new file mode 100644
index 0000000..edd1120
--- /dev/null
+++ b/Swift/QtUI/WinUIHelpers.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2012 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "WinUIHelpers.h"
+
+#include <windows.h>
+#include <Wincrypt.h>
+#include <cryptuiapi.h>
+#pragma comment(lib, "cryptui.lib")
+
+#include <boost/shared_ptr.hpp>
+
+#include <Swiften/Base/foreach.h>
+
+namespace Swift {
+
+void WinUIHelpers::displayCertificateChainAsSheet(QWidget* parent, const std::vector<Certificate::ref>& chain) {
+	if (chain.empty()) {
+		return;
+	}
+
+	// create certificate store to store the certificate chain in
+	HCERTSTORE chainStore = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, NULL, CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG, NULL);
+	if (!chainStore) {
+		return;
+	}
+
+	ByteArray certAsDER = chain[0]->toDER();
+	boost::shared_ptr<const CERT_CONTEXT> certificate_chain;
+	{
+		PCCERT_CONTEXT certChain;
+		BOOL ok = CertAddCertificateContextToStore(chainStore, CertCreateCertificateContext(X509_ASN_ENCODING, certAsDER.data(), certAsDER.size()), CERT_STORE_ADD_ALWAYS, &certChain);
+		// maybe free the cert contex we created
+		if (!ok || !certChain) {
+			return;
+		}
+		certificate_chain.reset(certChain, CertFreeCertificateContext);
+	}
+
+	for (size_t i = 1; i < chain.size(); ++i) {
+		ByteArray certAsDER = chain[i]->toDER();
+		CertAddCertificateContextToStore(chainStore, CertCreateCertificateContext(X509_ASN_ENCODING, certAsDER.data(), certAsDER.size()), CERT_STORE_ADD_ALWAYS, NULL);
+	}
+
+	CRYPTUI_VIEWCERTIFICATE_STRUCT viewDialogProperties = { 0 };
+	viewDialogProperties.dwSize = sizeof(viewDialogProperties);
+	viewDialogProperties.hwndParent = parent->winId();
+	viewDialogProperties.dwFlags = CRYPTUI_DISABLE_EDITPROPERTIES | CRYPTUI_DISABLE_ADDTOSTORE | CRYPTUI_ENABLE_REVOCATION_CHECKING;
+	viewDialogProperties.pCertContext = certificate_chain.get();
+	viewDialogProperties.cStores = 1;
+	viewDialogProperties.rghStores = &chainStore;
+	BOOL properties_changed;
+
+	// blocking call that shows modal certificate dialog
+	BOOL rv = ::CryptUIDlgViewCertificate(&viewDialogProperties, &properties_changed);
+}
+
+}
diff --git a/Swift/QtUI/WinUIHelpers.h b/Swift/QtUI/WinUIHelpers.h
new file mode 100644
index 0000000..d34d236
--- /dev/null
+++ b/Swift/QtUI/WinUIHelpers.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2012 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/TLS/Certificate.h>
+#include <QWidget>
+
+namespace Swift {
+
+class WinUIHelpers {
+public:
+	static void displayCertificateChainAsSheet(QWidget* parent, const std::vector<Certificate::ref>& chain);
+};
+
+}
+
diff --git a/Swiften/Client/ClientSession.h b/Swiften/Client/ClientSession.h
index 2205c8d..b67b23d 100644
--- a/Swiften/Client/ClientSession.h
+++ b/Swiften/Client/ClientSession.h
@@ -98,6 +98,10 @@ namespace Swift {
 				return rosterVersioningSupported;
 			}
 
+			std::vector<Certificate::ref> getPeerCertificateChain() const {
+				return stream->getPeerCertificateChain();
+			}
+
 			const JID& getLocalJID() const {
 				return localJID;
 			}
diff --git a/Swiften/Client/ClientSessionStanzaChannel.cpp b/Swiften/Client/ClientSessionStanzaChannel.cpp
index 5dc0a42..8d85953 100644
--- a/Swiften/Client/ClientSessionStanzaChannel.cpp
+++ b/Swiften/Client/ClientSessionStanzaChannel.cpp
@@ -82,6 +82,13 @@ bool ClientSessionStanzaChannel::getStreamManagementEnabled() const {
 	return false;
 }
 
+std::vector<Certificate::ref> ClientSessionStanzaChannel::getPeerCertificateChain() const {
+	if (session) {
+		return session->getPeerCertificateChain();
+	}
+	return std::vector<Certificate::ref>();
+}
+
 void ClientSessionStanzaChannel::handleStanzaAcked(boost::shared_ptr<Stanza> stanza) {
 	onStanzaAcked(stanza);
 }
diff --git a/Swiften/Client/ClientSessionStanzaChannel.h b/Swiften/Client/ClientSessionStanzaChannel.h
index 47fb50e..2743a16 100644
--- a/Swiften/Client/ClientSessionStanzaChannel.h
+++ b/Swiften/Client/ClientSessionStanzaChannel.h
@@ -27,6 +27,7 @@ namespace Swift {
 			void sendMessage(boost::shared_ptr<Message> message);
 			void sendPresence(boost::shared_ptr<Presence> presence);
 			bool getStreamManagementEnabled() const;
+			virtual std::vector<Certificate::ref> getPeerCertificateChain() const;
 
 			bool isAvailable() const {
 				return session && session->getState() == ClientSession::Initialized;
diff --git a/Swiften/Client/CoreClient.cpp b/Swiften/Client/CoreClient.cpp
index 8a922ba..36e27eb 100644
--- a/Swiften/Client/CoreClient.cpp
+++ b/Swiften/Client/CoreClient.cpp
@@ -360,6 +360,10 @@ bool CoreClient::getStreamManagementEnabled() const {
 	return stanzaChannel_->getStreamManagementEnabled();
 }
 
+bool CoreClient::isStreamEncrypted() const {
+	return sessionStream_->isTLSEncrypted();
+}
+
 StanzaChannel* CoreClient::getStanzaChannel() const {
 	return stanzaChannel_;
 }
diff --git a/Swiften/Client/CoreClient.h b/Swiften/Client/CoreClient.h
index cafc634..985bf7f 100644
--- a/Swiften/Client/CoreClient.h
+++ b/Swiften/Client/CoreClient.h
@@ -127,6 +127,11 @@ namespace Swift {
 			 */
 			bool getStreamManagementEnabled() const;
 
+			/**
+			 * Checks whether stream encryption (TLS) is currently active.
+			 */
+			bool isStreamEncrypted() const;
+
 			StanzaChannel* getStanzaChannel() const;
 
 			/**
diff --git a/Swiften/Client/DummyStanzaChannel.h b/Swiften/Client/DummyStanzaChannel.h
index c2f3919..5cdedba 100644
--- a/Swiften/Client/DummyStanzaChannel.h
+++ b/Swiften/Client/DummyStanzaChannel.h
@@ -79,6 +79,10 @@ namespace Swift {
 				return boost::dynamic_pointer_cast<T>(sentStanzas[index]);
 			}
 
+			std::vector<Certificate::ref> getPeerCertificateChain() const {
+				return std::vector<Certificate::ref>();
+			}
+
 			std::vector<boost::shared_ptr<Stanza> > sentStanzas;
 			bool available_;
 	};
diff --git a/Swiften/Client/StanzaChannel.h b/Swiften/Client/StanzaChannel.h
index f1d76e0..5e85d3c 100644
--- a/Swiften/Client/StanzaChannel.h
+++ b/Swiften/Client/StanzaChannel.h
@@ -12,6 +12,7 @@
 #include <Swiften/Queries/IQChannel.h>
 #include <Swiften/Elements/Message.h>
 #include <Swiften/Elements/Presence.h>
+#include <Swiften/TLS/Certificate.h>
 
 namespace Swift {
 	class StanzaChannel : public IQChannel {
@@ -20,6 +21,7 @@ namespace Swift {
 			virtual void sendPresence(boost::shared_ptr<Presence>) = 0;
 			virtual bool isAvailable() const = 0;
 			virtual bool getStreamManagementEnabled() const = 0;
+			virtual std::vector<Certificate::ref> getPeerCertificateChain() const = 0;
 
 			boost::signal<void (bool /* isAvailable */)> onAvailableChanged;
 			boost::signal<void (boost::shared_ptr<Message>)> onMessageReceived;
diff --git a/Swiften/Client/UnitTest/ClientSessionTest.cpp b/Swiften/Client/UnitTest/ClientSessionTest.cpp
index 6793643..d1ca70a 100644
--- a/Swiften/Client/UnitTest/ClientSessionTest.cpp
+++ b/Swiften/Client/UnitTest/ClientSessionTest.cpp
@@ -399,6 +399,10 @@ class ClientSessionTest : public CppUnit::TestFixture {
 					return Certificate::ref(new SimpleCertificate());
 				}
 
+				virtual std::vector<Certificate::ref> getPeerCertificateChain() const {
+					 return std::vector<Certificate::ref>();
+				}
+
 				virtual boost::shared_ptr<CertificateVerificationError> getPeerCertificateVerificationError() const {
 					return boost::shared_ptr<CertificateVerificationError>();
 				}
diff --git a/Swiften/Component/ComponentSessionStanzaChannel.h b/Swiften/Component/ComponentSessionStanzaChannel.h
index 45f90b5..4e133b8 100644
--- a/Swiften/Component/ComponentSessionStanzaChannel.h
+++ b/Swiften/Component/ComponentSessionStanzaChannel.h
@@ -31,6 +31,11 @@ namespace Swift {
 				return false;
 			}
 
+			std::vector<Certificate::ref> getPeerCertificateChain() const {
+				// TODO: actually implement this method
+				return std::vector<Certificate::ref>();
+			}
+
 			bool isAvailable() const {
 				return session && session->getState() == ComponentSession::Initialized;
 			}
diff --git a/Swiften/Component/UnitTest/ComponentSessionTest.cpp b/Swiften/Component/UnitTest/ComponentSessionTest.cpp
index da9ca7d..238d0b0 100644
--- a/Swiften/Component/UnitTest/ComponentSessionTest.cpp
+++ b/Swiften/Component/UnitTest/ComponentSessionTest.cpp
@@ -138,6 +138,10 @@ class ComponentSessionTest : public CppUnit::TestFixture {
 					return Certificate::ref();
 				}
 
+				virtual std::vector<Certificate::ref> getPeerCertificateChain() const {
+					 return std::vector<Certificate::ref>();
+				}
+
 				virtual boost::shared_ptr<CertificateVerificationError> getPeerCertificateVerificationError() const {
 					return boost::shared_ptr<CertificateVerificationError>();
 				}
diff --git a/Swiften/Session/BOSHSessionStream.cpp b/Swiften/Session/BOSHSessionStream.cpp
index 237a394..6f15b84 100644
--- a/Swiften/Session/BOSHSessionStream.cpp
+++ b/Swiften/Session/BOSHSessionStream.cpp
@@ -129,6 +129,10 @@ Certificate::ref BOSHSessionStream::getPeerCertificate() const {
 	return Certificate::ref();
 }
 
+std::vector<Certificate::ref> BOSHSessionStream::getPeerCertificateChain() const {
+	return std::vector<Certificate::ref>();
+}
+
 boost::shared_ptr<CertificateVerificationError> BOSHSessionStream::getPeerCertificateVerificationError() const {
 	return boost::shared_ptr<CertificateVerificationError>();
 }
diff --git a/Swiften/Session/BOSHSessionStream.h b/Swiften/Session/BOSHSessionStream.h
index 497d391..99851b5 100644
--- a/Swiften/Session/BOSHSessionStream.h
+++ b/Swiften/Session/BOSHSessionStream.h
@@ -61,6 +61,7 @@ namespace Swift {
 			virtual void addTLSEncryption();
 			virtual bool isTLSEncrypted();
 			virtual Certificate::ref getPeerCertificate() const;
+			virtual std::vector<Certificate::ref> getPeerCertificateChain() const;
 			virtual boost::shared_ptr<CertificateVerificationError> getPeerCertificateVerificationError() const;
 			virtual ByteArray getTLSFinishMessage() const;
 
diff --git a/Swiften/Session/BasicSessionStream.cpp b/Swiften/Session/BasicSessionStream.cpp
index b49ffc9..cbe85ab 100644
--- a/Swiften/Session/BasicSessionStream.cpp
+++ b/Swiften/Session/BasicSessionStream.cpp
@@ -129,6 +129,10 @@ Certificate::ref BasicSessionStream::getPeerCertificate() const {
 	return tlsLayer->getPeerCertificate();
 }
 
+std::vector<Certificate::ref> BasicSessionStream::getPeerCertificateChain() const {
+	return tlsLayer->getPeerCertificateChain();
+}
+
 boost::shared_ptr<CertificateVerificationError> BasicSessionStream::getPeerCertificateVerificationError() const {
 	return tlsLayer->getPeerCertificateVerificationError();
 }
diff --git a/Swiften/Session/BasicSessionStream.h b/Swiften/Session/BasicSessionStream.h
index e1f32f4..084a191 100644
--- a/Swiften/Session/BasicSessionStream.h
+++ b/Swiften/Session/BasicSessionStream.h
@@ -55,6 +55,8 @@ namespace Swift {
 			virtual void addTLSEncryption();
 			virtual bool isTLSEncrypted();
 			virtual Certificate::ref getPeerCertificate() const;
+			virtual std::vector<Certificate::ref> getPeerCertificateChain() const;
+
 			virtual boost::shared_ptr<CertificateVerificationError> getPeerCertificateVerificationError() const;
 			virtual ByteArray getTLSFinishMessage() const;
 
diff --git a/Swiften/Session/SessionStream.h b/Swiften/Session/SessionStream.h
index 32cb6b6..4735d5f 100644
--- a/Swiften/Session/SessionStream.h
+++ b/Swiften/Session/SessionStream.h
@@ -67,6 +67,7 @@ namespace Swift {
 			}
 
 			virtual Certificate::ref getPeerCertificate() const = 0;
+			virtual std::vector<Certificate::ref> getPeerCertificateChain() const = 0;
 			virtual boost::shared_ptr<CertificateVerificationError> getPeerCertificateVerificationError() const = 0;
 
 			virtual ByteArray getTLSFinishMessage() const = 0;
diff --git a/Swiften/StreamStack/TLSLayer.cpp b/Swiften/StreamStack/TLSLayer.cpp
index 6d416bc..86221ea 100644
--- a/Swiften/StreamStack/TLSLayer.cpp
+++ b/Swiften/StreamStack/TLSLayer.cpp
@@ -45,6 +45,10 @@ Certificate::ref TLSLayer::getPeerCertificate() const {
 	return context->getPeerCertificate();
 }
 
+std::vector<Certificate::ref> TLSLayer::getPeerCertificateChain() const {
+	return context->getPeerCertificateChain();
+}
+
 boost::shared_ptr<CertificateVerificationError> TLSLayer::getPeerCertificateVerificationError() const {
 	return context->getPeerCertificateVerificationError();
 }
diff --git a/Swiften/StreamStack/TLSLayer.h b/Swiften/StreamStack/TLSLayer.h
index ce0c89b..24978e0 100644
--- a/Swiften/StreamStack/TLSLayer.h
+++ b/Swiften/StreamStack/TLSLayer.h
@@ -26,6 +26,7 @@ namespace Swift {
 			bool setClientCertificate(CertificateWithKey::ref cert);
 
 			Certificate::ref getPeerCertificate() const;
+			std::vector<Certificate::ref> getPeerCertificateChain() const;
 			boost::shared_ptr<CertificateVerificationError> getPeerCertificateVerificationError() const;
 
 			void writeData(const SafeByteArray& data);
diff --git a/Swiften/TLS/OpenSSL/OpenSSLContext.cpp b/Swiften/TLS/OpenSSL/OpenSSLContext.cpp
index 8c03052..58a8d05 100644
--- a/Swiften/TLS/OpenSSL/OpenSSLContext.cpp
+++ b/Swiften/TLS/OpenSSL/OpenSSLContext.cpp
@@ -39,6 +39,12 @@ OpenSSLContext::OpenSSLContext() : state_(Start), context_(0), handle_(0), readB
 	ensureLibraryInitialized();
 	context_ = SSL_CTX_new(TLSv1_client_method());
 
+	// TODO: implement CRL checking
+	// TODO: download CRL (HTTP transport)
+	// TODO: cache CRL downloads for configurable time period
+
+	// TODO: implement OCSP support
+	// TODO: handle OCSP stapling see https://www.rfc-editor.org/rfc/rfc4366.txt
 	// Load system certs
 #if defined(SWIFTEN_PLATFORM_WINDOWS)
 	X509_STORE* store = SSL_CTX_get_cert_store(context_);
@@ -236,6 +242,18 @@ Certificate::ref OpenSSLContext::getPeerCertificate() const {
 	}
 }
 
+std::vector<Certificate::ref> OpenSSLContext::getPeerCertificateChain() const {
+	std::vector<Certificate::ref> result;
+	STACK_OF(X509)* chain = SSL_get_peer_cert_chain(handle_);
+	for (int i = 0; i < sk_X509_num(chain); ++i) {
+		boost::shared_ptr<X509> x509Cert(X509_dup(sk_X509_value(chain, i)), X509_free);
+
+		Certificate::ref cert = boost::make_shared<OpenSSLCertificate>(x509Cert);
+		result.push_back(cert);
+	}
+	return result;
+}
+
 boost::shared_ptr<CertificateVerificationError> OpenSSLContext::getPeerCertificateVerificationError() const {
 	int verifyResult = SSL_get_verify_result(handle_);
 	if (verifyResult != X509_V_OK) {
diff --git a/Swiften/TLS/OpenSSL/OpenSSLContext.h b/Swiften/TLS/OpenSSL/OpenSSLContext.h
index d8d0d2f..cee4f79 100644
--- a/Swiften/TLS/OpenSSL/OpenSSLContext.h
+++ b/Swiften/TLS/OpenSSL/OpenSSLContext.h
@@ -28,6 +28,7 @@ namespace Swift {
 			void handleDataFromApplication(const SafeByteArray&);
 
 			Certificate::ref getPeerCertificate() const;
+			std::vector<Certificate::ref> getPeerCertificateChain() const;
 			boost::shared_ptr<CertificateVerificationError> getPeerCertificateVerificationError() const;
 
 			virtual ByteArray getFinishMessage() const;
diff --git a/Swiften/TLS/Schannel/SchannelContext.cpp b/Swiften/TLS/Schannel/SchannelContext.cpp
index 641568d..997d760 100644
--- a/Swiften/TLS/Schannel/SchannelContext.cpp
+++ b/Swiften/TLS/Schannel/SchannelContext.cpp
@@ -633,6 +633,35 @@ Certificate::ref SchannelContext::getPeerCertificate() const {
 
 //------------------------------------------------------------------------
 
+std::vector<Certificate::ref> SchannelContext::getPeerCertificateChain() const {
+	std::vector<Certificate::ref> certificateChain;
+	ScopedCertContext pServerCert;
+	ScopedCertContext pIssuerCert;
+	ScopedCertContext pCurrentCert;
+	SECURITY_STATUS status = QueryContextAttributes(m_ctxtHandle, SECPKG_ATTR_REMOTE_CERT_CONTEXT, pServerCert.Reset());
+
+	if (status != SEC_E_OK) {
+		return certificateChain;
+	}
+	certificateChain.push_back(boost::make_shared<SchannelCertificate>(pServerCert));
+
+	pCurrentCert = pServerCert;
+	while(pCurrentCert.GetPointer()) {
+		DWORD dwVerificationFlags = 0;
+		pIssuerCert = CertGetIssuerCertificateFromStore(pServerCert->hCertStore, pCurrentCert, NULL, &dwVerificationFlags );
+		if (!(*pIssuerCert.GetPointer())) {
+			break;
+		}
+		certificateChain.push_back(boost::make_shared<SchannelCertificate>(pIssuerCert));
+
+		pCurrentCert = pIssuerCert;
+		pIssuerCert = NULL;
+	}
+	return certificateChain;
+}
+
+//------------------------------------------------------------------------
+
 CertificateVerificationError::ref SchannelContext::getPeerCertificateVerificationError() const {
 	return m_verificationError ? boost::make_shared<CertificateVerificationError>(*m_verificationError) : CertificateVerificationError::ref();
 }
diff --git a/Swiften/TLS/Schannel/SchannelContext.h b/Swiften/TLS/Schannel/SchannelContext.h
index 587d0e7..2d65a8a 100644
--- a/Swiften/TLS/Schannel/SchannelContext.h
+++ b/Swiften/TLS/Schannel/SchannelContext.h
@@ -51,6 +51,7 @@ namespace Swift
 		virtual void	handleDataFromApplication(const SafeByteArray& data);
 
 		virtual Certificate::ref getPeerCertificate() const;
+		virtual std::vector<Certificate::ref> getPeerCertificateChain() const;
 		virtual CertificateVerificationError::ref getPeerCertificateVerificationError() const;
 
 		virtual ByteArray getFinishMessage() const;
diff --git a/Swiften/TLS/TLSContext.h b/Swiften/TLS/TLSContext.h
index 5640fe1..388f8ee 100644
--- a/Swiften/TLS/TLSContext.h
+++ b/Swiften/TLS/TLSContext.h
@@ -29,6 +29,7 @@ namespace Swift {
 			virtual void handleDataFromApplication(const SafeByteArray&) = 0;
 
 			virtual Certificate::ref getPeerCertificate() const = 0;
+			virtual std::vector<Certificate::ref> getPeerCertificateChain() const = 0;
 			virtual CertificateVerificationError::ref getPeerCertificateVerificationError() const = 0;
 
 			virtual ByteArray getFinishMessage() const = 0;
-- 
cgit v0.10.2-6-g49f6