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/import_text_dialog.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/import_text_dialog.cpp')
-rw-r--r-- | ui/qt/import_text_dialog.cpp | 1089 |
1 files changed, 1089 insertions, 0 deletions
diff --git a/ui/qt/import_text_dialog.cpp b/ui/qt/import_text_dialog.cpp new file mode 100644 index 0000000..0916c41 --- /dev/null +++ b/ui/qt/import_text_dialog.cpp @@ -0,0 +1,1089 @@ +/* import_text_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#include "import_text_dialog.h" + +#include "wiretap/wtap.h" +#include "wiretap/pcap-encap.h" + +#include <epan/prefs.h> + +#include "ui/text_import_scanner.h" +#include "ui/util.h" +#include "ui/alert_box.h" +#include "ui/help_url.h" +#include "ui/capture_globals.h" + +#include "file.h" +#include "wsutil/file_util.h" +#include "wsutil/inet_addr.h" +#include "wsutil/time_util.h" +#include "wsutil/tempfile.h" +#include "wsutil/filesystem.h" + +#include <ui_import_text_dialog.h> +#include "main_application.h" +#include <ui/qt/utils/qt_ui_utils.h> +#include "ui/qt/widgets/wireshark_file_dialog.h" + +#include <QDebug> +#include <QJsonDocument> +#include <QJsonObject> +#include <QFile> + +#define HINT_BEGIN "<small><i>" +#define HINT_END "</i></small>" +#define HTML_LT "<" +#define HTML_GT ">" + +static const QString default_regex_hint = ImportTextDialog::tr("Supported fields are data, dir, time, seqno"); +static const QString missing_data_hint = ImportTextDialog::tr("Missing capturing group data (use (?" HTML_LT "data" HTML_GT "(...)) )"); + +#define SETTINGS_FILE "import_hexdump.json" + +ImportTextDialog::ImportTextDialog(QWidget *parent) : + QDialog(parent), + ti_ui_(new Ui::ImportTextDialog), + import_info_(), + file_ok_(false), + timestamp_format_ok_(true), + regex_ok_(false), + re_has_dir_(false), + in_indication_ok_(false), + out_indication_ok_(false), + re_has_time_(false), + ether_type_ok_(true), + proto_ok_(true), + source_addr_ok_(true), + dest_addr_ok_(true), + source_port_ok_(true), + dest_port_ok_(true), + tag_ok_(true), + ppi_ok_(true), + payload_ok_(true), + max_len_ok_(true) +{ + int encap; + int i; + int file_type_subtype; + + ti_ui_->setupUi(this); + setWindowTitle(mainApp->windowTitleString(tr("Import From Hex Dump"))); + memset(&import_info_, 0, sizeof(import_info_)); + + import_button_ = ti_ui_->buttonBox->button(QDialogButtonBox::Open); + import_button_->setText(tr("Import")); + import_button_->setEnabled(false); + + ti_ui_->regexHintLabel->setSmallText(true); + +#ifdef Q_OS_MAC + // The grid layout squishes each line edit otherwise. + int le_height = ti_ui_->textFileLineEdit->sizeHint().height(); + ti_ui_->ethertypeLineEdit->setMinimumHeight(le_height); + ti_ui_->protocolLineEdit->setMinimumHeight(le_height); + ti_ui_->sourceAddressLineEdit->setMinimumHeight(le_height); + ti_ui_->destinationAddressLineEdit->setMinimumHeight(le_height); + ti_ui_->sourcePortLineEdit->setMinimumHeight(le_height); + ti_ui_->destinationPortLineEdit->setMinimumHeight(le_height); + ti_ui_->tagLineEdit->setMinimumHeight(le_height); + ti_ui_->ppiLineEdit->setMinimumHeight(le_height); +#endif + + on_timestampFormatLineEdit_textChanged(ti_ui_->timestampFormatLineEdit->text()); + + encap_buttons = new QButtonGroup(this); + for (i = 0; i < ti_ui_->headerGridLayout->count(); i++) { + QRadioButton *rb = qobject_cast<QRadioButton *>(ti_ui_->headerGridLayout->itemAt(i)->widget()); + + if (rb) encap_buttons->addButton(rb); + } + /* There are two QButtonGroup::buttonToggled signals from Qt 5.2-5.15 with + * different parameters. This breaks connectSlotsByName, which only finds + * the deprecated one that doesn't exist in Qt 6. So we have to connect it + * manually, and avoid naming the slot in the normal way. + */ + connect(encap_buttons, SIGNAL(buttonToggled(QAbstractButton*, bool)), this, SLOT(encap_buttonsToggled(QAbstractButton*, bool))); + + /* fill the IP version combobox */ + ti_ui_->ipVersionComboBox->addItem("IPv4", QVariant(4)); + ti_ui_->ipVersionComboBox->addItem("IPv6", QVariant(6)); + + /* fill the data encoding dropdown in regex tab*/ + struct { + const char* name; + enum data_encoding id; + } encodings[] = { + {"Plain hex", ENCODING_PLAIN_HEX}, + {"Plain oct", ENCODING_PLAIN_OCT}, + {"Plain bin", ENCODING_PLAIN_BIN}, + {"Base 64", ENCODING_BASE64} + }; + for (i = 0; i < (int) (sizeof(encodings) / sizeof(encodings[0])); ++i) { + ti_ui_->dataEncodingComboBox->addItem(encodings[i].name, QVariant(encodings[i].id)); + } + + /* + * Scan all Wiretap encapsulation types. + * + * XXX - this "knows" that WTAP_ENCAP_ETHERNET is the first encapsulation + * type, skipping the special non-types WTAP_ENCAP_PER_PACKET and + * WTAP_ENCAP_UNKNOWN. We need a better way to express the notion + * of "for (all encapsulation types)". + */ + import_info_.encapsulation = WTAP_ENCAP_ETHERNET; + file_type_subtype = wtap_pcapng_file_type_subtype(); + for (encap = import_info_.encapsulation; encap < wtap_get_num_encap_types(); encap++) + { + /* Check if we can write to a pcapng file + * + * Exclude wtap encapsulations that require a pseudo header, + * because we won't setup one from the text we import and + * wiretap doesn't allow us to write 'raw' frames + */ + if (wtap_dump_can_write_encap(file_type_subtype, encap) && + !wtap_encap_requires_phdr(encap)) { + const char *name; + /* If it has got a name */ + if ((name = wtap_encap_description(encap))) + { + ti_ui_->encapComboBox->addItem(name, QVariant(encap)); + } + } + } + ti_ui_->encapComboBox->model()->sort(0); + + /* fill the dissector combo box */ + GList* dissector_names = get_dissector_names(); + for (GList* l = dissector_names; l != NULL; l = l->next) { + const char* name = (const char*) l->data; + ti_ui_->dissectorComboBox->addItem(name, QVariant(name)); + } + ti_ui_->dissectorComboBox->model()->sort(0); + g_list_free(dissector_names); + + ti_ui_->regexHintLabel->setText(default_regex_hint); + + applyDialogSettings(); + updateImportButtonState(); +} + +ImportTextDialog::~ImportTextDialog() +{ + storeDialogSettings(); + + delete ti_ui_; +} + +void ImportTextDialog::loadSettingsFile() +{ + QFileInfo fileInfo(gchar_free_to_qstring(get_profile_dir(get_profile_name(), FALSE)), QString(SETTINGS_FILE)); + QFile loadFile(fileInfo.filePath()); + + if (!fileInfo.exists() || !fileInfo.isFile()) { + return; + } + + if (loadFile.open(QIODevice::ReadOnly)) { + QByteArray loadData = loadFile.readAll(); + QJsonDocument document = QJsonDocument::fromJson(loadData); + + settings = document.object().toVariantMap(); + } +} + +void ImportTextDialog::saveSettingsFile() +{ + QFileInfo fileInfo(gchar_free_to_qstring(get_profile_dir(get_profile_name(), FALSE)), QString(SETTINGS_FILE)); + QFile saveFile(fileInfo.filePath()); + + if (fileInfo.exists() && !fileInfo.isFile()) { + return; + } + + if (saveFile.open(QIODevice::WriteOnly)) { + QJsonDocument document = QJsonDocument::fromVariant(settings); + QByteArray saveData = document.toJson(); + + saveFile.write(saveData); + } +} + +void ImportTextDialog::applyDialogSettings() +{ + loadSettingsFile(); + + // Hex Dump + QString offsetType = settings["hexdump.offsets"].toString(); + if (offsetType == "hex") { + ti_ui_->hexOffsetButton->setChecked(true); + } else if (offsetType == "dec") { + ti_ui_->decimalOffsetButton->setChecked(true); + } else if (offsetType == "oct") { + ti_ui_->octalOffsetButton->setChecked(true); + } else if (offsetType == "none") { + ti_ui_->noOffsetButton->setChecked(true); + } + ti_ui_->directionIndicationCheckBox->setChecked(settings["hexdump.hasDirection"].toBool()); + ti_ui_->asciiIdentificationCheckBox->setChecked(settings["hexdump.identifyAscii"].toBool()); + + // Regular Expression + ti_ui_->regexTextEdit->setText(settings["regex.format"].toString()); + QString encoding = settings["regex.encoding"].toString(); + if (encoding == "plainHex") { + ti_ui_->dataEncodingComboBox->setCurrentIndex(0); + } else if (encoding == "plainOct") { + ti_ui_->dataEncodingComboBox->setCurrentIndex(1); + } else if (encoding == "plainBin") { + ti_ui_->dataEncodingComboBox->setCurrentIndex(2); + } else if (encoding == "base64") { + ti_ui_->dataEncodingComboBox->setCurrentIndex(3); + } + ti_ui_->dirInIndicationLineEdit->setText(settings["regex.inIndication"].toString()); + ti_ui_->dirOutIndicationLineEdit->setText(settings["regex.outIndication"].toString()); + + // Import info + ti_ui_->timestampFormatLineEdit->setText(settings["timestampFormat"].toString()); + + const char *name = wtap_encap_description(settings["encapsulation"].toInt()); + ti_ui_->encapComboBox->setCurrentText(name); + + QString dummyHeader = settings["dummyHeader"].toString(); + if (dummyHeader == "ethernet") { + ti_ui_->ethernetButton->setChecked(true); + } else if (dummyHeader == "ipv4") { + ti_ui_->ipv4Button->setChecked(true); + } else if (dummyHeader == "udp") { + ti_ui_->udpButton->setChecked(true); + } else if (dummyHeader == "tcp") { + ti_ui_->tcpButton->setChecked(true); + } else if (dummyHeader == "sctp") { + ti_ui_->sctpButton->setChecked(true); + } else if (dummyHeader == "sctpData") { + ti_ui_->sctpDataButton->setChecked(true); + } else if (dummyHeader == "exportPDU") { + ti_ui_->exportPduButton->setChecked(true); + } else if (dummyHeader == "none") { + ti_ui_->noDummyButton->setChecked(true); + } + + if (settings["ipVersion"].toUInt() == 6) { + ti_ui_->ipVersionComboBox->setCurrentIndex(1); + } else { + ti_ui_->ipVersionComboBox->setCurrentIndex(0); + } + ti_ui_->ethertypeLineEdit->setText(settings["ethertype"].toString()); + ti_ui_->protocolLineEdit->setText(settings["ipProtocol"].toString()); + ti_ui_->sourceAddressLineEdit->setText(settings["sourceAddress"].toString()); + ti_ui_->destinationAddressLineEdit->setText(settings["destinationAddress"].toString()); + ti_ui_->sourcePortLineEdit->setText(settings["sourcePort"].toString()); + ti_ui_->destinationPortLineEdit->setText(settings["destinationPort"].toString()); + ti_ui_->tagLineEdit->setText(settings["sctpTag"].toString()); + ti_ui_->ppiLineEdit->setText(settings["sctpPPI"].toString()); + + if (settings.contains("pduPayload")) { + ti_ui_->dissectorComboBox->setCurrentText(settings["pduPayload"].toString()); + } else { + // Default to the data dissector when not previously set + ti_ui_->dissectorComboBox->setCurrentText("data"); + } + + ti_ui_->interfaceLineEdit->setText(settings["interfaceName"].toString()); + ti_ui_->maxLengthLineEdit->setText(settings["maxFrameLength"].toString()); + + // Select mode tab last to enableFieldWidgets() + QString mode(settings["mode"].toString()); + int modeIndex = (mode == "regex") ? 1 : 0; + ti_ui_->modeTabWidget->setCurrentIndex(modeIndex); + on_modeTabWidget_currentChanged(modeIndex); +} + +void ImportTextDialog::storeDialogSettings() +{ + int modeIndex = ti_ui_->modeTabWidget->currentIndex(); + if (modeIndex == 0) { + settings["mode"] = "hexdump"; + } else { + settings["mode"] = "regex"; + } + + // Hex Dump + if (ti_ui_->hexOffsetButton->isChecked()) { + settings["hexdump.offsets"] = "hex"; + } else if (ti_ui_->decimalOffsetButton->isChecked()) { + settings["hexdump.offsets"] = "dec"; + } else if (ti_ui_->octalOffsetButton->isChecked()) { + settings["hexdump.offsets"] = "oct"; + } else { + settings["hexdump.offsets"] = "none"; + } + settings["hexdump.hasDirection"] = ti_ui_->directionIndicationCheckBox->isChecked(); + settings["hexdump.identifyAscii"] = ti_ui_->asciiIdentificationCheckBox->isChecked(); + + // Regular Expression + settings["regex.format"] = ti_ui_->regexTextEdit->toPlainText(); + QVariant encodingVal = ti_ui_->dataEncodingComboBox->itemData(ti_ui_->dataEncodingComboBox->currentIndex()); + if (encodingVal.isValid()) { + enum data_encoding encoding = (enum data_encoding) encodingVal.toUInt(); + switch (encoding) { + case ENCODING_PLAIN_HEX: + settings["regex.encoding"] = "plainHex"; + break; + case ENCODING_PLAIN_OCT: + settings["regex.encoding"] = "plainOct"; + break; + case ENCODING_PLAIN_BIN: + settings["regex.encoding"] = "plainBin"; + break; + case ENCODING_BASE64: + settings["regex.encoding"] = "base64"; + break; + } + } else { + settings["regex.encoding"] = "plainHex"; + } + settings["regex.inIndication"] = ti_ui_->dirInIndicationLineEdit->text(); + settings["regex.outIndication"] = ti_ui_->dirOutIndicationLineEdit->text(); + + // Import info + settings["timestampFormat"] = ti_ui_->timestampFormatLineEdit->text(); + + QVariant encapVal = ti_ui_->encapComboBox->itemData(ti_ui_->encapComboBox->currentIndex()); + if (encapVal.isValid()) { + settings["encapsulation"] = encapVal.toUInt(); + } else { + settings["encapsulation"] = WTAP_ENCAP_ETHERNET; + } + + if (ti_ui_->ethernetButton->isChecked()) { + settings["dummyHeader"] = "ethernet"; + } else if (ti_ui_->ipv4Button->isChecked()) { + settings["dummyHeader"] = "ipv4"; + } else if (ti_ui_->udpButton->isChecked()) { + settings["dummyHeader"] = "udp"; + } else if (ti_ui_->tcpButton->isChecked()) { + settings["dummyHeader"] = "tcp"; + } else if (ti_ui_->sctpButton->isChecked()) { + settings["dummyHeader"] = "sctp"; + } else if (ti_ui_->sctpDataButton->isChecked()) { + settings["dummyHeader"] = "sctpData"; + } else if (ti_ui_->exportPduButton->isChecked()) { + settings["dummyHeader"] = "exportPDU"; + } else { + settings["dummyHeader"] = "none"; + } + + settings["ipVersion"] = ti_ui_->ipVersionComboBox->currentData().toUInt(); + settings["ethertype"] = ti_ui_->ethertypeLineEdit->text(); + settings["ipProtocol"] = ti_ui_->protocolLineEdit->text(); + settings["sourceAddress"] = ti_ui_->sourceAddressLineEdit->text(); + settings["destinationAddress"] = ti_ui_->destinationAddressLineEdit->text(); + settings["sourcePort"] = ti_ui_->sourcePortLineEdit->text(); + settings["destinationPort"] = ti_ui_->destinationPortLineEdit->text(); + settings["sctpTag"] = ti_ui_->tagLineEdit->text(); + settings["sctpPPI"] = ti_ui_->ppiLineEdit->text(); + settings["pduPayload"] = ti_ui_->dissectorComboBox->currentData().toString(); + + settings["interfaceName"] = ti_ui_->interfaceLineEdit->text(); + settings["maxFrameLength"] = ti_ui_->maxLengthLineEdit->text(); + + saveSettingsFile(); +} + +QString &ImportTextDialog::capfileName() { + return capfile_name_; +} + +int ImportTextDialog::exec() { + QVariant encap_val; + char* tmp; + GError* gerror = NULL; + int err; + gchar *err_info; + wtap_dump_params params; + int file_type_subtype; + QString interface_name; + + QDialog::exec(); + + if (result() != QDialog::Accepted) { + return result(); + } + + /* from here on the cleanup labels are used to free allocated resources in + * reverse order. + * naming is cleanup_<step_where_something_failed> + * Don't Declare new variables from here on + */ + + import_info_.import_text_filename = qstring_strdup(ti_ui_->textFileLineEdit->text()); + import_info_.timestamp_format = qstring_strdup(ti_ui_->timestampFormatLineEdit->text()); + if (strlen(import_info_.timestamp_format) == 0) { + g_free((gpointer) import_info_.timestamp_format); + import_info_.timestamp_format = NULL; + } + + switch (import_info_.mode) { + default: /* should never happen */ + setResult(QDialog::Rejected); + return QDialog::Rejected; + case TEXT_IMPORT_HEXDUMP: + import_info_.hexdump.import_text_FILE = ws_fopen(import_info_.import_text_filename, "rb"); + if (!import_info_.hexdump.import_text_FILE) { + open_failure_alert_box(import_info_.import_text_filename, errno, FALSE); + setResult(QDialog::Rejected); + goto cleanup_mode; + } + + import_info_.hexdump.offset_type = + ti_ui_->hexOffsetButton->isChecked() ? OFFSET_HEX : + ti_ui_->decimalOffsetButton->isChecked() ? OFFSET_DEC : + ti_ui_->octalOffsetButton->isChecked() ? OFFSET_OCT : + OFFSET_NONE; + break; + case TEXT_IMPORT_REGEX: + import_info_.regex.import_text_GMappedFile = g_mapped_file_new(import_info_.import_text_filename, true, &gerror); + if (gerror) { + open_failure_alert_box(import_info_.import_text_filename, gerror->code, FALSE); + g_error_free(gerror); + setResult(QDialog::Rejected); + goto cleanup_mode; + } + tmp = qstring_strdup(ti_ui_->regexTextEdit->toPlainText()); + import_info_.regex.format = g_regex_new(tmp, (GRegexCompileFlags) (G_REGEX_DUPNAMES | G_REGEX_OPTIMIZE | G_REGEX_MULTILINE), G_REGEX_MATCH_NOTEMPTY, &gerror); + g_free(tmp); + if (re_has_dir_) { + import_info_.regex.in_indication = qstring_strdup(ti_ui_->dirInIndicationLineEdit->text()); + import_info_.regex.out_indication = qstring_strdup(ti_ui_->dirOutIndicationLineEdit->text()); + } else { + import_info_.regex.in_indication = NULL; + import_info_.regex.out_indication = NULL; + } + break; + } + + encap_val = ti_ui_->encapComboBox->itemData(ti_ui_->encapComboBox->currentIndex()); + import_info_.dummy_header_type = HEADER_NONE; + if (encap_val.isValid() && (encap_buttons->checkedButton()->isEnabled()) + && !ti_ui_->noDummyButton->isChecked()) { + // Inputs were validated in the on_xxx_textChanged slots. + if (ti_ui_->ethernetButton->isChecked()) { + import_info_.dummy_header_type = HEADER_ETH; + } else if (ti_ui_->ipv4Button->isChecked()) { + import_info_.dummy_header_type = HEADER_IPV4; + } else if (ti_ui_->udpButton->isChecked()) { + import_info_.dummy_header_type = HEADER_UDP; + } else if (ti_ui_->tcpButton->isChecked()) { + import_info_.dummy_header_type = HEADER_TCP; + } else if (ti_ui_->sctpButton->isChecked()) { + import_info_.dummy_header_type = HEADER_SCTP; + } else if (ti_ui_->sctpDataButton->isChecked()) { + import_info_.dummy_header_type = HEADER_SCTP_DATA; + } else if (ti_ui_->exportPduButton->isChecked()) { + import_info_.dummy_header_type = HEADER_EXPORT_PDU; + } + } + if (import_info_.max_frame_length == 0) { + import_info_.max_frame_length = WTAP_MAX_PACKET_SIZE_STANDARD; + } + + import_info_.payload = qstring_strdup(ti_ui_->dissectorComboBox->currentData().toString()); + + capfile_name_.clear(); + wtap_dump_params_init(¶ms, NULL); + params.encap = import_info_.encapsulation; + params.snaplen = import_info_.max_frame_length; + params.tsprec = WTAP_TSPREC_NSEC; /* XXX - support other precisions? */ + /* Write a pcapng temporary file */ + file_type_subtype = wtap_pcapng_file_type_subtype(); + if (ti_ui_->interfaceLineEdit->text().length()) { + interface_name = ti_ui_->interfaceLineEdit->text(); + } else { + interface_name = ti_ui_->interfaceLineEdit->placeholderText(); + } + text_import_pre_open(¶ms, file_type_subtype, import_info_.import_text_filename, interface_name.toUtf8().constData()); + /* Use a random name for the temporary import buffer */ + import_info_.wdh = wtap_dump_open_tempfile(global_capture_opts.temp_dir, &tmp, "import", file_type_subtype, WTAP_UNCOMPRESSED, ¶ms, &err, &err_info); + capfile_name_.append(tmp ? tmp : "temporary file"); + import_info_.output_filename = tmp; + + if (import_info_.wdh == NULL) { + cfile_dump_open_failure_alert_box(capfile_name_.toUtf8().constData(), err, err_info, file_type_subtype); + setResult(QDialog::Rejected); + goto cleanup_wtap; + } + + err = text_import(&import_info_); + + if (err != 0) { + failure_alert_box("Import failed"); + setResult(QDialog::Rejected); + goto cleanup; + } + + cleanup: /* free in reverse order of allocation */ + if (!wtap_dump_close(import_info_.wdh, NULL, &err, &err_info)) + { + cfile_close_failure_alert_box(capfile_name_.toUtf8().constData(), err, err_info); + } + cleanup_wtap: + /* g_free checks for null */ + wtap_free_idb_info(params.idb_inf); + wtap_dump_params_cleanup(¶ms); + g_free(tmp); + g_free((gpointer) import_info_.payload); + switch (import_info_.mode) { + case TEXT_IMPORT_HEXDUMP: + fclose(import_info_.hexdump.import_text_FILE); + break; + case TEXT_IMPORT_REGEX: + g_mapped_file_unref(import_info_.regex.import_text_GMappedFile); + g_regex_unref((GRegex*) import_info_.regex.format); + g_free((gpointer) import_info_.regex.in_indication); + g_free((gpointer) import_info_.regex.out_indication); + break; + } + cleanup_mode: + g_free((gpointer) import_info_.import_text_filename); + g_free((gpointer) import_info_.timestamp_format); + return result(); +} + +/******************************************************************************* + * General Input + */ + +void ImportTextDialog::updateImportButtonState() +{ + /* XXX: This requires even buttons that aren't being used to have valid + * entries (addresses, ports, etc.) Fixing that can mean changing the + * encapsulation type in order to enable the line edits, which is a little + * awkward for the user. + */ + if (file_ok_ && timestamp_format_ok_ && ether_type_ok_ && + proto_ok_ && source_addr_ok_ && dest_addr_ok_ && + source_port_ok_ && dest_port_ok_ && + tag_ok_ && ppi_ok_ && payload_ok_ && max_len_ok_ && + ( + ( + import_info_.mode == TEXT_IMPORT_REGEX && regex_ok_ && + (!re_has_dir_ || (in_indication_ok_ && out_indication_ok_)) + ) || ( + import_info_.mode == TEXT_IMPORT_HEXDUMP + ) + ) + ) { + import_button_->setEnabled(true); + } else { + import_button_->setEnabled(false); + } +} + +void ImportTextDialog::on_textFileLineEdit_textChanged(const QString &file_name) +{ + QFile text_file(file_name); + + if (file_name.length() > 0 && text_file.open(QIODevice::ReadOnly)) { + file_ok_ = true; + text_file.close(); + } else { + file_ok_ = false; + } + updateImportButtonState(); +} + +void ImportTextDialog::on_textFileBrowseButton_clicked() +{ + QString open_dir; + if (ti_ui_->textFileLineEdit->text().length() > 0) { + open_dir = ti_ui_->textFileLineEdit->text(); + } else { + switch (prefs.gui_fileopen_style) { + + case FO_STYLE_LAST_OPENED: + /* The user has specified that we should start out in the last directory + we looked in. If we've already opened a file, use its containing + directory, if we could determine it, as the directory, otherwise + use the "last opened" directory saved in the preferences file if + there was one. */ + /* This is now the default behaviour in file_selection_new() */ + open_dir = get_open_dialog_initial_dir(); + break; + + case FO_STYLE_SPECIFIED: + /* The user has specified that we should always start out in a + specified directory; if they've specified that directory, + start out by showing the files in that dir. */ + if (prefs.gui_fileopen_dir[0] != '\0') + open_dir = prefs.gui_fileopen_dir; + break; + } + } + + QString file_name = WiresharkFileDialog::getOpenFileName(this, mainApp->windowTitleString(tr("Import Text File")), open_dir); + ti_ui_->textFileLineEdit->setText(file_name); +} + +bool ImportTextDialog::checkDateTimeFormat(const QString &time_format) +{ + /* nonstandard is f for fractions of seconds */ + const QString valid_code = "aAbBcdDFfHIjmMpsSTUwWxXyYzZ%"; + int idx = 0; + int ret = false; + + /* XXX: Temporary(?) hack to allow ISO format time, a checkbox is + * probably better */ + if (time_format == "ISO") { + ret = true; + } else while ((idx = static_cast<int>(time_format.indexOf("%", idx))) != -1) { + idx++; + if ((idx == time_format.size()) || !valid_code.contains(time_format[idx])) { + return false; + } + idx++; + ret = true; + } + return ret; +} + +void ImportTextDialog::on_timestampFormatLineEdit_textChanged(const QString &time_format) +{ + if (time_format.length() > 0) { + if (checkDateTimeFormat(time_format)) { + struct timespec timenow; + struct tm *cur_tm; + struct tm fallback; + char time_str[100]; + QString timefmt = QString(time_format); + + ws_clock_get_realtime(&timenow); + + /* On windows strftime/wcsftime does not support %s yet, this works on all OSs */ + timefmt.replace(QString("%s"), QString::number(timenow.tv_sec)); + /* subsecond example as usec */ + timefmt.replace(QString("%f"), QString("%1").arg(timenow.tv_nsec, 6, 10, QChar('0'))); + + cur_tm = localtime(&timenow.tv_sec); + if (cur_tm == NULL) { + memset(&fallback, 0, sizeof(fallback)); + cur_tm = &fallback; + } + strftime(time_str, sizeof time_str, timefmt.toUtf8(), cur_tm); + ti_ui_->timestampExampleLabel->setText(QString(tr(HINT_BEGIN "Example: %1" HINT_END)).arg(QString(time_str).toHtmlEscaped())); + timestamp_format_ok_ = true; + } + else { + ti_ui_->timestampExampleLabel->setText(tr(HINT_BEGIN "(Wrong date format)" HINT_END)); + timestamp_format_ok_ = false; + } + } else { + ti_ui_->timestampExampleLabel->setText(tr(HINT_BEGIN "(No format will be applied)" HINT_END)); + timestamp_format_ok_ = true; + } + updateImportButtonState(); +} + +void ImportTextDialog::on_modeTabWidget_currentChanged(int index) { + switch (index) { + default: + ti_ui_->modeTabWidget->setCurrentIndex(0); + /* fall through */ + case 0: /* these numbers depend on the UI */ + import_info_.mode = TEXT_IMPORT_HEXDUMP; + memset(&import_info_.hexdump, 0, sizeof(import_info_.hexdump)); + on_directionIndicationCheckBox_toggled(ti_ui_->directionIndicationCheckBox->isChecked()); + on_asciiIdentificationCheckBox_toggled(ti_ui_->asciiIdentificationCheckBox->isChecked()); + enableFieldWidgets(false, true); + break; + case 1: + import_info_.mode = TEXT_IMPORT_REGEX; + memset(&import_info_.regex, 0, sizeof(import_info_.regex)); + on_dataEncodingComboBox_currentIndexChanged(ti_ui_->dataEncodingComboBox->currentIndex()); + enableFieldWidgets(re_has_dir_, re_has_time_); + break; + } + on_textFileLineEdit_textChanged(ti_ui_->textFileLineEdit->text()); +} + +/******************************************************************************* + * Hex Dump Tab + */ + +void ImportTextDialog::on_noOffsetButton_toggled(bool checked) +{ + if (checked) { + ti_ui_->noOffsetLabel->setText("(only one packet will be created)"); + } else { + ti_ui_->noOffsetLabel->setText(""); + } +} + +void ImportTextDialog::on_directionIndicationCheckBox_toggled(bool checked) +{ + import_info_.hexdump.has_direction = checked; +} + +void ImportTextDialog::on_asciiIdentificationCheckBox_toggled(bool checked) +{ + import_info_.hexdump.identify_ascii = checked; +} + +/******************************************************************************* + * Regex Tab + */ + +void ImportTextDialog::on_regexTextEdit_textChanged() +{ + gchar* regex_gchar_p = qstring_strdup(ti_ui_->regexTextEdit->toPlainText());; + GError* gerror = NULL; + /* TODO: Use GLib's c++ interface or enable C++ int to enum casting + * because the flags are declared as enum, so we can't pass 0 like + * the specification recommends. These options don't hurt. + */ + GRegex* regex = g_regex_new(regex_gchar_p, G_REGEX_DUPNAMES, G_REGEX_MATCH_NOTEMPTY, &gerror); + if (gerror) { + regex_ok_ = false; + ti_ui_->regexHintLabel->setText(QString(gerror->message).toHtmlEscaped()); + g_error_free(gerror); + } else { + regex_ok_ = 0 <= g_regex_get_string_number(regex, "data"); + if (regex_ok_) + ti_ui_->regexHintLabel->setText(default_regex_hint); + else + ti_ui_->regexHintLabel->setText(missing_data_hint); + re_has_dir_ = 0 <= g_regex_get_string_number(regex, "dir"); + re_has_time_ = 0 <= g_regex_get_string_number(regex, "time"); + //re_has_seqno = 0 <= g_regex_get_string_number(regex, "seqno"); + g_regex_unref(regex); + } + g_free(regex_gchar_p); + enableFieldWidgets(re_has_dir_, re_has_time_); + updateImportButtonState(); +} + +void ImportTextDialog::enableFieldWidgets(bool enable_direction_input, bool enable_time_input) { + ti_ui_->dirIndicationLabel->setEnabled(enable_direction_input); + ti_ui_->dirInIndicationLineEdit->setEnabled(enable_direction_input); + ti_ui_->dirOutIndicationLineEdit->setEnabled(enable_direction_input); + ti_ui_->timestampLabel->setEnabled(enable_time_input); + ti_ui_->timestampFormatLineEdit->setEnabled(enable_time_input); + ti_ui_->timestampExampleLabel->setEnabled(enable_time_input); +} + +void ImportTextDialog::on_dataEncodingComboBox_currentIndexChanged(int index) +{ + QVariant val = ti_ui_->dataEncodingComboBox->itemData(index); +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + if (val.canConvert<int>()) +#else + if (val != QVariant::Invalid) +#endif + { + // data_encoding_ok = true; + import_info_.regex.encoding = (enum data_encoding) val.toUInt(); + switch (import_info_.regex.encoding) { + case ENCODING_PLAIN_HEX: + ti_ui_->encodingRegexExample->setText(HINT_BEGIN "(?" HTML_LT "data" HTML_GT "[0-9a-fA-F:\\s]+)" HINT_END); + break; + case ENCODING_PLAIN_BIN: + ti_ui_->encodingRegexExample->setText(HINT_BEGIN "(?" HTML_LT "data" HTML_GT "[0-1\\s]+)" HINT_END); + break; + case ENCODING_PLAIN_OCT: + ti_ui_->encodingRegexExample->setText(HINT_BEGIN "(?" HTML_LT "data" HTML_GT "[0-8:\\s]+)" HINT_END); + break; + case ENCODING_BASE64: + ti_ui_->encodingRegexExample->setText(HINT_BEGIN "(?" HTML_LT "data" HTML_GT "[0-9a-zA-Z+\\/\\s]+=*)" HINT_END); + break; + default: + ti_ui_->encodingRegexExample->setText(HINT_BEGIN HTML_LT "no example" HTML_GT HINT_END); + break; + } + /* for some reason this breaks when changing the text */ + ti_ui_->encodingRegexExample->setTextInteractionFlags(Qt::TextSelectableByMouse); + } + updateImportButtonState(); +} + +void ImportTextDialog::on_dirInIndicationLineEdit_textChanged(const QString &in_indication) +{ + in_indication_ok_ = in_indication.length() > 0; + updateImportButtonState(); +} + +void ImportTextDialog::on_dirOutIndicationLineEdit_textChanged(const QString &out_indication) +{ + out_indication_ok_ = out_indication.length() > 0; + updateImportButtonState(); +} + +/******************************************************************************* + * Encapsulation input + */ + +void ImportTextDialog::enableHeaderWidgets(uint encapsulation) { + bool ethertype = false; + bool ipv4_proto = false; + bool ip_address = false; + bool port = false; + bool sctp_tag = false; + bool sctp_ppi = false; + bool export_pdu = false; + bool enable_ethernet_buttons = (encapsulation == WTAP_ENCAP_ETHERNET); + bool enable_ip_buttons = (encapsulation == WTAP_ENCAP_RAW_IP || encapsulation == WTAP_ENCAP_RAW_IP4 || encapsulation == WTAP_ENCAP_RAW_IP6); + bool enable_export_pdu_buttons = (encapsulation == WTAP_ENCAP_WIRESHARK_UPPER_PDU); + + if (enable_ethernet_buttons) { + if (ti_ui_->ethernetButton->isChecked()) { + ethertype = true; + on_ethertypeLineEdit_textChanged(ti_ui_->ethertypeLineEdit->text()); + } + enable_ip_buttons = true; + } + + if (enable_ip_buttons) { + if (ti_ui_->ipv4Button->isChecked()) { + ipv4_proto = true; + ip_address = true; + on_protocolLineEdit_textChanged(ti_ui_->protocolLineEdit->text()); + } else if (ti_ui_->udpButton->isChecked() || ti_ui_->tcpButton->isChecked()) { + ip_address = true; + port = true; + on_sourcePortLineEdit_textChanged(ti_ui_->sourcePortLineEdit->text()); + on_destinationPortLineEdit_textChanged(ti_ui_->destinationPortLineEdit->text()); + } else if (ti_ui_->sctpButton->isChecked()) { + ip_address = true; + port = true; + sctp_tag = true; + on_sourcePortLineEdit_textChanged(ti_ui_->sourcePortLineEdit->text()); + on_destinationPortLineEdit_textChanged(ti_ui_->destinationPortLineEdit->text()); + on_tagLineEdit_textChanged(ti_ui_->tagLineEdit->text()); + } + if (ti_ui_->sctpDataButton->isChecked()) { + ip_address = true; + port = true; + sctp_ppi = true; + on_sourcePortLineEdit_textChanged(ti_ui_->sourcePortLineEdit->text()); + on_destinationPortLineEdit_textChanged(ti_ui_->destinationPortLineEdit->text()); + on_ppiLineEdit_textChanged(ti_ui_->ppiLineEdit->text()); + } + } + + if (enable_export_pdu_buttons) { + if (ti_ui_->exportPduButton->isChecked()) { + export_pdu = true; + } + } + + foreach (auto &&rb, encap_buttons->buttons()) { + rb->setEnabled(enable_ip_buttons); + } + + ti_ui_->ethernetButton->setEnabled(enable_ethernet_buttons); + ti_ui_->exportPduButton->setEnabled(enable_export_pdu_buttons); + ti_ui_->noDummyButton->setEnabled(enable_export_pdu_buttons || enable_ip_buttons); + + ti_ui_->ethertypeLabel->setEnabled(ethertype); + ti_ui_->ethertypeLineEdit->setEnabled(ethertype); + ti_ui_->protocolLabel->setEnabled(ipv4_proto); + ti_ui_->protocolLineEdit->setEnabled(ipv4_proto); + ti_ui_->ipVersionLabel->setEnabled(ip_address); + if (encapsulation == WTAP_ENCAP_RAW_IP4) { + ti_ui_->ipVersionComboBox->setEnabled(false); + ti_ui_->ipVersionComboBox->setCurrentIndex(0); + } else if (encapsulation == WTAP_ENCAP_RAW_IP6) { + ti_ui_->ipVersionComboBox->setEnabled(false); + ti_ui_->ipVersionComboBox->setCurrentIndex(1); + } else { + ti_ui_->ipVersionComboBox->setEnabled(ip_address); + } + ti_ui_->sourceAddressLabel->setEnabled(ip_address); + ti_ui_->sourceAddressLineEdit->setEnabled(ip_address); + ti_ui_->destinationAddressLabel->setEnabled(ip_address); + ti_ui_->destinationAddressLineEdit->setEnabled(ip_address); + ti_ui_->sourcePortLabel->setEnabled(port); + ti_ui_->sourcePortLineEdit->setEnabled(port); + ti_ui_->destinationPortLabel->setEnabled(port); + ti_ui_->destinationPortLineEdit->setEnabled(port); + ti_ui_->tagLabel->setEnabled(sctp_tag); + ti_ui_->tagLineEdit->setEnabled(sctp_tag); + ti_ui_->ppiLabel->setEnabled(sctp_ppi); + ti_ui_->ppiLineEdit->setEnabled(sctp_ppi); + ti_ui_->payloadLabel->setEnabled(export_pdu); + ti_ui_->dissectorComboBox->setEnabled(export_pdu); + + if (ti_ui_->noDummyButton->isEnabled() && !(encap_buttons->checkedButton()->isEnabled())) { + ti_ui_->noDummyButton->toggle(); + } +} + +void ImportTextDialog::on_encapComboBox_currentIndexChanged(int index) +{ + QVariant val = ti_ui_->encapComboBox->itemData(index); +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + if (val.canConvert<int>()) +#else + if (val != QVariant::Invalid) +#endif + { + import_info_.encapsulation = val.toUInt(); + } else { + import_info_.encapsulation = WTAP_ENCAP_UNKNOWN; + } + + enableHeaderWidgets(import_info_.encapsulation); +} + +void ImportTextDialog::encap_buttonsToggled(QAbstractButton *button _U_, bool checked) +{ + if (checked) enableHeaderWidgets(import_info_.encapsulation); +} + +void ImportTextDialog::on_ipVersionComboBox_currentIndexChanged(int index) +{ + import_info_.ipv6 = (index == 1) ? 1 : 0; + on_sourceAddressLineEdit_textChanged(ti_ui_->sourceAddressLineEdit->text()); + on_destinationAddressLineEdit_textChanged(ti_ui_->destinationAddressLineEdit->text()); +} + +void ImportTextDialog::check_line_edit(SyntaxLineEdit *le, bool &ok_enabled, const QString &num_str, int base, guint max_val, bool is_short, guint *val_ptr) { + bool conv_ok; + SyntaxLineEdit::SyntaxState syntax_state = SyntaxLineEdit::Empty; + + if (!le || !val_ptr) + return; + + ok_enabled = true; + if (num_str.length() < 1) { + *val_ptr = 0; + } else { + if (is_short) { + *val_ptr = num_str.toUShort(&conv_ok, base); + } else { + *val_ptr = (guint)num_str.toULong(&conv_ok, base); + } + if (conv_ok && *val_ptr <= max_val) { + syntax_state = SyntaxLineEdit::Valid; + } else { + syntax_state = SyntaxLineEdit::Invalid; + ok_enabled = false; + } + } + le->setSyntaxState(syntax_state); + updateImportButtonState(); +} + +void ImportTextDialog::checkAddress(SyntaxLineEdit *le, bool &ok_enabled, const QString &addr_str, ws_in4_addr *val_ptr) { + bool conv_ok; + SyntaxLineEdit::SyntaxState syntax_state = SyntaxLineEdit::Empty; + + if (!le || !val_ptr) + return; + + ok_enabled = true; + if (addr_str.length() < 1) { + *val_ptr = 0; + } else { + conv_ok = ws_inet_pton4(addr_str.toUtf8().data(), (ws_in4_addr*)val_ptr); + if (conv_ok) { + syntax_state= SyntaxLineEdit::Valid; + } else { + syntax_state = SyntaxLineEdit::Invalid; + ok_enabled = false; + } + } + le->setSyntaxState(syntax_state); + updateImportButtonState(); +} + +void ImportTextDialog::checkIPv6Address(SyntaxLineEdit *le, bool &ok_enabled, const QString &addr_str, ws_in6_addr *val_ptr) { + bool conv_ok; + SyntaxLineEdit::SyntaxState syntax_state = SyntaxLineEdit::Empty; + + if (!le || !val_ptr) + return; + + ok_enabled = true; + if (addr_str.length() < 1) { + *val_ptr = {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}; + } else { + conv_ok = ws_inet_pton6(addr_str.toUtf8().data(), (ws_in6_addr*)val_ptr); + if (conv_ok) { + syntax_state= SyntaxLineEdit::Valid; + } else { + syntax_state = SyntaxLineEdit::Invalid; + ok_enabled = false; + } + } + le->setSyntaxState(syntax_state); + updateImportButtonState(); +} + +void ImportTextDialog::on_ethertypeLineEdit_textChanged(const QString ðertype_str) +{ + check_line_edit(ti_ui_->ethertypeLineEdit, ether_type_ok_, ethertype_str, 16, 0xffff, true, &import_info_.pid); +} + +void ImportTextDialog::on_protocolLineEdit_textChanged(const QString &protocol_str) +{ + check_line_edit(ti_ui_->protocolLineEdit, proto_ok_, protocol_str, 10, 0xff, true, &import_info_.protocol); +} + +void ImportTextDialog::on_sourceAddressLineEdit_textChanged(const QString &source_addr_str) +{ + if (ti_ui_->ipVersionComboBox->currentIndex() == 1) { + checkIPv6Address(ti_ui_->sourceAddressLineEdit, source_addr_ok_, source_addr_str, &import_info_.ip_src_addr.ipv6); + } else { + checkAddress(ti_ui_->sourceAddressLineEdit, source_addr_ok_, source_addr_str, &import_info_.ip_src_addr.ipv4); + } +} + +void ImportTextDialog::on_destinationAddressLineEdit_textChanged(const QString &destination_addr_str) +{ + if (ti_ui_->ipVersionComboBox->currentIndex() == 1) { + checkIPv6Address(ti_ui_->destinationAddressLineEdit, dest_addr_ok_, destination_addr_str, &import_info_.ip_dest_addr.ipv6); + } else { + checkAddress(ti_ui_->destinationAddressLineEdit, dest_addr_ok_, destination_addr_str, &import_info_.ip_dest_addr.ipv4); + } +} + +void ImportTextDialog::on_sourcePortLineEdit_textChanged(const QString &source_port_str) +{ + check_line_edit(ti_ui_->sourcePortLineEdit, source_port_ok_, source_port_str, 10, 0xffff, true, &import_info_.src_port); +} + +void ImportTextDialog::on_destinationPortLineEdit_textChanged(const QString &destination_port_str) +{ + check_line_edit(ti_ui_->destinationPortLineEdit, dest_port_ok_, destination_port_str, 10, 0xffff, true, &import_info_.dst_port); +} + +void ImportTextDialog::on_tagLineEdit_textChanged(const QString &tag_str) +{ + check_line_edit(ti_ui_->tagLineEdit, tag_ok_, tag_str, 10, 0xffffffff, false, &import_info_.tag); +} + +void ImportTextDialog::on_ppiLineEdit_textChanged(const QString &ppi_str) +{ + check_line_edit(ti_ui_->ppiLineEdit, ppi_ok_, ppi_str, 10, 0xffffffff, false, &import_info_.ppi); +} + +/******************************************************************************* +* Footer +*/ + +void ImportTextDialog::on_maxLengthLineEdit_textChanged(const QString &max_frame_len_str) +{ + check_line_edit(ti_ui_->maxLengthLineEdit, max_len_ok_, max_frame_len_str, 10, WTAP_MAX_PACKET_SIZE_STANDARD, true, &import_info_.max_frame_length); +} + +void ImportTextDialog::on_buttonBox_helpRequested() +{ + mainApp->helpTopicAction(HELP_IMPORT_DIALOG); +} |