File indexing completed on 2024-04-28 04:43:37

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"