1 /*
2     ettercap -- GTK+ 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 #include <ec_gtk3.h>
24 #include <ec_redirect.h>
25 
26 /* proto */
27 
28 static void gtkui_sslredir_close(void);
29 static void gtkui_sslredir_detach(GtkWidget *child);
30 static void gtkui_sslredir_attach(void);
31 static void gtkui_sslredir_add(GtkWidget *widget, gpointer data);
32 static void gtkui_sslredir_del(GtkWidget *widget, gpointer data);
33 static void gtkui_sslredir_del_all(GtkWidget *widget, gpointer data);
34 static void gtkui_sslredir_add_list(struct redir_entry *re);
35 static void gtkui_sslredir_add_service(struct serv_entry *se);
36 static void gtkui_sslredir_create_lists(void);
37 static void gtkui_sslredir_af_changed(GtkWidget *widget, gpointer data);
38 static gboolean gtkui_sslredir_key_pressed(GtkWidget *widget,
39       GdkEventKey *event, gpointer data);
40 
41 /* globals */
42 
43 static GtkWidget *sslredir_window = NULL;
44 static GtkWidget *treeview = NULL;
45 static GtkListStore *redirrules = NULL;
46 static GtkListStore *proto_list = NULL;
47 static GtkListStore *af_list = NULL;
48 static GtkTreeSelection *selection = NULL;
49 
50 
51 
52 /*******************************************/
53 
54 
55 /*
56  * tab to configure traffic redirection for SSL interception
57  *    - no redirect of any interceptable traffic at startup
58  *    - selective redirect avoids SSL errors for destinations
59  *      not being subject of interception
60  *
61  */
gtkui_sslredir_show(GSimpleAction * action,GVariant * value,gpointer data)62 void gtkui_sslredir_show(GSimpleAction *action, GVariant *value, gpointer data)
63 {
64    GtkWidget *scrolled, *vbox, *hbox, *button, *context_menu, *items;
65    GtkTreeModel *model;
66    GtkCellRenderer *renderer;
67    GtkTreeViewColumn *column;
68 
69    (void) action;
70    (void) value;
71    (void) data;
72 
73    DEBUG_MSG("gtk_sslredir_show()");
74 
75    /* if the object already exist, set the focus to it */
76    if (sslredir_window) {
77       if(GTK_IS_WINDOW (sslredir_window))
78          gtk_window_present(GTK_WINDOW (sslredir_window));
79       else
80          gtkui_page_present(sslredir_window);
81       return;
82    }
83 
84    sslredir_window = gtkui_page_new("SSL Intercept",
85          &gtkui_sslredir_close,
86          &gtkui_sslredir_detach);
87 
88    vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
89    gtk_container_add(GTK_CONTAINER(sslredir_window), vbox);
90 
91    /* rules list */
92    scrolled = gtk_scrolled_window_new(NULL, NULL);
93    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
94          GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
95    gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled),
96          GTK_SHADOW_IN);
97    gtk_box_pack_start(GTK_BOX(vbox), scrolled, TRUE, TRUE, 0);
98 
99    treeview = gtk_tree_view_new();
100    gtk_container_add(GTK_CONTAINER(scrolled), treeview);
101 
102 
103    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
104    gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
105 
106    renderer = gtk_cell_renderer_text_new();
107    column = gtk_tree_view_column_new_with_attributes("IP Version", renderer,
108          "text", 1, NULL);
109    gtk_tree_view_column_set_sort_column_id(column, 1);
110    gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
111 
112    renderer = gtk_cell_renderer_text_new();
113    column = gtk_tree_view_column_new_with_attributes("Server IP", renderer,
114          "text", 2, NULL);
115    gtk_tree_view_column_set_sort_column_id(column, 2);
116    gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
117 
118    renderer = gtk_cell_renderer_text_new();
119    column = gtk_tree_view_column_new_with_attributes("Service", renderer,
120          "text", 6, NULL);
121    gtk_tree_view_column_set_sort_column_id(column, 6);
122    gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
123 
124    gtkui_sslredir_create_lists();
125 
126    model = gtk_tree_model_sort_new_with_model(GTK_TREE_MODEL(redirrules));
127    gtk_tree_view_set_model(GTK_TREE_VIEW(treeview), model);
128 
129    hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5);
130    gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
131 
132 
133    button = gtk_button_new_with_mnemonic("_Insert new redirect");
134    gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
135    if (proto_list)
136       g_signal_connect(G_OBJECT(button), "clicked",
137             G_CALLBACK(gtkui_sslredir_add), model);
138    else
139       gtk_widget_set_sensitive(button, FALSE);
140 
141 
142    button = gtk_button_new_with_mnemonic("_Remove redirect");
143    gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
144    if (proto_list)
145       g_signal_connect(G_OBJECT(button), "clicked",
146             G_CALLBACK(gtkui_sslredir_del), model);
147    else
148       gtk_widget_set_sensitive(button, FALSE);
149 
150    /* context menu */
151    context_menu = gtk_menu_new();
152    items = gtk_menu_item_new_with_label("Remove redirect");
153    gtk_menu_shell_append(GTK_MENU_SHELL(context_menu), items);
154    g_signal_connect(G_OBJECT(items), "activate",
155          G_CALLBACK(gtkui_sslredir_del), model);
156    gtk_widget_show(items);
157 
158    items = gtk_menu_item_new_with_label("Remove all redirects");
159    gtk_menu_shell_append(GTK_MENU_SHELL(context_menu), items);
160    g_signal_connect(G_OBJECT(items), "activate",
161          G_CALLBACK(gtkui_sslredir_del_all), model);
162    gtk_widget_show(items);
163 
164    g_signal_connect(G_OBJECT(treeview), "button-press-event",
165          G_CALLBACK(gtkui_context_menu), context_menu);
166 
167    /* remove entries if delete key is pressed */
168    g_signal_connect(G_OBJECT(treeview), "key-press-event",
169          G_CALLBACK(gtkui_sslredir_key_pressed), model);
170 
171    gtk_widget_show_all(sslredir_window);
172 
173 }
174 
175 /* Add a new line to the Rules list */
gtkui_sslredir_add(GtkWidget * widget,gpointer data)176 static void gtkui_sslredir_add(GtkWidget *widget, gpointer data)
177 {
178 
179    GtkWidget *dialog, *content, *grid, *destination, *label, *frame;
180    GtkWidget *proto, *af;
181    GtkTreeModel *model;
182    GtkTreeIter iter;
183    GtkCellRenderer *cell1, *cell2;
184    GVariant *gv_from_port, *gv_to_port;
185    int ret = 0;
186    guint16 from_port, to_port;
187    gchar *name;
188    const gchar *server;
189    ec_redir_proto_t ip_ver;
190 
191    /* unused variabled */
192    (void) widget;
193    (void) data;
194 
195 
196    DEBUG_MSG("gtkui_sslredir_add()");
197 
198    /* compile IP protocol family list if not already done */
199    if (af_list == NULL) {
200       af_list = gtk_list_store_new(2,
201             G_TYPE_STRING,  /* human friendly name */
202             G_TYPE_UINT);   /* protocol number for redirect */
203 
204       gtk_list_store_append(af_list, &iter);
205       gtk_list_store_set(af_list, &iter,
206             0, "IPv4", 1, EC_REDIR_PROTO_IPV4, -1);
207 #ifdef WITH_IPV6
208       gtk_list_store_append(af_list, &iter);
209       gtk_list_store_set(af_list, &iter,
210             0, "IPv6", 1, EC_REDIR_PROTO_IPV6, -1);
211 #endif
212    }
213 
214    dialog = gtk_dialog_new_with_buttons("Create new redirect rule",
215          GTK_WINDOW(window),
216          GTK_DIALOG_MODAL |
217          GTK_DIALOG_DESTROY_WITH_PARENT |
218          GTK_DIALOG_USE_HEADER_BAR,
219          "_Cancel", GTK_RESPONSE_CANCEL,
220          "_Insert", GTK_RESPONSE_OK,
221          NULL);
222 
223    content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
224    gtk_container_set_border_width(GTK_CONTAINER(content), 20);
225 
226    frame = gtk_frame_new("Redirect specification");
227    gtk_container_add(GTK_CONTAINER(content), frame);
228    gtk_widget_set_margin_bottom(frame, 10);
229 
230    grid = gtk_grid_new();
231    gtk_grid_set_row_spacing(GTK_GRID(grid), 5);
232    gtk_grid_set_column_spacing(GTK_GRID(grid), 5);
233    gtk_widget_set_halign(grid, GTK_ALIGN_CENTER);
234    gtk_container_set_border_width(GTK_CONTAINER(grid), 8);
235    gtk_container_add(GTK_CONTAINER(frame), grid);
236 
237    label = gtk_label_new("IP Version:");
238    gtk_widget_set_halign(label, GTK_ALIGN_START);
239    gtk_grid_attach(GTK_GRID(grid), label, GTK_POS_LEFT, GTK_POS_TOP, 1, 1);
240 
241    af = gtk_combo_box_new();
242    gtk_combo_box_set_model(GTK_COMBO_BOX(af), GTK_TREE_MODEL(af_list));
243    gtk_combo_box_set_active(GTK_COMBO_BOX(af), 0);
244 
245    cell1 = gtk_cell_renderer_text_new();
246    gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(af), cell1, TRUE);
247    gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(af), cell1,
248          "text", 0, NULL);
249    gtk_grid_attach(GTK_GRID(grid), af, GTK_POS_LEFT+1, GTK_POS_TOP, 1, 1);
250 
251    label = gtk_label_new("Server IP:");
252    gtk_widget_set_halign(label, GTK_ALIGN_START);
253    gtk_grid_attach(GTK_GRID(grid), label, GTK_POS_LEFT, GTK_POS_TOP+1, 1, 1);
254 
255    destination = gtk_entry_new();
256    gtk_entry_set_text(GTK_ENTRY(destination), "0.0.0.0/0");
257    gtk_grid_attach(GTK_GRID(grid), destination, GTK_POS_LEFT+1, GTK_POS_TOP+1, 1, 1);
258 
259    label = gtk_label_new("Service:");
260    gtk_widget_set_halign(label, GTK_ALIGN_START);
261    gtk_grid_attach(GTK_GRID(grid), label, GTK_POS_LEFT, GTK_POS_TOP+2, 1, 1);
262 
263    proto = gtk_combo_box_new();
264    gtk_combo_box_set_model(GTK_COMBO_BOX(proto), GTK_TREE_MODEL(proto_list));
265    gtk_combo_box_set_active(GTK_COMBO_BOX(proto), 0);
266 
267    cell2 = gtk_cell_renderer_text_new();
268    gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(proto), cell2, TRUE);
269    gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(proto), cell2,
270          "text", 1, NULL);
271    gtk_grid_attach(GTK_GRID(grid), proto, GTK_POS_LEFT+1, GTK_POS_TOP+2, 1, 1);
272 
273    g_signal_connect(G_OBJECT(af), "changed",
274          G_CALLBACK(gtkui_sslredir_af_changed), destination);
275 
276    gtk_widget_show_all(dialog);
277    gtk_widget_grab_focus(destination);
278 
279    if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK) {
280       gtk_widget_hide(dialog);
281 
282       /* extract information from widgets */
283       model = gtk_combo_box_get_model(GTK_COMBO_BOX(af));
284       gtk_combo_box_get_active_iter(GTK_COMBO_BOX(af), &iter);
285       gtk_tree_model_get(model, &iter, 1, &ip_ver, -1);
286 
287       model = gtk_combo_box_get_model(GTK_COMBO_BOX(proto));
288       gtk_combo_box_get_active_iter(GTK_COMBO_BOX(proto), &iter);
289       gtk_tree_model_get(model, &iter, 0, &name,
290             2, &gv_from_port, 3, &gv_to_port, -1);
291 
292       /* convert back from GVariant to uint16 */
293       from_port = g_variant_get_uint16(gv_from_port);
294       to_port = g_variant_get_uint16(gv_to_port);
295 
296       server = gtk_entry_get_text(GTK_ENTRY(destination));
297 
298       /* execute redirect action */
299       ret = ec_redirect(EC_REDIR_ACTION_INSERT, name, ip_ver,
300             server, from_port, to_port);
301 
302 
303       /* inform user if redirect execution wasn't successful */
304       if (ret != E_SUCCESS)
305          gtkui_infobar_show(GTK_MESSAGE_ERROR,
306                "Insertion of redirect rule failed.");
307       else { /* otherwise add rule to rules list */
308 
309          gtk_list_store_append(redirrules, &iter);
310          gtk_list_store_set(redirrules, &iter,
311                0, ip_ver,
312                1, (ip_ver == EC_REDIR_PROTO_IPV4 ? "IPv4" : "IPv6"),
313                2, server,
314                3, gv_from_port,
315                4, gv_to_port,
316                5, ec_strlc(name),
317                6, ec_struc(name),
318                -1);
319       }
320 
321 
322    }
323 
324    gtk_widget_destroy(dialog);
325 
326 
327 }
328 
329 /*
330  * remove selected redirect rules
331  */
gtkui_sslredir_del(GtkWidget * widget,gpointer data)332 void gtkui_sslredir_del(GtkWidget *widget, gpointer data)
333 {
334    GList *list;
335    GtkTreeIter iter, iter_unsorted;
336    GtkTreeModel *model;
337    GVariant *gv_from_port, *gv_to_port;
338    int ret;
339    gchar *name;
340    const gchar *server;
341    guint16 from_port, to_port;
342    ec_redir_proto_t ip_ver;
343 
344    /* variable not used */
345    (void) widget;
346 
347    DEBUG_MSG("gtkui_sslredir_del()");
348 
349    model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(data));
350 
351    /* get selected entries */
352    if (gtk_tree_selection_count_selected_rows(selection) > 0) {
353       list = gtk_tree_selection_get_selected_rows(selection, &model);
354       for (list = g_list_last(list); list; list = g_list_previous(list)) {
355          /* extract parameters from GtkTreeView model */
356          gtk_tree_model_get_iter(model, &iter, list->data);
357          gtk_tree_model_get(model, &iter,
358                0, &ip_ver,
359                2, &server,
360                3, &gv_from_port,
361                4, &gv_to_port,
362                5, &name,
363                -1);
364 
365          /* convert back from GVariant to uint16 */
366          from_port = g_variant_get_uint16(gv_from_port);
367          to_port = g_variant_get_uint16(gv_to_port);
368 
369          /* execute redirect action */
370          ret = ec_redirect(EC_REDIR_ACTION_REMOVE, name, ip_ver,
371                server, from_port, to_port);
372 
373          /* inform user if redirect execution wasn't successful */
374          if (ret != E_SUCCESS)
375             gtkui_infobar_show(GTK_MESSAGE_ERROR,
376                   "Removal of redirect rule failed.");
377          else { /* otherwise remove from list */
378             gtk_tree_model_sort_convert_iter_to_child_iter(
379                   GTK_TREE_MODEL_SORT(data), &iter_unsorted, &iter);
380             gtk_list_store_remove(GTK_LIST_STORE(redirrules), &iter_unsorted);
381          }
382       }
383 
384       /* free the list of selection */
385       g_list_free_full(list, (GDestroyNotify)gtk_tree_path_free);
386    }
387 
388 }
389 
390 /*
391  * select all entries in TreeModel then then call gtkui_sslredir_del
392  */
gtkui_sslredir_del_all(GtkWidget * widget,gpointer data)393 void gtkui_sslredir_del_all(GtkWidget *widget, gpointer data)
394 {
395 
396    DEBUG_MSG("gtkui_sslredir_del_all():");
397 
398    gtk_tree_selection_select_all(selection);
399    gtkui_sslredir_del(widget, data);
400 
401 }
402 
403 /* detach ssl redir tab */
gtkui_sslredir_detach(GtkWidget * child)404 static void gtkui_sslredir_detach(GtkWidget *child)
405 {
406    sslredir_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
407    gtk_window_set_title(GTK_WINDOW(sslredir_window), "SSL Intercept");
408    gtk_window_set_default_size(GTK_WINDOW(sslredir_window), 500, 250);
409    g_signal_connect(G_OBJECT(sslredir_window), "delete_event",
410          G_CALLBACK(gtkui_sslredir_close), NULL);
411 
412    gtkui_page_attach_shortcut(sslredir_window, gtkui_sslredir_attach);
413 
414    gtk_container_add(GTK_CONTAINER(sslredir_window), child);
415    gtk_window_present(GTK_WINDOW(sslredir_window));
416 
417 }
418 
419 /* callback for reattaching the detached ssl redir tab */
gtkui_sslredir_attach(void)420 static void gtkui_sslredir_attach(void)
421 {
422    gtkui_sslredir_close();
423    gtkui_sslredir_show(NULL, NULL, NULL);
424 }
425 
426 
427 /* close ssl redir tab */
gtkui_sslredir_close(void)428 static void gtkui_sslredir_close(void)
429 {
430    DEBUG_MSG("gtk_sslredir_close");
431 
432    gtk_widget_destroy(sslredir_window);
433    sslredir_window = NULL;
434 }
435 
436 /*
437  * create the list for the list of interceptable protocols
438  */
gtkui_sslredir_create_lists(void)439 static void gtkui_sslredir_create_lists(void)
440 {
441    int res;
442 
443    DEBUG_MSG("gtk_sslredir_create_lists()");
444 
445    /* populate redirect rules */
446    if (redirrules == NULL) {
447       redirrules = gtk_list_store_new(7,
448             G_TYPE_UINT,    /* IP address family */
449             G_TYPE_STRING,  /* IP address family human readable */
450             G_TYPE_STRING,  /* destination definition */
451             G_TYPE_VARIANT, /* protocol registered port */
452             G_TYPE_VARIANT, /* ettercap listener port */
453             G_TYPE_STRING,  /* protocol name lower case */
454             G_TYPE_STRING); /* protocol name upper case */
455       /* walk through list of registered redirects */
456       res = ec_walk_redirects(&gtkui_sslredir_add_list);
457 
458       if (res == -E_NOTFOUND) {
459          DEBUG_MSG("gtk_sslredir_create_lists(): no redirects registered - "
460                "apparently no redirect commands enabled in etter.conf");
461          gtkui_infobar_show(GTK_MESSAGE_WARNING,
462                "Traffic redirect not enabled in etter.conf. ");
463       }
464    }
465 
466 
467    /* populate registered services */
468    if (proto_list == NULL) {
469       proto_list = gtk_list_store_new(4,
470             G_TYPE_STRING,  /* protocol name lower case */
471             G_TYPE_STRING,  /* protocol name upper case */
472             G_TYPE_VARIANT, /* protocol registered port */
473             G_TYPE_VARIANT);/* ettercap listener port */
474 
475       res = ec_walk_redirect_services(&gtkui_sslredir_add_service);
476 
477       if (res == -E_NOTFOUND) {
478          g_object_unref(proto_list);
479          proto_list = NULL;
480       }
481    }
482 
483 
484 
485 }
486 
487 /*
488  * callback function to compose the list of active services
489  */
gtkui_sslredir_add_service(struct serv_entry * se)490 static void gtkui_sslredir_add_service(struct serv_entry *se)
491 {
492    GVariant *gv_from_port, *gv_to_port;
493    GtkTreeIter iter;
494 
495    DEBUG_MSG("gtkui_sslredir_add_service(%s)", se->name);
496 
497    /* Make GVariant from uint16 */
498    gv_from_port = g_variant_new_uint16(se->from_port);
499    gv_to_port = g_variant_new_uint16(se->to_port);
500 
501    /* update protocol list store */
502    gtk_list_store_append(proto_list, &iter);
503    gtk_list_store_set(proto_list, &iter,
504          0, ec_strlc(se->name),
505          1, ec_struc(se->name),
506          2, gv_from_port,
507          3, gv_to_port,
508          -1);
509 
510 }
511 /*
512  * callback function to compose the list of active redirects
513  */
gtkui_sslredir_add_list(struct redir_entry * re)514 static void gtkui_sslredir_add_list(struct redir_entry *re)
515 {
516    GVariant *gv_from_port, *gv_to_port;
517    GtkTreeIter iter;
518 
519    DEBUG_MSG("gtkui_sslredir_add_list(%s)", re->name);
520 
521    /* make GVariant from uint16 */
522    gv_from_port = g_variant_new_uint16(re->from_port);
523    gv_to_port = g_variant_new_uint16(re->to_port);
524 
525    /* add rule to rules list */
526    gtk_list_store_append(redirrules, &iter);
527    gtk_list_store_set(redirrules, &iter,
528          0, re->proto,
529          1, (re->proto == EC_REDIR_PROTO_IPV4 ? "IPv4" : "IPv6"),
530          2, re->destination,
531          3, gv_from_port,
532          4, gv_to_port,
533          5, ec_strlc(re->name),
534          6, ec_struc(re->name),
535          -1);
536 
537 }
538 
539 
540 /*
541  * callback when IP address family is changed
542  *    - update preset string of source / destination entry widgets
543  */
gtkui_sslredir_af_changed(GtkWidget * widget,gpointer data)544 void gtkui_sslredir_af_changed(GtkWidget *widget, gpointer data)
545 {
546    GtkTreeModel *model;
547    GtkTreeIter iter;
548    ec_redir_proto_t proto;
549 
550 
551    model = gtk_combo_box_get_model(GTK_COMBO_BOX(widget));
552    gtk_combo_box_get_active_iter(GTK_COMBO_BOX(widget), &iter);
553    gtk_tree_model_get(model, &iter, 1, &proto, -1);
554 
555    switch (proto) {
556       case EC_REDIR_PROTO_IPV4:
557          gtk_entry_set_text(GTK_ENTRY(data), "0.0.0.0/0");
558          gtk_widget_grab_focus(data);
559          break;
560       case EC_REDIR_PROTO_IPV6:
561          gtk_entry_set_text(GTK_ENTRY(data), "::/0");
562          gtk_widget_grab_focus(data);
563          break;
564       default:
565          break;
566    }
567 
568 }
569 
570 /*
571  * callback function when delete key is pressed in redirect rule list
572  */
gtkui_sslredir_key_pressed(GtkWidget * widget,GdkEventKey * event,gpointer data)573 gboolean gtkui_sslredir_key_pressed(GtkWidget *widget, GdkEventKey *event,
574       gpointer data)
575 {
576    DEBUG_MSG("gtkui_sslredir_key_pressed()");
577 
578    if (event->keyval == gdk_keyval_from_name("Delete")) {
579       gtkui_sslredir_del(widget, data);
580       return TRUE;
581    }
582 
583    if (event->keyval == gdk_keyval_from_name("Insert")) {
584       gtkui_sslredir_add(widget, data);
585       return TRUE;
586    }
587 
588    /* fall through to other handlers */
589    return FALSE;
590 }
591 
592 /* EOF */
593 
594 // vim:ts=3:expandtab
595 
596