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