/* enabled_protocols_model.cpp * * Wireshark - Network traffic analyzer * By Gerald Combs * Copyright 1998 Gerald Combs * * SPDX-License-Identifier: GPL-2.0-or-later */ #include #include #include #include #include #include "main_application.h" #include class ProtocolTreeItem : public EnabledProtocolItem { public: ProtocolTreeItem(protocol_t* proto, EnabledProtocolItem* parent) : EnabledProtocolItem(proto_get_protocol_short_name(proto), proto_get_protocol_long_name(proto), proto_is_protocol_enabled(proto), parent), proto_(proto) { } virtual ~ProtocolTreeItem() {} protected: virtual void applyValuePrivate(gboolean value) { if (! proto_can_toggle_protocol(proto_get_id(proto_))) { return; } proto_set_decoding(proto_get_id(proto_), value); } private: protocol_t* proto_; }; class HeuristicTreeItem : public EnabledProtocolItem { public: HeuristicTreeItem(heur_dtbl_entry_t *heuristic, EnabledProtocolItem* parent) : EnabledProtocolItem(heuristic->short_name, heuristic->display_name, heuristic->enabled, parent), heuristic_table_(heuristic) { type_ = EnabledProtocolItem::Heuristic; } virtual ~HeuristicTreeItem() {} protected: virtual void applyValuePrivate(gboolean value) { heuristic_table_->enabled = value; } private: heur_dtbl_entry_t *heuristic_table_; }; EnabledProtocolItem::EnabledProtocolItem(QString name, QString description, bool enabled, EnabledProtocolItem* parent) : ModelHelperTreeItem(parent), name_(name), description_(description), enabled_(enabled), enabledInit_(enabled), type_(EnabledProtocolItem::Standard) { } EnabledProtocolItem::~EnabledProtocolItem() { } EnabledProtocolItem::EnableProtocolType EnabledProtocolItem::type() const { return type_; } bool EnabledProtocolItem::applyValue() { if (enabledInit_ != enabled_) { applyValuePrivate(enabled_); return true; } return false; } EnabledProtocolsModel::EnabledProtocolsModel(QObject *parent) : QAbstractItemModel(parent), root_(new ProtocolTreeItem(NULL, NULL)) { } EnabledProtocolsModel::~EnabledProtocolsModel() { delete root_; } int EnabledProtocolsModel::rowCount(const QModelIndex &parent) const { EnabledProtocolItem *parent_item; if (parent.column() > 0) return 0; if (!parent.isValid()) parent_item = root_; else parent_item = static_cast(parent.internalPointer()); if (parent_item == NULL) return 0; return parent_item->childCount(); } int EnabledProtocolsModel::columnCount(const QModelIndex&) const { return colLast; } QVariant EnabledProtocolsModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { switch ((enum EnabledProtocolsColumn)section) { case colProtocol: return tr("Protocol"); case colDescription: return tr("Description"); default: break; } } return QVariant(); } QModelIndex EnabledProtocolsModel::parent(const QModelIndex& index) const { if (!index.isValid()) return QModelIndex(); EnabledProtocolItem* item = static_cast(index.internalPointer()); if (item != NULL) { EnabledProtocolItem* parent_item = item->parentItem(); if (parent_item != NULL) { if (parent_item == root_) return QModelIndex(); return createIndex(parent_item->row(), 0, parent_item); } } return QModelIndex(); } QModelIndex EnabledProtocolsModel::index(int row, int column, const QModelIndex& parent) const { if (!hasIndex(row, column, parent)) return QModelIndex(); EnabledProtocolItem *parent_item, *child_item; if (!parent.isValid()) parent_item = root_; else parent_item = static_cast(parent.internalPointer()); Q_ASSERT(parent_item); child_item = parent_item->child(row); if (child_item) { return createIndex(row, column, child_item); } return QModelIndex(); } Qt::ItemFlags EnabledProtocolsModel::flags(const QModelIndex &index) const { if (!index.isValid()) return Qt::ItemFlags(); Qt::ItemFlags flags = QAbstractItemModel::flags(index); switch(index.column()) { case colProtocol: flags |= Qt::ItemIsUserCheckable; break; default: break; } return flags; } QVariant EnabledProtocolsModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); EnabledProtocolItem* item = static_cast(index.internalPointer()); if (item == NULL) return QVariant(); switch (role) { case Qt::DisplayRole: switch ((enum EnabledProtocolsColumn)index.column()) { case colProtocol: return item->name(); case colDescription: return item->description(); default: break; } break; case Qt::CheckStateRole: switch ((enum EnabledProtocolsColumn)index.column()) { case colProtocol: return item->enabled() ? Qt::Checked : Qt::Unchecked; default: break; } break; case DATA_PROTOCOL_TYPE: return QVariant::fromValue(item->type()); break; default: break; } return QVariant(); } bool EnabledProtocolsModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (!index.isValid()) return false; if ((role != Qt::EditRole) && ((index.column() == colProtocol) && (role != Qt::CheckStateRole))) return false; if (data(index, role) == value) { // Data appears unchanged, do not do additional checks. return true; } EnabledProtocolItem* item = static_cast(index.internalPointer()); if (item == NULL) return false; item->setEnabled(value.toInt() == Qt::Checked ? true : false); return true; } static void addHeuristicItem(gpointer data, gpointer user_data) { heur_dtbl_entry_t* heur = (heur_dtbl_entry_t*)data; ProtocolTreeItem* protocol_item = (ProtocolTreeItem*)user_data; HeuristicTreeItem* heuristic_row = new HeuristicTreeItem(heur, protocol_item); protocol_item->prependChild(heuristic_row); } void EnabledProtocolsModel::populate() { void *cookie; protocol_t *protocol; beginResetModel(); // Iterate over all the protocols for (int i = proto_get_first_protocol(&cookie); i != -1; i = proto_get_next_protocol(&cookie)) { if (proto_can_toggle_protocol(i)) { protocol = find_protocol_by_id(i); if (!proto_is_pino(protocol)) { ProtocolTreeItem* protocol_row = new ProtocolTreeItem(protocol, root_); root_->prependChild(protocol_row); proto_heuristic_dissector_foreach(protocol, addHeuristicItem, protocol_row); } } } endResetModel(); } void EnabledProtocolsModel::applyChanges(bool writeChanges) { bool redissect = false; for (int proto_index = 0; proto_index < root_->childCount(); proto_index++) { EnabledProtocolItem* proto = root_->child(proto_index); redissect |= proto->applyValue(); for (int heur_index = 0; heur_index < proto->childCount(); heur_index++) { EnabledProtocolItem* heur = proto->child(heur_index); redissect |= heur->applyValue(); } } if (redissect) { saveChanges(writeChanges); } } void EnabledProtocolsModel::disableProtocol(struct _protocol *protocol) { ProtocolTreeItem disabled_proto(protocol, NULL); disabled_proto.setEnabled(false); if (disabled_proto.applyValue()) { saveChanges(); } } void EnabledProtocolsModel::saveChanges(bool writeChanges) { if (writeChanges) { save_enabled_and_disabled_lists(); } mainApp->emitAppSignal(MainApplication::PacketDissectionChanged); } EnabledProtocolsProxyModel::EnabledProtocolsProxyModel(QObject * parent) : QSortFilterProxyModel(parent), type_(EnabledProtocolsProxyModel::EveryWhere), protocolType_(EnabledProtocolItem::Any), filter_() {} bool EnabledProtocolsProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const { //Use EnabledProtocolItem directly for better performance EnabledProtocolItem* left_item = static_cast(left.internalPointer()); EnabledProtocolItem* right_item = static_cast(right.internalPointer()); if ((left_item != NULL) && (right_item != NULL)) { int compare_ret = 0; if (left.column() == EnabledProtocolsModel::colProtocol) compare_ret = left_item->name().compare(right_item->name(), Qt::CaseInsensitive); else if (left.column() == EnabledProtocolsModel::colDescription) compare_ret = left_item->description().compare(right_item->description(), Qt::CaseInsensitive); if (compare_ret < 0) return true; } return false; } Qt::ItemFlags EnabledProtocolsProxyModel::flags(const QModelIndex &index) const { Qt::ItemFlags flags = Qt::NoItemFlags; if (index.isValid()) { QModelIndex source = mapToSource(index); if (filterAcceptsSelf(source.row(), source.parent()) ) { flags = Qt::ItemIsEnabled; flags |= Qt::ItemIsSelectable; flags |= Qt::ItemIsUserCheckable; } } return flags; } bool EnabledProtocolsProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const { if (filterAcceptsSelf(sourceRow, sourceParent)) return true; #if 0 QModelIndex parent = sourceParent; while (parent.isValid()) { if (filterAcceptsSelf(parent.row(), parent.parent())) return true; parent = parent.parent(); } #endif if (filterAcceptsChild(sourceRow, sourceParent)) return true; return false; } bool EnabledProtocolsProxyModel::filterAcceptsSelf(int sourceRow, const QModelIndex &sourceParent) const { QModelIndex nameIdx = sourceModel()->index(sourceRow, EnabledProtocolsModel::colProtocol, sourceParent); if (! nameIdx.isValid()) return false; EnabledProtocolItem* item = static_cast(nameIdx.internalPointer()); if (! item) return false; QRegularExpression regex(filter_, QRegularExpression::CaseInsensitiveOption); if (! regex.isValid()) return false; if (protocolType_ == EnabledProtocolItem::Any || protocolType_ == item->type()) { if (type_ != EnabledProtocolsProxyModel::EnabledItems && type_ != EnabledProtocolsProxyModel::DisabledItems) { if (! filter_.isEmpty()) { if (item->name().contains(regex) && type_ != OnlyDescription) return true; if (item->description().contains(regex) && type_ != OnlyProtocol) return true; } else return true; } else if (filter_.isEmpty() || (! filter_.isEmpty() && (item->name().contains(regex) || item->description().contains(regex)))) { if (type_ == EnabledProtocolsProxyModel::EnabledItems && item->enabled()) return true; else if (type_ == EnabledProtocolsProxyModel::DisabledItems && ! item->enabled()) return true; } } return false; } bool EnabledProtocolsProxyModel::filterAcceptsChild(int sourceRow, const QModelIndex &sourceParent) const { QModelIndex item = sourceModel()->index(sourceRow, EnabledProtocolsModel::colProtocol, sourceParent); if (! item.isValid()) return false; int childCount = item.model()->rowCount(item); if (childCount == 0) return false; for (int i = 0; i < childCount; i++) { if (filterAcceptsSelf(i, item)) return true; #if 0 /* Recursive search disabled for performance reasons */ if (filterAcceptsChild(i, item)) return true; #endif } return false; } void EnabledProtocolsProxyModel::setFilter(const QString& filter, EnabledProtocolsProxyModel::SearchType type, EnabledProtocolItem::EnableProtocolType protocolType) { filter_ = filter; type_ = type; protocolType_ = protocolType; invalidateFilter(); } void EnabledProtocolsProxyModel::setItemsEnable(EnabledProtocolsProxyModel::EnableType enableType, QModelIndex parent) { if (! sourceModel()) return; if (! parent.isValid()) beginResetModel(); int rowcount = rowCount(parent); for (int row = 0; row < rowcount; row++) { QModelIndex idx = index(row, EnabledProtocolsModel::colProtocol, parent); QModelIndex sIdx = mapToSource(idx); if (sIdx.isValid()) { EnabledProtocolItem* item = static_cast(sIdx.internalPointer()); if (item && (protocolType_ == EnabledProtocolItem::Any || protocolType_ == item->type()) ) { Qt::CheckState enable = idx.data(Qt::CheckStateRole).value(); if (enableType == Enable) enable = Qt::Checked; else if (enableType == Disable) enable = Qt::Unchecked; else enable = enable == Qt::Checked ? Qt::Unchecked : Qt::Checked; sourceModel()->setData(mapToSource(idx), QVariant::fromValue(enable), Qt::CheckStateRole); } } setItemsEnable(enableType, idx); } if (! parent.isValid()) endResetModel(); }