wireshark/ui/qt/byte_view_tab.cpp

378 lines
12 KiB
C++

/* byte_view_tab.cpp
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "byte_view_tab.h"
#include "byte_view_text.h"
#include <QApplication>
#include <QClipboard>
#include <QMimeData>
#include <QTabBar>
#include <QTreeWidgetItem>
#include <ui/qt/variant_pointer.h>
// To do:
// - We might want to add a callback to free_data_sources in so that we
// don't have to blindly call clear().
ByteViewTab::ByteViewTab(QWidget *parent) :
QTabWidget(parent)
{
setAccessibleName(tr("Packet bytes"));
setTabPosition(QTabWidget::South);
setDocumentMode(true);
addTab();
}
void ByteViewTab::addTab(const char *name, tvbuff_t *tvb, proto_tree *tree, QTreeWidget *protoTree, packet_char_enc encoding) {
if (count() == 1) { // Remove empty placeholder.
ByteViewText *cur_text = qobject_cast<ByteViewText *>(currentWidget());
if (cur_text && cur_text->isEmpty()) delete currentWidget();
}
ByteViewText *byte_view_text = new ByteViewText(this, tvb, tree, protoTree, encoding);
byte_view_text->setAccessibleName(name);
byte_view_text->setMonospaceFont(mono_font_);
connect(this, SIGNAL(monospaceFontChanged(QFont)), byte_view_text, SLOT(setMonospaceFont(QFont)));
connect(byte_view_text, SIGNAL(byteFieldHovered(const QString&)), this, SIGNAL(byteFieldHovered(const QString&)));
QTabWidget::addTab(byte_view_text, name);
}
void ByteViewTab::clear()
{
bool visible = isVisible();
if (visible) {
hide();
}
while (currentWidget()) {
delete currentWidget();
}
addTab();
if (visible) {
show();
}
}
// XXX How many hex dump routines do we have?
const int byte_line_length_ = 16; // Print out data for 16 bytes on one line
void ByteViewTab::copyHexTextDump(const guint8 *data_p, int data_len, bool append_text)
{
QString clipboard_text;
/* Write hex data for a line, then ascii data, then concatenate and add to buffer */
QString hex_str, char_str;
int i;
bool end_of_line = true; /* Initial state is end of line */
int byte_line_part_length;
i = 0;
while (i < data_len) {
if(end_of_line) {
hex_str += QString("%1 ").arg(i, 4, 16, QChar('0')); /* Offset - note that we _append_ here */
}
hex_str += QString(" %1").arg(*data_p, 2, 16, QChar('0'));
if(append_text) {
char_str += QString("%1").arg(g_ascii_isprint(*data_p) ? QChar(*data_p) : '.');
}
++data_p;
/* Look ahead to see if this is the end of the data */
byte_line_part_length = (++i) % byte_line_length_;
if(i >= data_len){
/* End of data - need to fill in spaces in hex string and then do "end of line".
*
*/
if (append_text) {
int fill_len = byte_line_part_length == 0 ?
0 : byte_line_length_ - byte_line_part_length;
/* Add three spaces for each missing byte */
hex_str += QString(fill_len * 3, ' ');
}
end_of_line = true;
} else {
end_of_line = (byte_line_part_length == 0);
}
if (end_of_line){
/* End of line */
clipboard_text += hex_str;
if(append_text) {
/* Two spaces between hex and text */
clipboard_text += " ";
clipboard_text += char_str;
}
/* Setup ready for next line */
hex_str = "\n";
char_str.clear();
}
}
if (!clipboard_text.isEmpty()) {
qApp->clipboard()->setText(clipboard_text);
}
}
void ByteViewTab::copyPrintableText(const guint8 *data_p, int data_len)
{
QString clipboard_text;
for (int i = 0; i < data_len; i++) {
const guint8 c = data_p[i];
if (g_ascii_isprint(c) || g_ascii_isspace(c)) {
clipboard_text += QChar(c);
}
}
if (!clipboard_text.isEmpty()) {
qApp->clipboard()->setText(clipboard_text);
}
}
void ByteViewTab::copyHexStream(const guint8 *data_p, int data_len)
{
QString clipboard_text;
for (int i = 0; i < data_len; i++) {
clipboard_text += QString("%1").arg(data_p[i], 2, 16, QChar('0'));
}
if (!clipboard_text.isEmpty()) {
qApp->clipboard()->setText(clipboard_text);
}
}
void ByteViewTab::copyBinary(const guint8 *data_p, int data_len)
{
QByteArray clipboard_bytes = QByteArray::fromRawData((const char *) data_p, data_len);
if (!clipboard_bytes.isEmpty()) {
QMimeData *mime_data = new QMimeData;
// gtk/gui_utils.c:copy_binary_to_clipboard says:
/* XXX - this is not understood by most applications,
* but can be pasted into the better hex editors - is
* there something better that we can do?
*/
// As of 2015-07-30, pasting into Frhed works on Windows. Pasting into
// Hex Editor Neo and HxD does not.
mime_data->setData("application/octet-stream", clipboard_bytes);
qApp->clipboard()->setMimeData(mime_data);
}
}
void ByteViewTab::copyEscapedString(const guint8 *data_p, int data_len)
{
QString clipboard_text;
// Beginning quote
clipboard_text += QString("\"");
for (int i = 0; i < data_len; i++) {
// Terminate this line if it has reached 16 bytes,
// unless it is also the very last byte in the data,
// as the termination after this for loop will take
// care of that.
if (i % 16 == 0 && i != 0 && i != data_len - 1) {
clipboard_text += QString("\" \\\n\"");
}
clipboard_text += QString("\\x%1").arg(data_p[i], 2, 16, QChar('0'));
}
// End quote
clipboard_text += QString("\"\n");
if (!clipboard_text.isEmpty()) {
qApp->clipboard()->setText(clipboard_text);
}
}
void ByteViewTab::copyData(ByteViewTab::copyDataType copy_type, field_info *fi)
{
int i = 0;
ByteViewText *byte_view_text = qobject_cast<ByteViewText*>(widget(i));
if (fi) {
while (byte_view_text) {
if (byte_view_text->hasDataSource(fi->ds_tvb)) break;
byte_view_text = qobject_cast<ByteViewText*>(widget(++i));
}
}
if (!byte_view_text) return;
guint data_len = 0;
const guint8 *data_p;
data_p = byte_view_text->dataAndLength(&data_len);
if (!data_p) return;
if (fi && fi->start >= 0 && fi->length > 0 && fi->length <= (int) data_len) {
data_len = fi->length;
data_p += fi->start;
}
if (!data_len) return;
switch (copy_type) {
case copyDataHexTextDump:
copyHexTextDump(data_p, data_len, true);
break;
case copyDataHexDump:
copyHexTextDump(data_p, data_len, false);
break;
case copyDataPrintableText:
copyPrintableText(data_p, data_len);
break;
case copyDataHexStream:
copyHexStream(data_p, data_len);
break;
case copyDataBinary:
copyBinary(data_p, data_len);
break;
case copyDataEscapedString:
copyEscapedString(data_p, data_len);
break;
default:
break;
}
}
void ByteViewTab::tabInserted(int index) {
setTabsVisible();
QTabWidget::tabInserted(index);
}
void ByteViewTab::tabRemoved(int index) {
setTabsVisible();
QTabWidget::tabRemoved(index);
}
void ByteViewTab::setTabsVisible() {
if (count() > 1)
tabBar()->show();
else
tabBar()->hide();
}
void ByteViewTab::protoTreeItemChanged(QTreeWidgetItem *current) {
if (current && cap_file_) {
field_info *fi;
fi = VariantPointer<field_info>::asPtr(current->data(0, Qt::UserRole));
int i = 0;
ByteViewText *byte_view_text = qobject_cast<ByteViewText*>(widget(i));
while (byte_view_text) {
if (byte_view_text->hasDataSource(fi->ds_tvb)) {
QTreeWidgetItem *parent = current->parent();
field_info *parent_fi = NULL;
int f_start = -1, f_end = -1, f_len = -1;
int fa_start = -1, fa_end = -1, fa_len = -1;
int p_start = -1, p_end = -1, p_len = -1;
guint len = tvb_captured_length(fi->ds_tvb);
// Find and highlight the protocol bytes
while (parent && parent->parent()) {
parent = parent->parent();
}
if (parent) {
parent_fi = VariantPointer<field_info>::asPtr(parent->data(0, Qt::UserRole));
}
if (parent_fi && parent_fi->ds_tvb == fi->ds_tvb) {
p_start = parent_fi->start;
p_len = parent_fi->length;
}
if (cap_file_->search_in_progress && (cap_file_->hex || (cap_file_->string && cap_file_->packet_data))) {
// In the hex view, only highlight the target bytes or string. The entire
// field can then be displayed by clicking on any of the bytes in the field.
f_start = cap_file_->search_pos - cap_file_->search_len + 1;
f_len = cap_file_->search_len;
} else {
f_start = fi->start;
f_len = fi->length;
}
/* bmask = finfo->hfinfo->bitmask << hfinfo_bitshift(finfo->hfinfo); */ /* (value & mask) >> shift */
fa_start = fi->appendix_start;
fa_len = fi->appendix_length;
if (p_start >= 0 && p_len > 0 && (guint)p_start < len) {
p_end = p_start + p_len;
}
if (f_start >= 0 && f_len > 0 && (guint)f_start < len) {
f_end = f_start + f_len;
}
if (fa_start >= 0 && fa_len > 0 && (guint)fa_start < len) {
fa_end = fa_start + fa_len;
}
if (f_end == -1 && fa_end != -1) {
f_start = fa_start;
f_end = fa_end;
fa_start = fa_end = -1;
}
/* don't exceed the end of available data */
if (p_end != -1 && (guint)p_end > len) p_end = len;
if (f_end != -1 && (guint)f_end > len) f_end = len;
if (fa_end != -1 && (guint)fa_end > len) fa_end = len;
// Protocol
byte_view_text->setProtocolHighlight(p_start, p_end);
// Field bytes
byte_view_text->setFieldHighlight(f_start, f_end);
// Appendix (trailer) bytes
byte_view_text->setFieldAppendixHighlight(fa_start, fa_end);
setCurrentIndex(i);
}
byte_view_text = qobject_cast<ByteViewText*>(widget(++i));
}
}
}
void ByteViewTab::setCaptureFile(capture_file *cf)
{
cap_file_ = cf;
}
void ByteViewTab::setMonospaceFont(const QFont &mono_font)
{
mono_font_ = mono_font;
emit monospaceFontChanged(mono_font_);
update();
}
/*
* Editor modelines
*
* Local Variables:
* c-basic-offset: 4
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* ex: set shiftwidth=4 tabstop=8 expandtab:
* :indentSize=4:tabSize=8:noTabs=true:
*/