File indexing completed on 2024-09-08 04:18:38
0001 /* 0002 Copyright (C) 2007 Justin Karneges <justin@affinix.com> 0003 0004 Permission is hereby granted, free of charge, to any person obtaining a copy 0005 of this software and associated documentation files (the "Software"), to deal 0006 in the Software without restriction, including without limitation the rights 0007 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 0008 copies of the Software, and to permit persons to whom the Software is 0009 furnished to do so, subject to the following conditions: 0010 0011 The above copyright notice and this permission notice shall be included in 0012 all copies or substantial portions of the Software. 0013 0014 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 0015 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 0016 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 0017 AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 0018 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 0019 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 0020 */ 0021 0022 #include "keyselectdlg.h" 0023 0024 #include "ui_keyselect.h" 0025 #include <QMenu> 0026 #include <QMessageBox> 0027 #include <QPushButton> 0028 #include <QtCore> 0029 #include <QtCrypto> 0030 #include <QtGui> 0031 0032 #define ONLY_SHOW_KEYBUNDLE 0033 0034 typedef QMap<KeySelectDlg::IconType, QPixmap> KeyStoreIconset; 0035 0036 class KeyStoreItemShared 0037 { 0038 public: 0039 KeyStoreIconset iconset; 0040 QString notAvailableString; 0041 }; 0042 0043 class KeyStoreItem : public QStandardItem 0044 { 0045 public: 0046 enum Type 0047 { 0048 Store = UserType, 0049 Entry 0050 }; 0051 0052 enum Role 0053 { 0054 NameRole = Qt::UserRole, 0055 SubTypeRole, 0056 AvailabilityRole, 0057 PositionRole 0058 }; 0059 0060 QPixmap entryTypeToIcon(QCA::KeyStoreEntry::Type type) const 0061 { 0062 QPixmap out; 0063 if (!_shared) 0064 return out; 0065 const KeyStoreIconset &iconset = _shared->iconset; 0066 switch (type) { 0067 case QCA::KeyStoreEntry::TypeKeyBundle: 0068 out = iconset[KeySelectDlg::IconKeyBundle]; 0069 break; 0070 case QCA::KeyStoreEntry::TypeCertificate: 0071 out = iconset[KeySelectDlg::IconCert]; 0072 break; 0073 case QCA::KeyStoreEntry::TypeCRL: 0074 out = iconset[KeySelectDlg::IconCrl]; 0075 break; 0076 case QCA::KeyStoreEntry::TypePGPSecretKey: 0077 out = iconset[KeySelectDlg::IconPgpSec]; 0078 break; 0079 case QCA::KeyStoreEntry::TypePGPPublicKey: 0080 out = iconset[KeySelectDlg::IconPgpPub]; 0081 break; 0082 default: 0083 break; 0084 } 0085 return out; 0086 } 0087 0088 Type _type; 0089 KeyStoreItemShared *_shared; 0090 0091 QCA::KeyStore *keyStore; 0092 QCA::KeyStoreEntry keyStoreEntry; 0093 0094 KeyStoreItem(Type type, KeyStoreItemShared *shared) 0095 : _type(type) 0096 , _shared(shared) 0097 { 0098 setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); 0099 } 0100 0101 void setStore(const QString &name, QCA::KeyStore::Type type) 0102 { 0103 setData(name, NameRole); 0104 setData((int)type, SubTypeRole); 0105 } 0106 0107 void setEntry(const QString &name, QCA::KeyStoreEntry::Type type, bool available, int pos) 0108 { 0109 setData(name, NameRole); 0110 setData((int)type, SubTypeRole); 0111 setData(available, AvailabilityRole); 0112 setData(pos, PositionRole); 0113 } 0114 0115 virtual QVariant data(int role) const 0116 { 0117 if (role == Qt::DisplayRole) { 0118 if (_type == Store) { 0119 return data(NameRole).toString(); 0120 } else if (_type == Entry) { 0121 QString str = data(NameRole).toString(); 0122 if (_shared && !data(AvailabilityRole).toBool()) 0123 str += QString(" ") + _shared->notAvailableString; 0124 return str; 0125 } else 0126 return QStandardItem::data(role); 0127 } else if (role == Qt::DecorationRole) { 0128 if (_type == Entry) { 0129 QCA::KeyStoreEntry::Type type = (QCA::KeyStoreEntry::Type)data(SubTypeRole).toInt(); 0130 return entryTypeToIcon(type); 0131 } else 0132 return QStandardItem::data(role); 0133 } else 0134 return QStandardItem::data(role); 0135 } 0136 0137 virtual int type() const 0138 { 0139 return _type; 0140 } 0141 0142 virtual QStandardItem *clone() const 0143 { 0144 return new KeyStoreItem(*this); 0145 } 0146 }; 0147 0148 class KeyStoreModel : public QStandardItemModel 0149 { 0150 Q_OBJECT 0151 public: 0152 KeyStoreItemShared shared; 0153 0154 QCA::KeyStoreManager ksm; 0155 0156 KeyStoreModel(QObject *parent = 0) 0157 : QStandardItemModel(parent) 0158 , ksm(this) 0159 { 0160 shared.notAvailableString = tr("(not available)"); 0161 0162 // make sure keystores are started 0163 QCA::KeyStoreManager::start(); 0164 0165 connect(&ksm, SIGNAL(keyStoreAvailable(const QString &)), SLOT(ks_available(const QString &))); 0166 QStringList list = ksm.keyStores(); 0167 foreach (const QString &s, list) 0168 ks_available(s); 0169 0170 setSortRole(KeyStoreItem::PositionRole); 0171 } 0172 0173 KeyStoreItem *itemFromStore(QCA::KeyStore *ks) const 0174 { 0175 for (int n = 0; n < rowCount(); ++n) { 0176 KeyStoreItem *i = (KeyStoreItem *)item(n); 0177 if (i->keyStore == ks) 0178 return i; 0179 } 0180 return 0; 0181 } 0182 0183 private Q_SLOTS: 0184 void ks_available(const QString &keyStoreId) 0185 { 0186 QCA::KeyStore *ks = new QCA::KeyStore(keyStoreId, &ksm); 0187 0188 #ifdef ONLY_SHOW_KEYBUNDLE 0189 // only list stores containing keybundles (non-pgp identities) 0190 if (!ks->holdsIdentities() || ks->type() == QCA::KeyStore::PGPKeyring) 0191 return; 0192 #endif 0193 0194 connect(ks, SIGNAL(updated()), SLOT(ks_updated())); 0195 connect(ks, SIGNAL(unavailable()), SLOT(ks_unavailable())); 0196 0197 KeyStoreItem *store_item = new KeyStoreItem(KeyStoreItem::Store, &shared); 0198 store_item->setStore(ks->name(), ks->type()); 0199 store_item->keyStore = ks; 0200 ks->startAsynchronousMode(); 0201 appendRow(store_item); 0202 } 0203 0204 void ks_updated() 0205 { 0206 QCA::KeyStore *ks = (QCA::KeyStore *)sender(); 0207 KeyStoreItem *store_item = itemFromStore(ks); 0208 Q_ASSERT(store_item); 0209 0210 QList<QCA::KeyStoreEntry> newEntries = ks->entryList(); 0211 0212 #ifdef ONLY_SHOW_KEYBUNDLE 0213 // ignore entries that are not keybundles 0214 for (int n = 0; n < newEntries.count(); ++n) { 0215 if (newEntries[n].type() != QCA::KeyStoreEntry::TypeKeyBundle) { 0216 newEntries.removeAt(n); 0217 --n; // adjust position 0218 } 0219 } 0220 #endif 0221 0222 // update the store item itself 0223 store_item->setStore(ks->name(), ks->type()); 0224 0225 // handle removed child entries 0226 for (int n = 0; n < store_item->rowCount(); ++n) { 0227 KeyStoreItem *i = (KeyStoreItem *)store_item->child(n); 0228 0229 // is the existing entry in the new list? 0230 bool found = false; 0231 foreach (const QCA::KeyStoreEntry &ne, newEntries) { 0232 if (ne.id() == i->keyStoreEntry.id()) { 0233 found = true; 0234 break; 0235 } 0236 } 0237 0238 // if not, remove it 0239 if (!found) { 0240 store_item->removeRow(n); 0241 --n; // adjust position 0242 } 0243 } 0244 0245 // handle added/updated child entries 0246 for (int n = 0; n < newEntries.count(); ++n) { 0247 const QCA::KeyStoreEntry &ne = newEntries[n]; 0248 0249 // was this entry in the original list? 0250 KeyStoreItem *entry_item = 0; 0251 for (int k = 0; k < store_item->rowCount(); ++k) { 0252 KeyStoreItem *i = (KeyStoreItem *)store_item->child(k); 0253 if (i->keyStoreEntry.id() == ne.id()) { 0254 entry_item = i; 0255 break; 0256 } 0257 } 0258 0259 // if not, add it 0260 if (!entry_item) { 0261 entry_item = new KeyStoreItem(KeyStoreItem::Entry, &shared); 0262 entry_item->keyStoreEntry = ne; 0263 entry_item->setEntry(newEntries[n].name(), newEntries[n].type(), newEntries[n].isAvailable(), n); 0264 store_item->appendRow(entry_item); 0265 } 0266 // if so, update it 0267 else { 0268 entry_item->keyStoreEntry = ne; 0269 entry_item->setEntry(newEntries[n].name(), newEntries[n].type(), newEntries[n].isAvailable(), n); 0270 } 0271 } 0272 0273 store_item->sortChildren(0); 0274 } 0275 0276 void ks_unavailable() 0277 { 0278 QCA::KeyStore *ks = (QCA::KeyStore *)sender(); 0279 KeyStoreItem *store_item = itemFromStore(ks); 0280 Q_ASSERT(store_item); 0281 0282 store_item->removeRows(0, store_item->rowCount()); 0283 removeRow(store_item->row()); 0284 delete ks; 0285 } 0286 }; 0287 0288 class KeySelectDlg::Private : public QObject 0289 { 0290 Q_OBJECT 0291 public: 0292 KeySelectDlg *q; 0293 Ui_KeySelect ui; 0294 KeyStoreModel *model; 0295 QCA::KeyStoreEntry cur_entry; 0296 QAction *actionView; 0297 0298 Private(KeySelectDlg *_q) 0299 : QObject(_q) 0300 , q(_q) 0301 { 0302 ui.setupUi(q); 0303 0304 model = new KeyStoreModel(this); 0305 connect(&model->ksm, SIGNAL(busyStarted()), SLOT(ksm_busyStarted())); 0306 connect(&model->ksm, SIGNAL(busyFinished()), SLOT(ksm_busyFinished())); 0307 if (model->ksm.isBusy()) 0308 ksm_busyStarted(); 0309 0310 ui.lv_stores->header()->hide(); 0311 ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); 0312 ui.lv_stores->setModel(model); 0313 ui.lv_stores->setContextMenuPolicy(Qt::CustomContextMenu); 0314 connect(ui.lv_stores->selectionModel(), 0315 SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)), 0316 SLOT(stores_selectionChanged(const QItemSelection &, const QItemSelection &))); 0317 connect(ui.lv_stores, 0318 SIGNAL(customContextMenuRequested(const QPoint &)), 0319 SLOT(stores_customContextMenuRequested(const QPoint &))); 0320 0321 actionView = new QAction(tr("&View"), this); 0322 connect(actionView, SIGNAL(triggered()), SLOT(view())); 0323 actionView->setEnabled(false); 0324 } 0325 0326 private Q_SLOTS: 0327 void ksm_busyStarted() 0328 { 0329 ui.lb_busy->setText(tr("Looking for devices...")); 0330 } 0331 0332 void ksm_busyFinished() 0333 { 0334 ui.lb_busy->setText(""); 0335 } 0336 0337 void stores_selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) 0338 { 0339 Q_UNUSED(deselected); 0340 0341 KeyStoreItem *i = 0; 0342 if (!selected.indexes().isEmpty()) { 0343 QModelIndex index = selected.indexes().first(); 0344 i = (KeyStoreItem *)model->itemFromIndex(index); 0345 } 0346 0347 bool viewable = false; 0348 bool choosable = false; 0349 if (i && i->type() == KeyStoreItem::Entry) { 0350 QCA::KeyStoreEntry entry = i->keyStoreEntry; 0351 if (entry.type() == QCA::KeyStoreEntry::TypeKeyBundle) { 0352 viewable = true; 0353 choosable = true; 0354 cur_entry = entry; 0355 } 0356 } 0357 0358 if (!choosable) 0359 cur_entry = QCA::KeyStoreEntry(); 0360 0361 actionView->setEnabled(viewable); 0362 0363 QPushButton *ok = ui.buttonBox->button(QDialogButtonBox::Ok); 0364 if (choosable && !ok->isEnabled()) 0365 ok->setEnabled(true); 0366 else if (!choosable && ok->isEnabled()) 0367 ok->setEnabled(false); 0368 } 0369 0370 void stores_customContextMenuRequested(const QPoint &pos) 0371 { 0372 QItemSelection selection = ui.lv_stores->selectionModel()->selection(); 0373 if (selection.indexes().isEmpty()) 0374 return; 0375 0376 QModelIndex index = selection.indexes().first(); 0377 KeyStoreItem *i = (KeyStoreItem *)model->itemFromIndex(index); 0378 if (i && i->type() == KeyStoreItem::Entry) { 0379 QMenu menu(q); 0380 menu.addAction(actionView); 0381 menu.exec(ui.lv_stores->viewport()->mapToGlobal(pos)); 0382 } 0383 } 0384 0385 void view() 0386 { 0387 emit q->viewCertificate(cur_entry.keyBundle().certificateChain()); 0388 } 0389 }; 0390 0391 KeySelectDlg::KeySelectDlg(QWidget *parent) 0392 : QDialog(parent) 0393 { 0394 d = new Private(this); 0395 } 0396 0397 KeySelectDlg::~KeySelectDlg() 0398 { 0399 delete d; 0400 } 0401 0402 void KeySelectDlg::setIcon(IconType type, const QPixmap &icon) 0403 { 0404 d->model->shared.iconset[type] = icon; 0405 } 0406 0407 void KeySelectDlg::accept() 0408 { 0409 QCA::KeyStoreEntry entry = d->cur_entry; 0410 QDialog::accept(); 0411 emit selected(entry); 0412 } 0413 0414 #include "keyselectdlg.moc"