/* 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 #include #include #include void db_base::restart_timer() { if (!IS_GUI_APP) return; maintenanceTimer.setSingleShot(true); maintenanceTimer.setInterval(1000); maintenanceTimer.start(); } 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)); class_name = classname; connect(&maintenanceTimer, SIGNAL(timeout()), this, SLOT(timerMaintenance())); restart_timer(); } db_base::~db_base() { saveHeaderState(); qDeleteAll(allHeaders); delete rootItem; delete treeItem; } pki_base *db_base::newPKI(enum pki_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); rootItem->insert(pki); treeItem->insert(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 &typelist) { bool match = false; QList 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(); qDeleteAll(allHeaders); 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; isetupHeaderView(i, hv); } for (int i=0; ivisualIndex == -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; isortIndicator = -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); Store.remove(pki->getSqlItemId()); delete pki; } } 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 (parent != treeItem && treeview) idx = index(parent); if (curr_parent) { int row = curr_parent->indexOf(child); beginMoveRows(index(curr_parent), row, row, idx, 0); curr_parent->takeChild(child); } else { beginInsertRows(idx, 0, 0); } rootItem->insert(child); parent->insert(child); if (curr_parent) endMoveRows(); else 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 list = Store.sqlSELECTpki( QString("SELECT id FROM items WHERE name=? AND del=0 AND ") + sqlItemSelector(), QList() << QVariant(desc)); return list.isEmpty() ? NULL : list[0]; } pki_base *db_base::getByReference(pki_base *refpki) { if (refpki == NULL) return NULL; QList list = Store.sqlSELECTpki( QString("SELECT item FROM %1 WHERE hash=?").arg(sqlHashTable), QList() << 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()) { 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 || pki == treeItem || rootItem->indexOf(pki) == -1) 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 int((hd->isNumeric() ? Qt::AlignRight : Qt::AlignLeft) | Qt::AlignVCenter); case Qt::BackgroundRole: return item->bg_color(hd); case Qt::UserRole: return item->isVisible() != 0 && (!Settings["hide_unusable"] || !item->unusable()); 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 newname; pki_base *item; if (index.isValid() && role == Qt::EditRole) { newname = value.toString(); item = fromIndex(index); if (newname == item->getIntName()) return true; item->setIntName(newname); updateItem(item); return true; } return false; } void db_base::updateItem(pki_base *pki) { XSqlQuery q; QSqlError e; if (!pki->getSqlItemId().isValid()) return; Transaction; TransThrow(); SQL_PREPARE(q, "UPDATE items SET name=?, comment=? WHERE id=?"); q.bindValue(0, pki->getIntName()); q.bindValue(1, pki->getComment()); q.bindValue(2, pki->getSqlItemId()); q.exec(); e = q.lastError(); AffectedItems(pki->getSqlItemId()); XCA_SQLERROR(e); TransDone(e); 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::timerMaintenance() { int youngest = SECS_PER_DAY; bool minuteElapsed = false, hourElapsed = false; if (!rootItem) return; if (minuteMarker.age() > SECS_PER_MINUTE) { minuteElapsed = true; minuteMarker = a1time::now(); } if (hourMarker.age() > SECS_PER_HOUR) { hourElapsed = true; hourMarker = a1time::now(); } qDebug() << "Maintenance start" << class_name << minuteElapsed << hourElapsed << rootItem->getChildItems().count(); foreach(pki_base *pki, rootItem->getChildItems()) { 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 ((age < SECS_PER_MINUTE *2 || age % SECS_PER_MINUTE < 2)) do_emit = true; if (minuteElapsed && (age % SECS_PER_HOUR < SECS_PER_MINUTE *2)) do_emit = true; if (hourElapsed && (age % SECS_PER_DAY < SECS_PER_HOUR *2)) 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{Qt::DisplayRole}); #else emit dataChanged(i, i, QVector{Qt::DisplayRole}); #endif } } } int delay = youngest * 100; if (delay < 1000) delay = 1000; if (delay > SECS_PER_HOUR *1000) delay = SECS_PER_HOUR *1000; maintenanceTimer.setInterval(delay); maintenanceTimer.start(); qDebug() << "Maintenance end" << class_name << delay << youngest; } 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 { if (xport->match_all(F_JWK) && indexes.size() > 1) { QJsonArray arr; QSet names; foreach(QModelIndex idx, indexes) { QJsonObject jwk; pki_base *pki = fromIndex(idx); if (pki) { pki->fillJWK(jwk, xport); QString name = jwk["kid"].toString(); for (int i = 1; names.contains(name); i++) name = QString("%1 (%2)").arg(jwk["kid"].toString()).arg(i); jwk["kid"] = name; names.insert(name); arr.append(jwk); } } QJsonObject obj { { "keys", arr } }; QJsonDocument doc(obj); file.write(doc.toJson()); } else { foreach(QModelIndex idx, indexes) exportItem(idx, xport, file); } } void db_base::exportItem(const QModelIndex &index, const pki_export *xport, XFile &file) const { pki_base *pki = fromIndex(index); if (xport->match_all(F_JWK)) pki->exportToJWK(file, xport); } int db_base::exportFlags(const QModelIndexList &indexes) const { int disabled_flags = 0; foreach(const QModelIndex &idx, indexes) disabled_flags |= exportFlags(idx); return disabled_flags; } void db_base::setSelected(const QVariant &v) { selected = v; }