diff options
Diffstat (limited to '')
-rw-r--r-- | ui/qt/follow_stream_dialog.cpp | 620 |
1 files changed, 328 insertions, 292 deletions
diff --git a/ui/qt/follow_stream_dialog.cpp b/ui/qt/follow_stream_dialog.cpp index dbae1604..532d37f5 100644 --- a/ui/qt/follow_stream_dialog.cpp +++ b/ui/qt/follow_stream_dialog.cpp @@ -29,6 +29,7 @@ #include "wsutil/file_util.h" #include "wsutil/str_util.h" +#include "wsutil/filesystem.h" #include "ws_symbol_export.h" @@ -53,8 +54,6 @@ // - Instead of calling QMessageBox, display the error message in the text // box and disable the appropriate controls. // - Add a progress bar and connect captureCaptureUpdateContinue to it -// - User's Guide documents the "Raw" type as "same as ASCII, but saving binary -// data". However it currently displays hex-encoded data. // Matches SplashOverlay. static int info_update_freq_ = 100; @@ -63,7 +62,7 @@ static int info_update_freq_ = 100; static QMutex loop_break_mutex; // Indicates that a Follow Stream is currently running -static gboolean isReadRunning; +static bool isReadRunning; Q_DECLARE_METATYPE(bytes_show_type) @@ -72,7 +71,6 @@ FollowStreamDialog::FollowStreamDialog(QWidget &parent, CaptureFile &cf, int pro ui(new Ui::FollowStreamDialog), b_find_(NULL), follower_(NULL), - truncated_(false), client_buffer_count_(0), server_buffer_count_(0), client_packet_count_(0), @@ -101,6 +99,8 @@ FollowStreamDialog::FollowStreamDialog(QWidget &parent, CaptureFile &cf, int pro follow_info_.show_stream = BOTH_HOSTS; follow_info_.substream_id = SUBSTREAM_UNUSED; + nstime_set_zero(&last_ts_); + ui->teStreamContent->installEventFilter(this); connect(ui->leFind, SIGNAL(useRegexFind(bool)), this, SLOT(useRegexFind(bool))); @@ -118,27 +118,46 @@ FollowStreamDialog::FollowStreamDialog(QWidget &parent, CaptureFile &cf, int pro cbcs->setCurrentIndex(cbcs->findData(recent.gui_follow_show)); cbcs->blockSignals(false); + ui->deltaComboBox->setCurrentIndex(recent.gui_follow_delta); + b_filter_out_ = ui->buttonBox->addButton(tr("Filter Out This Stream"), QDialogButtonBox::ActionRole); - connect(b_filter_out_, SIGNAL(clicked()), this, SLOT(filterOut())); + connect(b_filter_out_, &QPushButton::clicked, this, &FollowStreamDialog::filterOut); b_print_ = ui->buttonBox->addButton(tr("Print"), QDialogButtonBox::ActionRole); - connect(b_print_, SIGNAL(clicked()), this, SLOT(printStream())); + connect(b_print_, &QPushButton::clicked, this, &FollowStreamDialog::printStream); b_save_ = ui->buttonBox->addButton(tr("Save as…"), QDialogButtonBox::ActionRole); - connect(b_save_, SIGNAL(clicked()), this, SLOT(saveAs())); + connect(b_save_, &QPushButton::clicked, this, &FollowStreamDialog::saveAs); b_back_ = ui->buttonBox->addButton(tr("Back"), QDialogButtonBox::ActionRole); - connect(b_back_, SIGNAL(clicked()), this, SLOT(backButton())); + connect(b_back_, &QPushButton::clicked, this, &FollowStreamDialog::backButton); ProgressFrame::addToButtonBox(ui->buttonBox, &parent); - connect(ui->buttonBox, SIGNAL(helpRequested()), this, SLOT(helpButton())); - connect(ui->teStreamContent, SIGNAL(mouseMovedToTextCursorPosition(int)), - this, SLOT(fillHintLabel(int))); - connect(ui->teStreamContent, SIGNAL(mouseClickedOnTextCursorPosition(int)), - this, SLOT(goToPacketForTextPos(int))); + connect(ui->cbDirections, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), + this, &FollowStreamDialog::cbDirectionsCurrentIndexChanged); + connect(ui->cbCharset, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), + this, &FollowStreamDialog::cbCharsetCurrentIndexChanged); + connect(ui->deltaComboBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), + this, &FollowStreamDialog::deltaComboBoxCurrentIndexChanged); + + connect(ui->streamNumberSpinBox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), + this, &FollowStreamDialog::streamNumberSpinBoxValueChanged); + connect(ui->subStreamNumberSpinBox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), + this, &FollowStreamDialog::subStreamNumberSpinBoxValueChanged); + + connect(ui->buttonBox, &QDialogButtonBox::helpRequested, this, &FollowStreamDialog::helpButton); + connect(ui->teStreamContent, &FollowStreamText::mouseMovedToPacket, + this, &FollowStreamDialog::fillHintLabel); + connect(ui->teStreamContent, &FollowStreamText::mouseClickedOnPacket, + this, &FollowStreamDialog::goToPacketForTextPos); + + connect(ui->bFind, &QPushButton::clicked, this, &FollowStreamDialog::bFindClicked); + connect(ui->leFind, &FindLineEdit::returnPressed, this, &FollowStreamDialog::leFindReturnPressed); + + connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &FollowStreamDialog::buttonBoxRejected); - fillHintLabel(-1); + fillHintLabel(); } FollowStreamDialog::~FollowStreamDialog() @@ -169,29 +188,37 @@ void FollowStreamDialog::printStream() #endif } -void FollowStreamDialog::fillHintLabel(int text_pos) +void FollowStreamDialog::fillHintLabel(int pkt) { QString hint; - int pkt = -1; - if (text_pos >= 0) { - QMap<int, guint32>::iterator it = text_pos_to_packet_.upperBound(text_pos); - if (it != text_pos_to_packet_.end()) { - pkt = it.value(); + bool is_logray = strcmp(get_configuration_namespace(), "Logray") == 0; + + if (is_logray) { + if (pkt > 0) { + hint = QString(tr("Event %1. ")).arg(pkt); } - } - if (pkt > 0) { - hint = QString(tr("Packet %1. ")).arg(pkt); - } + hint += tr("%Ln <span style=\"color: %1; background-color:%2\">reads</span>, ", "", client_packet_count_) + .arg(ColorUtils::fromColorT(prefs.st_client_fg).name(), + ColorUtils::fromColorT(prefs.st_client_bg).name()) + + tr("%Ln <span style=\"color: %1; background-color:%2\">writes</span>, ", "", server_packet_count_) + .arg(ColorUtils::fromColorT(prefs.st_server_fg).name(), + ColorUtils::fromColorT(prefs.st_server_bg).name()) + + tr("%Ln turn(s).", "", turns_); + } else { + if (pkt > 0) { + hint = QString(tr("Packet %1. ")).arg(pkt); + } - hint += tr("%Ln <span style=\"color: %1; background-color:%2\">client</span> pkt(s), ", "", client_packet_count_) - .arg(ColorUtils::fromColorT(prefs.st_client_fg).name()) - .arg(ColorUtils::fromColorT(prefs.st_client_bg).name()) - + tr("%Ln <span style=\"color: %1; background-color:%2\">server</span> pkt(s), ", "", server_packet_count_) - .arg(ColorUtils::fromColorT(prefs.st_server_fg).name()) - .arg(ColorUtils::fromColorT(prefs.st_server_bg).name()) - + tr("%Ln turn(s).", "", turns_); + hint += tr("%Ln <span style=\"color: %1; background-color:%2\">client</span> pkt(s), ", "", client_packet_count_) + .arg(ColorUtils::fromColorT(prefs.st_client_fg).name(), + ColorUtils::fromColorT(prefs.st_client_bg).name()) + + tr("%Ln <span style=\"color: %1; background-color:%2\">server</span> pkt(s), ", "", server_packet_count_) + .arg(ColorUtils::fromColorT(prefs.st_server_fg).name(), + ColorUtils::fromColorT(prefs.st_server_bg).name()) + + tr("%Ln turn(s).", "", turns_); + } if (pkt > 0) { hint.append(QString(tr(" Click to select."))); @@ -202,20 +229,12 @@ void FollowStreamDialog::fillHintLabel(int text_pos) ui->hintLabel->setText(hint); } -void FollowStreamDialog::goToPacketForTextPos(int text_pos) +void FollowStreamDialog::goToPacketForTextPos(int pkt) { - int pkt = -1; if (file_closed_) { return; } - if (text_pos >= 0) { - QMap<int, guint32>::iterator it = text_pos_to_packet_.upperBound(text_pos); - if (it != text_pos_to_packet_.end()) { - pkt = it.value(); - } - } - if (pkt > 0) { emit goToPacket(pkt); } @@ -255,20 +274,40 @@ void FollowStreamDialog::useRegexFind(bool use_regex) ui->lFind->setText(tr("Find:")); } +// This only calls itself with go_back false, so never recurses more than once. +// NOLINTNEXTLINE(misc-no-recursion) void FollowStreamDialog::findText(bool go_back) { if (ui->leFind->text().isEmpty()) return; bool found; + + QTextDocument::FindFlags options; + if (ui->caseCheckBox->isChecked()) { + options |= QTextDocument::FindCaseSensitively; + } if (use_regex_find_) { #if (QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)) + // https://bugreports.qt.io/browse/QTBUG-88721 + // QPlainTextEdit::find() searches case-insensitively unless + // QTextDocument::FindCaseSensitively is explicitly given. + // This *does* apply to QRegularExpression (overriding + // CaseInsensitiveOption), but not QRegExp. + // + // QRegularExpression and QRegExp do not support Perl's /i, but + // the former at least does support the mode modifiers (?i) and + // (?-i), which can override QTextDocument::FindCaseSensitively. + // + // To make matters worse, while the QTextDocument::find() documentation + // is correct, QPlainTextEdit::find() claims that QRegularExpression + // works like QRegExp, which is incorrect. QRegularExpression regex(ui->leFind->text(), QRegularExpression::UseUnicodePropertiesOption); #else - QRegExp regex(ui->leFind->text()); + QRegExp regex(ui->leFind->text(), (options & QTextDocument::FindCaseSensitively) ? Qt::CaseSensitive : Qt::CaseInsensitive); #endif - found = ui->teStreamContent->find(regex); + found = ui->teStreamContent->find(regex, options); } else { - found = ui->teStreamContent->find(ui->leFind->text()); + found = ui->teStreamContent->find(ui->leFind->text(), options); } if (found) { @@ -288,15 +327,17 @@ void FollowStreamDialog::saveAs() QFile file(file_name); if (!file.open(QIODevice::WriteOnly)) { - open_failure_alert_box(file_name.toUtf8().constData(), errno, TRUE); + open_failure_alert_box(file_name.toUtf8().constData(), errno, true); return; } + // XXX: What if truncated_ is true? We should save the entire stream. // Unconditionally save data as UTF-8 (even if data is decoded otherwise). QByteArray bytes = ui->teStreamContent->toPlainText().toUtf8(); if (recent.gui_follow_show == SHOW_RAW) { // The "Raw" format is currently displayed as hex data and needs to be - // converted to binary data. + // converted to binary data. fromHex() skips over non hex characters + // including line breaks, which is what we want. bytes = QByteArray::fromHex(bytes); } @@ -337,12 +378,12 @@ void FollowStreamDialog::close() // previous_filter if 'Close' (passed in follow() method) // filter_out_filter_ if 'Filter Out This Stream' (built by appending !current_stream to previous_filter) // leave filter alone if window closed. (current stream) - emit updateFilter(output_filter_, TRUE); + emit updateFilter(output_filter_, true); WiresharkDialog::close(); } -void FollowStreamDialog::on_cbDirections_currentIndexChanged(int idx) +void FollowStreamDialog::cbDirectionsCurrentIndexChanged(int idx) { switch(idx) { @@ -362,24 +403,43 @@ void FollowStreamDialog::on_cbDirections_currentIndexChanged(int idx) readStream(); } -void FollowStreamDialog::on_cbCharset_currentIndexChanged(int idx) +void FollowStreamDialog::cbCharsetCurrentIndexChanged(int idx) { if (idx < 0) return; recent.gui_follow_show = ui->cbCharset->currentData().value<bytes_show_type>(); + + switch (recent.gui_follow_show) { + case SHOW_EBCDIC: + case SHOW_ASCII: + case SHOW_CODEC: + ui->deltaComboBox->setEnabled(true); + break; + default: + ui->deltaComboBox->setEnabled(false); + } + + readStream(); +} + +void FollowStreamDialog::deltaComboBoxCurrentIndexChanged(int idx) +{ + if (idx < 0) return; + recent.gui_follow_delta = static_cast<follow_delta_type>(ui->deltaComboBox->currentIndex()); + readStream(); } -void FollowStreamDialog::on_bFind_clicked() +void FollowStreamDialog::bFindClicked() { findText(); } -void FollowStreamDialog::on_leFind_returnPressed() +void FollowStreamDialog::leFindReturnPressed() { findText(); } -void FollowStreamDialog::on_streamNumberSpinBox_valueChanged(int stream_num) +void FollowStreamDialog::streamNumberSpinBoxValueChanged(int stream_num) { if (file_closed_) return; @@ -388,7 +448,7 @@ void FollowStreamDialog::on_streamNumberSpinBox_valueChanged(int stream_num) sub_stream_num = ui->subStreamNumberSpinBox->value(); ui->subStreamNumberSpinBox->blockSignals(false); - gboolean ok; + bool ok; if (ui->subStreamNumberSpinBox->isVisible()) { /* We need to find a suitable sub stream for the new stream */ follow_sub_stream_id_func sub_stream_func; @@ -399,7 +459,7 @@ void FollowStreamDialog::on_streamNumberSpinBox_valueChanged(int stream_num) return; } - guint sub_stream_num_new = static_cast<guint>(sub_stream_num); + unsigned sub_stream_num_new = static_cast<unsigned>(sub_stream_num); if (sub_stream_num < 0) { // Stream ID 0 should always exist as it is used for control messages. // XXX: That is only guaranteed for HTTP2. For example, in QUIC, @@ -411,14 +471,14 @@ void FollowStreamDialog::on_streamNumberSpinBox_valueChanged(int stream_num) // follow? Right now the substream spinbox is left active and // the user can change the value to no effect. sub_stream_num_new = 0; - ok = TRUE; + ok = true; } else { - ok = sub_stream_func(static_cast<guint>(stream_num), sub_stream_num_new, FALSE, &sub_stream_num_new); + ok = sub_stream_func(static_cast<unsigned>(stream_num), sub_stream_num_new, false, &sub_stream_num_new); if (!ok) { - ok = sub_stream_func(static_cast<guint>(stream_num), sub_stream_num_new, TRUE, &sub_stream_num_new); + ok = sub_stream_func(static_cast<unsigned>(stream_num), sub_stream_num_new, true, &sub_stream_num_new); } } - sub_stream_num = static_cast<gint>(sub_stream_num_new); + sub_stream_num = static_cast<int>(sub_stream_num_new); } else { /* XXX: For HTTP and TLS, we use the TCP stream index, and really should * return false if the TCP stream doesn't have HTTP or TLS. (Or we could @@ -434,7 +494,7 @@ void FollowStreamDialog::on_streamNumberSpinBox_valueChanged(int stream_num) } -void FollowStreamDialog::on_subStreamNumberSpinBox_valueChanged(int sub_stream_num) +void FollowStreamDialog::subStreamNumberSpinBoxValueChanged(int sub_stream_num) { if (file_closed_) return; @@ -451,20 +511,20 @@ void FollowStreamDialog::on_subStreamNumberSpinBox_valueChanged(int sub_stream_n return; } - guint sub_stream_num_new = static_cast<guint>(sub_stream_num); - gboolean ok; + unsigned sub_stream_num_new = static_cast<unsigned>(sub_stream_num); + bool ok; /* previous_sub_stream_num_ is a hack to track which buttons was pressed without event handling */ if (sub_stream_num < 0) { // Stream ID 0 should always exist as it is used for control messages. // XXX: That is only guaranteed for HTTP2, see above. sub_stream_num_new = 0; - ok = TRUE; + ok = true; } else if (previous_sub_stream_num_ < sub_stream_num) { - ok = sub_stream_func(static_cast<guint>(stream_num), sub_stream_num_new, FALSE, &sub_stream_num_new); + ok = sub_stream_func(static_cast<unsigned>(stream_num), sub_stream_num_new, false, &sub_stream_num_new); } else { - ok = sub_stream_func(static_cast<guint>(stream_num), sub_stream_num_new, TRUE, &sub_stream_num_new); + ok = sub_stream_func(static_cast<unsigned>(stream_num), sub_stream_num_new, true, &sub_stream_num_new); } - sub_stream_num = static_cast<gint>(sub_stream_num_new); + sub_stream_num = static_cast<int>(sub_stream_num_new); if (ok) { follow(previous_filter_, true, stream_num, sub_stream_num); @@ -472,7 +532,7 @@ void FollowStreamDialog::on_subStreamNumberSpinBox_valueChanged(int sub_stream_n } } -void FollowStreamDialog::on_buttonBox_rejected() +void FollowStreamDialog::buttonBoxRejected() { // Ignore the close button if FollowStreamDialog::close() is running. if (terminating_) @@ -490,60 +550,34 @@ void FollowStreamDialog::removeStreamControls() ui->subStreamNumberSpinBox->setVisible(false); } -void FollowStreamDialog::resetStream() +void FollowStreamDialog::resetStream(void *tap_data) { - GList *cur; - follow_record_t *follow_record; - - filter_out_filter_.clear(); - text_pos_to_packet_.clear(); - if (!data_out_filename_.isEmpty()) { - ws_unlink(data_out_filename_.toUtf8().constData()); - } - for (cur = follow_info_.payload; cur; cur = gxx_list_next(cur)) { - follow_record = gxx_list_data(follow_record_t *, cur); - if (follow_record->data) { - g_byte_array_free(follow_record->data, TRUE); - } - g_free(follow_record); - } - g_list_free(follow_info_.payload); - - //Only TCP stream uses fragments - for (cur = follow_info_.fragments[0]; cur; cur = gxx_list_next(cur)) { - follow_record = gxx_list_data(follow_record_t *, cur); - if (follow_record->data) { - g_byte_array_free(follow_record->data, TRUE); - } - g_free(follow_record); - } - follow_info_.fragments[0] = Q_NULLPTR; - for (cur = follow_info_.fragments[1]; cur; cur = gxx_list_next(cur)) { - follow_record = gxx_list_data(follow_record_t *, cur); - if (follow_record->data) { - g_byte_array_free(follow_record->data, TRUE); - } - g_free(follow_record); - } - follow_info_.fragments[1] = Q_NULLPTR; + follow_info_t *follow_info = static_cast<follow_info_t*>(tap_data); + follow_reset_stream(follow_info); + // If we ever draw the text while tapping (instead of only after + // the tap finishes), reset the GUI here too. +} - free_address(&follow_info_.client_ip); - free_address(&follow_info_.server_ip); - follow_info_.payload = Q_NULLPTR; - follow_info_.client_port = 0; +void FollowStreamDialog::resetStream() +{ + FollowStreamDialog::resetStream(&follow_info_); } -frs_return_t -FollowStreamDialog::readStream() +void FollowStreamDialog::readStream() { // interrupt any reading already running loop_break_mutex.lock(); - isReadRunning = FALSE; + isReadRunning = false; loop_break_mutex.unlock(); + double scroll_ratio = 0.0; + int doc_length = ui->teStreamContent->verticalScrollBar()->maximum() + ui->teStreamContent->verticalScrollBar()->pageStep(); + if (doc_length > 0) { + scroll_ratio = static_cast<double>(ui->teStreamContent->verticalScrollBar()->value()) / doc_length; + } + ui->teStreamContent->clear(); - text_pos_to_packet_.clear(); switch (recent.gui_follow_show) { case SHOW_CARRAY: @@ -561,9 +595,6 @@ FollowStreamDialog::readStream() ui->teStreamContent->setWordWrapMode(QTextOption::WrapAnywhere); } - truncated_ = false; - frs_return_t ret; - client_buffer_count_ = 0; server_buffer_count_ = 0; client_packet_count_ = 0; @@ -571,16 +602,16 @@ FollowStreamDialog::readStream() last_packet_ = 0; turns_ = 0; - if (follower_) { - ret = readFollowStream(); - } else { - ret = (frs_return_t)0; + if (!follower_) { ws_assert_not_reached(); } + readFollowStream(); + ui->teStreamContent->moveCursor(QTextCursor::Start); - return ret; + doc_length = ui->teStreamContent->verticalScrollBar()->maximum() + ui->teStreamContent->verticalScrollBar()->pageStep(); + ui->teStreamContent->verticalScrollBar()->setValue(doc_length * scroll_ratio); } void @@ -589,49 +620,9 @@ FollowStreamDialog::followStream() readStream(); } -const int FollowStreamDialog::max_document_length_ = 500 * 1000 * 1000; // Just a guess -void FollowStreamDialog::addText(QString text, gboolean is_from_server, guint32 packet_num, gboolean colorize) +void FollowStreamDialog::addText(QString text, bool is_from_server, uint32_t packet_num, bool colorize) { - if (truncated_) { - return; - } - - int char_count = ui->teStreamContent->document()->characterCount(); - if (char_count + text.length() > max_document_length_) { - text.truncate(max_document_length_ - char_count); - truncated_ = true; - } - - setUpdatesEnabled(false); - int cur_pos = ui->teStreamContent->verticalScrollBar()->value(); - ui->teStreamContent->moveCursor(QTextCursor::End); - - QTextCharFormat tcf = ui->teStreamContent->currentCharFormat(); - if (!colorize) { - tcf.setBackground(palette().window().color()); - tcf.setForeground(palette().windowText().color()); - } else if (is_from_server) { - tcf.setForeground(ColorUtils::fromColorT(prefs.st_server_fg)); - tcf.setBackground(ColorUtils::fromColorT(prefs.st_server_bg)); - } else { - tcf.setForeground(ColorUtils::fromColorT(prefs.st_client_fg)); - tcf.setBackground(ColorUtils::fromColorT(prefs.st_client_bg)); - } - ui->teStreamContent->setCurrentCharFormat(tcf); - - ui->teStreamContent->insertPlainText(text); - text_pos_to_packet_[ui->teStreamContent->textCursor().anchor()] = packet_num; - - if (truncated_) { - tcf = ui->teStreamContent->currentCharFormat(); - tcf.setBackground(palette().window().color()); - tcf.setForeground(palette().windowText().color()); - ui->teStreamContent->insertPlainText("\n" + tr("[Stream output truncated]")); - ui->teStreamContent->moveCursor(QTextCursor::End); - } else { - ui->teStreamContent->verticalScrollBar()->setValue(cur_pos); - } - setUpdatesEnabled(true); + ui->teStreamContent->addText(std::move(text), is_from_server, packet_num, colorize); } // The following keyboard shortcuts should work (although @@ -682,33 +673,64 @@ void FollowStreamDialog::keyPressEvent(QKeyEvent *event) QDialog::keyPressEvent(event); } -static inline void sanitize_buffer(char *buffer, size_t nchars) { - for (size_t i = 0; i < nchars; i++) { - if (buffer[i] == '\n' || buffer[i] == '\r' || buffer[i] == '\t') +// Replaces non printable ASCII characters in the QByteArray with . +// Causes buffer to detach/deep copy *only* if a character has to be +// replaced. +static inline void sanitize_buffer(QByteArray &buffer, size_t nchars) { +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + for (int i = 0; i < (int)nchars; i++) { +#else + for (qsizetype i = 0; i < (qsizetype)nchars; i++) { +#endif + if (buffer.at(i) == '\n' || buffer.at(i) == '\r' || buffer.at(i) == '\t') continue; - if (! g_ascii_isprint((guchar)buffer[i])) { + if (! g_ascii_isprint((unsigned char)buffer.at(i))) { buffer[i] = '.'; } } } -frs_return_t -FollowStreamDialog::showBuffer(char *buffer, size_t nchars, gboolean is_from_server, guint32 packet_num, - nstime_t abs_ts, guint32 *global_pos) +void FollowStreamDialog::showBuffer(QByteArray &buffer, size_t nchars, bool is_from_server, uint32_t packet_num, + nstime_t abs_ts, uint32_t *global_pos) { - gchar initbuf[256]; - guint32 current_pos; - static const gchar hexchars[16] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; + char initbuf[256]; + uint32_t current_pos; + static const char hexchars[16] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; + bool show_delta = false; + + if (last_packet_ == 0) { + last_from_server_ = is_from_server; + } else { + if (recent.gui_follow_delta == FOLLOW_DELTA_ALL || + (recent.gui_follow_delta == FOLLOW_DELTA_TURN && last_from_server_ != is_from_server)) { + show_delta = true; + } + } + + double delta = 0.0; + if (!nstime_is_zero(&abs_ts)) { + // packet-tcp.c and possibly other dissectors can return a zero abs_ts when + // a fragment is missing. + nstime_t delta_ts; + nstime_delta(&delta_ts, &abs_ts, &last_ts_); + delta = nstime_to_sec(&delta_ts); + last_ts_ = abs_ts; + } switch (recent.gui_follow_show) { case SHOW_EBCDIC: { /* If our native arch is ASCII, call: */ - EBCDIC_to_ASCII((guint8*)buffer, (guint) nchars); + EBCDIC_to_ASCII((uint8_t*)buffer.data(), (unsigned) nchars); + if (show_delta) { + ui->teStreamContent->addDeltaTime(delta); + } + if (show_delta || last_from_server_ != is_from_server) { + addText("\n", is_from_server, packet_num); + } sanitize_buffer(buffer, nchars); - QByteArray ba = QByteArray(buffer, (int)nchars); - addText(ba, is_from_server, packet_num); + addText(buffer, is_from_server, packet_num); break; } @@ -717,32 +739,41 @@ FollowStreamDialog::showBuffer(char *buffer, size_t nchars, gboolean is_from_ser /* If our native arch is EBCDIC, call: * ASCII_TO_EBCDIC(buffer, nchars); */ + if (show_delta) { + ui->teStreamContent->addDeltaTime(delta); + } + if (show_delta || last_from_server_ != is_from_server) { + addText("\n", is_from_server, packet_num); + } sanitize_buffer(buffer, nchars); - QByteArray ba = QByteArray(buffer, (int)nchars); - addText(ba, is_from_server, packet_num); + addText(buffer, is_from_server, packet_num); break; } case SHOW_CODEC: { + if (show_delta) { + ui->teStreamContent->addDeltaTime(delta); + } + if (show_delta || last_from_server_ != is_from_server) { + addText("\n", is_from_server, packet_num); + } // This assumes that multibyte characters don't span packets in the // stream. To handle that case properly (which might occur with fixed // block sizes, e.g. transferring over TFTP, we would need to create // two stateful QTextDecoders, one for each direction, presumably in // on_cbCharset_currentIndexChanged() QTextCodec *codec = QTextCodec::codecForName(ui->cbCharset->currentText().toUtf8()); - QByteArray ba = QByteArray(buffer, (int)nchars); - QString decoded = codec->toUnicode(ba); - addText(decoded, is_from_server, packet_num); + addText(codec->toUnicode(buffer), is_from_server, packet_num); break; } case SHOW_HEXDUMP: current_pos = 0; while (current_pos < nchars) { - gchar hexbuf[256]; + char hexbuf[256]; int i; - gchar *cur = hexbuf, *ascii_start; + char *cur = hexbuf, *ascii_start; /* is_from_server indentation : put 4 spaces at the * beginning of the string */ @@ -756,9 +787,9 @@ FollowStreamDialog::showBuffer(char *buffer, size_t nchars, gboolean is_from_ser ascii_start = cur + 49 + 2; for (i = 0; i < 16 && current_pos + i < nchars; i++) { *cur++ = - hexchars[(buffer[current_pos + i] & 0xf0) >> 4]; + hexchars[(buffer.at(current_pos + i) & 0xf0) >> 4]; *cur++ = - hexchars[buffer[current_pos + i] & 0x0f]; + hexchars[buffer.at(current_pos + i) & 0x0f]; *cur++ = ' '; if (i == 7) *cur++ = ' '; @@ -770,8 +801,8 @@ FollowStreamDialog::showBuffer(char *buffer, size_t nchars, gboolean is_from_ser /* Now dump bytes as text */ for (i = 0; i < 16 && current_pos + i < nchars; i++) { *cur++ = - (g_ascii_isprint((guchar)buffer[current_pos + i]) ? - buffer[current_pos + i] : '.'); + (g_ascii_isprint((unsigned char)buffer.at(current_pos + i)) ? + buffer.at(current_pos + i) : '.'); if (i == 7) { *cur++ = ' '; } @@ -794,7 +825,7 @@ FollowStreamDialog::showBuffer(char *buffer, size_t nchars, gboolean is_from_ser addText(initbuf, is_from_server, packet_num); while (current_pos < nchars) { - gchar hexbuf[256]; + char hexbuf[256]; int i, cur; cur = 0; @@ -803,9 +834,9 @@ FollowStreamDialog::showBuffer(char *buffer, size_t nchars, gboolean is_from_ser hexbuf[cur++] = '0'; hexbuf[cur++] = 'x'; hexbuf[cur++] = - hexchars[(buffer[current_pos + i] & 0xf0) >> 4]; + hexchars[(buffer.at(current_pos + i) & 0xf0) >> 4]; hexbuf[cur++] = - hexchars[buffer[current_pos + i] & 0x0f]; + hexchars[buffer.at(current_pos + i) & 0x0f]; /* Delimit array entries with a comma */ if (current_pos + i + 1 < nchars) @@ -849,15 +880,13 @@ FollowStreamDialog::showBuffer(char *buffer, size_t nchars, gboolean is_from_ser " - peer: 0\n" " host: %1\n" " port: %2\n") - .arg(hostname0) - .arg(port0), false, 0); + .arg(hostname0, port0), false, 0); addText(QString( " - peer: 1\n" " host: %1\n" " port: %2\n") - .arg(hostname1) - .arg(port1), true, 0); + .arg(hostname1, port1), true, 0); wmem_free(NULL, port0); wmem_free(NULL, port1); @@ -879,7 +908,7 @@ FollowStreamDialog::showBuffer(char *buffer, size_t nchars, gboolean is_from_ser } while (current_pos < nchars) { int len = current_pos + base64_raw_len < nchars ? base64_raw_len : (int) nchars - current_pos; - QByteArray base64_data(&buffer[current_pos], len); + QByteArray base64_data(&buffer.constData()[current_pos], len); /* XXX: GCC 12.1 has a bogus stringop-overread warning using the Qt * conversions from QByteArray to QString at -O2 and higher due to @@ -896,15 +925,13 @@ DIAG_ON(stringop-overread) current_pos += len; (*global_pos) += len; } - addText(yaml_text, is_from_server, packet_num); + addText(std::move(yaml_text), is_from_server, packet_num); break; } case SHOW_RAW: { - QByteArray ba = QByteArray(buffer, (int)nchars).toHex(); - ba += '\n'; - addText(ba, is_from_server, packet_num); + addText(buffer.toHex() + '\n', is_from_server, packet_num); break; } @@ -915,10 +942,6 @@ DIAG_ON(stringop-overread) ws_assert_not_reached(); } - if (last_packet_ == 0) { - last_from_server_ = is_from_server; - } - if (packet_num != last_packet_) { last_packet_ = packet_num; if (is_from_server) { @@ -931,11 +954,9 @@ DIAG_ON(stringop-overread) turns_++; } } - - return FRS_OK; } -bool FollowStreamDialog::follow(QString previous_filter, bool use_stream_index, guint stream_num, guint sub_stream_num) +bool FollowStreamDialog::follow(QString previous_filter, bool use_stream_index, unsigned stream_num, unsigned sub_stream_num) { QString follow_filter; const char *hostname0 = NULL, *hostname1 = NULL; @@ -943,12 +964,10 @@ bool FollowStreamDialog::follow(QString previous_filter, bool use_stream_index, QString server_to_client_string; QString client_to_server_string; QString both_directions_string; - gboolean is_follower = FALSE; + bool is_follower = false; int stream_count; follow_stream_count_func stream_count_func = NULL; - resetStream(); - if (file_closed_) { QMessageBox::warning(this, tr("No capture file."), tr("Please make sure you have a capture file opened.")); @@ -969,8 +988,6 @@ bool FollowStreamDialog::follow(QString previous_filter, bool use_stream_index, } } - follow_reset_stream(&follow_info_); - /* Create a new filter that matches all packets in the TCP stream, and set the display filter entry accordingly */ if (use_stream_index) { @@ -993,7 +1010,7 @@ bool FollowStreamDialog::follow(QString previous_filter, bool use_stream_index, /* append the negation */ if (!previous_filter.isEmpty()) { filter_out_filter_ = QString("%1 and !(%2)") - .arg(previous_filter).arg(follow_filter); + .arg(previous_filter, follow_filter); } else { @@ -1005,7 +1022,8 @@ bool FollowStreamDialog::follow(QString previous_filter, bool use_stream_index, /* data will be passed via tap callback*/ if (!registerTapListener(get_follow_tap_string(follower_), &follow_info_, follow_filter.toUtf8().constData(), - 0, NULL, get_follow_tap_handler(follower_), NULL)) { + 0, FollowStreamDialog::resetStream, + get_follow_tap_handler(follower_), NULL)) { return false; } @@ -1026,9 +1044,9 @@ bool FollowStreamDialog::follow(QString previous_filter, bool use_stream_index, follow_sub_stream_id_func sub_stream_func; sub_stream_func = get_follow_sub_stream_id_func(follower_); if (sub_stream_func != NULL) { - guint substream_max_id = 0; - sub_stream_func(static_cast<guint>(stream_num), G_MAXINT32, TRUE, &substream_max_id); - stream_count = static_cast<gint>(substream_max_id); + unsigned substream_max_id = 0; + sub_stream_func(static_cast<unsigned>(stream_num), INT32_MAX, true, &substream_max_id); + stream_count = static_cast<int>(substream_max_id); ui->subStreamNumberSpinBox->blockSignals(true); ui->subStreamNumberSpinBox->setEnabled(true); ui->subStreamNumberSpinBox->setMaximum(stream_count); @@ -1054,43 +1072,83 @@ bool FollowStreamDialog::follow(QString previous_filter, bool use_stream_index, /* Run the display filter so it goes in effect - even if it's the same as the previous display filter. */ - emit updateFilter(follow_filter, TRUE); + /* XXX: This forces a cf_filter_packets() - but if a rescan (or something else + * that sets cf->read_lock) is in progress, this will queue the filter + * and return immediately. It will also cause a rescan in progress to + * stop and restart with the new filter. That also applies to this rescan; + * changing the main display filter (from the main window, or from, e.g. + * another FollowStreamDialog) will cause this to restart and reset the + * tap. + * + * Other tapping dialogs call cf_retap_packets (which retaps but doesn't + * set the main display filter, freeze the packet list, etc.), which + * has somewhat different behavior when another dialog tries to retap, + * but also results in the taps being reset mid tap. + * + * Either way, we should be event driven and listening for CaptureEvents + * instead of drawing after this returns. (Or like other taps, draw + * periodically in a callback, provided that can be done without causing + * issues with changing the Decode As type.) + */ + emit updateFilter(follow_filter, true); removeTapListeners(); - hostname0 = address_to_name(&follow_info_.client_ip); - hostname1 = address_to_name(&follow_info_.server_ip); - - port0 = get_follow_port_to_display(follower_)(NULL, follow_info_.client_port); - port1 = get_follow_port_to_display(follower_)(NULL, follow_info_.server_port); - - server_to_client_string = - QString("%1:%2 %3 %4:%5 (%6)") - .arg(hostname0).arg(port0) - .arg(UTF8_RIGHTWARDS_ARROW) - .arg(hostname1).arg(port1) - .arg(gchar_free_to_qstring(format_size( - follow_info_.bytes_written[0], - FORMAT_SIZE_UNIT_BYTES, FORMAT_SIZE_PREFIX_SI))); - - client_to_server_string = - QString("%1:%2 %3 %4:%5 (%6)") - .arg(hostname1).arg(port1) - .arg(UTF8_RIGHTWARDS_ARROW) - .arg(hostname0).arg(port0) - .arg(gchar_free_to_qstring(format_size( - follow_info_.bytes_written[1], - FORMAT_SIZE_UNIT_BYTES, FORMAT_SIZE_PREFIX_SI))); - - wmem_free(NULL, port0); - wmem_free(NULL, port1); - - both_directions_string = tr("Entire conversation (%1)") - .arg(gchar_free_to_qstring(format_size( - follow_info_.bytes_written[0] + follow_info_.bytes_written[1], - FORMAT_SIZE_UNIT_BYTES, FORMAT_SIZE_PREFIX_SI))); - setWindowSubtitle(tr("Follow %1 Stream (%2)").arg(proto_get_protocol_short_name(find_protocol_by_id(get_follow_proto_id(follower_)))) - .arg(follow_filter)); + bool is_logray = strcmp(get_configuration_namespace(), "Logray") == 0; + + if (is_logray) { + server_to_client_string = + tr("Read activity(%6)") + .arg(gchar_free_to_qstring(format_size( + follow_info_.bytes_written[0], + FORMAT_SIZE_UNIT_BYTES, FORMAT_SIZE_PREFIX_SI))); + + client_to_server_string = + tr("Write activity(%6)") + .arg(gchar_free_to_qstring(format_size( + follow_info_.bytes_written[1], + FORMAT_SIZE_UNIT_BYTES, FORMAT_SIZE_PREFIX_SI))); + + both_directions_string = tr("Entire I/O activity (%1)") + .arg(gchar_free_to_qstring(format_size( + follow_info_.bytes_written[0] + follow_info_.bytes_written[1], + FORMAT_SIZE_UNIT_BYTES, FORMAT_SIZE_PREFIX_SI))); + } else { + hostname0 = address_to_name(&follow_info_.client_ip); + hostname1 = address_to_name(&follow_info_.server_ip); + + port0 = get_follow_port_to_display(follower_)(NULL, follow_info_.client_port); + port1 = get_follow_port_to_display(follower_)(NULL, follow_info_.server_port); + + server_to_client_string = + QString("%1:%2 %3 %4:%5 (%6)") + .arg(hostname0, port0) + .arg(UTF8_RIGHTWARDS_ARROW) + .arg(hostname1, port1) + .arg(gchar_free_to_qstring(format_size( + follow_info_.bytes_written[0], + FORMAT_SIZE_UNIT_BYTES, FORMAT_SIZE_PREFIX_SI))); + + client_to_server_string = + QString("%1:%2 %3 %4:%5 (%6)") + .arg(hostname1, port1) + .arg(UTF8_RIGHTWARDS_ARROW) + .arg(hostname0, port0) + .arg(gchar_free_to_qstring(format_size( + follow_info_.bytes_written[1], + FORMAT_SIZE_UNIT_BYTES, FORMAT_SIZE_PREFIX_SI))); + + wmem_free(NULL, port0); + wmem_free(NULL, port1); + + both_directions_string = tr("Entire conversation (%1)") + .arg(gchar_free_to_qstring(format_size( + follow_info_.bytes_written[0] + follow_info_.bytes_written[1], + FORMAT_SIZE_UNIT_BYTES, FORMAT_SIZE_PREFIX_SI))); + } + + setWindowSubtitle(tr("Follow %1 Stream (%2)").arg(proto_get_protocol_short_name(find_protocol_by_id(get_follow_proto_id(follower_))), + follow_filter)); ui->cbDirections->blockSignals(true); ui->cbDirections->clear(); @@ -1100,13 +1158,13 @@ bool FollowStreamDialog::follow(QString previous_filter, bool use_stream_index, ui->cbDirections->blockSignals(false); followStream(); - fillHintLabel(-1); + fillHintLabel(); updateWidgets(false); endRetapPackets(); if (prefs.restore_filter_after_following_stream) { - emit updateFilter(previous_filter_, TRUE); + emit updateFilter(previous_filter_, true); } return true; @@ -1120,74 +1178,53 @@ void FollowStreamDialog::captureFileClosed() WiresharkDialog::captureFileClosed(); } -/* - * XXX - the routine pointed to by "print_line_fcn_p" doesn't get handed lines, - * it gets handed bufferfuls. That's fine for "follow_write_raw()" - * and "follow_add_to_gtk_text()", but, as "follow_print_text()" calls - * the "print_line()" routine from "print.c", and as that routine might - * genuinely expect to be handed a line (if, for example, it's using - * some OS or desktop environment's printing API, and that API expects - * to be handed lines), "follow_print_text()" should probably accumulate - * lines in a buffer and hand them "print_line()". (If there's a - * complete line in a buffer - i.e., there's nothing of the line in - * the previous buffer or the next buffer - it can just hand that to - * "print_line()" after filtering out non-printables, as an - * optimization.) - * - * This might or might not be the reason why C arrays display - * correctly but get extra blank lines very other line when printed. - */ -frs_return_t -FollowStreamDialog::readFollowStream() +void FollowStreamDialog::readFollowStream() { - guint32 global_client_pos = 0, global_server_pos = 0; - guint32 *global_pos; - gboolean skip; + uint32_t global_client_pos = 0, global_server_pos = 0; + uint32_t *global_pos; + bool skip; GList* cur; - frs_return_t frs_return; follow_record_t *follow_record; QElapsedTimer elapsed_timer; + QByteArray buffer; elapsed_timer.start(); loop_break_mutex.lock(); - isReadRunning = TRUE; + isReadRunning = true; loop_break_mutex.unlock(); for (cur = g_list_last(follow_info_.payload); cur; cur = g_list_previous(cur)) { if (dialogClosed() || !isReadRunning) break; follow_record = (follow_record_t *)cur->data; - skip = FALSE; + skip = false; if (!follow_record->is_server) { global_pos = &global_client_pos; if (follow_info_.show_stream == FROM_SERVER) { - skip = TRUE; + skip = true; } } else { global_pos = &global_server_pos; if (follow_info_.show_stream == FROM_CLIENT) { - skip = TRUE; + skip = true; } } - QByteArray buffer; if (!skip) { - // We want a deep copy. - buffer.clear(); - buffer.append((const char *) follow_record->data->data, - follow_record->data->len); - frs_return = showBuffer( - buffer.data(), - follow_record->data->len, - follow_record->is_server, - follow_record->packet_num, - follow_record->abs_ts, - global_pos); - if (frs_return == FRS_PRINT_ERROR) - return frs_return; + // This will only detach / deep copy if the buffer data is + // modified. Try to avoid doing that as much as possible + // (and avoid new memory allocations that have to be freed). + buffer.setRawData((char*)follow_record->data->data, follow_record->data->len); + showBuffer( + buffer, + follow_record->data->len, + follow_record->is_server, + follow_record->packet_num, + follow_record->abs_ts, + global_pos); if (elapsed_timer.elapsed() > info_update_freq_) { - fillHintLabel(ui->teStreamContent->textCursor().position()); + fillHintLabel(ui->teStreamContent->currentPacket()); mainApp->processEvents(); elapsed_timer.start(); } @@ -1195,8 +1232,7 @@ FollowStreamDialog::readFollowStream() } loop_break_mutex.lock(); - isReadRunning = FALSE; + isReadRunning = false; loop_break_mutex.unlock(); - - return FRS_OK; } + |