1 /* import_text_dialog.cpp
3 * Wireshark - Network traffic analyzer
4 * By Gerald Combs <gerald@wireshark.org>
5 * Copyright 1998 Gerald Combs
7 * SPDX-License-Identifier: GPL-2.0-or-later
12 #include "import_text_dialog.h"
14 #include "wiretap/wtap.h"
15 #include "wiretap/pcap-encap.h"
17 #include "ui/text_import_scanner.h"
19 #include "ui/alert_box.h"
20 #include "ui/help_url.h"
21 #include "ui/capture_globals.h"
24 #include "wsutil/file_util.h"
25 #include "wsutil/inet_addr.h"
26 #include "wsutil/time_util.h"
27 #include "wsutil/filesystem.h"
28 #include <wsutil/array.h>
30 #include <ui_import_text_dialog.h>
31 #include "main_application.h"
32 #include <ui/qt/utils/qt_ui_utils.h>
33 #include "ui/qt/widgets/wireshark_file_dialog.h"
36 #include <QJsonDocument>
37 #include <QJsonObject>
40 #define HINT_BEGIN "<small><i>"
41 #define HINT_END "</i></small>"
42 #define HTML_LT "<"
43 #define HTML_GT ">"
45 static const QString default_regex_hint
= ImportTextDialog::tr("Supported fields are data, dir, time, seqno");
46 static const QString missing_data_hint
= ImportTextDialog::tr("Missing capturing group data (use (?" HTML_LT
"data" HTML_GT
"(...)) )");
48 #define SETTINGS_FILE "import_hexdump.json"
50 ImportTextDialog::ImportTextDialog(QWidget
*parent
) :
52 ti_ui_(new Ui::ImportTextDialog
),
55 timestamp_format_ok_(true),
58 in_indication_ok_(false),
59 out_indication_ok_(false),
63 source_addr_ok_(true),
65 source_port_ok_(true),
74 int file_type_subtype
;
76 ti_ui_
->setupUi(this);
77 setWindowTitle(mainApp
->windowTitleString(tr("Import From Hex Dump")));
78 memset(&import_info_
, 0, sizeof(import_info_
));
80 import_button_
= ti_ui_
->buttonBox
->button(QDialogButtonBox::Open
);
81 import_button_
->setText(tr("Import"));
82 import_button_
->setEnabled(false);
84 ti_ui_
->regexHintLabel
->setSmallText(true);
87 // The grid layout squishes each line edit otherwise.
88 int le_height
= ti_ui_
->textFileLineEdit
->sizeHint().height();
89 ti_ui_
->ethertypeLineEdit
->setMinimumHeight(le_height
);
90 ti_ui_
->protocolLineEdit
->setMinimumHeight(le_height
);
91 ti_ui_
->sourceAddressLineEdit
->setMinimumHeight(le_height
);
92 ti_ui_
->destinationAddressLineEdit
->setMinimumHeight(le_height
);
93 ti_ui_
->sourcePortLineEdit
->setMinimumHeight(le_height
);
94 ti_ui_
->destinationPortLineEdit
->setMinimumHeight(le_height
);
95 ti_ui_
->tagLineEdit
->setMinimumHeight(le_height
);
96 ti_ui_
->ppiLineEdit
->setMinimumHeight(le_height
);
99 on_timestampFormatLineEdit_textChanged(ti_ui_
->timestampFormatLineEdit
->text());
101 encap_buttons
= new QButtonGroup(this);
102 for (i
= 0; i
< ti_ui_
->headerGridLayout
->count(); i
++) {
103 QRadioButton
*rb
= qobject_cast
<QRadioButton
*>(ti_ui_
->headerGridLayout
->itemAt(i
)->widget());
105 if (rb
) encap_buttons
->addButton(rb
);
107 /* There are two QButtonGroup::buttonToggled signals from Qt 5.2-5.15 with
108 * different parameters. This breaks connectSlotsByName, which only finds
109 * the deprecated one that doesn't exist in Qt 6. So we have to connect it
110 * manually, and avoid naming the slot in the normal way.
112 connect(encap_buttons
, SIGNAL(buttonToggled(QAbstractButton
*, bool)), this, SLOT(encap_buttonsToggled(QAbstractButton
*, bool)));
114 /* fill the IP version combobox */
115 ti_ui_
->ipVersionComboBox
->addItem("IPv4", QVariant(4));
116 ti_ui_
->ipVersionComboBox
->addItem("IPv6", QVariant(6));
118 /* fill the data encoding dropdown in regex tab*/
121 enum data_encoding id
;
123 {"Plain hex", ENCODING_PLAIN_HEX
},
124 {"Plain oct", ENCODING_PLAIN_OCT
},
125 {"Plain bin", ENCODING_PLAIN_BIN
},
126 {"Base 64", ENCODING_BASE64
}
128 for (i
= 0; i
< (int)array_length(encodings
); ++i
) {
129 ti_ui_
->dataEncodingComboBox
->addItem(encodings
[i
].name
, QVariant(encodings
[i
].id
));
133 * Scan all Wiretap encapsulation types.
135 * XXX - this "knows" that WTAP_ENCAP_ETHERNET is the first encapsulation
136 * type, skipping the special non-types WTAP_ENCAP_PER_PACKET and
137 * WTAP_ENCAP_UNKNOWN. We need a better way to express the notion
138 * of "for (all encapsulation types)".
140 import_info_
.encapsulation
= WTAP_ENCAP_ETHERNET
;
141 file_type_subtype
= wtap_pcapng_file_type_subtype();
142 for (encap
= import_info_
.encapsulation
; encap
< wtap_get_num_encap_types(); encap
++)
144 /* Check if we can write to a pcapng file
146 * Exclude wtap encapsulations that require a pseudo header,
147 * because we won't setup one from the text we import and
148 * wiretap doesn't allow us to write 'raw' frames
150 if (wtap_dump_can_write_encap(file_type_subtype
, encap
) &&
151 !wtap_encap_requires_phdr(encap
)) {
153 /* If it has got a name */
154 if ((name
= wtap_encap_description(encap
)))
156 ti_ui_
->encapComboBox
->addItem(name
, QVariant(encap
));
160 ti_ui_
->encapComboBox
->model()->sort(0);
162 /* fill the dissector combo box */
163 GList
* dissector_names
= get_dissector_names();
164 for (GList
* l
= dissector_names
; l
!= NULL
; l
= l
->next
) {
165 const char* name
= (const char*) l
->data
;
166 ti_ui_
->dissectorComboBox
->addItem(name
, QVariant(name
));
168 ti_ui_
->dissectorComboBox
->model()->sort(0);
169 g_list_free(dissector_names
);
171 ti_ui_
->regexHintLabel
->setText(default_regex_hint
);
173 applyDialogSettings();
174 updateImportButtonState();
177 ImportTextDialog::~ImportTextDialog()
179 storeDialogSettings();
184 void ImportTextDialog::loadSettingsFile()
186 QFileInfo
fileInfo(gchar_free_to_qstring(get_profile_dir(get_profile_name(), false)), QString(SETTINGS_FILE
));
187 QFile
loadFile(fileInfo
.filePath());
189 if (!fileInfo
.exists() || !fileInfo
.isFile()) {
193 if (loadFile
.open(QIODevice::ReadOnly
)) {
194 QByteArray loadData
= loadFile
.readAll();
195 QJsonDocument document
= QJsonDocument::fromJson(loadData
);
197 settings
= document
.object().toVariantMap();
201 void ImportTextDialog::saveSettingsFile()
203 QFileInfo
fileInfo(gchar_free_to_qstring(get_profile_dir(get_profile_name(), false)), QString(SETTINGS_FILE
));
204 QFile
saveFile(fileInfo
.filePath());
206 if (fileInfo
.exists() && !fileInfo
.isFile()) {
210 if (saveFile
.open(QIODevice::WriteOnly
)) {
211 QJsonDocument document
= QJsonDocument::fromVariant(settings
);
212 QByteArray saveData
= document
.toJson();
214 saveFile
.write(saveData
);
218 void ImportTextDialog::applyDialogSettings()
223 QString offsetType
= settings
["hexdump.offsets"].toString();
224 if (offsetType
== "hex") {
225 ti_ui_
->hexOffsetButton
->setChecked(true);
226 } else if (offsetType
== "dec") {
227 ti_ui_
->decimalOffsetButton
->setChecked(true);
228 } else if (offsetType
== "oct") {
229 ti_ui_
->octalOffsetButton
->setChecked(true);
230 } else if (offsetType
== "none") {
231 ti_ui_
->noOffsetButton
->setChecked(true);
233 ti_ui_
->directionIndicationCheckBox
->setChecked(settings
["hexdump.hasDirection"].toBool());
234 ti_ui_
->asciiIdentificationCheckBox
->setChecked(settings
["hexdump.identifyAscii"].toBool());
236 // Regular Expression
237 ti_ui_
->regexTextEdit
->setText(settings
["regex.format"].toString());
238 QString encoding
= settings
["regex.encoding"].toString();
239 if (encoding
== "plainHex") {
240 ti_ui_
->dataEncodingComboBox
->setCurrentIndex(0);
241 } else if (encoding
== "plainOct") {
242 ti_ui_
->dataEncodingComboBox
->setCurrentIndex(1);
243 } else if (encoding
== "plainBin") {
244 ti_ui_
->dataEncodingComboBox
->setCurrentIndex(2);
245 } else if (encoding
== "base64") {
246 ti_ui_
->dataEncodingComboBox
->setCurrentIndex(3);
248 ti_ui_
->dirInIndicationLineEdit
->setText(settings
["regex.inIndication"].toString());
249 ti_ui_
->dirOutIndicationLineEdit
->setText(settings
["regex.outIndication"].toString());
252 ti_ui_
->timestampFormatLineEdit
->setText(settings
["timestampFormat"].toString());
254 const char *name
= wtap_encap_description(settings
["encapsulation"].toInt());
255 ti_ui_
->encapComboBox
->setCurrentText(name
);
257 QString dummyHeader
= settings
["dummyHeader"].toString();
258 if (dummyHeader
== "ethernet") {
259 ti_ui_
->ethernetButton
->setChecked(true);
260 } else if (dummyHeader
== "ipv4") {
261 ti_ui_
->ipv4Button
->setChecked(true);
262 } else if (dummyHeader
== "udp") {
263 ti_ui_
->udpButton
->setChecked(true);
264 } else if (dummyHeader
== "tcp") {
265 ti_ui_
->tcpButton
->setChecked(true);
266 } else if (dummyHeader
== "sctp") {
267 ti_ui_
->sctpButton
->setChecked(true);
268 } else if (dummyHeader
== "sctpData") {
269 ti_ui_
->sctpDataButton
->setChecked(true);
270 } else if (dummyHeader
== "exportPDU") {
271 ti_ui_
->exportPduButton
->setChecked(true);
272 } else if (dummyHeader
== "none") {
273 ti_ui_
->noDummyButton
->setChecked(true);
276 if (settings
["ipVersion"].toUInt() == 6) {
277 ti_ui_
->ipVersionComboBox
->setCurrentIndex(1);
279 ti_ui_
->ipVersionComboBox
->setCurrentIndex(0);
281 ti_ui_
->ethertypeLineEdit
->setText(settings
["ethertype"].toString());
282 ti_ui_
->protocolLineEdit
->setText(settings
["ipProtocol"].toString());
283 ti_ui_
->sourceAddressLineEdit
->setText(settings
["sourceAddress"].toString());
284 ti_ui_
->destinationAddressLineEdit
->setText(settings
["destinationAddress"].toString());
285 ti_ui_
->sourcePortLineEdit
->setText(settings
["sourcePort"].toString());
286 ti_ui_
->destinationPortLineEdit
->setText(settings
["destinationPort"].toString());
287 ti_ui_
->tagLineEdit
->setText(settings
["sctpTag"].toString());
288 ti_ui_
->ppiLineEdit
->setText(settings
["sctpPPI"].toString());
290 if (settings
.contains("pduPayload")) {
291 ti_ui_
->dissectorComboBox
->setCurrentText(settings
["pduPayload"].toString());
293 // Default to the data dissector when not previously set
294 ti_ui_
->dissectorComboBox
->setCurrentText("data");
297 ti_ui_
->interfaceLineEdit
->setText(settings
["interfaceName"].toString());
298 ti_ui_
->maxLengthLineEdit
->setText(settings
["maxFrameLength"].toString());
300 // Select mode tab last to enableFieldWidgets()
301 QString
mode(settings
["mode"].toString());
302 int modeIndex
= (mode
== "regex") ? 1 : 0;
303 ti_ui_
->modeTabWidget
->setCurrentIndex(modeIndex
);
304 on_modeTabWidget_currentChanged(modeIndex
);
307 void ImportTextDialog::storeDialogSettings()
309 int modeIndex
= ti_ui_
->modeTabWidget
->currentIndex();
310 if (modeIndex
== 0) {
311 settings
["mode"] = "hexdump";
313 settings
["mode"] = "regex";
317 if (ti_ui_
->hexOffsetButton
->isChecked()) {
318 settings
["hexdump.offsets"] = "hex";
319 } else if (ti_ui_
->decimalOffsetButton
->isChecked()) {
320 settings
["hexdump.offsets"] = "dec";
321 } else if (ti_ui_
->octalOffsetButton
->isChecked()) {
322 settings
["hexdump.offsets"] = "oct";
324 settings
["hexdump.offsets"] = "none";
326 settings
["hexdump.hasDirection"] = ti_ui_
->directionIndicationCheckBox
->isChecked();
327 settings
["hexdump.identifyAscii"] = ti_ui_
->asciiIdentificationCheckBox
->isChecked();
329 // Regular Expression
330 settings
["regex.format"] = ti_ui_
->regexTextEdit
->toPlainText();
331 QVariant encodingVal
= ti_ui_
->dataEncodingComboBox
->itemData(ti_ui_
->dataEncodingComboBox
->currentIndex());
332 if (encodingVal
.isValid()) {
333 enum data_encoding encoding
= (enum data_encoding
) encodingVal
.toUInt();
335 case ENCODING_PLAIN_HEX
:
336 settings
["regex.encoding"] = "plainHex";
338 case ENCODING_PLAIN_OCT
:
339 settings
["regex.encoding"] = "plainOct";
341 case ENCODING_PLAIN_BIN
:
342 settings
["regex.encoding"] = "plainBin";
344 case ENCODING_BASE64
:
345 settings
["regex.encoding"] = "base64";
349 settings
["regex.encoding"] = "plainHex";
351 settings
["regex.inIndication"] = ti_ui_
->dirInIndicationLineEdit
->text();
352 settings
["regex.outIndication"] = ti_ui_
->dirOutIndicationLineEdit
->text();
355 settings
["timestampFormat"] = ti_ui_
->timestampFormatLineEdit
->text();
357 QVariant encapVal
= ti_ui_
->encapComboBox
->itemData(ti_ui_
->encapComboBox
->currentIndex());
358 if (encapVal
.isValid()) {
359 settings
["encapsulation"] = encapVal
.toUInt();
361 settings
["encapsulation"] = WTAP_ENCAP_ETHERNET
;
364 if (ti_ui_
->ethernetButton
->isChecked()) {
365 settings
["dummyHeader"] = "ethernet";
366 } else if (ti_ui_
->ipv4Button
->isChecked()) {
367 settings
["dummyHeader"] = "ipv4";
368 } else if (ti_ui_
->udpButton
->isChecked()) {
369 settings
["dummyHeader"] = "udp";
370 } else if (ti_ui_
->tcpButton
->isChecked()) {
371 settings
["dummyHeader"] = "tcp";
372 } else if (ti_ui_
->sctpButton
->isChecked()) {
373 settings
["dummyHeader"] = "sctp";
374 } else if (ti_ui_
->sctpDataButton
->isChecked()) {
375 settings
["dummyHeader"] = "sctpData";
376 } else if (ti_ui_
->exportPduButton
->isChecked()) {
377 settings
["dummyHeader"] = "exportPDU";
379 settings
["dummyHeader"] = "none";
382 settings
["ipVersion"] = ti_ui_
->ipVersionComboBox
->currentData().toUInt();
383 settings
["ethertype"] = ti_ui_
->ethertypeLineEdit
->text();
384 settings
["ipProtocol"] = ti_ui_
->protocolLineEdit
->text();
385 settings
["sourceAddress"] = ti_ui_
->sourceAddressLineEdit
->text();
386 settings
["destinationAddress"] = ti_ui_
->destinationAddressLineEdit
->text();
387 settings
["sourcePort"] = ti_ui_
->sourcePortLineEdit
->text();
388 settings
["destinationPort"] = ti_ui_
->destinationPortLineEdit
->text();
389 settings
["sctpTag"] = ti_ui_
->tagLineEdit
->text();
390 settings
["sctpPPI"] = ti_ui_
->ppiLineEdit
->text();
391 settings
["pduPayload"] = ti_ui_
->dissectorComboBox
->currentData().toString();
393 settings
["interfaceName"] = ti_ui_
->interfaceLineEdit
->text();
394 settings
["maxFrameLength"] = ti_ui_
->maxLengthLineEdit
->text();
399 QString
&ImportTextDialog::capfileName() {
400 return capfile_name_
;
403 int ImportTextDialog::exec() {
406 GError
* gerror
= NULL
;
409 wtap_dump_params params
;
410 int file_type_subtype
;
411 QString interface_name
;
415 if (result() != QDialog::Accepted
) {
419 /* from here on the cleanup labels are used to free allocated resources in
421 * naming is cleanup_<step_where_something_failed>
422 * Don't Declare new variables from here on
425 import_info_
.import_text_filename
= qstring_strdup(ti_ui_
->textFileLineEdit
->text());
426 import_info_
.timestamp_format
= qstring_strdup(ti_ui_
->timestampFormatLineEdit
->text());
427 if (strlen(import_info_
.timestamp_format
) == 0) {
428 g_free((void *) import_info_
.timestamp_format
);
429 import_info_
.timestamp_format
= NULL
;
432 mainApp
->setLastOpenDirFromFilename(QString(import_info_
.import_text_filename
));
434 switch (import_info_
.mode
) {
435 default: /* should never happen */
436 setResult(QDialog::Rejected
);
437 return QDialog::Rejected
;
438 case TEXT_IMPORT_HEXDUMP
:
439 import_info_
.hexdump
.import_text_FILE
= ws_fopen(import_info_
.import_text_filename
, "rb");
440 if (!import_info_
.hexdump
.import_text_FILE
) {
441 open_failure_alert_box(import_info_
.import_text_filename
, errno
, false);
442 setResult(QDialog::Rejected
);
446 import_info_
.hexdump
.offset_type
=
447 ti_ui_
->hexOffsetButton
->isChecked() ? OFFSET_HEX
:
448 ti_ui_
->decimalOffsetButton
->isChecked() ? OFFSET_DEC
:
449 ti_ui_
->octalOffsetButton
->isChecked() ? OFFSET_OCT
:
452 case TEXT_IMPORT_REGEX
:
453 import_info_
.regex
.import_text_GMappedFile
= g_mapped_file_new(import_info_
.import_text_filename
, true, &gerror
);
455 open_failure_alert_box(import_info_
.import_text_filename
, gerror
->code
, false);
456 g_error_free(gerror
);
457 setResult(QDialog::Rejected
);
460 tmp
= qstring_strdup(ti_ui_
->regexTextEdit
->toPlainText());
461 import_info_
.regex
.format
= g_regex_new(tmp
, (GRegexCompileFlags
) (G_REGEX_DUPNAMES
| G_REGEX_OPTIMIZE
| G_REGEX_MULTILINE
), G_REGEX_MATCH_NOTEMPTY
, &gerror
);
464 import_info_
.regex
.in_indication
= qstring_strdup(ti_ui_
->dirInIndicationLineEdit
->text());
465 import_info_
.regex
.out_indication
= qstring_strdup(ti_ui_
->dirOutIndicationLineEdit
->text());
467 import_info_
.regex
.in_indication
= NULL
;
468 import_info_
.regex
.out_indication
= NULL
;
473 encap_val
= ti_ui_
->encapComboBox
->itemData(ti_ui_
->encapComboBox
->currentIndex());
474 import_info_
.dummy_header_type
= HEADER_NONE
;
475 if (encap_val
.isValid() && (encap_buttons
->checkedButton()->isEnabled())
476 && !ti_ui_
->noDummyButton
->isChecked()) {
477 // Inputs were validated in the on_xxx_textChanged slots.
478 if (ti_ui_
->ethernetButton
->isChecked()) {
479 import_info_
.dummy_header_type
= HEADER_ETH
;
480 } else if (ti_ui_
->ipv4Button
->isChecked()) {
481 import_info_
.dummy_header_type
= HEADER_IPV4
;
482 } else if (ti_ui_
->udpButton
->isChecked()) {
483 import_info_
.dummy_header_type
= HEADER_UDP
;
484 } else if (ti_ui_
->tcpButton
->isChecked()) {
485 import_info_
.dummy_header_type
= HEADER_TCP
;
486 } else if (ti_ui_
->sctpButton
->isChecked()) {
487 import_info_
.dummy_header_type
= HEADER_SCTP
;
488 } else if (ti_ui_
->sctpDataButton
->isChecked()) {
489 import_info_
.dummy_header_type
= HEADER_SCTP_DATA
;
490 } else if (ti_ui_
->exportPduButton
->isChecked()) {
491 import_info_
.dummy_header_type
= HEADER_EXPORT_PDU
;
494 if (import_info_
.max_frame_length
== 0) {
495 import_info_
.max_frame_length
= WTAP_MAX_PACKET_SIZE_STANDARD
;
498 import_info_
.payload
= qstring_strdup(ti_ui_
->dissectorComboBox
->currentData().toString());
500 capfile_name_
.clear();
501 wtap_dump_params_init(¶ms
, NULL
);
502 params
.encap
= import_info_
.encapsulation
;
503 params
.snaplen
= import_info_
.max_frame_length
;
504 params
.tsprec
= WTAP_TSPREC_NSEC
; /* XXX - support other precisions? */
505 /* Write a pcapng temporary file */
506 file_type_subtype
= wtap_pcapng_file_type_subtype();
507 if (ti_ui_
->interfaceLineEdit
->text().length()) {
508 interface_name
= ti_ui_
->interfaceLineEdit
->text();
510 interface_name
= ti_ui_
->interfaceLineEdit
->placeholderText();
512 text_import_pre_open(¶ms
, file_type_subtype
, import_info_
.import_text_filename
, interface_name
.toUtf8().constData());
513 /* Use a random name for the temporary import buffer */
514 import_info_
.wdh
= wtap_dump_open_tempfile(global_capture_opts
.temp_dir
, &tmp
, "import", file_type_subtype
, WTAP_UNCOMPRESSED
, ¶ms
, &err
, &err_info
);
515 capfile_name_
.append(tmp
? tmp
: "temporary file");
516 import_info_
.output_filename
= tmp
;
518 if (import_info_
.wdh
== NULL
) {
519 cfile_dump_open_failure_alert_box(capfile_name_
.toUtf8().constData(), err
, err_info
, file_type_subtype
);
520 setResult(QDialog::Rejected
);
524 err
= text_import(&import_info_
);
527 failure_alert_box("Import failed");
528 setResult(QDialog::Rejected
);
532 cleanup
: /* free in reverse order of allocation */
533 if (!wtap_dump_close(import_info_
.wdh
, NULL
, &err
, &err_info
))
535 cfile_close_failure_alert_box(capfile_name_
.toUtf8().constData(), err
, err_info
);
538 /* g_free checks for null */
539 wtap_free_idb_info(params
.idb_inf
);
540 wtap_dump_params_cleanup(¶ms
);
542 g_free((void *) import_info_
.payload
);
543 switch (import_info_
.mode
) {
544 case TEXT_IMPORT_HEXDUMP
:
545 fclose(import_info_
.hexdump
.import_text_FILE
);
547 case TEXT_IMPORT_REGEX
:
548 g_mapped_file_unref(import_info_
.regex
.import_text_GMappedFile
);
549 g_regex_unref((GRegex
*) import_info_
.regex
.format
);
550 g_free((void *) import_info_
.regex
.in_indication
);
551 g_free((void *) import_info_
.regex
.out_indication
);
555 g_free((void *) import_info_
.import_text_filename
);
556 g_free((void *) import_info_
.timestamp_format
);
560 /*******************************************************************************
564 void ImportTextDialog::updateImportButtonState()
566 /* XXX: This requires even buttons that aren't being used to have valid
567 * entries (addresses, ports, etc.) Fixing that can mean changing the
568 * encapsulation type in order to enable the line edits, which is a little
569 * awkward for the user.
571 if (file_ok_
&& timestamp_format_ok_
&& ether_type_ok_
&&
572 proto_ok_
&& source_addr_ok_
&& dest_addr_ok_
&&
573 source_port_ok_
&& dest_port_ok_
&&
574 tag_ok_
&& ppi_ok_
&& payload_ok_
&& max_len_ok_
&&
577 import_info_
.mode
== TEXT_IMPORT_REGEX
&& regex_ok_
&&
578 (!re_has_dir_
|| (in_indication_ok_
&& out_indication_ok_
))
580 import_info_
.mode
== TEXT_IMPORT_HEXDUMP
584 import_button_
->setEnabled(true);
586 import_button_
->setEnabled(false);
590 void ImportTextDialog::on_textFileLineEdit_textChanged(const QString
&file_name
)
592 QFile
text_file(file_name
);
594 if (file_name
.length() > 0 && text_file
.open(QIODevice::ReadOnly
)) {
600 updateImportButtonState();
603 void ImportTextDialog::on_textFileBrowseButton_clicked()
606 if (ti_ui_
->textFileLineEdit
->text().length() > 0) {
607 open_dir
= ti_ui_
->textFileLineEdit
->text();
609 open_dir
= get_open_dialog_initial_dir();
612 QString file_name
= WiresharkFileDialog::getOpenFileName(this, mainApp
->windowTitleString(tr("Import Text File")), open_dir
);
613 ti_ui_
->textFileLineEdit
->setText(file_name
);
616 bool ImportTextDialog::checkDateTimeFormat(const QString
&time_format
)
618 /* nonstandard is f for fractions of seconds */
619 const QString valid_code
= "aAbBcdDFfHIjmMpsSTUwWxXyYzZ%";
623 /* XXX: Temporary(?) hack to allow ISO format time, a checkbox is
625 if (time_format
== "ISO") {
627 } else while ((idx
= static_cast<int>(time_format
.indexOf("%", idx
))) != -1) {
629 if ((idx
== time_format
.size()) || !valid_code
.contains(time_format
[idx
])) {
638 void ImportTextDialog::on_timestampFormatLineEdit_textChanged(const QString
&time_format
)
640 if (time_format
.length() > 0) {
641 if (checkDateTimeFormat(time_format
)) {
642 struct timespec timenow
;
646 QString timefmt
= QString(time_format
);
648 ws_clock_get_realtime(&timenow
);
650 /* On windows strftime/wcsftime does not support %s yet, this works on all OSs */
651 timefmt
.replace(QStringLiteral("%s"), QString::number(timenow
.tv_sec
));
652 /* subsecond example as usec */
653 timefmt
.replace(QStringLiteral("%f"), QStringLiteral("%1").arg(timenow
.tv_nsec
, 6, 10, QChar('0')));
655 cur_tm
= localtime(&timenow
.tv_sec
);
656 if (cur_tm
== NULL
) {
657 memset(&fallback
, 0, sizeof(fallback
));
660 strftime(time_str
, sizeof time_str
, timefmt
.toUtf8(), cur_tm
);
661 ti_ui_
->timestampExampleLabel
->setText(tr(HINT_BEGIN
"Example: %1" HINT_END
).arg(QString(time_str
).toHtmlEscaped()));
662 timestamp_format_ok_
= true;
665 ti_ui_
->timestampExampleLabel
->setText(tr(HINT_BEGIN
"(Wrong date format)" HINT_END
));
666 timestamp_format_ok_
= false;
669 ti_ui_
->timestampExampleLabel
->setText(tr(HINT_BEGIN
"(No format will be applied)" HINT_END
));
670 timestamp_format_ok_
= true;
672 updateImportButtonState();
675 void ImportTextDialog::on_modeTabWidget_currentChanged(int index
) {
678 ti_ui_
->modeTabWidget
->setCurrentIndex(0);
680 case 0: /* these numbers depend on the UI */
681 import_info_
.mode
= TEXT_IMPORT_HEXDUMP
;
682 memset(&import_info_
.hexdump
, 0, sizeof(import_info_
.hexdump
));
683 on_directionIndicationCheckBox_toggled(ti_ui_
->directionIndicationCheckBox
->isChecked());
684 on_asciiIdentificationCheckBox_toggled(ti_ui_
->asciiIdentificationCheckBox
->isChecked());
685 enableFieldWidgets(false, true);
688 import_info_
.mode
= TEXT_IMPORT_REGEX
;
689 memset(&import_info_
.regex
, 0, sizeof(import_info_
.regex
));
690 on_dataEncodingComboBox_currentIndexChanged(ti_ui_
->dataEncodingComboBox
->currentIndex());
691 enableFieldWidgets(re_has_dir_
, re_has_time_
);
694 on_textFileLineEdit_textChanged(ti_ui_
->textFileLineEdit
->text());
697 /*******************************************************************************
701 void ImportTextDialog::on_noOffsetButton_toggled(bool checked
)
704 ti_ui_
->noOffsetLabel
->setText("(only one packet will be created)");
706 ti_ui_
->noOffsetLabel
->setText("");
710 void ImportTextDialog::on_directionIndicationCheckBox_toggled(bool checked
)
712 import_info_
.hexdump
.has_direction
= checked
;
715 void ImportTextDialog::on_asciiIdentificationCheckBox_toggled(bool checked
)
717 import_info_
.hexdump
.identify_ascii
= checked
;
720 /*******************************************************************************
724 void ImportTextDialog::on_regexTextEdit_textChanged()
726 char* regex_gchar_p
= qstring_strdup(ti_ui_
->regexTextEdit
->toPlainText());
727 GError
* gerror
= NULL
;
728 /* TODO: Use GLib's c++ interface or enable C++ int to enum casting
729 * because the flags are declared as enum, so we can't pass 0 like
730 * the specification recommends. These options don't hurt.
732 GRegex
* regex
= g_regex_new(regex_gchar_p
, G_REGEX_DUPNAMES
, G_REGEX_MATCH_NOTEMPTY
, &gerror
);
735 ti_ui_
->regexHintLabel
->setText(QString(gerror
->message
).toHtmlEscaped());
736 g_error_free(gerror
);
738 regex_ok_
= 0 <= g_regex_get_string_number(regex
, "data");
740 ti_ui_
->regexHintLabel
->setText(default_regex_hint
);
742 ti_ui_
->regexHintLabel
->setText(missing_data_hint
);
743 re_has_dir_
= 0 <= g_regex_get_string_number(regex
, "dir");
744 re_has_time_
= 0 <= g_regex_get_string_number(regex
, "time");
745 //re_has_seqno = 0 <= g_regex_get_string_number(regex, "seqno");
746 g_regex_unref(regex
);
748 g_free(regex_gchar_p
);
749 enableFieldWidgets(re_has_dir_
, re_has_time_
);
750 updateImportButtonState();
753 void ImportTextDialog::enableFieldWidgets(bool enable_direction_input
, bool enable_time_input
) {
754 ti_ui_
->dirIndicationLabel
->setEnabled(enable_direction_input
);
755 ti_ui_
->dirInIndicationLineEdit
->setEnabled(enable_direction_input
);
756 ti_ui_
->dirOutIndicationLineEdit
->setEnabled(enable_direction_input
);
757 ti_ui_
->timestampLabel
->setEnabled(enable_time_input
);
758 ti_ui_
->timestampFormatLineEdit
->setEnabled(enable_time_input
);
759 ti_ui_
->timestampExampleLabel
->setEnabled(enable_time_input
);
762 void ImportTextDialog::on_dataEncodingComboBox_currentIndexChanged(int index
)
764 QVariant val
= ti_ui_
->dataEncodingComboBox
->itemData(index
);
765 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
766 if (val
.canConvert
<int>())
768 if (val
!= QVariant::Invalid
)
771 // data_encoding_ok = true;
772 import_info_
.regex
.encoding
= (enum data_encoding
) val
.toUInt();
773 switch (import_info_
.regex
.encoding
) {
774 case ENCODING_PLAIN_HEX
:
775 ti_ui_
->encodingRegexExample
->setText(HINT_BEGIN
"(?" HTML_LT
"data" HTML_GT
"[0-9a-fA-F:\\s]+)" HINT_END
);
777 case ENCODING_PLAIN_BIN
:
778 ti_ui_
->encodingRegexExample
->setText(HINT_BEGIN
"(?" HTML_LT
"data" HTML_GT
"[0-1\\s]+)" HINT_END
);
780 case ENCODING_PLAIN_OCT
:
781 ti_ui_
->encodingRegexExample
->setText(HINT_BEGIN
"(?" HTML_LT
"data" HTML_GT
"[0-8:\\s]+)" HINT_END
);
783 case ENCODING_BASE64
:
784 ti_ui_
->encodingRegexExample
->setText(HINT_BEGIN
"(?" HTML_LT
"data" HTML_GT
"[0-9a-zA-Z+\\/\\s]+=*)" HINT_END
);
787 ti_ui_
->encodingRegexExample
->setText(HINT_BEGIN HTML_LT
"no example" HTML_GT HINT_END
);
790 /* for some reason this breaks when changing the text */
791 ti_ui_
->encodingRegexExample
->setTextInteractionFlags(Qt::TextSelectableByMouse
);
793 updateImportButtonState();
796 void ImportTextDialog::on_dirInIndicationLineEdit_textChanged(const QString
&in_indication
)
798 in_indication_ok_
= in_indication
.length() > 0;
799 updateImportButtonState();
802 void ImportTextDialog::on_dirOutIndicationLineEdit_textChanged(const QString
&out_indication
)
804 out_indication_ok_
= out_indication
.length() > 0;
805 updateImportButtonState();
808 /*******************************************************************************
809 * Encapsulation input
812 void ImportTextDialog::enableHeaderWidgets(uint encapsulation
) {
813 bool ethertype
= false;
814 bool ipv4_proto
= false;
815 bool ip_address
= false;
817 bool sctp_tag
= false;
818 bool sctp_ppi
= false;
819 bool export_pdu
= false;
820 bool enable_ethernet_buttons
= (encapsulation
== WTAP_ENCAP_ETHERNET
);
821 bool enable_ip_buttons
= (encapsulation
== WTAP_ENCAP_RAW_IP
|| encapsulation
== WTAP_ENCAP_RAW_IP4
|| encapsulation
== WTAP_ENCAP_RAW_IP6
);
822 bool enable_export_pdu_buttons
= (encapsulation
== WTAP_ENCAP_WIRESHARK_UPPER_PDU
);
824 if (enable_ethernet_buttons
) {
825 if (ti_ui_
->ethernetButton
->isChecked()) {
827 on_ethertypeLineEdit_textChanged(ti_ui_
->ethertypeLineEdit
->text());
829 enable_ip_buttons
= true;
832 if (enable_ip_buttons
) {
833 if (ti_ui_
->ipv4Button
->isChecked()) {
836 on_protocolLineEdit_textChanged(ti_ui_
->protocolLineEdit
->text());
837 } else if (ti_ui_
->udpButton
->isChecked() || ti_ui_
->tcpButton
->isChecked()) {
840 on_sourcePortLineEdit_textChanged(ti_ui_
->sourcePortLineEdit
->text());
841 on_destinationPortLineEdit_textChanged(ti_ui_
->destinationPortLineEdit
->text());
842 } else if (ti_ui_
->sctpButton
->isChecked()) {
846 on_sourcePortLineEdit_textChanged(ti_ui_
->sourcePortLineEdit
->text());
847 on_destinationPortLineEdit_textChanged(ti_ui_
->destinationPortLineEdit
->text());
848 on_tagLineEdit_textChanged(ti_ui_
->tagLineEdit
->text());
850 if (ti_ui_
->sctpDataButton
->isChecked()) {
854 on_sourcePortLineEdit_textChanged(ti_ui_
->sourcePortLineEdit
->text());
855 on_destinationPortLineEdit_textChanged(ti_ui_
->destinationPortLineEdit
->text());
856 on_ppiLineEdit_textChanged(ti_ui_
->ppiLineEdit
->text());
860 if (enable_export_pdu_buttons
) {
861 if (ti_ui_
->exportPduButton
->isChecked()) {
866 foreach (auto &&rb
, encap_buttons
->buttons()) {
867 rb
->setEnabled(enable_ip_buttons
);
870 ti_ui_
->ethernetButton
->setEnabled(enable_ethernet_buttons
);
871 ti_ui_
->exportPduButton
->setEnabled(enable_export_pdu_buttons
);
872 ti_ui_
->noDummyButton
->setEnabled(enable_export_pdu_buttons
|| enable_ip_buttons
);
874 ti_ui_
->ethertypeLabel
->setEnabled(ethertype
);
875 ti_ui_
->ethertypeLineEdit
->setEnabled(ethertype
);
876 ti_ui_
->protocolLabel
->setEnabled(ipv4_proto
);
877 ti_ui_
->protocolLineEdit
->setEnabled(ipv4_proto
);
878 ti_ui_
->ipVersionLabel
->setEnabled(ip_address
);
879 if (encapsulation
== WTAP_ENCAP_RAW_IP4
) {
880 ti_ui_
->ipVersionComboBox
->setEnabled(false);
881 ti_ui_
->ipVersionComboBox
->setCurrentIndex(0);
882 } else if (encapsulation
== WTAP_ENCAP_RAW_IP6
) {
883 ti_ui_
->ipVersionComboBox
->setEnabled(false);
884 ti_ui_
->ipVersionComboBox
->setCurrentIndex(1);
886 ti_ui_
->ipVersionComboBox
->setEnabled(ip_address
);
888 ti_ui_
->sourceAddressLabel
->setEnabled(ip_address
);
889 ti_ui_
->sourceAddressLineEdit
->setEnabled(ip_address
);
890 ti_ui_
->destinationAddressLabel
->setEnabled(ip_address
);
891 ti_ui_
->destinationAddressLineEdit
->setEnabled(ip_address
);
892 ti_ui_
->sourcePortLabel
->setEnabled(port
);
893 ti_ui_
->sourcePortLineEdit
->setEnabled(port
);
894 ti_ui_
->destinationPortLabel
->setEnabled(port
);
895 ti_ui_
->destinationPortLineEdit
->setEnabled(port
);
896 ti_ui_
->tagLabel
->setEnabled(sctp_tag
);
897 ti_ui_
->tagLineEdit
->setEnabled(sctp_tag
);
898 ti_ui_
->ppiLabel
->setEnabled(sctp_ppi
);
899 ti_ui_
->ppiLineEdit
->setEnabled(sctp_ppi
);
900 ti_ui_
->payloadLabel
->setEnabled(export_pdu
);
901 ti_ui_
->dissectorComboBox
->setEnabled(export_pdu
);
903 if (ti_ui_
->noDummyButton
->isEnabled() && !(encap_buttons
->checkedButton()->isEnabled())) {
904 ti_ui_
->noDummyButton
->toggle();
908 void ImportTextDialog::on_encapComboBox_currentIndexChanged(int index
)
910 QVariant val
= ti_ui_
->encapComboBox
->itemData(index
);
911 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
912 if (val
.canConvert
<int>())
914 if (val
!= QVariant::Invalid
)
917 import_info_
.encapsulation
= val
.toUInt();
919 import_info_
.encapsulation
= WTAP_ENCAP_UNKNOWN
;
922 enableHeaderWidgets(import_info_
.encapsulation
);
925 void ImportTextDialog::encap_buttonsToggled(QAbstractButton
*button _U_
, bool checked
)
927 if (checked
) enableHeaderWidgets(import_info_
.encapsulation
);
930 void ImportTextDialog::on_ipVersionComboBox_currentIndexChanged(int index
)
932 import_info_
.ipv6
= (index
== 1) ? 1 : 0;
933 on_sourceAddressLineEdit_textChanged(ti_ui_
->sourceAddressLineEdit
->text());
934 on_destinationAddressLineEdit_textChanged(ti_ui_
->destinationAddressLineEdit
->text());
937 void ImportTextDialog::check_line_edit(SyntaxLineEdit
*le
, bool &ok_enabled
, const QString
&num_str
, int base
, unsigned max_val
, bool is_short
, unsigned *val_ptr
) {
939 SyntaxLineEdit::SyntaxState syntax_state
= SyntaxLineEdit::Empty
;
945 if (num_str
.length() < 1) {
949 *val_ptr
= num_str
.toUShort(&conv_ok
, base
);
951 *val_ptr
= (unsigned)num_str
.toULong(&conv_ok
, base
);
953 if (conv_ok
&& *val_ptr
<= max_val
) {
954 syntax_state
= SyntaxLineEdit::Valid
;
956 syntax_state
= SyntaxLineEdit::Invalid
;
960 le
->setSyntaxState(syntax_state
);
961 updateImportButtonState();
964 void ImportTextDialog::checkAddress(SyntaxLineEdit
*le
, bool &ok_enabled
, const QString
&addr_str
, ws_in4_addr
*val_ptr
) {
966 SyntaxLineEdit::SyntaxState syntax_state
= SyntaxLineEdit::Empty
;
972 if (addr_str
.length() < 1) {
975 conv_ok
= ws_inet_pton4(addr_str
.toUtf8().data(), (ws_in4_addr
*)val_ptr
);
977 syntax_state
= SyntaxLineEdit::Valid
;
979 syntax_state
= SyntaxLineEdit::Invalid
;
983 le
->setSyntaxState(syntax_state
);
984 updateImportButtonState();
987 void ImportTextDialog::checkIPv6Address(SyntaxLineEdit
*le
, bool &ok_enabled
, const QString
&addr_str
, ws_in6_addr
*val_ptr
) {
989 SyntaxLineEdit::SyntaxState syntax_state
= SyntaxLineEdit::Empty
;
995 if (addr_str
.length() < 1) {
996 *val_ptr
= {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
998 conv_ok
= ws_inet_pton6(addr_str
.toUtf8().data(), (ws_in6_addr
*)val_ptr
);
1000 syntax_state
= SyntaxLineEdit::Valid
;
1002 syntax_state
= SyntaxLineEdit::Invalid
;
1006 le
->setSyntaxState(syntax_state
);
1007 updateImportButtonState();
1010 void ImportTextDialog::on_ethertypeLineEdit_textChanged(const QString
ðertype_str
)
1012 check_line_edit(ti_ui_
->ethertypeLineEdit
, ether_type_ok_
, ethertype_str
, 16, 0xffff, true, &import_info_
.pid
);
1015 void ImportTextDialog::on_protocolLineEdit_textChanged(const QString
&protocol_str
)
1017 check_line_edit(ti_ui_
->protocolLineEdit
, proto_ok_
, protocol_str
, 10, 0xff, true, &import_info_
.protocol
);
1020 void ImportTextDialog::on_sourceAddressLineEdit_textChanged(const QString
&source_addr_str
)
1022 if (ti_ui_
->ipVersionComboBox
->currentIndex() == 1) {
1023 checkIPv6Address(ti_ui_
->sourceAddressLineEdit
, source_addr_ok_
, source_addr_str
, &import_info_
.ip_src_addr
.ipv6
);
1025 checkAddress(ti_ui_
->sourceAddressLineEdit
, source_addr_ok_
, source_addr_str
, &import_info_
.ip_src_addr
.ipv4
);
1029 void ImportTextDialog::on_destinationAddressLineEdit_textChanged(const QString
&destination_addr_str
)
1031 if (ti_ui_
->ipVersionComboBox
->currentIndex() == 1) {
1032 checkIPv6Address(ti_ui_
->destinationAddressLineEdit
, dest_addr_ok_
, destination_addr_str
, &import_info_
.ip_dest_addr
.ipv6
);
1034 checkAddress(ti_ui_
->destinationAddressLineEdit
, dest_addr_ok_
, destination_addr_str
, &import_info_
.ip_dest_addr
.ipv4
);
1038 void ImportTextDialog::on_sourcePortLineEdit_textChanged(const QString
&source_port_str
)
1040 check_line_edit(ti_ui_
->sourcePortLineEdit
, source_port_ok_
, source_port_str
, 10, 0xffff, true, &import_info_
.src_port
);
1043 void ImportTextDialog::on_destinationPortLineEdit_textChanged(const QString
&destination_port_str
)
1045 check_line_edit(ti_ui_
->destinationPortLineEdit
, dest_port_ok_
, destination_port_str
, 10, 0xffff, true, &import_info_
.dst_port
);
1048 void ImportTextDialog::on_tagLineEdit_textChanged(const QString
&tag_str
)
1050 check_line_edit(ti_ui_
->tagLineEdit
, tag_ok_
, tag_str
, 10, 0xffffffff, false, &import_info_
.tag
);
1053 void ImportTextDialog::on_ppiLineEdit_textChanged(const QString
&ppi_str
)
1055 check_line_edit(ti_ui_
->ppiLineEdit
, ppi_ok_
, ppi_str
, 10, 0xffffffff, false, &import_info_
.ppi
);
1058 /*******************************************************************************
1062 void ImportTextDialog::on_maxLengthLineEdit_textChanged(const QString
&max_frame_len_str
)
1064 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
);
1067 void ImportTextDialog::on_buttonBox_helpRequested()
1069 mainApp
->helpTopicAction(HELP_IMPORT_DIALOG
);