mirror of
https://github.com/chris2511/xca.git
synced 2026-09-12 19:51:05 +05:00
XCA now supports both, Qt6 and Qt5 down to Qt-5.2 Use CMAKE_PREFIX_PATH to point to the QT dompiler directory like $ cmake -B build -DCMAKE_PREFIX_PATH=$HOME/6.3.1/gcc_64
684 lines
15 KiB
C++
684 lines
15 KiB
C++
/* vi: set sw=4 ts=4:
|
|
*
|
|
* Copyright (C) 2001 - 2015 Christian Hohnstaedt.
|
|
*
|
|
* All rights reserved.
|
|
*/
|
|
|
|
#include "db_base.h"
|
|
#include "func.h"
|
|
#include "exception.h"
|
|
#include "XcaWarningCore.h"
|
|
|
|
#include <QDir>
|
|
#include <QDebug>
|
|
#include <QMimeData>
|
|
#include <QFileInfo>
|
|
|
|
void db_base::restart_timer()
|
|
{
|
|
if (!IS_GUI_APP)
|
|
return;
|
|
killTimer(secondsTimer);
|
|
killTimer(minutesTimer);
|
|
killTimer(hoursTimer);
|
|
|
|
secondsTimer = startTimer(1000);
|
|
minutesTimer = startTimer(MSECS_PER_MINUTE);
|
|
hoursTimer = startTimer(MSECS_PER_HOUR);
|
|
}
|
|
|
|
db_base::db_base(const char *classname)
|
|
:QAbstractItemModel()
|
|
{
|
|
rootItem = new pki_base(QString("ROOTitem(%1)").arg(classname));
|
|
treeItem = new pki_base(QString("TREEitem(%1)").arg(classname));
|
|
colResizing = 0;
|
|
class_name = classname;
|
|
secondsTimer = minutesTimer = hoursTimer = 0;
|
|
treeview = true;
|
|
restart_timer();
|
|
}
|
|
|
|
db_base::~db_base()
|
|
{
|
|
saveHeaderState();
|
|
delete rootItem;
|
|
delete treeItem;
|
|
}
|
|
|
|
pki_base *db_base::newPKI(enum pki_type type)
|
|
{
|
|
(void)type;
|
|
return new pki_base();
|
|
}
|
|
|
|
void db_base::createSuccess(const pki_base *pki) const
|
|
{
|
|
if (!pki)
|
|
return;
|
|
|
|
if (Settings["suppress_messages"])
|
|
return;
|
|
|
|
XCA_INFO(pki->getMsg(pki_base::msg_create).arg(pki->getIntName()));
|
|
}
|
|
|
|
void db_base::remFromCont(const QModelIndex &idx)
|
|
{
|
|
if (!idx.isValid())
|
|
return;
|
|
pki_base *pki = fromIndex(idx);
|
|
pki_base *parent_pki = pki->getParent();
|
|
int row = rownumber(pki);
|
|
|
|
beginRemoveRows(parent(idx), row, row);
|
|
parent_pki->takeChild(pki);
|
|
rootItem->takeChild(pki);
|
|
endRemoveRows();
|
|
emit columnsContentChanged();
|
|
}
|
|
|
|
QString db_base::sqlItemSelector()
|
|
{
|
|
QStringList sl;
|
|
QString selector;
|
|
|
|
foreach(enum pki_type pt, pkitype)
|
|
sl << QString("type=%1").arg(pt);
|
|
|
|
return sl.join(" OR ");
|
|
}
|
|
|
|
void db_base::loadContainer()
|
|
{
|
|
XSqlQuery q;
|
|
QSqlError e;
|
|
QString stmt;
|
|
|
|
SQL_PREPARE(q, QString("SELECT * FROM view_") + sqlHashTable);
|
|
q.exec();
|
|
e = q.lastError();
|
|
XCA_SQLERROR(e);
|
|
|
|
while (q.next()) try {
|
|
enum pki_type t;
|
|
QSqlRecord rec = q.record();
|
|
t = (enum pki_type)q.value(VIEW_item_type).toInt();
|
|
pki_base *pki = newPKI(t);
|
|
pki->restoreSql(rec);
|
|
insertChild(pki);
|
|
Store.add(q.value(VIEW_item_id), pki);
|
|
} catch (errorEx &ex) {
|
|
XCA_ERROR(ex);
|
|
}
|
|
|
|
QString view = Settings[class_name + "_hdView"];
|
|
if (view.isEmpty()) {
|
|
for (int i=0; i< allHeaders.count(); i++) {
|
|
allHeaders[i]->reset();
|
|
}
|
|
} else {
|
|
allHeaders.fromData(view);
|
|
}
|
|
restart_timer();
|
|
emit columnsContentChanged();
|
|
}
|
|
|
|
void db_base::reloadContainer(const QList<enum pki_type> &typelist)
|
|
{
|
|
bool match = false;
|
|
QList<enum pki_type> all_types = pkitype + pkitype_depends;
|
|
foreach(enum pki_type t, typelist) {
|
|
if (all_types.contains(t)) {
|
|
match = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!match)
|
|
return;
|
|
qDebug() << "RELOAD" << class_name << all_types << typelist;
|
|
beginResetModel();
|
|
rootItem->clear();
|
|
treeItem->clear();
|
|
endResetModel();
|
|
|
|
loadContainer();
|
|
}
|
|
|
|
void db_base::updateHeaders()
|
|
{
|
|
QString s = allHeaders.toData();
|
|
foreach(dbheader *h, allHeaders)
|
|
delete h;
|
|
allHeaders = getHeaders();
|
|
allHeaders.fromData(s);
|
|
}
|
|
|
|
dbheaderList db_base::getHeaders()
|
|
{
|
|
dbheaderList h;
|
|
/* "No." handled in XcaProxyModel */
|
|
h << new dbheader(HD_internal_name, true, tr("Internal name"))
|
|
<< new num_dbheader(HD_counter, false, tr("No."))
|
|
<< new num_dbheader(HD_primary_key, false, tr("Primary key"),
|
|
tr("Database unique number"))
|
|
<< new date_dbheader(HD_creation, false, tr("Date"),
|
|
tr("Date of creation or insertion"))
|
|
<< new dbheader(HD_source, false, tr("Source"),
|
|
tr("Generated, Imported, Transformed"))
|
|
<< new dbheader(HD_comment, false, tr("Comment"),
|
|
tr("First line of the comment field"));
|
|
return h;
|
|
}
|
|
|
|
void db_base::saveHeaderState()
|
|
{
|
|
if (QSqlDatabase::database().isOpen())
|
|
Settings[class_name + "_hdView"] = allHeaders.toData();
|
|
}
|
|
|
|
void db_base::setVisualIndex(int i, int visualIndex)
|
|
{
|
|
if (colResizing)
|
|
return;
|
|
allHeaders[i]->visualIndex = visualIndex;
|
|
}
|
|
|
|
void db_base::sectionResized(int i, int, int newSize)
|
|
{
|
|
if (!allHeaders[i]->show || newSize <= 0 || colResizing)
|
|
return;
|
|
allHeaders[i]->size = newSize;
|
|
}
|
|
|
|
bool db_base::fixedHeaderSize(int sect)
|
|
{
|
|
return allHeaders[sect]->size != -1;
|
|
}
|
|
|
|
void db_base::initHeaderView(QHeaderView *hv)
|
|
{
|
|
int max = allHeaders.count();
|
|
colResizeStart();
|
|
for (int i=0; i<max; i++) {
|
|
allHeaders[i]->setupHeaderView(i, hv);
|
|
}
|
|
for (int i=0; i<max; i++) {
|
|
if (allHeaders[i]->visualIndex == -1)
|
|
continue;
|
|
if (hv->visualIndex(i) != allHeaders[i]->visualIndex) {
|
|
hv->moveSection(hv->visualIndex(i),
|
|
allHeaders[i]->visualIndex);
|
|
}
|
|
}
|
|
colResizeEnd();
|
|
}
|
|
|
|
void db_base::sortIndicatorChanged(int logicalIndex, Qt::SortOrder order)
|
|
{
|
|
int max = allHeaders.count();
|
|
if (!isValidCol(logicalIndex))
|
|
return;
|
|
for (int i=0; i<max; i++) {
|
|
allHeaders[i]->sortIndicator = -1;
|
|
}
|
|
allHeaders[logicalIndex]->sortIndicator = order;
|
|
}
|
|
|
|
pki_base *db_base::insertPKI(pki_base *pki)
|
|
{
|
|
QString filename = pki->getFilename();
|
|
if (!filename.isEmpty()) {
|
|
pki->selfComment(tr("Import from: %1").arg(filename));
|
|
pki->setFilename(QString());
|
|
}
|
|
Transaction;
|
|
if (!TransBegin()) {
|
|
delete pki;
|
|
return NULL;
|
|
}
|
|
QSqlError e = pki->insertSql();
|
|
if (e.isValid()) {
|
|
XCA_SQLERROR(e);
|
|
TransRollback();
|
|
delete pki;
|
|
return NULL;
|
|
}
|
|
Store.add(pki->getSqlItemId(), pki);
|
|
inToCont(pki);
|
|
TransCommit();
|
|
restart_timer();
|
|
emit columnsContentChanged();
|
|
return pki;
|
|
}
|
|
|
|
QString db_base::pem2QString(QModelIndexList indexes) const
|
|
{
|
|
BioByteArray bba;
|
|
|
|
foreach(QModelIndex idx, indexes) {
|
|
if (idx.column() != 0)
|
|
continue;
|
|
pki_base *pki = fromIndex(idx);
|
|
pki->pem(bba);
|
|
openssl_error();
|
|
}
|
|
return bba.qstring();
|
|
}
|
|
|
|
void db_base::deletePKI(QModelIndex idx)
|
|
{
|
|
pki_base *pki = fromIndex(idx);
|
|
QSqlDatabase db = QSqlDatabase::database();
|
|
try {
|
|
try {
|
|
pki->deleteFromToken();
|
|
} catch (errorEx &err) {
|
|
XCA_ERROR(err);
|
|
}
|
|
Transaction;
|
|
if (TransBegin()) {
|
|
QSqlError e = pki->deleteSql();
|
|
TransDone(e);
|
|
if (!e.isValid())
|
|
remFromCont(idx);
|
|
AffectedItems(pki->getSqlItemId());
|
|
XCA_SQLERROR(e);
|
|
}
|
|
} catch (errorEx &err) {
|
|
XCA_ERROR(err);
|
|
}
|
|
}
|
|
|
|
void db_base::insertChild(pki_base *child, pki_base *parent)
|
|
{
|
|
QModelIndex idx = QModelIndex();
|
|
pki_base *curr_parent = child->getParent();
|
|
|
|
if (!parent || parent == child)
|
|
parent = treeItem;
|
|
|
|
if (curr_parent) {
|
|
/* Need to take it */
|
|
if (curr_parent != treeItem && treeview)
|
|
idx = index(curr_parent);
|
|
int row = rownumber(child);
|
|
beginRemoveRows(idx, row, row);
|
|
curr_parent->takeChild(child);
|
|
endRemoveRows();
|
|
}
|
|
|
|
if (parent != treeItem && treeview)
|
|
idx = index(parent);
|
|
|
|
beginInsertRows(idx, 0, 0);
|
|
parent->insert(child);
|
|
child->setParent(parent);
|
|
rootItem->insert(child);
|
|
endInsertRows();
|
|
|
|
qDebug() << "insertChild" << *child << "To parent" << *parent
|
|
<< "From" << (curr_parent ? QString(*curr_parent) : "NEW")
|
|
<< "COUNT root" << rootItem->childCount()
|
|
<< "Count tree" << treeItem->childCount();
|
|
}
|
|
|
|
int db_base::rownumber(const pki_base *child) const
|
|
{
|
|
pki_base *parent = treeview ? child->getParent() : rootItem;
|
|
return parent ? parent->indexOf(child) : 0;
|
|
}
|
|
|
|
/* Does all the linking from existing keys, crls, certs
|
|
* to the new imported or generated item
|
|
* called before the new item will be inserted into the database
|
|
*/
|
|
void db_base::inToCont(pki_base *pki)
|
|
{
|
|
insertChild(pki);
|
|
}
|
|
|
|
pki_base *db_base::getByName(QString desc)
|
|
{
|
|
QList<pki_base*> list = Store.sqlSELECTpki<pki_base>(
|
|
QString("SELECT id FROM items WHERE name=? AND del=0 AND ") +
|
|
sqlItemSelector(),
|
|
QList<QVariant>() << QVariant(desc));
|
|
return list.isEmpty() ? NULL : list[0];
|
|
}
|
|
|
|
pki_base *db_base::getByReference(pki_base *refpki)
|
|
{
|
|
if (refpki == NULL)
|
|
return NULL;
|
|
QList<pki_base*> list = Store.sqlSELECTpki<pki_base>(
|
|
QString("SELECT item FROM %1 WHERE hash=?").arg(sqlHashTable),
|
|
QList<QVariant>() << QVariant(refpki->hash()));
|
|
foreach(pki_base *pki, list) {
|
|
if (refpki->compare(pki))
|
|
return pki;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
pki_base *db_base::insert(pki_base *item)
|
|
{
|
|
return insertPKI(item);
|
|
}
|
|
|
|
void db_base::dump(const QString &dir) const
|
|
{
|
|
QString dirname = dir + "/" + class_name;
|
|
QDir d(dirname);
|
|
if (!d.exists() && !d.mkdir(dirname)) {
|
|
throw errorEx(tr("Could not create directory %1")
|
|
.arg(nativeSeparator(dirname)));
|
|
}
|
|
|
|
try {
|
|
foreach(pki_base *pki, Store.getAll<pki_base>()) {
|
|
if (pkitype.contains(pki->getType()))
|
|
pki->writeDefault(dirname);
|
|
}
|
|
}
|
|
catch (errorEx &err) {
|
|
XCA_ERROR(err);
|
|
}
|
|
}
|
|
|
|
QModelIndex db_base::index(int row, int column,
|
|
const QModelIndex &parent) const
|
|
{
|
|
pki_base *parentItem = treeview ? treeItem : rootItem;
|
|
|
|
if (parent.isValid() && treeview)
|
|
parentItem = fromIndex(parent);
|
|
|
|
pki_base *childItem = parentItem->child(row);
|
|
return childItem ? createIndex(row, column, childItem) : QModelIndex();
|
|
}
|
|
|
|
QModelIndex db_base::index(pki_base *pki) const
|
|
{
|
|
if (!pki)
|
|
return QModelIndex();
|
|
return createIndex(rownumber(pki), 0, pki);
|
|
}
|
|
|
|
QModelIndex db_base::parent(const QModelIndex &idx) const
|
|
{
|
|
if (!idx.isValid())
|
|
return QModelIndex();
|
|
|
|
pki_base *childItem = fromIndex(idx);
|
|
pki_base *parentItem = childItem->getParent();
|
|
|
|
if (parentItem == treeItem || !treeview)
|
|
parentItem = NULL;
|
|
|
|
return index(parentItem);
|
|
}
|
|
|
|
int db_base::rowCount(const QModelIndex &parent) const
|
|
{
|
|
pki_base *parentItem = treeview ? treeItem : rootItem;
|
|
|
|
if (parent.isValid())
|
|
parentItem = treeview ? fromIndex(parent) : NULL;
|
|
|
|
return parentItem ? parentItem->childCount() : 0;
|
|
}
|
|
|
|
int db_base::columnCount(const QModelIndex &) const
|
|
{
|
|
return allHeaders.count();
|
|
}
|
|
|
|
QVariant db_base::data(const QModelIndex &index, int role) const
|
|
{
|
|
if (!index.isValid())
|
|
return QVariant();
|
|
dbheader *hd = allHeaders[index.column()];
|
|
pki_base *item = fromIndex(index);
|
|
switch (role) {
|
|
case Qt::EditRole:
|
|
case Qt::DisplayRole:
|
|
if (hd->id==HD_internal_name || item->isVisible()==1)
|
|
return item->column_data(hd);
|
|
break;
|
|
case Qt::DecorationRole:
|
|
return item->getIcon(hd);
|
|
case Qt::TextAlignmentRole:
|
|
return hd->isNumeric() ? Qt::AlignRight : Qt::AlignLeft;
|
|
case Qt::BackgroundRole:
|
|
return item->bg_color(hd);
|
|
case Qt::UserRole:
|
|
return item->isVisible();
|
|
case Qt::ToolTipRole:
|
|
if (hd->id==HD_internal_name || item->isVisible()==1)
|
|
return item->column_tooltip(hd);
|
|
break;
|
|
}
|
|
return QVariant();
|
|
}
|
|
static QVariant getHeaderViewInfo(dbheader *h)
|
|
{
|
|
return QVariant(
|
|
#if 0
|
|
QString("H[%1] Show:%2%3 Size:%4 VI:%5 Indi:%6").
|
|
arg(sect).arg(h->show).arg(h->showDefault).arg(h->size).
|
|
arg(h->visualIndex).arg(h->sortIndicator)
|
|
#else
|
|
h->getTooltip()
|
|
#endif
|
|
);
|
|
}
|
|
|
|
void db_base::changeView()
|
|
{
|
|
beginResetModel();
|
|
treeview = !treeview;
|
|
endResetModel();
|
|
}
|
|
|
|
QVariant db_base::headerData(int section, Qt::Orientation orientation,
|
|
int role) const
|
|
{
|
|
if (!isValidCol(section))
|
|
return QVariant();
|
|
if (orientation == Qt::Horizontal) {
|
|
switch (role) {
|
|
case Qt::DisplayRole:
|
|
return QVariant(allHeaders[section]->getName());
|
|
case Qt::ToolTipRole:
|
|
return getHeaderViewInfo(allHeaders[section]);
|
|
}
|
|
}
|
|
return QVariant();
|
|
}
|
|
|
|
Qt::ItemFlags db_base::flags(const QModelIndex &index) const
|
|
{
|
|
if (!index.isValid())
|
|
return Qt::NoItemFlags;
|
|
|
|
Qt::ItemFlags flags = QAbstractItemModel::flags(index) |
|
|
Qt::ItemIsDragEnabled;
|
|
pki_base *item = fromIndex(index);
|
|
if (item->isVisible() == 2)
|
|
flags &= ~Qt::ItemIsEnabled;
|
|
else if (index.column() == 0)
|
|
flags |= Qt::ItemIsEditable;
|
|
return flags;
|
|
}
|
|
|
|
bool db_base::setData(const QModelIndex &index, const QVariant &value, int role)
|
|
{
|
|
QString on, nn;
|
|
pki_base *item;
|
|
if (index.isValid() && role == Qt::EditRole) {
|
|
nn = value.toString();
|
|
item = fromIndex(index);
|
|
on = item->getIntName();
|
|
if (nn == on)
|
|
return true;
|
|
updateItem(item, nn, item->getComment());
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void db_base::updateItem(pki_base *pki, const QString &name,
|
|
const QString &comment)
|
|
{
|
|
XSqlQuery q;
|
|
QSqlError e;
|
|
|
|
if (name == pki->getIntName() && comment == pki->getComment())
|
|
return;
|
|
|
|
Transaction;
|
|
TransThrow();
|
|
|
|
SQL_PREPARE(q, "UPDATE items SET name=?, comment=? WHERE id=?");
|
|
q.bindValue(0, name);
|
|
q.bindValue(1, comment);
|
|
q.bindValue(2, pki->getSqlItemId());
|
|
q.exec();
|
|
e = q.lastError();
|
|
AffectedItems(pki->getSqlItemId());
|
|
|
|
XCA_SQLERROR(e);
|
|
TransDone(e);
|
|
pki->setIntName(name);
|
|
pki->setComment(comment);
|
|
pki->recheckVisibility();
|
|
|
|
QModelIndex i, j;
|
|
i = index(pki);
|
|
j = index(i.row(), allHeaders.size(), i.parent());
|
|
emit dataChanged(i, j);
|
|
emit pkiChanged(pki);
|
|
restart_timer();
|
|
}
|
|
|
|
void db_base::timerEvent(QTimerEvent *event)
|
|
{
|
|
int youngest = SECS_PER_DAY;
|
|
int id = event->timerId();
|
|
|
|
foreach(pki_base *pki, Store.getAll<pki_base>()) {
|
|
for (int idx=0; idx < allHeaders.count(); idx++) {
|
|
dbheader *hd = allHeaders[idx];
|
|
if (hd->type != dbheader::hd_asn1time)
|
|
continue;
|
|
a1time t = pki->column_a1time(hd);
|
|
if (t.isUndefined())
|
|
continue;
|
|
int age = t.age();
|
|
if (age < 0)
|
|
age *= -1;
|
|
bool do_emit = false;
|
|
if (age < youngest)
|
|
youngest = age;
|
|
if (!hd->show)
|
|
continue;
|
|
if (id == secondsTimer && (age < SECS_PER_MINUTE *2 ||
|
|
age % SECS_PER_MINUTE < 2))
|
|
do_emit = true;
|
|
if (id == minutesTimer && (age % SECS_PER_HOUR < 60))
|
|
do_emit = true;
|
|
if (id == hoursTimer &&
|
|
(age % SECS_PER_DAY < SECS_PER_HOUR))
|
|
do_emit = true;
|
|
if (do_emit) {
|
|
qDebug() << "Date changed for" << pki->getIntName() << ":" << hd->getName() << "Col:" << idx << t.toSortable();
|
|
QModelIndex i;
|
|
i = createIndex(rownumber(pki), idx, pki);
|
|
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
|
|
emit dataChanged(i, i, QList<int>{Qt::DisplayRole});
|
|
#else
|
|
emit dataChanged(i, i, QVector<int>{Qt::DisplayRole});
|
|
#endif
|
|
|
|
}
|
|
}
|
|
}
|
|
if (secondsTimer && youngest > SECS_PER_HOUR *2) {
|
|
killTimer(secondsTimer);
|
|
secondsTimer = 0;
|
|
}
|
|
if (minutesTimer && youngest > SECS_PER_DAY *2) {
|
|
killTimer(minutesTimer);
|
|
minutesTimer = 0;
|
|
}
|
|
}
|
|
|
|
bool db_base::columnHidden(int col) const
|
|
{
|
|
if (!isValidCol(col))
|
|
return true;
|
|
if (Settings["disable_netscape"] &&
|
|
allHeaders[col]->type == dbheader::hd_v3ext_ns)
|
|
return true;
|
|
return !allHeaders[col]->show;
|
|
}
|
|
|
|
void db_base::columnResetDefaults()
|
|
{
|
|
dbheader *hd;
|
|
foreach(hd, allHeaders) {
|
|
hd->reset();
|
|
}
|
|
emit resetHeader();
|
|
}
|
|
|
|
bool db_base::isValidCol(int col) const
|
|
{
|
|
return col >= allHeaders.size() || col < 0 ? false : true;
|
|
}
|
|
|
|
QMimeData *db_base::mimeData(const QModelIndexList &indexes) const
|
|
{
|
|
QString data = pem2QString(indexes);
|
|
|
|
if (data.isEmpty())
|
|
return NULL;
|
|
|
|
QMimeData *mimeData = new QMimeData();
|
|
mimeData->setText(data.toLatin1());
|
|
mimeData->setData(X_XCA_DRAG_DATA, QByteArray());
|
|
return mimeData;
|
|
}
|
|
|
|
void db_base::writeVcalendar(XFile &file, QStringList vcal) const
|
|
{
|
|
QStringList ics; ics <<
|
|
"BEGIN:VCALENDAR" <<
|
|
"VERSION:2.0" <<
|
|
"PRODID:-//" XCA_TITLE "//" XCA_VERSION "//" <<
|
|
vcal <<
|
|
"END:VCALENDAR";
|
|
file.write(ics.join("\r\n").toUtf8());
|
|
}
|
|
|
|
void db_base::exportItems(const QModelIndexList &indexes,
|
|
const pki_export *xport, XFile &file) const
|
|
{
|
|
foreach(QModelIndex idx, indexes)
|
|
exportItem(idx, xport, file);
|
|
}
|
|
|
|
int db_base::exportFlags(const QModelIndexList &indexes) const
|
|
{
|
|
int disabled_flags = 0;
|
|
foreach(const QModelIndex &idx, indexes)
|
|
disabled_flags |= exportFlags(idx);
|
|
if (indexes.size() > 1)
|
|
disabled_flags |= F_SINGLE;
|
|
else
|
|
disabled_flags |= F_MULTI;
|
|
return disabled_flags;
|
|
}
|