From 2ff570e92c12b8ccc6f99d129b4f0dfba1548334 Mon Sep 17 00:00:00 2001 From: nowrep Date: Wed, 12 Oct 2011 22:28:41 +0200 Subject: [PATCH] Added new SSL Manager, it is now possible to specify location for custom CA Certificates --- bin/locale/qt_nl.qm | Bin 0 -> 144243 bytes src/3rdparty/qtwin.cpp | 4 +- src/app/mainapplication.cpp | 4 +- src/downloads/downloaditem.cpp | 12 +- src/network/networkmanager.cpp | 123 ++++++++++--- src/network/networkmanager.h | 27 ++- src/other/aboutdialog.cpp | 5 +- src/preferences/sslmanager.cpp | 167 ++++++++++++------ src/preferences/sslmanager.h | 21 ++- src/preferences/sslmanager.ui | 257 +++++++++++++++++++++++----- src/tools/certificateinfowidget.cpp | 117 ++++++++++++- src/tools/certificateinfowidget.h | 19 ++ src/tools/certificateinfowidget.ui | 25 +-- src/tools/globalfunctions.cpp | 22 ++- src/tools/globalfunctions.h | 2 + 15 files changed, 646 insertions(+), 159 deletions(-) create mode 100644 bin/locale/qt_nl.qm diff --git a/bin/locale/qt_nl.qm b/bin/locale/qt_nl.qm new file mode 100644 index 0000000000000000000000000000000000000000..ce9f56f47ceb665095cfcf2df671c317cd81068a GIT binary patch literal 144243 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�~7U*8|=89n}e;CZH}Xcor* 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 obj_collection->Release(); } -#endif //Q_WS_WIN #endif //W7API +#endif //Q_WS_WIN void QtWin::setupJumpList() { #ifdef W7API diff --git a/src/app/mainapplication.cpp b/src/app/mainapplication.cpp index e48fcb6bb..459ab91e5 100644 --- a/src/app/mainapplication.cpp +++ b/src/app/mainapplication.cpp @@ -165,7 +165,7 @@ MainApplication::MainApplication(const QList &cm settings2.setValue("Plugin-Settings/EnablePlugins", false); } - networkManager()->loadCertExceptions(); + networkManager()->loadCertificates(); plugins()->loadPlugins(); loadSettings(); @@ -403,7 +403,7 @@ void MainApplication::quitApplication() m_historymodel->clearHistory(); cookieJar()->saveCookies(); - m_networkmanager->saveCertExceptions(); + m_networkmanager->saveCertificates(); m_plugins->c2f_saveSettings(); AdBlockManager::instance()->save(); QFile::remove(getActiveProfilPath() + "WebpageIcons.db"); diff --git a/src/downloads/downloaditem.cpp b/src/downloads/downloaditem.cpp index fe0d166be..c9e066778 100644 --- a/src/downloads/downloaditem.cpp +++ b/src/downloads/downloaditem.cpp @@ -42,7 +42,7 @@ DownloadItem::DownloadItem(QListWidgetItem* item, QNetworkReply* reply, const QS #ifdef DOWNMANAGER_DEBUG qDebug() << __FUNCTION__ << item << reply << path << fileName; #endif - QString fullPath = path+fileName; + QString fullPath = path + fileName; if (QFile::exists(fullPath)) QFile::remove(fullPath); @@ -69,7 +69,7 @@ DownloadItem::DownloadItem(QListWidgetItem* item, QNetworkReply* reply, const QS connect(manager, SIGNAL(resized(QSize)), this, SLOT(parentResized(QSize))); m_downloading = true; - m_timer.start(1000*1, this); + m_timer.start(1000, this); readyRead(); QTimer::singleShot(500, this, SLOT(updateDownload())); @@ -281,13 +281,7 @@ void DownloadItem::customContextMenuRequested(const QPoint &pos) menu.addAction(tr("Go to Download Page"), this, SLOT(goToDownloadPage()))->setEnabled(!m_downloadPage.isEmpty()); menu.addAction(QIcon::fromTheme("edit-copy"), tr("Copy Download Link"), this, SLOT(copyDownloadLink())); menu.addSeparator(); - menu.addAction( -#ifdef Q_WS_X11 - style()->standardIcon(QStyle::SP_BrowserStop) -#else - QIcon(":/icons/faenza/stop.png") -#endif - ,tr("Cancel downloading"), this, SLOT(stop()))->setEnabled(m_downloading); + menu.addAction(IconProvider::standardIcon(QStyle::SP_BrowserStop), tr("Cancel downloading"), this, SLOT(stop()))->setEnabled(m_downloading); menu.addAction(QIcon::fromTheme("window-close"), tr("Clear"), this, SLOT(clear()))->setEnabled(!m_downloading); if (m_downloading || ui->downloadInfo->text().startsWith(tr("Cancelled")) || ui->downloadInfo->text().startsWith(tr("Error"))) diff --git a/src/network/networkmanager.cpp b/src/network/networkmanager.cpp index 5028120f7..e83d61b99 100644 --- a/src/network/networkmanager.cpp +++ b/src/network/networkmanager.cpp @@ -26,6 +26,8 @@ #include "adblocknetwork.h" #include "networkproxyfactory.h" #include "qupzillaschemehandler.h" +#include "certificateinfowidget.h" +#include "globalfunctions.h" NetworkManager::NetworkManager(QupZilla* mainClass, QObject* parent) : NetworkManagerProxy(mainClass, parent) @@ -55,7 +57,6 @@ void NetworkManager::loadSettings() m_diskCache->setMaximumCacheSize(settings.value("MaximumCacheSize",50).toInt() * 1024*1024); //MegaBytes setCache(m_diskCache); } - m_ignoreAllWarnings = settings.value("IgnoreAllSSLWarnings", false).toBool(); m_doNotTrack = settings.value("DoNotTrack", false).toBool(); settings.endGroup(); @@ -105,14 +106,14 @@ void NetworkManager::sslError(QNetworkReply* reply, QList errors) QStringList actions; foreach (QSslError error, errors) { - if (m_certExceptions.contains(error.certificate())) + if (m_localCerts.contains(error.certificate())) continue; if (error.error() == QSslError::NoError) //Weird behavior on Windows continue; QSslCertificate cert = error.certificate(); - actions.append(tr("Organization: ") + cert.subjectInfo(QSslCertificate::Organization)); - actions.append(tr("Domain Name: ") + cert.subjectInfo(QSslCertificate::CommonName)); + actions.append(tr("Organization: ") + CertificateInfoWidget::clearCertSpecialSymbols(cert.subjectInfo(QSslCertificate::Organization))); + actions.append(tr("Domain Name: ") + CertificateInfoWidget::clearCertSpecialSymbols(cert.subjectInfo(QSslCertificate::CommonName))); actions.append(tr("Expiration Date: ") + cert.expiryDate().toString("hh:mm:ss dddd d. MMMM yyyy")); actions.append(tr("Error: ") + error.errorString()); } @@ -130,9 +131,9 @@ void NetworkManager::sslError(QNetworkReply* reply, QList errors) } foreach (QSslError error, errors) { - if (m_certExceptions.contains(error.certificate())) + if (m_localCerts.contains(error.certificate())) continue; - m_certExceptions.append(error.certificate()); + addLocalCertificate(error.certificate()); } reply->ignoreSslErrors(errors); @@ -264,36 +265,102 @@ QNetworkReply* NetworkManager::createRequest(QNetworkAccessManager::Operation op return reply; } -void NetworkManager::saveCertExceptions() +void NetworkManager::removeLocalCertificate(const QSslCertificate &cert) { - QFile file(mApp->getActiveProfilPath()+"sslexceptions.dat"); - file.open(QIODevice::WriteOnly); - QDataStream stream(&file); + m_localCerts.removeOne(cert); + QList certs = QSslSocket::defaultCaCertificates(); + certs.removeOne(cert); + QSslSocket::setDefaultCaCertificates(certs); - int count = m_certExceptions.count(); - stream << count; + //Delete cert file from profile + QString certFileName = CertificateInfoWidget::certificateItemText(cert); + int startIndex = 0; + QDirIterator it(mApp->getActiveProfilPath() + "certificates", QDir::Files, QDirIterator::FollowSymlinks | QDirIterator::Subdirectories); + while (it.hasNext()) { + QString filePath = startIndex == 0 ? it.next() : it.next().mid(startIndex); + if (!filePath.contains(certFileName)) + continue; - for (int i = 0; i < count; i++) { - stream << m_certExceptions.at(i).toPem(); + QFile file(filePath); + file.remove(); + break; } - - file.close(); } -void NetworkManager::loadCertExceptions() +void NetworkManager::addLocalCertificate(const QSslCertificate &cert) { - QFile file(mApp->getActiveProfilPath()+"sslexceptions.dat"); - file.open(QIODevice::ReadOnly); - QDataStream stream(&file); + if (!cert.isValid()) + return; - int count; - stream >> count; - QByteArray cert; + m_localCerts.append(cert); + QSslSocket::addDefaultCaCertificate(cert); - for (int i = 0; i < count; i++) { - stream >> cert; - m_certExceptions.append(QSslCertificate::fromData(cert)); + QDir dir(mApp->getActiveProfilPath()); + if (!dir.exists("certificates")) + dir.mkdir("certificates"); + + QString fileName = qz_ensureUniqueFilename(mApp->getActiveProfilPath() + "certificates/" + CertificateInfoWidget::certificateItemText(cert).remove(" ") + ".crt"); + QFile file(fileName); + if (file.open(QFile::WriteOnly)) { + file.write(cert.toPem()); + file.close(); } - - file.close(); +} + +void NetworkManager::saveCertificates() +{ + QSettings settings(mApp->getActiveProfilPath() + "settings.ini", QSettings::IniFormat); + settings.beginGroup("SSL-Configuration"); + settings.setValue("CACertPaths", m_certPaths); + settings.setValue("IgnoreAllSSLWarnings", m_ignoreAllWarnings); + settings.endGroup(); +} + +void NetworkManager::loadCertificates() +{ + QSettings settings(mApp->getActiveProfilPath() + "settings.ini", QSettings::IniFormat); + settings.beginGroup("SSL-Configuration"); + m_certPaths = settings.value("CACertPaths", QStringList()).toStringList(); + m_ignoreAllWarnings = settings.value("IgnoreAllSSLWarnings", false).toBool(); + settings.endGroup(); + + //CA Certificates + m_caCerts = QSslSocket::defaultCaCertificates(); + foreach (QString path, m_certPaths) { +#ifdef Q_WS_WIN + // Used from Qt 4.7.4 qsslcertificate.cpp and modified because QSslCertificate::fromPath + // is kind of a bugged on Windows, it does work only with full path to cert file + int startIndex = 0; + QDirIterator it(path, QDir::Files, QDirIterator::FollowSymlinks | QDirIterator::Subdirectories); + while (it.hasNext()) { + QString filePath = startIndex == 0 ? it.next() : it.next().mid(startIndex); + if (!filePath.endsWith(".crt")) + continue; + + QFile file(filePath); + if (file.open(QIODevice::ReadOnly | QIODevice::Text)) + m_caCerts += QSslCertificate::fromData(file.readAll(), QSsl::Pem); + } +#else + m_caCerts += QSslCertificate::fromPath(path + "/*.crt", QSsl::Pem, QRegExp::Wildcard); +#endif + } + //Local Certificates +#ifdef Q_WS_WIN + int startIndex = 0; + QDirIterator it_(mApp->getActiveProfilPath() + "certificates", QDir::Files, QDirIterator::FollowSymlinks | QDirIterator::Subdirectories); + while (it_.hasNext()) { + QString filePath = startIndex == 0 ? it_.next() : it_.next().mid(startIndex); + if (!filePath.endsWith(".crt")) + continue; + + QFile file(filePath); + if (file.open(QIODevice::ReadOnly | QIODevice::Text)) + m_localCerts += QSslCertificate::fromData(file.readAll(), QSsl::Pem); + } +#else + m_localCerts += QSslCertificate::fromPath(mApp->getActiveProfilPath() + "certificates/*.crt", QSsl::Pem, QRegExp::Wildcard); +#endif + + QSslSocket::setDefaultCaCertificates(m_caCerts + m_localCerts); } diff --git a/src/network/networkmanager.h b/src/network/networkmanager.h index e4cb858aa..b45c6d5df 100644 --- a/src/network/networkmanager.h +++ b/src/network/networkmanager.h @@ -28,6 +28,8 @@ #include #include #include +#include +#include #include "networkmanagerproxy.h" @@ -42,10 +44,21 @@ public: explicit NetworkManager(QupZilla* mainClass, QObject* parent = 0); QNetworkReply* createRequest(QNetworkAccessManager::Operation op, const QNetworkRequest &request, QIODevice* outgoingData); - QList getCertExceptions() { return m_certExceptions; } - void setCertExceptions(QList certs) { m_certExceptions = certs; } - void saveCertExceptions(); - void loadCertExceptions(); + void saveCertificates(); + void loadCertificates(); + + QList getCaCertificates() { return m_caCerts; } + QList getLocalCertificates() { return m_localCerts; } + + void removeLocalCertificate(const QSslCertificate &cert); + void addLocalCertificate(const QSslCertificate &cert); + + void setCertificatePaths(const QStringList &paths) { m_certPaths = paths; } + QStringList certificatePaths() { return m_certPaths; } + + void setIgnoreAllWarnings(bool state) { m_ignoreAllWarnings = state; } + bool isIgnoringAllWarnings() { return m_ignoreAllWarnings; } + void loadSettings(); signals: @@ -62,12 +75,14 @@ private slots: private: AdBlockNetwork* m_adblockNetwork; QupZilla* p_QupZilla; - QList m_certExceptions; QNetworkDiskCache* m_diskCache; NetworkProxyFactory* m_proxyFactory; - QupZillaSchemeHandler* m_qupzillaSchemeHandler; + QStringList m_certPaths; + QList m_caCerts; + QList m_localCerts; + bool m_ignoreAllWarnings; bool m_doNotTrack; }; diff --git a/src/other/aboutdialog.cpp b/src/other/aboutdialog.cpp index abeb7a1ef..904a44bf9 100644 --- a/src/other/aboutdialog.cpp +++ b/src/other/aboutdialog.cpp @@ -70,8 +70,9 @@ void AboutDialog::showAuthors() if (m_authorsHtml.isEmpty()) { m_authorsHtml.append(""); } ui->textBrowser->setHtml(m_authorsHtml); diff --git a/src/preferences/sslmanager.cpp b/src/preferences/sslmanager.cpp index 7bb9aaa41..2b89fe6f3 100644 --- a/src/preferences/sslmanager.cpp +++ b/src/preferences/sslmanager.cpp @@ -19,86 +19,157 @@ #include "ui_sslmanager.h" #include "networkmanager.h" #include "mainapplication.h" +#include "globalfunctions.h" +#include "certificateinfowidget.h" -SSLManager::SSLManager(QWidget* parent) : - QWidget(parent), - ui(new Ui::SSLManager) +SSLManager::SSLManager(QWidget* parent) + : QWidget(parent) + , ui(new Ui::SSLManager) { setAttribute(Qt::WA_DeleteOnClose); ui->setupUi(this); + qz_centerWidgetOnScreen(this); - refresh(); + refreshLocalList(); + refreshCAList(); + refreshPaths(); - connect(ui->list, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(showCertificateInfo())); - connect(ui->infoButton, SIGNAL(clicked()), this, SLOT(showCertificateInfo())); + connect(ui->caList, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(showCaCertInfo())); + connect(ui->caInfoButton, SIGNAL(clicked()), this, SLOT(showCaCertInfo())); connect(ui->deleteButton, SIGNAL(clicked()), this, SLOT(deleteCertificate())); + + connect(ui->localList, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(showLocalCertInfo())); + connect(ui->localInfoButton, SIGNAL(clicked()), this, SLOT(showLocalCertInfo())); + + connect(ui->addPath, SIGNAL(clicked()), this, SLOT(addPath())); + connect(ui->deletePath, SIGNAL(clicked()), this, SLOT(deletePath())); connect(ui->ignoreAll, SIGNAL(clicked(bool)), this, SLOT(ignoreAll(bool))); - QSettings settings(mApp->getActiveProfilPath()+"settings.ini", QSettings::IniFormat); - settings.beginGroup("Web-Browser-Settings"); - ui->ignoreAll->setChecked( settings.value("IgnoreAllSSLWarnings", false).toBool() ); - settings.endGroup(); + ui->ignoreAll->setChecked(mApp->networkManager()->isIgnoringAllWarnings()); } -void SSLManager::refresh() +void SSLManager::addPath() { - ui->list->setUpdatesEnabled(false); - ui->list->clear(); - m_certs = mApp->networkManager()->getCertExceptions(); - foreach (QSslCertificate cert, m_certs) { - QListWidgetItem* item = new QListWidgetItem(ui->list); - item->setText( cert.subjectInfo(QSslCertificate::Organization) + " " + cert.subjectInfo(QSslCertificate::CommonName) ); - item->setWhatsThis(QString::number(m_certs.indexOf(cert))); - ui->list->addItem(item); + QString path = QFileDialog::getExistingDirectory(this, tr("Choose path...")); + if (path.isEmpty()) + return; + + ui->pathList->addItem(path); +} + +void SSLManager::deletePath() +{ + QListWidgetItem* currentItem = ui->pathList->currentItem(); + if (!currentItem) + return; + + delete currentItem; +} + +void SSLManager::refreshCAList() +{ + ui->caList->setUpdatesEnabled(false); + ui->caList->clear(); + m_caCerts = QSslSocket::defaultCaCertificates(); + foreach (QSslCertificate cert, m_caCerts) { + QListWidgetItem* item = new QListWidgetItem(ui->caList); + item->setText( CertificateInfoWidget::certificateItemText(cert) ); + item->setWhatsThis(QString::number(m_caCerts.indexOf(cert))); + ui->caList->addItem(item); } - ui->list->setCurrentRow(0); - ui->list->setUpdatesEnabled(true); + ui->caList->setCurrentRow(0); + ui->caList->setUpdatesEnabled(true); } -void SSLManager::showCertificateInfo() +void SSLManager::refreshLocalList() { - QListWidgetItem* item = ui->list->currentItem(); + ui->localList->setUpdatesEnabled(false); + ui->localList->clear(); + m_localCerts = mApp->networkManager()->getLocalCertificates(); + foreach (QSslCertificate cert, m_localCerts) { + QListWidgetItem* item = new QListWidgetItem(ui->localList); + item->setText( CertificateInfoWidget::certificateItemText(cert) ); + item->setWhatsThis(QString::number(m_localCerts.indexOf(cert))); + ui->localList->addItem(item); + } + ui->localList->setCurrentRow(0); + ui->localList->setUpdatesEnabled(true); +} + +void SSLManager::refreshPaths() +{ + foreach (QString path, mApp->networkManager()->certificatePaths()) + ui->pathList->addItem(path); +} + +void SSLManager::showCaCertInfo() +{ + QListWidgetItem* item = ui->caList->currentItem(); if (!item) return; - QSslCertificate cert = m_certs.at(item->whatsThis().toInt()); - QStringList actions; - actions.append(tr("Organization: ") + cert.subjectInfo(QSslCertificate::Organization)); - actions.append(tr("Domain Name: ") + cert.subjectInfo(QSslCertificate::CommonName)); - actions.append(tr("Locality Name: ") + cert.subjectInfo(QSslCertificate::LocalityName)); - actions.append(tr("Country Name: ") + cert.subjectInfo(QSslCertificate::CountryName)); - actions.append(tr("Verified by: ") + cert.subjectInfo(QSslCertificate::OrganizationalUnitName)); - actions.append(tr("Expiration Date: ") + cert.expiryDate().toString("hh:mm:ss dddd d. MMMM yyyy")); + QSslCertificate cert = m_caCerts.at(item->whatsThis().toInt()); + showCertificateInfo(cert); +} - QString message = QString(QLatin1String("
  • %3
")).arg(actions.join(QLatin1String("
  • "))); +void SSLManager::showLocalCertInfo() +{ + QListWidgetItem* item = ui->localList->currentItem(); + if (!item) + return; - QMessageBox mes; - mes.setIcon(QMessageBox::Information); - mes.setWindowTitle(tr("SSL Certificate Informations")); - mes.setText(message); - mes.setDetailedText(cert.toPem()); - mes.exec(); + QSslCertificate cert = m_localCerts.at(item->whatsThis().toInt()); + showCertificateInfo(cert); +} + +void SSLManager::showCertificateInfo(const QSslCertificate &cert) +{ + QWidget* w = new QWidget(); + w->setAttribute(Qt::WA_DeleteOnClose); + w->setWindowTitle(tr("Certificate Informations")); + w->setLayout(new QVBoxLayout); + CertificateInfoWidget* c = new CertificateInfoWidget(cert); + w->layout()->addWidget(c); + QDialogButtonBox* b = new QDialogButtonBox(w); + b->setStandardButtons(QDialogButtonBox::Close); + connect(b, SIGNAL(clicked(QAbstractButton*)), w, SLOT(close())); + w->layout()->addWidget(b); + w->resize(w->sizeHint()); + qz_centerWidgetOnScreen(w); + w->show(); } void SSLManager::deleteCertificate() { - QListWidgetItem* item = ui->list->currentItem(); + QListWidgetItem* item = ui->localList->currentItem(); if (!item) return; - QSslCertificate cert = m_certs.at(item->whatsThis().toInt()); - m_certs.removeOne(cert); - mApp->networkManager()->setCertExceptions(m_certs); - refresh(); + QSslCertificate cert = m_localCerts.at(item->whatsThis().toInt()); + m_localCerts.removeOne(cert); + mApp->networkManager()->removeLocalCertificate(cert); + refreshLocalList(); } void SSLManager::ignoreAll(bool state) { - QSettings settings(mApp->getActiveProfilPath()+"settings.ini", QSettings::IniFormat); - settings.beginGroup("Web-Browser-Settings"); - settings.setValue("IgnoreAllSSLWarnings", state); - settings.endGroup(); - mApp->networkManager()->loadSettings(); + mApp->networkManager()->setIgnoreAllWarnings(state); +} + +void SSLManager::closeEvent(QCloseEvent *e) +{ + QStringList paths; + for (int i = 0; i < ui->pathList->count(); i++) { + QListWidgetItem* item = ui->pathList->item(i); + if (!item || item->text().isEmpty()) + continue; + + paths.append(item->text()); + } + + mApp->networkManager()->setCertificatePaths(paths); + + QWidget::closeEvent(e); } SSLManager::~SSLManager() diff --git a/src/preferences/sslmanager.h b/src/preferences/sslmanager.h index 3adddcd36..3ad0c62d9 100644 --- a/src/preferences/sslmanager.h +++ b/src/preferences/sslmanager.h @@ -24,6 +24,8 @@ #include #include #include +#include +#include namespace Ui { class SSLManager; @@ -38,14 +40,27 @@ public: ~SSLManager(); private slots: - void showCertificateInfo(); + void showLocalCertInfo(); + void showCaCertInfo(); + void deleteCertificate(); void ignoreAll(bool state); + void addPath(); + void deletePath(); + private: - void refresh(); + void closeEvent(QCloseEvent *e); + + void refreshLocalList(); + void refreshCAList(); + void refreshPaths(); + + void showCertificateInfo(const QSslCertificate &cert); + Ui::SSLManager* ui; - QList m_certs; + QList m_localCerts; + QList m_caCerts; }; #endif // SSLMANAGER_H diff --git a/src/preferences/sslmanager.ui b/src/preferences/sslmanager.ui index 16c878640..cd9f37600 100644 --- a/src/preferences/sslmanager.ui +++ b/src/preferences/sslmanager.ui @@ -6,53 +6,228 @@ 0 0 - 473 - 250 + 653 + 389 SSL Manager - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Show info - - - - - - - Delete - - - - - - - - - Ignore all warnings + + + + + 0 + + + CA Authorities Certificates + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Show info + + + + + + + + + This is list of CA Authorities Certificates stored in standard system path and in user specified paths. + + + true + + + + + + + + Local Certificates + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Show info + + + + + + + Delete + + + + + + + + + This is list of Local Certificates stored in user profile. This list also contains all certificates, that have received an exception. + + + true + + + + + + + + Settings + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Add + + + + + + + Delete + + + + + + + + + If CA Authorities Certificates were not automatically loaded from system, You can specify manual paths where certificates are stored. + + + true + + + + + + + + + <b>NOTE:</b> Setting this option is big security vulnerability! + + + false + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 30 + 20 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Ignore all SSL Warnings + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + diff --git a/src/tools/certificateinfowidget.cpp b/src/tools/certificateinfowidget.cpp index a6eb5f3ad..f55f76696 100644 --- a/src/tools/certificateinfowidget.cpp +++ b/src/tools/certificateinfowidget.cpp @@ -1,11 +1,126 @@ +/* ============================================================ +* QupZilla - WebKit based browser +* Copyright (C) 2010-2011 nowrep +* +* 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 3 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. If not, see . +* ============================================================ */ #include "certificateinfowidget.h" #include "ui_certificateinfowidget.h" +#include + +QString CertificateInfoWidget::certificateItemText(const QSslCertificate &cert) +{ + QString commonName = cert.subjectInfo(QSslCertificate::CommonName); + QString organization = cert.subjectInfo(QSslCertificate::Organization); + + if (commonName.isEmpty()) + return clearCertSpecialSymbols(organization); + + return clearCertSpecialSymbols(commonName); +} + +QString CertificateInfoWidget::clearCertSpecialSymbols(const QString &string) +{ + if (!string.contains("\\")) + return string; + + QString n = string; + //Credits to http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/176679?help-en + n.replace("\\xC3\\x80", "A"); n.replace("\\xC3\\x81", "A"); n.replace("\\xC3\\x82", "A"); n.replace("\\xC3\\x83", "A"); + n.replace("\\xC3\\x84", "A"); n.replace("\\xC3\\x85", "A"); n.replace("\\xC3\\x86", "AE"); n.replace("\\xC3\\x87", "C"); + n.replace("\\xC3\\x88", "E"); n.replace("\\xC3\\x89", "E"); n.replace("\\xC3\\x8A", "E"); n.replace("\\xC3\\x8B", "E"); + n.replace("\\xC3\\x8C", "I"); n.replace("\\xC3\\x8D", "I"); n.replace("\\xC3\\x8E", "I"); n.replace("\\xC3\\x8F", "I"); + n.replace("\\xC3\\x90", "D"); n.replace("\\xC3\\x91", "N"); n.replace("\\xC3\\x92", "O"); n.replace("\\xC3\\x93", "O"); + n.replace("\\xC3\\x94", "O"); n.replace("\\xC3\\x95", "O"); n.replace("\\xC3\\x96", "O"); n.replace("\\xC3\\x98", "O"); + n.replace("\\xC3\\x99", "U"); n.replace("\\xC3\\x9A", "U"); n.replace("\\xC3\\x9B", "U"); n.replace("\\xC3\\x9C", "U"); + n.replace("\\xC3\\x9D", "Y"); n.replace("\\xC3\\x9E", "P"); n.replace("\\xC3\\x9F", "ss"); n.replace("\\xC9\\x99", "e"); + n.replace("\\xC3\\xA0", "a"); n.replace("\\xC3\\xA1", "a"); n.replace("\\xC3\\xA2", "a"); n.replace("\\xC3\\xA3", "a"); + n.replace("\\xC3\\xA4", "a"); n.replace("\\xC3\\xA5", "a"); n.replace("\\xC3\\xA6", "ae"); n.replace("\\xC3\\xA7", "c"); + n.replace("\\xC3\\xA8", "e"); n.replace("\\xC3\\xA9", "e"); n.replace("\\xC3\\xAA", "e"); n.replace("\\xC3\\xAB", "e"); + n.replace("\\xC3\\xAC", "i"); n.replace("\\xC3\\xAD", "i"); n.replace("\\xC3\\xAE", "i"); n.replace("\\xC3\\xAF", "i"); + n.replace("\\xC3\\xB0", "o"); n.replace("\\xC3\\xB1", "n"); n.replace("\\xC3\\xB2", "o"); n.replace("\\xC3\\xB3", "o"); + n.replace("\\xC3\\xB4", "o"); n.replace("\\xC3\\xB5", "o"); n.replace("\\xC3\\xB6", "o"); n.replace("\\xC3\\xB8", "o"); + n.replace("\\xC3\\xB9", "u"); n.replace("\\xC3\\xBA", "u"); n.replace("\\xC3\\xBB", "u"); n.replace("\\xC3\\xBC", "u"); + n.replace("\\xC3\\xBD", "y"); n.replace("\\xC3\\xBE", "p"); n.replace("\\xC3\\xBF", "y"); n.replace("\\xC7\\xBF", "o"); + n.replace("\\xC4\\x80", "A"); n.replace("\\xC4\\x81", "a"); n.replace("\\xC4\\x82", "A"); n.replace("\\xC4\\x83", "a"); + n.replace("\\xC4\\x84", "A"); n.replace("\\xC4\\x85", "a"); n.replace("\\xC4\\x86", "C"); n.replace("\\xC4\\x87", "c"); + n.replace("\\xC4\\x88", "C"); n.replace("\\xC4\\x89", "c"); n.replace("\\xC4\\x8A", "C"); n.replace("\\xC4\\x8B", "c"); + n.replace("\\xC4\\x8C", "C"); n.replace("\\xC4\\x8D", "c"); n.replace("\\xC4\\x8E", "D"); n.replace("\\xC4\\x8F", "d"); + n.replace("\\xC4\\x90", "D"); n.replace("\\xC4\\x91", "d"); n.replace("\\xC4\\x92", "E"); n.replace("\\xC4\\x93", "e"); + n.replace("\\xC4\\x94", "E"); n.replace("\\xC4\\x95", "e"); n.replace("\\xC4\\x96", "E"); n.replace("\\xC4\\x97", "e"); + n.replace("\\xC4\\x98", "E"); n.replace("\\xC4\\x99", "e"); n.replace("\\xC4\\x9A", "E"); n.replace("\\xC4\\x9B", "e"); + n.replace("\\xC4\\x9C", "G"); n.replace("\\xC4\\x9D", "g"); n.replace("\\xC4\\x9E", "G"); n.replace("\\xC4\\x9F", "g"); + n.replace("\\xC4\\xA0", "G"); n.replace("\\xC4\\xA1", "g"); n.replace("\\xC4\\xA2", "G"); n.replace("\\xC4\\xA3", "g"); + n.replace("\\xC4\\xA4", "H"); n.replace("\\xC4\\xA5", "h"); n.replace("\\xC4\\xA6", "H"); n.replace("\\xC4\\xA7", "h"); + n.replace("\\xC4\\xA8", "I"); n.replace("\\xC4\\xA9", "i"); n.replace("\\xC4\\xAA", "I"); n.replace("\\xC4\\xAB", "i"); + n.replace("\\xC4\\xAC", "I"); n.replace("\\xC4\\xAD", "i"); n.replace("\\xC4\\xAE", "I"); n.replace("\\xC4\\xAF", "i"); + n.replace("\\xC4\\xB0", "I"); n.replace("\\xC4\\xB1", "i"); n.replace("\\xC4\\xB2", "IJ"); n.replace("\\xC4\\xB3", "ij"); + n.replace("\\xC4\\xB4", "J"); n.replace("\\xC4\\xB5", "j"); n.replace("\\xC4\\xB6", "K"); n.replace("\\xC4\\xB7", "k"); + n.replace("\\xC4\\xB8", "k"); n.replace("\\xC4\\xB9", "L"); n.replace("\\xC4\\xBA", "l"); n.replace("\\xC4\\xBB", "L"); + n.replace("\\xC4\\xBC", "l"); n.replace("\\xC4\\xBD", "L"); n.replace("\\xC4\\xBE", "l"); n.replace("\\xC4\\xBF", "L"); + n.replace("\\xC5\\x80", "l"); n.replace("\\xC5\\x81", "L"); n.replace("\\xC5\\x82", "l"); n.replace("\\xC5\\x83", "N"); + n.replace("\\xC5\\x84", "n"); n.replace("\\xC5\\x85", "N"); n.replace("\\xC5\\x86", "n"); n.replace("\\xC5\\x87", "N"); + n.replace("\\xC5\\x88", "n"); n.replace("\\xC5\\x89", "n"); n.replace("\\xC5\\x8A", "N"); n.replace("\\xC5\\x8B", "n"); + n.replace("\\xC5\\x8C", "O"); n.replace("\\xC5\\x8D", "o"); n.replace("\\xC5\\x8E", "O"); n.replace("\\xC5\\x8F", "o"); + n.replace("\\xC5\\x90", "O"); n.replace("\\xC5\\x91", "o"); n.replace("\\xC5\\x92", "CE"); n.replace("\\xC5\\x93", "ce"); + n.replace("\\xC5\\x94", "R"); n.replace("\\xC5\\x95", "r"); n.replace("\\xC5\\x96", "R"); n.replace("\\xC5\\x97", "r"); + n.replace("\\xC5\\x98", "R"); n.replace("\\xC5\\x99", "r"); n.replace("\\xC5\\x9A", "S"); n.replace("\\xC5\\x9B", "s"); + n.replace("\\xC5\\x9C", "S"); n.replace("\\xC5\\x9D", "s"); n.replace("\\xC5\\x9E", "S"); n.replace("\\xC5\\x9F", "s"); + n.replace("\\xC5\\xA0", "S"); n.replace("\\xC5\\xA1", "s"); n.replace("\\xC5\\xA2", "T"); n.replace("\\xC5\\xA3", "t"); + n.replace("\\xC5\\xA4", "T"); n.replace("\\xC5\\xA5", "t"); n.replace("\\xC5\\xA6", "T"); n.replace("\\xC5\\xA7", "t"); + n.replace("\\xC5\\xA8", "U"); n.replace("\\xC5\\xA9", "u"); n.replace("\\xC5\\xAA", "U"); n.replace("\\xC5\\xAB", "u"); + n.replace("\\xC5\\xAC", "U"); n.replace("\\xC5\\xAD", "u"); n.replace("\\xC5\\xAE", "U"); n.replace("\\xC5\\xAF", "u"); + n.replace("\\xC5\\xB0", "U"); n.replace("\\xC5\\xB1", "u"); n.replace("\\xC5\\xB2", "U"); n.replace("\\xC5\\xB3", "u"); + n.replace("\\xC5\\xB4", "W"); n.replace("\\xC5\\xB5", "w"); n.replace("\\xC5\\xB6", "Y"); n.replace("\\xC5\\xB7", "y"); + n.replace("\\xC5\\xB8", "Y"); n.replace("\\xC5\\xB9", "Z"); n.replace("\\xC5\\xBA", "z"); n.replace("\\xC5\\xBB", "Z"); + n.replace("\\xC5\\xBC", "z"); n.replace("\\xC5\\xBD", "Z"); n.replace("\\xC5\\xBE", "z"); n.replace("\\xC6\\x8F", "E"); + n.replace("\\xC6\\xA0", "O"); n.replace("\\xC6\\xA1", "o"); n.replace("\\xC6\\xAF", "U"); n.replace("\\xC6\\xB0", "u"); + n.replace("\\xC7\\x8D", "A"); n.replace("\\xC7\\x8E", "a"); n.replace("\\xC7\\x8F", "I"); n.replace("\\xC7\\x93", "U"); + n.replace("\\xC7\\x90", "i"); n.replace("\\xC7\\x91", "O"); n.replace("\\xC7\\x92", "o"); n.replace("\\xC7\\x97", "U"); + n.replace("\\xC7\\x94", "u"); n.replace("\\xC7\\x95", "U"); n.replace("\\xC7\\x96", "u"); n.replace("\\xC7\\x9B", "U"); + n.replace("\\xC7\\x98", "u"); n.replace("\\xC7\\x99", "U"); n.replace("\\xC7\\x9A", "u"); n.replace("\\xC7\\xBD", "ae"); + n.replace("\\xC7\\x9C", "u"); n.replace("\\xC7\\xBB", "a"); n.replace("\\xC7\\xBC", "AE"); n.replace("\\xC7\\xBE", "O"); + n.replace("\\xC7\\xBA", "A"); + + n.replace("\\xC2\\x82", ","); // High code comma + n.replace("\\xC2\\x84", ",,"); // High code double comma + n.replace("\\xC2\\x85", "..."); // Tripple dot + n.replace("\\xC2\\x88", "^"); // High carat + n.replace("\\xC2\\x91", "\\x27"); // Forward single quote + n.replace("\\xC2\\x92", "\\x27"); // Reverse single quote + n.replace("\\xC2\\x93", "\\x22"); // Forward double quote + n.replace("\\xC2\\x94", "\\x22"); // Reverse double quote + n.replace("\\xC2\\x96", "-"); // High hyphen + n.replace("\\xC2\\x97", "--"); // Double hyphen + n.replace("\\xC2\\xA6", "|"); // Split vertical bar + n.replace("\\xC2\\xAB", "<<"); // Double less than + n.replace("\\xC2\\xBB", ">>"); // Double greater than + n.replace("\\xC2\\xBC", "1/4"); // one quarter + n.replace("\\xC2\\xBD", "1/2"); // one half + n.replace("\\xC2\\xBE", "3/4"); // three quarters + n.replace("\\xCA\\xBF", "\\x27"); // c-single quote + n.replace("\\xCC\\xA8", ""); // modifier - under curve + n.replace("\\xCC\\xB1", ""); // modifier - under line + + return n; +} QString CertificateInfoWidget::showCertInfo(const QString &string) { if (string.isEmpty()) return tr(""); - else return string; + else return clearCertSpecialSymbols(string); } CertificateInfoWidget::CertificateInfoWidget(const QSslCertificate &cert, QWidget *parent) diff --git a/src/tools/certificateinfowidget.h b/src/tools/certificateinfowidget.h index 0b36bdf15..c0eb6d486 100644 --- a/src/tools/certificateinfowidget.h +++ b/src/tools/certificateinfowidget.h @@ -1,3 +1,20 @@ +/* ============================================================ +* QupZilla - WebKit based browser +* Copyright (C) 2010-2011 nowrep +* +* 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 3 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. If not, see . +* ============================================================ */ #ifndef CERTIFICATEINFOWIDGET_H #define CERTIFICATEINFOWIDGET_H @@ -18,6 +35,8 @@ public: ~CertificateInfoWidget(); static QString showCertInfo(const QString &string); + static QString clearCertSpecialSymbols(const QString &string); + static QString certificateItemText(const QSslCertificate &cert); private: Ui::CertificateInfoWidget *ui; diff --git a/src/tools/certificateinfowidget.ui b/src/tools/certificateinfowidget.ui index 2a62bc51d..f559087cb 100644 --- a/src/tools/certificateinfowidget.ui +++ b/src/tools/certificateinfowidget.ui @@ -26,7 +26,7 @@ - + @@ -40,7 +40,7 @@ - + @@ -54,7 +54,7 @@ - + @@ -68,7 +68,7 @@ - + @@ -89,7 +89,7 @@ - + @@ -103,7 +103,7 @@ - + @@ -117,7 +117,7 @@ - + @@ -138,7 +138,7 @@ - + @@ -152,7 +152,7 @@ - + @@ -160,13 +160,6 @@ - - - SqueezeLabelV2 - QLabel -
    squeezelabelv2.h
    -
    -
    diff --git a/src/tools/globalfunctions.cpp b/src/tools/globalfunctions.cpp index 07e827897..3da51b9a5 100644 --- a/src/tools/globalfunctions.cpp +++ b/src/tools/globalfunctions.cpp @@ -56,7 +56,6 @@ QString qz_samePartOfStrings(const QString &one, const QString &other) } return one.left(i); } -#include QUrl qz_makeRelativeUrl(const QUrl &baseUrl, const QUrl &rUrl) { @@ -88,6 +87,27 @@ QUrl qz_makeRelativeUrl(const QUrl &baseUrl, const QUrl &rUrl) return returnUrl; } +QString qz_ensureUniqueFilename(const QString &pathToFile) +{ + if (!QFile::exists(pathToFile)) + return pathToFile; + + QString tmpFileName = pathToFile; + int i = 1; + while (QFile::exists(tmpFileName)) { + tmpFileName = pathToFile; + int index = tmpFileName.lastIndexOf("."); + + if (index == -1) { + tmpFileName.append("("+QString::number(i)+")"); + } else { + tmpFileName = tmpFileName.mid(0, index) + "("+QString::number(i)+")" + tmpFileName.mid(index); + } + i++; + } + return tmpFileName; +} + QString qz_buildSystem() { #ifdef Q_OS_LINUX diff --git a/src/tools/globalfunctions.h b/src/tools/globalfunctions.h index ae13102d7..9ea6cf5c5 100644 --- a/src/tools/globalfunctions.h +++ b/src/tools/globalfunctions.h @@ -34,6 +34,8 @@ void qz_centerWidgetOnScreen(QWidget* w); QString qz_samePartOfStrings(const QString &one, const QString &other); QUrl qz_makeRelativeUrl(const QUrl &baseUrl, const QUrl &rUrl); +QString qz_ensureUniqueFilename(const QString &name); + QString qz_buildSystem(); #endif // GLOBALFUNCTIONS_H
  • "); m_authorsHtml.append(tr("

    Main developers:
    %1 <%2>

    ").arg(QupZilla::AUTHOR, "
    nowrep@gmail.com")); - m_authorsHtml.append(tr("

    Other contributors:
    %1

    ").arg("Rajny :: Graphics
    Mikino :: Slovakia Translation")); - m_authorsHtml.append(tr("

    Thanks to:
    %1

    ").arg("Patrick :: First User")); + m_authorsHtml.append(tr("

    Other contributors:
    %1

    ").arg("Heimen Stoffels - Dutch Translation
    " + "Peter Vacula - Slovakia Translation")); + m_authorsHtml.append(tr("

    Thanks to:
    %1

    ").arg("Patrick for support in the beginning")); m_authorsHtml.append("