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