/* import_text_dialog.cpp * * Wireshark - Network traffic analyzer * By Gerald Combs * 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 #include "ui/text_import_scanner.h" #include "ui/last_open_dir.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 #include "main_application.h" #include #include "ui/qt/widgets/wireshark_file_dialog.h" #include #include #include #include #define HINT_BEGIN "" #define HINT_END "" #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(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(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(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_ * 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 */ g_free(params.idb_inf); 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_last_open_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(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()) #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()) #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); }