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