2 * Routines for color filters
4 * Wireshark - Network traffic analyzer
5 * By Gerald Combs <gerald@wireshark.org>
6 * Copyright 1998 Gerald Combs
8 * SPDX-License-Identifier: GPL-2.0-or-later
11 * Updated 1 Dec 10 jjm
22 #include <wsutil/filesystem.h>
23 #include <wsutil/file_util.h>
25 #include <epan/packet.h>
26 #include "color_filters.h"
28 #include <epan/dfilter/dfilter.h>
29 #include <epan/prefs.h>
30 #include <epan/epan_dissect.h>
32 #define RED_COMPONENT(x) (guint16) (((((x) >> 16) & 0xff) * 65535 / 255))
33 #define GREEN_COMPONENT(x) (guint16) (((((x) >> 8) & 0xff) * 65535 / 255))
34 #define BLUE_COMPONENT(x) (guint16) ( (((x) & 0xff) * 65535 / 255))
36 static int read_filters_file(const gchar *path, FILE *f, gpointer user_data, color_filter_add_cb_func add_cb);
38 /* the currently active filters */
39 static GSList *color_filter_list = NULL;
41 /* keep "old" deleted filters in this list until
42 * the dissection no longer needs them (e.g. file is closed) */
43 static GSList *color_filter_deleted_list = NULL;
44 static GSList *color_filter_valid_list = NULL;
46 /* Color Filters can en-/disabled. */
47 static gboolean filters_enabled = TRUE;
49 /* Remember if there are temporary coloring filters set to
50 * add sensitivity to the "Reset Coloring 1-10" menu item
52 static gboolean tmp_colors_set = FALSE;
54 /* Create a new filter */
56 color_filter_new(const gchar *name, /* The name of the filter to create */
57 const gchar *filter_string, /* The string representing the filter */
58 color_t *bg_color, /* The background color */
59 color_t *fg_color, /* The foreground color */
60 gboolean disabled) /* Is the filter disabled? */
62 color_filter_t *colorf;
64 colorf = (color_filter_t *)g_malloc0(sizeof (color_filter_t));
65 colorf->filter_name = g_strdup(name);
66 colorf->filter_text = g_strdup(filter_string);
67 colorf->bg_color = *bg_color;
68 colorf->fg_color = *fg_color;
69 colorf->disabled = disabled;
73 /* Add ten empty (temporary) colorfilters for easy coloring */
75 color_filters_add_tmp(GSList **cfl)
82 color_t bg_color, fg_color;
83 color_filter_t *colorf;
85 g_assert(strlen(prefs.gui_colorized_fg)==69);
86 g_assert(strlen(prefs.gui_colorized_bg)==69);
87 fg_colors = g_strsplit(prefs.gui_colorized_fg, ",", -1);
88 bg_colors = g_strsplit(prefs.gui_colorized_bg, ",", -1);
90 for ( i=1 ; i<=10 ; i++ ) {
91 name = g_strdup_printf("%s%02d",CONVERSATION_COLOR_PREFIX,i);
93 /* retrieve background and foreground colors */
94 cval = strtoul(fg_colors[i-1], NULL, 16);
95 fg_color.red = RED_COMPONENT(cval);
96 fg_color.green = GREEN_COMPONENT(cval);
97 fg_color.blue = BLUE_COMPONENT(cval);
98 cval = strtoul(bg_colors[i-1], NULL, 16);
99 bg_color.red = RED_COMPONENT(cval);
100 bg_color.green = GREEN_COMPONENT(cval);
101 bg_color.blue = BLUE_COMPONENT(cval);
102 colorf = color_filter_new(name, NULL, &bg_color, &fg_color, TRUE);
103 colorf->filter_text = g_strdup("frame");
104 *cfl = g_slist_append(*cfl, colorf);
109 g_strfreev(fg_colors);
110 g_strfreev(bg_colors);
116 color_filters_find_by_name_cb(gconstpointer arg1, gconstpointer arg2)
118 const color_filter_t *colorf = (const color_filter_t *)arg1;
119 const gchar *name = (const gchar *)arg2;
121 return strcmp(colorf->filter_name, name);
125 /* Set the filter off a temporary colorfilters and enable it */
127 color_filters_set_tmp(guint8 filt_nr, const gchar *filter, gboolean disabled, gchar **err_msg)
130 const gchar *tmpfilter = NULL;
132 color_filter_t *colorf;
133 dfilter_t *compiled_filter;
135 gchar *local_err_msg = NULL;
136 /* Go through the temporary filters and look for the same filter string.
137 * If found, clear it so that a filter can be "moved" up and down the list
139 for ( i=1 ; i<=10 ; i++ ) {
140 /* If we need to reset the temporary filter (filter==NULL), don't look
141 * for other rules with the same filter string
143 if( i!=filt_nr && filter==NULL )
146 name = g_strdup_printf("%s%02d",CONVERSATION_COLOR_PREFIX,i);
147 cfl = g_slist_find_custom(color_filter_list, name, color_filters_find_by_name_cb);
148 colorf = (color_filter_t *)cfl->data;
150 /* Only change the filter rule if this is the rule to change or if
151 * a matching filter string has been found
153 if(colorf && ( (i==filt_nr) || (!strcmp(filter, colorf->filter_text)) ) ) {
154 /* set filter string to "frame" if we are resetting the rules
155 * or if we found a matching filter string which need to be cleared
157 tmpfilter = ( (filter==NULL) || (i!=filt_nr) ) ? "frame" : filter;
158 if (!dfilter_compile(tmpfilter, &compiled_filter, &local_err_msg)) {
159 *err_msg = g_strdup_printf( "Could not compile color filter name: \"%s\" text: \"%s\".\n%s", name, filter, local_err_msg);
160 g_free(local_err_msg);
163 g_free(colorf->filter_text);
164 dfilter_free(colorf->c_colorfilter);
165 colorf->filter_text = g_strdup(tmpfilter);
166 colorf->c_colorfilter = compiled_filter;
167 colorf->disabled = ((i!=filt_nr) ? TRUE : disabled);
168 /* Remember that there are now temporary coloring filters set */
170 tmp_colors_set = TRUE;
178 const color_filter_t *
179 color_filters_tmp_color(guint8 filter_num) {
181 color_filter_t *colorf = NULL;
184 name = g_strdup_printf("%s%02d", CONVERSATION_COLOR_PREFIX, filter_num);
185 cfl = g_slist_find_custom(color_filter_list, name, color_filters_find_by_name_cb);
187 colorf = (color_filter_t *)cfl->data;
194 /* Reset the temporary colorfilters */
196 color_filters_reset_tmp(gchar **err_msg)
200 for ( i=1 ; i<=10 ; i++ ) {
201 if (!color_filters_set_tmp(i, NULL, TRUE, err_msg))
204 /* Remember that there are now *no* temporary coloring filters set */
205 tmp_colors_set = FALSE;
209 /* delete the specified filter */
211 color_filter_delete(color_filter_t *colorf)
213 g_free(colorf->filter_name);
214 g_free(colorf->filter_text);
215 dfilter_free(colorf->c_colorfilter);
219 /* delete the specified filter (called from g_slist_foreach) */
221 color_filter_delete_cb(gpointer filter_arg)
223 color_filter_t *colorf = (color_filter_t *)filter_arg;
225 color_filter_delete(colorf);
228 /* delete the specified list */
230 color_filter_list_delete(GSList **cfl)
232 g_slist_free_full(*cfl, color_filter_delete_cb);
236 /* clone a single list entries from normal to edit list */
237 static color_filter_t *
238 color_filter_clone(color_filter_t *colorf)
240 color_filter_t *new_colorf;
242 new_colorf = (color_filter_t *)g_malloc(sizeof (color_filter_t));
243 new_colorf->filter_name = g_strdup(colorf->filter_name);
244 new_colorf->filter_text = g_strdup(colorf->filter_text);
245 new_colorf->bg_color = colorf->bg_color;
246 new_colorf->fg_color = colorf->fg_color;
247 new_colorf->disabled = colorf->disabled;
248 new_colorf->c_colorfilter = NULL;
254 color_filter_list_clone_cb(gpointer filter_arg, gpointer cfl_arg)
256 GSList **cfl = (GSList **)cfl_arg;
257 color_filter_t *new_colorf;
259 new_colorf = color_filter_clone((color_filter_t *)filter_arg);
260 *cfl = g_slist_append(*cfl, new_colorf);
263 /* clone the specified list */
265 color_filter_list_clone(GSList *cfl)
267 GSList *new_list = NULL;
269 g_slist_foreach(cfl, color_filter_list_clone_cb, &new_list);
275 color_filters_get(gchar** err_msg, color_filter_add_cb_func add_cb)
281 /* start the list with the temporary colorizing rules */
282 color_filters_add_tmp(&color_filter_list);
285 * Try to get the user's filters.
287 * Get the path for the file that would have their filters, and
290 path = get_persconffile_path(COLORFILTERS_FILE_NAME, TRUE);
291 if ((f = ws_fopen(path, "r")) == NULL) {
292 if (errno != ENOENT) {
293 /* Error trying to open the file; give up. */
294 *err_msg = g_strdup_printf("Could not open filter file\n\"%s\": %s.", path,
299 /* They don't have any filters; try to read the global filters */
301 return color_filters_read_globals(&color_filter_list, err_msg, add_cb);
305 * We've opened it; try to read it.
307 ret = read_filters_file(path, f, &color_filter_list, add_cb);
309 *err_msg = g_strdup_printf("Error reading filter file\n\"%s\": %s.",
310 path, g_strerror(errno));
322 /* Initialize the filter structures (reading from file) for general running, including app startup */
324 color_filters_init(gchar** err_msg, color_filter_add_cb_func add_cb)
326 /* delete all currently existing filters */
327 color_filter_list_delete(&color_filter_list);
329 /* now try to construct the filters list */
330 return color_filters_get(err_msg, add_cb);
334 color_filters_reload(gchar** err_msg, color_filter_add_cb_func add_cb)
336 /* "move" old entries to the deleted list
337 * we must keep them until the dissection no longer needs them */
338 color_filter_deleted_list = g_slist_concat(color_filter_deleted_list, color_filter_list);
339 color_filter_list = NULL;
341 /* now try to construct the filters list */
342 return color_filters_get(err_msg, add_cb);
346 color_filters_cleanup(void)
348 /* delete the previously deleted filters */
349 color_filter_list_delete(&color_filter_deleted_list);
352 typedef struct _color_clone
355 color_filter_add_cb_func add_cb;
359 color_filters_clone_cb(gpointer filter_arg, gpointer user_data)
361 color_clone_t* clone_data = (color_clone_t*)user_data;
362 color_filter_t * new_colorf = color_filter_clone((color_filter_t *)filter_arg);
364 clone_data->add_cb (new_colorf, clone_data->user_data);
368 color_filters_clone(gpointer user_data, color_filter_add_cb_func add_cb)
370 color_clone_t clone_data;
372 clone_data.user_data = user_data;
373 clone_data.add_cb = add_cb;
374 g_slist_foreach(color_filter_list, color_filters_clone_cb, &clone_data);
379 color_filter_compile_cb(gpointer filter_arg, gpointer err)
381 color_filter_t *colorf = (color_filter_t *)filter_arg;
382 gchar **err_msg = (gchar**)err;
383 gchar *local_err_msg = NULL;
385 g_assert(colorf->c_colorfilter == NULL);
387 /* If the filter is disabled it doesn't matter if it compiles or not. */
388 if (colorf->disabled) return;
390 if (!dfilter_compile(colorf->filter_text, &colorf->c_colorfilter, &local_err_msg)) {
391 *err_msg = g_strdup_printf("Could not compile color filter name: \"%s\" text: \"%s\".\n%s",
392 colorf->filter_name, colorf->filter_text, local_err_msg);
393 g_free(local_err_msg);
394 /* this filter was compilable before, so this should never happen */
395 /* except if the OK button of the parent window has been clicked */
396 /* so don't use g_assert_not_reached() but check the filters again */
401 color_filter_validate_cb(gpointer filter_arg, gpointer err)
403 color_filter_t *colorf = (color_filter_t *)filter_arg;
404 gchar **err_msg = (gchar**)err;
405 gchar *local_err_msg;
407 g_assert(colorf->c_colorfilter == NULL);
409 /* If the filter is disabled it doesn't matter if it compiles or not. */
410 if (colorf->disabled) return;
412 if (!dfilter_compile(colorf->filter_text, &colorf->c_colorfilter, &local_err_msg)) {
413 *err_msg = g_strdup_printf("Disabling color filter name: \"%s\" filter: \"%s\".\n%s",
414 colorf->filter_name, colorf->filter_text, local_err_msg);
415 g_free(local_err_msg);
417 /* Disable the color filter in the list of color filters. */
418 colorf->disabled = TRUE;
422 /* apply changes from the edit list */
424 color_filters_apply(GSList *tmp_cfl, GSList *edit_cfl, gchar** err_msg)
430 /* "move" old entries to the deleted list
431 * we must keep them until the dissection no longer needs them */
432 color_filter_deleted_list = g_slist_concat(color_filter_deleted_list, color_filter_list);
433 color_filter_list = NULL;
435 /* clone all list entries from tmp/edit to normal list */
436 color_filter_valid_list = NULL;
437 color_filter_valid_list = color_filter_list_clone(tmp_cfl);
438 color_filter_valid_list = g_slist_concat(color_filter_valid_list,
439 color_filter_list_clone(edit_cfl) );
441 /* compile all filter */
442 g_slist_foreach(color_filter_valid_list, color_filter_validate_cb, err_msg);
443 if (*err_msg != NULL) {
447 /* clone all list entries from tmp/edit to normal list */
448 color_filter_list = color_filter_list_clone(color_filter_valid_list);
450 /* compile all filter */
451 g_slist_foreach(color_filter_list, color_filter_compile_cb, err_msg);
452 if (*err_msg != NULL) {
460 color_filters_used(void)
462 return color_filter_list != NULL && filters_enabled;
466 tmp_color_filters_used(void)
468 return tmp_colors_set;
471 /* prepare the epan_dissect_t for the filter */
473 prime_edt(gpointer data, gpointer user_data)
475 color_filter_t *colorf = (color_filter_t *)data;
476 epan_dissect_t *edt = (epan_dissect_t *)user_data;
478 if (colorf->c_colorfilter != NULL)
479 epan_dissect_prime_with_dfilter(edt, colorf->c_colorfilter);
482 /* Prime the epan_dissect_t with all the compiler
483 * color filters in 'color_filter_list'. */
485 color_filters_prime_edt(epan_dissect_t *edt)
487 if (color_filters_used())
488 g_slist_foreach(color_filter_list, prime_edt, edt);
491 /* * Return the color_t for later use */
492 const color_filter_t *
493 color_filters_colorize_packet(epan_dissect_t *edt)
496 color_filter_t *colorf;
498 /* If we have color filters, "search" for the matching one. */
499 if ((edt->tree != NULL) && (color_filters_used())) {
500 curr = color_filter_list;
502 while(curr != NULL) {
503 colorf = (color_filter_t *)curr->data;
504 if ( (!colorf->disabled) &&
505 (colorf->c_colorfilter != NULL) &&
506 dfilter_apply_edt(colorf->c_colorfilter, edt)) {
509 curr = g_slist_next(curr);
516 /* read filters from the given file */
517 /* XXX - Would it make more sense to use GStrings here instead of reallocing
520 read_filters_file(const gchar *path, FILE *f, gpointer user_data, color_filter_add_cb_func add_cb)
522 #define INIT_BUF_SIZE 128
524 gchar *filter_exp = NULL;
525 guint32 name_len = INIT_BUF_SIZE;
526 guint32 filter_exp_len = INIT_BUF_SIZE;
529 guint16 fg_r, fg_g, fg_b, bg_r, bg_g, bg_b;
530 gboolean disabled = FALSE;
531 gboolean skip_end_of_line = FALSE;
534 name = (gchar *)g_malloc(name_len + 1);
535 filter_exp = (gchar *)g_malloc(filter_exp_len + 1);
537 prefs.unknown_colorfilters = FALSE;
541 if (skip_end_of_line) {
543 c = ws_getc_unlocked(f);
544 } while (c != EOF && c != '\n');
548 skip_end_of_line = FALSE;
551 while ((c = ws_getc_unlocked(f)) != EOF && g_ascii_isspace(c)) {
565 /* skip # comments and invalid lines */
567 skip_end_of_line = TRUE;
571 /* we get the @ delimiter.
573 * @name@filter expression@[background r,g,b][foreground r,g,b]
579 c = ws_getc_unlocked(f);
580 if (c == EOF || c == '@')
583 /* buffer isn't long enough; double its length.*/
585 name = (gchar *)g_realloc(name, name_len + 1);
594 skip_end_of_line = TRUE;
598 /* retrieve filter expression */
601 c = ws_getc_unlocked(f);
602 if (c == EOF || c == '@')
604 if (i >= filter_exp_len) {
605 /* buffer isn't long enough; double its length.*/
607 filter_exp = (gchar *)g_realloc(filter_exp, filter_exp_len + 1);
611 filter_exp[i] = '\0';
616 skip_end_of_line = TRUE;
620 /* retrieve background and foreground colors */
621 if (fscanf(f,"[%hu,%hu,%hu][%hu,%hu,%hu]",
622 &bg_r, &bg_g, &bg_b, &fg_r, &fg_g, &fg_b) == 6) {
624 /* we got a complete color filter */
626 color_t bg_color, fg_color;
627 color_filter_t *colorf;
628 dfilter_t *temp_dfilter = NULL;
629 gchar *local_err_msg = NULL;
631 if (!disabled && !dfilter_compile(filter_exp, &temp_dfilter, &local_err_msg)) {
632 g_warning("Could not compile \"%s\" in colorfilters file \"%s\".\n%s",
633 name, path, local_err_msg);
634 g_free(local_err_msg);
635 prefs.unknown_colorfilters = TRUE;
637 /* skip_end_of_line = TRUE; */
642 fg_color.green = fg_g;
643 fg_color.blue = fg_b;
646 bg_color.green = bg_g;
647 bg_color.blue = bg_b;
649 colorf = color_filter_new(name, filter_exp, &bg_color,
650 &fg_color, disabled);
651 if(user_data == &color_filter_list) {
652 GSList **cfl = (GSList **)user_data;
655 colorf->c_colorfilter = temp_dfilter;
656 *cfl = g_slist_append(*cfl, colorf);
659 /* just editing, don't need the compiled filter */
660 dfilter_free(temp_dfilter);
661 add_cb(colorf, user_data);
665 skip_end_of_line = TRUE;
676 /* read filters from the filter file */
678 color_filters_read_globals(gpointer user_data, gchar** err_msg, color_filter_add_cb_func add_cb)
685 * Try to get the global filters.
687 * Get the path for the file that would have the global filters, and
690 path = get_datafile_path(COLORFILTERS_FILE_NAME);
691 if ((f = ws_fopen(path, "r")) == NULL) {
692 if (errno != ENOENT) {
693 /* Error trying to open the file; give up. */
694 *err_msg = g_strdup_printf("Could not open global filter file\n\"%s\": %s.", path,
701 * There is no global filter file; treat that as equivalent to
702 * that file existing bug being empty, and say we succeeded.
708 ret = read_filters_file(path, f, user_data, add_cb);
710 *err_msg = g_strdup_printf("Error reading global filter file\n\"%s\": %s.",
711 path, g_strerror(errno));
722 /* read filters from some other filter file (import) */
724 color_filters_import(const gchar *path, gpointer user_data, gchar **err_msg, color_filter_add_cb_func add_cb)
729 if ((f = ws_fopen(path, "r")) == NULL) {
730 *err_msg = g_strdup_printf("Could not open filter file\n%s\nfor reading: %s.",
731 path, g_strerror(errno));
735 ret = read_filters_file(path, f, user_data, add_cb);
737 *err_msg = g_strdup_printf("Error reading filter file\n\"%s\": %s.",
738 path, g_strerror(errno));
747 struct write_filter_data
750 gboolean only_selected;
753 /* save a single filter */
755 write_filter(gpointer filter_arg, gpointer data_arg)
757 struct write_filter_data *data = (struct write_filter_data *)data_arg;
758 color_filter_t *colorf = (color_filter_t *)filter_arg;
761 if ( (!data->only_selected) &&
762 (strstr(colorf->filter_name,CONVERSATION_COLOR_PREFIX)==NULL) ) {
763 fprintf(f,"%s@%s@%s@[%u,%u,%u][%u,%u,%u]\n",
764 colorf->disabled ? "!" : "",
767 colorf->bg_color.red,
768 colorf->bg_color.green,
769 colorf->bg_color.blue,
770 colorf->fg_color.red,
771 colorf->fg_color.green,
772 colorf->fg_color.blue);
776 /* save filters in a filter file */
778 write_filters_file(GSList *cfl, FILE *f, gboolean only_selected)
780 struct write_filter_data data;
783 data.only_selected = only_selected;
785 fprintf(f,"# DO NOT EDIT THIS FILE! It was created by Wireshark\n");
786 g_slist_foreach(cfl, write_filter, &data);
790 /* save filters in users filter file */
792 color_filters_write(GSList *cfl, gchar** err_msg)
798 /* Create the directory that holds personal configuration files,
800 if (create_persconffile_dir(&pf_dir_path) == -1) {
801 *err_msg = g_strdup_printf("Can't create directory\n\"%s\"\nfor color files: %s.",
802 pf_dir_path, g_strerror(errno));
807 path = get_persconffile_path(COLORFILTERS_FILE_NAME, TRUE);
808 if ((f = ws_fopen(path, "w+")) == NULL) {
809 *err_msg = g_strdup_printf("Could not open\n%s\nfor writing: %s.",
810 path, g_strerror(errno));
815 write_filters_file(cfl, f, FALSE);
820 /* save filters in some other filter file (export) */
822 color_filters_export(const gchar *path, GSList *cfl, gboolean only_marked, gchar** err_msg)
826 if ((f = ws_fopen(path, "w+")) == NULL) {
827 *err_msg = g_strdup_printf("Could not open\n%s\nfor writing: %s.",
828 path, g_strerror(errno));
831 write_filters_file(cfl, f, only_marked);
837 * Editor modelines - http://www.wireshark.org/tools/modelines.html
842 * indent-tabs-mode: nil
845 * vi: set shiftwidth=4 tabstop=8 expandtab:
846 * :indentSize=4:tabSize=8:noTabs=true: