Initial revision
[obnox/wireshark/wip.git] / filter.c
1 /* filter.c
2  * Routines for managing filter sets
3  *
4  * Ethereal - Network traffic analyzer
5  * By Gerald Combs <gerald@zing.org>
6  * Copyright 1998 Gerald Combs
7  *
8  * 
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation; either version 2
12  * of the License, or (at your option) any later version.
13  * 
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  * 
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22  */
23
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
27
28 #include <gtk/gtk.h>
29
30 #include <string.h>
31 #include <sys/stat.h>
32 #include <unistd.h>
33
34 #include "filter.h"
35 #include "packet.h"
36 #include "file.h"
37 #include "menu.h"
38
39 extern capture_file cf;
40
41 const gchar *fn_key = "filter_name";
42 const gchar *fl_key = "filter_label";
43 GtkWidget   *filter_l, *chg_bt, *copy_bt, *del_bt, *name_te, *filter_te;
44 gint         in_cancel = FALSE;
45 GList       *fl = NULL;
46
47 GList *
48 read_filter_list() {
49   filter_def *filt;
50   FILE       *ff;
51   gchar      *ff_path, *ff_name = ".ethereal/filters", f_buf[256];
52   gchar      *name_begin, *name_end, *filt_begin;
53   int         len, line = 0;
54   
55   in_cancel = FALSE;
56   
57   /* To do: generalize this */
58   ff_path = (gchar *) g_malloc(strlen(getenv("HOME")) + strlen(ff_name) +  4);
59   sprintf(ff_path, "%s/%s", getenv("HOME"), ff_name);
60
61   if ((ff = fopen(ff_path, "r")) == NULL) {
62     g_free(ff_path);
63     return NULL;
64   }
65
66   while (fgets(f_buf, 256, ff)) {
67     line++;
68     len = strlen(f_buf);
69     if (f_buf[len - 1] = '\n') {
70       len--;
71       f_buf[len] = '\0';
72     }
73     name_begin = strchr(f_buf, '"');
74     /* Empty line */
75     if (name_begin == NULL)
76       continue;
77     name_end = strchr(name_begin + 1, '"');
78     /* No terminating quote */
79     if (name_end == NULL) {
80       g_warning("Malformed filter in '%s' line %d.", ff_path, line);
81       continue;
82     }
83     name_begin++;
84     name_end[0] = '\0';
85     filt_begin  = name_end + 1;
86     while(isspace(filt_begin[0])) filt_begin++;
87     /* No filter string */
88     if (filt_begin[0] == '\0') {
89       g_warning("Malformed filter in '%s' line %d.", ff_path, line);
90       continue;
91     }
92     filt         = (filter_def *) g_malloc(sizeof(filter_def));
93     filt->name   = g_strdup(name_begin);
94     filt->strval = g_strdup(filt_begin);
95     fl = g_list_append(fl, filt);
96   }
97   fclose(ff);
98   g_free(ff_path);
99   return fl;
100 }
101
102 /* filter_sel_cb - Create and display the filter selection dialog. */
103 /* Called when the 'Filter' menu item is selected. */
104 void
105 filter_sel_cb(GtkWidget *w, gpointer d) {
106   GtkWidget      *filter_w, *main_vb, *top_hb, *list_bb, *bbox,
107                  *new_bt, *ok_bt, *save_bt, *cancel_bt, *filter_sc, *nl_item,
108                  *nl_lb, *middle_hb, *name_lb, *bottom_hb, *filter_lb;
109   GtkWidget      *l_select = NULL;
110   GList          *flp = NULL, *nl = NULL;
111   filter_def     *filt;
112   
113   fl = read_filter_list();
114
115   filter_w = gtk_window_new(GTK_WINDOW_TOPLEVEL);
116   gtk_window_set_title(GTK_WINDOW(filter_w), "Ethereal: Filters");
117   
118   /* Container for each row of widgets */
119   main_vb = gtk_vbox_new(FALSE, 5);
120   gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
121   gtk_container_add(GTK_CONTAINER(filter_w), main_vb);
122   gtk_widget_show(main_vb);
123   
124   /* Top row: Filter list and buttons */
125   top_hb = gtk_hbox_new(FALSE, 5);
126   gtk_container_add(GTK_CONTAINER(main_vb), top_hb);
127   gtk_widget_show(top_hb);
128   
129   list_bb = gtk_vbutton_box_new();
130   gtk_button_box_set_layout (GTK_BUTTON_BOX (list_bb), GTK_BUTTONBOX_START);
131   gtk_container_add(GTK_CONTAINER(top_hb), list_bb);
132   gtk_widget_show(list_bb);
133
134   new_bt = gtk_button_new_with_label ("New");
135   gtk_signal_connect(GTK_OBJECT(new_bt), "clicked",
136     GTK_SIGNAL_FUNC(filter_sel_new_cb), NULL);
137   gtk_container_add(GTK_CONTAINER(list_bb), new_bt);
138   gtk_widget_show(new_bt);
139   
140   chg_bt = gtk_button_new_with_label ("Change");
141   gtk_widget_set_sensitive(chg_bt, FALSE);
142   gtk_signal_connect(GTK_OBJECT(chg_bt), "clicked",
143     GTK_SIGNAL_FUNC(filter_sel_chg_cb), NULL);
144   gtk_container_add(GTK_CONTAINER(list_bb), chg_bt);
145   gtk_widget_show(chg_bt);
146   
147   copy_bt = gtk_button_new_with_label ("Copy");
148   gtk_widget_set_sensitive(copy_bt, FALSE);
149   gtk_signal_connect(GTK_OBJECT(copy_bt), "clicked",
150     GTK_SIGNAL_FUNC(filter_sel_copy_cb), NULL);
151   gtk_container_add(GTK_CONTAINER(list_bb), copy_bt);
152   gtk_widget_show(copy_bt);
153   
154   del_bt = gtk_button_new_with_label ("Delete");
155   gtk_widget_set_sensitive(del_bt, FALSE);
156   gtk_signal_connect(GTK_OBJECT(del_bt), "clicked",
157     GTK_SIGNAL_FUNC(filter_sel_del_cb), NULL);
158   gtk_container_add(GTK_CONTAINER(list_bb), del_bt);
159   gtk_widget_show(del_bt);
160   
161   filter_sc = gtk_scrolled_window_new(NULL, NULL);
162   gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(filter_sc),
163     GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
164   gtk_widget_set_usize(filter_sc, 250, 150);
165   gtk_container_add(GTK_CONTAINER(top_hb), filter_sc);
166   gtk_widget_show(filter_sc);
167
168   filter_l = gtk_list_new();
169   gtk_signal_connect(GTK_OBJECT(filter_l), "selection_changed",
170     GTK_SIGNAL_FUNC(filter_sel_list_cb), NULL);
171   gtk_container_add(GTK_CONTAINER(filter_sc), filter_l);
172   gtk_widget_show(filter_l);
173
174   flp = g_list_first(fl);
175   while (flp) {
176     filt    = (filter_def *) flp->data;
177     nl_lb   = gtk_label_new(filt->name);
178     nl_item = gtk_list_item_new();
179     gtk_misc_set_alignment (GTK_MISC (nl_lb), 0.0, 0.5);
180     gtk_container_add(GTK_CONTAINER(nl_item), nl_lb);
181     gtk_widget_show(nl_lb);
182     gtk_container_add(GTK_CONTAINER(filter_l), nl_item);
183     gtk_widget_show(nl_item);
184     gtk_object_set_data(GTK_OBJECT(nl_item), fl_key, nl_lb);
185     gtk_object_set_data(GTK_OBJECT(nl_item), fn_key, flp);
186     if (cf.filter && filt->strval)
187       if (strcmp(cf.filter, filt->strval) == 0)
188         l_select = nl_item;
189     flp = flp->next;
190   }
191   
192   /* Middle row: Filter name entry */
193   middle_hb = gtk_hbox_new(FALSE, 5);
194   gtk_container_add(GTK_CONTAINER(main_vb), middle_hb);
195   gtk_widget_show(middle_hb);
196   
197   name_lb = gtk_label_new("Filter name:");
198   gtk_box_pack_start(GTK_BOX(middle_hb), name_lb, FALSE, FALSE, 3);
199   gtk_widget_show(name_lb);
200   
201   name_te = gtk_entry_new();
202   gtk_box_pack_start(GTK_BOX(middle_hb), name_te, TRUE, TRUE, 3);
203   gtk_widget_show(name_te);
204
205   /* Bottom row: Filter text entry */
206   bottom_hb = gtk_hbox_new(FALSE, 5);
207   gtk_container_add(GTK_CONTAINER(main_vb), bottom_hb);
208   gtk_widget_show(bottom_hb);
209   
210   filter_lb = gtk_label_new("Filter string:");
211   gtk_box_pack_start(GTK_BOX(bottom_hb), filter_lb, FALSE, FALSE, 3);
212   gtk_widget_show(filter_lb);
213   
214   filter_te = gtk_entry_new();
215   gtk_box_pack_start(GTK_BOX(bottom_hb), filter_te, TRUE, TRUE, 3);
216   gtk_widget_show(filter_te);
217
218   /* Button row: OK and cancel buttons */
219   bbox = gtk_hbutton_box_new();
220   gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_END);
221   gtk_container_add(GTK_CONTAINER(main_vb), bbox);
222   gtk_widget_show(bbox);
223   
224   ok_bt = gtk_button_new_with_label ("OK");
225   gtk_signal_connect(GTK_OBJECT(ok_bt), "clicked",
226     GTK_SIGNAL_FUNC(filter_sel_ok_cb), (gpointer) filter_w);
227   gtk_container_add(GTK_CONTAINER(bbox), ok_bt);
228   GTK_WIDGET_SET_FLAGS(ok_bt, GTK_CAN_DEFAULT);
229   gtk_widget_grab_default(ok_bt);  
230   gtk_widget_show(ok_bt);
231
232   save_bt = gtk_button_new_with_label ("Save");
233   gtk_signal_connect(GTK_OBJECT(save_bt), "clicked",
234     GTK_SIGNAL_FUNC(filter_sel_save_cb), (gpointer) fl);
235   gtk_container_add(GTK_CONTAINER(bbox), save_bt);
236   gtk_widget_show(save_bt);
237   
238   cancel_bt = gtk_button_new_with_label ("Cancel");
239   gtk_signal_connect(GTK_OBJECT(cancel_bt), "clicked",
240     GTK_SIGNAL_FUNC(filter_sel_cancel_cb), (gpointer) filter_w);
241   gtk_container_add(GTK_CONTAINER(bbox), cancel_bt);
242   gtk_widget_show(cancel_bt);
243   
244   gtk_widget_show(filter_w);
245
246   if (l_select)
247     gtk_list_select_child(GTK_LIST(filter_l), l_select);
248 }
249
250 void
251 filter_sel_list_cb(GtkWidget *l, gpointer data) {
252   filter_def *filt;
253   gchar      *name = "", *strval = "";
254   GList      *sl, *flp;
255   GtkObject  *l_item;
256   gint        sensitivity = FALSE;
257
258   sl = GTK_LIST(l)->selection;
259           
260   if (sl) {  /* Something was selected */
261     l_item = GTK_OBJECT(sl->data);
262     flp    = (GList *) gtk_object_get_data(l_item, fn_key);
263     if (flp) {
264       filt   = (filter_def *) flp->data;
265       name   = filt->name;
266       strval = filt->strval;
267       sensitivity = TRUE;
268     }
269   }
270
271   /* Did you know that this function is called when the window is destroyed? */
272   /* Funny, that. */
273   if (!in_cancel) {
274     gtk_entry_set_text(GTK_ENTRY(name_te), name);
275     gtk_entry_set_text(GTK_ENTRY(filter_te), strval);
276     gtk_widget_set_sensitive(chg_bt, sensitivity);
277     gtk_widget_set_sensitive(copy_bt, sensitivity);
278     gtk_widget_set_sensitive(del_bt, sensitivity);
279   }
280 }
281
282 /* To do: add input checking to each of these callbacks */
283  
284 void
285 filter_sel_new_cb(GtkWidget *w, gpointer data) {
286   GList      *nl = NULL;
287   filter_def *filt;
288   gchar      *name, *strval;
289   GtkWidget  *nl_item, *nl_lb;
290   
291   name   = gtk_entry_get_text(GTK_ENTRY(name_te));
292   strval = gtk_entry_get_text(GTK_ENTRY(filter_te));
293   
294   if (strlen(name) > 0 && strlen(strval) > 0) {
295     filt         = (filter_def *) g_malloc(sizeof(filter_def));
296     filt->name   = g_strdup(name);
297     filt->strval = g_strdup(strval);
298     fl           = g_list_append(fl, filt);
299     nl_lb        = gtk_label_new(filt->name);
300     nl_item      = gtk_list_item_new();
301     gtk_misc_set_alignment (GTK_MISC (nl_lb), 0.0, 0.5);
302     gtk_container_add(GTK_CONTAINER(nl_item), nl_lb);
303     gtk_widget_show(nl_lb);
304     gtk_container_add(GTK_CONTAINER(filter_l), nl_item);
305     gtk_widget_show(nl_item);
306     gtk_object_set_data(GTK_OBJECT(nl_item), fl_key, nl_lb);
307     gtk_object_set_data(GTK_OBJECT(nl_item), fn_key, g_list_last(fl));
308   }
309 }
310
311 void
312 filter_sel_chg_cb(GtkWidget *w, gpointer data) {
313   filter_def *filt;
314   gchar      *name = "", *strval = "";
315   GList      *sl, *flp;
316   GtkObject  *l_item;
317   GtkLabel   *nl_lb;
318   gint        sensitivity = FALSE;
319
320   sl     = GTK_LIST(filter_l)->selection;
321   name   = gtk_entry_get_text(GTK_ENTRY(name_te));
322   strval = gtk_entry_get_text(GTK_ENTRY(filter_te));
323
324   if (sl) {  /* Something was selected */
325     l_item = GTK_OBJECT(sl->data);
326     flp    = (GList *) gtk_object_get_data(l_item, fn_key);
327     nl_lb  = (GtkLabel *) gtk_object_get_data(l_item, fl_key);
328     if (flp && nl_lb) {
329       filt = (filter_def *) flp->data;
330       
331       if (strlen(name) > 0 && strlen(strval) > 0 && filt) {
332         g_free(filt->name);
333         g_free(filt->strval);
334         filt->name   = g_strdup(name);
335         filt->strval = g_strdup(strval);
336         gtk_label_set(nl_lb, filt->name);
337       }
338     }
339   }
340 }
341
342 void
343 filter_sel_copy_cb(GtkWidget *w, gpointer data) {
344   GList      *nl = NULL, *sl, *flp;
345   filter_def *filt, *nfilt;
346   gchar      *name, *strval, *prefix = "Copy of ";
347   GtkObject  *l_item;
348   GtkWidget  *nl_item, *nl_lb;
349   
350   sl     = GTK_LIST(filter_l)->selection;
351   if (sl) {  /* Something was selected */
352     l_item = GTK_OBJECT(sl->data);
353     flp    = (GList *) gtk_object_get_data(l_item, fn_key);
354     if (flp) {
355       filt          = (filter_def *) flp->data;
356       nfilt         = (filter_def *) g_malloc(sizeof(filter_def));
357       nfilt->name   = g_malloc(strlen(prefix) + strlen(filt->name) + 1);
358       sprintf(nfilt->name, "%s%s", prefix, filt->name);
359       nfilt->strval = g_strdup(filt->strval);
360       fl            = g_list_append(fl, nfilt);
361       nl_lb         = gtk_label_new(nfilt->name);
362       nl_item       = gtk_list_item_new();
363       gtk_misc_set_alignment (GTK_MISC (nl_lb), 0.0, 0.5);
364       gtk_container_add(GTK_CONTAINER(nl_item), nl_lb);
365       gtk_widget_show(nl_lb);
366       gtk_container_add(GTK_CONTAINER(filter_l), nl_item);
367       gtk_widget_show(nl_item);
368       gtk_object_set_data(GTK_OBJECT(nl_item), fl_key, nl_lb);
369       gtk_object_set_data(GTK_OBJECT(nl_item), fn_key, g_list_last(fl));
370     }
371   }
372 }
373
374 void
375 filter_sel_del_cb(GtkWidget *w, gpointer data) {
376   GList      *sl, *flp;
377   filter_def *filt;
378   GtkObject  *l_item;
379   GtkWidget  *nl_item;
380   gint        pos;
381   
382   sl = GTK_LIST(filter_l)->selection;
383   if (sl) {  /* Something was selected */
384     l_item = GTK_OBJECT(sl->data);
385     pos    = gtk_list_child_position(GTK_LIST(filter_l),
386       GTK_WIDGET(l_item));
387     flp    = (GList *) gtk_object_get_data(l_item, fn_key);
388     if (flp) {
389       filt = (filter_def *) flp->data;
390       g_free(filt->name);
391       g_free(filt->strval);
392       g_free(filt);
393       fl = g_list_remove_link(fl, flp);
394       gtk_list_clear_items(GTK_LIST(filter_l), pos, pos + 1);
395     } 
396   }
397 }
398
399 void
400 filter_sel_ok_cb(GtkWidget *w, gpointer data) {
401   GList      *flp, *sl;
402   GtkObject  *l_item;
403   filter_def *filt;
404
405   if (cf.filter) {
406     g_free(cf.filter);
407     cf.filter = NULL;
408   }
409     
410   sl = GTK_LIST(filter_l)->selection;
411   if (sl) {  /* Something was selected */
412     l_item = GTK_OBJECT(sl->data);
413     flp    = (GList *) gtk_object_get_data(l_item, fn_key);
414     if (flp) {
415       filt = (filter_def *) flp->data;
416       cf.filter = g_strdup(filt->strval);
417     }
418   }
419
420   filter_sel_cancel_cb(w, data);
421 }
422
423 void
424 filter_sel_save_cb(GtkWidget *w, gpointer data) {
425   GList       *flp;
426   filter_def  *filt;
427   gchar       *ff_path, *ff_dir = ".ethereal", *ff_name = "filters";
428   FILE        *ff;
429   struct stat  s_buf;
430   
431   ff_path = (gchar *) g_malloc(strlen(getenv("HOME")) + strlen(ff_dir) +  
432     strlen(ff_name) + 4);
433   sprintf(ff_path, "%s/%s", getenv("HOME"), ff_dir);
434
435   if (stat(ff_path, &s_buf) != 0)
436     mkdir(ff_path, 0755);
437     
438   sprintf(ff_path, "%s/%s/%s", getenv("HOME"), ff_dir, ff_name);
439
440   if ((ff = fopen(ff_path, "w")) != NULL) {
441     flp = g_list_first(fl);
442     while (flp) {
443       filt = (filter_def *) flp->data;
444       fprintf(ff, "\"%s\" %s\n", filt->name, filt->strval);
445       flp = flp->next;
446     }
447     fclose(ff);
448   }
449
450   g_free(ff_path);
451 }
452
453 void
454 filter_sel_cancel_cb(GtkWidget *w, gpointer win) {
455   filter_def *filt;
456   GList      *sl;
457   
458   while (fl) {
459     if (fl->data) {
460       filt = (filter_def *) fl->data;
461       g_free(filt->name);
462       g_free(filt->strval);
463       g_free(filt);
464     }
465     fl = g_list_remove_link(fl, fl);
466   }
467
468   /* Let the list cb know we're about to destroy the widget tree, so it */
469   /* doesn't operate on widgets that don't exist. */  
470   in_cancel = TRUE;    
471   gtk_widget_destroy(GTK_WIDGET(win));
472