1 /* data_printer.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 <ui/qt/utils/data_printer.h>
11 #include <ui/qt/utils/variant_pointer.h>
12 
13 #include <ui/recent.h>
14 
15 #include <wsutil/utf8_entities.h>
16 
17 #include <stdint.h>
18 
19 #include <QApplication>
20 #include <QClipboard>
21 #include <QString>
22 #include <QMimeData>
23 
DataPrinter(QObject * parent)24 DataPrinter::DataPrinter(QObject * parent)
25 : QObject(parent),
26   byteLineLength_(16)
27 {}
28 
toClipboard(DataPrinter::DumpType type,IDataPrintable * printable)29 void DataPrinter::toClipboard(DataPrinter::DumpType type, IDataPrintable * printable)
30 {
31     const QByteArray printData = printable->printableData();
32 
33     QString clipboard_text;
34 
35     switch(type)
36     {
37     case DP_PrintableText:
38         for (int i = 0; i < printData.length(); i++) {
39             QChar ch(printData[i]);
40             if (ch.isSpace() || ch.isPrint()) {
41                 clipboard_text += ch;
42             }
43         }
44         break;
45     case DP_HexStream:
46         for (int i = 0; i < printData.length(); i++)
47             clipboard_text += QString("%1").arg((uint8_t) printData[i], 2, 16, QChar('0'));
48         break;
49     case DP_EscapedString:
50         // Beginning quote
51         clipboard_text += QString("\"");
52 
53         for (int i = 0; i < printData.length(); i++) {
54             // Terminate this line if it has reached 16 bytes,
55             // unless it is also the very last byte in the data,
56             // as the termination after this for loop will take
57             // care of that.
58             if (i % 16 == 0 && i != 0 && i != printData.length() - 1) {
59                 clipboard_text += QString("\" \\\n\"");
60             }
61             clipboard_text += QString("\\x%1").arg((uint8_t) printData[i], 2, 16, QChar('0'));
62         }
63         // End quote
64         clipboard_text += QString("\"\n");
65         break;
66     case DP_Binary:
67         binaryDump(printData);
68         break;
69     case DP_HexDump:
70         clipboard_text = hexTextDump(printData, true);
71         break;
72     case DP_HexOnly:
73         clipboard_text = hexTextDump(printData, false);
74         break;
75     default:
76         break;
77     }
78 
79     if (!clipboard_text.isEmpty()) {
80         qApp->clipboard()->setText(clipboard_text);
81     }
82 }
83 
binaryDump(const QByteArray printData)84 void DataPrinter::binaryDump(const QByteArray printData)
85 {
86     if (!printData.isEmpty()) {
87         QMimeData *mime_data = new QMimeData;
88         // gtk/gui_utils.c:copy_binary_to_clipboard says:
89         /* XXX - this is not understood by most applications,
90          * but can be pasted into the better hex editors - is
91          * there something better that we can do?
92          */
93         // As of 2015-07-30, pasting into Frhed works on Windows. Pasting into
94         // Hex Editor Neo and HxD does not.
95         mime_data->setData("application/octet-stream", printData);
96         qApp->clipboard()->setMimeData(mime_data);
97     }
98 }
99 
setByteLineLength(int bll)100 void DataPrinter::setByteLineLength(int bll)
101 {
102     byteLineLength_ = bll;
103 }
104 
byteLineLength() const105 int DataPrinter::byteLineLength() const
106 {
107     return byteLineLength_;
108 }
109 
hexChars()110 int DataPrinter::hexChars()
111 {
112     int row_width = recent.gui_bytes_view == BYTES_HEX ? 16 : 8;
113     int chars_per_byte = recent.gui_bytes_view == BYTES_HEX ? 3 : 9;
114     return (row_width * chars_per_byte) + ((row_width - 1) / separatorInterval());
115 }
116 
hexTextDump(const QByteArray printData,bool showASCII)117 QString DataPrinter::hexTextDump(const QByteArray printData, bool showASCII)
118 {
119     QString clipboard_text;
120 
121     QString byteStr;
122     QString dataStr;
123 
124     int cnt = 0;
125     while (cnt < printData.length())
126     {
127         byteStr += QString(" %1").arg((uint8_t) printData[cnt], 2, 16, QChar('0'));
128         if (showASCII)
129         {
130             QChar ch(printData[cnt]);
131             if (g_ascii_isprint(printData[cnt]))
132                 dataStr += printData[cnt];
133             else
134                 dataStr += '.';
135         }
136         cnt++;
137     }
138 
139     int lines = printData.length() / byteLineLength_;
140     if (printData.length() % byteLineLength_ > 0)
141         lines++;
142 
143     for (cnt = 0; cnt < lines; cnt++)
144     {
145         int offset = cnt * 0x10;
146 
147         clipboard_text += QString("%1  ").arg(offset, 4, 16, QChar('0'));
148         clipboard_text += byteStr.mid(offset * 3, byteLineLength_ * 3);
149 
150         if (showASCII)
151         {
152             /* separation bytes for byte and text */
153             clipboard_text += QString(3, ' ');
154 
155             /* separation bytes last line */
156             if (cnt == (lines - 1) )
157             {
158                 int remSpace = byteLineLength_ - dataStr.mid(offset, byteLineLength_).length();
159                 clipboard_text += QString(remSpace * 3, ' ');
160             }
161 
162             /* text representation */
163             clipboard_text += dataStr.mid(offset, byteLineLength_);
164         }
165 
166         clipboard_text += "\n";
167     }
168 
169     return clipboard_text;
170 }
171 
instance()172 DataPrinter * DataPrinter::instance()
173 {
174     static DataPrinter * inst = Q_NULLPTR;
175     if (inst == Q_NULLPTR)
176         inst = new DataPrinter();
177     return inst;
178 }
179 
copyActions(QObject * copyClass,QObject * data)180 QActionGroup * DataPrinter::copyActions(QObject * copyClass, QObject * data)
181 {
182     QActionGroup * actions = new QActionGroup(copyClass);
183 
184     if (! data && ! dynamic_cast<IDataPrintable *>(copyClass))
185         return actions;
186 
187     DataPrinter * dpi = DataPrinter::instance();
188 
189     if (data)
190         actions->setProperty("idataprintable", VariantPointer<QObject>::asQVariant(data));
191     else
192         actions->setProperty("idataprintable", VariantPointer<QObject>::asQVariant(copyClass));
193 
194     // Mostly duplicated from main_window.ui
195     QAction * action = new QAction(tr("Copy Bytes as Hex + ASCII Dump"), actions);
196     action->setToolTip(tr("Copy packet bytes as a hex and ASCII dump."));
197     action->setProperty("printertype", DataPrinter::DP_HexDump);
198     connect(action, &QAction::triggered, dpi, &DataPrinter::copyIDataBytes);
199 
200     action = new QAction(tr("…as Hex Dump"), actions);
201     action->setToolTip(tr("Copy packet bytes as a hex dump."));
202     action->setProperty("printertype", DataPrinter::DP_HexOnly);
203     connect(action, &QAction::triggered, dpi, &DataPrinter::copyIDataBytes);
204 
205     action = new QAction(tr("…as Printable Text"), actions);
206     action->setToolTip(tr("Copy only the printable text in the packet."));
207     action->setProperty("printertype", DataPrinter::DP_PrintableText);
208     connect(action, &QAction::triggered, dpi, &DataPrinter::copyIDataBytes);
209 
210     action = new QAction(tr("…as a Hex Stream"), actions);
211     action->setToolTip(tr("Copy packet bytes as a stream of hex."));
212     action->setProperty("printertype", DataPrinter::DP_HexStream);
213     connect(action, &QAction::triggered, dpi, &DataPrinter::copyIDataBytes);
214 
215     action = new QAction(tr("…as Raw Binary"), actions);
216     action->setToolTip(tr("Copy packet bytes as application/octet-stream MIME data."));
217     action->setProperty("printertype", DataPrinter::DP_Binary);
218     connect(action, &QAction::triggered, dpi, &DataPrinter::copyIDataBytes);
219 
220     action = new QAction(tr("…as Escaped String"), actions);
221     action->setToolTip(tr("Copy packet bytes as an escaped string."));
222     action->setProperty("printertype", DataPrinter::DP_EscapedString);
223     connect(action, &QAction::triggered, dpi, &DataPrinter::copyIDataBytes);
224 
225     return actions;
226 }
227 
copyIDataBytes(bool)228 void DataPrinter::copyIDataBytes(bool /* state */)
229 {
230     if (! dynamic_cast<QAction*>(sender()))
231         return;
232 
233     QAction * sendingAction = dynamic_cast<QAction *>(sender());
234     if (! sendingAction->actionGroup() || ! sendingAction->actionGroup()->property("idataprintable").isValid())
235         return;
236 
237     QObject * dataObject = VariantPointer<QObject>::asPtr(sendingAction->actionGroup()->property("idataprintable"));
238     if (! dataObject || ! dynamic_cast<IDataPrintable *>(dataObject))
239         return;
240 
241     int dump_type = sendingAction->property("printertype").toInt();
242 
243     if (dump_type >= 0 && dump_type <= DataPrinter::DP_Binary) {
244         DataPrinter printer;
245         printer.toClipboard((DataPrinter::DumpType) dump_type, dynamic_cast<IDataPrintable *>(dataObject));
246     }
247 }
248