KIO
cacertificatespage.cpp
Go to the documentation of this file.
00001 /* This file is part of the KDE project 00002 Copyright (C) 2010 Andreas Hartmetz <ahartmetz@gmail.com> 00003 00004 This library is free software; you can redistribute it and/or 00005 modify it under the terms of the GNU Library General Public 00006 License as published by the Free Software Foundation; either 00007 version 2 of the License, or (at your option) any later version. 00008 00009 This program is distributed in the hope that it will be useful, 00010 but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 GNU General Public License for more details. 00013 00014 You should have received a copy of the GNU General Public License 00015 along with this program; if not, write to the Free Software 00016 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 00017 02110-1301, USA. 00018 */ 00019 00020 #include "cacertificatespage.h" 00021 #include "displaycertdialog_p.h" 00022 00023 #include <ksslcertificatemanager.h> 00024 #include <ksslcertificatemanager_p.h> 00025 00026 #include <kdebug.h> 00027 #include <kfiledialog.h> 00028 00029 #include <QList> 00030 #include <QSslCertificate> 00031 #include <QtGui/QTreeWidgetItem> 00032 #include <QtGui/QStandardItemModel> 00033 00034 enum Columns { 00035 OrgCnColumn = 0, 00036 OrgUnitColumn, 00037 HiddenSortColumn 00038 }; 00039 00040 00041 static QString nonemptyIssuer(const QSslCertificate &cert) 00042 { 00043 QString issuerText; 00044 static const QSslCertificate::SubjectInfo fields[3] = { 00045 QSslCertificate::Organization, 00046 QSslCertificate::CommonName, 00047 QSslCertificate::OrganizationalUnitName 00048 }; 00049 for (int i = 0; i < 3; i++) { 00050 issuerText = cert.issuerInfo(fields[i]); 00051 if (!issuerText.isEmpty()) { 00052 return issuerText; 00053 } 00054 } 00055 return issuerText; 00056 } 00057 00058 00059 class CaCertificateItem : public QTreeWidgetItem 00060 { 00061 public: 00062 CaCertificateItem(QTreeWidgetItem *parent, const QSslCertificate &cert, bool isEnabled) 00063 : QTreeWidgetItem(parent, m_type), 00064 m_cert(cert) 00065 { 00066 setEnabled(isEnabled); 00067 } 00068 00069 QVariant data(int column, int role) const 00070 { 00071 switch (role) { 00072 case Qt::DisplayRole: 00073 switch (column) { 00074 case OrgCnColumn: 00075 case HiddenSortColumn: { 00076 QString subjectText = m_cert.issuerInfo(QSslCertificate::CommonName); 00077 if (column == HiddenSortColumn) { 00078 return subjectText.toLower(); 00079 } 00080 return subjectText; } 00081 case OrgUnitColumn: 00082 return m_cert.issuerInfo(QSslCertificate::OrganizationalUnitName); 00083 } 00084 } 00085 00086 return QTreeWidgetItem::data(column, role); 00087 } 00088 00089 bool isEnabled() const 00090 { 00091 return data(OrgCnColumn, Qt::CheckStateRole).toInt() == Qt::Checked; 00092 } 00093 00094 void setEnabled(bool enabled) 00095 { 00096 setData(OrgCnColumn, Qt::CheckStateRole, enabled ? Qt::Checked : Qt::Unchecked); 00097 } 00098 00099 static const int m_type = QTreeWidgetItem::UserType; 00100 QSslCertificate m_cert; 00101 }; 00102 00103 CaCertificatesPage::CaCertificatesPage(QWidget *parent) 00104 : m_firstShowEvent(true), 00105 m_blockItemChanged(false) 00106 { 00107 m_ui.setupUi(this); 00108 connect(m_ui.displaySelection, SIGNAL(clicked()), SLOT(displaySelectionClicked())); 00109 connect(m_ui.disableSelection, SIGNAL(clicked()), SLOT(disableSelectionClicked())); 00110 connect(m_ui.enableSelection, SIGNAL(clicked()), SLOT(enableSelectionClicked())); 00111 connect(m_ui.removeSelection, SIGNAL(clicked()), SLOT(removeSelectionClicked())); 00112 connect(m_ui.add, SIGNAL(clicked()), SLOT(addCertificateClicked())); 00113 connect(m_ui.treeWidget, SIGNAL(itemChanged(QTreeWidgetItem *, int)), 00114 SLOT(itemChanged(QTreeWidgetItem *, int))); 00115 connect(m_ui.treeWidget, SIGNAL(itemSelectionChanged()), 00116 SLOT(itemSelectionChanged())); 00117 00118 m_ui.treeWidget->setColumnCount(HiddenSortColumn + 1); 00119 m_ui.treeWidget->setColumnHidden(HiddenSortColumn, true); 00120 } 00121 00122 00123 void CaCertificatesPage::load() 00124 { 00125 m_ui.treeWidget->clear(); 00126 m_ui.treeWidget->sortByColumn(-1); // disable during mass insertion 00127 m_knownCertificates.clear(); 00128 00129 m_systemCertificatesParent = new QTreeWidgetItem(m_ui.treeWidget); 00130 m_systemCertificatesParent->setText(0, i18n("System certificates")); 00131 // make system certificates come first in the sorted view 00132 m_systemCertificatesParent->setText(HiddenSortColumn, QLatin1String("a")); 00133 m_systemCertificatesParent->setExpanded(true); 00134 m_systemCertificatesParent->setFlags(m_systemCertificatesParent->flags() & ~Qt::ItemIsSelectable); 00135 00136 m_userCertificatesParent = new QTreeWidgetItem(m_ui.treeWidget); 00137 m_userCertificatesParent->setText(0, i18n("User-added certificates")); 00138 m_userCertificatesParent->setText(HiddenSortColumn, QLatin1String("b")); 00139 m_userCertificatesParent->setExpanded(true); 00140 m_userCertificatesParent->setFlags(m_userCertificatesParent->flags() & ~Qt::ItemIsSelectable); 00141 00142 QList<KSslCaCertificate> caCerts = _allKsslCaCertificates(KSslCertificateManager::self()); 00143 kDebug(7029) << "# certs:" << caCerts.count(); 00144 foreach (const KSslCaCertificate &caCert, caCerts) { 00145 addCertificateItem(caCert); 00146 } 00147 00148 m_ui.treeWidget->sortByColumn(HiddenSortColumn, Qt::AscendingOrder); 00149 } 00150 00151 void CaCertificatesPage::showEvent(QShowEvent *event) 00152 { 00153 if (m_firstShowEvent) { 00154 // TODO use QTextMetrics 00155 m_ui.treeWidget->setColumnWidth(OrgCnColumn, 420); 00156 m_firstShowEvent = false; 00157 } 00158 QWidget::showEvent(event); 00159 } 00160 00161 void CaCertificatesPage::save() 00162 { 00163 QList<KSslCaCertificate> newState; 00164 00165 KSslCaCertificate::Store store = KSslCaCertificate::SystemStore; 00166 QTreeWidgetItem *grandParent = m_systemCertificatesParent; 00167 00168 for (int i = 0; i < 2; i++) { 00169 for (int j = 0; j < grandParent->childCount(); j++) { 00170 00171 QTreeWidgetItem *parentItem = grandParent->child(j); 00172 for (int k = 0; k < parentItem->childCount(); k++) { 00173 CaCertificateItem *item = static_cast<CaCertificateItem *>(parentItem->child(k)); 00174 newState += KSslCaCertificate(item->m_cert, store, !item->isEnabled()); 00175 } 00176 } 00177 store = KSslCaCertificate::UserStore; 00178 grandParent = m_userCertificatesParent; 00179 } 00180 00181 kDebug(7029) << "# certs:" << newState.count(); 00182 _setAllKsslCaCertificates(KSslCertificateManager::self(), newState); 00183 emit changed(false); 00184 } 00185 00186 00187 void CaCertificatesPage::defaults() 00188 { 00189 //### is that all? 00190 load(); 00191 emit changed(false); 00192 } 00193 00194 // private slot 00195 void CaCertificatesPage::itemSelectionChanged() 00196 { 00197 kDebug(7029) << m_ui.treeWidget->selectionModel()->hasSelection(); 00198 int numRemovable = 0; 00199 int numEnabled = 0; 00200 int numDisplayable = 0; 00201 foreach(const QTreeWidgetItem *twItem, m_ui.treeWidget->selectedItems()) { 00202 const CaCertificateItem *item = dynamic_cast<const CaCertificateItem *>(twItem); 00203 Q_ASSERT(item); 00204 if (item) { 00205 numDisplayable++; 00206 if (item->parent()->parent() == m_userCertificatesParent) { 00207 numRemovable++; 00208 } 00209 if (item->isEnabled()) { 00210 numEnabled++; 00211 } 00212 } 00213 } 00214 m_ui.displaySelection->setEnabled(numDisplayable); 00215 m_ui.removeSelection->setEnabled(numRemovable); 00216 m_ui.disableSelection->setEnabled(numEnabled); 00217 m_ui.enableSelection->setEnabled(numDisplayable > numEnabled); // the rest is disabled 00218 } 00219 00220 // private slot 00221 void CaCertificatesPage::displaySelectionClicked() 00222 { 00223 QList<QSslCertificate> certs; 00224 foreach(const QTreeWidgetItem *twItem, m_ui.treeWidget->selectedItems()) { 00225 const CaCertificateItem *item = dynamic_cast<const CaCertificateItem *>(twItem); 00226 Q_ASSERT(item); 00227 if (item) { 00228 certs += item->m_cert; 00229 } 00230 } 00231 DisplayCertDialog dc(this); 00232 dc.setCertificates(certs); 00233 dc.exec(); 00234 } 00235 00236 // private slot 00237 void CaCertificatesPage::disableSelectionClicked() 00238 { 00239 enableDisableSelectionClicked(false); 00240 } 00241 00242 // private slot 00243 void CaCertificatesPage::enableSelectionClicked() 00244 { 00245 enableDisableSelectionClicked(true); 00246 } 00247 00248 void CaCertificatesPage::enableDisableSelectionClicked(bool isEnable) 00249 { 00250 const bool prevBlockItemChanged = m_blockItemChanged; 00251 m_blockItemChanged = true; 00252 foreach(QTreeWidgetItem *twItem, m_ui.treeWidget->selectedItems()) { 00253 CaCertificateItem *item = dynamic_cast<CaCertificateItem *>(twItem); 00254 Q_ASSERT(item); 00255 if (item) { 00256 item->setEnabled(isEnable); 00257 } 00258 } 00259 emit changed(true); 00260 m_blockItemChanged = prevBlockItemChanged; 00261 // now make sure that the buttons are enabled as appropriate 00262 itemSelectionChanged(); 00263 } 00264 00265 00266 // private slot 00267 void CaCertificatesPage::removeSelectionClicked() 00268 { 00269 bool didRemove = false; 00270 foreach(QTreeWidgetItem *twItem, m_ui.treeWidget->selectedItems()) { 00271 const CaCertificateItem *item = dynamic_cast<const CaCertificateItem *>(twItem); 00272 Q_ASSERT(item); 00273 if (!item || item->parent()->parent() != m_userCertificatesParent) { 00274 continue; 00275 } 00276 QTreeWidgetItem *parent = item->parent(); 00277 m_knownCertificates.remove(item->m_cert.digest().toHex()); 00278 delete item; 00279 didRemove = true; 00280 if (parent->childCount() == 0) { 00281 delete parent; 00282 } 00283 } 00284 if (didRemove) { 00285 emit changed(true); 00286 } 00287 } 00288 00289 // private slot 00290 void CaCertificatesPage::addCertificateClicked() 00291 { 00292 QStringList certFiles 00293 = KFileDialog::getOpenFileNames(KUrl(), QLatin1String("application/x-x509-ca-cert"), 00294 this, i18n("Pick Certificates")); 00295 00296 QList<QSslCertificate> certs; 00297 foreach (const QString &certFile, certFiles) { 00298 // trying both formats is easiest to program and most user-friendly if somewhat sloppy 00299 const int prevCertCount = certs.count(); 00300 certs += QSslCertificate::fromPath(certFile, QSsl::Pem); 00301 if (prevCertCount == certs.count()) { 00302 certs += QSslCertificate::fromPath(certFile, QSsl::Der); 00303 } 00304 if (prevCertCount == certs.count()) { 00305 kDebug(7029) << "failed to load certificate file" << certFile; 00306 } 00307 } 00308 00309 bool didAddCertificates = false; 00310 foreach (const QSslCertificate &cert, certs) { 00311 KSslCaCertificate caCert(cert, KSslCaCertificate::UserStore, false); 00312 if (!addCertificateItem(caCert)) { 00313 // ### tell the user? 00314 } else { 00315 didAddCertificates = true; 00316 } 00317 } 00318 if (didAddCertificates) { 00319 emit changed(true); 00320 } 00321 } 00322 00323 // private slot 00324 void CaCertificatesPage::itemChanged(QTreeWidgetItem *item, int column) 00325 { 00326 if (m_blockItemChanged) { 00327 return; 00328 } 00329 kDebug(7029); 00330 // we could try to emit changed(false) if everything was changed back to status quo 00331 00332 // a click on the checkbox of an unselected item first invokes itemSelectionChanged(), 00333 // then itemChanged(). we'll have to rerun the checks in itemSelectionChanged(). 00334 itemSelectionChanged(); 00335 emit changed(true); 00336 } 00337 00338 static QTreeWidgetItem *findImmediateChild(QTreeWidgetItem *parent, const QString &issuerText) 00339 { 00340 for (int i = 0; i < parent->childCount(); i ++) { 00341 QTreeWidgetItem *candidate = parent->child(i); 00342 if (candidate->text(OrgCnColumn) == issuerText) { 00343 return candidate; 00344 } 00345 } 00346 return 0; 00347 } 00348 00349 bool CaCertificatesPage::addCertificateItem(const KSslCaCertificate &caCert) 00350 { 00351 if (m_knownCertificates.contains(caCert.certHash)) { 00352 kDebug(7029) << "CaCertificatesPage::addCertificateItem(): refusing duplicate"; 00353 return false; 00354 } 00355 const bool prevBlockItemChanged = m_blockItemChanged; 00356 m_blockItemChanged = true; 00357 QTreeWidgetItem *grandParent = caCert.store == KSslCaCertificate::SystemStore ? 00358 m_systemCertificatesParent : m_userCertificatesParent; 00359 const QString issuerOrganization = nonemptyIssuer(caCert.cert); 00360 00361 QTreeWidgetItem *parent = findImmediateChild(grandParent, issuerOrganization); 00362 if (!parent) { 00363 parent = new QTreeWidgetItem(grandParent); 00364 parent->setText(OrgCnColumn, issuerOrganization); 00365 parent->setText(HiddenSortColumn, issuerOrganization.toLower()); 00366 parent->setExpanded(true); 00367 parent->setFlags(parent->flags() & ~Qt::ItemIsSelectable); 00368 } 00369 00370 CaCertificateItem *it = new CaCertificateItem(parent, caCert.cert, !caCert.isBlacklisted); 00371 m_knownCertificates.insert(caCert.certHash); 00372 m_blockItemChanged = prevBlockItemChanged; 00373 return true; 00374 } 00375 00376 #include "cacertificatespage.moc"
KDE 4.6 API Reference