fix the memory leak problem mentioned lately by adding and using color_filters_cleanup()
[metze/wireshark/wip.git] / color_filters.c
1 /* color_filters.c
2  * Routines for color filters
3  *
4  * $Id$
5  *
6  * Wireshark - Network traffic analyzer
7  * By Gerald Combs <gerald@wireshark.org>
8  * Copyright 1998 Gerald Combs
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23  */
24 /*
25  * Updated 1 Dec 10 jjm
26  */
27  
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <glib.h>
33 #include <ctype.h>
34 #include <string.h>
35
36 #include <epan/filesystem.h>
37 #include "file_util.h"
38
39 #include <epan/packet.h>
40 #include "color.h"
41 #include "color_filters.h"
42 #include "file.h"
43 #include <epan/dfilter/dfilter.h>
44 #include "simple_dialog.h"
45 #include "ui_util.h"
46
47 static gboolean read_users_filters(GSList **cfl);
48
49 /* the currently active filters */
50 static GSList *color_filter_list = NULL;
51
52 /* keep "old" deleted filters in this list until 
53  * the dissection no longer needs them (e.g. file is closed) */
54 static GSList *color_filter_deleted_list = NULL;
55
56 /* Color Filters can en-/disabled. */
57 gboolean filters_enabled = TRUE;
58
59
60 /* Create a new filter */
61 color_filter_t *
62 color_filter_new(const gchar *name,    /* The name of the filter to create */
63                  const gchar *filter_string, /* The string representing the filter */
64                  color_t *bg_color,    /* The background color */
65                  color_t *fg_color)    /* The foreground color */
66 {
67         color_filter_t *colorf;
68
69         colorf = g_malloc(sizeof (color_filter_t));
70         colorf->filter_name = g_strdup(name);
71         colorf->filter_text = g_strdup(filter_string);
72         colorf->bg_color = *bg_color;
73         colorf->fg_color = *fg_color;
74         colorf->c_colorfilter = NULL;
75         colorf->edit_dialog = NULL;
76         colorf->selected = FALSE;
77         return colorf;
78 }
79
80 /* delete the specified filter */
81 void
82 color_filter_delete(color_filter_t *colorf)
83 {
84         if (colorf->filter_name != NULL)
85                 g_free(colorf->filter_name);
86         if (colorf->filter_text != NULL)
87                 g_free(colorf->filter_text);
88         if (colorf->c_colorfilter != NULL)
89                 dfilter_free(colorf->c_colorfilter);
90         g_free(colorf);
91 }
92
93 /* delete the specified filter (called from g_slist_foreach) */
94 static void
95 color_filter_delete_cb(gpointer filter_arg, gpointer unused _U_)
96 {
97         color_filter_t *colorf = filter_arg;
98
99         color_filter_delete(colorf);
100 }
101
102 /* delete the specified list */
103 void
104 color_filter_list_delete(GSList **cfl)
105 {
106         g_slist_foreach(*cfl, color_filter_delete_cb, NULL);
107         g_slist_free(*cfl);
108         *cfl = NULL;
109 }
110
111 /* clone a single list entries from normal to edit list */
112 static color_filter_t *
113 color_filter_clone(color_filter_t *colorf)
114 {
115         color_filter_t *new_colorf;
116
117         new_colorf = g_malloc(sizeof (color_filter_t));
118         new_colorf->filter_name = g_strdup(colorf->filter_name);
119         new_colorf->filter_text = g_strdup(colorf->filter_text);
120         new_colorf->bg_color = colorf->bg_color;
121         new_colorf->fg_color = colorf->fg_color;
122         new_colorf->c_colorfilter = NULL;
123         new_colorf->edit_dialog = NULL;
124         new_colorf->selected = FALSE;
125
126         return new_colorf;
127 }
128
129 static void
130 color_filter_list_clone_cb(gpointer filter_arg, gpointer *cfl)
131 {
132     color_filter_t *new_colorf;
133
134     new_colorf = color_filter_clone(filter_arg);
135     *cfl = g_slist_append(*cfl, new_colorf);
136 }
137
138 /* clone the specified list */
139 static GSList *
140 color_filter_list_clone(GSList *cfl)
141 {
142         GSList *new_list = NULL;
143
144         g_slist_foreach(cfl, color_filter_list_clone_cb, &new_list);
145
146         return new_list;
147 }
148
149 /* Initialize the filter structures (reading from file) for general running, including app startup */
150 void
151 color_filters_init(void)
152 {
153         /* delete all currently existing filters */
154         color_filter_list_delete(&color_filter_list);
155
156         /* try to read the users filters */
157         if (!read_users_filters(&color_filter_list))
158                 /* if that failed, try to read the global filters */
159                 color_filters_read_globals(&color_filter_list);
160 }
161
162 void
163 color_filters_cleanup(void)
164 {
165         /* delete the previously deleted filters */
166         color_filter_list_delete(&color_filter_deleted_list);
167 }
168
169 static void
170 color_filters_clone_cb(gpointer filter_arg, gpointer user_data)
171 {
172         color_filter_t * new_colorf = color_filter_clone(filter_arg);
173
174         color_filter_add_cb (new_colorf, user_data);
175 }
176
177 void
178 color_filters_clone(gpointer user_data)
179 {
180     g_slist_foreach(color_filter_list, color_filters_clone_cb, user_data);
181 }
182
183
184 static void
185 color_filter_compile_cb(gpointer filter_arg, gpointer *cfl)
186 {
187         color_filter_t *colorf = filter_arg;
188
189         g_assert(colorf->c_colorfilter == NULL);
190         if (!dfilter_compile(colorf->filter_text, &colorf->c_colorfilter)) {
191                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
192                 "Could not compile color filter name: \"%s\" text: \"%s\".\n%s",
193                               colorf->filter_name, colorf->filter_text, dfilter_error_msg);
194                 /* this filter was compilable before, so this should never happen */
195                 g_assert_not_reached();
196         }
197 }
198
199 /* apply changes from the edit list */
200 void
201 color_filters_apply(GSList *cfl)
202 {
203         /* "move" old entries to the deleted list
204          * we must keep them until the dissection no longer needs them */
205         color_filter_deleted_list = g_slist_concat(color_filter_deleted_list, color_filter_list);
206         color_filter_list = NULL;
207
208         /* clone all list entries from edit to normal list */
209         color_filter_list = color_filter_list_clone(cfl);
210
211         /* compile all filter */
212         g_slist_foreach(color_filter_list, color_filter_compile_cb, NULL);
213 }
214
215 gboolean 
216 color_filters_used(void)
217 {
218         return color_filter_list != NULL && filters_enabled;
219 }
220
221 void
222 color_filters_enable(gboolean enable)
223 {
224         filters_enabled = enable;
225 }
226
227
228 /* prepare the epan_dissect_t for the filter */
229 static void
230 prime_edt(gpointer data, gpointer user_data)
231 {
232         color_filter_t  *colorf = data;
233         epan_dissect_t   *edt = user_data;
234
235         if (colorf->c_colorfilter != NULL)
236                 epan_dissect_prime_dfilter(edt, colorf->c_colorfilter);
237 }
238
239 /* Prime the epan_dissect_t with all the compiler
240  * color filters in 'color_filter_list'. */
241 void
242 color_filters_prime_edt(epan_dissect_t *edt)
243 {
244         g_slist_foreach(color_filter_list, prime_edt, edt);
245 }
246
247 /* Colorize a single packet of the packet list */
248 color_filter_t *
249 color_filters_colorize_packet(gint row, epan_dissect_t *edt)
250 {
251     GSList *curr;
252     color_filter_t *colorf;
253
254     /* If we have color filters, "search" for the matching one. */
255     if (color_filters_used()) {
256         curr = color_filter_list;
257
258         while( (curr = g_slist_next(curr)) != NULL) {
259             colorf = curr->data;
260             if ((colorf->c_colorfilter != NULL) &&
261                  dfilter_apply_edt(colorf->c_colorfilter, edt)) {
262                     /* this is the filter to use, apply it to the packet list */
263                     packet_list_set_colors(row, &(colorf->fg_color), &(colorf->bg_color));
264                     return colorf;
265             }
266         }
267     }
268
269     return NULL;
270 }
271
272 /* read filters from the given file */
273 /* XXX - Would it make more sense to use GStrings here instead of reallocing
274    our buffers? */
275 static gboolean
276 read_filters_file(FILE *f, gpointer user_data)
277 {
278 #define INIT_BUF_SIZE 128
279         gchar  *name = NULL;
280         gchar  *filter_exp = NULL;
281         guint32 name_len = INIT_BUF_SIZE;
282         guint32 filter_exp_len = INIT_BUF_SIZE;
283         guint32 i = 0;
284         gint32  c;
285         guint16 fg_r, fg_g, fg_b, bg_r, bg_g, bg_b;
286         gboolean skip_end_of_line = FALSE;
287
288         name = g_malloc(name_len + 1);
289         filter_exp = g_malloc(filter_exp_len + 1);
290
291         while (1) {
292
293                 if (skip_end_of_line) {
294                         do {
295                                 c = getc(f);
296                         } while (c != EOF && c != '\n');
297                         if (c == EOF)
298                                 break;
299                         skip_end_of_line = FALSE;
300                 }
301
302                 while ((c = getc(f)) != EOF && isspace(c)) {
303                         if (c == '\n') {
304                                 continue;
305                         }
306                 }
307
308                 if (c == EOF)
309                         break;
310
311                 /* skip # comments and invalid lines */
312                 if (c != '@') { 
313                         skip_end_of_line = TRUE;
314                         continue;
315                 }
316
317                 /* we get the @ delimiter.
318                  * Format is:
319                  * @name@filter expression@[background r,g,b][foreground r,g,b]
320                  */
321
322                 /* retrieve name */
323                 i = 0;
324                 while (1) {
325                         c = getc(f);
326                         if (c == EOF || c == '@')
327                                 break;
328                         if (i >= name_len) {
329                                 /* buffer isn't long enough; double its length.*/
330                                 name_len *= 2;
331                                 name = g_realloc(name, name_len + 1);
332                         }
333                         name[i++] = c;            
334                 }
335                 name[i] = '\0';
336
337                 if (c == EOF) {
338                         break;
339                 } else if (i == 0) {
340                         skip_end_of_line = TRUE;
341                         continue;
342                 }
343
344                 /* retrieve filter expression */
345                 i = 0;
346                 while (1) {
347                         c = getc(f);
348                         if (c == EOF || c == '@')
349                                 break;
350                         if (i >= filter_exp_len) {
351                                 /* buffer isn't long enough; double its length.*/
352                                 filter_exp_len *= 2;
353                                 filter_exp = g_realloc(filter_exp, filter_exp_len + 1);
354                         }
355                         filter_exp[i++] = c;
356                 }
357                 filter_exp[i] = '\0';
358
359                 if (c == EOF) {
360                         break;
361                 } else if (i == 0) {
362                         skip_end_of_line = TRUE;
363                         continue;
364                 }
365
366                 /* retrieve background and foreground colors */
367                 if (fscanf(f,"[%hu,%hu,%hu][%hu,%hu,%hu]",
368                         &bg_r, &bg_g, &bg_b, &fg_r, &fg_g, &fg_b) == 6) {
369
370                         /* we got a complete color filter */
371
372                         color_t bg_color, fg_color;
373                         color_filter_t *colorf;
374                         dfilter_t *temp_dfilter;
375
376                         if (!dfilter_compile(filter_exp, &temp_dfilter)) {
377                                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
378                                 "Could not compile color filter %s from saved filters.\n%s",
379                                               name, dfilter_error_msg);
380                                 skip_end_of_line = TRUE;
381                                 continue;
382                         }
383
384                         if (!initialize_color(&fg_color, fg_r, fg_g, fg_b)) {
385                                 /* oops */
386                                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
387                                     "Could not allocate foreground color "
388                                     "specified in input file for %s.", name);
389                                 dfilter_free(temp_dfilter);
390                                 skip_end_of_line = TRUE;
391                                 continue;
392                         }
393                         if (!initialize_color(&bg_color, bg_r, bg_g, bg_b)) {
394                                 /* oops */
395                                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
396                                     "Could not allocate background color "
397                                     "specified in input file for %s.", name);
398                                 dfilter_free(temp_dfilter);
399                                 skip_end_of_line = TRUE;
400                                 continue;
401                         }
402
403                         colorf = color_filter_new(name, filter_exp, &bg_color,
404                             &fg_color);
405                         if(user_data == &color_filter_list) {
406                                 GSList **cfl = (GSList **)user_data;
407
408                                 /* internal call */
409                                 colorf->c_colorfilter = temp_dfilter;
410                                 *cfl = g_slist_append(*cfl, colorf);
411                         } else {
412                                 /* external call */
413                                 /* just editing, don't need the compiled filter */
414                                 dfilter_free(temp_dfilter);
415                                 color_filter_add_cb (colorf, user_data);
416                         }
417                 }    /* if sscanf */
418
419                 skip_end_of_line = TRUE;
420         }
421
422         g_free(name);
423         g_free(filter_exp);
424         return TRUE;
425 }
426
427 /* read filters from the user's filter file */
428 static gboolean
429 read_users_filters(GSList **cfl)
430 {
431         gchar *path;
432         FILE *f;
433         gboolean ret;
434
435         /* decide what file to open (from dfilter code) */
436         path = get_persconffile_path("colorfilters", FALSE);
437         if ((f = eth_fopen(path, "r")) == NULL) {
438                 if (errno != ENOENT) {
439                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
440                             "Could not open filter file\n\"%s\": %s.", path,
441                             strerror(errno));
442                 }
443                 g_free(path);
444                 return FALSE;
445         }
446         g_free(path);
447         path = NULL;
448
449         ret = read_filters_file(f, cfl);
450         fclose(f);
451         return ret;
452 }
453
454 /* read filters from the filter file */
455 gboolean
456 color_filters_read_globals(gpointer user_data)
457 {
458         gchar *path;
459         FILE *f;
460         gboolean ret;
461
462         /* decide what file to open (from dfilter code) */
463         path = get_datafile_path("colorfilters");
464         if ((f = eth_fopen(path, "r")) == NULL) {
465                 if (errno != ENOENT) {
466                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
467                             "Could not open global filter file\n\"%s\": %s.", path,
468                             strerror(errno));
469                 }
470                 g_free(path);
471                 return FALSE;
472         }
473         g_free(path);
474         path = NULL;
475
476         ret = read_filters_file(f, user_data);
477         fclose(f);
478         return ret;
479 }
480
481 /* read filters from some other filter file (import) */
482 gboolean
483 color_filters_import(gchar *path, gpointer user_data)
484 {
485         FILE *f;
486         gboolean ret;
487
488         if ((f = eth_fopen(path, "r")) == NULL) {
489                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
490                     "Could not open\n%s\nfor reading: %s.",
491                     path, strerror(errno));
492                 return FALSE;
493         }
494
495         ret = read_filters_file(f, user_data);
496         fclose(f);
497         return ret;
498 }
499
500 struct write_filter_data
501 {
502   FILE * f;
503   gboolean only_selected;
504 };
505
506 /* save a single filter */
507 static void
508 write_filter(gpointer filter_arg, gpointer data_arg)
509 {
510         struct write_filter_data *data = data_arg;
511         color_filter_t *colorf = filter_arg;
512         FILE *f = data->f;
513
514         if (colorf->selected || !data->only_selected) {
515                 fprintf(f,"@%s@%s@[%d,%d,%d][%d,%d,%d]\n",
516                     colorf->filter_name,
517                     colorf->filter_text,
518                     colorf->bg_color.red,
519                     colorf->bg_color.green,
520                     colorf->bg_color.blue,
521                     colorf->fg_color.red,
522                     colorf->fg_color.green,
523                     colorf->fg_color.blue);
524         }
525 }
526
527 /* save filters in a filter file */
528 static gboolean
529 write_filters_file(GSList *cfl, FILE *f, gboolean only_selected)
530 {
531         struct write_filter_data data;
532
533         data.f = f;
534         data.only_selected = only_selected;
535   
536         fprintf(f,"# DO NOT EDIT THIS FILE!  It was created by Wireshark\n");
537         g_slist_foreach(cfl, write_filter, &data);
538         return TRUE;
539 }
540
541 /* save filters in users filter file */
542 gboolean
543 color_filters_write(GSList *cfl)
544 {
545         gchar *pf_dir_path;
546         const gchar *path;
547         FILE *f;
548
549         /* Create the directory that holds personal configuration files,
550            if necessary.  */
551         if (create_persconffile_dir(&pf_dir_path) == -1) {
552                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
553                     "Can't create directory\n\"%s\"\nfor color files: %s.",
554                     pf_dir_path, strerror(errno));
555                 g_free(pf_dir_path);
556                 return FALSE;
557         }
558
559         path = get_persconffile_path("colorfilters", TRUE);
560         if ((f = eth_fopen(path, "w+")) == NULL) {
561                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
562                     "Could not open\n%s\nfor writing: %s.",
563                     path, strerror(errno));
564                 return FALSE;
565         }
566         write_filters_file(cfl, f, FALSE);
567         fclose(f);
568         return TRUE;
569 }
570
571 /* save filters in some other filter file (export) */
572 gboolean
573 color_filters_export(gchar *path, GSList *cfl, gboolean only_marked)
574 {
575         FILE *f;
576
577         if ((f = eth_fopen(path, "w+")) == NULL) {
578                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
579                     "Could not open\n%s\nfor writing: %s.",
580                     path, strerror(errno));
581                 return FALSE;
582         }
583         write_filters_file(cfl, f, only_marked);
584         fclose(f);
585         return TRUE;
586 }
587