Improve column handling

right-align numeric columns and make them monospaced.
Save their current state in the database.
Also add column-menu to the other context menu

fix minor bugs

Support for "Unstructured Name" and "challange password" columns
	in the request-table
Support for name hash as used by OpenSSL to lookup certs.
	As table column and as distinguished name information
This commit is contained in:
Christian Hohnstaedt 2010-07-31 15:19:34 +02:00
parent 603cc7d9be
commit ced687be5b
21 changed files with 222 additions and 64 deletions

View File

@ -1,4 +1,7 @@
* Add de/selection of columns and add a lot of new possible columns
All Subject entries, the subject hash and whole name,
Certificate fingerprints, dates, CA info, CRL number
* Improve CRL generation [3035294] CRLNumber, CRLReason
* improve creating templates from cert
- enhance parsing of CRL-DP, SAN, IAN and AuthInfoAcc

View File

@ -7,6 +7,7 @@
#include "db_base.h"
#include "func.h"
#include "exception.h"
#include <QtGui/QMessageBox>
#include <QtGui/QListView>
@ -17,6 +18,7 @@
db_base::db_base(QString db, MainWindow *mw)
:QAbstractItemModel(NULL)
{
fixedHeaders= false;
dbName = db;
rootItem = newPKI();
mainwin = mw;
@ -67,6 +69,7 @@ void db_base::loadContainer()
unsigned char *p = NULL;
db_header_t head;
pki_base *pki;
bool loaded = false;
for (int i=0; pkitype[i] != none; i++) {
mydb.first();
@ -100,16 +103,52 @@ void db_base::loadContainer()
pki = NULL;
}
free(p);
if (pki)
if (pki) {
inToCont(pki);
loaded = true;
}
next:
if (mydb.next())
break;
}
}
if (!loaded)
fixedHeaders = false;
view->columnsResize();
}
bool db_base::loadHeaderState(QHeaderView *hv)
{
QByteArray ba;
db_header_t u_header;
db mydb(dbName);
char *p;
if (!mydb.find(setting, class_name + "_hdView")) {
if ((p = (char *)mydb.load(&u_header))) {
ba = QByteArray(p, u_header.len - sizeof(db_header_t));
free(p);
}
if (!((XcaHeaderView*)hv)->setState(ba))
return false;
int max = MIN(hv->count(), allHeaders.count());
for (int i=0; i<max; i++) {
allHeaders[i]->show = !hv->isSectionHidden(i);
}
if (rootItem->childCount())
fixedHeaders = true;
return true;
}
return false;
}
void db_base::saveHeaderState(QHeaderView *hv)
{
QByteArray ba = hv->saveState();
db mydb(dbName);
mydb.set((const unsigned char *)ba.constData(), ba.size(), 1,
setting, class_name + "_hdView");
}
void db_base::insertPKI(pki_base *pki)
{
QString name;
@ -409,6 +448,13 @@ QVariant db_base::data(const QModelIndex &index, int role) const
return item->column_data(hd->id);
case Qt::DecorationRole:
return item->getIcon(hd->id);
case Qt::TextAlignmentRole:
return hd->isNumeric() ? Qt::AlignRight : Qt::AlignLeft;
case Qt::FontRole: {
if (hd->isNumeric())
return QVariant(QFont("Monospace"));
return QVariant(QApplication::font());
}
}
return QVariant();
}
@ -524,6 +570,11 @@ bool db_base::isNumericCol(int col) const
}
void db_base::showHeaderMenu(QContextMenuEvent *e, int sect)
{
contextMenu(e, NULL, sect);
}
void db_base::contextMenu(QContextMenuEvent *e, QMenu *parent, int sect)
{
int shown = 0;
QMenu *menu = new QMenu(mainwin);
@ -535,7 +586,7 @@ void db_base::showHeaderMenu(QContextMenuEvent *e, int sect)
foreach(hd, allHeaders) {
if (hd->isNid()) {
if (!dn)
dn = menu->addMenu(tr("Subject"));
dn = menu->addMenu(tr("Subject entries"));
a = dn->addAction(hd->name);
} else {
a = menu->addAction(hd->name);
@ -546,8 +597,16 @@ void db_base::showHeaderMenu(QContextMenuEvent *e, int sect)
a->setToolTip(hd->tooltip);
hd->action = a;
}
menu->exec(e->globalPos());
if (parent) {
parent->addMenu(menu)->setText(tr("Columns"));
parent->exec(e->globalPos());
} else {
menu->exec(e->globalPos());
}
foreach(hd, allHeaders) {
if (!hd->action)
continue;
hd->show = hd->action->isChecked();
shown += hd->show ? 1 : 0;
hd->action = NULL;
@ -555,4 +614,7 @@ void db_base::showHeaderMenu(QContextMenuEvent *e, int sect)
if (!shown)
allHeaders[0]->show = true;
delete menu;
if (parent)
delete parent;
emit updateHeader();
}

View File

@ -43,6 +43,7 @@ class db_base: public QAbstractItemModel
QString class_name;
dbheaderList allHeaders;
public:
bool fixedHeaders;
pki_base *rootItem;
db_base(QString db, MainWindow *mw);
virtual ~db_base();
@ -83,6 +84,10 @@ class db_base: public QAbstractItemModel
void showHeaderMenu(QContextMenuEvent *e, int sect);
bool columnHidden(int col) const;
bool isNumericCol(int col) const;
bool loadHeaderState(QHeaderView *hv);
void saveHeaderState(QHeaderView *hv);
void contextMenu(QContextMenuEvent *e,
QMenu *parent = NULL, int sect = -1);
public slots:
void deletePKI();
@ -98,6 +103,7 @@ class db_base: public QAbstractItemModel
signals:
void connNewX509(NewX509 *dlg);
void resetHeader();
void updateHeader();
};
#endif

View File

@ -198,8 +198,8 @@ pki_crl *db_crl::newItem(pki_x509 *cert)
}
crl->setLastUpdate(dlg->lastUpdate->getDate());
crl->setNextUpdate(dlg->nextUpdate->getDate());
cert->setCrlExpiry(dlg->nextUpdate->getDate());
crl->sign(cert->getRefKey(), dlg->hashAlgo->currentHash());
cert->setCrlExpiry(dlg->nextUpdate->getDate());
mainwin->certs->updatePKI(cert);
createSuccess(insert(crl));
}
@ -224,8 +224,7 @@ void db_crl::showContextMenu(QContextMenuEvent *e, const QModelIndex &index)
menu->addAction(tr("Export"), this, SLOT(store()));
menu->addAction(tr("Delete"), this, SLOT(delete_ask()));
}
menu->exec(e->globalPos());
delete menu;
contextMenu(e, menu);
currentIdx = QModelIndex();
return;
}

View File

@ -260,8 +260,7 @@ void db_key::showContextMenu(QContextMenuEvent *e, const QModelIndex &index)
this, SLOT(toToken()));
}
}
menu->exec(e->globalPos());
delete menu;
contextMenu(e, menu);
currentIdx = QModelIndex();
return;
}

View File

@ -242,8 +242,7 @@ void db_temp::showContextMenu(QContextMenuEvent *e, const QModelIndex &index)
SLOT(certFromTemp()));
menu->addAction(tr("Create request"), this, SLOT(reqFromTemp()));
}
menu->exec(e->globalPos());
delete menu;
contextMenu(e, menu);
currentIdx = QModelIndex();
return;
}

View File

@ -38,7 +38,7 @@ db_x509::db_x509(QString DBfile, MainWindow *mw)
tr("not Before")) <<
new dbheader(HD_cert_notAfter, true, tr("Expiry date"),
tr("not After")) <<
new dbheader(HD_cert_trust, true, tr("Trust state")) <<
new dbheader(HD_cert_trust, false,tr("Trust state")) <<
new dbheader(HD_cert_revokation,true, tr("Revocation"));
view = mw->certView;
@ -121,6 +121,7 @@ void db_x509::changeView()
return;
temproot = new pki_base();
mainwin->certView->hide();
mainwin->certView->setModel(NULL);
beginRemoveRows(QModelIndex(), 0, rows -1);
pki_base *pki = rootItem;
@ -147,6 +148,7 @@ void db_x509::changeView()
}
delete temproot;
mainwin->certView->setModel(this);
mainwin->certView->show();
}
void db_x509::calcEffTrust()
@ -672,8 +674,7 @@ void db_x509::showContextMenu(QContextMenuEvent *e, const QModelIndex &index)
subP7->setEnabled(privkey);
#endif
}
menu->exec(e->globalPos());
delete menu;
contextMenu(e, menu);
currentIdx = QModelIndex();
return;
}

View File

@ -19,7 +19,9 @@ db_x509req::db_x509req(QString DBfile, MainWindow *mw)
:db_x509super(DBfile, mw)
{
allHeaders << new dbheader(HD_req_signed, true, tr("Signed"),
tr("whether the request is already signed or not"));
tr("whether the request is already signed or not")) <<
new dbheader(HD_req_unstr_name, false, tr("Unstructured name")) <<
new dbheader(HD_req_chall_pass, false, tr("Challange password"));
view = mw->reqView;
class_name = "requests";
pkitype[0] = x509_req;
@ -185,8 +187,7 @@ void db_x509req::showContextMenu(QContextMenuEvent *e, const QModelIndex &index)
expItem->setEnabled(!req->isSpki());
}
menu->exec(e->globalPos());
delete menu;
contextMenu(e, menu);
currentIdx = QModelIndex();
return;
}

View File

@ -14,8 +14,11 @@ db_x509name::db_x509name(QString db, MainWindow *mw)
:db_base(db, mw)
{
NIDlist dn_nid = *MainWindow::dn_nid;
allHeaders << new dbheader(HD_subject_name, false, tr("Full name"),
tr("Complete distinguished name"));
allHeaders << new dbheader(HD_subject_name, false, tr("Subject"),
tr("Complete distinguished name")) <<
new dbheader(HD_subject_hash, false, tr("Subject hash"),
tr("Hash to lookup certs in directories"));
for (int i=0; i < dn_nid.count(); i++) {
int nid = dn_nid[i];
allHeaders << new dbheader(nid, nid == NID_commonName);

View File

@ -7,6 +7,7 @@
#define HD_undef NID_undef
#define HD_internal_name -2
#define HD_subject_name -3
#define HD_subject_hash -4
#define HD_cert_serial -10
#define HD_cert_notBefore -11
@ -18,6 +19,8 @@
#define HD_cert_sha1fp -17
#define HD_req_signed -20
#define HD_req_unstr_name -21
#define HD_req_chall_pass -22
#define HD_temp_type -30
#define HD_crl_signer -40
@ -82,6 +85,9 @@ class dbheader
case HD_cert_serial:
case HD_crl_revoked:
case HD_crl_crlnumber:
case HD_subject_hash:
case HD_cert_md5fp:
case HD_cert_sha1fp:
return true;
}
return false;

View File

@ -94,6 +94,10 @@ class pki_base : public QObject
{
return 0;
};
QByteArray i2d()
{
return QByteArray();
}
};
#endif

View File

@ -157,14 +157,11 @@ QByteArray pki_crl::toData()
bool pki_crl::compare(pki_base *refcrl)
{
bool ret;
ret = X509_CRL_cmp(crl, ((pki_crl *)refcrl)->crl) == 0 &&
getLastUpdate() == ((pki_crl *)refcrl)->getLastUpdate() &&
getNextUpdate() == ((pki_crl *)refcrl)->getNextUpdate() ;
ret = i2d() != refcrl->i2d();
pki_openssl_error();
return ret;
}
void pki_crl::addRev(const x509rev &xrev)
{
sk_X509_REVOKED_push(crl->crl->revoked, xrev.get());

View File

@ -778,9 +778,9 @@ QVariant pki_x509::column_data(int id)
case HD_cert_serial:
return QVariant(getSerial().toHex());
case HD_cert_notBefore:
return QVariant(getNotBefore().qDateTime());
return QVariant(getNotBefore().toSortable());
case HD_cert_notAfter:
return QVariant(getNotAfter().qDateTime());
return QVariant(getNotAfter().toSortable());
case HD_cert_trust:
return QVariant(truststatus[getTrust()]);
case HD_cert_revokation:
@ -797,9 +797,12 @@ QVariant pki_x509::column_data(int id)
case HD_cert_ca: {
a1int len;
bool ca, haslen;
if (caAndPathLen(&ca, &len, &haslen))
if (caAndPathLen(&ca, &len, &haslen)) {
if (ca && haslen)
return QVariant(len.toDec());
if (!ca)
return QVariant(tr("No"));
}
return QVariant();
}
}

View File

@ -396,11 +396,36 @@ ASN1_IA5STRING *pki_x509req::spki_challange()
return NULL;
}
static QString getAttribute(X509_REQ *req, int nid)
{
int n;
n = X509_REQ_get_attr_by_NID(req, nid, -1);
if (n == -1)
return QString();
X509_ATTRIBUTE *att = X509_REQ_get_attr(req, n);
if (!att)
return QString();
if (att->single)
return asn1ToQString(att->value.single->value.asn1_string);
int count = sk_ASN1_TYPE_num(att->value.set);
QStringList ret;
for (int j=0; j<count; j++) {
ret << asn1ToQString(sk_ASN1_TYPE_value(att->value.set, j)->
value.asn1_string);
}
return ret.join(", ");
}
QVariant pki_x509req::column_data(int id)
{
switch (id) {
case HD_req_signed:
return QVariant(done ? tr("Signed") : tr("Unhandled"));
case HD_req_signed:
return QVariant(done ? tr("Signed") : tr("Unhandled"));
case HD_req_unstr_name:
return getAttribute(request, NID_pkcs9_unstructuredName);
case HD_req_chall_pass:
return getAttribute(request, NID_pkcs9_challengePassword);
}
return pki_x509super::column_data(id);
}

View File

@ -61,9 +61,15 @@ void pki_x509name::autoIntName()
QVariant pki_x509name::column_data(int id)
{
if (id == HD_subject_name)
return QVariant(getSubject().oneLine(XN_FLAG_ONELINE));
if (dbheader::isNid(id))
return QVariant(getSubject().getEntryByNid(id));
switch (id) {
case HD_subject_name:
return QVariant(getSubject().oneLine(
XN_FLAG_ONELINE & ~ASN1_STRFLGS_ESC_MSB));
case HD_subject_hash:
return QVariant(getSubject().hash());
default:
if (dbheader::isNid(id))
return QVariant(getSubject().getEntryByNid(id));
}
return pki_base::column_data(id);
}

View File

@ -127,6 +127,11 @@ QString x509name::popEntryByNid(int nid)
return n;
}
QString x509name::hash() const
{
return QString("%1").arg(X509_NAME_hash(xn), 8, 16, QChar('0'));
}
QStringList x509name::entryList(int i) const
{
QStringList sl;

View File

@ -42,6 +42,7 @@ class x509name
X509_NAME *get() const;
QString getMostPopular() const;
QString taggedValues() const;
QString hash() const;
};
#endif

View File

@ -31,6 +31,9 @@ XcaTreeView::XcaTreeView(QWidget *parent)
proxy->setDynamicSortFilter(true);
sortByColumn(0, Qt::AscendingOrder);
basemodel = NULL;
connect(header(), SIGNAL(sectionHandleDoubleClicked(int)),
this, SLOT(resizeColumnToContents(int)));
header()->setClickable(true);
}
XcaTreeView::~XcaTreeView()
@ -38,7 +41,7 @@ XcaTreeView::~XcaTreeView()
delete proxy;
}
void XcaTreeView::contextMenuEvent(QContextMenuEvent * e )
void XcaTreeView::contextMenuEvent(QContextMenuEvent * e)
{
if (!basemodel)
return;
@ -57,22 +60,34 @@ void XcaTreeView::showHideSections()
header()->showSection(i);
}
columnsResize();
header()->update();
}
void XcaTreeView::setModel(QAbstractItemModel *model)
{
basemodel = (db_base *)model;
QByteArray ba;
if (basemodel)
basemodel->saveHeaderState(header());
basemodel = (db_base *)model;
if (basemodel) {
connect(basemodel, SIGNAL(resetHeader()),
header(), SLOT(resetMoves()));
connect(basemodel, SIGNAL(resetHeader()),
this, SLOT(columnsForceResize()));
connect(basemodel, SIGNAL(updateHeader()),
this, SLOT(showHideSections()));
basemodel->loadHeaderState(header());
}
proxy->setSourceModel(model);
QTreeView::setModel(proxy);
showHideSections();
}
void XcaTreeView::headerEvent(QContextMenuEvent *e, int col)
{
basemodel->showHeaderMenu(e, col);
showHideSections();
}
QModelIndex XcaTreeView::getIndex(const QModelIndex &index)
@ -91,7 +106,7 @@ QModelIndexList XcaTreeView::getSelectedIndexes()
return proxy->mapSelectionToSource(indexes).indexes();
}
void XcaTreeView::columnsResize()
void XcaTreeView::columnsForceResize()
{
int cnt, i;
if (basemodel) {
@ -101,6 +116,12 @@ void XcaTreeView::columnsResize()
}
}
void XcaTreeView::columnsResize()
{
if (basemodel && !basemodel->fixedHeaders)
columnsForceResize();
}
XcaProxyModel::XcaProxyModel(QWidget *parent)
:QSortFilterProxyModel(parent)
{
@ -146,3 +167,18 @@ void XcaHeaderView::resetMoves()
}
}
}
bool XcaHeaderView::setState(const QByteArray &state)
{
setting = state;
return QHeaderView::restoreState(setting);
}
void XcaHeaderView::showEvent(QShowEvent *event)
{
if (setting.size()) {
if (QHeaderView::restoreState(setting))
setting.clear();
setStretchLastSection(false);
}
}

View File

@ -19,14 +19,17 @@ class db_base;
class XcaHeaderView: public QHeaderView
{
Q_OBJECT
QByteArray setting;
protected:
void showEvent(QShowEvent *event);
public:
XcaHeaderView()
:QHeaderView(Qt::Horizontal)
{
setStretchLastSection(true);
setMovable(true);
}
void contextMenuEvent(QContextMenuEvent *e);
bool setState(const QByteArray &state);
public slots:
void resetMoves();
};
@ -47,7 +50,9 @@ class XcaTreeView: public QTreeView
QModelIndexList getSelectedIndexes();
void columnsResize();
void headerEvent(QContextMenuEvent *e, int col);
public slots:
void showHideSections();
void columnsForceResize();
};
class XcaProxyModel: public QSortFilterProxyModel

View File

@ -1,6 +1,6 @@
/* vi: set sw=4 ts=4:
*
* Copyright (C) 2001 - 2007 Christian Hohnstaedt.
* Copyright (C) 2001 - 2010 Christian Hohnstaedt.
*
* All rights reserved.
*/
@ -17,24 +17,32 @@
DistName::DistName(QWidget* parent)
: QWidget(parent)
{
QHBoxLayout *h = new QHBoxLayout();
QVBoxLayout *v = new QVBoxLayout(this);
QLabel *l = new QLabel(QString("RFC 2253:"), this);
lineEdit = new QLineEdit(this);
DistNameLayout = new QGridLayout();
DistNameLayout->setAlignment(Qt::AlignTop);
DistNameLayout->setSpacing(6);
DistNameLayout->setMargin(11);
QGridLayout *g = new QGridLayout();
g->setAlignment(Qt::AlignTop);
g->setSpacing(6);
g->setMargin(11);
QVBoxLayout *v = new QVBoxLayout(this);
v->setSpacing(6);
v->setMargin(11);
v->addLayout(DistNameLayout);
v->addLayout(h);
v->addStretch();
v->addLayout(g);
v->setSpacing(6);
h->addWidget(l);
h->addWidget(lineEdit);
lineEdit->setReadOnly(true);
rfc2253 = new QLineEdit(this);
rfc2253->setReadOnly(true);
g->addWidget(new QLabel(QString("RFC 2253:"), this), 0, 0);
g->addWidget(rfc2253, 0, 1);
namehash = new QLineEdit(this);
namehash->setReadOnly(true);
g->addWidget(new QLabel(QString("Hash:"), this), 1, 0);
g->addWidget(namehash, 1, 1);
}
void DistName::setX509name(const x509name &n)
@ -56,17 +64,8 @@ void DistName::setX509name(const x509name &n)
DistNameLayout->addWidget( l1, i, 0 );
DistNameLayout->addWidget( l2, i, 1 );
}
lineEdit->setText(n.oneLine(XN_FLAG_RFC2253));
lineEdit->setCursorPosition(0);
rfc2253->setText(n.oneLine(XN_FLAG_RFC2253));
rfc2253->setCursorPosition(0);
namehash->setText(n.hash());
updateGeometry();
}
DistName::~DistName()
{
// no need to delete child widgets, Qt does it all for us
}
void DistName::resizeEvent( QResizeEvent *e)
{
QWidget::resizeEvent(e);
}

View File

@ -22,14 +22,12 @@ class DistName : public QWidget
Q_OBJECT
public:
DistName(QWidget* parent);
~DistName();
DistName(QWidget *parent);
void setX509name(const x509name &n);
protected:
QGridLayout* DistNameLayout;
QLineEdit *lineEdit;
void resizeEvent( QResizeEvent *e);
QLineEdit *rfc2253;
QLineEdit *namehash;
};
#endif