Do case insensitive search for lua scripts to load.
[obnox/wireshark/wip.git] / gtk / export_object.c
1 /* export_object.c
2  * Common routines for tracking & saving objects found in streams of data
3  * Copyright 2007, Stephen Fisher (see AUTHORS file)
4  *
5  * $Id$
6  *
7  * Wireshark - Network traffic analyzer
8  * By Gerald Combs <gerald@wireshark.org>
9  * Copyright 1998 Gerald Combs
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
24  * USA.
25  */
26
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #include <gtk/gtk.h>
32
33 #ifdef HAVE_FCNTL_H
34 #include <fcntl.h>
35 #endif
36
37 #ifdef HAVE_UNISTD_H
38 #include <unistd.h>
39 #endif
40
41 #include <epan/packet_info.h>
42 #include <epan/prefs.h>
43 #include <epan/tap.h>
44
45 #include <../alert_box.h>
46 #include <../simple_dialog.h>
47 #include <wsutil/file_util.h>
48
49 #include <gtk/dlg_utils.h>
50 #include <gtk/file_dlg.h>
51 #include <gtk/gui_utils.h>
52 #include <gtk/help_dlg.h>
53 #include <gtk/main.h>
54 #include <gtk/stock_icons.h>
55 #include "gtk/export_object.h"
56 #include <string.h>
57
58 enum {
59         EO_PKT_NUM_COLUMN,
60         EO_HOSTNAME_COLUMN,
61         EO_CONTENT_TYPE_COLUMN,
62         EO_BYTES_COLUMN,
63         EO_FILENAME_COLUMN,
64         EO_NUM_COLUMNS /* must be last */
65 };
66
67 static eo_protocoldata_reset_cb eo_protocoldata_reset = NULL;
68
69
70 static void
71 eo_remember_this_row(GtkTreeModel *model _U_, GtkTreePath *path,
72                      GtkTreeIter *iter _U_, gpointer arg)
73 {
74         export_object_list_t *object_list = arg;
75         export_object_entry_t *entry;
76
77         gint *path_index;
78
79         if((path_index = gtk_tree_path_get_indices(path)) == NULL)
80                 /* Row not found in tree - shouldn't happen */
81                 return;
82
83         object_list->row_selected = path_index[0];
84
85         /* Select the corresponding packet in the packet list */
86         entry = g_slist_nth_data(object_list->entries,
87                                  object_list->row_selected);
88         cf_goto_frame(&cfile, entry->pkt_num);
89 }
90
91 static void
92 eo_remember_row_num(GtkTreeSelection *sel, gpointer data)
93 {
94         gtk_tree_selection_selected_foreach(sel, eo_remember_this_row, data);
95 }
96
97
98 /* Called when the Export Object window is closed in any way */
99 static void
100 eo_win_destroy_cb(GtkWindow *win _U_, gpointer data)
101 {
102         export_object_list_t *object_list = data;
103         export_object_entry_t *entry;
104         GSList *slist = object_list->entries;
105
106         protect_thread_critical_region();
107         remove_tap_listener(object_list);
108         unprotect_thread_critical_region();
109
110         /* Free the GSList attributes */
111         while(slist) {
112                 entry = slist->data;
113
114                 g_free(entry->hostname);
115                 g_free(entry->content_type);
116                 g_free(entry->filename);
117                 g_free(entry->payload_data);
118
119                 slist = slist->next;
120                 g_free(entry);
121         }
122
123         /* Free the GSList elements */
124         g_slist_free(object_list->entries);
125         g_free(object_list);
126
127         /* Free the private export_object_xxx data */
128         if (eo_protocoldata_reset != NULL) eo_protocoldata_reset();
129 }
130
131 static gboolean
132 eo_save_entry(gchar *save_as_filename, export_object_entry_t *entry, gboolean show_err)
133 {
134         int to_fd;
135         gint64 bytes_left;
136         int bytes_to_write;
137         ssize_t bytes_written;
138         guint8 *ptr;
139         int err;
140
141         to_fd = ws_open(save_as_filename, O_WRONLY | O_CREAT | O_EXCL |
142                          O_BINARY, 0644);
143         if(to_fd == -1) { /* An error occurred */
144                 if (show_err)
145                         open_failure_alert_box(save_as_filename, errno, TRUE);
146                 g_free(save_as_filename);
147                 return FALSE;
148         }
149
150         /*
151          * The third argument to _write() on Windows is an unsigned int,
152          * so, on Windows, that's the size of the third argument to
153          * ws_write().
154          *
155          * The third argument to write() on UN*X is a size_t, although
156          * the return value is an ssize_t, so one probably shouldn't
157          * write more than the max value of an ssize_t.
158          *
159          * In either case, there's no guarantee that a gint64 such as
160          * payload_len can be passed to ws_write(), so we write in
161          * chunks of, at most 2^31 bytes.
162          */
163         ptr = entry->payload_data;
164         bytes_left = entry->payload_len;
165         while (bytes_left != 0) {
166                 if (bytes_left > 0x40000000)
167                         bytes_to_write = 0x40000000;
168                 else
169                         bytes_to_write = (int)bytes_left;
170                 bytes_written = ws_write(to_fd, ptr, bytes_to_write);
171                 if(bytes_written <= 0) {
172                         if (bytes_written < 0)
173                                 err = errno;
174                         else
175                                 err = WTAP_ERR_SHORT_WRITE;
176                         if (show_err)
177                                 write_failure_alert_box(save_as_filename, err);
178                         ws_close(to_fd);
179                         g_free(save_as_filename);
180                         return FALSE;
181                 }
182                 bytes_left -= bytes_written;
183                 ptr += bytes_written;
184         }
185         if (ws_close(to_fd) < 0) {
186                 if (show_err)
187                         write_failure_alert_box(save_as_filename, errno);
188                 g_free(save_as_filename);
189                 return FALSE;
190         }
191
192         g_free(save_as_filename);
193         return TRUE;
194 }
195
196
197 static void
198 eo_save_clicked_cb(GtkWidget *widget _U_, gpointer arg)
199 {
200         GtkWidget *save_as_w;
201         export_object_list_t *object_list = arg;
202         export_object_entry_t *entry = NULL;
203
204         entry = g_slist_nth_data(object_list->entries,
205                                  object_list->row_selected);
206
207         if(!entry) {
208                 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK, "No object was selected for saving.  Please click on an object and click save again.");
209                 return;
210         }
211
212         save_as_w = file_selection_new("Wireshark: Save Object As ...",
213                                        FILE_SELECTION_SAVE);
214
215         gtk_window_set_transient_for(GTK_WINDOW(save_as_w),
216                                      GTK_WINDOW(object_list->dlg));
217
218         gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(save_as_w),
219                                           entry->filename);
220
221         if(gtk_dialog_run(GTK_DIALOG(save_as_w)) == GTK_RESPONSE_ACCEPT)
222                 eo_save_entry(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(save_as_w)), entry, TRUE);
223
224         window_destroy(save_as_w);
225 }
226
227 #define HINIBBLE(x)             (((x) >> 4) & 0xf)
228 #define LONIBBLE(x)             ((x) & 0xf)
229 #define HEXTOASCII(x)   (((x) < 10) ? ((x) + '0') : ((x) - 10 + 'a'))
230 #define MAXFILELEN              255
231
232 static GString *eo_rename(GString *gstr, gchar dup)
233 {
234         gchar tmp[4] = "( )";
235         gchar *tmp_ptr;
236         GString *ext_str;
237
238         tmp[1] = dup;
239         if ( (tmp_ptr = strrchr(gstr->str, '.')) != NULL ) {
240                 /* Retain the extension */
241                 ext_str = g_string_new(tmp_ptr);
242                 gstr = g_string_truncate(gstr, gstr->len - ext_str->len);
243                 if ( gstr->len >= (MAXFILELEN - (strlen(tmp) + ext_str->len)) )
244                         gstr = g_string_truncate(gstr, MAXFILELEN - (strlen(tmp) + ext_str->len));
245                 gstr = g_string_append(gstr, tmp);
246                 gstr = g_string_append(gstr, ext_str->str);
247                 g_string_free(ext_str, TRUE);
248         }
249         else {
250                 if ( gstr->len >= (MAXFILELEN - strlen(tmp)) )
251                         gstr = g_string_truncate(gstr, MAXFILELEN - strlen(tmp));
252                 gstr = g_string_append(gstr, tmp);
253         }
254         return gstr;
255 }
256
257 static GString *
258 eo_massage_str(const gchar *in_str, gsize maxlen, gchar dup)
259 {
260         gchar *tmp_ptr;
261         /* The characters in "reject" come from:
262          * http://msdn.microsoft.com/en-us/library/aa365247%28VS.85%29.aspx.
263          * Add to the list as necessary for other OS's.
264          */
265         const gchar *reject = "<>:\"/\\|?*"
266                 "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a"
267         "\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14"
268         "\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f";
269         GString *out_str;
270         GString *ext_str;
271
272         out_str = g_string_new("");
273
274         /* Find all disallowed characters/bytes and replace them with %xx */
275         while ( (tmp_ptr = strpbrk(in_str, reject)) != NULL ) {
276                 out_str = g_string_append_len(out_str, in_str, tmp_ptr - in_str);
277                 out_str = g_string_append_c(out_str, '%');
278                 out_str = g_string_append_c(out_str, HEXTOASCII(HINIBBLE(*tmp_ptr)));
279                 out_str = g_string_append_c(out_str, HEXTOASCII(LONIBBLE(*tmp_ptr)));
280                 in_str = tmp_ptr + 1;
281         }
282         out_str = g_string_append(out_str, in_str);
283         if ( out_str->len > maxlen ) {
284                 if ( (tmp_ptr = strrchr(out_str->str, '.')) != NULL ) {
285                         /* Retain the extension */
286                         ext_str = g_string_new(tmp_ptr);
287                         out_str = g_string_truncate(out_str, maxlen - ext_str->len);
288                         out_str = g_string_append(out_str, ext_str->str);
289                         g_string_free(ext_str, TRUE);
290                 }
291                 else
292                         out_str = g_string_truncate(out_str, maxlen);
293         }
294         if ( dup != '0' )
295                 out_str = eo_rename(out_str, dup);
296         return out_str;
297 }
298
299 static void
300 eo_save_all_clicked_cb(GtkWidget *widget _U_, gpointer arg)
301 {
302         gchar *save_as_fullpath;
303         export_object_list_t *object_list = arg;
304         export_object_entry_t *entry;
305         GtkWidget *save_in_w;
306         GSList *slist = object_list->entries;
307         gboolean all_saved = TRUE;
308         gchar *save_in_path;
309         GString *safe_filename;
310         int count = 0;
311
312         save_in_w = file_selection_new("Wireshark: Save All Objects In ...",
313                                        FILE_SELECTION_CREATE_FOLDER);
314
315         gtk_window_set_transient_for(GTK_WINDOW(save_in_w),
316                                      GTK_WINDOW(object_list->dlg));
317
318         if(gtk_dialog_run(GTK_DIALOG(save_in_w)) == GTK_RESPONSE_ACCEPT) {
319                 while(slist) {
320                         entry = slist->data;
321
322                         save_in_path = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(save_in_w));
323                         if ( strlen(save_in_path) < MAXFILELEN ) {
324                                 do {
325                                         safe_filename = eo_massage_str(entry->filename,
326                                                 MAXFILELEN - strlen(save_in_path), count | 0x30);
327                                         save_as_fullpath = g_build_filename(
328                                                 save_in_path, safe_filename->str, NULL);
329                                         g_string_free(safe_filename, TRUE);
330                                 } while ( g_file_test(save_as_fullpath, G_FILE_TEST_EXISTS) && (++count < 10) );
331                                 count = 0;
332                                 if (!eo_save_entry(save_as_fullpath, entry, TRUE))
333                                         all_saved = FALSE;
334                         }
335                         else
336                                 all_saved = FALSE;
337
338                         slist = slist->next;
339                 }
340         }
341
342         if (!all_saved)
343                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
344                       "Some files could not be saved.");
345
346         window_destroy(save_in_w);
347 }
348
349 /* Runs at the beginning of tapping only */
350 static void
351 eo_reset(void *tapdata)
352 {
353         export_object_list_t *object_list = tapdata;
354
355         object_list->entries = NULL;
356         object_list->iter = NULL;
357         object_list->row_selected = -1;
358
359         if (eo_protocoldata_reset != NULL) eo_protocoldata_reset();
360 }
361
362 static void
363 eo_draw(void *tapdata)
364 {
365         export_object_list_t *object_list = tapdata;
366         export_object_entry_t *eo_entry;
367
368         GSList *slist = object_list->entries;
369         GtkTreeIter new_iter;
370
371         /*  Free the tree first, since we may get called more than once for the same capture 
372             Not doing so caused duplicate entries and clicking them caused crashes.
373         */
374
375         gtk_tree_store_clear(object_list->store);
376
377         while(slist) {
378                 eo_entry = slist->data;
379
380                 gtk_tree_store_append(object_list->store, &new_iter,
381                                       object_list->iter);
382
383                 gtk_tree_store_set(object_list->store, &new_iter,
384                                    EO_PKT_NUM_COLUMN, eo_entry->pkt_num,
385                                    EO_HOSTNAME_COLUMN, eo_entry->hostname,
386                                    EO_CONTENT_TYPE_COLUMN, eo_entry->content_type,
387                                    EO_BYTES_COLUMN, eo_entry->payload_len,
388                                    EO_FILENAME_COLUMN, eo_entry->filename,
389                                    -1);
390
391                 slist = slist->next;
392         }
393 }
394
395 void
396 export_object_window(const gchar *tapname, const gchar *name, tap_packet_cb tap_packet, eo_protocoldata_reset_cb eo_protocoldata_resetfn)
397 {
398         GtkWidget *sw;
399         GtkCellRenderer *renderer;
400         GtkTreeViewColumn *column;
401         GtkTreeSelection *selection;
402         GtkWidget *vbox, *bbox, *help_bt, *cancel_bt, *save_bt, *save_all_bt;
403         GString *error_msg;
404         export_object_list_t *object_list;
405         gchar *window_title;
406
407         /* Initialize the pointer to the private data clearing function */
408         eo_protocoldata_reset = eo_protocoldata_resetfn;
409
410         /* Initialize our object list structure */
411         object_list = g_malloc0(sizeof(export_object_list_t));
412
413         /* Data will be gathered via a tap callback */
414         error_msg = register_tap_listener(tapname, object_list, NULL, 0,
415                                           eo_reset,
416                                           tap_packet,
417                                           eo_draw);
418
419         if (error_msg) {
420                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
421                               "Can't register %s tap: %s\n", name, error_msg->str);
422                 g_string_free(error_msg, TRUE);
423                 g_free(object_list);
424                 return;
425         }
426
427         /* Setup our GUI window */
428         window_title = g_strdup_printf("Wireshark: %s object list", name);
429         object_list->dlg = dlg_window_new(window_title);
430         g_free(window_title);
431
432         gtk_window_set_default_size(GTK_WINDOW(object_list->dlg),
433                                     DEF_WIDTH, DEF_HEIGHT);
434
435         vbox = gtk_vbox_new(FALSE, 5);
436
437         gtk_container_set_border_width(GTK_CONTAINER(vbox), 5);
438         gtk_container_add(GTK_CONTAINER(object_list->dlg), vbox);
439
440         sw = scrolled_window_new(NULL, NULL);
441         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
442                                             GTK_SHADOW_IN);
443
444         gtk_container_add(GTK_CONTAINER(vbox), sw);
445
446         object_list->store = gtk_tree_store_new(EO_NUM_COLUMNS,
447                                                  G_TYPE_INT, G_TYPE_STRING,
448                                                  /* we need a UINT64
449                                                     (was G_TYPE_STRING, G_TYPE_INT,) */
450                                                  G_TYPE_STRING, G_TYPE_INT64,
451                                                  G_TYPE_STRING);
452
453         object_list->tree = tree_view_new(GTK_TREE_MODEL(object_list->store));
454         g_object_unref(G_OBJECT(object_list->store));
455
456         object_list->tree_view = GTK_TREE_VIEW(object_list->tree);
457
458         renderer = gtk_cell_renderer_text_new();
459         column = gtk_tree_view_column_new_with_attributes("Packet num",
460                                                           renderer,
461                                                           "text",
462                                                           EO_PKT_NUM_COLUMN,
463                                                           NULL);
464         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
465         gtk_tree_view_append_column(object_list->tree_view, column);
466
467         renderer = gtk_cell_renderer_text_new();
468         column = gtk_tree_view_column_new_with_attributes("Hostname",
469                                                           renderer,
470                                                           "text",
471                                                           EO_HOSTNAME_COLUMN,
472                                                           NULL);
473         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
474         gtk_tree_view_append_column(object_list->tree_view, column);
475
476         renderer = gtk_cell_renderer_text_new();
477         column = gtk_tree_view_column_new_with_attributes("Content Type",
478                                                           renderer,
479                                                           "text",
480                                                           EO_CONTENT_TYPE_COLUMN,
481                                                           NULL);
482         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
483         gtk_tree_view_append_column(object_list->tree_view, column);
484
485         renderer = gtk_cell_renderer_text_new();
486         column = gtk_tree_view_column_new_with_attributes("Bytes",
487                                                           renderer,
488                                                           "text",
489                                                           EO_BYTES_COLUMN,
490                                                           NULL);
491         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
492         gtk_tree_view_append_column(object_list->tree_view, column);
493
494         renderer = gtk_cell_renderer_text_new();
495         column = gtk_tree_view_column_new_with_attributes("Filename",
496                                                           renderer,
497                                                           "text",
498                                                           EO_FILENAME_COLUMN,
499                                                           NULL);
500         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
501         gtk_tree_view_append_column(object_list->tree_view, column);
502
503         gtk_container_add(GTK_CONTAINER(sw), object_list->tree);
504
505         selection = gtk_tree_view_get_selection(object_list->tree_view);
506         g_signal_connect(selection, "changed", G_CALLBACK(eo_remember_row_num), object_list);
507
508         bbox = dlg_button_row_new(GTK_STOCK_HELP, WIRESHARK_STOCK_SAVE_ALL, GTK_STOCK_SAVE_AS, GTK_STOCK_CANCEL, NULL);
509
510         /* Help button */
511         help_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
512         g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_EXPORT_OBJECT_LIST);
513         gtk_widget_set_tooltip_text(help_bt, "Show help for this dialog.");
514
515         /* Save All button */
516         save_all_bt = g_object_get_data(G_OBJECT(bbox), WIRESHARK_STOCK_SAVE_ALL);
517         g_signal_connect(save_all_bt, "clicked", G_CALLBACK(eo_save_all_clicked_cb),
518                        object_list);
519         gtk_widget_set_tooltip_text(save_all_bt, "Save all listed objects with their displayed filenames.");
520
521         /* Save As button */
522         save_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_SAVE_AS);
523         g_signal_connect(save_bt, "clicked", G_CALLBACK(eo_save_clicked_cb), object_list);
524         gtk_widget_set_tooltip_text(save_bt, "Saves the currently selected content to a file.");
525
526         /* Cancel button */
527         cancel_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CANCEL);
528         gtk_widget_set_tooltip_text(cancel_bt, "Cancel this dialog.");
529
530         /* Pack the buttons into the "button box" */
531         gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
532         gtk_widget_show(bbox);
533
534         /* Setup cancel/delete/destroy signal handlers */
535         g_signal_connect(object_list->dlg, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
536         g_signal_connect(object_list->dlg, "destroy",
537                        G_CALLBACK(eo_win_destroy_cb), object_list);
538         window_set_cancel_button(object_list->dlg, cancel_bt,
539                                  window_cancel_button_cb);
540
541         /* Show the window */
542         gtk_widget_show_all(object_list->dlg);
543         window_present(object_list->dlg);
544
545         cf_retap_packets(&cfile);
546 }