1 /* show_packet_bytes_dialog.cpp
2 *
3 * Wireshark - Network traffic analyzer
4 * By Gerald Combs <gerald@wireshark.org>
5 * Copyright 1998 Gerald Combs
6 *
7 * SPDX-License-Identifier: GPL-2.0-or-later
8 */
9
10 #include "show_packet_bytes_dialog.h"
11 #include <ui_show_packet_bytes_dialog.h>
12
13 #include "main_window.h"
14 #include "wireshark_application.h"
15 #include "ui/qt/widgets/wireshark_file_dialog.h"
16
17 #include "epan/charsets.h"
18
19 #include "wsutil/utf8_entities.h"
20
21 #include <QAction>
22 #include <QImage>
23 #include <QKeyEvent>
24 #include <QMenu>
25 #include <QPrintDialog>
26 #include <QPrinter>
27 #include <QTextCodec>
28 #include <QTextStream>
29
30 // To do:
31 // - Add show as custom protocol in a Packet Details view
32 // - Use ByteViewText to ShowAsHexDump and supplementary view for custom protocol
33 // - Handle large data blocks
34
ShowPacketBytesDialog(QWidget & parent,CaptureFile & cf)35 ShowPacketBytesDialog::ShowPacketBytesDialog(QWidget &parent, CaptureFile &cf) :
36 WiresharkDialog(parent, cf),
37 ui(new Ui::ShowPacketBytesDialog),
38 finfo_(cf.capFile()->finfo_selected),
39 decode_as_(DecodeAsNone),
40 show_as_(ShowAsASCII),
41 use_regex_find_(false)
42 {
43 ui->setupUi(this);
44 loadGeometry(parent.width() * 2 / 3, parent.height() * 3 / 4);
45
46 QString field_name = QString("%1 (%2)").arg(finfo_->hfinfo->name, finfo_->hfinfo->abbrev);
47 setWindowSubtitle (field_name);
48
49 hint_label_ = tr("Frame %1, %2, %Ln byte(s).", "", finfo_->length)
50 .arg(cf.capFile()->current_frame->num)
51 .arg(field_name);
52
53 ui->tePacketBytes->installEventFilter(this);
54
55 connect(ui->tePacketBytes, SIGNAL(showSelected(int,int)), this, SLOT(showSelected(int,int)));
56 connect(ui->leFind, SIGNAL(useRegexFind(bool)), this, SLOT(useRegexFind(bool)));
57
58 ui->cbDecodeAs->blockSignals(true);
59 ui->cbDecodeAs->addItem(tr("None"), DecodeAsNone);
60 ui->cbDecodeAs->addItem(tr("Base64"), DecodeAsBASE64);
61 ui->cbDecodeAs->addItem(tr("Compressed"), DecodeAsCompressed);
62 ui->cbDecodeAs->addItem(tr("Hex Digits"), DecodeAsHexDigits);
63 ui->cbDecodeAs->addItem(tr("Quoted-Printable"), DecodeAsQuotedPrintable);
64 ui->cbDecodeAs->addItem(tr("ROT13"), DecodeAsROT13);
65 ui->cbDecodeAs->blockSignals(false);
66
67 ui->cbShowAs->blockSignals(true);
68 ui->cbShowAs->addItem(tr("ASCII"), ShowAsASCII);
69 ui->cbShowAs->addItem(tr("ASCII & Control"), ShowAsASCIIandControl);
70 ui->cbShowAs->addItem(tr("C Array"), ShowAsCArray);
71 ui->cbShowAs->addItem(tr("EBCDIC"), ShowAsEBCDIC);
72 ui->cbShowAs->addItem(tr("Hex Dump"), ShowAsHexDump);
73 ui->cbShowAs->addItem(tr("HTML"), ShowAsHTML);
74 ui->cbShowAs->addItem(tr("Image"), ShowAsImage);
75 ui->cbShowAs->addItem(tr("Raw"), ShowAsRAW);
76 // UTF-8 is guaranteed to exist as a QTextCodec
77 ui->cbShowAs->addItem(tr("UTF-8"), ShowAsCodec);
78 ui->cbShowAs->addItem(tr("YAML"), ShowAsYAML);
79 ui->cbShowAs->setCurrentIndex(show_as_);
80 ui->cbShowAs->blockSignals(false);
81
82 ui->sbStart->setMinimum(0);
83 ui->sbEnd->setMaximum(finfo_->length);
84
85 print_button_ = ui->buttonBox->addButton(tr("Print"), QDialogButtonBox::ActionRole);
86 connect(print_button_, SIGNAL(clicked()), this, SLOT(printBytes()));
87
88 copy_button_ = ui->buttonBox->addButton(tr("Copy"), QDialogButtonBox::ActionRole);
89 connect(copy_button_, SIGNAL(clicked()), this, SLOT(copyBytes()));
90
91 save_as_button_ = ui->buttonBox->addButton(tr("Save as…"), QDialogButtonBox::ActionRole);
92 connect(save_as_button_, SIGNAL(clicked()), this, SLOT(saveAs()));
93
94 connect(ui->buttonBox, SIGNAL(helpRequested()), this, SLOT(helpButton()));
95
96 setStartAndEnd(0, finfo_->length);
97 updateFieldBytes(true);
98 }
99
~ShowPacketBytesDialog()100 ShowPacketBytesDialog::~ShowPacketBytesDialog()
101 {
102 delete ui;
103 }
104
addCodecs(const QMap<QString,QTextCodec * > & codecMap)105 void ShowPacketBytesDialog::addCodecs(const QMap<QString, QTextCodec *> &codecMap)
106 {
107 ui->cbShowAs->blockSignals(true);
108 // Make the combobox respect max visible items?
109 //ui->cbShowAs->setStyleSheet("QComboBox { combobox-popup: 0;}");
110 ui->cbShowAs->insertSeparator(ui->cbShowAs->count());
111 #if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0)
112 for (const auto &codec : qAsConst(codecMap)) {
113 #else
114 foreach (const QTextCodec *codec, codecMap) {
115 #endif
116 // This is already placed in the menu and handled separately
117 if (codec->name() != "US-ASCII" && codec->name() != "UTF-8")
118 ui->cbShowAs->addItem(tr(codec->name()), ShowAsCodec);
119 }
120 ui->cbShowAs->blockSignals(false);
121 }
122
123 void ShowPacketBytesDialog::showSelected(int start, int end)
124 {
125 if (end == -1) {
126 // end set to -1 means show all packet bytes
127 setStartAndEnd(0, finfo_->length);
128 } else {
129 if (show_as_ == ShowAsRAW) {
130 start /= 2;
131 end = (end + 1) / 2;
132 }
133 setStartAndEnd(start_ + start, start_ + end);
134 }
135 updateFieldBytes();
136 }
137
138 void ShowPacketBytesDialog::setStartAndEnd(int start, int end)
139 {
140 start_ = start;
141 end_ = end;
142
143 ui->sbStart->blockSignals(true);
144 ui->sbStart->setMaximum(end_);
145 ui->sbStart->setValue(start_);
146 ui->sbStart->blockSignals(false);
147
148 ui->sbEnd->blockSignals(true);
149 ui->sbEnd->setMinimum(start_);
150 ui->sbEnd->setValue(end_);
151 ui->sbEnd->blockSignals(false);
152
153 updateHintLabel();
154 }
155
156 bool ShowPacketBytesDialog::enableShowSelected()
157 {
158 // "Show Selected" only works when showing all bytes:
159 // - DecodeAs must not alter the number of bytes in the buffer
160 // - ShowAs must show all bytes in the buffer
161
162 return (((decode_as_ == DecodeAsNone) ||
163 (decode_as_ == DecodeAsROT13)) &&
164 ((show_as_ == ShowAsASCII) ||
165 (show_as_ == ShowAsASCIIandControl) ||
166 (show_as_ == ShowAsEBCDIC) ||
167 (show_as_ == ShowAsRAW)));
168 }
169
170 void ShowPacketBytesDialog::updateWidgets()
171 {
172 WiresharkDialog::updateWidgets();
173 }
174
175 void ShowPacketBytesDialog::updateHintLabel()
176 {
177 QString hint = hint_label_;
178
179 if (start_ > 0 || end_ < finfo_->length) {
180 hint.append(" <span style=\"color: red\">" +
181 tr("Displaying %Ln byte(s).", "", end_ - start_) +
182 "</span>");
183 }
184
185 ui->hintLabel->setText("<small><i>" + hint + "</i></small>");
186 }
187
188 void ShowPacketBytesDialog::on_sbStart_valueChanged(int value)
189 {
190 start_ = value;
191 ui->sbEnd->setMinimum(value);
192
193 updateHintLabel();
194 updateFieldBytes();
195 }
196
197 void ShowPacketBytesDialog::on_sbEnd_valueChanged(int value)
198 {
199 end_ = value;
200 ui->sbStart->setMaximum(value);
201
202 updateHintLabel();
203 updateFieldBytes();
204 }
205
206 void ShowPacketBytesDialog::on_cbDecodeAs_currentIndexChanged(int idx)
207 {
208 if (idx < 0) return;
209 decode_as_ = static_cast<DecodeAsType>(ui->cbDecodeAs->itemData(idx).toInt());
210
211 ui->tePacketBytes->setShowSelectedEnabled(enableShowSelected());
212
213 updateFieldBytes();
214 }
215
216 void ShowPacketBytesDialog::on_cbShowAs_currentIndexChanged(int idx)
217 {
218 if (idx < 0) return;
219 show_as_ = static_cast<ShowAsType>(ui->cbShowAs->itemData(idx).toInt());
220
221 ui->tePacketBytes->setShowSelectedEnabled(enableShowSelected());
222 ui->lFind->setEnabled(true);
223 ui->leFind->setEnabled(true);
224 ui->bFind->setEnabled(true);
225 print_button_->setEnabled(true);
226 copy_button_->setEnabled(true);
227 save_as_button_->setEnabled(true);
228
229 updatePacketBytes();
230 }
231
232 void ShowPacketBytesDialog::useRegexFind(bool use_regex)
233 {
234 use_regex_find_ = use_regex;
235 if (use_regex_find_)
236 ui->lFind->setText(tr("Regex Find:"));
237 else
238 ui->lFind->setText(tr("Find:"));
239 }
240
241 void ShowPacketBytesDialog::findText(bool go_back)
242 {
243 if (ui->leFind->text().isEmpty()) return;
244
245 bool found;
246 if (use_regex_find_) {
247 QRegExp regex(ui->leFind->text());
248 found = ui->tePacketBytes->find(regex);
249 } else {
250 found = ui->tePacketBytes->find(ui->leFind->text());
251 }
252
253 if (found) {
254 ui->tePacketBytes->setFocus();
255 } else if (go_back) {
256 ui->tePacketBytes->moveCursor(QTextCursor::Start);
257 findText(false);
258 }
259 }
260
261 void ShowPacketBytesDialog::printBytes()
262 {
263 #ifndef QT_NO_PRINTER
264 QPrinter printer(QPrinter::HighResolution);
265 QPrintDialog dialog(&printer, this);
266 if (dialog.exec() == QDialog::Accepted)
267 ui->tePacketBytes->print(&printer);
268 #endif
269 }
270
271 void ShowPacketBytesDialog::copyBytes()
272 {
273 switch (show_as_) {
274
275 case ShowAsASCII:
276 {
277 QByteArray ba(field_bytes_);
278 sanitizeBuffer(ba, true);
279 wsApp->clipboard()->setText(ba);
280 break;
281 }
282
283 case ShowAsASCIIandControl:
284 case ShowAsCArray:
285 case ShowAsEBCDIC:
286 case ShowAsHexDump:
287 case ShowAsRAW:
288 case ShowAsYAML:
289 wsApp->clipboard()->setText(ui->tePacketBytes->toPlainText());
290 break;
291
292 case ShowAsHTML:
293 wsApp->clipboard()->setText(ui->tePacketBytes->toHtml());
294 break;
295
296 case ShowAsImage:
297 wsApp->clipboard()->setImage(image_);
298 break;
299
300 case ShowAsCodec:
301 wsApp->clipboard()->setText(ui->tePacketBytes->toPlainText().toUtf8());
302 break;
303 }
304 }
305
306 void ShowPacketBytesDialog::saveAs()
307 {
308 QString file_name = WiresharkFileDialog::getSaveFileName(this, wsApp->windowTitleString(tr("Save Selected Packet Bytes As…")));
309
310 if (file_name.isEmpty())
311 return;
312
313 QFile::OpenMode open_mode = QFile::WriteOnly;
314 switch (show_as_) {
315 case ShowAsASCII:
316 case ShowAsASCIIandControl:
317 case ShowAsCArray:
318 // We always save as UTF-8, so set text mode as we would for UTF-8
319 case ShowAsCodec:
320 case ShowAsHexDump:
321 case ShowAsYAML:
322 case ShowAsHTML:
323 open_mode |= QFile::Text;
324 default:
325 break;
326 }
327
328 QFile file(file_name);
329 file.open(open_mode);
330
331 switch (show_as_) {
332
333 case ShowAsASCII:
334 {
335 QByteArray ba(field_bytes_);
336 sanitizeBuffer(ba, true);
337 file.write(ba);
338 break;
339 }
340
341 case ShowAsASCIIandControl:
342 case ShowAsCArray:
343 case ShowAsEBCDIC:
344 case ShowAsHexDump:
345 case ShowAsYAML:
346 {
347 QTextStream out(&file);
348 out << ui->tePacketBytes->toPlainText();
349 break;
350 }
351
352 case ShowAsHTML:
353 {
354 QTextStream out(&file);
355 out << ui->tePacketBytes->toHtml();
356 break;
357 }
358
359 case ShowAsCodec:
360 {
361 QTextStream out(&file);
362 out << ui->tePacketBytes->toPlainText().toUtf8();
363 break;
364 }
365
366 case ShowAsImage:
367 case ShowAsRAW:
368 file.write(field_bytes_);
369 break;
370 }
371
372 file.close();
373 }
374
375 void ShowPacketBytesDialog::helpButton()
376 {
377 wsApp->helpTopicAction(HELP_SHOW_PACKET_BYTES_DIALOG);
378 }
379
380 void ShowPacketBytesDialog::on_bFind_clicked()
381 {
382 findText();
383 }
384
385 void ShowPacketBytesDialog::on_leFind_returnPressed()
386 {
387 findText();
388 }
389
390 // Not sure why we have to do this manually.
391 void ShowPacketBytesDialog::on_buttonBox_rejected()
392 {
393 WiresharkDialog::reject();
394 }
395
396 // The following keyboard shortcuts should work (although
397 // they may not work consistently depending on focus):
398 // / (slash), Ctrl-F - Focus and highlight the search box
399 // Ctrl-G, Ctrl-N, F3 - Find next
400 // Should we make it so that typing any text starts searching?
401 bool ShowPacketBytesDialog::eventFilter(QObject *, QEvent *event)
402 {
403 if (ui->tePacketBytes->hasFocus() && event->type() == QEvent::KeyPress) {
404 QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
405 if (keyEvent->matches(QKeySequence::SelectAll) || keyEvent->matches(QKeySequence::Copy)
406 || keyEvent->text().isEmpty()) {
407 return false;
408 }
409 ui->leFind->setFocus();
410 if (keyEvent->matches(QKeySequence::Find)) {
411 return true;
412 } else if (keyEvent->matches(QKeySequence::FindNext)) {
413 findText();
414 return true;
415 }
416 }
417
418 return false;
419 }
420
421 void ShowPacketBytesDialog::keyPressEvent(QKeyEvent *event)
422 {
423 if (ui->leFind->hasFocus()) {
424 if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) {
425 findText();
426 return;
427 }
428 } else {
429 if (event->key() == Qt::Key_Slash || event->matches(QKeySequence::Find)) {
430 ui->leFind->setFocus();
431 ui->leFind->selectAll();
432 }
433 return;
434 }
435
436 if (event->key() == Qt::Key_F3 || (event->key() == Qt::Key_N && event->modifiers() & Qt::ControlModifier)) {
437 findText();
438 return;
439 }
440
441 QDialog::keyPressEvent(event);
442 }
443
444 void ShowPacketBytesDialog::sanitizeBuffer(QByteArray &ba, bool keep_CR)
445 {
446 for (int i = 0; i < ba.length(); i++) {
447 if (ba[i] == '\n' || (keep_CR && ba[i] == '\r'))
448 // Keep LF and optionally CR
449 continue;
450
451 if (ba[i] == '\0' || g_ascii_isspace(ba[i])) {
452 ba[i] = ' ';
453 } else if (!g_ascii_isprint(ba[i])) {
454 ba.replace(i, 1, UTF8_MIDDLE_DOT);
455 i += sizeof(UTF8_MIDDLE_DOT) - 2;
456 }
457 }
458 }
459
460 void ShowPacketBytesDialog::symbolizeBuffer(QByteArray &ba)
461 {
462 for (int i = 0; i < ba.length(); i++) {
463 if ((ba[i] < '\0' || ba[i] >= ' ') && ba[i] != (char)0x7f && !g_ascii_isprint(ba[i])) {
464 ba.replace(i, 1, UTF8_MIDDLE_DOT);
465 i += sizeof(UTF8_MIDDLE_DOT) - 2;
466 }
467 }
468
469 QByteArray symbol(UTF8_SYMBOL_FOR_NULL);
470 for (char i = 0; i < ' '; i++) {
471 ba.replace(i, symbol);
472 symbol[2] = symbol[2] + 1;
473 }
474 symbol[2] = symbol[2] + 1; // Skip SP
475 ba.replace((char)0x7f, symbol); // DEL
476 }
477
478 QByteArray ShowPacketBytesDialog::decodeQuotedPrintable(const guint8 *bytes, int length)
479 {
480 QByteArray ba;
481
482 for (int i = 0; i < length; i++) {
483 if (bytes[i] == '=' && i + 1 < length) {
484 if (bytes[i+1] == '\n') {
485 i++; // Soft line break LF
486 } else if (bytes[i+1] == '\r' && i + 2 < length && bytes[i+2] == '\n') {
487 i += 2; // Soft line break CRLF
488 } else if (g_ascii_isxdigit(bytes[i+1]) && i + 2 < length && g_ascii_isxdigit(bytes[i+2])) {
489 ba.append(QByteArray::fromHex(QByteArray((const char *)&bytes[i+1], 2)));
490 i += 2; // Valid Quoted-Printable sequence
491 } else {
492 // Illegal Quoted-Printable, just add byte
493 ba.append(bytes[i]);
494 }
495 } else {
496 ba.append(bytes[i]);
497 }
498 }
499
500 return ba;
501 }
502
503 void ShowPacketBytesDialog::rot13(QByteArray &ba)
504 {
505 for (int i = 0; i < ba.length(); i++) {
506 gchar upper = g_ascii_toupper(ba[i]);
507 if (upper >= 'A' && upper <= 'M') ba[i] = ba[i] + 13;
508 else if (upper >= 'N' && upper <= 'Z') ba[i] = ba[i] - 13;
509 }
510 }
511
512 void ShowPacketBytesDialog::updateFieldBytes(bool initialization)
513 {
514 int start = finfo_->start + start_;
515 int length = end_ - start_;
516 const guint8 *bytes;
517 gsize new_length = 0;
518
519 if (!finfo_->ds_tvb)
520 return;
521
522 switch (decode_as_) {
523
524 case DecodeAsNone:
525 bytes = tvb_get_ptr(finfo_->ds_tvb, start, -1);
526 field_bytes_ = QByteArray((const char *)bytes, length);
527 break;
528
529 case DecodeAsBASE64:
530 {
531 bytes = tvb_get_ptr(finfo_->ds_tvb, start, -1);
532 field_bytes_ = QByteArray((const char *)bytes, length);
533 if (field_bytes_.size() > 1) {
534 g_base64_decode_inplace(field_bytes_.data(), &new_length);
535 }
536 field_bytes_.resize((int)new_length);
537 break;
538 }
539
540 case DecodeAsCompressed:
541 {
542 tvbuff *uncompr_tvb = tvb_uncompress(finfo_->ds_tvb, start, length);
543 if (uncompr_tvb) {
544 bytes = tvb_get_ptr(uncompr_tvb, 0, -1);
545 field_bytes_ = QByteArray((const char *)bytes, tvb_reported_length(uncompr_tvb));
546 tvb_free(uncompr_tvb);
547 } else {
548 field_bytes_.clear();
549 }
550 break;
551 }
552
553 case DecodeAsHexDigits:
554 bytes = tvb_get_ptr(finfo_->ds_tvb, start, -1);
555 field_bytes_ = QByteArray::fromHex(QByteArray::fromRawData((const char *)bytes, length));
556 break;
557
558 case DecodeAsQuotedPrintable:
559 bytes = tvb_get_ptr(finfo_->ds_tvb, start, -1);
560 field_bytes_ = decodeQuotedPrintable(bytes, length);
561 break;
562
563 case DecodeAsROT13:
564 bytes = tvb_get_ptr(finfo_->ds_tvb, start, -1);
565 field_bytes_ = QByteArray((const char *)bytes, length);
566 rot13(field_bytes_);
567 break;
568 }
569
570 // Try loading as image at startup
571 if (initialization && image_.loadFromData(field_bytes_)) {
572 show_as_ = ShowAsImage;
573 ui->cbShowAs->blockSignals(true);
574 ui->cbShowAs->setCurrentIndex(ShowAsImage);
575 ui->cbShowAs->blockSignals(false);
576 }
577
578 updatePacketBytes();
579 }
580
581 void ShowPacketBytesDialog::updatePacketBytes(void)
582 {
583 static const gchar hexchars[16] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
584
585 ui->tePacketBytes->clear();
586 ui->tePacketBytes->setCurrentFont(wsApp->monospaceFont());
587
588 switch (show_as_) {
589
590 case ShowAsASCII:
591 {
592 QByteArray ba(field_bytes_);
593 sanitizeBuffer(ba, false);
594 ui->tePacketBytes->setLineWrapMode(QTextEdit::WidgetWidth);
595 ui->tePacketBytes->setPlainText(ba);
596 break;
597 }
598
599 case ShowAsASCIIandControl:
600 {
601 QByteArray ba(field_bytes_);
602 symbolizeBuffer(ba);
603 ui->tePacketBytes->setLineWrapMode(QTextEdit::WidgetWidth);
604 ui->tePacketBytes->setPlainText(ba);
605 break;
606 }
607
608 case ShowAsCArray:
609 {
610 int pos = 0, len = field_bytes_.length();
611 QString text("char packet_bytes[] = {\n");
612
613 while (pos < len) {
614 gchar hexbuf[256];
615 char *cur = hexbuf;
616 int i;
617
618 *cur++ = ' ';
619 for (i = 0; i < 8 && pos + i < len; i++) {
620 // Prepend entries with " 0x"
621 *cur++ = ' ';
622 *cur++ = '0';
623 *cur++ = 'x';
624 *cur++ = hexchars[(field_bytes_[pos + i] & 0xf0) >> 4];
625 *cur++ = hexchars[field_bytes_[pos + i] & 0x0f];
626
627 // Delimit array entries with a comma
628 if (pos + i + 1 < len)
629 *cur++ = ',';
630 }
631
632 pos += i;
633 *cur++ = '\n';
634 *cur = 0;
635
636 text.append(hexbuf);
637 }
638
639 text.append("};\n");
640 ui->tePacketBytes->setLineWrapMode(QTextEdit::NoWrap);
641 ui->tePacketBytes->setPlainText(text);
642 break;
643 }
644
645 case ShowAsCodec:
646 {
647 // The QTextCodecs docs say that there's a flag to cause invalid
648 // characters to be replaced with null. It's unclear what happens
649 // in the default case; it might depend on the codec though it
650 // seems that in practice replacement characters are used.
651 QTextCodec *codec = QTextCodec::codecForName(ui->cbShowAs->currentText().toUtf8());
652 QByteArray ba(field_bytes_);
653 QString decoded = codec->toUnicode(ba);
654 ui->tePacketBytes->setLineWrapMode(QTextEdit::WidgetWidth);
655 ui->tePacketBytes->setPlainText(decoded);
656 break;
657 }
658
659 case ShowAsEBCDIC:
660 {
661 QByteArray ba(field_bytes_);
662 EBCDIC_to_ASCII((guint8*)ba.data(), ba.length());
663 sanitizeBuffer(ba, false);
664 ui->tePacketBytes->setLineWrapMode(QTextEdit::WidgetWidth);
665 ui->tePacketBytes->setPlainText(ba);
666 break;
667 }
668
669 case ShowAsHexDump:
670 {
671 int pos = 0, len = field_bytes_.length();
672 // Use 16-bit offset if there are <= 65536 bytes, 32-bit offset if there are more
673 unsigned int offset_chars = (len - 1 <= 0xFFFF) ? 4 : 8;
674 QString text;
675 text.reserve((len / 16) * 80);
676
677 while (pos < len) {
678 char hexbuf[256];
679 char *cur = hexbuf;
680 int i;
681
682 // Dump offset
683 cur += g_snprintf(cur, 20, "%0*X ", offset_chars, pos);
684
685 // Dump bytes as hex
686 for (i = 0; i < 16 && pos + i < len; i++) {
687 *cur++ = hexchars[(field_bytes_[pos + i] & 0xf0) >> 4];
688 *cur++ = hexchars[field_bytes_[pos + i] & 0x0f];
689 *cur++ = ' ';
690 if (i == 7)
691 *cur++ = ' ';
692 }
693
694 while (cur < hexbuf + offset_chars + 53)
695 *cur++ = ' '; // Fill it up with space to ascii column
696
697 // Dump bytes as text
698 for (i = 0; i < 16 && pos + i < len; i++) {
699 if (g_ascii_isprint(field_bytes_[pos + i])) {
700 *cur++ = field_bytes_[pos + i];
701 } else {
702 memcpy(cur, UTF8_MIDDLE_DOT, sizeof(UTF8_MIDDLE_DOT) - 1);
703 cur += sizeof(UTF8_MIDDLE_DOT) - 1;
704 }
705 if (i == 7)
706 *cur++ = ' ';
707 }
708
709 pos += i;
710 *cur++ = '\n';
711 *cur = 0;
712
713 text.append(hexbuf);
714 }
715
716 ui->tePacketBytes->setLineWrapMode(QTextEdit::NoWrap);
717 ui->tePacketBytes->setPlainText(text);
718 break;
719 }
720
721 case ShowAsHTML:
722 ui->tePacketBytes->setLineWrapMode(QTextEdit::WidgetWidth);
723 ui->tePacketBytes->setHtml(field_bytes_);
724 break;
725
726 case ShowAsImage:
727 {
728 ui->lFind->setEnabled(false);
729 ui->leFind->setEnabled(false);
730 ui->bFind->setEnabled(false);
731
732 ui->tePacketBytes->setLineWrapMode(QTextEdit::WidgetWidth);
733 if (image_.loadFromData(field_bytes_)) {
734 ui->tePacketBytes->textCursor().insertImage(image_);
735 }
736
737 print_button_->setEnabled(!image_.isNull());
738 copy_button_->setEnabled(!image_.isNull());
739 save_as_button_->setEnabled(!image_.isNull());
740 break;
741 }
742
743 case ShowAsYAML:
744 {
745 const int base64_raw_len = 57; // Encodes to 76 bytes, common in RFCs
746 int pos = 0, len = field_bytes_.length();
747 QString text("# Packet Bytes: !!binary |\n");
748
749 while (pos < len) {
750 QByteArray base64_data = field_bytes_.mid(pos, base64_raw_len);
751 pos += base64_data.length();
752 text.append(" " + base64_data.toBase64() + "\n");
753 }
754
755 ui->tePacketBytes->setLineWrapMode(QTextEdit::NoWrap);
756 ui->tePacketBytes->setPlainText(text);
757 break;
758 }
759
760 case ShowAsRAW:
761 ui->tePacketBytes->setLineWrapMode(QTextEdit::WidgetWidth);
762 ui->tePacketBytes->setPlainText(field_bytes_.toHex());
763 break;
764 }
765 }
766
767 void ShowPacketBytesDialog::captureFileClosing()
768 {
769 finfo_ = NULL; // This will invalidate the source backend
770
771 WiresharkDialog::captureFileClosing();
772 }
773
774 void ShowPacketBytesDialog::captureFileClosed()
775 {
776 // We have lost the source backend and must disable all functions
777 // for manipulating decoding and displayed range.
778
779 ui->tePacketBytes->setMenusEnabled(false);
780 ui->lDecodeAs->setEnabled(false);
781 ui->cbDecodeAs->setEnabled(false);
782 ui->lStart->setEnabled(false);
783 ui->sbStart->setEnabled(false);
784 ui->lEnd->setEnabled(false);
785 ui->sbEnd->setEnabled(false);
786
787 WiresharkDialog::captureFileClosed();
788 }
789
790 void ShowPacketBytesTextEdit::contextMenuEvent(QContextMenuEvent *event)
791 {
792 QMenu *menu = createStandardContextMenu();
793 QAction *action;
794
795 menu->addSeparator();
796
797 action = menu->addAction(tr("Show Selected"));
798 action->setEnabled(menus_enabled_ && show_selected_enabled_ && textCursor().hasSelection());
799 connect(action, SIGNAL(triggered()), this, SLOT(showSelected()));
800
801 action = menu->addAction(tr("Show All"));
802 action->setEnabled(menus_enabled_);
803 connect(action, SIGNAL(triggered()), this, SLOT(showAll()));
804
805 menu->exec(event->globalPos());
806 delete menu;
807 }
808
809 void ShowPacketBytesTextEdit::showSelected()
810 {
811 QTextCursor cursor = textCursor();
812 int start = cursor.selectionStart();
813 int end = cursor.selectionEnd();
814
815 emit showSelected(start, end);
816 }
817
818 void ShowPacketBytesTextEdit::showAll()
819 {
820 emit showSelected(0, -1);
821 }
822