wireshark/ui/qt/import_text_dialog.cpp

1080 lines
40 KiB
C++

/* 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/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 <ui_import_text_dialog.h>
#include "wireshark_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 "&lt;"
#define HTML_GT "&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(wsApp->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(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_<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(&params, 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(&params, 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, &params, &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, wsApp->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 = 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 (val != QVariant::Invalid) {
// 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 (val != QVariant::Invalid) {
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.toLocal8Bit().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.toLocal8Bit().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 &ethertype_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()
{
wsApp->helpTopicAction(HELP_IMPORT_DIALOG);
}