1 /* capture_file_properties_dialog.cpp
5 * Wireshark - Network traffic analyzer
6 * By Gerald Combs <gerald@wireshark.org>
7 * Copyright 1998 Gerald Combs
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 #include "capture_file_properties_dialog.h"
25 #include <ui_capture_file_properties_dialog.h>
29 #include "wsutil/str_util.h"
30 #include "ws_version_info.h"
32 #include "qt_ui_utils.h"
33 #include "wireshark_application.h"
35 #include <QPushButton>
37 #include <QTextStream>
41 // - Add formats (HTML, plain text, YAML)?
43 CaptureFilePropertiesDialog::CaptureFilePropertiesDialog(QWidget &parent, CaptureFile &capture_file) :
44 WiresharkDialog(parent, capture_file),
45 ui(new Ui::CaptureFilePropertiesDialog)
48 loadGeometry(parent.width() * 2 / 3, parent.height());
50 ui->detailsTextEdit->setAcceptRichText(true);
52 QPushButton *button = ui->buttonBox->button(QDialogButtonBox::Reset);
54 button->setText(tr("Refresh"));
57 button = ui->buttonBox->button(QDialogButtonBox::Apply);
59 button->setText(tr("Copy To Clipboard"));
62 button = ui->buttonBox->button(QDialogButtonBox::Save);
64 button->setText(tr("Save Comments"));
67 setWindowSubtitle(tr("Capture File Properties"));
68 QTimer::singleShot(0, this, SLOT(updateWidgets()));
75 CaptureFilePropertiesDialog::~CaptureFilePropertiesDialog()
82 void CaptureFilePropertiesDialog::updateWidgets()
84 QPushButton *refresh_bt = ui->buttonBox->button(QDialogButtonBox::Reset);
85 QPushButton *save_bt = ui->buttonBox->button(QDialogButtonBox::Save);
87 if (file_closed_ || !cap_file_.isValid()) {
89 refresh_bt->setEnabled(false);
91 ui->commentsTextEdit->setReadOnly(true);
93 save_bt->setEnabled(false);
95 WiresharkDialog::updateWidgets();
99 bool enable = wtap_dump_can_write(cap_file_.capFile()->linktypes, WTAP_COMMENT_PER_SECTION);
100 save_bt->setEnabled(enable);
101 ui->commentsTextEdit->setEnabled(enable);
104 ui->commentsTextEdit->setText(cf_read_shb_comment(cap_file_.capFile()));
106 WiresharkDialog::updateWidgets();
109 static const QString section_tmpl_ = "<p><strong>%1</strong></p>\n";
110 static const QString para_tmpl_ = "<p>%1</p>\n";
112 QString CaptureFilePropertiesDialog::summaryToHtml()
114 summary_tally summary;
115 double seconds = 0.0;
116 double disp_seconds = 0.0;
117 double marked_seconds = 0.0;
119 memset(&summary, 0, sizeof(summary_tally));
121 QString table_begin, table_end;
122 QString table_row_begin, table_ul_row_begin, table_row_end;
123 QString table_vheader_tmpl, table_hheader20_tmpl, table_hheader25_tmpl;
124 QString table_data_tmpl;
126 table_begin = "<p><table>\n";
127 table_end = "</table></p>\n";
128 table_row_begin = "<tr>\n";
129 table_ul_row_begin = "<tr style=\"border-bottom: 1px solid gray;\">\n";
130 table_row_end = "</tr>\n";
131 table_vheader_tmpl = "<td width=\"20%\">%1:</td>"; // <th align="left"> looked odd
132 table_hheader20_tmpl = "<td width=\"20%\"><u>%1</u></td>";
133 table_hheader25_tmpl = "<td width=\"25%\"><u>%1</u></td>";
134 table_data_tmpl = "<td>%1</td>";
137 /* initial computations */
138 summary_fill_in(cap_file_.capFile(), &summary);
140 summary_fill_in_capture(cap_file_.capFile(), &global_capture_opts, &summary);
144 seconds = summary.stop_time - summary.start_time;
145 disp_seconds = summary.filtered_stop - summary.filtered_start;
146 marked_seconds = summary.marked_stop - summary.marked_start;
149 QTextStream out(&summary_str);
150 QString unknown = tr("Unknown");
153 out << section_tmpl_.arg(tr("File"));
156 out << table_row_begin
157 << table_vheader_tmpl.arg(tr("Name"))
158 << table_data_tmpl.arg(summary.filename)
161 out << table_row_begin
162 << table_vheader_tmpl.arg(tr("Length"))
163 << table_data_tmpl.arg(file_size_to_qstring(summary.file_length))
166 QString format_str = wtap_file_type_subtype_string(summary.file_type);
167 if (summary.iscompressed) {
168 format_str.append(tr(" (gzip compressed)"));
170 out << table_row_begin
171 << table_vheader_tmpl.arg(tr("Format"))
172 << table_data_tmpl.arg(format_str)
176 if (summary.file_encap_type == WTAP_ENCAP_PER_PACKET) {
177 for (guint i = 0; i < summary.packet_encap_types->len; i++)
179 encaps_str = QString(wtap_encap_string(g_array_index(summary.packet_encap_types, int, i)));
182 encaps_str = QString(wtap_encap_string(summary.file_encap_type));
184 out << table_row_begin
185 << table_vheader_tmpl.arg(tr("Encapsulation"))
186 << table_data_tmpl.arg(encaps_str)
189 if (summary.has_snap) {
190 out << table_row_begin
191 << table_vheader_tmpl.arg(tr("Snapshot length"))
192 << table_data_tmpl.arg(summary.snap)
199 if (summary.packet_count_ts == summary.packet_count &&
200 summary.packet_count >= 1)
202 out << section_tmpl_.arg(tr("Time"));
206 out << table_row_begin
207 << table_vheader_tmpl.arg(tr("First packet"))
208 << table_data_tmpl.arg(time_t_to_qstring((time_t)summary.start_time))
212 out << table_row_begin
213 << table_vheader_tmpl.arg(tr("Last packet"))
214 << table_data_tmpl.arg(time_t_to_qstring((time_t)summary.stop_time))
217 // elapsed seconds (capture duration)
218 if (summary.packet_count_ts >= 2)
220 /* elapsed seconds */
222 unsigned int elapsed_time = (unsigned int)summary.elapsed_time;
223 if (elapsed_time/86400)
225 elapsed_str = QString("%1 days ").arg(elapsed_time / 86400);
228 elapsed_str += QString("%1:%2:%3")
229 .arg(elapsed_time % 86400 / 3600, 2, 10, QChar('0'))
230 .arg(elapsed_time % 3600 / 60, 2, 10, QChar('0'))
231 .arg(elapsed_time % 60, 2, 10, QChar('0'));
232 out << table_row_begin
233 << table_vheader_tmpl.arg(tr("Elapsed"))
234 << table_data_tmpl.arg(elapsed_str)
242 out << section_tmpl_.arg(tr("Capture"));
245 wtap_block_t shb_inf = wtap_file_get_shb(cap_file_.capFile()->wth);
248 if (shb_inf != NULL) {
249 QString capture_hardware(unknown);
250 if (wtap_block_get_string_option_value(shb_inf, OPT_SHB_HARDWARE, &str) == WTAP_OPTTYPE_SUCCESS) {
251 if (str != NULL && str[0] != '\0') {
252 capture_hardware = str;
256 out << table_row_begin
257 << table_vheader_tmpl.arg(tr("Hardware"))
258 << table_data_tmpl.arg(capture_hardware)
261 QString capture_os(unknown);
262 if (wtap_block_get_string_option_value(shb_inf, OPT_SHB_OS, &str) == WTAP_OPTTYPE_SUCCESS) {
263 if (str != NULL && str[0] != '\0') {
267 out << table_row_begin
268 << table_vheader_tmpl.arg(tr("OS"))
269 << table_data_tmpl.arg(capture_os)
272 QString capture_app(unknown);
273 if (wtap_block_get_string_option_value(shb_inf, OPT_SHB_USERAPPL, &str) == WTAP_OPTTYPE_SUCCESS) {
274 if (str != NULL && str[0] != '\0') {
278 out << table_row_begin
279 << table_vheader_tmpl.arg(tr("Application"))
280 << table_data_tmpl.arg(capture_app)
286 // capture interfaces info
287 if (summary.ifaces->len > 0) {
288 out << section_tmpl_.arg(tr("Interfaces"));
291 out << table_ul_row_begin
292 << table_hheader20_tmpl.arg(tr("Interface"))
293 << table_hheader20_tmpl.arg(tr("Dropped packets"))
294 << table_hheader20_tmpl.arg(tr("Capture filter"))
295 << table_hheader20_tmpl.arg(tr("Link type"))
296 << table_hheader20_tmpl.arg(tr("Packet size limit"))
300 for (guint i = 0; i < summary.ifaces->len; i++) {
302 iface = g_array_index(summary.ifaces, iface_options, i);
305 QString interface_name(unknown);
307 interface_name = iface.descr;
308 } else if (iface.name)
310 interface_name = iface.name;
314 QString interface_drops(unknown);
315 if (iface.drops_known) {
316 interface_drops = QString("%1 (%2 %)").arg(iface.drops).arg(QString::number(
317 /* MSVC cannot convert from unsigned __int64 to float, so first convert to signed __int64 */
318 summary.packet_count ?(100.0 * (gint64)iface.drops)/summary.packet_count : 0.0f, 'g', 1));
322 QString interface_cfilter(unknown);
323 if (iface.cfilter && iface.cfilter[0] != '\0') {
324 interface_cfilter = iface.cfilter;
325 } else if (iface.name) {
326 interface_cfilter = QString(tr("none"));
329 QString interface_snaplen = QString(tr("%1 bytes").arg(iface.snap));
331 out << table_row_begin
332 << table_data_tmpl.arg(interface_name)
333 << table_data_tmpl.arg(interface_drops)
334 << table_data_tmpl.arg(interface_cfilter)
335 << table_data_tmpl.arg(wtap_encap_string(iface.encap_type))
336 << table_data_tmpl.arg(interface_snaplen)
340 if (summary.ifaces->len > 0) {
344 // Statistics Section
345 out << section_tmpl_.arg(tr("Statistics"));
348 out << table_ul_row_begin
349 << table_hheader25_tmpl.arg(tr("Measurement"))
350 << table_hheader25_tmpl.arg(tr("Captured"))
351 << table_hheader25_tmpl.arg(tr("Displayed"))
352 << table_hheader25_tmpl.arg(tr("Marked"))
355 // TRANSLATOR Abbreviation for "not applicable"
356 QString n_a = tr("N/A");
357 QString captured_str, displayed_str, marked_str;
360 displayed_str = marked_str = n_a;
361 if (summary.filtered_count > 0 && summary.packet_count > 0) {
362 displayed_str = QString("%1 (%2%)")
363 .arg(summary.filtered_count)
364 .arg(100.0 * summary.filtered_count / summary.packet_count, 1, 'f', 1);
366 if (summary.packet_count > 0 && summary.marked_count > 0) {
367 marked_str = QString("%1 (%2%)")
368 .arg(summary.marked_count)
369 .arg(100.0 * summary.marked_count / summary.packet_count, 1, 'f', 1);
372 out << table_row_begin
373 << table_data_tmpl.arg(tr("Packets"))
374 << table_data_tmpl.arg(summary.packet_count)
375 << table_data_tmpl.arg(displayed_str)
376 << table_data_tmpl.arg(marked_str)
379 // Time between first and last
380 captured_str = displayed_str = marked_str = n_a;
382 captured_str = QString("%1").arg(seconds, 1, 'f', 3);
384 if (disp_seconds > 0) {
385 displayed_str = QString("%1").arg(disp_seconds, 1, 'f', 3);
387 if (marked_seconds > 0) {
388 marked_str = QString("%1").arg(marked_seconds, 1, 'f', 3);
390 out << table_row_begin
391 << table_data_tmpl.arg(tr("Time span, s"))
392 << table_data_tmpl.arg(captured_str)
393 << table_data_tmpl.arg(displayed_str)
394 << table_data_tmpl.arg(marked_str)
397 // Average packets per second
398 captured_str = displayed_str = marked_str = n_a;
400 captured_str = QString("%1").arg(summary.packet_count/seconds, 1, 'f', 1);
402 if (disp_seconds > 0) {
403 displayed_str = QString("%1").arg(summary.filtered_count/disp_seconds, 1, 'f', 1);
405 if (marked_seconds > 0) {
406 marked_str = QString("%1").arg(summary.marked_count/marked_seconds, 1, 'f', 1);
408 out << table_row_begin
409 << table_data_tmpl.arg(tr("Average pps"))
410 << table_data_tmpl.arg(captured_str)
411 << table_data_tmpl.arg(displayed_str)
412 << table_data_tmpl.arg(marked_str)
415 // Average packets per second
416 captured_str = displayed_str = marked_str = n_a;
417 if (summary.packet_count > 0) {
418 captured_str = QString("%1").arg(summary.bytes/summary.packet_count + 0.5, 1, 'f', 1);
420 if (summary.filtered_count > 0) {
421 displayed_str = QString("%1").arg(summary.filtered_bytes/summary.filtered_count + 0.5, 1, 'f', 1);
423 if (summary.marked_count > 0) {
424 marked_str = QString("%1").arg(summary.marked_bytes/summary.marked_count + 0.5, 1, 'f', 1);
426 out << table_row_begin
427 << table_data_tmpl.arg(tr("Average packet size, B"))
428 << table_data_tmpl.arg(captured_str)
429 << table_data_tmpl.arg(displayed_str)
430 << table_data_tmpl.arg(marked_str)
434 displayed_str = marked_str = "0";
435 if (summary.bytes > 0 && summary.filtered_bytes > 0) {
436 displayed_str = QString("%1 (%2%)")
437 .arg(summary.filtered_bytes)
438 .arg(100.0 * summary.filtered_bytes / summary.bytes, 1, 'f', 1);
440 if (summary.bytes > 0 && summary.marked_bytes > 0) {
441 marked_str = QString("%1 (%2%)")
442 .arg(summary.marked_bytes)
443 .arg(100.0 * summary.marked_bytes / summary.bytes, 1, 'f', 1);
445 out << table_row_begin
446 << table_data_tmpl.arg(tr("Bytes"))
447 << table_data_tmpl.arg(summary.bytes)
448 << table_data_tmpl.arg(displayed_str)
449 << table_data_tmpl.arg(marked_str)
453 captured_str = displayed_str = marked_str = n_a;
456 gchar_free_to_qstring(format_size(summary.bytes / seconds, format_size_unit_none|format_size_prefix_si));
458 if (disp_seconds > 0) {
460 gchar_free_to_qstring(format_size(summary.filtered_bytes / disp_seconds, format_size_unit_none|format_size_prefix_si));
462 if (marked_seconds > 0) {
464 gchar_free_to_qstring(format_size(summary.marked_bytes / marked_seconds, format_size_unit_none|format_size_prefix_si));
466 out << table_row_begin
467 << table_data_tmpl.arg(tr("Average bytes/s"))
468 << table_data_tmpl.arg(captured_str)
469 << table_data_tmpl.arg(displayed_str)
470 << table_data_tmpl.arg(marked_str)
474 captured_str = displayed_str = marked_str = n_a;
477 gchar_free_to_qstring(format_size(summary.bytes * 8 / seconds, format_size_unit_none|format_size_prefix_si));
479 if (disp_seconds > 0) {
481 gchar_free_to_qstring(format_size(summary.filtered_bytes * 8 / disp_seconds, format_size_unit_none|format_size_prefix_si));
483 if (marked_seconds > 0) {
485 gchar_free_to_qstring(format_size(summary.marked_bytes * 8 / marked_seconds, format_size_unit_none|format_size_prefix_si));
487 out << table_row_begin
488 << table_data_tmpl.arg(tr("Average bits/s"))
489 << table_data_tmpl.arg(captured_str)
490 << table_data_tmpl.arg(displayed_str)
491 << table_data_tmpl.arg(marked_str)
499 void CaptureFilePropertiesDialog::fillDetails()
501 if (!cap_file_.isValid()) return;
503 ui->detailsTextEdit->clear();
505 QTextCursor cursor = ui->detailsTextEdit->textCursor();
506 QString summary = summaryToHtml();
507 cursor.insertHtml(summary);
508 cursor.insertBlock(); // Work around rendering oddity.
510 QString file_comments = cf_read_shb_comment(cap_file_.capFile());
511 if (!file_comments.isEmpty()) {
512 QString file_comments_html;
514 QString comment_escaped;
515 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
516 comment_escaped = Qt::escape(file_comments);
518 comment_escaped = file_comments.toHtmlEscaped();
520 file_comments_html = section_tmpl_.arg(tr("File Comment"));
521 file_comments_html += para_tmpl_.arg(comment_escaped);
523 cursor.insertBlock();
524 cursor.insertHtml(file_comments_html);
527 if (cap_file_.capFile()->packet_comment_count > 0) {
528 cursor.insertBlock();
529 cursor.insertHtml(section_tmpl_.arg(tr("Packet Comments")));
531 for (guint32 framenum = 1; framenum <= cap_file_.capFile()->count ; framenum++) {
532 frame_data *fdata = frame_data_sequence_find(cap_file_.capFile()->frames, framenum);
533 char *pkt_comment = cf_get_comment(cap_file_.capFile(), fdata);
536 QString frame_comment_html = tr("<p>Frame %1: ").arg(framenum);
537 QString raw_comment = pkt_comment;
539 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
540 frame_comment_html += Qt::escape(raw_comment);
542 frame_comment_html += raw_comment.toHtmlEscaped();
544 frame_comment_html += "</p>\n";
545 cursor.insertBlock();
546 cursor.insertHtml(frame_comment_html);
550 ui->detailsTextEdit->verticalScrollBar()->setValue(0);
553 void CaptureFilePropertiesDialog::changeEvent(QEvent* event)
557 switch (event->type())
559 case QEvent::LanguageChange:
560 ui->retranslateUi(this);
567 QDialog::changeEvent(event);
570 void CaptureFilePropertiesDialog::on_buttonBox_helpRequested()
572 wsApp->helpTopicAction(HELP_STATS_SUMMARY_DIALOG);
575 void CaptureFilePropertiesDialog::on_buttonBox_accepted()
577 if (file_closed_ || !cap_file_.capFile()->filename) {
581 if (wtap_dump_can_write(cap_file_.capFile()->linktypes, WTAP_COMMENT_PER_SECTION))
583 gchar *str = qstring_strdup(ui->commentsTextEdit->toPlainText());
584 cf_update_capture_comment(cap_file_.capFile(), str);
585 emit captureCommentChanged();
590 void CaptureFilePropertiesDialog::on_buttonBox_clicked(QAbstractButton *button)
592 if (button == ui->buttonBox->button(QDialogButtonBox::Apply)) {
593 QClipboard *clipboard = QApplication::clipboard();
594 QString details = tr("Created by Wireshark %1\n\n").arg(get_ws_vcs_version_info());
595 details.append(ui->detailsTextEdit->toPlainText());
596 clipboard->setText(details);
597 } else if (button == ui->buttonBox->button(QDialogButtonBox::Reset)) {
602 void CaptureFilePropertiesDialog::on_buttonBox_rejected()
613 * indent-tabs-mode: nil
616 * ex: set shiftwidth=4 tabstop=8 expandtab:
617 * :indentSize=4:tabSize=8:noTabs=true: