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