2012-01-04 22:13:01 +00:00
|
|
|
/* 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
|
2012-06-28 22:56:06 +00:00
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
2012-01-04 22:13:01 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "byte_view_tab.h"
|
|
|
|
#include "byte_view_text.h"
|
2015-07-29 23:24:39 +00:00
|
|
|
|
|
|
|
#include <QApplication>
|
|
|
|
#include <QClipboard>
|
|
|
|
#include <QMimeData>
|
2012-01-04 22:13:01 +00:00
|
|
|
#include <QTabBar>
|
|
|
|
#include <QTreeWidgetItem>
|
|
|
|
|
2015-02-22 22:56:38 +00:00
|
|
|
// To do:
|
|
|
|
// - We might want to add a callback to free_data_sources in so that we
|
|
|
|
// don't have to blindly call clear().
|
|
|
|
|
2012-01-04 22:13:01 +00:00
|
|
|
ByteViewTab::ByteViewTab(QWidget *parent) :
|
|
|
|
QTabWidget(parent)
|
|
|
|
{
|
2012-03-07 10:16:33 +00:00
|
|
|
setAccessibleName(tr("Packet bytes"));
|
2015-02-11 23:00:27 +00:00
|
|
|
setTabPosition(QTabWidget::South);
|
|
|
|
setDocumentMode(true);
|
2012-01-04 22:13:01 +00:00
|
|
|
addTab();
|
|
|
|
}
|
|
|
|
|
2012-10-30 19:21:24 +00:00
|
|
|
void ByteViewTab::addTab(const char *name, tvbuff_t *tvb, proto_tree *tree, QTreeWidget *protoTree, packet_char_enc encoding) {
|
2015-05-29 18:33:16 +00:00
|
|
|
if (count() == 1) { // Remove empty placeholder.
|
|
|
|
ByteViewText *cur_text = qobject_cast<ByteViewText *>(currentWidget());
|
|
|
|
if (cur_text && cur_text->isEmpty()) delete currentWidget();
|
|
|
|
}
|
2012-01-04 22:13:01 +00:00
|
|
|
|
2015-05-29 18:33:16 +00:00
|
|
|
ByteViewText *byte_view_text = new ByteViewText(this, tvb, tree, protoTree, encoding);
|
2012-10-30 19:21:24 +00:00
|
|
|
byte_view_text->setAccessibleName(name);
|
2014-09-23 20:35:10 +00:00
|
|
|
byte_view_text->setMonospaceFont(mono_font_);
|
|
|
|
connect(this, SIGNAL(monospaceFontChanged(QFont)), byte_view_text, SLOT(setMonospaceFont(QFont)));
|
2015-03-04 23:43:33 +00:00
|
|
|
connect(byte_view_text, SIGNAL(byteFieldHovered(const QString&)), this, SIGNAL(byteFieldHovered(const QString&)));
|
2012-10-30 19:21:24 +00:00
|
|
|
QTabWidget::addTab(byte_view_text, name);
|
2012-01-04 22:13:01 +00:00
|
|
|
}
|
|
|
|
|
2012-11-06 21:49:16 +00:00
|
|
|
void ByteViewTab::clear()
|
|
|
|
{
|
2015-06-25 21:18:36 +00:00
|
|
|
bool visible = isVisible();
|
2016-03-30 21:49:06 +00:00
|
|
|
if (visible) {
|
|
|
|
hide();
|
|
|
|
}
|
2012-11-06 21:49:16 +00:00
|
|
|
while (currentWidget()) {
|
|
|
|
delete currentWidget();
|
|
|
|
}
|
2015-05-29 18:33:16 +00:00
|
|
|
addTab();
|
2016-03-30 21:49:06 +00:00
|
|
|
if (visible) {
|
|
|
|
show();
|
|
|
|
}
|
2012-11-06 21:49:16 +00:00
|
|
|
}
|
|
|
|
|
2015-07-29 23:24:39 +00:00
|
|
|
// 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()) {
|
2015-07-31 20:44:42 +00:00
|
|
|
QMimeData *mime_data = new QMimeData;
|
2015-07-29 23:24:39 +00:00
|
|
|
// 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?
|
|
|
|
*/
|
2015-07-31 20:44:42 +00:00
|
|
|
// 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);
|
2015-07-29 23:24:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-01-04 22:13:01 +00:00
|
|
|
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) {
|
2012-10-30 19:21:24 +00:00
|
|
|
if (current && cap_file_) {
|
2012-01-04 22:13:01 +00:00
|
|
|
field_info *fi;
|
|
|
|
|
2012-08-19 20:47:11 +00:00
|
|
|
fi = current->data(0, Qt::UserRole).value<field_info *>();
|
2012-01-04 22:13:01 +00:00
|
|
|
|
|
|
|
int i = 0;
|
2012-10-30 20:24:08 +00:00
|
|
|
ByteViewText *byte_view_text = qobject_cast<ByteViewText*>(widget(i));
|
2012-10-30 19:21:24 +00:00
|
|
|
while (byte_view_text) {
|
|
|
|
if (byte_view_text->hasDataSource(fi->ds_tvb)) {
|
2012-01-04 22:13:01 +00:00
|
|
|
QTreeWidgetItem *parent = current->parent();
|
|
|
|
field_info *parent_fi = NULL;
|
2012-10-30 19:21:24 +00:00
|
|
|
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;
|
2015-06-24 04:30:15 +00:00
|
|
|
guint len = tvb_captured_length(fi->ds_tvb);
|
2012-10-30 19:21:24 +00:00
|
|
|
|
|
|
|
// Find and highlight the protocol bytes
|
2012-01-04 22:13:01 +00:00
|
|
|
while (parent && parent->parent()) {
|
|
|
|
parent = parent->parent();
|
|
|
|
}
|
|
|
|
if (parent) {
|
2012-08-19 20:47:11 +00:00
|
|
|
parent_fi = parent->data(0, Qt::UserRole).value<field_info *>();
|
2012-01-04 22:13:01 +00:00
|
|
|
}
|
|
|
|
if (parent_fi && parent_fi->ds_tvb == fi->ds_tvb) {
|
2012-10-30 19:21:24 +00:00
|
|
|
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))) {
|
2016-02-23 08:30:48 +00:00
|
|
|
// 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;
|
2012-01-04 22:13:01 +00:00
|
|
|
} else {
|
2012-10-30 19:21:24 +00:00
|
|
|
f_start = fi->start;
|
|
|
|
f_len = fi->length;
|
|
|
|
}
|
|
|
|
|
2013-08-06 20:10:59 +00:00
|
|
|
/* bmask = finfo->hfinfo->bitmask << hfinfo_bitshift(finfo->hfinfo); */ /* (value & mask) >> shift */
|
2012-10-30 19:21:24 +00:00
|
|
|
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;
|
2012-01-04 22:13:01 +00:00
|
|
|
}
|
2012-10-30 19:21:24 +00:00
|
|
|
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);
|
|
|
|
|
2012-10-30 20:24:08 +00:00
|
|
|
setCurrentIndex(i);
|
2012-01-04 22:13:01 +00:00
|
|
|
}
|
2012-10-30 20:24:08 +00:00
|
|
|
byte_view_text = qobject_cast<ByteViewText*>(widget(++i));
|
2012-01-04 22:13:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-09-04 08:18:31 +00:00
|
|
|
|
2012-10-30 19:21:24 +00:00
|
|
|
void ByteViewTab::setCaptureFile(capture_file *cf)
|
|
|
|
{
|
|
|
|
cap_file_ = cf;
|
|
|
|
}
|
|
|
|
|
2014-09-23 20:35:10 +00:00
|
|
|
void ByteViewTab::setMonospaceFont(const QFont &mono_font)
|
|
|
|
{
|
|
|
|
mono_font_ = mono_font;
|
|
|
|
emit monospaceFontChanged(mono_font_);
|
|
|
|
update();
|
|
|
|
}
|
|
|
|
|
2012-09-04 08:18:31 +00:00
|
|
|
/*
|
|
|
|
* 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:
|
|
|
|
*/
|