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 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 100 ShowPacketBytesDialog::~ShowPacketBytesDialog() 101 { 102 delete ui; 103 } 104 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