diff --git a/ui/qt/CMakeLists.txt b/ui/qt/CMakeLists.txt index 596ef02818..01b4fb64cb 100644 --- a/ui/qt/CMakeLists.txt +++ b/ui/qt/CMakeLists.txt @@ -55,6 +55,8 @@ set(WIRESHARK_UTILS_HEADERS set(WIRESHARK_MODEL_HEADERS models/cache_proxy_model.h + models/decode_as_delegate.h + models/decode_as_model.h models/interface_sort_filter_model.h models/interface_tree_cache_model.h models/interface_tree_model.h @@ -252,6 +254,8 @@ set(WIRESHARK_UTILS_SRCS set(WIRESHARK_MODEL_SRCS models/cache_proxy_model.cpp + models/decode_as_delegate.cpp + models/decode_as_model.cpp models/interface_sort_filter_model.cpp models/interface_tree_cache_model.cpp models/interface_tree_model.cpp diff --git a/ui/qt/Makefile.am b/ui/qt/Makefile.am index 37723c9360..c7647628c3 100644 --- a/ui/qt/Makefile.am +++ b/ui/qt/Makefile.am @@ -184,6 +184,8 @@ MOC_UTILS_HDRS = \ # Files for delegates and models MOC_MODELS_HDRS = \ models/cache_proxy_model.h \ + models/decode_as_delegate.h \ + models/decode_as_model.h \ models/interface_sort_filter_model.h \ models/interface_tree_cache_model.h \ models/interface_tree_model.h \ @@ -495,6 +497,8 @@ WIRESHARK_QT_UTILS_SRC = \ WIRESHARK_QT_MODELS_SRCS = \ models/cache_proxy_model.cpp \ + models/decode_as_delegate.cpp \ + models/decode_as_model.cpp \ models/interface_sort_filter_model.cpp \ models/interface_tree_cache_model.cpp \ models/interface_tree_model.cpp \ diff --git a/ui/qt/decode_as_dialog.cpp b/ui/qt/decode_as_dialog.cpp index e79530cf8d..ced22e5780 100644 --- a/ui/qt/decode_as_dialog.cpp +++ b/ui/qt/decode_as_dialog.cpp @@ -23,10 +23,7 @@ #include #include "epan/decode_as.h" -#include "epan/dissectors/packet-dcerpc.h" #include "epan/epan_dissect.h" -#include "epan/prefs.h" -#include "epan/prefs-int.h" #include "ui/decode_as_utils.h" #include "ui/simple_dialog.h" @@ -41,266 +38,66 @@ #include #include #include +#include // To do: // - Ranges // - Add DCERPC support (or make DCERPC use a regular dissector table?) -// - Fix string (BER) selectors -// - Use a StyledItemDelegate to edit entries instead of managing widgets -// by hand. See the coloring rules dialog for an example. - -const int table_col_ = 0; -const int selector_col_ = 1; -const int type_col_ = 2; -const int default_col_ = 3; // aka "initial" -const int proto_col_ = 4; // aka "current" - -const char *default_table_ = "TCP port"; -const char *default_proto_ = DECODE_AS_NONE; -const char *default_int_selector_ = "0"; // Arbitrary -const char *default_str_selector_ = "foo"; // Arbitrary - -typedef struct _dissector_info_t { - QString proto_name; - dissector_handle_t dissector_handle; -} dissector_info_t; - -Q_DECLARE_METATYPE(dissector_info_t *) - -typedef struct _table_item_t { - const gchar* proto_name; - guint8 curr_layer_num; -} table_item_t; - -Q_DECLARE_METATYPE(table_item_t) DecodeAsDialog::DecodeAsDialog(QWidget *parent, capture_file *cf, bool create_new) : GeometryStateDialog(parent), ui(new Ui::DecodeAsDialog), - cap_file_(cf), - table_names_combo_box_(NULL), - selector_combo_box_(NULL), - cur_proto_combo_box_(NULL) + model_(new DecodeAsModel(this, cf)), + delegate_(NULL) { ui->setupUi(this); loadGeometry(); + delegate_ = new DecodeAsDelegate(ui->decodeAsTreeView, cf); + + ui->decodeAsTreeView->setModel(model_); + ui->decodeAsTreeView->setItemDelegate(delegate_); + setWindowTitle(wsApp->windowTitleString(tr("Decode As" UTF8_HORIZONTAL_ELLIPSIS))); - ui->deleteToolButton->setEnabled(false); - GList *cur; - for (cur = decode_as_list; cur; cur = cur->next) { - decode_as_t *entry = (decode_as_t *) cur->data; - QString table_ui_name = get_dissector_table_ui_name(entry->table_name); - if (!table_ui_name.isEmpty()) { - ui_name_to_name_[table_ui_name] = entry->table_name; - } - } - - connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(fillTable())); fillTable(); - if (create_new) on_newToolButton_clicked(); + if (create_new) + on_newToolButton_clicked(); } DecodeAsDialog::~DecodeAsDialog() { delete ui; -} - -void DecodeAsDialog::setCaptureFile(capture_file *cf) -{ - cap_file_ = cf; - fillTable(); -} - -QString DecodeAsDialog::entryString(const gchar *table_name, gpointer value) -{ - QString entry_str; - ftenum_t selector_type = get_dissector_table_selector_type(table_name); - - switch (selector_type) { - - case FT_UINT8: - case FT_UINT16: - case FT_UINT24: - case FT_UINT32: - { - uint num_val = GPOINTER_TO_UINT(value); - switch (get_dissector_table_param(table_name)) { - - case BASE_DEC: - entry_str = QString::number(num_val); - break; - - case BASE_HEX: - int width; - switch (selector_type) { - case FT_UINT8: - width = 2; - break; - case FT_UINT16: - width = 4; - break; - case FT_UINT24: - width = 6; - break; - case FT_UINT32: - width = 8; - break; - - default: - g_assert_not_reached(); - break; - } - entry_str = QString("%1").arg(int_to_qstring(num_val, width, 16)); - break; - - case BASE_OCT: - entry_str = "0" + QString::number(num_val, 8); - break; - } - break; - } - - case FT_STRING: - case FT_STRINGZ: - case FT_UINT_STRING: - case FT_STRINGZPAD: - entry_str = (char *)value; - break; - - case FT_GUID: - //TODO: DCE/RPC dissector table - break; - - default: - g_assert_not_reached(); - break; - } - return entry_str; + delete model_; + delete delegate_; } void DecodeAsDialog::fillTable() { - ui->decodeAsTreeWidget->clear(); - dissector_all_tables_foreach_changed(buildChangedList, this); - decode_dcerpc_add_show_list(buildDceRpcChangedList, this); + model_->fillTable(); - if (ui->decodeAsTreeWidget->topLevelItemCount() > 0) { - for (int i = 0; i < ui->decodeAsTreeWidget->columnCount(); i++) { - ui->decodeAsTreeWidget->resizeColumnToContents(i); + resizeColumns(); + + //set selection as first row + if (model_->rowCount() > 0) { + const QModelIndex &new_index = model_->index(0, 0); + ui->decodeAsTreeView->setCurrentIndex(new_index); + } +} + +void DecodeAsDialog::resizeColumns() +{ + if (model_->rowCount() > 0) { + for (int i = 0; i < model_->columnCount(); i++) { + ui->decodeAsTreeView->resizeColumnToContents(i); } } } -void DecodeAsDialog::activateLastItem() +void DecodeAsDialog::on_decodeAsTreeView_currentItemChanged(const QModelIndex ¤t, const QModelIndex&) { - int last_idx = ui->decodeAsTreeWidget->topLevelItemCount() - 1; - if (last_idx < 0) return; - - QTreeWidgetItem *last_item = ui->decodeAsTreeWidget->invisibleRootItem()->child(last_idx); - if (!last_item) return; - - ui->decodeAsTreeWidget->setCurrentItem(last_item); - on_decodeAsTreeWidget_itemActivated(last_item); -} - -void DecodeAsDialog::on_decodeAsTreeWidget_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous) -{ - if (current == previous) return; - - for (int col = 0; col < ui->decodeAsTreeWidget->columnCount(); col++) { - if (previous && ui->decodeAsTreeWidget->itemWidget(previous, col)) { - ui->decodeAsTreeWidget->removeItemWidget(previous, col); - } - } -} - -void DecodeAsDialog::on_decodeAsTreeWidget_itemActivated(QTreeWidgetItem *item, int) -{ - GList *cur; - - table_names_combo_box_ = new QComboBox(); - QString current_text = item->text(table_col_); - QSet da_set; - - // If a packet is selected group its tables at the top in order - // from last-dissected to first. - - for (cur = decode_as_list; cur; cur = cur->next) { - decode_as_t *entry = (decode_as_t *) cur->data; - const char *table_name = get_dissector_table_ui_name(entry->table_name); - if (table_name) { - da_set.insert(get_dissector_table_ui_name(entry->table_name)); - } - } - - if (cap_file_ && cap_file_->edt) { - bool copying = !current_text.isEmpty(); - wmem_list_frame_t * protos = wmem_list_head(cap_file_->edt->pi.layers); - guint8 curr_layer_num = 1; - while (protos != NULL) { - int proto_id = GPOINTER_TO_INT(wmem_list_frame_data(protos)); - const gchar * proto_name = proto_get_protocol_filter_name(proto_id); - for (cur = decode_as_list; cur; cur = cur->next) { - decode_as_t *entry = (decode_as_t *) cur->data; - if (g_strcmp0(proto_name, entry->name) == 0) { - QString table_ui_name = get_dissector_table_ui_name(entry->table_name); - table_item_t table_item; - table_item.proto_name = proto_name; - table_item.curr_layer_num = curr_layer_num; - table_names_combo_box_->insertItem(0, table_ui_name, QVariant::fromValue(table_item)); - da_set.remove(table_ui_name); - if (!copying) { - current_text = table_ui_name; - } - } - } - protos = wmem_list_frame_next(protos); - curr_layer_num++; - } - } - - if (table_names_combo_box_->count() > 0) { - table_names_combo_box_->insertSeparator(table_names_combo_box_->count()); - } - - QList da_list = da_set.toList(); - qSort(da_list.begin(), da_list.end()); - - foreach (QString table_ui_name, da_list) { - table_names_combo_box_->addItem(table_ui_name, ui_name_to_name_[table_ui_name]); - } - - if (current_text.isEmpty()) current_text = default_table_; - ui->decodeAsTreeWidget->setItemWidget(item, table_col_, table_names_combo_box_); - - selector_combo_box_ = new QComboBox(); - selector_combo_box_->setEditable(true); - selector_combo_box_->lineEdit()->setText(item->text(selector_col_)); - - connect(selector_combo_box_, SIGNAL(editTextChanged(QString)), this, SLOT(selectorEditTextChanged(QString))); - - ui->decodeAsTreeWidget->setItemWidget(item, selector_col_, selector_combo_box_); - - cur_proto_combo_box_ = new QComboBox(); - - ui->decodeAsTreeWidget->setItemWidget(item, proto_col_, cur_proto_combo_box_); - connect(cur_proto_combo_box_, SIGNAL(currentIndexChanged(const QString &)), - this, SLOT(curProtoCurrentIndexChanged(const QString &))); - - table_names_combo_box_->setCurrentIndex(table_names_combo_box_->findText(current_text)); - tableNamesCurrentIndexChanged(current_text); - - connect(table_names_combo_box_, SIGNAL(currentIndexChanged(const QString &)), - this, SLOT(tableNamesCurrentIndexChanged(const QString &))); - table_names_combo_box_->setFocus(); -} - -void DecodeAsDialog::on_decodeAsTreeWidget_itemSelectionChanged() -{ - if (ui->decodeAsTreeWidget->selectedItems().length() > 0) { + if (current.isValid()) { ui->deleteToolButton->setEnabled(true); ui->copyToolButton->setEnabled(true); } else { @@ -309,99 +106,28 @@ void DecodeAsDialog::on_decodeAsTreeWidget_itemSelectionChanged() } } -void DecodeAsDialog::buildChangedList(const gchar *table_name, ftenum_t, gpointer key, gpointer value, gpointer user_data) -{ - DecodeAsDialog *da_dlg = (DecodeAsDialog *)user_data; - if (!da_dlg) return; - - dissector_handle_t default_dh, current_dh; - QString default_proto_name = DECODE_AS_NONE, current_proto_name = DECODE_AS_NONE; - QTreeWidgetItem *item = new QTreeWidgetItem(); - - item->setText(table_col_, get_dissector_table_ui_name(table_name)); - item->setText(selector_col_, da_dlg->entryString(table_name, key)); - da_dlg->fillTypeColumn(item); - - default_dh = dtbl_entry_get_initial_handle((dtbl_entry_t *)value); - if (default_dh) { - default_proto_name = dissector_handle_get_short_name(default_dh); - } - item->setText(default_col_, default_proto_name); - - current_dh = dtbl_entry_get_handle((dtbl_entry_t *)value); - if (current_dh) { - current_proto_name = dissector_handle_get_short_name(current_dh); - } - item->setText(proto_col_, current_proto_name); - - dissector_info_t *dissector_info = new dissector_info_t(); - dissector_info->proto_name = current_proto_name; - dissector_info->dissector_handle = current_dh; - item->setData(proto_col_, Qt::UserRole, VariantPointer::asQVariant(dissector_info)); - - da_dlg->ui->decodeAsTreeWidget->addTopLevelItem(item); -} - -void DecodeAsDialog::buildDceRpcChangedList(gpointer, gpointer) -{ - // decode_dcerpc_bind_values_t *binding = (decode_dcerpc_bind_values_t *)data; -} - void DecodeAsDialog::addRecord(bool copy_from_current) { - QTreeWidgetItem *cur_ti = NULL; + const QModelIndex ¤t = ui->decodeAsTreeView->currentIndex(); + if (copy_from_current && !current.isValid()) return; +// XXX - This doesn't appear to work as intended to give "edit triggers on demand" + ui->decodeAsTreeView->setEditTriggers(ui->decodeAsTreeView->editTriggers() | QAbstractItemView::CurrentChanged | QAbstractItemView::AnyKeyPressed); + + // should not fail, but you never know. + if (!model_->insertRows(model_->rowCount(), 1)) { + qDebug() << "Failed to add a new record"; + return; + } + const QModelIndex &new_index = model_->index(model_->rowCount() - 1, 0); if (copy_from_current) { - cur_ti = ui->decodeAsTreeWidget->currentItem(); - if (!cur_ti) return; + model_->copyRow(new_index.row(), current.row()); } - QTreeWidgetItem *ti = new QTreeWidgetItem(); - ui->decodeAsTreeWidget->addTopLevelItem(ti); + resizeColumns(); - if (cur_ti) { - ti->setText(table_col_, cur_ti->text(table_col_)); - ti->setText(selector_col_, cur_ti->text(selector_col_)); - ti->setText(default_col_, cur_ti->text(default_col_)); - ti->setText(proto_col_, cur_ti->text(proto_col_)); - ti->setData(proto_col_, Qt::UserRole, cur_ti->data(proto_col_, Qt::UserRole)); - } - - activateLastItem(); - - if (ui->decodeAsTreeWidget->topLevelItemCount() > 0) { - for (int i = 0; i < ui->decodeAsTreeWidget->columnCount(); i++) { - ui->decodeAsTreeWidget->resizeColumnToContents(i); - } - } -} - -void DecodeAsDialog::fillTypeColumn(QTreeWidgetItem *item) -{ - if (!item) return; - const char *table_name = ui_name_to_name_[item->text(table_col_)]; - - ftenum_t selector_type = get_dissector_table_selector_type(table_name); - - if (IS_FT_STRING(selector_type)) { - item->setText(type_col_, tr("String")); - } else { - QString type_desc = tr("Integer, base "); - switch (get_dissector_table_param(table_name)) { - case BASE_OCT: - type_desc.append("8"); - break; - case BASE_DEC: - type_desc.append("10"); - break; - case BASE_HEX: - type_desc.append("16"); - break; - default: - type_desc.append(tr("unknown")); - } - item->setText(type_col_, type_desc); - } + // due to an EditTrigger, this will also start editing. + ui->decodeAsTreeView->setCurrentIndex(new_index); } void DecodeAsDialog::on_newToolButton_clicked() @@ -411,9 +137,12 @@ void DecodeAsDialog::on_newToolButton_clicked() void DecodeAsDialog::on_deleteToolButton_clicked() { - QTreeWidgetItem *item = ui->decodeAsTreeWidget->currentItem(); - if (!item) return; - delete item; + const QModelIndex ¤t = ui->decodeAsTreeView->currentIndex(); + if (model_ && current.isValid()) { + if (!model_->removeRows(current.row(), 1)) { + qDebug() << "Failed to remove row"; + } + } } void DecodeAsDialog::on_copyToolButton_clicked() @@ -421,262 +150,9 @@ void DecodeAsDialog::on_copyToolButton_clicked() addRecord(true); } -void DecodeAsDialog::decodeAddProtocol(const gchar *, const gchar *proto_name, gpointer value, gpointer user_data) -{ - QSet *dissector_info_set = (QSet *)user_data; - if (!dissector_info_set) return; - - dissector_info_t *dissector_info = new dissector_info_t(); - dissector_info->proto_name = proto_name; - dissector_info->dissector_handle = (dissector_handle_t) value; - - dissector_info_set->insert(dissector_info); -} - -void DecodeAsDialog::tableNamesCurrentIndexChanged(const QString &text) -{ - QTreeWidgetItem *item = ui->decodeAsTreeWidget->currentItem(); - if (!item || text.isEmpty() || !selector_combo_box_ || !cur_proto_combo_box_) return; - - QString current_text = item->text(proto_col_); - if (current_text.isEmpty()) current_text = default_proto_; - - item->setText(table_col_, text); - fillTypeColumn(item); - - selector_combo_box_->clear(); - - bool edt_present = cap_file_ && cap_file_->edt; - QVariant variant = table_names_combo_box_->itemData(table_names_combo_box_->currentIndex()); - gint8 curr_layer_num_saved = edt_present ? cap_file_->edt->pi.curr_layer_num : 0; - const gchar *proto_name = NULL; - if (variant.canConvert()) { - table_item_t table_item = variant.value(); - if (edt_present) { - cap_file_->edt->pi.curr_layer_num = table_item.curr_layer_num; - } - proto_name = table_item.proto_name; - } - - QSet dissector_info_set; - GList *cur; - for (cur = decode_as_list; cur; cur = cur->next) { - decode_as_t *entry = (decode_as_t *) cur->data; - if (((proto_name == NULL) || (g_strcmp0(proto_name, entry->name) == 0)) && - (g_strcmp0(ui_name_to_name_[text], entry->table_name) == 0)) { - if (edt_present) { - for (uint ni = 0; ni < entry->num_items; ni++) { - if (entry->values[ni].num_values == 1) { // Skip over multi-value ("both") entries - selector_combo_box_->addItem(entryString(entry->table_name, - entry->values[ni].build_values[0](&cap_file_->edt->pi))); - } - } - selector_combo_box_->setCurrentIndex(entry->default_index_value); - } - entry->populate_list(entry->table_name, decodeAddProtocol, &dissector_info_set); - } - } - if (edt_present) { - cap_file_->edt->pi.curr_layer_num = curr_layer_num_saved; - } - if (selector_combo_box_->count() > 0) { - selector_combo_box_->setCurrentIndex(0); - } else { - ftenum_t selector_type = get_dissector_table_selector_type(ui_name_to_name_[text]); - if (IS_FT_STRING(selector_type)) { - selector_combo_box_->setEditText(default_str_selector_); - } else { - selector_combo_box_->setEditText(default_int_selector_); - } - } - - cur_proto_combo_box_->clear(); - cur_proto_combo_box_->addItem(DECODE_AS_NONE); - cur_proto_combo_box_->insertSeparator(cur_proto_combo_box_->count()); - - QSetIterator i(dissector_info_set); - while (i.hasNext()) { - dissector_info_t *dissector_info = i.next(); - - cur_proto_combo_box_->addItem(dissector_info->proto_name, VariantPointer::asQVariant(dissector_info)); - } - - cur_proto_combo_box_->model()->sort(0); - cur_proto_combo_box_->setCurrentIndex(cur_proto_combo_box_->findText(current_text)); -} - -void DecodeAsDialog::selectorEditTextChanged(const QString &text) -{ - QTreeWidgetItem *item = ui->decodeAsTreeWidget->currentItem(); - if (!item || !table_names_combo_box_ || !selector_combo_box_) return; - - const char *table_name = ui_name_to_name_[table_names_combo_box_->currentText()]; - if (!table_name) return; - - item->setText(selector_col_, text); - ftenum_t selector_type = get_dissector_table_selector_type(table_name); - dissector_handle_t dissector; - - if (IS_FT_STRING(selector_type)) { - dissector = dissector_get_default_string_handle(table_name, text.toUtf8().constData()); - } else { - dissector = dissector_get_default_uint_handle(table_name, text.toInt(NULL, 0)); - } - - if (dissector) { - item->setText(default_col_, dissector_handle_get_short_name(dissector)); - } else { - item->setText(default_col_, DECODE_AS_NONE); - } -} - -void DecodeAsDialog::curProtoCurrentIndexChanged(const QString &text) -{ - QTreeWidgetItem *item = ui->decodeAsTreeWidget->currentItem(); - if (!item) return; - item->setText(proto_col_, text); - item->setData(proto_col_, Qt::UserRole, cur_proto_combo_box_->itemData(cur_proto_combo_box_->findText(text))); -} - -typedef QPair UintPair; -typedef QPair CharPtrPair; - -void DecodeAsDialog::gatherChangedEntries(const gchar *table_name, - ftenum_t selector_type, gpointer key, gpointer, gpointer user_data) -{ - DecodeAsDialog *da_dlg = qobject_cast((DecodeAsDialog *)user_data); - if (!da_dlg) return; - - switch (selector_type) { - case FT_UINT8: - case FT_UINT16: - case FT_UINT24: - case FT_UINT32: - da_dlg->changed_uint_entries_ << UintPair(table_name, GPOINTER_TO_UINT(key)); - break; - case FT_STRING: - case FT_STRINGZ: - case FT_UINT_STRING: - case FT_STRINGZPAD: - da_dlg->changed_string_entries_ << CharPtrPair(table_name, (const char *) key); - break; - default: - break; - } -} - void DecodeAsDialog::applyChanges() { - dissector_table_t sub_dissectors; - module_t *module; - pref_t* pref_value; - dissector_handle_t handle; - // Reset all dissector tables, then apply all rules from GUI. - - // We can't call g_hash_table_removed from g_hash_table_foreach, which - // means we can't call dissector_reset_{string,uint} from - // dissector_all_tables_foreach_changed. Collect changed entries in - // lists and remove them separately. - // - // If dissector_all_tables_remove_changed existed we could call it - // instead. - dissector_all_tables_foreach_changed(gatherChangedEntries, this); - foreach (UintPair uint_entry, changed_uint_entries_) { - /* Set "Decode As preferences" to default values */ - sub_dissectors = find_dissector_table(uint_entry.first); - handle = dissector_get_uint_handle(sub_dissectors, uint_entry.second); - if (handle != NULL) { - module = prefs_find_module(proto_get_protocol_filter_name(dissector_handle_get_protocol_index(handle))); - pref_value = prefs_find_preference(module, uint_entry.first); - if (pref_value != NULL) { - module->prefs_changed = TRUE; - reset_pref(pref_value); - } - } - - dissector_reset_uint(uint_entry.first, uint_entry.second); - } - changed_uint_entries_.clear(); - foreach (CharPtrPair char_ptr_entry, changed_string_entries_) { - dissector_reset_string(char_ptr_entry.first, char_ptr_entry.second); - } - changed_string_entries_.clear(); - - for (int i = 0; i < ui->decodeAsTreeWidget->topLevelItemCount(); i++) { - QTreeWidgetItem *item = ui->decodeAsTreeWidget->topLevelItem(i); - ftenum_t selector_type = get_dissector_table_selector_type(ui_name_to_name_[item->text(table_col_)]); - dissector_info_t *dissector_info; - QVariant variant = item->data(proto_col_, Qt::UserRole); - decode_as_t *decode_as_entry; - - if (variant == QVariant::Invalid) { - continue; - } - - dissector_info = VariantPointer::asPtr(variant); - - for (GList *cur = decode_as_list; cur; cur = cur->next) { - decode_as_entry = (decode_as_t *) cur->data; - - if (!g_strcmp0(decode_as_entry->table_name, ui_name_to_name_[item->text(table_col_)])) { - gpointer selector_value; - QByteArray byteArray; - - switch (selector_type) { - case FT_UINT8: - case FT_UINT16: - case FT_UINT24: - case FT_UINT32: - selector_value = GUINT_TO_POINTER(item->text(selector_col_).toUInt(0, 0)); - break; - case FT_STRING: - case FT_STRINGZ: - case FT_UINT_STRING: - case FT_STRINGZPAD: - byteArray = item->text(selector_col_).toUtf8(); - selector_value = (gpointer) byteArray.constData(); - break; - default: - continue; - } - - if (item->text(proto_col_) == DECODE_AS_NONE || !dissector_info->dissector_handle) { - decode_as_entry->reset_value(decode_as_entry->table_name, selector_value); - sub_dissectors = find_dissector_table(decode_as_entry->table_name); - - /* For now, only numeric dissector tables can use preferences */ - if (IS_FT_UINT(dissector_table_get_type(sub_dissectors))) { - if (dissector_info->dissector_handle != NULL) { - module = prefs_find_module(proto_get_protocol_filter_name(dissector_handle_get_protocol_index(dissector_info->dissector_handle))); - pref_value = prefs_find_preference(module, decode_as_entry->table_name); - if (pref_value != NULL) { - module->prefs_changed = TRUE; - prefs_remove_decode_as_value(pref_value, GPOINTER_TO_UINT(selector_value), TRUE); - } - } - } - break; - } else { - decode_as_entry->change_value(decode_as_entry->table_name, selector_value, &dissector_info->dissector_handle, (char *) item->text(proto_col_).toUtf8().constData()); - sub_dissectors = find_dissector_table(decode_as_entry->table_name); - - /* For now, only numeric dissector tables can use preferences */ - if (IS_FT_UINT(dissector_table_get_type(sub_dissectors))) { - module = prefs_find_module(proto_get_protocol_filter_name(dissector_handle_get_protocol_index(dissector_info->dissector_handle))); - pref_value = prefs_find_preference(module, decode_as_entry->table_name); - if (pref_value != NULL) { - module->prefs_changed = TRUE; - prefs_add_decode_as_value(pref_value, GPOINTER_TO_UINT(selector_value), FALSE); - } - } - break; - } - } - } - - delete(dissector_info); - } - + model_->applyChanges(); wsApp->queueAppSignal(WiresharkApplication::PacketDissectionChanged); } diff --git a/ui/qt/decode_as_dialog.h b/ui/qt/decode_as_dialog.h index 9682f7d8c7..4acf6fe51c 100644 --- a/ui/qt/decode_as_dialog.h +++ b/ui/qt/decode_as_dialog.h @@ -27,6 +27,8 @@ #include #include "cfile.h" +#include +#include #include "geometry_state_dialog.h" #include @@ -47,45 +49,24 @@ public: explicit DecodeAsDialog(QWidget *parent = 0, capture_file *cf = NULL, bool create_new = false); ~DecodeAsDialog(); -public slots: - void setCaptureFile(capture_file *cf); - private: Ui::DecodeAsDialog *ui; - capture_file *cap_file_; - QComboBox *table_names_combo_box_; - QComboBox *selector_combo_box_; - QComboBox *cur_proto_combo_box_; - QMap ui_name_to_name_; - QList > changed_uint_entries_; - QList > changed_string_entries_; + DecodeAsModel* model_; + DecodeAsDelegate* delegate_; - QString entryString(const gchar *table_name, gpointer value); - static void gatherChangedEntries(const gchar *table_name, ftenum_t selector_type, - gpointer key, gpointer value, gpointer user_data); - static void buildChangedList(const gchar *table_name, ftenum_t selector_type, - gpointer key, gpointer value, gpointer user_data); - static void buildDceRpcChangedList(gpointer data, gpointer user_data); - static void decodeAddProtocol(const gchar *table_name, const gchar *proto_name, gpointer value, gpointer user_data); void addRecord(bool copy_from_current = false); - void fillTypeColumn(QTreeWidgetItem *item); + void applyChanges(); + void fillTable(); + void resizeColumns(); private slots: - void fillTable(); - void activateLastItem(); + void on_decodeAsTreeView_currentItemChanged(const QModelIndex ¤t, const QModelIndex &previous); - void on_decodeAsTreeWidget_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous); - void on_decodeAsTreeWidget_itemActivated(QTreeWidgetItem *item, int column = 0); - void on_decodeAsTreeWidget_itemSelectionChanged(); void on_newToolButton_clicked(); void on_deleteToolButton_clicked(); void on_copyToolButton_clicked(); - void tableNamesCurrentIndexChanged(const QString & text); - void selectorEditTextChanged(const QString & text); - void curProtoCurrentIndexChanged(const QString & text); - void applyChanges(); void on_buttonBox_clicked(QAbstractButton *button); }; diff --git a/ui/qt/decode_as_dialog.ui b/ui/qt/decode_as_dialog.ui index b65242104f..f8a78b1e0c 100644 --- a/ui/qt/decode_as_dialog.ui +++ b/ui/qt/decode_as_dialog.ui @@ -12,47 +12,10 @@ - + 0 - - - Field - - - Match using this field - - - - - Value - - - Current "Decode As" behavior - - - - - Type - - - - - Default - - - Default "Decode As" behavior - - - - - Current - - - Change behavior when the protocol field matches this value - - @@ -76,6 +39,9 @@ Remove this dissection behavior. + + false + :/stock/minus-8.png:/stock/minus-8.png @@ -148,6 +114,11 @@ QLabel
widgets/elided_label.h
+ + TabnavTreeView + QTreeView +
widgets/tabnav_tree_view.h
+
diff --git a/ui/qt/models/decode_as_delegate.cpp b/ui/qt/models/decode_as_delegate.cpp new file mode 100644 index 0000000000..bc632f5449 --- /dev/null +++ b/ui/qt/models/decode_as_delegate.cpp @@ -0,0 +1,408 @@ +/* decode_as_delegate.cpp + * Delegates for editing various field types in a Decode As record. + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "decode_as_delegate.h" + +#include "epan/decode_as.h" +#include "epan/epan_dissect.h" + +#include + +#include +#include +#include +#include + +typedef struct _dissector_info_t { + QString proto_name; + dissector_handle_t dissector_handle; +} dissector_info_t; + +Q_DECLARE_METATYPE(dissector_info_t *) + +DecodeAsDelegate::DecodeAsDelegate(QObject *parent, capture_file *cf) + : QStyledItemDelegate(parent), + cap_file_(cf) +{ + cachePacketProtocols(); +} + +DecodeAsItem* DecodeAsDelegate::indexToField(const QModelIndex &index) const +{ + const QVariant v = index.model()->data(index, Qt::UserRole); + return static_cast(v.value()); +} + +void DecodeAsDelegate::cachePacketProtocols() +{ + //cache the list of potential decode as protocols in the current packet + if (cap_file_ && cap_file_->edt) { + + wmem_list_frame_t * protos = wmem_list_head(cap_file_->edt->pi.layers); + guint8 curr_layer_num = 1; + + while (protos != NULL) { + int proto_id = GPOINTER_TO_INT(wmem_list_frame_data(protos)); + const gchar * proto_name = proto_get_protocol_filter_name(proto_id); + for (GList *cur = decode_as_list; cur; cur = cur->next) { + decode_as_t *entry = (decode_as_t *) cur->data; + if (g_strcmp0(proto_name, entry->name) == 0) { + packet_proto_data_t proto_data; + + proto_data.table_ui_name = get_dissector_table_ui_name(entry->table_name); + proto_data.proto_name = proto_name; + proto_data.curr_layer_num = curr_layer_num; + + packet_proto_list_.append(proto_data); + } + } + protos = wmem_list_frame_next(protos); + curr_layer_num++; + } + } +} + +void DecodeAsDelegate::collectDAProtocols(QSet& all_protocols, QList& current_list) const +{ + // If a packet is selected group its tables at the top in order + // from last-dissected to first. + + //gather the initial list + for (GList *cur = decode_as_list; cur; cur = cur->next) { + decode_as_t *entry = (decode_as_t *) cur->data; + const char *table_name = get_dissector_table_ui_name(entry->table_name); + if (table_name) { + all_protocols.insert(get_dissector_table_ui_name(entry->table_name)); + } + } + + //filter out those in selected packet + foreach(packet_proto_data_t proto, packet_proto_list_) + { + current_list.append(proto.table_ui_name); + all_protocols.remove(proto.table_ui_name); + } +} + +//Determine if there are multiple values in the selector field that would +//correspond to using a combo box +bool DecodeAsDelegate::isSelectorCombo(DecodeAsItem* item) const +{ + const gchar *proto_name = NULL; + + foreach(packet_proto_data_t proto, packet_proto_list_) + { + if (g_strcmp0(proto.table_ui_name, item->tableUIName_) == 0) { + proto_name = proto.proto_name; + break; + } + } + + for (GList *cur = decode_as_list; cur; cur = cur->next) { + decode_as_t *entry = (decode_as_t *) cur->data; + if ((g_strcmp0(proto_name, entry->name) == 0) && + (g_strcmp0(item->tableName_, entry->table_name) == 0) && + (cap_file_ && cap_file_->edt) && + (entry->num_items > 1)) { + return true; + } + } + + return false; +} + +void DecodeAsDelegate::decodeAddProtocol(const gchar *, const gchar *proto_name, gpointer value, gpointer user_data) +{ + QMap* proto_list = (QMap*)user_data; + + if (!proto_list) + return; + + dissector_info_t *dissector_info = new dissector_info_t(); + dissector_info->proto_name = proto_name; + dissector_info->dissector_handle = (dissector_handle_t) value; + + proto_list->insert(proto_name, dissector_info); +} + +QWidget* DecodeAsDelegate::createEditor(QWidget *parentWidget, const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + DecodeAsItem* item = indexToField(index); + + switch(index.column()) + { + case DecodeAsModel::colTable: + { + QComboBox *editor = new QComboBox(parentWidget); + QSet da_set; + QList packet_list; + QString table_ui_name; + + collectDAProtocols(da_set, packet_list); + + editor->setSizeAdjustPolicy(QComboBox::AdjustToContents); + + //put the protocols from the packet first in the combo box + foreach (table_ui_name, packet_list) { + editor->addItem(table_ui_name, table_ui_name); + } + if (packet_list.count() > 0) { + editor->insertSeparator(packet_list.count()); + } + + //put the rest of the protocols in the combo box + QList da_list = da_set.toList(); + qSort(da_list.begin(), da_list.end()); + + foreach (table_ui_name, da_list) { + editor->addItem(table_ui_name, table_ui_name); + } + + //Make sure the combo box is at least as wide as the column + QTreeView* parentTree = (QTreeView*)parent(); + int protoColWidth = parentTree->columnWidth(index.column()); + if (protoColWidth > editor->size().width()) + editor->setFixedWidth(protoColWidth); + + return editor; + } + case DecodeAsModel::colSelector: + { + QComboBox *editor = NULL; + const gchar *proto_name = NULL; + bool edt_present = cap_file_ && cap_file_->edt; + gint8 curr_layer_num_saved = edt_present ? cap_file_->edt->pi.curr_layer_num : 0; + + foreach(packet_proto_data_t proto, packet_proto_list_) + { + if (g_strcmp0(proto.table_ui_name, item->tableUIName_) == 0) { + if (edt_present) { + cap_file_->edt->pi.curr_layer_num = proto.curr_layer_num; + } + proto_name = proto.proto_name; + //XXX - break? Or do we always want the last layer of tunnelled protocols? + } + } + + for (GList *cur = decode_as_list; cur; cur = cur->next) { + decode_as_t *entry = (decode_as_t *) cur->data; + if ((g_strcmp0(proto_name, entry->name) == 0) && + (g_strcmp0(item->tableName_, entry->table_name) == 0)) { + if (edt_present) { + if (entry->num_items > 1) + { + //only create a combobox if there is a choice of values, otherwise it looks funny + editor = new QComboBox(parentWidget); + + //Don't limit user to just what's in combo box + editor->setEditable(true); + + editor->setSizeAdjustPolicy(QComboBox::AdjustToContents); + + //add the current value of the column + const QString& current_value = index.model()->data(index, Qt::EditRole).toString(); + if (!current_value.isEmpty()) + editor->addItem(current_value); + + //get the value(s) from the packet + for (uint ni = 0; ni < entry->num_items; ni++) { + if (entry->values[ni].num_values == 1) { // Skip over multi-value ("both") entries + QString entryStr = DecodeAsModel::entryString(entry->table_name, + entry->values[ni].build_values[0](&cap_file_->edt->pi)); + //don't duplicate entries + if (editor->findText(entryStr) < 0) + editor->addItem(entryStr); + } + } + editor->setCurrentIndex(entry->default_index_value); + + //Make sure the combo box is at least as wide as the column + QTreeView* parentTree = (QTreeView*)parent(); + int protoColWidth = parentTree->columnWidth(index.column()); + if (protoColWidth > editor->size().width()) + editor->setFixedWidth(protoColWidth); + + } + } + break; + } + } + + if (edt_present) { + cap_file_->edt->pi.curr_layer_num = curr_layer_num_saved; + } + + //if there isn't a need for a combobox, just let user have a text box for direct edit + if (editor == NULL) + return QStyledItemDelegate::createEditor(parentWidget, option, index); + + return editor; + } + + case DecodeAsModel::colProtocol: + { + QComboBox *editor = new QComboBox(parentWidget); + QMap protocols; + + editor->setSizeAdjustPolicy(QComboBox::AdjustToContents); + + for (GList *cur = decode_as_list; cur; cur = cur->next) { + decode_as_t *entry = (decode_as_t *) cur->data; + if (g_strcmp0(item->tableName_, entry->table_name) == 0) { + entry->populate_list(entry->table_name, decodeAddProtocol, &protocols); + break; + } + } + + editor->addItem(DECODE_AS_NONE); + editor->insertSeparator(editor->count()); + + //QMap already sorts the keys (protocols) alphabetically + QMap::iterator protocol; + for(protocol = protocols.begin(); protocol != protocols.end(); protocol++) + { + editor->addItem(protocol.key(), VariantPointer::asQVariant(protocol.value())); + } + + //Make sure the combo box is at least as wide as the column + QTreeView* parentTree = (QTreeView*)parent(); + int protoColWidth = parentTree->columnWidth(index.column()); + if (protoColWidth > editor->size().width()) + editor->setFixedWidth(protoColWidth); + + return editor; + } + } + + return NULL; +} + +void DecodeAsDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const +{ + DecodeAsItem* item = indexToField(index); + + switch(index.column()) + { + case DecodeAsModel::colTable: + case DecodeAsModel::colProtocol: + { + QComboBox *combobox = static_cast(editor); + const QString &data = index.model()->data(index, Qt::EditRole).toString(); +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) + combobox->setCurrentText(data); +#else + int new_index = combobox->findText(data); + if (new_index >= 0) { + combobox->setCurrentIndex(new_index); + } +#endif + } + break; + case DecodeAsModel::colSelector: + if (isSelectorCombo(item)) { + QComboBox *combobox = static_cast(editor); + const QString &data = index.model()->data(index, Qt::EditRole).toString(); +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) + combobox->setCurrentText(data); +#else + int new_index = combobox->findText(data); + if (new_index >= 0) { + combobox->setCurrentIndex(new_index); + } +#endif + } + else { + QStyledItemDelegate::setEditorData(editor, index); + } + break; + default: + QStyledItemDelegate::setEditorData(editor, index); + break; + } +} + +void DecodeAsDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const +{ + DecodeAsItem* item = indexToField(index); + + switch(index.column()) + { + case DecodeAsModel::colTable: + { + QComboBox *combobox = static_cast(editor); + const QString &data = combobox->currentText(); + model->setData(index, data, Qt::EditRole); + break; + } + case DecodeAsModel::colSelector: + if (isSelectorCombo(item)) { + QComboBox *combobox = static_cast(editor); + const QString &data = combobox->currentText(); + model->setData(index, data, Qt::EditRole); + } else { + QStyledItemDelegate::setModelData(editor, model, index); + } + break; + case DecodeAsModel::colProtocol: + { + QComboBox *combobox = static_cast(editor); + const QString &data = combobox->currentText(); + model->setData(index, data, Qt::EditRole); + + //set the dissector handle + QVariant var = combobox->itemData(combobox->currentIndex()); + dissector_info_t* dissector_info = VariantPointer::asPtr(var); + if (dissector_info != NULL) { + ((DecodeAsModel*)model)->setDissectorHandle(index, dissector_info->dissector_handle); + } else { + ((DecodeAsModel*)model)->setDissectorHandle(index, NULL); + } + break; + } + default: + QStyledItemDelegate::setModelData(editor, model, index); + break; + } +} + +#if 0 +// Qt docs suggest overriding updateEditorGeometry, but the defaults seem sane. +void UatDelegate::updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + QStyledItemDelegate::updateEditorGeometry(editor, option, index); +} +#endif + +/* * Editor modelines + * + * Local Variables: + * c-basic-offset: 4 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * ex: set shiftwidth=4 tabstop=8 expandtab: + * :indentSize=4:tabSize=8:noTabs=true: + */ diff --git a/ui/qt/models/decode_as_delegate.h b/ui/qt/models/decode_as_delegate.h new file mode 100644 index 0000000000..50461f00d9 --- /dev/null +++ b/ui/qt/models/decode_as_delegate.h @@ -0,0 +1,71 @@ +/* decode_as_delegate.h + * Delegates for editing various field types in a Decode As record. + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef DECODE_AS_DELEGATE_H +#define DECODE_AS_DELEGATE_H + +#include +#include + +#include "cfile.h" + +#include +#include +#include +#include + +typedef struct _packet_proto_data_t { + const gchar* proto_name; + const gchar* table_ui_name; + guint8 curr_layer_num; +} packet_proto_data_t; + +class DecodeAsDelegate : public QStyledItemDelegate +{ + Q_OBJECT + +public: + DecodeAsDelegate(QObject *parent = 0, capture_file *cf = NULL); + + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + void setEditorData(QWidget *editor, const QModelIndex &index) const; + void setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const; + +#if 0 + void updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, const QModelIndex &index) const; +#endif + +private: + DecodeAsItem *indexToField(const QModelIndex &index) const; + void collectDAProtocols(QSet& all_protocols, QList& current_list) const; + void cachePacketProtocols(); + bool isSelectorCombo(DecodeAsItem* item) const; + + static void decodeAddProtocol(const gchar *table_name, const gchar *proto_name, gpointer value, gpointer user_data); + + capture_file *cap_file_; + QList packet_proto_list_; +}; +#endif // DECODE_AS_DELEGATE_H diff --git a/ui/qt/models/decode_as_model.cpp b/ui/qt/models/decode_as_model.cpp new file mode 100644 index 0000000000..2c15e419fc --- /dev/null +++ b/ui/qt/models/decode_as_model.cpp @@ -0,0 +1,650 @@ +/* decode_as_model.cpp + * Data model for Decode As records. + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "decode_as_model.h" +#include +#include +#include +#include +#include +#include +#include + +#include + +static const char *DEFAULT_TABLE = "tcp.port"; // Arbitrary +static const char *DEFAULT_UI_TABLE = "TCP port"; // Arbitrary + +DecodeAsItem::DecodeAsItem() + : tableName_(DEFAULT_TABLE), + tableUIName_(DEFAULT_UI_TABLE), + selectorUint_(0), + selectorString_(""), + default_proto_(DECODE_AS_NONE), + current_proto_(DECODE_AS_NONE), + dissector_handle_(NULL) +{ +} + +DecodeAsItem::~DecodeAsItem() +{ +} + + +DecodeAsModel::DecodeAsModel(QObject *parent, capture_file *cf) : + QAbstractTableModel(parent), + cap_file_(cf) +{ +} + +Qt::ItemFlags DecodeAsModel::flags(const QModelIndex &index) const +{ + if (!index.isValid()) + return 0; + + Qt::ItemFlags flags = QAbstractTableModel::flags(index); + switch(index.column()) + { + case DecodeAsModel::colTable: + case DecodeAsModel::colSelector: + case DecodeAsModel::colProtocol: + flags |= Qt::ItemIsEditable; + break; + } + + return flags; +} + +QVariant DecodeAsModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) { + return QVariant(); + } + + DecodeAsItem* item; + + switch (role) + { + case Qt::ToolTipRole: + switch (index.column()) + { + case colTable: + return tr("Match using this field"); + case colSelector: + return tr("Current\"Decode As\" behavior"); + case colType: + return QVariant(); + case colDefault: + return tr("Default \"Decode As\" behavior"); + case colProtocol: + return tr("Change behavior when the protocol field matches this value"); + } + return QVariant(); + case Qt::DisplayRole: + case Qt::EditRole: + item = decode_as_items_[index.row()]; + if (item == NULL) + return QVariant(); + + switch (index.column()) + { + case colTable: + return item->tableUIName_; + case colSelector: + { + ftenum_t selector_type = get_dissector_table_selector_type(item->tableName_); + if (IS_FT_UINT(selector_type)) { + return entryString(item->tableName_, GUINT_TO_POINTER(item->selectorUint_)); + } else if (IS_FT_STRING(selector_type)) { + return entryString(item->tableName_, (gpointer)item->selectorString_.toUtf8().constData()); + } + + return DECODE_AS_NONE; + } + case colType: + { + ftenum_t selector_type = get_dissector_table_selector_type(item->tableName_); + + if (IS_FT_STRING(selector_type)) { + return tr("String"); + } else { + QString type_desc = tr("Integer, base "); + switch (get_dissector_table_param(item->tableName_)) { + case BASE_OCT: + type_desc.append("8"); + break; + case BASE_DEC: + type_desc.append("10"); + break; + case BASE_HEX: + type_desc.append("16"); + break; + default: + type_desc.append(tr("unknown")); + } + return type_desc; + } + } + case colDefault: + return item->default_proto_; + case colProtocol: + return item->current_proto_; + } + return QVariant(); + + case Qt::UserRole: + item = decode_as_items_[index.row()]; + return QVariant::fromValue(static_cast(item)); + } + + return QVariant(); +} + +QVariant DecodeAsModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role != Qt::DisplayRole || orientation != Qt::Horizontal) + return QVariant(); + + switch (section) { + case colTable: + return tr("Field"); + case colSelector: + return tr("Value"); + case colType: + return tr("Type"); + case colDefault: + return tr("Default"); + case colProtocol: + return tr("Current"); + default: + g_assert_not_reached(); + } + + return QVariant(); +} + +int DecodeAsModel::rowCount(const QModelIndex &parent) const +{ + // there are no children + if (parent.isValid()) { + return 0; + } + + return decode_as_items_.count(); +} + +int DecodeAsModel::columnCount(const QModelIndex &parent) const +{ + // there are no children + if (parent.isValid()) { + return 0; + } + + return colDecodeAsMax; +} + +bool DecodeAsModel::setData(const QModelIndex &cur_index, const QVariant &value, int role) +{ + if (!cur_index.isValid()) + return false; + + if (role != Qt::EditRole) + return false; + + if (data(cur_index, role) == value) { + // Data appears unchanged, do not do additional checks. + return true; + } + + DecodeAsItem* item = decode_as_items_[cur_index.row()]; + + switch(cur_index.column()) + { + case DecodeAsModel::colTable: + { + QString valueStr = value.toString(); + //grab the table values from the Decode As list because they are persistent + for (GList *cur = decode_as_list; cur; cur = cur->next) { + decode_as_t *entry = (decode_as_t *) cur->data; + if (valueStr.compare(entry->name) == 0) { + item->tableName_ = entry->table_name; + item->tableUIName_ = get_dissector_table_ui_name(entry->table_name); + + //all other columns affected + emit dataChanged(index(cur_index.row(), colSelector), + index(cur_index.row(), colProtocol)); + + } + } + } + break; + case DecodeAsModel::colProtocol: + item->current_proto_ = value.toString(); + break; + case DecodeAsModel::colSelector: + { + ftenum_t selector_type = get_dissector_table_selector_type(item->tableName_); + + if (IS_FT_STRING(selector_type)) { + item->selectorString_ = value.toString(); + } else if (IS_FT_UINT(selector_type)) { + item->selectorUint_ = value.toUInt(); + } + } + break; + } + + return true; +} + +bool DecodeAsModel::insertRows(int row, int count, const QModelIndex &/*parent*/) +{ + // support insertion of just one item for now. + if (count != 1 || row < 0 || row > rowCount()) + return false; + + beginInsertRows(QModelIndex(), row, row); + + DecodeAsItem* item = new DecodeAsItem(); + + if (cap_file_ && cap_file_->edt) { + //populate the new Decode As item with the last protocol layer + //that can support Decode As + wmem_list_frame_t * protos = wmem_list_head(cap_file_->edt->pi.layers); + gint8 curr_layer_num_saved = cap_file_->edt->pi.curr_layer_num; + guint8 curr_layer_num = 1; + + while (protos != NULL) { + int proto_id = GPOINTER_TO_INT(wmem_list_frame_data(protos)); + const gchar * proto_name = proto_get_protocol_filter_name(proto_id); + for (GList *cur = decode_as_list; cur; cur = cur->next) { + decode_as_t *entry = (decode_as_t *) cur->data; + if (g_strcmp0(proto_name, entry->name) == 0) { + dissector_handle_t dissector = NULL; + ftenum_t selector_type = get_dissector_table_selector_type(entry->table_name); + + //reset the default and current protocols in case previous layer + //populated it and this layer doesn't have a handle for it + item->default_proto_ = item->current_proto_ = DECODE_AS_NONE; + + item->tableName_ = entry->table_name; + item->tableUIName_ = get_dissector_table_ui_name(entry->table_name); + + //see if there is a default dissector that matches value + if (IS_FT_STRING(selector_type)) { + + //pick the first value in the packet as the default + if (entry->num_items > 1) { + cap_file_->edt->pi.curr_layer_num = curr_layer_num; + gpointer selector = entry->values[0].build_values[0](&cap_file_->edt->pi); + if (selector != NULL) { + item->selectorString_ = entryString(item->tableName_, selector); + dissector = dissector_get_default_string_handle(entry->table_name, (const gchar*)selector); + } else { + item->selectorString_ = ""; + } + } + + } else if (IS_FT_UINT(selector_type)) { + + //pick the first value in the packet as the default + if (entry->num_items > 1) { + cap_file_->edt->pi.curr_layer_num = curr_layer_num; + item->selectorUint_ = GPOINTER_TO_UINT(entry->values[0].build_values[0](&cap_file_->edt->pi)); + } + + dissector = dissector_get_default_uint_handle(entry->table_name, item->selectorUint_); + } + + if (dissector != NULL) { + item->default_proto_ = dissector_handle_get_short_name(dissector); + //When adding a new record, "default" should equal "current", so the user can + //explicitly change it + item->current_proto_ = item->default_proto_; + } + } + } + protos = wmem_list_frame_next(protos); + curr_layer_num++; + } + + cap_file_->edt->pi.curr_layer_num = curr_layer_num_saved; + } + + decode_as_items_ << item; + + endInsertRows(); + + return true; +} + +bool DecodeAsModel::removeRows(int row, int count, const QModelIndex &/*parent*/) +{ + if (count != 1 || row < 0 || row >= rowCount()) + return false; + + beginRemoveRows(QModelIndex(), row, row); + decode_as_items_.removeAt(row); + endRemoveRows(); + + return true; +} + +bool DecodeAsModel::copyRow(int dst_row, int src_row) +{ + if (src_row < 0 || src_row >= rowCount() || dst_row < 0 || dst_row >= rowCount()) { + return false; + } + + DecodeAsItem* src = decode_as_items_[src_row]; + DecodeAsItem* dst = decode_as_items_[dst_row]; + + dst->tableName_ = src->tableName_; + dst->tableUIName_ = src->tableUIName_; + dst->selectorUint_ = src->selectorUint_; + dst->selectorString_ = src->selectorString_; + dst->default_proto_ = src->default_proto_; + dst->current_proto_ = src->current_proto_; + dst->dissector_handle_ = src->dissector_handle_; + + QVector roles; + roles << Qt::EditRole << Qt::BackgroundRole; + emit dataChanged(index(dst_row, 0), index(dst_row, columnCount()) +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) + , roles +#endif + ); + + return true; +} + +QString DecodeAsModel::entryString(const gchar *table_name, gpointer value) +{ + QString entry_str; + ftenum_t selector_type = get_dissector_table_selector_type(table_name); + + switch (selector_type) { + + case FT_UINT8: + case FT_UINT16: + case FT_UINT24: + case FT_UINT32: + { + uint num_val = GPOINTER_TO_UINT(value); + switch (get_dissector_table_param(table_name)) { + + case BASE_DEC: + entry_str = QString::number(num_val); + break; + + case BASE_HEX: + int width; + switch (selector_type) { + case FT_UINT8: + width = 2; + break; + case FT_UINT16: + width = 4; + break; + case FT_UINT24: + width = 6; + break; + case FT_UINT32: + width = 8; + break; + + default: + g_assert_not_reached(); + break; + } + entry_str = QString("%1").arg(int_to_qstring(num_val, width, 16)); + break; + + case BASE_OCT: + entry_str = "0" + QString::number(num_val, 8); + break; + } + break; + } + + case FT_STRING: + case FT_STRINGZ: + case FT_UINT_STRING: + case FT_STRINGZPAD: + entry_str = (char *)value; + break; + + case FT_GUID: + //TODO: DCE/RPC dissector table + break; + + default: + g_assert_not_reached(); + break; + } + return entry_str; +} + +void DecodeAsModel::fillTable() +{ + decode_as_items_.clear(); + emit beginResetModel(); + + dissector_all_tables_foreach_changed(buildChangedList, this); + decode_dcerpc_add_show_list(buildDceRpcChangedList, this); + + emit endResetModel(); +} + +void DecodeAsModel::setDissectorHandle(const QModelIndex &index, dissector_handle_t dissector_handle) +{ + DecodeAsItem* item = decode_as_items_[index.row()]; + if (item != NULL) + item->dissector_handle_ = dissector_handle; +} + +void DecodeAsModel::buildChangedList(const gchar *table_name, ftenum_t, gpointer key, gpointer value, gpointer user_data) +{ + DecodeAsModel *model = (DecodeAsModel*)user_data; + if (model == NULL) + return; + + dissector_handle_t default_dh, current_dh; + QString default_proto_name(DECODE_AS_NONE), current_proto_name(DECODE_AS_NONE); + DecodeAsItem* item = new DecodeAsItem(); + ftenum_t selector_type = get_dissector_table_selector_type(table_name); + + item->tableName_ = table_name; + item->tableUIName_ = get_dissector_table_ui_name(table_name); + if (IS_FT_UINT(selector_type)) { + item->selectorUint_ = GPOINTER_TO_UINT(key); + } else if (IS_FT_STRING(selector_type)) { + item->selectorString_ = entryString(table_name, key); + } + + default_dh = dtbl_entry_get_initial_handle((dtbl_entry_t *)value); + if (default_dh) { + default_proto_name = dissector_handle_get_short_name(default_dh); + } + item->default_proto_ = default_proto_name; + + current_dh = dtbl_entry_get_handle((dtbl_entry_t *)value); + if (current_dh) { + current_proto_name = QString(dissector_handle_get_short_name(current_dh)); + } + item->current_proto_ = current_proto_name; + item->dissector_handle_ = current_dh; + + model->decode_as_items_ << item; +} + +void DecodeAsModel::buildDceRpcChangedList(gpointer, gpointer) +{ + // decode_dcerpc_bind_values_t *binding = (decode_dcerpc_bind_values_t *)data; +} + +typedef QPair UintPair; +typedef QPair CharPtrPair; + +void DecodeAsModel::gatherChangedEntries(const gchar *table_name, + ftenum_t selector_type, gpointer key, gpointer, gpointer user_data) +{ + DecodeAsModel *model = qobject_cast((DecodeAsModel*)user_data); + if (model == NULL) + return; + + switch (selector_type) { + case FT_UINT8: + case FT_UINT16: + case FT_UINT24: + case FT_UINT32: + model->changed_uint_entries_ << UintPair(table_name, GPOINTER_TO_UINT(key)); + break; + case FT_STRING: + case FT_STRINGZ: + case FT_UINT_STRING: + case FT_STRINGZPAD: + model->changed_string_entries_ << CharPtrPair(table_name, (const char *) key); + break; + default: + break; + } +} + +void DecodeAsModel::applyChanges() +{ + dissector_table_t sub_dissectors; + module_t *module; + pref_t* pref_value; + dissector_handle_t handle; + // Reset all dissector tables, then apply all rules from model. + + // We can't call g_hash_table_removed from g_hash_table_foreach, which + // means we can't call dissector_reset_{string,uint} from + // dissector_all_tables_foreach_changed. Collect changed entries in + // lists and remove them separately. + // + // If dissector_all_tables_remove_changed existed we could call it + // instead. + dissector_all_tables_foreach_changed(gatherChangedEntries, this); + foreach (UintPair uint_entry, changed_uint_entries_) { + /* Set "Decode As preferences" to default values */ + sub_dissectors = find_dissector_table(uint_entry.first); + handle = dissector_get_uint_handle(sub_dissectors, uint_entry.second); + if (handle != NULL) { + module = prefs_find_module(proto_get_protocol_filter_name(dissector_handle_get_protocol_index(handle))); + pref_value = prefs_find_preference(module, uint_entry.first); + if (pref_value != NULL) { + module->prefs_changed = TRUE; + reset_pref(pref_value); + } + } + + dissector_reset_uint(uint_entry.first, uint_entry.second); + } + changed_uint_entries_.clear(); + foreach (CharPtrPair char_ptr_entry, changed_string_entries_) { + dissector_reset_string(char_ptr_entry.first, char_ptr_entry.second); + } + changed_string_entries_.clear(); + + foreach(DecodeAsItem *item, decode_as_items_) { + decode_as_t *decode_as_entry; + + if (item->current_proto_.isEmpty()) { + continue; + } + + for (GList *cur = decode_as_list; cur; cur = cur->next) { + decode_as_entry = (decode_as_t *) cur->data; + + if (!g_strcmp0(decode_as_entry->table_name, item->tableName_)) { + + ftenum_t selector_type = get_dissector_table_selector_type(item->tableName_); + gpointer selector_value; + QByteArray byteArray; + + switch (selector_type) { + case FT_UINT8: + case FT_UINT16: + case FT_UINT24: + case FT_UINT32: + selector_value = GUINT_TO_POINTER(item->selectorUint_); + break; + case FT_STRING: + case FT_STRINGZ: + case FT_UINT_STRING: + case FT_STRINGZPAD: + byteArray = item->selectorString_.toUtf8(); + selector_value = (gpointer) byteArray.constData(); + break; + default: + continue; + } + + if ((item->current_proto_ == DECODE_AS_NONE) || !item->dissector_handle_) { + decode_as_entry->reset_value(decode_as_entry->table_name, selector_value); + sub_dissectors = find_dissector_table(decode_as_entry->table_name); + + /* For now, only numeric dissector tables can use preferences */ + if (IS_FT_UINT(dissector_table_get_type(sub_dissectors))) { + if (item->dissector_handle_ != NULL) { + module = prefs_find_module(proto_get_protocol_filter_name(dissector_handle_get_protocol_index(item->dissector_handle_))); + pref_value = prefs_find_preference(module, decode_as_entry->table_name); + if (pref_value != NULL) { + module->prefs_changed = TRUE; + prefs_remove_decode_as_value(pref_value, item->selectorUint_, TRUE); + } + } + } + break; + } else { + decode_as_entry->change_value(decode_as_entry->table_name, selector_value, &item->dissector_handle_, (char *) item->current_proto_.toUtf8().constData()); + sub_dissectors = find_dissector_table(decode_as_entry->table_name); + + /* For now, only numeric dissector tables can use preferences */ + if (IS_FT_UINT(dissector_table_get_type(sub_dissectors))) { + module = prefs_find_module(proto_get_protocol_filter_name(dissector_handle_get_protocol_index(item->dissector_handle_))); + pref_value = prefs_find_preference(module, decode_as_entry->table_name); + if (pref_value != NULL) { + module->prefs_changed = TRUE; + prefs_add_decode_as_value(pref_value, item->selectorUint_, FALSE); + } + } + break; + } + } + } + } +} + +/* * Editor modelines + * + * Local Variables: + * c-basic-offset: 4 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * ex: set shiftwidth=4 tabstop=8 expandtab: + * :indentSize=4:tabSize=8:noTabs=true: + */ diff --git a/ui/qt/models/decode_as_model.h b/ui/qt/models/decode_as_model.h new file mode 100644 index 0000000000..d2990a8867 --- /dev/null +++ b/ui/qt/models/decode_as_model.h @@ -0,0 +1,106 @@ +/* decode_as_model.h + * Data model for Decode As records. + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef DECODE_AS_MODEL_H +#define DECODE_AS_MODEL_H + +#include +#include + +#include +#include + +#include "cfile.h" + +#include + +class DecodeAsItem +{ +public: + DecodeAsItem(); + virtual ~DecodeAsItem(); + + const gchar* tableName_; + const gchar* tableUIName_; + + //save our sanity and not have to worry about memory management + //between (lack of) persistent data in GUI and underlying data + uint selectorUint_; + QString selectorString_; + + QString default_proto_; + QString current_proto_; + dissector_handle_t dissector_handle_; +}; + +class DecodeAsModel : public QAbstractTableModel +{ + Q_OBJECT + +public: + DecodeAsModel(QObject *parent, capture_file *cf = NULL); + + enum DecodeAsColumn { + colTable = 0, + colSelector, + colType, + colDefault, // aka "initial" + colProtocol, // aka "current" + colDecodeAsMax //not used + }; + + Qt::ItemFlags flags(const QModelIndex &index) const; + QVariant data(const QModelIndex &index, int role) const; + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); + void fillTable(); + + void setDissectorHandle(const QModelIndex &index, dissector_handle_t dissector_handle); + + bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()); + bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()); + bool copyRow(int dst_row, int src_row); + + static QString entryString(const gchar *table_name, gpointer value); + + void applyChanges(); + +protected: + static void buildChangedList(const gchar *table_name, ftenum_t selector_type, + gpointer key, gpointer value, gpointer user_data); + static void buildDceRpcChangedList(gpointer data, gpointer user_data); + static void gatherChangedEntries(const gchar *table_name, ftenum_t selector_type, + gpointer key, gpointer value, gpointer user_data); + + +private: + capture_file *cap_file_; + QList decode_as_items_; + QList > changed_uint_entries_; + QList > changed_string_entries_; +}; + +#endif // DECODE_AS_MODEL_H