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 : QWidget(parent), 00105 m_firstShowEvent(true), 00106 m_blockItemChanged(false) 00107 { 00108 m_ui.setupUi(this); 00109 connect(m_ui.displaySelection, SIGNAL(clicked()), SLOT(displaySelectionClicked())); 00110 connect(m_ui.disableSelection, SIGNAL(clicked()), SLOT(disableSelectionClicked())); 00111 connect(m_ui.enableSelection, SIGNAL(clicked()), SLOT(enableSelectionClicked())); 00112 connect(m_ui.removeSelection, SIGNAL(clicked()), SLOT(removeSelectionClicked())); 00113 connect(m_ui.add, SIGNAL(clicked()), SLOT(addCertificateClicked())); 00114 connect(m_ui.treeWidget, SIGNAL(itemChanged(QTreeWidgetItem *, int)), 00115 SLOT(itemChanged(QTreeWidgetItem *, int))); 00116 connect(m_ui.treeWidget, SIGNAL(itemSelectionChanged()), 00117 SLOT(itemSelectionChanged())); 00118 00119 m_ui.treeWidget->setColumnCount(HiddenSortColumn + 1); 00120 m_ui.treeWidget->setColumnHidden(HiddenSortColumn, true); 00121 } 00122 00123 00124 void CaCertificatesPage::load() 00125 { 00126 m_ui.treeWidget->clear(); 00127 m_ui.treeWidget->sortByColumn(-1); // disable during mass insertion 00128 m_knownCertificates.clear(); 00129 00130 m_systemCertificatesParent = new QTreeWidgetItem(m_ui.treeWidget); 00131 m_systemCertificatesParent->setText(0, i18n("System certificates")); 00132 // make system certificates come first in the sorted view 00133 m_systemCertificatesParent->setText(HiddenSortColumn, QLatin1String("a")); 00134 m_systemCertificatesParent->setExpanded(true); 00135 m_systemCertificatesParent->setFlags(m_systemCertificatesParent->flags() & ~Qt::ItemIsSelectable); 00136 00137 m_userCertificatesParent = new QTreeWidgetItem(m_ui.treeWidget); 00138 m_userCertificatesParent->setText(0, i18n("User-added certificates")); 00139 m_userCertificatesParent->setText(HiddenSortColumn, QLatin1String("b")); 00140 m_userCertificatesParent->setExpanded(true); 00141 m_userCertificatesParent->setFlags(m_userCertificatesParent->flags() & ~Qt::ItemIsSelectable); 00142 00143 QList<KSslCaCertificate> caCerts = _allKsslCaCertificates(KSslCertificateManager::self()); 00144 kDebug(7029) << "# certs:" << caCerts.count(); 00145 foreach (const KSslCaCertificate &caCert, caCerts) { 00146 addCertificateItem(caCert); 00147 } 00148 00149 m_ui.treeWidget->sortByColumn(HiddenSortColumn, Qt::AscendingOrder); 00150 } 00151 00152 void CaCertificatesPage::showEvent(QShowEvent *event) 00153 { 00154 if (m_firstShowEvent) { 00155 // TODO use QTextMetrics 00156 m_ui.treeWidget->setColumnWidth(OrgCnColumn, 420); 00157 m_firstShowEvent = false; 00158 } 00159 QWidget::showEvent(event); 00160 } 00161 00162 void CaCertificatesPage::save() 00163 { 00164 QList<KSslCaCertificate> newState; 00165 00166 KSslCaCertificate::Store store = KSslCaCertificate::SystemStore; 00167 QTreeWidgetItem *grandParent = m_systemCertificatesParent; 00168 00169 for (int i = 0; i < 2; i++) { 00170 for (int j = 0; j < grandParent->childCount(); j++) { 00171 00172 QTreeWidgetItem *parentItem = grandParent->child(j); 00173 for (int k = 0; k < parentItem->childCount(); k++) { 00174 CaCertificateItem *item = static_cast<CaCertificateItem *>(parentItem->child(k)); 00175 newState += KSslCaCertificate(item->m_cert, store, !item->isEnabled()); 00176 } 00177 } 00178 store = KSslCaCertificate::UserStore; 00179 grandParent = m_userCertificatesParent; 00180 } 00181 00182 kDebug(7029) << "# certs:" << newState.count(); 00183 _setAllKsslCaCertificates(KSslCertificateManager::self(), newState); 00184 emit changed(false); 00185 } 00186 00187 00188 void CaCertificatesPage::defaults() 00189 { 00190 //### is that all? 00191 load(); 00192 emit changed(false); 00193 } 00194 00195 // private slot 00196 void CaCertificatesPage::itemSelectionChanged() 00197 { 00198 kDebug(7029) << m_ui.treeWidget->selectionModel()->hasSelection(); 00199 int numRemovable = 0; 00200 int numEnabled = 0; 00201 int numDisplayable = 0; 00202 foreach(const QTreeWidgetItem *twItem, m_ui.treeWidget->selectedItems()) { 00203 const CaCertificateItem *item = dynamic_cast<const CaCertificateItem *>(twItem); 00204 Q_ASSERT(item); 00205 if (item) { 00206 numDisplayable++; 00207 if (item->parent()->parent() == m_userCertificatesParent) { 00208 numRemovable++; 00209 } 00210 if (item->isEnabled()) { 00211 numEnabled++; 00212 } 00213 } 00214 } 00215 m_ui.displaySelection->setEnabled(numDisplayable); 00216 m_ui.removeSelection->setEnabled(numRemovable); 00217 m_ui.disableSelection->setEnabled(numEnabled); 00218 m_ui.enableSelection->setEnabled(numDisplayable > numEnabled); // the rest is disabled 00219 } 00220 00221 // private slot 00222 void CaCertificatesPage::displaySelectionClicked() 00223 { 00224 QList<QSslCertificate> certs; 00225 foreach(const QTreeWidgetItem *twItem, m_ui.treeWidget->selectedItems()) { 00226 const CaCertificateItem *item = dynamic_cast<const CaCertificateItem *>(twItem); 00227 Q_ASSERT(item); 00228 if (item) { 00229 certs += item->m_cert; 00230 } 00231 } 00232 DisplayCertDialog dc(this); 00233 dc.setCertificates(certs); 00234 dc.exec(); 00235 } 00236 00237 // private slot 00238 void CaCertificatesPage::disableSelectionClicked() 00239 { 00240 enableDisableSelectionClicked(false); 00241 } 00242 00243 // private slot 00244 void CaCertificatesPage::enableSelectionClicked() 00245 { 00246 enableDisableSelectionClicked(true); 00247 } 00248 00249 void CaCertificatesPage::enableDisableSelectionClicked(bool isEnable) 00250 { 00251 const bool prevBlockItemChanged = m_blockItemChanged; 00252 m_blockItemChanged = true; 00253 foreach(QTreeWidgetItem *twItem, m_ui.treeWidget->selectedItems()) { 00254 CaCertificateItem *item = dynamic_cast<CaCertificateItem *>(twItem); 00255 Q_ASSERT(item); 00256 if (item) { 00257 item->setEnabled(isEnable); 00258 } 00259 } 00260 emit changed(true); 00261 m_blockItemChanged = prevBlockItemChanged; 00262 // now make sure that the buttons are enabled as appropriate 00263 itemSelectionChanged(); 00264 } 00265 00266 00267 // private slot 00268 void CaCertificatesPage::removeSelectionClicked() 00269 { 00270 bool didRemove = false; 00271 foreach(QTreeWidgetItem *twItem, m_ui.treeWidget->selectedItems()) { 00272 const CaCertificateItem *item = dynamic_cast<const CaCertificateItem *>(twItem); 00273 Q_ASSERT(item); 00274 if (!item || item->parent()->parent() != m_userCertificatesParent) { 00275 continue; 00276 } 00277 QTreeWidgetItem *parent = item->parent(); 00278 m_knownCertificates.remove(item->m_cert.digest().toHex()); 00279 delete item; 00280 didRemove = true; 00281 if (parent->childCount() == 0) { 00282 delete parent; 00283 } 00284 } 00285 if (didRemove) { 00286 emit changed(true); 00287 } 00288 } 00289 00290 // private slot 00291 void CaCertificatesPage::addCertificateClicked() 00292 { 00293 QStringList certFiles 00294 = KFileDialog::getOpenFileNames(KUrl(), QLatin1String("application/x-x509-ca-cert"), 00295 this, i18n("Pick Certificates")); 00296 00297 QList<QSslCertificate> certs; 00298 foreach (const QString &certFile, certFiles) { 00299 // trying both formats is easiest to program and most user-friendly if somewhat sloppy 00300 const int prevCertCount = certs.count(); 00301 certs += QSslCertificate::fromPath(certFile, QSsl::Pem); 00302 if (prevCertCount == certs.count()) { 00303 certs += QSslCertificate::fromPath(certFile, QSsl::Der); 00304 } 00305 if (prevCertCount == certs.count()) { 00306 kDebug(7029) << "failed to load certificate file" << certFile; 00307 } 00308 } 00309 00310 bool didAddCertificates = false; 00311 foreach (const QSslCertificate &cert, certs) { 00312 KSslCaCertificate caCert(cert, KSslCaCertificate::UserStore, false); 00313 if (!addCertificateItem(caCert)) { 00314 // ### tell the user? 00315 } else { 00316 didAddCertificates = true; 00317 } 00318 } 00319 if (didAddCertificates) { 00320 emit changed(true); 00321 } 00322 } 00323 00324 // private slot 00325 void CaCertificatesPage::itemChanged(QTreeWidgetItem *item, int column) 00326 { 00327 Q_UNUSED(item) 00328 Q_UNUSED(column) 00329 00330 if (m_blockItemChanged) { 00331 return; 00332 } 00333 kDebug(7029); 00334 // we could try to emit changed(false) if everything was changed back to status quo 00335 00336 // a click on the checkbox of an unselected item first invokes itemSelectionChanged(), 00337 // then itemChanged(). we'll have to rerun the checks in itemSelectionChanged(). 00338 itemSelectionChanged(); 00339 emit changed(true); 00340 } 00341 00342 static QTreeWidgetItem *findImmediateChild(QTreeWidgetItem *parent, const QString &issuerText) 00343 { 00344 for (int i = 0; i < parent->childCount(); i ++) { 00345 QTreeWidgetItem *candidate = parent->child(i); 00346 if (candidate->text(OrgCnColumn) == issuerText) { 00347 return candidate; 00348 } 00349 } 00350 return 0; 00351 } 00352 00353 bool CaCertificatesPage::addCertificateItem(const KSslCaCertificate &caCert) 00354 { 00355 if (m_knownCertificates.contains(caCert.certHash)) { 00356 kDebug(7029) << "CaCertificatesPage::addCertificateItem(): refusing duplicate"; 00357 return false; 00358 } 00359 const bool prevBlockItemChanged = m_blockItemChanged; 00360 m_blockItemChanged = true; 00361 QTreeWidgetItem *grandParent = caCert.store == KSslCaCertificate::SystemStore ? 00362 m_systemCertificatesParent : m_userCertificatesParent; 00363 const QString issuerOrganization = nonemptyIssuer(caCert.cert); 00364 00365 QTreeWidgetItem *parent = findImmediateChild(grandParent, issuerOrganization); 00366 if (!parent) { 00367 parent = new QTreeWidgetItem(grandParent); 00368 parent->setText(OrgCnColumn, issuerOrganization); 00369 parent->setText(HiddenSortColumn, issuerOrganization.toLower()); 00370 parent->setExpanded(true); 00371 parent->setFlags(parent->flags() & ~Qt::ItemIsSelectable); 00372 } 00373 00374 (void) new CaCertificateItem(parent, caCert.cert, !caCert.isBlacklisted); 00375 m_knownCertificates.insert(caCert.certHash); 00376 m_blockItemChanged = prevBlockItemChanged; 00377 return true; 00378 } 00379 00380 #include "cacertificatespage.moc"
KDE 4.7 API Reference