diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 20:34:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 20:34:10 +0000 |
commit | e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc (patch) | |
tree | 68cb5ef9081156392f1dd62a00c6ccc1451b93df /ui/qt/models/interface_tree_cache_model.cpp | |
parent | Initial commit. (diff) | |
download | wireshark-e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc.tar.xz wireshark-e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc.zip |
Adding upstream version 4.2.2.upstream/4.2.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ui/qt/models/interface_tree_cache_model.cpp')
-rw-r--r-- | ui/qt/models/interface_tree_cache_model.cpp | 584 |
1 files changed, 584 insertions, 0 deletions
diff --git a/ui/qt/models/interface_tree_cache_model.cpp b/ui/qt/models/interface_tree_cache_model.cpp new file mode 100644 index 0000000..71eda50 --- /dev/null +++ b/ui/qt/models/interface_tree_cache_model.cpp @@ -0,0 +1,584 @@ +/* interface_tree_cache_model.cpp + * Model caching interface changes before sending them to global storage + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + + +#include <ui/qt/models/interface_tree_cache_model.h> + +#include "epan/prefs.h" + +#include <ui/qt/utils/qt_ui_utils.h> +#include "ui/capture_globals.h" +#include "wsutil/utf8_entities.h" + +#include "wiretap/wtap.h" + +#include "main_application.h" + +#include <QIdentityProxyModel> + +InterfaceTreeCacheModel::InterfaceTreeCacheModel(QObject *parent) : + QIdentityProxyModel(parent) +{ + /* ATTENTION: This cache model is not intended to be used with anything + * else then InterfaceTreeModel, and will break with anything else + * leading to unintended results. */ + sourceModel = new InterfaceTreeModel(parent); + + QIdentityProxyModel::setSourceModel(sourceModel); + storage = new QMap<int, QMap<InterfaceTreeColumns, QVariant> *>(); + + checkableColumns << IFTREE_COL_HIDDEN << IFTREE_COL_PROMISCUOUSMODE; +#ifdef HAVE_PCAP_CREATE + checkableColumns << IFTREE_COL_MONITOR_MODE; +#endif + + editableColumns << IFTREE_COL_COMMENT << IFTREE_COL_SNAPLEN << IFTREE_COL_PIPE_PATH; + +#ifdef CAN_SET_CAPTURE_BUFFER_SIZE + editableColumns << IFTREE_COL_BUFFERLEN; +#endif +} + +InterfaceTreeCacheModel::~InterfaceTreeCacheModel() +{ +#ifdef HAVE_LIBPCAP + /* This list should only exist, if the dialog is closed, without calling save first */ + newDevices.clear(); +#endif + + delete storage; + delete sourceModel; +} + +QVariant InterfaceTreeCacheModel::getColumnContent(int idx, int col, int role) +{ + return InterfaceTreeCacheModel::data(index(idx, col), role); +} + +#ifdef HAVE_LIBPCAP +void InterfaceTreeCacheModel::reset(int row) +{ + if (row < 0) + { + delete storage; + storage = new QMap<int, QMap<InterfaceTreeColumns, QVariant> *>(); + } + else + { + if (storage->count() > row) + storage->remove(storage->keys().at(row)); + } +} + +void InterfaceTreeCacheModel::saveNewDevices() +{ + QList<interface_t>::const_iterator it = newDevices.constBegin(); + /* idx is used for iterating only over the indices of the new devices. As all new + * devices are stored with an index higher then sourceModel->rowCount(), we start + * only with those storage indices. + * it is just the iterator over the new devices. A new device must not necessarily + * have storage, which will lead to that device not being stored in global_capture_opts */ + for (int idx = sourceModel->rowCount(); it != newDevices.constEnd(); ++it, idx++) + { + interface_t *device = const_cast<interface_t *>(&(*it)); + bool useDevice = false; + + QMap<InterfaceTreeColumns, QVariant> * dataField = storage->value(idx, 0); + /* When devices are being added, they are added using generic values. So only devices + * whose data have been changed should be used from here on out. */ + if (dataField != 0) + { + if (device->if_info.type != IF_PIPE) + { + continue; + } + + if (device->if_info.type == IF_PIPE) + { + QVariant saveValue = dataField->value(IFTREE_COL_PIPE_PATH); + if (saveValue.isValid()) + { + g_free(device->if_info.name); + device->if_info.name = qstring_strdup(saveValue.toString()); + + g_free(device->name); + device->name = qstring_strdup(saveValue.toString()); + + g_free(device->display_name); + device->display_name = qstring_strdup(saveValue.toString()); + useDevice = true; + } + } + + if (useDevice) + g_array_append_val(global_capture_opts.all_ifaces, *device); + + } + + /* All entries of this new devices have been considered */ + storage->remove(idx); + delete dataField; + } + + newDevices.clear(); +} + +void InterfaceTreeCacheModel::save() +{ + if (storage->count() == 0) + return; + + QMap<char**, QStringList> prefStorage; + + /* No devices are hidden until checking "Show" state */ + prefStorage[&prefs.capture_devices_hide] = QStringList(); + + /* Storing new devices first including their changed values */ + saveNewDevices(); + + + for (unsigned int idx = 0; idx < global_capture_opts.all_ifaces->len; idx++) + { + interface_t *device = &g_array_index(global_capture_opts.all_ifaces, interface_t, idx); + + if (! device->name) + continue; + + /* Try to load a saved value row for this index */ + QMap<InterfaceTreeColumns, QVariant> * dataField = storage->value(idx, 0); + + /* Handle the storing of values for this device here */ + if (dataField) + { + QMap<InterfaceTreeColumns, QVariant>::const_iterator it = dataField->constBegin(); + while (it != dataField->constEnd()) + { + InterfaceTreeColumns col = it.key(); + QVariant saveValue = it.value(); + + /* Setting the field values for each individual saved value cannot be generic, as the + * struct cannot be accessed in a generic way. Therefore below, each individually changed + * value has to be handled separately. Comments are stored only in the preference file + * and applied to the data name during loading. Therefore comments are not handled here */ + + if (col == IFTREE_COL_HIDDEN) + { + device->hidden = saveValue.toBool(); + } + else if (device->if_info.type == IF_EXTCAP) + { + /* extcap interfaces do not have the following columns. + * ATTENTION: all generic columns must be added, BEFORE this + * if-clause, or they will be ignored for extcap interfaces */ + } + else if (col == IFTREE_COL_PROMISCUOUSMODE) + { + device->pmode = saveValue.toBool(); + } +#ifdef HAVE_PCAP_CREATE + else if (col == IFTREE_COL_MONITOR_MODE) + { + device->monitor_mode_enabled = saveValue.toBool(); + } +#endif + else if (col == IFTREE_COL_SNAPLEN) + { + int iVal = saveValue.toInt(); + if (iVal != WTAP_MAX_PACKET_SIZE_STANDARD) + { + device->has_snaplen = true; + device->snaplen = iVal; + } + else + { + device->has_snaplen = false; + device->snaplen = WTAP_MAX_PACKET_SIZE_STANDARD; + } + } +#ifdef CAN_SET_CAPTURE_BUFFER_SIZE + else if (col == IFTREE_COL_BUFFERLEN) + { + device->buffer = saveValue.toInt(); + } +#endif + ++it; + } + } + + QVariant content = getColumnContent(idx, IFTREE_COL_HIDDEN, Qt::CheckStateRole); + if (content.isValid() && static_cast<Qt::CheckState>(content.toInt()) == Qt::Unchecked) + prefStorage[&prefs.capture_devices_hide] << QString(device->name); + + content = getColumnContent(idx, IFTREE_COL_COMMENT); + if (content.isValid() && content.toString().size() > 0) + prefStorage[&prefs.capture_devices_descr] << QString("%1(%2)").arg(device->name).arg(content.toString()); + + bool allowExtendedColumns = true; + + if (device->if_info.type == IF_EXTCAP) + allowExtendedColumns = false; + + if (allowExtendedColumns) + { + content = getColumnContent(idx, IFTREE_COL_PROMISCUOUSMODE, Qt::CheckStateRole); + if (content.isValid()) + { + bool value = static_cast<Qt::CheckState>(content.toInt()) == Qt::Checked; + prefStorage[&prefs.capture_devices_pmode] << QString("%1(%2)").arg(device->name).arg(value ? 1 : 0); + } + +#ifdef HAVE_PCAP_CREATE + content = getColumnContent(idx, IFTREE_COL_MONITOR_MODE, Qt::CheckStateRole); + if (content.isValid() && static_cast<Qt::CheckState>(content.toInt()) == Qt::Checked) + prefStorage[&prefs.capture_devices_monitor_mode] << QString(device->name); +#endif + + content = getColumnContent(idx, IFTREE_COL_SNAPLEN); + if (content.isValid()) + { + int value = content.toInt(); + prefStorage[&prefs.capture_devices_snaplen] << + QString("%1:%2(%3)").arg(device->name). + arg(device->has_snaplen ? 1 : 0). + arg(device->has_snaplen ? value : WTAP_MAX_PACKET_SIZE_STANDARD); + } + +#ifdef CAN_SET_CAPTURE_BUFFER_SIZE + content = getColumnContent(idx, IFTREE_COL_BUFFERLEN); + if (content.isValid()) + { + int value = content.toInt(); + if (value != -1) + { + prefStorage[&prefs.capture_devices_buffersize] << + QString("%1(%2)").arg(device->name). + arg(value); + } + } +#endif + } + } + + QMap<char**, QStringList>::const_iterator it = prefStorage.constBegin(); + while (it != prefStorage.constEnd()) + { + char ** key = it.key(); + + g_free(*key); + *key = qstring_strdup(it.value().join(",")); + + ++it; + } + + mainApp->emitAppSignal(MainApplication::LocalInterfacesChanged); +} +#endif + +int InterfaceTreeCacheModel::rowCount(const QModelIndex & parent) const +{ + int totalCount = sourceModel->rowCount(parent); +#ifdef HAVE_LIBPCAP + totalCount += newDevices.size(); +#endif + return totalCount; +} + +bool InterfaceTreeCacheModel::changeIsAllowed(InterfaceTreeColumns col) const +{ + if (editableColumns.contains(col) || checkableColumns.contains(col)) + return true; + return false; +} + +#ifdef HAVE_LIBPCAP +const interface_t * InterfaceTreeCacheModel::lookup(const QModelIndex &index) const +{ + const interface_t * result = 0; + + if (! index.isValid() || ! global_capture_opts.all_ifaces) + return result; + + int idx = index.row(); + + if ((unsigned int) idx >= global_capture_opts.all_ifaces->len) + { + idx = idx - global_capture_opts.all_ifaces->len; + if (idx < newDevices.size()) + result = &newDevices[idx]; + } + else + { + result = &g_array_index(global_capture_opts.all_ifaces, interface_t, idx); + } + + return result; +} +#endif + +/* This checks if the column can be edited for the given index. This differs from + * isAvailableField in such a way, that it is only used in flags and not any + * other method.*/ +bool InterfaceTreeCacheModel::isAllowedToBeEdited(const QModelIndex &index) const +{ +#ifndef HAVE_LIBPCAP + Q_UNUSED(index); +#else + const interface_t * device = lookup(index); + if (device == 0) + return false; + + InterfaceTreeColumns col = (InterfaceTreeColumns) index.column(); + if (device->if_info.type == IF_EXTCAP) + { + /* extcap interfaces do not have those settings */ + if (col == IFTREE_COL_PROMISCUOUSMODE || col == IFTREE_COL_SNAPLEN) + return false; +#ifdef CAN_SET_CAPTURE_BUFFER_SIZE + if (col == IFTREE_COL_BUFFERLEN) + return false; +#endif + } +#endif + return true; +} + +// Whether this field is available for modification and display. +bool InterfaceTreeCacheModel::isAvailableField(const QModelIndex &index) const +{ +#ifndef HAVE_LIBPCAP + Q_UNUSED(index); +#else + const interface_t * device = lookup(index); + + if (device == 0) + return false; + + InterfaceTreeColumns col = (InterfaceTreeColumns) index.column(); + if (col == IFTREE_COL_HIDDEN) + { + // Do not allow default capture interface to be hidden. + if (! g_strcmp0(prefs.capture_device, device->display_name)) + return false; + } +#endif + + return true; +} + +Qt::ItemFlags InterfaceTreeCacheModel::flags(const QModelIndex &index) const +{ + if (! index.isValid()) + return Qt::ItemFlags(); + + Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable; + + InterfaceTreeColumns col = (InterfaceTreeColumns) index.column(); + + if (changeIsAllowed(col) && isAvailableField(index) && isAllowedToBeEdited(index)) + { + if (checkableColumns.contains(col)) + { + flags |= Qt::ItemIsUserCheckable; + } + else + { + flags |= Qt::ItemIsEditable; + } + } + + return flags; +} + +bool InterfaceTreeCacheModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (! index.isValid()) + return false; + + if (! isAvailableField(index)) + return false; + + int row = index.row(); + InterfaceTreeColumns col = (InterfaceTreeColumns)index.column(); + + if (role == Qt::CheckStateRole || role == Qt::EditRole) + { + if (changeIsAllowed(col) ) + { + QVariant saveValue = value; + + QMap<InterfaceTreeColumns, QVariant> * dataField = 0; + /* obtain the list of already stored changes for this row. If none exist + * create a new storage row for this entry */ + if ((dataField = storage->value(row, 0)) == 0) + { + dataField = new QMap<InterfaceTreeColumns, QVariant>(); + storage->insert(row, dataField); + } + + dataField->insert(col, saveValue); + + return true; + } + } + + return false; +} + +QVariant InterfaceTreeCacheModel::data(const QModelIndex &index, int role) const +{ + if (! index.isValid()) + return QVariant(); + + int row = index.row(); + + InterfaceTreeColumns col = (InterfaceTreeColumns)index.column(); + + if (isAvailableField(index) && isAllowedToBeEdited(index)) + { + if (((role == Qt::DisplayRole || role == Qt::EditRole) && editableColumns.contains(col)) || + (role == Qt::CheckStateRole && checkableColumns.contains(col)) ) + { + QMap<InterfaceTreeColumns, QVariant> * dataField = 0; + if ((dataField = storage->value(row, 0)) != 0) + { + if (dataField->contains(col)) + { + return dataField->value(col, QVariant()); + } + } + } + } + else + { + if (role == Qt::CheckStateRole) + return QVariant(); + else if (role == Qt::DisplayRole) + return QString(UTF8_EM_DASH); + } + + if (row < sourceModel->rowCount()) + { + return sourceModel->data(index, role); + } +#ifdef HAVE_LIBPCAP + else + { + /* Handle all fields, which will have to be displayed for new devices. Only pipes + * are supported at the moment, so the information to be displayed is pretty limited. + * After saving, the devices are stored in global_capture_opts and no longer + * classify as new devices. */ + const interface_t * device = lookup(index); + + if (device != 0) + { + if (role == Qt::DisplayRole || role == Qt::EditRole) + { + if (col == IFTREE_COL_PIPE_PATH || + col == IFTREE_COL_NAME || + col == IFTREE_COL_DESCRIPTION) + { + + QMap<InterfaceTreeColumns, QVariant> * dataField = 0; + if ((dataField = storage->value(row, 0)) != 0 && + dataField->contains(IFTREE_COL_PIPE_PATH)) + { + return dataField->value(IFTREE_COL_PIPE_PATH, QVariant()); + } + else + return QString(device->name); + } + else if (col == IFTREE_COL_TYPE) + { + return QVariant::fromValue((int)device->if_info.type); + } + } + else if (role == Qt::CheckStateRole) + { + if (col == IFTREE_COL_HIDDEN) + { + // Do not allow default capture interface to be hidden. + if (! g_strcmp0(prefs.capture_device, device->display_name)) + return QVariant(); + + /* Hidden is a de-selection, therefore inverted logic here */ + return device->hidden ? Qt::Unchecked : Qt::Checked; + } + } + } + } +#endif + + return QVariant(); +} + +#ifdef HAVE_LIBPCAP +QModelIndex InterfaceTreeCacheModel::index(int row, int column, const QModelIndex &parent) const +{ + if (row >= sourceModel->rowCount() && (row - sourceModel->rowCount()) < newDevices.count()) + { + return createIndex(row, column, (void *)0); + } + + return QIdentityProxyModel::index(row, column, parent); +} + +void InterfaceTreeCacheModel::addDevice(const interface_t * newDevice) +{ + emit beginInsertRows(QModelIndex(), rowCount(), rowCount()); + newDevices << *newDevice; + emit endInsertRows(); +} + +void InterfaceTreeCacheModel::deleteDevice(const QModelIndex &index) +{ + if (! index.isValid()) + return; + + emit beginRemoveRows(QModelIndex(), index.row(), index.row()); + + int row = index.row(); + + /* device is in newDevices */ + if (row >= sourceModel->rowCount()) + { + int newDeviceIdx = row - sourceModel->rowCount(); + + newDevices.removeAt(newDeviceIdx); + if (storage->contains(index.row())) + storage->remove(index.row()); + + /* The storage array has to be resorted, if the index, that was removed + * had been in the middle of the array. Can't start at index.row(), as + * it may not be contained in storage + * We must iterate using a list, not an iterator, otherwise the change + * will fold on itself. */ + QList<int> storageKeys = storage->keys(); + for (int i = 0; i < storageKeys.size(); ++i) + { + int key = storageKeys.at(i); + if (key > index.row()) + { + storage->insert(key - 1, storage->value(key)); + storage->remove(key); + } + } + + emit endRemoveRows(); + } + else + { + interface_t *device = &g_array_index(global_capture_opts.all_ifaces, interface_t, row); + capture_opts_free_interface_t(device); + global_capture_opts.all_ifaces = g_array_remove_index(global_capture_opts.all_ifaces, row); + emit endRemoveRows(); + mainApp->emitAppSignal(MainApplication::LocalInterfacesChanged); + } +} +#endif |