"Fossies" - the Fresh Open Source Software Archive 
Member "ettercap-0.8.3.1/src/interfaces/gtk3/ec_gtk3.c" (1 Aug 2020, 57992 Bytes) of package /linux/privat/ettercap-0.8.3.1.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "ec_gtk3.c" see the
Fossies "Dox" file reference documentation and the latest
Fossies "Diffs" side-by-side code changes report:
0.8.3_vs_0.8.3.1.
1 /*
2 ettercap -- GTK+3/GNOME GUI
3
4 Copyright (C) ALoR & NaGA
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19
20 */
21
22 #include <ec.h>
23
24 #include <ec_gtk3.h>
25 #include <ec_capture.h>
26 #include <ec_version.h>
27
28 #include <pcap.h>
29 #include <string.h>
30
31 /* \Device\NPF{...} and description are huge. There should be 2 buffers
32 * for this; one for dev-name and 1 for description. Note: dev->description
33 * on WinPcap can contain <tab> and newlines!
34 */
35 #define IFACE_LEN 100
36
37 /* globals */
38
39 GtkApplication *etterapp = NULL;
40 GtkWidget *window = NULL; /* main window */
41 GtkWidget *notebook = NULL;
42 GtkWidget *main_menu = NULL;
43 GtkUIManager *menu_manager = NULL;
44 guint merge_id;
45 GTimer *progress_timer = NULL;
46
47 GtkWidget *notebook_frame = NULL;
48 GtkWidget *textview = NULL;
49 GtkWidget *infobar = NULL;
50 GtkWidget *infolabel = NULL;
51 GtkWidget *infoframe = NULL;
52 static guint infotimer = 0;
53 GtkTextBuffer *msgbuffer = NULL;
54 GtkTextMark *endmark = NULL;
55 static gboolean progress_canceled = FALSE;
56 static GtkWidget *progress_dialog = NULL;
57 static GtkWidget *progress_bar = NULL;
58
59
60 /* proto */
61
62 void gtkui_start(void);
63
64 static void gtkui_init(void);
65 static void gtkui_cleanup(void);
66 static void gtkui_update(int target);
67 static void gtkui_msg(const char *msg);
68 gboolean gtkui_infobar_expired(gpointer data);
69 static void gtkui_error(const char *msg);
70 static void gtkui_fatal_error(const char *msg);
71 static gboolean gtkui_flush_msg(gpointer data);
72 static void gtkui_progress(char *title, int value, int max);
73
74 GtkApplication* gtkui_setup(void * activate_func, gpointer activate_param);
75 static void gtkui_build_widgets(GApplication* app, gpointer data);
76
77 static void toggle_unoffensive(GSimpleAction *action, GVariant *value, gpointer data);
78 static void toggle_nopromisc(GSimpleAction *action, GVariant *value, gpointer data);
79
80 static void gtkui_file_open(GSimpleAction *action, GVariant *value, gpointer data);
81 static void read_pcapfile(gchar *file);
82 static void gtkui_file_write(GSimpleAction *action, GVariant *value, gpointer data);
83 static void write_pcapfile(void);
84 static void gtkui_set_iface_unified(GtkComboBox *combo, gpointer data);
85 static void gtkui_set_iface_bridge(GtkComboBox *combo, gpointer data);
86 static gboolean gtkui_bridged_switch(GtkSwitch *switcher, gboolean state, gpointer data);
87 static gboolean gtkui_autostart_switch(GtkSwitch *switcher, gboolean state, gpointer data);
88 static void gtkui_sniff(GtkButton *button, gpointer data);
89 static void gtkui_pcap_filter(GSimpleAction *action, GVariant *value, gpointer data);
90 static void gtkui_set_netmask(GSimpleAction *action, GVariant *value, gpointer data);
91 static gboolean gtkui_progress_cancel(GtkWidget *window, gpointer data);
92
93 #define ENABLED "true"
94 #define DISABLED "false"
95
96 /* wrapper functions which inject the real function call into the main
97 * idle loop, ensugin only th emain thread performs GTK operations
98 */
99 static gboolean gtkui_cleanup_shim(gpointer data)
100 {
101 /* variable not used */
102 (void) data;
103
104 gtkui_cleanup();
105 return FALSE;
106 }
107
108 static void gtkui_cleanup_wrap(void)
109 {
110 g_idle_add(gtkui_cleanup_shim, NULL);
111 }
112
113 static gboolean gtkui_msg_shim(gpointer data)
114 {
115 gtkui_msg(data);
116 SAFE_FREE(data);
117 return FALSE;
118 }
119
120 static void gtkui_msg_wrap(const char *msg)
121 {
122 char *copy = strdup(msg);
123 if (msg) {
124 g_idle_add(gtkui_msg_shim, copy);
125 } else {
126 FATAL_ERROR("out of memory");
127 }
128 }
129
130 static gboolean gtkui_error_shim(gpointer data)
131 {
132 gtkui_error(data);
133 SAFE_FREE(data);
134 return FALSE;
135 }
136
137 static void gtkui_error_wrap(const char *msg)
138 {
139
140 char *copy = strdup(msg);
141 if (msg) {
142 g_idle_add(gtkui_error_shim, copy);
143 } else {
144 FATAL_ERROR("out of memory");
145 }
146 }
147
148 static gboolean gtkui_fatal_error_shim(gpointer data) {
149 gtkui_fatal_error(data);
150 SAFE_FREE(data);
151 return FALSE;
152 }
153
154 static void gtkui_fatal_error_wrap(const char *msg) {
155
156 char *copy = strdup(msg);
157 if (msg) {
158 gtkui_fatal_error_shim(copy);
159 //g_idle_add(gtkui_fatal_error_shim, copy);
160 } else {
161 FATAL_ERROR("out of memory");
162 }
163 }
164
165 struct gtkui_input_data {
166 char *title;
167 char *input;
168 size_t n;
169 void (*callback)(void);
170 };
171
172 struct gtkui_progress_data {
173 char *title;
174 int value;
175 int max;
176 };
177
178 static gboolean gtkui_progress_shim(gpointer data) {
179
180 struct gtkui_progress_data *gpd = data;
181 gdouble delay;
182 gulong usec;
183
184 delay = g_timer_elapsed(progress_timer, &usec);
185 delay += usec / 1000000;
186
187 /* render progress bar if not canceled or lasting longer than 750 ms */
188 if (!progress_canceled && delay >= 0.75)
189 gtkui_progress(gpd->title, gpd->value, gpd->max);
190 SAFE_FREE(gpd->title);
191 SAFE_FREE(gpd);
192 return FALSE;
193 }
194
195 static int gtkui_progress_wrap(char *title, int value, int max) {
196
197 struct gtkui_progress_data *gpd;
198
199 if (value <= 1) {
200 g_timer_start(progress_timer);
201 progress_canceled = FALSE;
202 }
203
204 if (progress_canceled == TRUE) {
205 return UI_PROGRESS_INTERRUPTED;
206 }
207
208 if (!title) {
209 return UI_PROGRESS_UPDATED;
210 }
211
212 gpd = malloc(sizeof *gpd);
213 if (gpd) {
214 gpd->title = strdup(title);
215 gpd->value = value;
216 gpd->max = max;
217 g_idle_add(gtkui_progress_shim, gpd);
218 } else {
219 FATAL_ERROR("out of memory");
220 }
221
222 return value == max
223 ? UI_PROGRESS_FINISHED
224 : UI_PROGRESS_UPDATED;
225 }
226
227
228
229
230
231 /********************************************/
232
233 void set_gtk_interface(void)
234 {
235 struct ui_ops ops;
236
237 /* wipe the struct */
238 memset(&ops, 0, sizeof(ops));
239
240 /* register the functions */
241 ops.init = >kui_init;
242 ops.start = >kui_start;
243 ops.type = UI_GTK;
244 ops.cleanup = >kui_cleanup_wrap;
245 ops.msg = >kui_msg_wrap;
246 ops.error = >kui_error_wrap;
247 ops.fatal_error = >kui_fatal_error_wrap;
248 ops.input = >kui_input;
249 ops.progress = >kui_progress_wrap;
250 ops.update = >kui_update;
251
252 ui_register(&ops);
253
254 DEBUG_MSG("GTK3 -> gtk+3 %d.%d.%d\n", gtk_major_version, gtk_minor_version, gtk_micro_version);
255 }
256
257
258 /*
259 * prepare GTK, create the menu/messages window, enter the first loop
260 */
261 static void gtkui_init(void)
262 {
263 DEBUG_MSG("gtkui_init");
264
265 if(!gtk_init_check(0, NULL)) {
266 FATAL_ERROR("GTK3 failed to initialize. Is X running?");
267 return;
268 }
269
270 gtkui_conf_read();
271
272 /* try to explicitely enforce dark theme if preferred */
273 if (EC_GBL_CONF->gtkui_prefer_dark_theme)
274 g_object_set(gtk_settings_get_default(),
275 "gtk-application-prefer-dark-theme", TRUE,
276 NULL);
277
278 etterapp = gtkui_setup(gtkui_build_widgets, NULL);
279
280 /* initialize timer */
281 progress_timer = g_timer_new();
282
283 /* gui init loop, calling gtkui_sniff (--> g_application_quit) will cause
284 * this to exit so we can proceed to the main loop
285 * later. */
286 g_application_run(G_APPLICATION(etterapp), 0, NULL);
287 g_object_unref(G_OBJECT(etterapp));
288
289 EC_GBL_UI->initialized = 1;
290 }
291
292 /*
293 * exit ettercap
294 */
295 void gtkui_exit(GSimpleAction *action, GVariant *value, gpointer data)
296 {
297 int left, top, width, height;
298
299 (void) action;
300 (void) value;
301 (void) data;
302 DEBUG_MSG("gtkui_exit");
303
304 g_timer_destroy(progress_timer);
305
306 gtk_window_get_position(GTK_WINDOW (window), &left, &top);
307 gtk_window_get_size(GTK_WINDOW (window), &width, &height);
308 gtkui_conf_set("window_left", left);
309 gtkui_conf_set("window_top", top);
310 gtkui_conf_set("window_width", width);
311 gtkui_conf_set("window_height", height);
312
313 g_object_unref(etterapp);
314 gtkui_conf_save();
315 clean_exit(0);
316 }
317
318 /*
319 * reset to the previous state
320 */
321 static void gtkui_cleanup(void)
322 {
323 DEBUG_MSG("gtk_cleanup");
324
325
326 }
327
328
329 /*
330 * process an UI update notification
331 */
332 static void gtkui_update(int target)
333 {
334 switch (target) {
335 case UI_UPDATE_HOSTLIST:
336 g_idle_add((GSourceFunc)gtkui_refresh_host_list, NULL);
337 break;
338 case UI_UPDATE_PLUGINLIST:
339 g_idle_add((GSourceFunc)gtkui_refresh_plugin_list, NULL);
340 break;
341 }
342
343 }
344
345 /*
346 * print a USER_MSG() extracting it from the queue
347 */
348 static void gtkui_msg(const char *msg)
349 {
350 GtkTextIter iter;
351 gchar *unicode = NULL;
352
353 DEBUG_MSG("gtkui_msg: %s", msg);
354
355 if((unicode = gtkui_utf8_validate((char *)msg)) == NULL)
356 return;
357
358 gtk_text_buffer_get_end_iter(msgbuffer, &iter);
359 gtk_text_buffer_insert(msgbuffer, &iter, unicode, -1);
360 gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW (textview),
361 endmark, 0, FALSE, 0, 0);
362 return;
363 }
364
365 /* flush pending messages */
366 gboolean gtkui_flush_msg(gpointer data)
367 {
368 /* variable not used */
369 (void) data;
370
371 ui_msg_flush(MSG_ALL);
372
373 return(TRUE);
374 }
375
376 /*
377 * display about dialog
378 */
379 void gtkui_about(GSimpleAction *action, GVariant *value, gpointer data)
380 {
381 GtkWidget *dialog, *content, *scroll, *vbox, *logo, *label;
382 GtkWidget *textview, *header, *stack, *stackswitch;
383 GtkTextBuffer *textbuf;
384 GtkTextIter iter;
385 GError *error = NULL;
386 const gchar *path, *unicode;
387 gchar *license, *authors;
388 gsize length;
389
390 (void) action;
391 (void) value;
392 (void) data;
393
394 header = gtk_header_bar_new();
395 gtk_header_bar_set_title(GTK_HEADER_BAR(header), "About");
396 gtk_header_bar_set_show_close_button(GTK_HEADER_BAR(header), TRUE);
397 gtk_header_bar_set_decoration_layout(GTK_HEADER_BAR(header), ":close");
398
399 dialog = gtk_dialog_new();
400 gtk_window_set_title(GTK_WINDOW(dialog), "About");
401 gtk_window_set_titlebar(GTK_WINDOW(dialog), header);
402 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
403 gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(window));
404 gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER_ON_PARENT);
405 gtk_window_set_default_size(GTK_WINDOW(dialog), 450, 300);
406
407 stack = gtk_stack_new();
408 gtk_stack_set_transition_type(GTK_STACK(stack), GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT_RIGHT);
409 stackswitch = gtk_stack_switcher_new();
410 gtk_stack_switcher_set_stack(GTK_STACK_SWITCHER(stackswitch), GTK_STACK(stack));
411 gtk_header_bar_set_custom_title(GTK_HEADER_BAR(header), stackswitch);
412
413
414 /* General page */
415 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 10);
416
417 path = INSTALL_DATADIR "/" PROGRAM "/" LOGO_FILE_SMALL;
418 if(g_file_test(path, G_FILE_TEST_EXISTS))
419 logo = gtk_image_new_from_file(path);
420 else /* if neither path is valid gtk will use a broken image icon */
421 logo = gtk_image_new_from_file("./share/" LOGO_FILE_SMALL);
422 gtk_box_pack_start(GTK_BOX(vbox), logo, TRUE, TRUE, 0);
423
424 label = gtk_label_new("");
425 gtk_label_set_markup(GTK_LABEL(label),
426 "<span size=\"xx-large\" weight=\"bold\">"
427 PROGRAM " " EC_VERSION
428 "</span>");
429 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
430
431 label = gtk_label_new("www.ettercap-project.org");
432 gtk_label_set_selectable(GTK_LABEL(label), TRUE);
433 gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 0);
434 label = gtk_label_new("#ettercap on FreeNode IRC");
435 gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 0);
436 label = gtk_label_new(" ");
437 gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 30);
438 gtk_stack_add_titled(GTK_STACK(stack), vbox, "general", "General");
439
440 /* Authors page */
441 scroll= gtk_scrolled_window_new(NULL, NULL);
442 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW (scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
443 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW (scroll), GTK_SHADOW_IN);
444
445 /* load the authors file */
446 g_file_get_contents("./AUTHORS",
447 &authors, &length, &error);
448 if (error != NULL) {
449 /* no debug message */
450 g_error_free(error);
451 error = NULL;
452
453 /* 2nd try */
454 g_file_get_contents(INSTALL_DATADIR "/" PROGRAM "/AUTHORS",
455 &authors, &length, &error);
456 if (error != NULL) {
457 DEBUG_MSG("failed to load authors file: %s", error->message);
458 gtkui_error("Failed to load AUTHORS file.");
459 g_error_free(error);
460 error = NULL;
461 }
462 }
463 textview = gtk_text_view_new();
464 gtk_text_view_set_editable(GTK_TEXT_VIEW(textview), FALSE);
465 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview));
466 if (authors && (unicode = gtkui_utf8_validate(authors)) != NULL) {
467 gtk_text_buffer_get_end_iter(textbuf, &iter);
468 gtk_text_buffer_insert(textbuf, &iter, unicode, -1);
469 }
470 gtk_container_add(GTK_CONTAINER(scroll), textview);
471 gtk_stack_add_titled(GTK_STACK(stack), scroll, "authors", "Authors");
472
473 /* License page */
474 scroll= gtk_scrolled_window_new(NULL, NULL);
475 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW (scroll),
476 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
477 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW (scroll), GTK_SHADOW_IN);
478
479 /* load license file */
480 g_file_get_contents("./LICENSE",
481 &license, &length, &error);
482 if (error != NULL) {
483 /* no debug message */
484 g_error_free(error);
485 error = NULL;
486
487 /* 2nd try */
488 g_file_get_contents(INSTALL_DATADIR "/" PROGRAM "/LICENSE",
489 &license, &length, &error);
490 #ifndef OS_WINDOWS
491 if (error != NULL) {
492 DEBUG_MSG("failed to load license file: %s, try system path ...", error->message);
493 g_error_free(error);
494 error = NULL;
495
496 /* 3rd try */
497 g_file_get_contents("/usr/share/common-licenses/GPL-2",
498 &license, &length, &error);
499 }
500 #endif
501 if (error != NULL) {
502 DEBUG_MSG("failed to load license file: %s", error->message);
503 gtkui_error("Failed to load LICENSE file.");
504 g_error_free(error);
505 error = NULL;
506 }
507 }
508
509 textview = gtk_text_view_new();
510 gtk_text_view_set_editable(GTK_TEXT_VIEW(textview), FALSE);
511 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview));
512 if (license && (unicode = gtkui_utf8_validate(license)) != NULL) {
513 gtk_text_buffer_get_end_iter(textbuf, &iter);
514 gtk_text_buffer_insert(textbuf, &iter, unicode, -1);
515 }
516 gtk_container_add(GTK_CONTAINER(scroll), textview);
517
518 gtk_stack_add_titled(GTK_STACK(stack), scroll, "license", "License");
519
520 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
521 gtk_container_add(GTK_CONTAINER(content), stack);
522
523 // TODO Ctrl+w shall close the window
524
525 gtk_widget_show_all(GTK_WIDGET(dialog));
526
527
528 gtk_dialog_run(GTK_DIALOG(dialog));
529
530 if (authors)
531 g_free(authors);
532 if (license)
533 g_free(license);
534
535 gtk_widget_destroy(dialog);
536
537 }
538
539 /*
540 * reimplementation of gtk_message_dialog_new() to display a message
541 * dialog with a header-bar since the GTK implementation has hardcoded
542 * disabled this feature in the convenience function gtk_message_dialog_new()
543 * and gtk_message_dialog_new() is also not meant anymore to display
544 * images or icons indicating the type of message
545 */
546 GtkWidget* gtkui_message_dialog(GtkWindow *parent, GtkDialogFlags flags,
547 GtkMessageType type, GtkButtonsType buttons,
548 const char *msg)
549 {
550 GtkWidget *dialog, *label, *icon, *button, *content, *box, *header;
551
552 dialog = gtk_dialog_new();
553
554
555 /* implement flags */
556 if (parent)
557 gtk_window_set_transient_for(GTK_WINDOW(dialog), parent);
558
559 if (flags & GTK_DIALOG_MODAL)
560 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
561
562 if (flags & GTK_DIALOG_DESTROY_WITH_PARENT)
563 gtk_window_set_destroy_with_parent(GTK_WINDOW(dialog), TRUE);
564
565 if (flags & GTK_DIALOG_USE_HEADER_BAR) {
566 header = gtk_header_bar_new();
567 gtk_header_bar_set_decoration_layout(GTK_HEADER_BAR(header), ":close");
568 gtk_header_bar_set_show_close_button(GTK_HEADER_BAR(header), TRUE);
569 gtk_window_set_titlebar(GTK_WINDOW(dialog), header);
570 gtk_widget_show(header);
571 }
572
573
574 /* buttons */
575 switch (buttons) {
576 case GTK_BUTTONS_OK:
577 button = gtk_dialog_add_button(GTK_DIALOG(dialog),
578 "_OK", GTK_RESPONSE_OK);
579 gtk_widget_grab_default(button);
580 break;
581
582 case GTK_BUTTONS_CLOSE:
583 button = gtk_dialog_add_button(GTK_DIALOG(dialog),
584 "_Close", GTK_RESPONSE_CLOSE);
585 gtk_widget_grab_default(button);
586 break;
587
588 case GTK_BUTTONS_CANCEL:
589 button = gtk_dialog_add_button(GTK_DIALOG(dialog),
590 "_Cancel", GTK_RESPONSE_CANCEL);
591 gtk_widget_grab_default(button);
592 break;
593
594 case GTK_BUTTONS_YES_NO:
595 button = gtk_dialog_add_button(GTK_DIALOG(dialog),
596 "_Yes", GTK_RESPONSE_YES);
597 gtk_widget_grab_default(button);
598
599 button = gtk_dialog_add_button(GTK_DIALOG(dialog),
600 "_No", GTK_RESPONSE_NO);
601 break;
602
603 case GTK_BUTTONS_OK_CANCEL:
604 button = gtk_dialog_add_button(GTK_DIALOG(dialog),
605 "_OK", GTK_RESPONSE_OK);
606 gtk_widget_grab_default(button);
607
608 button = gtk_dialog_add_button(GTK_DIALOG(dialog),
609 "_Cancel", GTK_RESPONSE_CANCEL);
610 break;
611
612 default: // GTK_BUTTONS_NONE
613 break;
614 }
615
616 /* create horizontal box for icon and message text */
617 box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
618 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
619 gtk_container_set_border_width(GTK_CONTAINER(content), 10);
620 gtk_container_add(GTK_CONTAINER(content), box);
621
622 /* icon depending on message type */
623 switch (type) {
624 case GTK_MESSAGE_INFO:
625 gtk_window_set_title(GTK_WINDOW(dialog), "Information");
626 icon = gtk_image_new_from_icon_name("dialog-information", GTK_ICON_SIZE_DIALOG);
627 gtk_box_pack_start(GTK_BOX(box), icon, FALSE, FALSE, 0);
628 break;
629 case GTK_MESSAGE_WARNING:
630 gtk_window_set_title(GTK_WINDOW(dialog), "Warning");
631 icon = gtk_image_new_from_icon_name("dialog-warning", GTK_ICON_SIZE_DIALOG);
632 gtk_box_pack_start(GTK_BOX(box), icon, FALSE, FALSE, 0);
633 break;
634 case GTK_MESSAGE_QUESTION:
635 gtk_window_set_title(GTK_WINDOW(dialog), "Question");
636 icon = gtk_image_new_from_icon_name("dialog-question", GTK_ICON_SIZE_DIALOG);
637 gtk_box_pack_start(GTK_BOX(box), icon, FALSE, FALSE, 0);
638 break;
639 case GTK_MESSAGE_ERROR:
640 gtk_window_set_title(GTK_WINDOW(dialog), "Error");
641 icon = gtk_image_new_from_icon_name("dialog-error", GTK_ICON_SIZE_DIALOG);
642 gtk_box_pack_start(GTK_BOX(box), icon, FALSE, FALSE, 0);
643 break;
644 default: // GTK_MESSAGE_OTHER
645 break;
646 }
647
648 /* message text */
649 label = gtk_label_new(msg);
650 gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 0);
651
652 gtk_widget_show_all(box);
653
654 return dialog;
655 }
656
657 /*
658 * some minor notifications don't need a dedicated dialog
659 * instead an the infobar widget is being used
660 */
661 GtkWidget* gtkui_infobar_new(GtkWidget *infoframe)
662 {
663 infobar = gtk_info_bar_new();
664 gtk_widget_set_no_show_all(infobar, TRUE);
665
666 infolabel = gtk_label_new("");
667 gtk_widget_show(infolabel);
668
669 gtk_container_add(GTK_CONTAINER(
670 gtk_info_bar_get_content_area(GTK_INFO_BAR(infobar))), infolabel);
671
672 gtk_info_bar_add_button(GTK_INFO_BAR(infobar), "_OK", GTK_RESPONSE_OK);
673
674 if (infoframe == NULL)
675 infoframe = gtk_frame_new(NULL);
676 gtk_widget_set_no_show_all(infoframe, TRUE);
677 gtk_frame_set_shadow_type(GTK_FRAME(infoframe), GTK_SHADOW_NONE);
678 gtk_container_add(GTK_CONTAINER(infoframe), infobar);
679 g_signal_connect(G_OBJECT(infobar), "response",
680 G_CALLBACK(gtkui_infobar_hide), NULL);
681
682 return infoframe;
683 }
684
685 /*
686 * show infobar
687 */
688 void gtkui_infobar_show(GtkMessageType type, const gchar *msg)
689 {
690
691 if (!infobar && !infoframe)
692 return;
693
694 if (infobar == NULL)
695 infoframe = gtkui_infobar_new(infoframe);
696
697 gtk_label_set_text(GTK_LABEL(infolabel), msg);
698 gtk_info_bar_set_message_type(GTK_INFO_BAR(infobar), type);
699 gtk_info_bar_set_default_response(GTK_INFO_BAR(infobar), GTK_RESPONSE_OK);
700
701 gtk_widget_show(infobar);
702 gtk_widget_show(infoframe);
703
704 infotimer = g_timeout_add_seconds(3, gtkui_infobar_expired, infobar);
705
706 }
707
708 /*
709 * callback when info bar timer expired
710 */
711 gboolean gtkui_infobar_expired(gpointer data)
712 {
713 gtkui_infobar_hide(GTK_WIDGET(data), 0, NULL);
714 /* stop timer */
715 return FALSE;
716 }
717
718
719 /*
720 * callback wrapper to hide infobar necessary due to still (Feb 2018)
721 * unfixed animation bug: https://bugzilla.gnome.org/show_bug.cgi?id=710888
722 * implementing suggested workaround to remove and reattach widget
723 */
724 void gtkui_infobar_hide(GtkWidget *widget, gint response, gpointer data)
725 {
726 (void) response;
727 (void) data;
728 (void) widget;
729
730
731 if (!infobar || !infoframe)
732 return;
733
734 if (infotimer)
735 g_source_remove(infotimer);
736
737 gtk_widget_hide(infobar);
738 gtk_widget_hide(infoframe);
739 gtk_widget_destroy(infobar);
740 infobar = NULL;
741 }
742
743 /*
744 * print an error
745 */
746 static void gtkui_error(const char *msg)
747 {
748 gchar *unicode = NULL;
749
750 DEBUG_MSG("gtkui_error: %s", msg);
751
752 if((unicode = gtkui_utf8_validate((char *)msg)) == NULL)
753 return;
754
755 gtkui_infobar_show(GTK_MESSAGE_ERROR, msg);
756
757 return;
758 }
759
760
761 /*
762 * handle a fatal error and exit
763 */
764 static void gtkui_fatal_error(const char *msg)
765 {
766 /* if the gui is working at this point
767 display the message in a dialog */
768 if(window)
769 gtkui_error(msg);
770
771 /* also dump it to console in case ettercap was started in an xterm */
772 fprintf(stderr, "FATAL ERROR: %s\n\n\n", msg);
773
774 clean_exit(-1);
775 }
776
777
778 /*
779 * get an input from the user
780 */
781 void gtkui_input(const char *title, char *input, size_t n, void (*callback)(void))
782 {
783 GtkWidget *dialog, *entry, *label, *hbox, *vbox, *image, *content_area;
784
785 dialog = gtk_dialog_new_with_buttons(PROGRAM" Input", GTK_WINDOW (window),
786 GTK_DIALOG_MODAL|GTK_DIALOG_USE_HEADER_BAR,
787 "_Cancel", GTK_RESPONSE_CANCEL,
788 "_OK", GTK_RESPONSE_OK,
789 NULL);
790 gtk_container_set_border_width(GTK_CONTAINER (dialog), 5);
791
792 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
793
794 content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
795 gtk_container_add(GTK_CONTAINER(content_area), hbox);
796
797 image = gtk_image_new_from_icon_name("dialog-question", GTK_ICON_SIZE_DIALOG);
798 gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
799
800 label = gtk_label_new (title);
801 gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
802 gtk_label_set_selectable (GTK_LABEL (label), TRUE);
803 gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
804
805 entry = gtk_entry_new();
806 gtk_entry_set_max_length(GTK_ENTRY(entry), n);
807 g_object_set_data(G_OBJECT (entry), "dialog", dialog);
808 g_signal_connect(G_OBJECT (entry), "activate", G_CALLBACK (gtkui_dialog_enter), NULL);
809
810
811 if (input)
812 gtk_entry_set_text(GTK_ENTRY (entry), input);
813
814 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
815 gtk_box_pack_start(GTK_BOX(vbox), entry, TRUE, FALSE, 0);
816
817 gtk_box_pack_start(GTK_BOX (hbox), vbox, FALSE, FALSE, 5);
818 gtk_widget_show_all (hbox);
819
820 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK) {
821
822 strncpy(input, gtk_entry_get_text(GTK_ENTRY (entry)), n);
823
824 if (callback != NULL) {
825 gtk_widget_destroy(dialog);
826
827 callback();
828 return;
829 }
830 }
831 gtk_widget_destroy(dialog);
832 }
833
834
835 /*
836 * show or update the progress bar
837 */
838 static void gtkui_progress(char *title, int value, int max)
839 {
840 static GtkWidget *hbox, *header, *content;
841
842 /* the first time, create the object */
843 if (progress_bar == NULL) {
844 header = gtk_header_bar_new();
845 gtk_header_bar_set_title(GTK_HEADER_BAR(header), "Progress");
846 gtk_header_bar_set_decoration_layout(GTK_HEADER_BAR(header), ":close");
847 gtk_header_bar_set_show_close_button(GTK_HEADER_BAR(header), TRUE);
848
849 progress_dialog = gtk_dialog_new();
850 gtk_window_set_title(GTK_WINDOW (progress_dialog), PROGRAM);
851 gtk_window_set_titlebar(GTK_WINDOW(progress_dialog), header);
852 gtk_window_set_modal(GTK_WINDOW (progress_dialog), TRUE);
853 gtk_window_set_transient_for(GTK_WINDOW(progress_dialog), GTK_WINDOW(window));
854 gtk_window_set_position(GTK_WINDOW(progress_dialog), GTK_WIN_POS_CENTER_ON_PARENT);
855 gtk_container_set_border_width(GTK_CONTAINER (progress_dialog), 10);
856 g_signal_connect(G_OBJECT(progress_dialog), "delete_event", G_CALLBACK(gtkui_progress_cancel), NULL);
857
858 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3);
859 content = gtk_dialog_get_content_area(GTK_DIALOG(progress_dialog));
860 gtk_container_add(GTK_CONTAINER(content), hbox);
861
862 progress_bar = gtk_progress_bar_new();
863 gtk_progress_bar_set_show_text(GTK_PROGRESS_BAR(progress_bar), TRUE);
864 gtk_box_pack_start(GTK_BOX(hbox), progress_bar, TRUE, TRUE, 20);
865
866 }
867
868 /* the subsequent calls have to only update the object */
869 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progress_bar), title);
870 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress_bar), (gdouble)((gdouble)value / (gdouble)max));
871
872 /* update dialog window */
873 gtk_widget_show_all(progress_dialog);
874
875 /*
876 * when 100%, destroy it
877 */
878 if (value == max) {
879 if (progress_dialog)
880 gtk_widget_destroy(progress_dialog);
881 progress_dialog = NULL;
882 progress_bar = NULL;
883 }
884
885 }
886
887 static gboolean gtkui_progress_cancel(GtkWidget *window, gpointer data)
888 {
889 /* variable not used */
890 (void) window;
891
892 progress_canceled = TRUE;
893
894 /* the progress dialog must be manually destroyed if the cancel button is used */
895 if (data != NULL && GTK_IS_WIDGET(data)) {
896 gtk_widget_destroy(data);
897 progress_dialog = NULL;
898 progress_bar = NULL;
899 }
900 return(FALSE);
901 }
902
903 /*
904 * print a message
905 */
906 void gtkui_message(const char *msg)
907 {
908 DEBUG_MSG("gtkui_message: %s", msg);
909 gtkui_infobar_show(GTK_MESSAGE_INFO, msg);
910 }
911
912
913 /*
914 * Create the main interface and enter the second loop
915 */
916
917 void gtkui_start(void)
918 {
919 guint idle_flush;
920 gint online;
921
922 DEBUG_MSG("gtkui_start");
923
924 idle_flush = g_timeout_add(500, gtkui_flush_msg, NULL);
925
926 /* which interface do we have to display ? */
927 online = (EC_GBL_OPTIONS->read ? 0 : 1);
928
929 /* create second instance of the UI application */
930 etterapp = gtkui_setup(gtkui_create_menu, GINT_TO_POINTER(online));
931
932 /* start plugins defined on CLI */
933 g_idle_add(gtkui_plugins_autostart, NULL);
934
935 /* the main gui loop, once this exits the gui will be destroyed */
936 g_application_run(G_APPLICATION(etterapp), 0, NULL);
937 g_object_unref(G_OBJECT(etterapp));
938
939 g_source_remove(idle_flush);
940 }
941
942 static void toggle_unoffensive(GSimpleAction *action, GVariant *value, gpointer data)
943 {
944 (void) data;
945
946 g_simple_action_set_state(action, value);
947
948 EC_GBL_OPTIONS->unoffensive ^= 1;
949 }
950
951 static void toggle_nopromisc(GSimpleAction *action, GVariant *value, gpointer data)
952 {
953 (void) data;
954
955 g_simple_action_set_state(action, value);
956
957 EC_GBL_PCAP->promisc ^= 1;
958 }
959
960 /*
961 * display the initial menu to setup global options
962 * at startup.
963 */
964 GtkApplication* gtkui_setup(void * activate_func, gpointer data)
965 {
966 GtkApplication *app;
967 DEBUG_MSG("gtkui_setup");
968
969 app = gtk_application_new("org.gtk.Ettercap", 0);
970 g_signal_connect(app, "activate", G_CALLBACK(activate_func), data);
971
972 return app;
973 }
974
975 /*
976 * activate callback for GtkApplication for building the widgets
977 * for the setup dialog
978 */
979 static void gtkui_build_widgets(GApplication* app, gpointer data)
980 {
981 GtkWidget *header, *menubutton, *logo, *switcher;
982 GtkWidget *layout, *label, *combo1, *combo2, *setting_frame, *grid, *box;
983 GtkBuilder *builder;
984 GtkListStore *iface_list;
985 GtkTreeIter iter;
986 GtkCellRenderer *cell1, *cell2;
987 gint width, height, left, top;
988 gchar *title = NULL;
989 char *path = NULL, *markup = NULL;
990 guint i;
991 pcap_if_t *dev;
992
993 (void) data;
994
995 /* accelerators */
996 static gtkui_accel_map_t accels[] = {
997 {"app.pcap_filter", {"<Primary>p", NULL}},
998 {"app.set_netmask", {"<Primary>n", NULL}},
999 {"app.open", {"<Primary>o", NULL}},
1000 {"app.save", {"<Primary>s", NULL}},
1001 #ifndef OS_WINDOWS
1002 {"app.help", {"F1", NULL}},
1003 #endif
1004 {"app.quit", {"<Primary>q", "<Primary>x", NULL}}
1005 };
1006
1007
1008 /* actions */
1009 static GActionEntry action_entries[] = {
1010 {"set_promisc", NULL, NULL, ENABLED, toggle_nopromisc, {}},
1011 {"set_unoffensive", NULL, NULL, DISABLED, toggle_unoffensive, {}},
1012 {"open", gtkui_file_open, NULL, NULL, NULL, {}},
1013 {"save", gtkui_file_write, NULL, NULL, NULL, {}},
1014 {"about", gtkui_about, NULL, NULL, NULL, {}},
1015 {"shortcuts", gtkui_show_shortcuts, "s", NULL, NULL, {}},
1016 #ifndef OS_WINDOWS
1017 {"help", gtkui_help, NULL, NULL, NULL, {}},
1018 #endif
1019 {"quit", gtkui_exit, NULL, NULL, NULL, {}},
1020 {"pcap_filter", gtkui_pcap_filter, NULL, NULL, NULL, {}},
1021 {"set_netmask", gtkui_set_netmask, NULL, NULL, NULL, {}}
1022 };
1023
1024
1025
1026 DEBUG_MSG("gtkui_build_widgets (activate method)");
1027
1028 /* honor CLI options */
1029 if(!EC_GBL_PCAP->promisc)
1030 /* setting the menu item active will toggle this setting */
1031 /* it will be TRUE after the menu is updated */
1032 action_entries[0].state = DISABLED;
1033
1034 if(EC_GBL_OPTIONS->unoffensive)
1035 action_entries[1].state = ENABLED;
1036
1037
1038 /* add actions to the application */
1039 g_action_map_add_action_entries(G_ACTION_MAP(app), action_entries,
1040 G_N_ELEMENTS(action_entries), app);
1041
1042 /* map accelerators to actions */
1043 for (i = 0; i < G_N_ELEMENTS(accels); i++) {
1044 gtk_application_set_accels_for_action(GTK_APPLICATION(app),
1045 accels[i].action, accels[i].accel);
1046 }
1047
1048 /* menu structures */
1049 builder = gtk_builder_new();
1050 gtk_builder_add_from_string(builder,
1051 "<interface>"
1052 " <menu id='app-menu'>"
1053 " <section>"
1054 " <item>"
1055 " <attribute name='label' translatable='yes'>_Open PCAP</attribute>"
1056 " <attribute name='action'>app.open</attribute>"
1057 " <attribute name='icon'>document-open</attribute>"
1058 " </item>"
1059 " <item>"
1060 " <attribute name='label' translatable='yes'>_Save PCAP</attribute>"
1061 " <attribute name='action'>app.save</attribute>"
1062 " <attribute name='icon'>document-save</attribute>"
1063 " </item>"
1064 " </section>"
1065 " <section>"
1066 #ifndef OS_WINDOWS
1067 " <item>"
1068 " <attribute name='label' translatable='yes'>Help</attribute>"
1069 " <attribute name='action'>app.help</attribute>"
1070 " <attribute name='icon'>help-browser</attribute>"
1071 " </item>"
1072 #endif
1073 " <item>"
1074 " <attribute name='label' translatable='yes'>Shortcuts</attribute>"
1075 " <attribute name='action'>app.shortcuts</attribute>"
1076 " <attribute name='target'>setup-shortcuts</attribute>"
1077 " </item>"
1078 " <item>"
1079 " <attribute name='label' translatable='yes'>_About Ettercap</attribute>"
1080 " <attribute name='action'>app.about</attribute>"
1081 " <attribute name='icon'>help-about</attribute>"
1082 " </item>"
1083 " </section>"
1084 " <section>"
1085 " <item>"
1086 " <attribute name='label' translatable='yes'>_Quit</attribute>"
1087 " <attribute name='action'>app.quit</attribute>"
1088 " <attribute name='icon'>application-exit</attribute>"
1089 " </item>"
1090 " </section>"
1091 " </menu>"
1092 " <menu id='options-menu'>"
1093 " <section>"
1094 " <attribute name='label' translatable='yes'>Options</attribute>"
1095 " <item>"
1096 " <attribute name='label' translatable='yes'>Unoffensive</attribute>"
1097 " <attribute name='action'>app.set_unoffensive</attribute>"
1098 " </item>"
1099 " <item>"
1100 " <attribute name='label' translatable='yes'>Promisc mode</attribute>"
1101 " <attribute name='action'>app.set_promisc</attribute>"
1102 " </item>"
1103 " <item>"
1104 " <attribute name='label' translatable='yes'>Set Netmask</attribute>"
1105 " <attribute name='action'>app.set_netmask</attribute>"
1106 " </item>"
1107 " </section>"
1108 " </menu>"
1109 "</interface>", -1, NULL);
1110
1111
1112 /* set app menu */
1113 gtk_application_set_app_menu(GTK_APPLICATION(app),
1114 G_MENU_MODEL(gtk_builder_get_object(builder, "app-menu")));
1115
1116 if (g_getenv("APP_MENU_FALLBACK"))
1117 g_object_set(gtk_settings_get_default(), "gtk-shell-shows-app-menu", FALSE, NULL);
1118
1119
1120 /* position main window */
1121 width = gtkui_conf_get("window_width");
1122 height = gtkui_conf_get("window_height");
1123 left = gtkui_conf_get("window_left");
1124 top = gtkui_conf_get("window_top");
1125
1126 /* setup window needs minimal size */
1127 width = width < 800 ? 800 : width;
1128 height = height < 400 ? 400 : height;
1129
1130 /* Adjust title formatting */
1131 title = g_strdup(PROGRAM);
1132 *title = g_ascii_toupper(*title);
1133
1134 /* create main window */
1135 window = gtk_application_window_new(GTK_APPLICATION(app));
1136 gtk_application_window_set_show_menubar(GTK_APPLICATION_WINDOW(window), TRUE);
1137 gtk_window_set_title(GTK_WINDOW(window), title);
1138 gtk_window_set_default_size(GTK_WINDOW(window), width, height);
1139
1140 /* set window icon */
1141 path = ICON_DIR "/" ICON_FILE;
1142 if (g_file_test(path, G_FILE_TEST_EXISTS)) {
1143 gtk_window_set_icon(GTK_WINDOW(window), gdk_pixbuf_new_from_file(path, NULL));
1144 }
1145 else { /* if neither path is valid gtk will use a broken image icon */
1146 gtk_window_set_icon(GTK_WINDOW(window), gdk_pixbuf_new_from_file("./share/" ICON_FILE, NULL));
1147 }
1148
1149 if(left > 0 || top > 0)
1150 gtk_window_move(GTK_WINDOW(window), left, top);
1151
1152 g_signal_connect(G_OBJECT (window), "delete_event", G_CALLBACK(gtkui_exit), NULL);
1153
1154
1155 /* create header bar and menu buttons */
1156 header = gtk_header_bar_new();
1157 gtk_header_bar_set_title(GTK_HEADER_BAR(header), title);
1158 gtk_header_bar_set_subtitle(GTK_HEADER_BAR(header), EC_VERSION);
1159 gtk_header_bar_set_show_close_button(GTK_HEADER_BAR(header), TRUE);
1160 gtk_window_set_titlebar(GTK_WINDOW(window), header);
1161
1162 menubutton = gtk_menu_button_new();
1163 gtk_widget_set_tooltip_text(menubutton, "Options");
1164 gtk_menu_button_set_menu_model(GTK_MENU_BUTTON(menubutton),
1165 G_MENU_MODEL(gtk_builder_get_object(builder, "options-menu")));
1166 gtk_button_set_image(GTK_BUTTON(menubutton),
1167 gtk_image_new_from_icon_name("open-menu-symbolic", GTK_ICON_SIZE_MENU));
1168 gtk_header_bar_pack_end(GTK_HEADER_BAR(header), menubutton);
1169
1170
1171 /* main content area */
1172 box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
1173 gtk_container_add(GTK_CONTAINER(window), box);
1174
1175 /* prepare infobar for later notifications */
1176 infoframe = gtkui_infobar_new(NULL);
1177 gtk_box_pack_start(GTK_BOX(box), infoframe, FALSE, FALSE, 0);
1178
1179 /* the ettercap logo */
1180 path = INSTALL_DATADIR "/" PROGRAM "/" LOGO_FILE;
1181 if(g_file_test(path, G_FILE_TEST_EXISTS))
1182 logo = gtk_image_new_from_file(path);
1183 else /* if neither path is valid gtk will use a broken image icon */
1184 logo = gtk_image_new_from_file("./share/" LOGO_FILE);
1185
1186 /* create overlay to display the logo and overlay the settings widgets */
1187 layout = gtk_layout_new(NULL, NULL);
1188 gtk_box_pack_start(GTK_BOX(box), layout, TRUE, TRUE, 0);
1189 gtk_layout_put(GTK_LAYOUT(layout), logo, 0, 0);
1190
1191 setting_frame = gtk_frame_new(NULL);
1192 gtk_frame_set_label(GTK_FRAME(setting_frame), "Setup");
1193 gtk_frame_set_label_align(GTK_FRAME(setting_frame), 0.5, 0.0);
1194 gtk_frame_set_shadow_type(GTK_FRAME(setting_frame), GTK_SHADOW_ETCHED_OUT);
1195
1196 grid = gtk_grid_new();
1197 gtk_grid_set_row_spacing(GTK_GRID(grid), 10);
1198 gtk_grid_set_column_spacing(GTK_GRID(grid), 10);
1199 g_object_set(grid, "margin", 10, NULL);
1200 gtk_container_add(GTK_CONTAINER(setting_frame), grid);
1201
1202 label = gtk_label_new(NULL);
1203 markup = g_markup_printf_escaped(
1204 "<span style='italic'>%s</span>",
1205 "Primary Interface");
1206 gtk_label_set_markup(GTK_LABEL(label), markup);
1207 gtk_widget_set_halign(label, GTK_ALIGN_START);
1208 gtk_grid_attach(GTK_GRID(grid), label, 0, 1, 1, 1);
1209 g_free(markup);
1210
1211 /* make a list of network interfaces */
1212 iface_list = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
1213 for (dev = (pcap_if_t *)EC_GBL_PCAP->ifs; dev != NULL; dev = dev->next) {
1214 gtk_list_store_append(iface_list, &iter);
1215 gtk_list_store_set(iface_list, &iter,
1216 0, dev->name, 1, dev->description, -1);
1217 }
1218
1219 /* make a drop down box for the primary interface and attach the list */
1220 combo1 = gtk_combo_box_new();
1221 gtk_combo_box_set_model(GTK_COMBO_BOX(combo1), GTK_TREE_MODEL(iface_list));
1222 cell1 = gtk_cell_renderer_text_new();
1223 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo1), cell1, TRUE);
1224 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo1), cell1,
1225 "text", 1, NULL);
1226 g_signal_connect(G_OBJECT(combo1), "changed",
1227 G_CALLBACK(gtkui_set_iface_unified), NULL);
1228 gtk_combo_box_set_active(GTK_COMBO_BOX(combo1), 0);
1229 gtk_grid_attach(GTK_GRID(grid), combo1, 1, 1, 1, 1);
1230
1231
1232 label = gtk_label_new(NULL);
1233 markup = g_markup_printf_escaped(
1234 "<span style='italic'>%s</span>",
1235 "Sniffing at startup");
1236 gtk_label_set_markup(GTK_LABEL(label), markup);
1237 gtk_widget_set_halign(label, GTK_ALIGN_START);
1238 gtk_grid_attach(GTK_GRID(grid), label, 0, 0, 1, 1);
1239 g_free(markup);
1240
1241 switcher = gtk_switch_new();
1242 box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
1243 gtk_box_set_homogeneous(GTK_BOX(box), FALSE);
1244 gtk_box_pack_start(GTK_BOX(box), switcher, FALSE, FALSE, 0);
1245 gtk_grid_attach(GTK_GRID(grid), box, 1, 0, 1, 1);
1246 if (EC_GBL_CONF->sniffing_at_startup)
1247 gtk_switch_set_active(GTK_SWITCH(switcher), TRUE);
1248 g_signal_connect(G_OBJECT(switcher), "state-set",
1249 G_CALLBACK(gtkui_autostart_switch), NULL);
1250
1251 label = gtk_label_new(NULL);
1252 markup = g_markup_printf_escaped(
1253 "<span style='italic'>%s</span>",
1254 "Bridged sniffing");
1255 gtk_label_set_markup(GTK_LABEL(label), markup);
1256 gtk_widget_set_halign(label, GTK_ALIGN_START);
1257 gtk_grid_attach(GTK_GRID(grid), label, 0, 2, 1, 1);
1258 g_free(markup);
1259
1260 switcher = gtk_switch_new();
1261 box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
1262 gtk_box_set_homogeneous(GTK_BOX(box), FALSE);
1263 gtk_box_pack_start(GTK_BOX(box), switcher, FALSE, FALSE, 0);
1264 gtk_grid_attach(GTK_GRID(grid), box, 1, 2, 1, 1);
1265
1266 label = gtk_label_new(NULL);
1267 markup = g_markup_printf_escaped(
1268 "<span style='italic'>%s</span>",
1269 "Bridged Interface");
1270 gtk_label_set_markup(GTK_LABEL(label), markup);
1271 gtk_widget_set_halign(label, GTK_ALIGN_START);
1272 gtk_grid_attach(GTK_GRID(grid), label, 0, 3, 1, 1);
1273
1274 /* make a drop down box for the bridge interface and assign the list to it */
1275 combo2 = gtk_combo_box_new();
1276 gtk_combo_box_set_model(GTK_COMBO_BOX(combo2), GTK_TREE_MODEL(iface_list));
1277 cell2 = gtk_cell_renderer_text_new();
1278 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo2), cell2, TRUE);
1279 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo2), cell2,
1280 "text", 1, NULL);
1281 g_signal_connect(G_OBJECT(combo2), "changed",
1282 G_CALLBACK(gtkui_set_iface_bridge), NULL);
1283 gtk_combo_box_set_active(GTK_COMBO_BOX(combo2), 1);
1284 gtk_grid_attach(GTK_GRID(grid), combo2, 1, 3 , 1, 1);
1285 gtk_widget_set_sensitive(combo2, FALSE);
1286
1287 /* enable / disable bridged interface combo box */
1288 g_signal_connect(G_OBJECT(switcher), "state-set",
1289 G_CALLBACK(gtkui_bridged_switch), combo2);
1290
1291 gtk_layout_put(GTK_LAYOUT(layout), setting_frame, 450, 10);
1292
1293 menubutton = gtk_button_new();
1294 gtk_widget_set_tooltip_text(menubutton, "Accept");
1295 gtk_button_set_image(GTK_BUTTON(menubutton),
1296 gtk_image_new_from_icon_name("emblem-ok-symbolic", GTK_ICON_SIZE_BUTTON));
1297 gtk_header_bar_pack_end(GTK_HEADER_BAR(header), menubutton);
1298 g_signal_connect(G_OBJECT(menubutton), "clicked", G_CALLBACK(gtkui_sniff), switcher);
1299
1300 gtk_widget_show_all(GTK_WIDGET(window));
1301
1302 g_object_unref(iface_list);
1303 g_object_unref(builder);
1304 g_free(title);
1305
1306
1307 DEBUG_MSG("gtk_setup: end");
1308 }
1309
1310 /*
1311 * display the file open dialog
1312 */
1313 static void gtkui_file_open(GSimpleAction *action, GVariant *value, gpointer data)
1314 {
1315 GtkWidget *dialog, *chooser, *content;
1316 gchar *filename;
1317 int response = 0;
1318
1319 (void) action;
1320 (void) value;
1321 (void) data;
1322
1323 DEBUG_MSG("gtk_file_open");
1324
1325
1326 dialog = gtk_dialog_new_with_buttons("Select a PCAP file for offline sniffing ...",
1327 GTK_WINDOW (window),
1328 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_USE_HEADER_BAR,
1329 "_Cancel", GTK_RESPONSE_CANCEL,
1330 "_OK", GTK_RESPONSE_OK,
1331 NULL);
1332 gtk_container_set_border_width(GTK_CONTAINER(dialog), 10);
1333
1334 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1335 chooser = gtk_file_chooser_widget_new(GTK_FILE_CHOOSER_ACTION_OPEN);
1336 gtk_container_add(GTK_CONTAINER(content), chooser);
1337 gtk_widget_show(chooser);
1338
1339 /* This way the file chooser dialog doesn't start in the recent section */
1340 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(chooser), "");
1341
1342 response = gtk_dialog_run (GTK_DIALOG (dialog));
1343
1344 if (response == GTK_RESPONSE_OK) {
1345 gtk_widget_hide(dialog);
1346 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(chooser));
1347 /* destroy needs to come before read_pcapfile so gtk_main_quit
1348 can reside inside read_pcapfile, which is why destroy is here
1349 twice and not after the if() block */
1350 gtk_widget_destroy (dialog);
1351
1352 read_pcapfile (filename);
1353 g_free(filename);
1354 } else {
1355 gtk_widget_destroy (dialog);
1356 }
1357
1358 }
1359
1360 static void read_pcapfile(gchar *file)
1361 {
1362 char pcap_errbuf[PCAP_ERRBUF_SIZE];
1363
1364 DEBUG_MSG("read_pcapfile %s", file);
1365
1366 SAFE_CALLOC(EC_GBL_OPTIONS->pcapfile_in, strlen(file)+1, sizeof(char));
1367
1368 snprintf(EC_GBL_OPTIONS->pcapfile_in, strlen(file)+1, "%s", file);
1369
1370 /* check if the file is good */
1371 if (is_pcap_file(EC_GBL_OPTIONS->pcapfile_in, pcap_errbuf) != E_SUCCESS) {
1372 ui_error("%s", pcap_errbuf);
1373 SAFE_FREE(EC_GBL_OPTIONS->pcapfile_in);
1374 return;
1375 }
1376
1377 /* set the options for reading from file */
1378 EC_GBL_OPTIONS->silent = 1;
1379 EC_GBL_OPTIONS->unoffensive = 1;
1380 EC_GBL_OPTIONS->write = 0;
1381 EC_GBL_OPTIONS->read = 1;
1382
1383 gtk_main_quit();
1384 }
1385
1386 /*
1387 * display the write file menu
1388 */
1389 static void gtkui_file_write(GSimpleAction *action, GVariant *value, gpointer data)
1390 {
1391 #define FILE_LEN 40
1392
1393 GtkWidget *dialog, *content, *chooser;
1394 gchar *filename;
1395 int response = 0;
1396
1397 (void) action;
1398 (void) value;
1399 (void) data;
1400
1401 DEBUG_MSG("gtk_file_write");
1402
1403 dialog = gtk_dialog_new_with_buttons("Save traffic in a PCAP file ...",
1404 GTK_WINDOW (window),
1405 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_USE_HEADER_BAR,
1406 "_Cancel", GTK_RESPONSE_CANCEL,
1407 "_OK", GTK_RESPONSE_OK,
1408 NULL);
1409 gtk_container_set_border_width(GTK_CONTAINER(dialog), 10);
1410
1411 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1412 chooser = gtk_file_chooser_widget_new(GTK_FILE_CHOOSER_ACTION_SAVE);
1413 gtk_container_add(GTK_CONTAINER(content), chooser);
1414 gtk_widget_show(chooser);
1415
1416 /* This way the file chooser dialog doesn't start in the recent section */
1417 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(chooser), "");
1418
1419 response = gtk_dialog_run (GTK_DIALOG (dialog));
1420
1421 if (response == GTK_RESPONSE_OK) {
1422 gtk_widget_hide(dialog);
1423 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(chooser));
1424 /* destroy needs to come before read_pcapfile so gtk_main_quit
1425 can reside inside read_pcapfile, which is why destroy is here
1426 twice and not after the if() block */
1427 gtk_widget_destroy (dialog);
1428
1429 EC_GBL_OPTIONS->pcapfile_out = filename;
1430 write_pcapfile();
1431 } else {
1432 gtk_widget_destroy (dialog);
1433 }
1434 }
1435
1436 static void write_pcapfile(void)
1437 {
1438 FILE *f;
1439
1440 DEBUG_MSG("write_pcapfile");
1441
1442 /* check if the file is writeable */
1443 f = fopen(EC_GBL_OPTIONS->pcapfile_out, "w");
1444 if (f == NULL) {
1445 ui_error("Cannot write %s", EC_GBL_OPTIONS->pcapfile_out);
1446 g_free(EC_GBL_OPTIONS->pcapfile_out);
1447 return;
1448 }
1449
1450 /* if ok, delete it */
1451 fclose(f);
1452 unlink(EC_GBL_OPTIONS->pcapfile_out);
1453
1454 /* set the options for writing to a file */
1455 EC_GBL_OPTIONS->write = 1;
1456 EC_GBL_OPTIONS->read = 0;
1457 }
1458
1459
1460 /*
1461 * set unified interface when changed in UI
1462 */
1463 static void gtkui_set_iface_unified(GtkComboBox *combo, gpointer data)
1464 {
1465 GtkTreeIter iter;
1466 gchar *iface;
1467
1468 (void) data;
1469
1470 gtk_combo_box_get_active_iter(combo, &iter);
1471 gtk_tree_model_get(gtk_combo_box_get_model(combo), &iter, 0, &iface, -1);
1472
1473 DEBUG_MSG("gtkui_set_iface_unified: set iface '%s'", iface);
1474
1475 SAFE_FREE(EC_GBL_OPTIONS->iface);
1476 SAFE_CALLOC(EC_GBL_OPTIONS->iface, IFACE_LEN, sizeof(char));
1477 strncpy(EC_GBL_OPTIONS->iface, iface, IFACE_LEN);
1478 }
1479
1480 /*
1481 * set bridged interface when changed
1482 */
1483 static void gtkui_set_iface_bridge(GtkComboBox *combo, gpointer data)
1484 {
1485 GtkTreeIter iter;
1486 gchar *iface;
1487
1488 (void) data;
1489
1490 gtk_combo_box_get_active_iter(combo, &iter);
1491 gtk_tree_model_get(gtk_combo_box_get_model(combo), &iter, 0, &iface, -1);
1492
1493 DEBUG_MSG("gtkui_set_iface_bridge: set iface '%s'", iface);
1494
1495 SAFE_FREE(EC_GBL_OPTIONS->iface_bridge);
1496 SAFE_CALLOC(EC_GBL_OPTIONS->iface_bridge, IFACE_LEN, sizeof(char));
1497 strncpy(EC_GBL_OPTIONS->iface_bridge, iface, IFACE_LEN);
1498 }
1499
1500
1501 /*
1502 * bridged sniffing switcher callback
1503 */
1504 static gboolean gtkui_bridged_switch(GtkSwitch *switcher, gboolean state, gpointer data)
1505 {
1506 (void) data;
1507 //gtk_switch_set_active(switcher, state);
1508 gtk_switch_set_state(switcher, state);
1509 gtk_widget_set_sensitive(GTK_WIDGET(data), gtk_switch_get_active(switcher));
1510
1511 return TRUE;
1512 }
1513
1514
1515
1516 /*
1517 * sniffing at startup switcher callback
1518 */
1519 static gboolean gtkui_autostart_switch(GtkSwitch *switcher, gboolean state, gpointer data)
1520 {
1521 (void) data;
1522 //gtk_switch_set_active(switcher, state);
1523 gtk_switch_set_state(switcher, state);
1524 EC_GBL_CONF->sniffing_at_startup = gtk_switch_get_active(switcher);
1525
1526 return TRUE;
1527 }
1528
1529 /*
1530 * check if unified or bridged sniffing,
1531 * then quit this application intance to continue
1532 * main functions further initializing ettercap
1533 */
1534 static void gtkui_sniff(GtkButton *button, gpointer data)
1535 {
1536 (void) button;
1537
1538 /* set bridge sniffing if switcher has been set to "on" */
1539 if (gtk_switch_get_active(GTK_SWITCH(data)))
1540 set_bridge_sniff();
1541
1542 /* quit first instance of GtkApplication */
1543 g_application_quit(G_APPLICATION(etterapp));
1544
1545 }
1546
1547 /*
1548 * display the pcap filter dialog
1549 */
1550 static void gtkui_pcap_filter(GSimpleAction *action, GVariant *value, gpointer data)
1551 {
1552 (void) action;
1553 (void) value;
1554 (void) data;
1555
1556 #define PCAP_FILTER_LEN 50
1557
1558 DEBUG_MSG("gtk_pcap_filter");
1559
1560 if (EC_GBL_PCAP->filter == NULL)
1561 SAFE_CALLOC(EC_GBL_PCAP->filter, PCAP_FILTER_LEN, sizeof(char));
1562
1563 /*
1564 * no callback, the filter is set but we have to return to
1565 * the interface for other user input
1566 */
1567 gtkui_input("Pcap filter :", EC_GBL_PCAP->filter, PCAP_FILTER_LEN, NULL);
1568 }
1569
1570 /*
1571 * set a different netmask than the system one
1572 */
1573 static void gtkui_set_netmask(GSimpleAction *action, GVariant *value, gpointer data)
1574 {
1575 struct ip_addr net;
1576
1577 (void) action;
1578 (void) value;
1579 (void) data;
1580
1581 DEBUG_MSG("gtkui_set_netmask");
1582
1583 if (EC_GBL_OPTIONS->netmask == NULL)
1584 SAFE_CALLOC(EC_GBL_OPTIONS->netmask, IP_ASCII_ADDR_LEN, sizeof(char));
1585
1586 /*
1587 * no callback, the filter is set but we have to return to
1588 * the interface for other user input
1589 */
1590 gtkui_input("Netmask :", EC_GBL_OPTIONS->netmask, IP_ASCII_ADDR_LEN, NULL);
1591
1592 /* sanity check */
1593 if (strcmp(EC_GBL_OPTIONS->netmask, "") &&
1594 ip_addr_pton(EC_GBL_OPTIONS->netmask, &net) != E_SUCCESS)
1595 ui_error("Invalid netmask %s", EC_GBL_OPTIONS->netmask);
1596
1597 /* if no netmask was specified, free it */
1598 if (!strcmp(EC_GBL_OPTIONS->netmask, ""))
1599 SAFE_FREE(EC_GBL_OPTIONS->netmask);
1600 }
1601
1602
1603 /*
1604 * Callback for g_timeout_add() to resolve a IP to name asyncronously
1605 * if the name is not already in the cache, host_iptoa
1606 * immediately returns but starts the resolution process
1607 * in the background.
1608 * This function periodically recalls this host_iptoa until
1609 * a result in available in the cache and updates the widget.
1610 */
1611 gboolean gtkui_iptoa_deferred(gpointer data)
1612 {
1613 struct resolv_object *ro;
1614 char name[MAX_HOSTNAME_LEN];
1615 ro = (struct resolv_object *)data;
1616
1617 DEBUG_MSG("gtkui_iptoa_deferred");
1618
1619 if (host_iptoa(ro->ip, name) == E_SUCCESS) {
1620 /*
1621 * Name has now been resolved in the background
1622 * Set the widget text and destroy the timer
1623 */
1624 if (ro->type == GTK_TYPE_LABEL)
1625 gtk_label_set_text(GTK_LABEL(ro->widget), name);
1626 else if (ro->type == GTK_TYPE_LIST_STORE)
1627 gtk_list_store_set(GTK_LIST_STORE(ro->liststore),
1628 &ro->treeiter, ro->column, name, -1);
1629
1630 /* Free allocated memory */
1631 SAFE_FREE(ro);
1632
1633 /* destroy timer */
1634 return FALSE;
1635 }
1636 else {
1637 /* Keep trying */
1638 return TRUE;
1639 }
1640 }
1641
1642
1643 /* hitting "Enter" keyy in a combo box does the same as clicking OK button */
1644 gboolean gtkui_combo_enter(GtkWidget *widget, GdkEventKey *event, gpointer data)
1645 {
1646 GtkWidget *dialog;
1647
1648 /* variable not used */
1649 (void) data;
1650
1651 if (event->keyval == GDK_KEY_Return) {
1652 dialog = g_object_get_data(G_OBJECT(widget), "dialog");
1653 gtk_dialog_response(GTK_DIALOG (dialog), GTK_RESPONSE_OK);
1654
1655 return TRUE;
1656 }
1657
1658 return FALSE;
1659 }
1660
1661 /* hitting "Enter" key in dialog does same as clicking OK button */
1662 void gtkui_dialog_enter(GtkWidget *widget, gpointer data) {
1663 GtkWidget *dialog;
1664
1665 /* variable not used */
1666 (void) data;
1667
1668 dialog = g_object_get_data(G_OBJECT(widget), "dialog");
1669 gtk_dialog_response(GTK_DIALOG (dialog), GTK_RESPONSE_OK);
1670 }
1671
1672 /* create a new notebook (tab) page */
1673 /* returns a parent widget to pack the contents of the page into */
1674 GtkWidget *gtkui_page_new(char *title, void (*callback)(void), void (*detacher)(GtkWidget *)) {
1675 GtkWidget *parent, *label;
1676 GtkWidget *hbox, *button, *image;
1677
1678 /* a container to hold the close button and tab label */
1679 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
1680 gtk_widget_show(hbox);
1681
1682 /* the label for the tab title */
1683 label = gtk_label_new(title);
1684 gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
1685 gtk_widget_show(label);
1686
1687 /* the close button */
1688 button = gtk_button_new();
1689 gtk_button_set_relief(GTK_BUTTON (button), GTK_RELIEF_NONE);
1690 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
1691 gtk_widget_set_size_request(button, 20, 20);
1692 gtk_widget_show(button);
1693
1694 /* an image for the button */
1695 image = gtk_image_new_from_icon_name("window-close", GTK_ICON_SIZE_MENU);
1696 gtk_container_add(GTK_CONTAINER (button), image);
1697 gtk_widget_show(image);
1698
1699 /* a parent to pack the contents into */
1700 parent = gtk_frame_new(NULL);
1701 gtk_frame_set_shadow_type(GTK_FRAME(parent), GTK_SHADOW_NONE);
1702 gtk_widget_show(parent);
1703
1704 if(!notebook && notebook_frame) {
1705 gtk_container_remove(GTK_CONTAINER (notebook_frame), gtk_bin_get_child(GTK_BIN (notebook_frame)));
1706
1707 notebook = gtk_notebook_new();
1708 gtk_notebook_set_tab_pos(GTK_NOTEBOOK (notebook), GTK_POS_TOP);
1709 gtk_notebook_set_scrollable(GTK_NOTEBOOK (notebook), TRUE);
1710 gtk_container_add(GTK_CONTAINER (notebook_frame), notebook);
1711 gtk_widget_show(notebook);
1712
1713 gtkui_create_tab_menu();
1714 }
1715
1716 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), parent, hbox);
1717
1718 /* attach callback to destroy the tab/page */
1719 g_signal_connect(G_OBJECT (button), "clicked", G_CALLBACK(gtkui_page_close), parent);
1720
1721 /* attach callback to do specific clean-up */
1722 if(callback)
1723 g_object_set_data(G_OBJECT (parent), "destroy", callback);
1724
1725 if(detacher)
1726 g_object_set_data(G_OBJECT (parent), "detach", detacher);
1727
1728 gtkui_page_present(parent);
1729
1730 return(parent);
1731 }
1732
1733 /* show and focus the page containing child */
1734 void gtkui_page_present(GtkWidget *child) {
1735 int num = 0;
1736
1737 num = gtk_notebook_page_num(GTK_NOTEBOOK (notebook), child);
1738 gtk_notebook_set_current_page(GTK_NOTEBOOK (notebook), num);
1739
1740 }
1741
1742 /* close the page containing the child passed in "data" */
1743 void gtkui_page_close(GtkWidget *widget, gpointer data) {
1744 GtkWidget *child;
1745 gint num = 0;
1746 void (*callback)(void);
1747
1748 /* variable not used */
1749 (void) widget;
1750 (void) data;
1751
1752 DEBUG_MSG("gtkui_page_close");
1753
1754 num = gtk_notebook_page_num(GTK_NOTEBOOK(notebook), GTK_WIDGET (data));
1755 child = gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), num);
1756 g_object_ref(G_OBJECT(child));
1757
1758 gtk_notebook_remove_page(GTK_NOTEBOOK(notebook), num);
1759
1760 callback = g_object_get_data(G_OBJECT (child), "destroy");
1761 if(callback)
1762 callback();
1763 }
1764
1765 /* close the currently focused notebook page */
1766 void gtkui_page_close_current(GSimpleAction *action, GVariant *value, gpointer data) {
1767 GtkWidget *child;
1768 gint num = 0;
1769
1770 (void) action;
1771 (void) value;
1772 (void) data;
1773
1774 num = gtk_notebook_get_current_page(GTK_NOTEBOOK (notebook));
1775 child = gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), num);
1776
1777 gtkui_page_close(NULL, child);
1778 }
1779
1780 /* show the context menu when the notebook tabs receive a mouse right-click */
1781 gboolean gtkui_context_menu(GtkWidget *widget, GdkEventButton *event, gpointer data) {
1782 /* variable not used */
1783 (void) widget;
1784
1785 if(event->button == 3) {
1786 #if GTK_CHECK_VERSION(3,22,0)
1787 gtk_menu_popup_at_pointer(GTK_MENU(data), (GdkEvent*)event);
1788 #else
1789 gtk_menu_popup(GTK_MENU(data), NULL, NULL, NULL, NULL, 3, event->time);
1790 #endif
1791 /*
1792 * button press event handle must return TRUE to keep the selection
1793 * active when pressing the mouse button
1794 */
1795 return TRUE;
1796 }
1797
1798 return FALSE;
1799 }
1800
1801 /* detach the currently focused notebook page into a free window */
1802 void gtkui_page_detach_current(GSimpleAction *action, GVariant *value, gpointer data) {
1803 void (*detacher)(GtkWidget *);
1804 GtkWidget *child;
1805 gint num = 0;
1806
1807 (void) action;
1808 (void) value;
1809 (void) data;
1810
1811 num = gtk_notebook_get_current_page(GTK_NOTEBOOK (notebook));
1812 if(num < 0)
1813 return;
1814 child = gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), num);
1815 g_object_ref(G_OBJECT(child));
1816
1817 gtk_notebook_remove_page(GTK_NOTEBOOK(notebook), num);
1818
1819 detacher = g_object_get_data(G_OBJECT (child), "detach");
1820 if(detacher)
1821 detacher(child);
1822 }
1823
1824 void gtkui_page_attach_shortcut(GtkWidget *win, void (*attacher)(void))
1825 {
1826 GtkAccelGroup *accel;
1827 GClosure *closure = NULL;
1828 GdkModifierType mods;
1829 gint keyval;
1830
1831 accel = gtk_accel_group_new ();
1832 gtk_window_add_accel_group(GTK_WINDOW (win), accel);
1833 closure = g_cclosure_new(G_CALLBACK(attacher), NULL, NULL);
1834 gtk_accelerator_parse ("<control>D", &keyval, &mods);
1835 gtk_accel_group_connect(accel, keyval, mods, 0, closure);
1836 }
1837
1838 /* change view and focus to the next notebook page */
1839 void gtkui_page_right(GSimpleAction *action, GVariant *value, gpointer data) {
1840 (void) action;
1841 (void) value;
1842 (void) data;
1843
1844 gtk_notebook_next_page(GTK_NOTEBOOK (notebook));
1845 }
1846
1847 /* change view and focus to previous notebook page */
1848 void gtkui_page_left(GSimpleAction *action, GVariant *value, gpointer data) {
1849 (void) action;
1850 (void) value;
1851 (void) data;
1852
1853 gtk_notebook_prev_page(GTK_NOTEBOOK (notebook));
1854 }
1855
1856 /* for connecting to browse buttons, pass entry widget as callback "data" */
1857 void gtkui_filename_browse(GtkWidget *widget, gpointer data)
1858 {
1859 GtkWidget *dialog = NULL;
1860 gint response = 0;
1861 const char *filename = NULL;
1862
1863 /* variable not used */
1864 (void) widget;
1865
1866 dialog = gtk_file_chooser_dialog_new("Select a file...",
1867 NULL, GTK_FILE_CHOOSER_ACTION_OPEN,
1868 "_Cancel", GTK_RESPONSE_CANCEL,
1869 "_OK", GTK_RESPONSE_OK,
1870 NULL);
1871
1872 response = gtk_dialog_run (GTK_DIALOG (dialog));
1873
1874 if (response == GTK_RESPONSE_OK) {
1875 gtk_widget_hide(dialog);
1876 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
1877
1878 gtk_entry_set_text(GTK_ENTRY (data), filename);
1879 }
1880 gtk_widget_destroy(dialog);
1881 }
1882
1883 /* make sure data is valid UTF8 */
1884 char *gtkui_utf8_validate(char *data) {
1885 const gchar *end;
1886 char *unicode = NULL;
1887
1888 unicode = data;
1889 if(!g_utf8_validate (data, -1, &end)) {
1890 /* if "end" pointer is at beginning of string, we have no valid text to print */
1891 if(end == unicode) return(NULL);
1892
1893 /* cut off the invalid part so we don't lose the whole string */
1894 /* this shouldn't happen often */
1895 unicode = (char *)end;
1896 *unicode = 0;
1897 unicode = data;
1898 }
1899
1900 return(unicode);
1901 }
1902
1903 /* EOF */
1904
1905 // vim:ts=3:expandtab
1906