From 10e368538ec36b6a5f0c115196fb1177d547a64a Mon Sep 17 00:00:00 2001
From: Tobias Markmann <tm@ayena.de>
Date: Sun, 2 Sep 2012 14:30:24 +0200
Subject: Adding Qt based certificate viewer as fallback for Linux.

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

diff --git a/Swift/QtUI/QtCertificateViewerDialog.cpp b/Swift/QtUI/QtCertificateViewerDialog.cpp
new file mode 100644
index 0000000..2a1e32a
--- /dev/null
+++ b/Swift/QtUI/QtCertificateViewerDialog.cpp
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2012 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "QtCertificateViewerDialog.h"
+#include "ui_QtCertificateViewerDialog.h"
+
+#include <Swiften/Base/foreach.h>
+
+#include <QTreeWidgetItem>
+#include <QLabel>
+#include <QDateTime>
+
+namespace Swift {
+
+QtCertificateViewerDialog::QtCertificateViewerDialog(QWidget *parent) : QDialog(parent), ui(new Ui::QtCertificateViewerDialog) {
+	ui->setupUi(this);
+	connect(ui->certChainTreeWidget, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), SLOT(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)));
+
+	setAttribute(Qt::WA_DeleteOnClose);
+}
+
+QtCertificateViewerDialog::~QtCertificateViewerDialog() {
+	delete ui;
+}
+
+void QtCertificateViewerDialog::setCertificateChain(const std::vector<Certificate::ref> &chain) {
+	// clean widgets
+	ui->certChainTreeWidget->clear();
+
+	if (chain.empty()) return;
+
+	// convert Swift certificate chain to qt certificate chain (root goes first)
+	currentChain.clear();
+	foreach(Certificate::ref cert, chain) {
+		ByteArray certAsDer = cert->toDER();
+		QByteArray dataArray(reinterpret_cast<const char*>(certAsDer.data()), certAsDer.size());
+		currentChain.push_front(QSslCertificate(dataArray, QSsl::Der));
+	}
+
+	// fill treeWidget
+	QTreeWidgetItem* root = new QTreeWidgetItem(ui->certChainTreeWidget, QStringList(currentChain.at(0).subjectInfo(QSslCertificate::CommonName)));
+	root->setData(0, Qt::UserRole, QVariant(0));
+	root->setExpanded(true);
+	ui->certChainTreeWidget->addTopLevelItem(root);
+	if (currentChain.size() > 1) {
+		QTreeWidgetItem* parent = root;
+		for (int n = 1; n < currentChain.size(); n++) {
+			QTreeWidgetItem* link = new QTreeWidgetItem(parent, QStringList(QString("↳ ") + currentChain.at(n).subjectInfo(QSslCertificate::CommonName)));
+			link->setExpanded(true);
+			link->setData(0, Qt::UserRole, QVariant(n));
+			parent = link;
+		}
+		ui->certChainTreeWidget->setCurrentItem(parent);
+	} else {
+		ui->certChainTreeWidget->setCurrentItem(root);
+	}
+}
+
+void QtCertificateViewerDialog::displayCertificateChainAsSheet(QWidget *parent, const std::vector<Certificate::ref> &chain) {
+	QtCertificateViewerDialog* dialog = new QtCertificateViewerDialog(parent);
+	dialog->setCertificateChain(chain);
+	dialog->show();
+}
+
+void QtCertificateViewerDialog::currentItemChanged(QTreeWidgetItem* current, QTreeWidgetItem*) {
+	setCertificateDetails(currentChain.at(current->data(0, Qt::UserRole).toInt()));
+}
+
+#define ADD_SECTION( TITLE ) \
+	ui->certGridLayout->addWidget(new QLabel("<strong>" + TITLE + "</strong>"), rowCount++, 0, 1, 2);
+
+#define ADD_FIELD( TITLE, VALUE) \
+	ui->certGridLayout->addWidget(new QLabel(TITLE), rowCount, 0, 1, 1, Qt::AlignRight); \
+	valueLabel = new QLabel(VALUE); \
+	valueLabel->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); \
+	ui->certGridLayout->addWidget(valueLabel, rowCount++, 1, 1, 1, Qt::AlignLeft);
+
+void QtCertificateViewerDialog::setCertificateDetails(const QSslCertificate &cert) {
+	QLayoutItem* item;
+	while ((item = ui->certGridLayout->takeAt(0)) != NULL ) {
+		delete item->widget();
+		delete item;
+	}
+
+	int rowCount = 0;
+
+	ui->certGridLayout->setColumnStretch(2, 1);
+
+	QLabel* valueLabel = 0;
+
+	ADD_SECTION(tr("General"));
+	ADD_FIELD(tr("Valid From"), cert.effectiveDate().toString(Qt::TextDate));
+	ADD_FIELD(tr("Valid To"), cert.expiryDate().toString(Qt::TextDate));
+	ADD_FIELD(tr("Serial Number"), QString(cert.serialNumber().toHex()));
+	ADD_FIELD(tr("Version"), QString(cert.version()));
+
+	ADD_SECTION(tr("Subject"));
+	ADD_FIELD(tr("Organization"), cert.subjectInfo(QSslCertificate::Organization));
+	ADD_FIELD(tr("Common Name"), cert.subjectInfo(QSslCertificate::CommonName));
+	ADD_FIELD(tr("Locality"), cert.subjectInfo(QSslCertificate::LocalityName));
+	ADD_FIELD(tr("Organizational Unit"), cert.subjectInfo(QSslCertificate::OrganizationalUnitName));
+	ADD_FIELD(tr("Country"), cert.subjectInfo(QSslCertificate::CountryName));
+	ADD_FIELD(tr("State"), cert.subjectInfo(QSslCertificate::StateOrProvinceName));
+
+	if (!cert.alternateSubjectNames().empty()) {
+		ADD_SECTION(tr("Alternate Subject Names"));
+		QMultiMap<QSsl::AlternateNameEntryType, QString> altNames = cert.alternateSubjectNames();
+		foreach (const QSsl::AlternateNameEntryType &type, altNames.uniqueKeys()) {
+			foreach (QString name, altNames.values(type)) {
+				if (type == QSsl::EmailEntry) {
+					ADD_FIELD(tr("E-mail Address"), name);
+				} else {
+					ADD_FIELD(tr("DNS Name"), name);
+				}
+			}
+		}
+	}
+
+	ADD_SECTION(tr("Issuer"));
+	ADD_FIELD(tr("Organization"), cert.issuerInfo(QSslCertificate::Organization));
+	ADD_FIELD(tr("Common Name"), cert.issuerInfo(QSslCertificate::CommonName));
+	ADD_FIELD(tr("Locality"), cert.issuerInfo(QSslCertificate::LocalityName));
+	ADD_FIELD(tr("Organizational Unit"), cert.issuerInfo(QSslCertificate::OrganizationalUnitName));
+	ADD_FIELD(tr("Country"), cert.issuerInfo(QSslCertificate::CountryName));
+	ADD_FIELD(tr("State"), cert.issuerInfo(QSslCertificate::StateOrProvinceName));
+
+	ui->certGridLayout->setRowStretch(rowCount + 1, 1);
+}
+
+}
diff --git a/Swift/QtUI/QtCertificateViewerDialog.h b/Swift/QtUI/QtCertificateViewerDialog.h
new file mode 100644
index 0000000..9475a83
--- /dev/null
+++ b/Swift/QtUI/QtCertificateViewerDialog.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2012 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <QDialog>
+#include <QList>
+#include <QSslCertificate>
+#include <QTreeWidgetItem>
+
+#include <Swiften/TLS/Certificate.h>
+
+namespace Ui {
+class QtCertificateViewerDialog;
+}
+
+namespace Swift {
+
+class QtCertificateViewerDialog : public QDialog {
+	Q_OBJECT
+		
+	public:
+		explicit QtCertificateViewerDialog(QWidget* parent = 0);
+		~QtCertificateViewerDialog();
+
+		void setCertificateChain(const std::vector<Certificate::ref>& chain);
+
+		static void displayCertificateChainAsSheet(QWidget* parent, const std::vector<Certificate::ref>& chain);
+
+	private slots:
+		void currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*);
+
+	private:
+		void setCertificateDetails(const QSslCertificate& cert);
+
+	private:
+		Ui::QtCertificateViewerDialog *ui;
+		QList<QSslCertificate> currentChain;
+};
+
+}
diff --git a/Swift/QtUI/QtCertificateViewerDialog.ui b/Swift/QtUI/QtCertificateViewerDialog.ui
new file mode 100644
index 0000000..63a96bf
--- /dev/null
+++ b/Swift/QtUI/QtCertificateViewerDialog.ui
@@ -0,0 +1,131 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>QtCertificateViewerDialog</class>
+ <widget class="QDialog" name="QtCertificateViewerDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>655</width>
+    <height>514</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Certificate Viewer</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QGridLayout" name="gridLayout" rowstretch="0,0,0,0">
+   <item row="3" column="0">
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Ok</set>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="0">
+    <widget class="QScrollArea" name="scrollArea">
+     <property name="autoFillBackground">
+      <bool>false</bool>
+     </property>
+     <property name="horizontalScrollBarPolicy">
+      <enum>Qt::ScrollBarAlwaysOff</enum>
+     </property>
+     <property name="widgetResizable">
+      <bool>true</bool>
+     </property>
+     <widget class="QWidget" name="scrollAreaWidgetContents">
+      <property name="geometry">
+       <rect>
+        <x>0</x>
+        <y>0</y>
+        <width>629</width>
+        <height>368</height>
+       </rect>
+      </property>
+      <layout class="QVBoxLayout" name="verticalLayout">
+       <item>
+        <layout class="QGridLayout" name="certGridLayout"/>
+       </item>
+      </layout>
+     </widget>
+    </widget>
+   </item>
+   <item row="0" column="0">
+    <widget class="QTreeWidget" name="certChainTreeWidget">
+     <property name="maximumSize">
+      <size>
+       <width>16777215</width>
+       <height>70</height>
+      </size>
+     </property>
+     <property name="editTriggers">
+      <set>QAbstractItemView::NoEditTriggers</set>
+     </property>
+     <property name="showDropIndicator" stdset="0">
+      <bool>false</bool>
+     </property>
+     <property name="dragEnabled">
+      <bool>false</bool>
+     </property>
+     <property name="rootIsDecorated">
+      <bool>false</bool>
+     </property>
+     <property name="itemsExpandable">
+      <bool>false</bool>
+     </property>
+     <property name="expandsOnDoubleClick">
+      <bool>false</bool>
+     </property>
+     <attribute name="headerVisible">
+      <bool>false</bool>
+     </attribute>
+     <column>
+      <property name="text">
+       <string notr="true">1</string>
+      </property>
+     </column>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>QtCertificateViewerDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>QtCertificateViewerDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
diff --git a/Swift/QtUI/QtMainWindow.cpp b/Swift/QtUI/QtMainWindow.cpp
index ced375f..5d50c1e 100644
--- a/Swift/QtUI/QtMainWindow.cpp
+++ b/Swift/QtUI/QtMainWindow.cpp
@@ -41,6 +41,8 @@
 #include <Swift/QtUI/CocoaUIHelpers.h>
 #elif defined(SWIFTEN_PLATFORM_WINDOWS)
 #include <Swift/QtUI/WinUIHelpers.h>
+#else
+#include <Swift/QtUI/QtCertificateViewerDialog.h>
 #endif
 
 namespace Swift {
@@ -309,7 +311,7 @@ void QtMainWindow::openCertificateDialog(const std::vector<Certificate::ref>& ch
 #elif defined(SWIFTEN_PLATFORM_WINDOWS)
 	WinUIHelpers::displayCertificateChainAsSheet(parent, chain);
 #else
-#pragma message ("No certificate dialog available on this platform.")
+	QtCertificateViewerDialog::displayCertificateChainAsSheet(parent, chain);
 #endif
 }
 
diff --git a/Swift/QtUI/SConscript b/Swift/QtUI/SConscript
index 27ff237..a8a87de 100644
--- a/Swift/QtUI/SConscript
+++ b/Swift/QtUI/SConscript
@@ -51,6 +51,9 @@ myenv.Tool("textfile", toolpath = ["#/BuildTools/SCons/Tools"])
 qt4modules = ['QtCore', 'QtGui', 'QtWebKit']
 if env["PLATFORM"] == "posix" :
 	qt4modules += ["QtDBus"]
+if env["PLATFORM"] != "win32" and env["PLATFORM"] != "darwin" :
+	qt4modules += ["QtNetwork"]
+
 myenv.EnableQt4Modules(qt4modules, debug = False)
 
 myenv.Append(CPPPATH = ["."])
@@ -185,6 +188,8 @@ if env["PLATFORM"] == "darwin" or env["PLATFORM"] == "win32" :
   swiftProgram = myenv.Program("Swift", sources)
 else :
   swiftProgram = myenv.Program("swift-im", sources)
+  sources += ["QtCertificateViewerDialog.cpp"];
+  myenv.Uic4("QtCertificateViewerDialog.ui");
 
 if env["PLATFORM"] != "darwin" and env["PLATFORM"] != "win32" :
 	openURIProgram = myenv.Program("swift-open-uri", "swift-open-uri.cpp")
-- 
cgit v0.10.2-6-g49f6