67cd5c1af9031a0ee7bfd1759be314d4a935d9ac
[obnox/wireshark/wip.git] / color_filters.c
1 /* color_filters.c
2  * Routines for color filters
3  *
4  * $Id$
5  *
6  * Ethereal - Network traffic analyzer
7  * By Gerald Combs <gerald@ethereal.com>
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 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #include <glib.h>
32 #include <ctype.h>
33 #include <string.h>
34
35 #include <epan/filesystem.h>
36
37 #include <epan/packet.h>
38 #include "color.h"
39 #include "color_filters.h"
40 #include "file.h"
41 #include <epan/dfilter/dfilter.h>
42 #include "simple_dialog.h"
43
44 static gboolean read_filters(void);
45 static gboolean read_global_filters(void);
46
47 GSList *filter_list = NULL;
48 GSList *removed_filter_list = NULL;
49
50 /* Remove the specified filter from the list of existing color filters,
51  * and add it to the list of removed color filters.
52  * This way, unmarking and marking a packet which matches a now removed
53  * color filter will still be colored correctly as the color filter is
54  * still reachable. */
55 void remove_color_filter(color_filter_t *colorf)
56 {
57         /* Remove colorf from the list of color filters */
58         filter_list = g_slist_remove(filter_list, colorf);
59         /* Add colorf to the list of removed color filters */
60         removed_filter_list = g_slist_prepend(removed_filter_list, colorf);
61 }
62
63 /* delete the specified filter */
64 static void
65 delete_color_filter(color_filter_t *colorf)
66 {
67         if (colorf->filter_name != NULL)
68                 g_free(colorf->filter_name);
69         if (colorf->filter_text != NULL)
70                 g_free(colorf->filter_text);
71         if (colorf->c_colorfilter != NULL)
72                 dfilter_free(colorf->c_colorfilter);
73         g_free(colorf);
74 }
75
76 /* delete the specified filter as an iterator*/
77 static void
78 delete_color_filter_it(gpointer filter_arg, gpointer unused _U_)
79 {
80         color_filter_t *colorf = filter_arg;
81         delete_color_filter(colorf);
82 }
83
84 /* delete all the filters */
85 static void
86 delete_all_color_filters (void)
87 {
88         g_slist_foreach(filter_list, delete_color_filter_it, NULL);
89         g_slist_free(filter_list);
90         filter_list = NULL;
91         g_slist_foreach(removed_filter_list, delete_color_filter_it, NULL);
92         g_slist_free(removed_filter_list);
93         removed_filter_list = NULL;
94 }
95
96 /* Initialize the filter structures (reading from file) for general running, including app startup */
97 void
98 colfilter_init(void)
99 {
100         delete_all_color_filters();
101         if (!read_filters())
102                 read_global_filters();
103 }
104
105 /* Create a new filter */
106 color_filter_t *
107 new_color_filter(gchar *name,          /* The name of the filter to create */
108                  gchar *filter_string, /* The string representing the filter */
109                  color_t *bg_color,    /* The background color */
110                  color_t *fg_color)    /* The foreground color */
111 {
112         color_filter_t *colorf;
113
114         colorf = g_malloc(sizeof (color_filter_t));
115         colorf->filter_name = g_strdup(name);
116         colorf->filter_text = g_strdup(filter_string);
117         colorf->bg_color = *bg_color;
118         colorf->fg_color = *fg_color;
119         colorf->c_colorfilter = NULL;
120         colorf->edit_dialog = NULL;
121         colorf->marked = FALSE;
122         filter_list = g_slist_append(filter_list, colorf);
123         return colorf;
124 }
125
126 static void
127 prime_edt(gpointer data, gpointer user_data)
128 {
129         color_filter_t  *colorf = data;
130         epan_dissect_t   *edt = user_data;
131
132         if (colorf->c_colorfilter != NULL)
133                 epan_dissect_prime_dfilter(edt, colorf->c_colorfilter);
134 }
135
136 /* Prime the epan_dissect_t with all the compiler
137  * color filters in 'filter_list'. */
138 void
139 filter_list_prime_edt(epan_dissect_t *edt)
140 {
141         g_slist_foreach(filter_list, prime_edt, edt);
142 }
143
144 /* read filters from the given file */
145 static gboolean
146 read_filters_file(FILE *f, gpointer arg)
147 {
148 #define INIT_BUF_SIZE 128
149         gchar  *name = NULL;
150         gchar  *filter_exp = NULL;
151         guint32 name_len = INIT_BUF_SIZE;
152         guint32 filter_exp_len = INIT_BUF_SIZE;
153         guint32 i = 0;
154         gint32  c;
155         guint16 fg_r, fg_g, fg_b, bg_r, bg_g, bg_b;
156         gboolean skip_end_of_line = FALSE;
157
158         name = g_malloc(name_len + 1);
159         filter_exp = g_malloc(filter_exp_len + 1);
160
161         while (1) {
162
163                 if (skip_end_of_line) {
164                         do {
165                                 c = getc(f);
166                         } while (c != EOF && c != '\n');
167                         if (c == EOF)
168                                 break;
169                         skip_end_of_line = FALSE;
170                 }
171
172                 while ((c = getc(f)) != EOF && isspace(c)) {
173                         if (c == '\n') {
174                                 continue;
175                         }
176                 }
177
178                 if (c == EOF)
179                         break;
180
181                 /* skip # comments and invalid lines */
182                 if (c != '@') { 
183                         skip_end_of_line = TRUE;
184                         continue;
185                 }
186
187                 /* we get the @ delimiter.
188                  * Format is:
189                  * @name@filter expression@[background r,g,b][foreground r,g,b]
190                  */
191
192                 /* retrieve name */
193                 i = 0;
194                 while (1) {
195                         c = getc(f);
196                         if (c == EOF || c == '@')
197                                 break;
198                         if (i >= name_len) {
199                                 /* buffer isn't long enough; double its length.*/
200                                 name_len *= 2;
201                                 name = g_realloc(name, name_len + 1);
202                         }
203                         name[i++] = c;            
204                 }
205                 name[i] = '\0';
206
207                 if (c == EOF) {
208                         break;
209                 } else if (i == 0) {
210                         skip_end_of_line = TRUE;
211                         continue;
212                 }
213
214                 /* retrieve filter expression */
215                 i = 0;
216                 while (1) {
217                         c = getc(f);
218                         if (c == EOF || c == '@')
219                                 break;
220                         if (i >= filter_exp_len) {
221                                 /* buffer isn't long enough; double its length.*/
222                                 filter_exp_len *= 2;
223                                 filter_exp = g_realloc(filter_exp, filter_exp_len + 1);
224                         }
225                         filter_exp[i++] = c;
226                 }
227                 filter_exp[i] = '\0';
228
229                 if (c == EOF) {
230                         break;
231                 } else if (i == 0) {
232                         skip_end_of_line = TRUE;
233                         continue;
234                 }
235
236                 /* retrieve background and foreground colors */
237                 if (fscanf(f,"[%hu,%hu,%hu][%hu,%hu,%hu]",
238                         &bg_r, &bg_g, &bg_b, &fg_r, &fg_g, &fg_b) == 6) {
239
240                         /* we got a complete color filter */
241
242                         color_t bg_color, fg_color;
243                         color_filter_t *colorf;
244                         dfilter_t *temp_dfilter;
245
246                         if (!dfilter_compile(filter_exp, &temp_dfilter)) {
247                                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
248                                 "Could not compile color filter %s from saved filters.\n%s",
249                                               name, dfilter_error_msg);
250                                 skip_end_of_line = TRUE;
251                                 continue;
252                         }
253
254                         if (!create_color(&fg_color, fg_r, fg_g, fg_b)) {
255                                 /* oops */
256                                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
257                                     "Could not allocate foreground color "
258                                     "specified in input file for %s.", name);
259                                 dfilter_free(temp_dfilter);
260                                 skip_end_of_line = TRUE;
261                                 continue;
262                         }
263                         if (!create_color(&bg_color, bg_r, bg_g, bg_b)) {
264                                 /* oops */
265                                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
266                                     "Could not allocate background color "
267                                     "specified in input file for %s.", name);
268                                 dfilter_free(temp_dfilter);
269                                 skip_end_of_line = TRUE;
270                                 continue;
271                         }
272
273                         colorf = new_color_filter(name, filter_exp, &bg_color,
274                             &fg_color);
275                         colorf->c_colorfilter = temp_dfilter;
276
277                         if (arg != NULL)
278                                 color_add_filter_cb (colorf, arg);
279                 }    /* if sscanf */
280
281                 skip_end_of_line = TRUE;
282         }
283
284         g_free(name);
285         g_free(filter_exp);
286         return TRUE;
287 }
288
289 /* read filters from the user's filter file */
290 static gboolean
291 read_filters(void)
292 {
293         gchar *path;
294         FILE *f;
295         gboolean ret;
296
297         /* decide what file to open (from dfilter code) */
298         path = get_persconffile_path("colorfilters", FALSE);
299         if ((f = fopen(path, "r")) == NULL) {
300                 if (errno != ENOENT) {
301                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
302                             "Could not open filter file\n\"%s\": %s.", path,
303                             strerror(errno));
304                 }
305                 g_free(path);
306                 return FALSE;
307         }
308         g_free(path);
309         path = NULL;
310
311         ret = read_filters_file(f, NULL);
312         fclose(f);
313         return ret;
314 }
315
316 /* read filters from the filter file */
317 static gboolean
318 read_global_filters(void)
319 {
320         gchar *path;
321         FILE *f;
322         gboolean ret;
323
324         /* decide what file to open (from dfilter code) */
325         path = get_datafile_path("colorfilters");
326         if ((f = fopen(path, "r")) == NULL) {
327                 if (errno != ENOENT) {
328                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
329                             "Could not open global filter file\n\"%s\": %s.", path,
330                             strerror(errno));
331                 }
332                 g_free(path);
333                 return FALSE;
334         }
335         g_free(path);
336         path = NULL;
337
338         ret = read_filters_file(f, NULL);
339         fclose(f);
340         return ret;
341 }
342
343 /* save filters in some other filter file */
344
345 gboolean
346 read_other_filters(gchar *path, gpointer arg)
347 {
348         FILE *f;
349         gboolean ret;
350
351         if ((f = fopen(path, "r")) == NULL) {
352                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
353                     "Could not open\n%s\nfor reading: %s.",
354                     path, strerror(errno));
355                 return FALSE;
356         }
357
358         ret = read_filters_file(f, arg);
359         fclose(f);
360         return ret;
361 }
362
363 struct write_filter_data
364 {
365   FILE * f;
366   gboolean only_marked;
367 };
368
369 static void
370 write_filter(gpointer filter_arg, gpointer data_arg)
371 {
372         struct write_filter_data *data = data_arg;
373         color_filter_t *colorf = filter_arg;
374         FILE *f = data->f;
375
376         if (colorf->marked || !data->only_marked) {
377                 fprintf(f,"@%s@%s@[%d,%d,%d][%d,%d,%d]\n",
378                     colorf->filter_name,
379                     colorf->filter_text,
380                     colorf->bg_color.red,
381                     colorf->bg_color.green,
382                     colorf->bg_color.blue,
383                     colorf->fg_color.red,
384                     colorf->fg_color.green,
385                     colorf->fg_color.blue);
386         }
387 }
388
389 /* save filters in a filter file */
390 gboolean
391 write_filters_file(FILE *f, gboolean only_marked)
392 {
393         struct write_filter_data data;
394
395         data.f = f;
396         data.only_marked = only_marked;
397   
398         fprintf(f,"# DO NOT EDIT THIS FILE!  It was created by Ethereal\n");
399         g_slist_foreach(filter_list, write_filter, &data);
400         return TRUE;
401 }
402
403 /* save filters in users filter file */
404
405 gboolean
406 write_filters(void)
407 {
408         gchar *pf_dir_path;
409         const gchar *path;
410         FILE *f;
411
412         /* Create the directory that holds personal configuration files,
413            if necessary.  */
414         if (create_persconffile_dir(&pf_dir_path) == -1) {
415                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
416                     "Can't create directory\n\"%s\"\nfor color files: %s.",
417                     pf_dir_path, strerror(errno));
418                 g_free(pf_dir_path);
419                 return FALSE;
420         }
421
422         path = get_persconffile_path("colorfilters", TRUE);
423         if ((f = fopen(path, "w+")) == NULL) {
424                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
425                     "Could not open\n%s\nfor writing: %s.",
426                     path, strerror(errno));
427                 return FALSE;
428         }
429         write_filters_file(f, FALSE);
430         fclose(f);
431         return TRUE;
432 }
433
434 /* delete users filter file and reload global filters*/
435
436 gboolean
437 revert_filters(void)
438 {
439         gchar *pf_dir_path;
440         const gchar *path;
441
442         /* Create the directory that holds personal configuration files,
443            if necessary.  */
444         if (create_persconffile_dir(&pf_dir_path) == -1) {
445                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
446                     "Can't create directory\n\"%s\"\nfor color files: %s.",
447                     pf_dir_path, strerror(errno));
448                 g_free(pf_dir_path);
449                 return FALSE;
450         }
451
452         path = get_persconffile_path("colorfilters", TRUE);
453         if (!deletefile(path)) {
454                 g_free((gchar *)path);
455                 return FALSE;
456         }
457
458         g_free((gchar *)path);
459
460         /* Reload the (global) filters - Note: this does not update the dialog. */
461         colfilter_init();
462         return TRUE;
463 }
464
465
466 /* save filters in some other filter file */
467
468 gboolean
469 write_other_filters(gchar *path, gboolean only_marked)
470 {
471         FILE *f;
472
473         if ((f = fopen(path, "w+")) == NULL) {
474                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
475                     "Could not open\n%s\nfor writing: %s.",
476                     path, strerror(errno));
477                 return FALSE;
478         }
479         write_filters_file(f, only_marked);
480         fclose(f);
481         return TRUE;
482 }