smb2-dissector: learn the "REPLAY_OPERATION" flag
[obnox/wireshark/wip.git] / ui / gtk / packet_win.c
1 /* packet_win.c
2  * Routines for popping a window to display current packet
3  *
4  * Copyright 2000, Jeffrey C. Foster <jfoste@woodward.com>
5  *
6  * $Id$
7  *
8  * Wireshark - Network traffic analyzer
9  * By Gerald Combs <gerald@wireshark.org>
10  * Copyright 1998 Gerald Combs
11  *
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public License
14  * as published by the Free Software Foundation; either version 2
15  * of the License, or (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
25  *
26  * To do:
27  * - Add close button to bottom.
28  * - improve the window Title and allow user to config it
29  * - Add print support ? ( could be a mess)
30  * - Add button to have main window jump to this packet ?
31  */
32
33
34 #ifdef HAVE_CONFIG_H
35 # include "config.h"
36 #endif
37
38 #ifdef HAVE_SYS_TYPES_H
39 #include <sys/types.h>
40 #endif
41
42 #include <gtk/gtk.h>
43 #include <gdk/gdkkeysyms.h>
44 #if GTK_CHECK_VERSION(3,0,0)
45 # include <gdk/gdkkeysyms-compat.h>
46 #endif
47
48 #include <string.h>
49
50 #include <epan/epan.h>
51 #include <epan/timestamp.h>
52 #include <epan/packet.h>
53 #include <epan/prefs.h>
54 #include <epan/column.h>
55 #include <epan/addr_resolv.h>
56 #include <epan/plugins.h>
57 #include <epan/epan_dissect.h>
58 #include <epan/strutil.h>
59 #include <epan/tvbuff-int.h>
60
61 #include "../file.h"
62 #include "../print.h"
63 #include "../summary.h"
64
65 #include "ui/recent.h"
66 #include "ui/simple_dialog.h"
67 #include "ui/ui_util.h"
68
69 #include "ui/gtk/font_utils.h"
70 #include "ui/gtk/main.h"
71 #include "ui/gtk/packet_win.h"
72 #include "ui/gtk/main_proto_draw.h"
73 #include "ui/gtk/keys.h"
74 #include "ui/gtk/gtkglobals.h"
75 #include "ui/gtk/gui_utils.h"
76
77 #define BV_SIZE 75
78 #define TV_SIZE 95
79
80 /* Data structure holding information about a packet-detail window. */
81 struct PacketWinData {
82         frame_data *frame;         /* The frame being displayed */
83         union wtap_pseudo_header pseudo_header; /* Pseudo-header for packet */
84         guint8     *pd;            /* Data for packet */
85         GtkWidget  *main;
86         GtkWidget  *tv_scrollw;
87         GtkWidget  *tree_view;
88         GtkWidget  *bv_nb_ptr;
89         field_info *finfo_selected;
90         epan_dissect_t  edt;
91
92         int pd_offset;
93         int pd_bitoffset;
94 };
95
96 #ifdef WANT_PACKET_EDITOR
97 struct FieldinfoWinData {
98         frame_data *frame;         /* The frame being displayed */
99         union wtap_pseudo_header pseudo_header; /* Pseudo-header for packet */
100         guint8     *pd;            /* Data for packet */
101         int start_offset;
102
103         field_info *finfo;
104 /* fvalue */
105         GtkWidget *edit;
106         GtkWidget *repr;
107 /* byteviews */
108         GtkWidget *bv;
109         GtkWidget *app_bv;
110
111         int pd_offset;
112         int pd_bitoffset;
113 };
114
115 struct CommonWinData {
116         frame_data *frame;         /* The frame being displayed */
117         guint8     *pd;            /* Data for packet */
118
119         int pd_offset;
120         int pd_bitoffset;
121         int val;
122 };
123
124 static gboolean edit_pkt_common_key_pressed_cb(GdkEventKey *event, struct CommonWinData *DataPtr);
125 #endif
126
127 /* List of all the packet-detail windows popped up. */
128 static GList *detail_windows;
129
130 static void new_tree_view_selection_changed_cb(GtkTreeSelection *sel,
131                                                gpointer user_data);
132
133
134 static void destroy_new_window(GObject *object, gpointer user_data);
135
136 static gboolean
137 button_press_handler(GtkWidget *widget, GdkEvent *event, gpointer data _U_)
138 {
139         if (widget == NULL || event == NULL) {
140                 return FALSE;
141         }
142
143         tree_view_select(widget, (GdkEventButton *) event);
144
145         /* GDK_2BUTTON_PRESS is a doubleclick -> expand/collapse tree row */
146         if (event->type == GDK_2BUTTON_PRESS) {
147                 GtkTreePath      *path;
148
149                 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget),
150                                                   (gint) (((GdkEventButton *)event)->x),
151                                                   (gint) (((GdkEventButton *)event)->y),
152                                                   &path, NULL, NULL, NULL))
153                 {
154                         if (gtk_tree_view_row_expanded(GTK_TREE_VIEW(widget), path)) {
155                                 gtk_tree_view_collapse_row(GTK_TREE_VIEW(widget), path);
156                         }       else {
157                                 gtk_tree_view_expand_row(GTK_TREE_VIEW(widget), path, FALSE);
158                         }
159                         gtk_tree_path_free(path);
160                 }
161         }
162
163         return FALSE;
164 }
165
166 #ifdef WANT_PACKET_EDITOR
167 static field_info *
168 proto_finfo_find(proto_tree *tree, field_info *old_finfo)
169 {
170         proto_node *node;
171
172         for (node = tree->first_child; node != NULL; node = node->next) {
173                 field_info *cur = PNODE_FINFO(node);
174
175                 if (!cur)
176                         continue;
177
178                 /* check everything, if it doesn't work report to me */
179                 if (cur->hfinfo == old_finfo->hfinfo &&
180                         cur->start == old_finfo->start && cur->length == old_finfo->length &&
181                         cur->appendix_start == old_finfo->appendix_start && cur->appendix_length == old_finfo->appendix_length &&
182                         cur->tree_type == old_finfo->tree_type && cur->flags == old_finfo->flags)
183                 {
184                         return cur;
185                 }
186
187                 if ((cur = proto_finfo_find((proto_tree *)node, old_finfo)))
188                         return cur;
189         }
190         return NULL;
191 }
192
193 static gboolean
194 finfo_window_refresh(struct FieldinfoWinData *DataPtr)
195 {
196         field_info *old_finfo = DataPtr->finfo;
197         field_info *finfo;
198         epan_dissect_t edt;
199
200         const guint8 *data;
201         GtkWidget *byte_view;
202         gchar label_str[ITEM_LABEL_LENGTH];
203
204         /* always update byteviews */
205         if (DataPtr->bv && (byte_view = get_notebook_bv_ptr(DataPtr->bv))) {
206                 int pos_inside = DataPtr->pd_offset - DataPtr->start_offset - old_finfo->start;
207
208                 if (pos_inside < 0 || pos_inside >= old_finfo->length)
209                         pos_inside = -1;
210
211                 data = DataPtr->pd + DataPtr->start_offset + old_finfo->start;
212                 packet_hex_editor_print(byte_view, data, DataPtr->frame, pos_inside, DataPtr->pd_bitoffset, old_finfo->length);
213         }
214
215         if (DataPtr->app_bv && (byte_view = get_notebook_bv_ptr(DataPtr->app_bv))) {
216                 int pos_inside = DataPtr->pd_offset - DataPtr->start_offset - old_finfo->appendix_start;
217
218                 if (pos_inside < 0 || pos_inside >= old_finfo->appendix_length)
219                         pos_inside = -1;
220
221                 data = DataPtr->pd + DataPtr->start_offset + old_finfo->appendix_start;
222                 packet_hex_editor_print(byte_view, data, DataPtr->frame, pos_inside, DataPtr->pd_bitoffset, old_finfo->appendix_length);
223         }
224
225         /* redisect */
226         epan_dissect_init(&edt, TRUE, TRUE);
227         /* Makes any sense?
228         if (old_finfo->hfinfo)
229                 proto_tree_prime_hfid(edt.tree, old_finfo->hfinfo->id);
230         */
231         epan_dissect_run(&edt, &DataPtr->pseudo_header, DataPtr->pd, DataPtr->frame, NULL);
232
233         /* Try to find finfo which looks like old_finfo.
234          * We might not found one, if protocol requires specific magic values, etc... */
235         if (!(finfo = proto_finfo_find(edt.tree, old_finfo))) {
236                 epan_dissect_cleanup(&edt);
237                 gtk_entry_set_text(GTK_ENTRY(DataPtr->repr), "[finfo not found, try with another value, or restore old. If you think it is bug, fill bugreport]");
238                 return FALSE;
239         }
240
241         /* XXX, update fvalue_edit, e.g. when hexedit was changed */
242
243         if (finfo->rep == NULL) {
244                 proto_item_fill_label(finfo, label_str);
245                 gtk_entry_set_text(GTK_ENTRY(DataPtr->repr), label_str);
246         } else
247                 gtk_entry_set_text(GTK_ENTRY(DataPtr->repr), finfo->rep->representation);
248
249         epan_dissect_cleanup(&edt);
250         return TRUE;
251 }
252
253 static void
254 finfo_integer_common(struct FieldinfoWinData *DataPtr, guint64 u_val)
255 {
256         const field_info *finfo = DataPtr->finfo;
257         const header_field_info *hfinfo = finfo->hfinfo;
258         /* XXX, appendix? */
259         unsigned int finfo_offset = DataPtr->start_offset + finfo->start;
260         int finfo_length = finfo->length;
261
262         if (finfo_offset <= DataPtr->frame->cap_len && finfo_offset + finfo_length <= DataPtr->frame->cap_len) {
263                 guint32 u_mask = hfinfo->bitmask;
264
265                 while (finfo_length--) {
266                         guint8 *ptr = (FI_GET_FLAG(finfo, FI_LITTLE_ENDIAN)) ?
267                                         &(DataPtr->pd[finfo_offset++]) :
268                                         &(DataPtr->pd[finfo_offset + finfo_length]);
269
270                         if (u_mask) {
271                                 guint8 n_val = *ptr;
272                                 int i;
273
274                                 for (i = 0; i < 8; i++) {
275                                         if (u_mask & 1) {
276                                                 if (u_val & 1)
277                                                         n_val |= (1 << i);
278                                                 else
279                                                         n_val &= ~(1 << i);
280                                         }
281                                         u_mask >>= 1;
282                                         u_val >>= 1;
283                                 }
284                                 *ptr = n_val;
285
286                                 if (!u_mask)
287                                         break;
288                         } else {
289                                 *ptr = u_val & 0xff;
290                                 u_val >>= 8;
291                         }
292                 }
293         }
294         finfo_window_refresh(DataPtr);
295 }
296
297 static void
298 finfo_string_changed(GtkEditable *editable, gpointer user_data)
299 {
300         struct FieldinfoWinData *DataPtr = (struct FieldinfoWinData *) user_data;
301
302         /* XXX, appendix? */
303         const field_info *finfo = DataPtr->finfo;
304         unsigned int finfo_offset = DataPtr->start_offset + finfo->start;
305         int finfo_length = finfo->length;
306         int finfo_type = (finfo->hfinfo) ? finfo->hfinfo->type : FT_NONE;
307
308         const gchar *val = gtk_entry_get_text(GTK_ENTRY(editable));
309
310         if (finfo_offset <= DataPtr->frame->cap_len && finfo_offset + finfo_length <= DataPtr->frame->cap_len) {
311                 /* strncpy */
312                 while (finfo_length && *val) {
313                         DataPtr->pd[finfo_offset++] = *val;
314                         finfo_length--;
315                         val++;
316                 }
317
318                 /* When FT_STRINGZ is there free space for NUL? */
319                 if (finfo_type == FT_STRINGZ && finfo_length) {
320                         DataPtr->pd[finfo_offset++] = '\0';
321                         finfo_length--;
322                 }
323
324                 /* XXX, string shorter than previous one. Warn user (red background?), for now fill with NULs */
325                 while (finfo_length > 0) {
326                         DataPtr->pd[finfo_offset++] = '\0';
327                         finfo_length--;
328                 }
329         }
330         finfo_window_refresh(DataPtr);
331 }
332
333 static void
334 finfo_boolean_changed(GtkToggleButton *togglebutton, gpointer user_data)
335 {
336         struct FieldinfoWinData *DataPtr = (struct FieldinfoWinData *) user_data;
337
338         gboolean val = gtk_toggle_button_get_active(togglebutton);
339
340         finfo_integer_common(DataPtr, val ? G_MAXUINT64 : 0);
341 }
342
343 static void
344 finfo_integer_changed(GtkSpinButton *spinbutton, gpointer user_data)
345 {
346         struct FieldinfoWinData *DataPtr = (struct FieldinfoWinData *) user_data;
347
348         const field_info *finfo = DataPtr->finfo;
349         const header_field_info *hfinfo = finfo->hfinfo;
350         int finfo_type = (hfinfo) ? hfinfo->type : FT_NONE;
351
352         gdouble val = gtk_spin_button_get_value(spinbutton);
353         guint64 u_val;
354
355         if (finfo_type == FT_INT8 || finfo_type == FT_INT16 || finfo_type == FT_INT24 || finfo_type == FT_INT32 || finfo_type == FT_INT64)
356                 u_val = (guint64) ((gint64) val);
357
358         else if (finfo_type == FT_UINT8 || finfo_type == FT_UINT16 || finfo_type == FT_UINT24 || finfo_type == FT_UINT32 || finfo_type == FT_UINT64)
359                 u_val = (guint64) val;
360         else {
361                 g_assert_not_reached();
362                 return;
363         }
364
365         if (hfinfo->bitmask && hfinfo->bitshift > 0)
366                 u_val <<= hfinfo->bitshift;
367
368         finfo_integer_common(DataPtr, u_val);
369 }
370
371 static void
372 finfo_ipv4_changed(GtkSpinButton *spinbutton, gpointer user_data)
373 {
374         struct FieldinfoWinData *DataPtr = (struct FieldinfoWinData *) user_data;
375
376         gdouble val = gtk_spin_button_get_value(spinbutton);
377
378         finfo_integer_common(DataPtr, (guint32) val);
379 }
380
381 static gboolean
382 finfo_bv_key_pressed_cb(GtkWidget *bv _U_, GdkEventKey *event, gpointer user_data)
383 {
384         struct FieldinfoWinData *DataPtr = (struct FieldinfoWinData *)user_data;
385         const field_info *finfo = DataPtr->finfo;
386         struct CommonWinData data;
387         gboolean have_appendix;
388         gboolean ret;
389
390         /* save */
391         data.frame = DataPtr->frame;
392         data.pd = DataPtr->pd;
393         data.pd_offset = DataPtr->pd_offset;
394         data.pd_bitoffset = DataPtr->pd_bitoffset;
395
396         ret = edit_pkt_common_key_pressed_cb(event, &data);
397
398         /* restore */
399         DataPtr->pd_offset = data.pd_offset;
400         DataPtr->pd_bitoffset = data.pd_bitoffset;
401
402         /* XXX, assuming finfo->appendix_start >= finfo->start, and if appendix exists, main exists also.
403          *      easy to fix if needed */
404         have_appendix = (finfo->appendix_start >= 0 && finfo->appendix_length > 0);
405
406         if ((DataPtr->pd_offset >= DataPtr->start_offset + finfo->start && DataPtr->pd_offset < DataPtr->start_offset + finfo->start + finfo->length) ||
407                 (have_appendix && DataPtr->pd_offset >= DataPtr->start_offset + finfo->appendix_start && DataPtr->pd_offset < DataPtr->start_offset + finfo->appendix_start + finfo->appendix_length))
408                 { /* pd_offset ok */ }
409         else
410         if (have_appendix && DataPtr->pd_offset >= DataPtr->start_offset + finfo->appendix_start + finfo->appendix_length) {
411                 DataPtr->pd_offset = DataPtr->start_offset + finfo->start;
412                 DataPtr->pd_bitoffset = 0; /* first bit */
413
414         } else if (DataPtr->pd_offset >= DataPtr->start_offset + finfo->start + finfo->length) {
415                 if (have_appendix)
416                         DataPtr->pd_offset = DataPtr->start_offset + finfo->appendix_start;
417                 else
418                         DataPtr->pd_offset = DataPtr->start_offset + finfo->start;
419                 DataPtr->pd_bitoffset = 0; /* first bit */
420         }
421         else
422         if (DataPtr->pd_offset < DataPtr->start_offset + finfo->start) {
423                 if (have_appendix)
424                         DataPtr->pd_offset = DataPtr->start_offset + finfo->appendix_start + finfo->appendix_length-1;
425                 else
426                         DataPtr->pd_offset = DataPtr->start_offset + finfo->start + finfo->length-1;
427                 /* XXX, last bit/octect? */
428
429         } else if (have_appendix && DataPtr->pd_offset < DataPtr->start_offset + finfo->appendix_start) {
430                 DataPtr->pd_offset = DataPtr->start_offset + finfo->start + finfo->length-1;
431                 /* XXX, last bit/octect? */
432         }
433
434         if (ret)
435                 finfo_window_refresh(DataPtr);
436         return ret;
437 }
438
439 static gint
440 finfo_ipv4_input(GtkSpinButton *spinbutton, gpointer arg1, gpointer user_data _U_)
441 {
442         const gchar *addr_str = gtk_entry_get_text(GTK_ENTRY(spinbutton));
443         gdouble *out_val = (gdouble *) arg1;
444         guint32 addr;
445 #if 0
446         /* XXX, get_host_ipaddr() support hostname resolution */
447         if (!get_host_ipaddr(addr_str, &addr))
448                 return GTK_INPUT_ERROR;
449         addr = GUINT32_FROM_BE(addr);
450 #else
451         unsigned int a0, a1, a2, a3;
452
453         if (sscanf(addr_str, "%u.%u.%u.%u", &a0, &a1, &a2, &a3) != 4)
454                 return GTK_INPUT_ERROR;
455
456         if (a0 > 255 || a1 > 255 || a2 > 255 || a3 > 255)
457                 return GTK_INPUT_ERROR;
458
459         addr = a0 << 24 | a1 << 16 | a2 << 8 | a3;
460 #endif
461         *out_val = (gdouble) addr;
462         return TRUE;
463 }
464
465 static gboolean
466 finfo_ipv4_output(GtkSpinButton *spinbutton, gpointer user_data _U_)
467 {
468         GtkAdjustment *adj;
469         guint32 value;
470
471         adj = gtk_spin_button_get_adjustment(spinbutton);
472         value = (guint32) gtk_adjustment_get_value(adj);
473         value = GUINT32_TO_BE(value);
474         /* ip_to_str_buf((guint8*)&value, buf, MAX_IP_STR_LEN); */      /* not exported */
475         gtk_entry_set_text(GTK_ENTRY(spinbutton), ip_to_str((guint8*)&value));  /* XXX, can we ep_alloc() inside gui? */
476         return TRUE;
477 }
478
479 static gint
480 new_finfo_window(GtkWidget *w, struct FieldinfoWinData *DataPtr)
481 {
482         field_info *finfo = DataPtr->finfo;
483         const header_field_info *hfinfo = finfo->hfinfo;
484         int finfo_type = (hfinfo) ? hfinfo->type : FT_NONE;
485
486         GtkWidget *dialog = gtk_dialog_new_with_buttons("Editing finfo: ....",
487                         GTK_WINDOW(w),
488                         GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
489                         GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
490                         GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
491                         NULL);
492
493         GtkWidget *dialog_vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
494         GtkWidget *fvalue_edit;
495         GtkWidget *native_repr;
496         GtkWidget *bv_nb_ptr;
497         GtkWidget *frame, *frame_vbox;
498
499         gint result;
500
501         if (!FI_GET_FLAG(finfo, FI_LITTLE_ENDIAN) && !FI_GET_FLAG(finfo, FI_BIG_ENDIAN)) {
502                 fvalue_edit = gtk_entry_new();
503                 gtk_entry_set_text(GTK_ENTRY(fvalue_edit), "<not added by proto_tree_add_item()>");
504                 gtk_editable_set_editable(GTK_EDITABLE(fvalue_edit), FALSE);
505                 gtk_widget_set_sensitive(fvalue_edit, FALSE);
506
507         } /* else if (XXX) {
508                 fvalue_edit = gtk_entry_new();
509                 gtk_entry_set_text(GTK_ENTRY(fvalue_edit), "<ERROR: Value stored in finfo doesn't match value from tvb>");
510                 gtk_editable_set_editable(GTK_EDITABLE(fvalue_edit), FALSE);
511                 gtk_widget_set_sensitive(fvalue_edit, FALSE);
512
513         } */ else if (finfo_type == FT_INT8 || finfo_type == FT_INT16 || finfo_type == FT_INT24 || finfo_type == FT_INT32 ||
514                         finfo_type == FT_UINT8 || finfo_type == FT_UINT16 || finfo_type == FT_UINT24 || finfo_type == FT_UINT32)
515         {
516 #if GTK_CHECK_VERSION(3,0,0)
517                 GtkAdjustment *adj;
518 #else
519                 GtkObject *adj;
520 #endif
521                 int bitcount = 0;
522
523                 if (finfo_type == FT_INT8 || finfo_type == FT_UINT8)
524                         bitcount = 8;
525                 if (finfo_type == FT_INT16 || finfo_type == FT_UINT16)
526                         bitcount = 16;
527                 if (finfo_type == FT_INT24 || finfo_type == FT_UINT24)
528                         bitcount = 24;
529                 if (finfo_type == FT_INT32 || finfo_type == FT_UINT32)
530                         bitcount = 32;
531                 /* if (finfo_type == FT_INT64 || finfo_type == FT_UINT64)
532                         bitcount = 64; */
533
534                 if (finfo->length * 8 < bitcount)
535                         bitcount = finfo->length / 8;
536
537                 if (hfinfo->bitmask && hfinfo->bitshift > 0)
538                         bitcount -= hfinfo->bitshift;
539
540                 /* XXX, hfinfo->bitmask: Can we configure GTK_ADJUSTMENT to do custom step? (value-changed signal?) */
541
542                 /* XXX, I'm little worried about these casts from (unsigned) integer to double... */
543
544                 if (finfo_type == FT_INT8 || finfo_type == FT_INT16 || finfo_type == FT_INT24 || finfo_type == FT_INT32 /* || finfo_type == FT_INT64 */)
545                         adj = gtk_adjustment_new((double) fvalue_get_sinteger(&finfo->value), (double) -(G_GINT64_CONSTANT(1) << (bitcount-1)), (double) ((G_GINT64_CONSTANT(1) << (bitcount-1))-1), 1.0, 10.0, 0);
546                 else if (finfo_type == FT_UINT8 || finfo_type == FT_UINT16 || finfo_type == FT_UINT24 || finfo_type == FT_UINT32 /* || finfo_type == FT_UINT64 */ )
547                         adj = gtk_adjustment_new((double) fvalue_get_uinteger(&finfo->value), 0.0, (double) ((G_GINT64_CONSTANT(1U) << bitcount)-1), 1.0, 10.0, 0);
548                 else {
549                         g_assert_not_reached();
550                         goto not_supported;
551                 }
552
553                 fvalue_edit = gtk_spin_button_new(GTK_ADJUSTMENT(adj), 1.0, 0);
554                 gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(fvalue_edit), TRUE);
555                 g_signal_connect(fvalue_edit, "value-changed", G_CALLBACK(finfo_integer_changed), DataPtr);
556
557         } else if (finfo_type == FT_STRING || finfo_type == FT_STRINGZ) {
558                 fvalue_edit = gtk_entry_new();
559                 gtk_entry_set_max_length(GTK_ENTRY(fvalue_edit), finfo->length);
560                 gtk_entry_set_text(GTK_ENTRY(fvalue_edit), fvalue_get(&finfo->value));
561                 g_signal_connect(fvalue_edit, "changed", G_CALLBACK(finfo_string_changed), DataPtr);
562
563         } else if (finfo_type == FT_BOOLEAN) {
564                 fvalue_edit = gtk_check_button_new();
565                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(fvalue_edit), (fvalue_get_uinteger(&finfo->value) != 0));
566                 g_signal_connect(fvalue_edit, "toggled", G_CALLBACK(finfo_boolean_changed), DataPtr);
567
568         } else if (finfo_type == FT_IPv4) {
569                 guint32 net_addr = ipv4_get_net_order_addr(fvalue_get(&finfo->value));
570 #if GTK_CHECK_VERSION(3,0,0)
571                 GtkAdjustment *adj;
572 #else
573                 GtkObject *adj;
574 #endif
575                 adj = gtk_adjustment_new((double) (GUINT32_FROM_BE(net_addr)), 0.0, 4294967295.0 /* (2^32)-1 */, 1.0, 256.0, 0);
576
577                 /* XXX, create four gtk_spin_button_new which takes 0..255 */
578                 fvalue_edit = gtk_spin_button_new(GTK_ADJUSTMENT(adj), 1.0, 0);
579                 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(fvalue_edit), GTK_UPDATE_IF_VALID);
580                 g_signal_connect(fvalue_edit, "value-changed", G_CALLBACK(finfo_ipv4_changed), DataPtr);
581                 g_signal_connect(fvalue_edit, "input", G_CALLBACK(finfo_ipv4_input), NULL);
582                 g_signal_connect(fvalue_edit, "output", G_CALLBACK(finfo_ipv4_output), NULL);
583
584         } else {
585 not_supported:
586                 /* List of unsupported FT_*:
587                         FT_NONE, FT_PROTOCOL,
588                         FT_BYTES, FT_UINT_BYTES,
589                         FT_INT64, FT_UINT64,                    ; should work with FT_INT[8,16,24,32] code
590                         FT_FLOAT, FT_DOUBLE,
591                         FT_IPXNET, FT_IPv6, FT_ETHER,
592                         FT_GUID, FT_OID,
593                         FT_UINT_STRING,
594                         FT_ABSOLUTE_TIME, FT_RELATIVE_TIME
595                 */
596                 fvalue_edit = gtk_entry_new();
597                 gtk_entry_set_text(GTK_ENTRY(fvalue_edit), "<not supported>");
598                 gtk_editable_set_editable(GTK_EDITABLE(fvalue_edit), FALSE);
599                 gtk_widget_set_sensitive(fvalue_edit, FALSE);
600         }
601         gtk_box_pack_start(GTK_BOX(dialog_vbox), fvalue_edit, FALSE, FALSE, 0);
602         gtk_widget_show(fvalue_edit);
603
604         DataPtr->edit = fvalue_edit;
605
606         native_repr = gtk_entry_new();
607         gtk_editable_set_editable(GTK_EDITABLE(native_repr), FALSE);
608         gtk_widget_set_sensitive(native_repr, FALSE);
609         gtk_box_pack_start(GTK_BOX(dialog_vbox), native_repr, FALSE, FALSE, 0);
610         gtk_widget_show(native_repr);
611
612         DataPtr->repr = native_repr;
613
614         frame = gtk_frame_new("Hex edit");
615         frame_vbox = gtk_vbox_new(TRUE, 1);
616
617         /* raw hex edit */
618         if (finfo->start >= 0 && finfo->length > 0) {
619                 GtkWidget *byte_view;
620                 /* Byte view */
621                 bv_nb_ptr = byte_view_new();
622                 gtk_container_add(GTK_CONTAINER(frame_vbox), bv_nb_ptr);
623                 gtk_widget_set_size_request(bv_nb_ptr, -1, BV_SIZE);
624                 gtk_widget_show(bv_nb_ptr);
625
626                 if ((byte_view = get_notebook_bv_ptr(bv_nb_ptr)))
627                         g_signal_connect(byte_view, "key-press-event", G_CALLBACK(finfo_bv_key_pressed_cb), DataPtr);
628                 DataPtr->bv = bv_nb_ptr;
629         }
630
631         if (finfo->appendix_start >= 0 && finfo->appendix_length > 0) {
632                 GtkWidget *byte_view;
633                 /* Appendix byte view */
634                 bv_nb_ptr = byte_view_new();
635                 gtk_container_add(GTK_CONTAINER(frame_vbox), bv_nb_ptr);
636                 gtk_widget_set_size_request(bv_nb_ptr, -1, BV_SIZE);
637                 gtk_widget_show(bv_nb_ptr);
638
639                 if ((byte_view = get_notebook_bv_ptr(bv_nb_ptr)))
640                         g_signal_connect(byte_view, "key-press-event", G_CALLBACK(finfo_bv_key_pressed_cb), DataPtr);
641                 DataPtr->app_bv = bv_nb_ptr;
642         }
643         gtk_container_add(GTK_CONTAINER(frame), frame_vbox);
644         gtk_widget_show(frame_vbox); gtk_widget_show(frame);
645         gtk_container_add(GTK_CONTAINER(dialog_vbox), frame);
646
647         gtk_window_set_default_size(GTK_WINDOW(dialog), DEF_WIDTH, -1);
648         finfo_window_refresh(DataPtr);
649         result = gtk_dialog_run(GTK_DIALOG(dialog));
650         gtk_widget_destroy(dialog);
651         return result;
652 }
653
654 static void
655 edit_pkt_tree_row_activated_cb(GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column _U_, gpointer user_data)
656 {
657         struct PacketWinData *DataPtr = (struct PacketWinData*)user_data;
658         GtkTreeModel *model;
659         GtkTreeIter iter;
660         field_info *finfo;
661
662         model = gtk_tree_view_get_model(tree_view);
663         if (!gtk_tree_model_get_iter(model, &iter, path))
664                 return;
665
666         gtk_tree_model_get(model, &iter, 1, &finfo, -1);
667         if (!finfo)
668                 return;
669
670         if (!FI_GET_FLAG(finfo, FI_GENERATED) &&
671                         finfo->ds_tvb && finfo->ds_tvb->real_data >= DataPtr->pd && finfo->ds_tvb->real_data <= DataPtr->pd + DataPtr->frame->cap_len)
672         {
673                 struct FieldinfoWinData data;
674
675                 data.frame = DataPtr->frame;
676                 data.pseudo_header = DataPtr->pseudo_header;
677                 data.pd = g_memdup(DataPtr->pd, DataPtr->frame->cap_len);
678                 data.start_offset = (int) (finfo->ds_tvb->real_data - DataPtr->pd);
679
680                 data.finfo = finfo;
681                 data.app_bv = data.bv = NULL;
682                 data.repr = data.edit = NULL;
683
684                 data.pd_offset = data.start_offset + finfo->start;
685                 data.pd_bitoffset = 0;
686
687                 if (new_finfo_window(DataPtr->main, &data) == GTK_RESPONSE_ACCEPT) {
688                         /* DataPtr->pseudo_header = data.pseudo_header; */
689                         memcpy(DataPtr->pd, data.pd, DataPtr->frame->cap_len);
690
691                         epan_dissect_cleanup(&(DataPtr->edt));
692                         epan_dissect_init(&(DataPtr->edt), TRUE, TRUE);
693                         epan_dissect_run(&(DataPtr->edt), &DataPtr->pseudo_header, DataPtr->pd, DataPtr->frame, NULL);
694                         add_byte_views(&(DataPtr->edt), DataPtr->tree_view, DataPtr->bv_nb_ptr);
695                         proto_tree_draw(DataPtr->edt.tree, DataPtr->tree_view);
696                 }
697                 g_free(data.pd);
698
699         } else {
700                 /* XXX, simple_dialog() is shown on top of main_window, instead of edit_window. */
701                 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK, "Item can't be edited. FI_GENERATED or tvb not subset of packet data (uncompressed?)");
702         }
703 }
704
705 static gboolean
706 edit_pkt_common_key_pressed_cb(GdkEventKey *event, struct CommonWinData *DataPtr)
707 {
708         int val = -1;
709
710         switch (recent.gui_bytes_view) {
711         case BYTES_HEX:
712                 if (event->keyval >= 'a' && event->keyval <= 'f')
713                         val = (event->keyval - 'a') + 10;
714                 else if (event->keyval >= 'A' && event->keyval <= 'F')
715                         val = (event->keyval - 'A') + 10;
716                 else if (event->keyval >= '0' && event->keyval <= '9')
717                         val = (event->keyval - '0');
718                 else if (event->keyval == GDK_Left)
719                         DataPtr->pd_bitoffset -= 4;
720                 else if (event->keyval == GDK_Right)
721                         DataPtr->pd_bitoffset += 4;
722                 else
723                         return FALSE;
724
725                 if (val != -1) {
726                         /* Lazy...
727                          * XXX Allow (DataPtr->pd_bitoffset % 4) != 0 ? */
728                         if (DataPtr->pd_bitoffset < 4) {
729                                 DataPtr->pd[DataPtr->pd_offset] = (DataPtr->pd[DataPtr->pd_offset] & 0x0f) | (val << 4);
730                                 DataPtr->pd_bitoffset = 4;
731                         } else {
732                                 DataPtr->pd[DataPtr->pd_offset] = (DataPtr->pd[DataPtr->pd_offset] & 0xf0) | val;
733                                 DataPtr->pd_bitoffset = 8;
734                         }
735                         /* DataPtr->pd_bitoffset += 4; */
736                 }
737                 break;
738
739         case BYTES_BITS:
740                 if (event->keyval == '0' || event->keyval == '1')
741                         val = (event->keyval != '0');
742                 else if (event->keyval == GDK_Left)
743                         DataPtr->pd_bitoffset -= 1;
744                 else if (event->keyval == GDK_Right)
745                         DataPtr->pd_bitoffset += 1;
746                 else
747                         return FALSE;
748
749                 if (val != -1) {
750                         if (val)
751                                 DataPtr->pd[DataPtr->pd_offset] |= (1 << (7-DataPtr->pd_bitoffset));
752                         else
753                                 DataPtr->pd[DataPtr->pd_offset] &= ~(1 << (7-DataPtr->pd_bitoffset));
754                         DataPtr->pd_bitoffset += 1;
755                 }
756                 break;
757
758         default:
759                 g_assert_not_reached();
760                 return FALSE;
761         }
762
763         while (DataPtr->pd_bitoffset >= 8) {
764                 DataPtr->pd_offset += 1;
765                 DataPtr->pd_bitoffset -= 8;
766         }
767         while (DataPtr->pd_bitoffset < 0) {
768                 DataPtr->pd_offset -= 1;
769                 DataPtr->pd_bitoffset += 8;
770         }
771
772         DataPtr->val = val;
773         return TRUE;
774 }
775
776 static gboolean
777 edit_pkt_win_key_pressed_cb(GtkWidget *win _U_, GdkEventKey *event, gpointer user_data)
778 {
779         struct PacketWinData *DataPtr = (struct PacketWinData *)user_data;
780         struct CommonWinData data;
781         GSList *src_le;
782         gboolean ret;
783         tvbuff_t *ds_tvb = NULL;
784
785         /* save */
786         data.frame = DataPtr->frame;
787         data.pd = DataPtr->pd;
788         data.pd_offset = DataPtr->pd_offset;
789         data.pd_bitoffset = DataPtr->pd_bitoffset;
790
791         ret = edit_pkt_common_key_pressed_cb(event, &data);
792
793         /* restore */
794         DataPtr->pd_offset = data.pd_offset;
795         DataPtr->pd_bitoffset = data.pd_bitoffset;
796
797         if (DataPtr->pd_offset < 0) {
798                 DataPtr->pd_offset = DataPtr->frame->cap_len-1;
799                 /* XXX, last bit/octect? */
800         }
801
802         if ((guint)DataPtr->pd_offset >= DataPtr->frame->cap_len) {
803                 DataPtr->pd_offset = 0;
804                 DataPtr->pd_bitoffset = 0; /* first bit */
805         }
806
807         if (!ret)
808                 return FALSE;
809
810         /* redissect if changed */
811         if (data.val != -1) {
812                 /* XXX, can be optimized? */
813                 epan_dissect_cleanup(&(DataPtr->edt));
814                 epan_dissect_init(&(DataPtr->edt), TRUE, TRUE);
815                 epan_dissect_run(&(DataPtr->edt), &DataPtr->pseudo_header, DataPtr->pd, DataPtr->frame, NULL);
816                 add_byte_views(&(DataPtr->edt), DataPtr->tree_view, DataPtr->bv_nb_ptr);
817                 proto_tree_draw(DataPtr->edt.tree, DataPtr->tree_view);
818         }
819
820         for (src_le = DataPtr->edt.pi.data_src; src_le != NULL; src_le = src_le->next) {
821                 const data_source *src = src_le->data;
822                 tvbuff_t *tvb = src->tvb;
823
824                 if (tvb && tvb->real_data == DataPtr->pd) {
825                         ds_tvb = tvb;
826                         break;
827                 }
828         }
829
830         if (ds_tvb != NULL) {
831                 GtkWidget    *byte_view;
832
833                 set_notebook_page(DataPtr->bv_nb_ptr, ds_tvb);
834                 byte_view = get_notebook_bv_ptr(DataPtr->bv_nb_ptr);
835                 if (byte_view)
836                         packet_hex_editor_print(byte_view, DataPtr->pd, DataPtr->frame, DataPtr->pd_offset, DataPtr->pd_bitoffset, DataPtr->frame->cap_len);
837         }
838         return TRUE;
839 }
840
841 static void
842 edit_pkt_destroy_new_window(GObject *object _U_, gpointer user_data)
843 {
844         /* like destroy_new_window, but without freeding DataPtr->pd */
845         struct PacketWinData *DataPtr = user_data;
846
847         detail_windows = g_list_remove(detail_windows, DataPtr);
848         epan_dissect_cleanup(&(DataPtr->edt));
849         g_free(DataPtr);
850
851         /* XXX, notify main packet list that packet should be redisplayed */
852 }
853
854 static gint g_direct_compare_func(gconstpointer a, gconstpointer b, gpointer user_data _U_) {
855         if (a > b)
856                 return 1;
857         else if (a < b)
858                 return -1;
859         else
860                 return 0;
861 }
862
863 static void modifed_frame_data_free(gpointer data) {
864         modified_frame_data *mfd = (modified_frame_data *) data;
865
866         g_free(mfd->pd);
867         g_free(mfd);
868 }
869
870 #endif /* WANT_PACKET_EDITOR */
871
872 void new_packet_window(GtkWidget *w _U_, gboolean editable _U_)
873 {
874 #define NewWinTitleLen 1000
875         char Title[NewWinTitleLen] = "";
876         const char *TextPtr;
877         GtkWidget *main_w, *main_vbox, *pane,
878                 *tree_view, *tv_scrollw,
879                 *bv_nb_ptr;
880         struct PacketWinData *DataPtr;
881         int i;
882
883         if (!cfile.current_frame) {
884                 /* nothing has been captured so far */
885                 return;
886         }
887
888         /* With the new packetlists "lazy columns" it's neccesary to reread the frame */
889         if (!cf_read_frame(&cfile, cfile.current_frame)) {
890                 /* error reading the frame */
891                 return;
892         }
893
894         /* Allocate data structure to represent this window. */
895         DataPtr = (struct PacketWinData *) g_malloc(sizeof(struct PacketWinData));
896
897         DataPtr->frame = cfile.current_frame;
898         memcpy(&DataPtr->pseudo_header, &cfile.pseudo_header, sizeof DataPtr->pseudo_header);
899         DataPtr->pd = g_malloc(DataPtr->frame->cap_len);
900         memcpy(DataPtr->pd, cfile.pd, DataPtr->frame->cap_len);
901
902         epan_dissect_init(&(DataPtr->edt), TRUE, TRUE);
903         epan_dissect_run(&(DataPtr->edt), &DataPtr->pseudo_header, DataPtr->pd,
904                          DataPtr->frame, &cfile.cinfo);
905         epan_dissect_fill_in_columns(&(DataPtr->edt), FALSE, TRUE);
906
907         /*
908          * Build title of window by getting column data constructed when the
909          * frame was dissected.
910          */
911         for (i = 0; i < cfile.cinfo.num_cols; ++i) {
912                 TextPtr = cfile.cinfo.col_data[i];
913                 if ((strlen(Title) + strlen(TextPtr)) < NewWinTitleLen - 1) {
914                         g_strlcat(Title, TextPtr, NewWinTitleLen);
915                         g_strlcat(Title, " ", NewWinTitleLen);
916                 }
917         }
918
919         main_w = window_new(GTK_WINDOW_TOPLEVEL, Title);
920         gtk_window_set_default_size(GTK_WINDOW(main_w), DEF_WIDTH, -1);
921
922         /* Container for paned windows  */
923         main_vbox = gtk_vbox_new(FALSE, 1);
924         gtk_container_set_border_width(GTK_CONTAINER(main_vbox), 1);
925         gtk_container_add(GTK_CONTAINER(main_w), main_vbox);
926         gtk_widget_show(main_vbox);
927
928         /* Panes for the tree and byte view */
929         pane = gtk_vpaned_new();
930         gtk_container_add(GTK_CONTAINER(main_vbox), pane);
931         gtk_widget_show(pane);
932
933         /* Tree view */
934         tv_scrollw = main_tree_view_new(&prefs, &tree_view);
935         gtk_paned_pack1(GTK_PANED(pane), tv_scrollw, TRUE, TRUE);
936         gtk_widget_set_size_request(tv_scrollw, -1, TV_SIZE);
937         gtk_widget_show(tv_scrollw);
938         gtk_widget_show(tree_view);
939
940         /* Byte view */
941         bv_nb_ptr = byte_view_new();
942         gtk_paned_pack2(GTK_PANED(pane), bv_nb_ptr, FALSE, FALSE);
943         gtk_widget_set_size_request(bv_nb_ptr, -1, BV_SIZE);
944         gtk_widget_show(bv_nb_ptr);
945
946         DataPtr->main = main_w;
947         DataPtr->tv_scrollw = tv_scrollw;
948         DataPtr->tree_view = tree_view;
949         DataPtr->bv_nb_ptr = bv_nb_ptr;
950         detail_windows = g_list_append(detail_windows, DataPtr);
951
952         /* load callback handlers */
953         g_signal_connect(gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view)),
954                          "changed", G_CALLBACK(new_tree_view_selection_changed_cb), DataPtr);
955         g_signal_connect(tree_view, "button_press_event", G_CALLBACK(button_press_handler), NULL);
956 #ifdef WANT_PACKET_EDITOR
957         if (editable && DataPtr->frame->cap_len != 0) {
958                 g_signal_connect(main_w, "key-press-event", G_CALLBACK(edit_pkt_win_key_pressed_cb), DataPtr);
959                 /* XXX, popup-menu instead of row-activated? */
960                 g_signal_connect(tree_view, "row-activated", G_CALLBACK(edit_pkt_tree_row_activated_cb), DataPtr);
961                 g_signal_connect(main_w, "destroy", G_CALLBACK(edit_pkt_destroy_new_window), DataPtr);
962         } else
963 #endif
964                 g_signal_connect(main_w, "destroy", G_CALLBACK(destroy_new_window), DataPtr);
965
966         /* draw the protocol tree & print hex data */
967         add_byte_views(&(DataPtr->edt), tree_view, DataPtr->bv_nb_ptr);
968         proto_tree_draw(DataPtr->edt.tree, tree_view);
969
970         DataPtr->finfo_selected = NULL;
971         DataPtr->pd_offset = 0;
972         DataPtr->pd_bitoffset = 0;
973         gtk_widget_show(main_w);
974
975 #ifdef WANT_PACKET_EDITOR
976         if (editable && DataPtr->frame->cap_len != 0) {
977                 /* XXX, there's no Save button here, so lets assume packet is always edited */
978                 modified_frame_data *mfd = g_malloc(sizeof(modified_frame_data));
979
980                 mfd->pd = DataPtr->pd;
981                 mfd->ph = DataPtr->pseudo_header;
982
983                 if (cfile.edited_frames == NULL)
984                         cfile.edited_frames = g_tree_new_full(g_direct_compare_func, NULL, NULL, modifed_frame_data_free);
985                 g_tree_insert(cfile.edited_frames, GINT_TO_POINTER(DataPtr->frame->num), mfd);
986                 DataPtr->frame->file_off = -1;
987         }
988 #endif
989 }
990
991 static void
992 destroy_new_window(GObject *object _U_, gpointer user_data)
993 {
994         struct PacketWinData *DataPtr = user_data;
995
996         detail_windows = g_list_remove(detail_windows, DataPtr);
997         epan_dissect_cleanup(&(DataPtr->edt));
998         g_free(DataPtr->pd);
999         g_free(DataPtr);
1000 }
1001
1002 /* called when a tree row is (un)selected in the popup packet window */
1003 static void
1004 new_tree_view_selection_changed_cb(GtkTreeSelection *sel, gpointer user_data)
1005 {
1006         field_info   *finfo;
1007         GtkWidget    *byte_view;
1008         const guint8 *data;
1009         guint         len;
1010         GtkTreeModel *model;
1011         GtkTreeIter   iter;
1012
1013         struct PacketWinData *DataPtr = (struct PacketWinData*)user_data;
1014
1015         /* if something is selected */
1016         if (gtk_tree_selection_get_selected(sel, &model, &iter))
1017         {
1018                 gtk_tree_model_get(model, &iter, 1, &finfo, -1);
1019                 if (!finfo) return;
1020
1021                 set_notebook_page(DataPtr->bv_nb_ptr, finfo->ds_tvb);
1022                 byte_view = get_notebook_bv_ptr(DataPtr->bv_nb_ptr);
1023                 if (!byte_view) /* exit if no hex window to write in */
1024                         return;
1025
1026                 data = get_byte_view_data_and_length(byte_view, &len);
1027                 if (data == NULL) {
1028                         data = DataPtr->pd;
1029                         len =  DataPtr->frame->cap_len;
1030                 }
1031
1032                 DataPtr->finfo_selected = finfo;
1033
1034 #ifdef WANT_PACKET_EDITOR
1035                 DataPtr->pd_offset = 0;
1036                 DataPtr->pd_bitoffset = 0;
1037
1038                 if (!FI_GET_FLAG(finfo, FI_GENERATED) &&
1039                     finfo->ds_tvb && finfo->ds_tvb->real_data >= DataPtr->pd && finfo->ds_tvb->real_data <= DataPtr->pd + DataPtr->frame->cap_len)
1040                 {
1041                         /* I haven't really test if TVB subsets works, but why not? :> */
1042                         int pd_offset = (int) (finfo->ds_tvb->real_data - DataPtr->pd);
1043
1044                         /* some code from packet_hex_print */
1045                         int finfo_offset = finfo->start;
1046                         int finfo_len = finfo->length;
1047
1048                         if (!(finfo_offset >= 0 && finfo_len > 0)) {
1049                                 finfo_offset = finfo->appendix_start;
1050                                 finfo_len = finfo->appendix_length;
1051                         }
1052
1053                         /* Don't care about things like bitmask or LE/BE, just point DataPtr->tvb_[bit]offset to proper offsets. */
1054                         if (finfo_offset >= 0 && finfo_len > 0) {
1055                                 DataPtr->pd_offset = pd_offset + finfo_offset;
1056                                 DataPtr->pd_bitoffset = 0; /* XXX */
1057                         }
1058
1059                         if (DataPtr->pd_offset < 0)
1060                                 DataPtr->pd_offset = 0;
1061                         if ((guint)DataPtr->pd_offset >= DataPtr->frame->cap_len)
1062                                 DataPtr->pd_offset = 0;
1063                 }
1064 #endif
1065
1066                 packet_hex_print(byte_view, data, DataPtr->frame, finfo, len);
1067         }
1068         else
1069         {
1070                 DataPtr->finfo_selected = NULL;
1071
1072                 byte_view = get_notebook_bv_ptr(DataPtr->bv_nb_ptr);
1073                 if (!byte_view) /* exit if no hex window to write in */
1074                         return;
1075
1076                 data = get_byte_view_data_and_length(byte_view, &len);
1077                 g_assert(data != NULL);
1078                 packet_hex_reprint(byte_view);
1079         }
1080 }
1081
1082 /* Functions called from elsewhere to act on all popup packet windows. */
1083
1084 /* Destroy all popup packet windows. */
1085 void
1086 destroy_packet_wins(void)
1087 {
1088         struct PacketWinData *DataPtr;
1089
1090         /* Destroying a packet window causes it to be removed from
1091            the list of packet windows, so we can't do a "g_list_foreach()"
1092            to go through the list of all packet windows and destroy them
1093            as we find them; instead, as long as the list is non-empty,
1094            we destroy the first window on the list. */
1095         while (detail_windows != NULL) {
1096                 DataPtr = (struct PacketWinData *)(detail_windows->data);
1097                 window_destroy(DataPtr->main);
1098         }
1099 }
1100
1101 static void
1102 redraw_packet_bytes_cb(gpointer data, gpointer user_data _U_)
1103 {
1104         struct PacketWinData *DataPtr = (struct PacketWinData *)data;
1105
1106         redraw_packet_bytes(DataPtr->bv_nb_ptr, DataPtr->frame, DataPtr->finfo_selected);
1107 }
1108
1109 /* Redraw the packet bytes part of all the popup packet windows. */
1110 void
1111 redraw_packet_bytes_packet_wins(void)
1112 {
1113         g_list_foreach(detail_windows, redraw_packet_bytes_cb, NULL);
1114 }