update epan/dissectors/pidl/drsuapi/drsuapi.idl from samba
[wireshark-sm.git] / ui / qt / import_text_dialog.cpp
blob8501dff9e4c1d78c2b14a57059b3fe140ddca9ce
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
8 */
10 #include "config.h"
12 #include "import_text_dialog.h"
14 #include "wiretap/wtap.h"
15 #include "wiretap/pcap-encap.h"
17 #include "ui/text_import_scanner.h"
18 #include "ui/util.h"
19 #include "ui/alert_box.h"
20 #include "ui/help_url.h"
21 #include "ui/capture_globals.h"
23 #include "file.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"
35 #include <QDebug>
36 #include <QJsonDocument>
37 #include <QJsonObject>
38 #include <QFile>
40 #define HINT_BEGIN "<small><i>"
41 #define HINT_END "</i></small>"
42 #define HTML_LT "&lt;"
43 #define HTML_GT "&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) :
51 QDialog(parent),
52 ti_ui_(new Ui::ImportTextDialog),
53 import_info_(),
54 file_ok_(false),
55 timestamp_format_ok_(true),
56 regex_ok_(false),
57 re_has_dir_(false),
58 in_indication_ok_(false),
59 out_indication_ok_(false),
60 re_has_time_(false),
61 ether_type_ok_(true),
62 proto_ok_(true),
63 source_addr_ok_(true),
64 dest_addr_ok_(true),
65 source_port_ok_(true),
66 dest_port_ok_(true),
67 tag_ok_(true),
68 ppi_ok_(true),
69 payload_ok_(true),
70 max_len_ok_(true)
72 int encap;
73 int i;
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);
86 #ifdef Q_OS_MAC
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);
97 #endif
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*/
119 struct {
120 const char* name;
121 enum data_encoding id;
122 } encodings[] = {
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)) {
152 const char *name;
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();
181 delete ti_ui_;
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()) {
190 return;
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()) {
207 return;
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()
220 loadSettingsFile();
222 // Hex Dump
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());
251 // Import info
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);
278 } else {
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());
292 } else {
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";
312 } else {
313 settings["mode"] = "regex";
316 // Hex Dump
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";
323 } else {
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();
334 switch (encoding) {
335 case ENCODING_PLAIN_HEX:
336 settings["regex.encoding"] = "plainHex";
337 break;
338 case ENCODING_PLAIN_OCT:
339 settings["regex.encoding"] = "plainOct";
340 break;
341 case ENCODING_PLAIN_BIN:
342 settings["regex.encoding"] = "plainBin";
343 break;
344 case ENCODING_BASE64:
345 settings["regex.encoding"] = "base64";
346 break;
348 } else {
349 settings["regex.encoding"] = "plainHex";
351 settings["regex.inIndication"] = ti_ui_->dirInIndicationLineEdit->text();
352 settings["regex.outIndication"] = ti_ui_->dirOutIndicationLineEdit->text();
354 // Import info
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();
360 } else {
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";
378 } else {
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();
396 saveSettingsFile();
399 QString &ImportTextDialog::capfileName() {
400 return capfile_name_;
403 int ImportTextDialog::exec() {
404 QVariant encap_val;
405 char* tmp;
406 GError* gerror = NULL;
407 int err;
408 char *err_info;
409 wtap_dump_params params;
410 int file_type_subtype;
411 QString interface_name;
413 QDialog::exec();
415 if (result() != QDialog::Accepted) {
416 return result();
419 /* from here on the cleanup labels are used to free allocated resources in
420 * reverse order.
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);
443 goto cleanup_mode;
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 :
450 OFFSET_NONE;
451 break;
452 case TEXT_IMPORT_REGEX:
453 import_info_.regex.import_text_GMappedFile = g_mapped_file_new(import_info_.import_text_filename, true, &gerror);
454 if (gerror) {
455 open_failure_alert_box(import_info_.import_text_filename, gerror->code, false);
456 g_error_free(gerror);
457 setResult(QDialog::Rejected);
458 goto cleanup_mode;
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);
462 g_free(tmp);
463 if (re_has_dir_) {
464 import_info_.regex.in_indication = qstring_strdup(ti_ui_->dirInIndicationLineEdit->text());
465 import_info_.regex.out_indication = qstring_strdup(ti_ui_->dirOutIndicationLineEdit->text());
466 } else {
467 import_info_.regex.in_indication = NULL;
468 import_info_.regex.out_indication = NULL;
470 break;
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(&params, 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();
509 } else {
510 interface_name = ti_ui_->interfaceLineEdit->placeholderText();
512 text_import_pre_open(&params, 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, &params, &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);
521 goto cleanup_wtap;
524 err = text_import(&import_info_);
526 if (err != 0) {
527 failure_alert_box("Import failed");
528 setResult(QDialog::Rejected);
529 goto cleanup;
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);
537 cleanup_wtap:
538 /* g_free checks for null */
539 wtap_free_idb_info(params.idb_inf);
540 wtap_dump_params_cleanup(&params);
541 g_free(tmp);
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);
546 break;
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);
552 break;
554 cleanup_mode:
555 g_free((void *) import_info_.import_text_filename);
556 g_free((void *) import_info_.timestamp_format);
557 return result();
560 /*******************************************************************************
561 * General Input
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_))
579 ) || (
580 import_info_.mode == TEXT_IMPORT_HEXDUMP
584 import_button_->setEnabled(true);
585 } else {
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)) {
595 file_ok_ = true;
596 text_file.close();
597 } else {
598 file_ok_ = false;
600 updateImportButtonState();
603 void ImportTextDialog::on_textFileBrowseButton_clicked()
605 QString open_dir;
606 if (ti_ui_->textFileLineEdit->text().length() > 0) {
607 open_dir = ti_ui_->textFileLineEdit->text();
608 } else {
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%";
620 int idx = 0;
621 bool ret = false;
623 /* XXX: Temporary(?) hack to allow ISO format time, a checkbox is
624 * probably better */
625 if (time_format == "ISO") {
626 ret = true;
627 } else while ((idx = static_cast<int>(time_format.indexOf("%", idx))) != -1) {
628 idx++;
629 if ((idx == time_format.size()) || !valid_code.contains(time_format[idx])) {
630 return false;
632 idx++;
633 ret = true;
635 return ret;
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;
643 struct tm *cur_tm;
644 struct tm fallback;
645 char time_str[100];
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));
658 cur_tm = &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;
664 else {
665 ti_ui_->timestampExampleLabel->setText(tr(HINT_BEGIN "(Wrong date format)" HINT_END));
666 timestamp_format_ok_ = false;
668 } else {
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) {
676 switch (index) {
677 default:
678 ti_ui_->modeTabWidget->setCurrentIndex(0);
679 /* fall through */
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);
686 break;
687 case 1:
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_);
692 break;
694 on_textFileLineEdit_textChanged(ti_ui_->textFileLineEdit->text());
697 /*******************************************************************************
698 * Hex Dump Tab
701 void ImportTextDialog::on_noOffsetButton_toggled(bool checked)
703 if (checked) {
704 ti_ui_->noOffsetLabel->setText("(only one packet will be created)");
705 } else {
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 /*******************************************************************************
721 * Regex Tab
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);
733 if (gerror) {
734 regex_ok_ = false;
735 ti_ui_->regexHintLabel->setText(QString(gerror->message).toHtmlEscaped());
736 g_error_free(gerror);
737 } else {
738 regex_ok_ = 0 <= g_regex_get_string_number(regex, "data");
739 if (regex_ok_)
740 ti_ui_->regexHintLabel->setText(default_regex_hint);
741 else
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>())
767 #else
768 if (val != QVariant::Invalid)
769 #endif
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);
776 break;
777 case ENCODING_PLAIN_BIN:
778 ti_ui_->encodingRegexExample->setText(HINT_BEGIN "(?" HTML_LT "data" HTML_GT "[0-1\\s]+)" HINT_END);
779 break;
780 case ENCODING_PLAIN_OCT:
781 ti_ui_->encodingRegexExample->setText(HINT_BEGIN "(?" HTML_LT "data" HTML_GT "[0-8:\\s]+)" HINT_END);
782 break;
783 case ENCODING_BASE64:
784 ti_ui_->encodingRegexExample->setText(HINT_BEGIN "(?" HTML_LT "data" HTML_GT "[0-9a-zA-Z+\\/\\s]+=*)" HINT_END);
785 break;
786 default:
787 ti_ui_->encodingRegexExample->setText(HINT_BEGIN HTML_LT "no example" HTML_GT HINT_END);
788 break;
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;
816 bool port = 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()) {
826 ethertype = true;
827 on_ethertypeLineEdit_textChanged(ti_ui_->ethertypeLineEdit->text());
829 enable_ip_buttons = true;
832 if (enable_ip_buttons) {
833 if (ti_ui_->ipv4Button->isChecked()) {
834 ipv4_proto = true;
835 ip_address = true;
836 on_protocolLineEdit_textChanged(ti_ui_->protocolLineEdit->text());
837 } else if (ti_ui_->udpButton->isChecked() || ti_ui_->tcpButton->isChecked()) {
838 ip_address = true;
839 port = true;
840 on_sourcePortLineEdit_textChanged(ti_ui_->sourcePortLineEdit->text());
841 on_destinationPortLineEdit_textChanged(ti_ui_->destinationPortLineEdit->text());
842 } else if (ti_ui_->sctpButton->isChecked()) {
843 ip_address = true;
844 port = true;
845 sctp_tag = true;
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()) {
851 ip_address = true;
852 port = true;
853 sctp_ppi = true;
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()) {
862 export_pdu = true;
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);
885 } else {
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>())
913 #else
914 if (val != QVariant::Invalid)
915 #endif
917 import_info_.encapsulation = val.toUInt();
918 } else {
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) {
938 bool conv_ok;
939 SyntaxLineEdit::SyntaxState syntax_state = SyntaxLineEdit::Empty;
941 if (!le || !val_ptr)
942 return;
944 ok_enabled = true;
945 if (num_str.length() < 1) {
946 *val_ptr = 0;
947 } else {
948 if (is_short) {
949 *val_ptr = num_str.toUShort(&conv_ok, base);
950 } else {
951 *val_ptr = (unsigned)num_str.toULong(&conv_ok, base);
953 if (conv_ok && *val_ptr <= max_val) {
954 syntax_state = SyntaxLineEdit::Valid;
955 } else {
956 syntax_state = SyntaxLineEdit::Invalid;
957 ok_enabled = false;
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) {
965 bool conv_ok;
966 SyntaxLineEdit::SyntaxState syntax_state = SyntaxLineEdit::Empty;
968 if (!le || !val_ptr)
969 return;
971 ok_enabled = true;
972 if (addr_str.length() < 1) {
973 *val_ptr = 0;
974 } else {
975 conv_ok = ws_inet_pton4(addr_str.toUtf8().data(), (ws_in4_addr*)val_ptr);
976 if (conv_ok) {
977 syntax_state= SyntaxLineEdit::Valid;
978 } else {
979 syntax_state = SyntaxLineEdit::Invalid;
980 ok_enabled = false;
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) {
988 bool conv_ok;
989 SyntaxLineEdit::SyntaxState syntax_state = SyntaxLineEdit::Empty;
991 if (!le || !val_ptr)
992 return;
994 ok_enabled = true;
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}};
997 } else {
998 conv_ok = ws_inet_pton6(addr_str.toUtf8().data(), (ws_in6_addr*)val_ptr);
999 if (conv_ok) {
1000 syntax_state= SyntaxLineEdit::Valid;
1001 } else {
1002 syntax_state = SyntaxLineEdit::Invalid;
1003 ok_enabled = false;
1006 le->setSyntaxState(syntax_state);
1007 updateImportButtonState();
1010 void ImportTextDialog::on_ethertypeLineEdit_textChanged(const QString &ethertype_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);
1024 } else {
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);
1033 } else {
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 /*******************************************************************************
1059 * Footer
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);