Bug Summary

File:builds/wireshark/wireshark/epan/color_filters.c
Warning:line 360, column 24
Use of memory after it is released

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name color_filters.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -fno-delete-null-pointer-checks -mframe-pointer=all -relaxed-aliasing -fmath-errno -ffp-contract=on -fno-rounding-math -ffloat16-excess-precision=fast -fbfloat16-excess-precision=fast -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/builds/wireshark/wireshark/build -fcoverage-compilation-dir=/builds/wireshark/wireshark/build -resource-dir /usr/lib/llvm-22/lib/clang/22 -isystem /usr/include/glib-2.0 -isystem /usr/lib/x86_64-linux-gnu/glib-2.0/include -isystem /builds/wireshark/wireshark/epan -isystem /builds/wireshark/wireshark/build/epan -isystem /usr/include/mit-krb5 -isystem /usr/include/lua5.5 -isystem /usr/include/libxml2 -D CARES_NO_DEPRECATED -D G_DISABLE_DEPRECATED -D G_DISABLE_SINGLE_INCLUDES -D REX_API=extern -D WS_BUILD_DLL -D WS_DEBUG -D WS_DEBUG_UTF_8 -D epan_EXPORTS -I /builds/wireshark/wireshark/build -I /builds/wireshark/wireshark -I /builds/wireshark/wireshark/include -I /builds/wireshark/wireshark/wiretap -D _GLIBCXX_ASSERTIONS -internal-isystem /usr/lib/llvm-22/lib/clang/22/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -fmacro-prefix-map=/builds/wireshark/wireshark/= -fmacro-prefix-map=/builds/wireshark/wireshark/build/= -fmacro-prefix-map=../= -Wno-format-nonliteral -std=gnu17 -ferror-limit 19 -fvisibility=hidden -fwrapv -fwrapv-pointer -fstrict-flex-arrays=3 -stack-protector 2 -fstack-clash-protection -fcf-protection=full -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fexceptions -fcolor-diagnostics -analyzer-output=html -faddrsig -fdwarf2-cfi-asm -o /builds/wireshark/wireshark/sbout/2026-09-30-100354-3662-1 -x c /builds/wireshark/wireshark/epan/color_filters.c
1/* color_filters.c
2 * Routines for color filters
3 *
4 * Wireshark - Network traffic analyzer
5 * By Gerald Combs <[email protected]>
6 * Copyright 1998 Gerald Combs
7 *
8 * SPDX-License-Identifier: GPL-2.0-or-later
9 */
10/*
11 * Updated 1 Dec 10 jjm
12 */
13
14#include "config.h"
15#define WS_LOG_DOMAIN"Epan" LOG_DOMAIN_EPAN"Epan"
16
17#include <glib.h>
18
19#include <errno(*__errno_location ()).h>
20#include <stdio.h>
21#include <stdlib.h>
22#include <string.h>
23
24#include <wsutil/filesystem.h>
25#include <wsutil/file_util.h>
26#include <wsutil/report_message.h>
27#include <wsutil/wslog.h>
28#include <wsutil/ws_assert.h>
29#include <wsutil/wmem/wmem_list.h>
30#include <wsutil/wsjson.h>
31#include <wsutil/json_dumper.h>
32
33#include <epan/packet.h>
34#include "color_filters.h"
35#include "file.h"
36#include <epan/dfilter/dfilter.h>
37#include <epan/prefs.h>
38#include <epan/epan_dissect.h>
39
40/*
41 * The coloring_rules.jsonc file is formatted as JSONC (jsonc.org). It
42 * must contain an array of named objects with the following key:value
43 * pairs:
44 * [
45 * { "Filter name": {
46 * "filter": "<display filter string>",
47 * "foreground": "#rrggbb or rrggbb",
48 * "background": "#rrggbb or rrggbb",
49 * "enabled": "<boolean value>",
50 * }
51 * }
52 * ]
53 *
54 * Each line in the legacy colorfilters file has the following format:
55 *
56 * @<filter name>@<filter string>@[<background>][<foreground>]
57 * Background and foreground colors are 16-bit comma-separated RGB
58 * triplets. Colors are 16 bits because that's what GdkColor used.
59 * We might want to use a more standard, copy+paste-able color scheme
60 * such as #RRGGBB instead.
61 */
62
63static int read_filters_file(const char *path, FILE *f, void *user_data, color_filter_add_cb_func add_cb);
64static int read_legacy_filters_file(const char *path, FILE *f, void *user_data, color_filter_add_cb_func add_cb);
65
66/* the currently active filters */
67static GSList *color_filter_list;
68
69/* keep "old" deleted filters in this list until
70 * the dissection no longer needs them (e.g. file is closed) */
71static GSList *color_filter_deleted_list;
72static GSList *color_filter_valid_list;
73
74/* Color Filters can en-/disabled. */
75static bool_Bool filters_enabled = true1;
76
77/* Session-level disabled (paused) filters */
78static GHashTable *session_disabled_filters;
79
80/* Remember if there are temporary coloring filters set to
81 * add sensitivity to the "Reset Coloring 1-10" menu item
82 */
83static bool_Bool tmp_colors_set;
84
85/* Create a new filter */
86color_filter_t *
87color_filter_new(const char *name, /* The name of the filter to create */
88 const char *filter_string, /* The string representing the filter */
89 color_t *bg_color, /* The background color */
90 color_t *fg_color, /* The foreground color */
91 bool_Bool enabled) /* Is the filter enabled? */
92{
93 color_filter_t *colorf;
94
95 colorf = g_new0(color_filter_t, 1)((color_filter_t *) g_malloc0_n ((1), sizeof (color_filter_t)
))
;
96 colorf->filter_name = g_strdup(name)g_strdup_inline (name);
97 colorf->filter_text = g_strdup(filter_string)g_strdup_inline (filter_string);
98 colorf->bg_color = *bg_color;
99 colorf->fg_color = *fg_color;
100 colorf->enabled = enabled;
101 return colorf;
102}
103
104/* Add ten empty (temporary) colorfilters for easy coloring */
105static void
106color_filters_add_tmp(GSList **cfl)
107{
108 char *name = NULL((void*)0);
109 uint32_t i;
110 char** bg_colors;
111 char** fg_colors;
112 unsigned long cval;
113 color_t bg_color, fg_color;
114 color_filter_t *colorf;
115
116 ws_assert(strlen(prefs.gui_colorized_fg)==69)do { if ((1) && !(strlen(prefs.gui_colorized_fg)==69)
) ws_log_fatal_full("Epan", LOG_LEVEL_ERROR, "epan/color_filters.c"
, 116, __func__, "assertion failed: %s", "strlen(prefs.gui_colorized_fg)==69"
); } while (0)
;
117 ws_assert(strlen(prefs.gui_colorized_bg)==69)do { if ((1) && !(strlen(prefs.gui_colorized_bg)==69)
) ws_log_fatal_full("Epan", LOG_LEVEL_ERROR, "epan/color_filters.c"
, 117, __func__, "assertion failed: %s", "strlen(prefs.gui_colorized_bg)==69"
); } while (0)
;
118 fg_colors = g_strsplit(prefs.gui_colorized_fg, ",", -1);
119 bg_colors = g_strsplit(prefs.gui_colorized_bg, ",", -1);
120
121 for ( i=1 ; i<=10 ; i++ ) {
122 name = ws_strdup_printf("%s%02d",CONVERSATION_COLOR_PREFIX,i)wmem_strdup_printf(((void*)0), "%s%02d","___conversation_color_filter___"
,i)
;
123
124 /* retrieve background and foreground colors */
125 cval = strtoul(fg_colors[i-1], NULL((void*)0), 16);
126 fg_color.red = RED_COMPONENT(cval)(uint16_t) (((((cval) >> 16) & 0xff) * 65535 / 255)
)
;
127 fg_color.green = GREEN_COMPONENT(cval)(uint16_t) (((((cval) >> 8) & 0xff) * 65535 / 255));
128 fg_color.blue = BLUE_COMPONENT(cval)(uint16_t) ( (((cval) & 0xff) * 65535 / 255));
129 cval = strtoul(bg_colors[i-1], NULL((void*)0), 16);
130 bg_color.red = RED_COMPONENT(cval)(uint16_t) (((((cval) >> 16) & 0xff) * 65535 / 255)
)
;
131 bg_color.green = GREEN_COMPONENT(cval)(uint16_t) (((((cval) >> 8) & 0xff) * 65535 / 255));
132 bg_color.blue = BLUE_COMPONENT(cval)(uint16_t) ( (((cval) & 0xff) * 65535 / 255));
133 colorf = color_filter_new(name, NULL((void*)0), &bg_color, &fg_color, false0);
134 colorf->filter_text = g_strdup("frame")g_strdup_inline ("frame");
135 *cfl = g_slist_append(*cfl, colorf);
136
137 g_free(name)(__builtin_object_size ((name), 0) != ((size_t) - 1)) ? g_free_sized
(name, __builtin_object_size ((name), 0)) : (g_free) (name)
;
138 }
139
140 g_strfreev(fg_colors);
141 g_strfreev(bg_colors);
142}
143
144static int
145color_filters_find_by_name_cb(const void *arg1, const void *arg2)
146{
147 const color_filter_t *colorf = (const color_filter_t *)arg1;
148 const char *name = (const char *)arg2;
149
150 return strcmp(colorf->filter_name, name);
151}
152
153/* Get the filter of a temporary color filter */
154char*
155color_filters_get_tmp(uint8_t filt_nr)
156{
157 char* name = NULL((void*)0);
158 char* filter = NULL((void*)0);
159 GSList* cfl;
160 color_filter_t* colorf;
161 /* Only perform a lookup if the supplied filter number is in the expected range */
162 if (filt_nr < 1 || filt_nr > 10)
163 return NULL((void*)0);
164
165 name = ws_strdup_printf("%s%02d", CONVERSATION_COLOR_PREFIX, filt_nr)wmem_strdup_printf(((void*)0), "%s%02d", "___conversation_color_filter___"
, filt_nr)
;
166 cfl = g_slist_find_custom(color_filter_list, name, color_filters_find_by_name_cb);
167 colorf = (color_filter_t*)cfl->data;
168
169 if (colorf->enabled)
170 filter = g_strdup(colorf->filter_text)g_strdup_inline (colorf->filter_text);
171
172 g_free(name)(__builtin_object_size ((name), 0) != ((size_t) - 1)) ? g_free_sized
(name, __builtin_object_size ((name), 0)) : (g_free) (name)
;
173
174 return filter;
175}
176
177/* Set the filter off a temporary colorfilters and enable it */
178bool_Bool
179color_filters_set_tmp(uint8_t filt_nr, const char *filter, bool_Bool enabled, char **err_msg)
180{
181 char *name = NULL((void*)0);
182 const char *tmpfilter = NULL((void*)0);
183 GSList *cfl;
184 color_filter_t *colorf;
185 dfilter_t *compiled_filter;
186 uint8_t i;
187 df_error_t *df_err = NULL((void*)0);
188 /* Go through the temporary filters and look for the same filter string.
189 * If found, clear it so that a filter can be "moved" up and down the list
190 */
191 for ( i=1 ; i<=10 ; i++ ) {
192 /* If we need to reset the temporary filter (filter==NULL), don't look
193 * for other rules with the same filter string
194 */
195 if( i!=filt_nr && filter==NULL((void*)0) )
196 continue;
197
198 name = ws_strdup_printf("%s%02d",CONVERSATION_COLOR_PREFIX,i)wmem_strdup_printf(((void*)0), "%s%02d","___conversation_color_filter___"
,i)
;
199 cfl = g_slist_find_custom(color_filter_list, name, color_filters_find_by_name_cb);
200 colorf = (color_filter_t *)cfl->data;
201
202 /* Only change the filter rule if this is the rule to change or if
203 * a matching filter string has been found
204 */
205 if(colorf && ( i == filt_nr || filter == NULL((void*)0) || !strcmp(filter, colorf->filter_text) ) ) {
206 /* set filter string to "frame" if we are resetting the rules
207 * or if we found a matching filter string which need to be cleared
208 */
209 tmpfilter = ( (filter==NULL((void*)0)) || (i!=filt_nr) ) ? "frame" : filter;
210 if (!dfilter_compile(tmpfilter, &compiled_filter, &df_err)dfilter_compile_full(tmpfilter, &compiled_filter, &df_err
, (1U << 1)|(1U << 2), __func__)
) {
211 *err_msg = ws_strdup_printf( "Could not compile color filter name: \"%s\" text: \"%s\".\n%s", name, filter, df_err->msg)wmem_strdup_printf(((void*)0), "Could not compile color filter name: \"%s\" text: \"%s\".\n%s"
, name, filter, df_err->msg)
;
212 df_error_free(&df_err);
213 g_free(name)(__builtin_object_size ((name), 0) != ((size_t) - 1)) ? g_free_sized
(name, __builtin_object_size ((name), 0)) : (g_free) (name)
;
214 return false0;
215 } else {
216 g_free(colorf->filter_text)(__builtin_object_size ((colorf->filter_text), 0) != ((size_t
) - 1)) ? g_free_sized (colorf->filter_text, __builtin_object_size
((colorf->filter_text), 0)) : (g_free) (colorf->filter_text
)
;
217 dfilter_free(colorf->c_colorfilter);
218 colorf->filter_text = g_strdup(tmpfilter)g_strdup_inline (tmpfilter);
219 colorf->c_colorfilter = compiled_filter;
220 colorf->enabled = ((i!=filt_nr) ? false0 : enabled);
221 /* Remember that there are now temporary coloring filters set */
222 if( filter )
223 tmp_colors_set = true1;
224 }
225 }
226 g_free(name)(__builtin_object_size ((name), 0) != ((size_t) - 1)) ? g_free_sized
(name, __builtin_object_size ((name), 0)) : (g_free) (name)
;
227 }
228 return true1;
229}
230
231const color_filter_t *
232color_filters_tmp_color(uint8_t filter_num) {
233 char *name;
234 color_filter_t *colorf = NULL((void*)0);
235 GSList *cfl;
236
237 name = ws_strdup_printf("%s%02d", CONVERSATION_COLOR_PREFIX, filter_num)wmem_strdup_printf(((void*)0), "%s%02d", "___conversation_color_filter___"
, filter_num)
;
238 cfl = g_slist_find_custom(color_filter_list, name, color_filters_find_by_name_cb);
239 if (cfl) {
240 colorf = (color_filter_t *)cfl->data;
241 }
242 g_free(name)(__builtin_object_size ((name), 0) != ((size_t) - 1)) ? g_free_sized
(name, __builtin_object_size ((name), 0)) : (g_free) (name)
;
243
244 return colorf;
245}
246
247/* Reset the temporary colorfilters */
248bool_Bool
249color_filters_reset_tmp(char **err_msg)
250{
251 uint8_t i;
252
253 for ( i=1 ; i<=10 ; i++ ) {
254 if (!color_filters_set_tmp(i, NULL((void*)0), false0, err_msg))
255 return false0;
256 }
257 /* Remember that there are now *no* temporary coloring filters set */
258 tmp_colors_set = false0;
259 return true1;
260}
261
262/* delete the specified filter */
263void
264color_filter_delete(color_filter_t *colorf)
265{
266 g_free(colorf->filter_name)(__builtin_object_size ((colorf->filter_name), 0) != ((size_t
) - 1)) ? g_free_sized (colorf->filter_name, __builtin_object_size
((colorf->filter_name), 0)) : (g_free) (colorf->filter_name
)
;
267 g_free(colorf->filter_text)(__builtin_object_size ((colorf->filter_text), 0) != ((size_t
) - 1)) ? g_free_sized (colorf->filter_text, __builtin_object_size
((colorf->filter_text), 0)) : (g_free) (colorf->filter_text
)
;
268 dfilter_free(colorf->c_colorfilter);
269 g_free(colorf)(__builtin_object_size ((colorf), 0) != ((size_t) - 1)) ? g_free_sized
(colorf, __builtin_object_size ((colorf), 0)) : (g_free) (colorf
)
;
270}
271
272/* delete the specified filter (called from g_slist_foreach) */
273static void
274color_filter_delete_cb(void *filter_arg)
275{
276 color_filter_t *colorf = (color_filter_t *)filter_arg;
277
278 color_filter_delete(colorf);
279}
280
281/* delete the specified list */
282void
283color_filter_list_delete(GSList **cfl)
284{
285 g_slist_free_full(*cfl, color_filter_delete_cb);
286 *cfl = NULL((void*)0);
287}
288
289/* clone a single list entries from normal to edit list */
290static color_filter_t *
291color_filter_clone(color_filter_t *colorf)
292{
293 color_filter_t *new_colorf;
294
295 new_colorf = g_new(color_filter_t, 1)((color_filter_t *) g_malloc_n ((1), sizeof (color_filter_t))
)
;
296 new_colorf->filter_name = g_strdup(colorf->filter_name)g_strdup_inline (colorf->filter_name);
297 new_colorf->filter_text = g_strdup(colorf->filter_text)g_strdup_inline (colorf->filter_text);
298 new_colorf->bg_color = colorf->bg_color;
299 new_colorf->fg_color = colorf->fg_color;
300 new_colorf->enabled = colorf->enabled;
301 new_colorf->c_colorfilter = NULL((void*)0);
302
303 return new_colorf;
304}
305
306static void
307color_filter_list_clone_cb(void *filter_arg, void *cfl_arg)
308{
309 GSList **cfl = (GSList **)cfl_arg;
310 color_filter_t *new_colorf;
311
312 new_colorf = color_filter_clone((color_filter_t *)filter_arg);
313 *cfl = g_slist_append(*cfl, new_colorf);
314}
315
316/* clone the specified list */
317static GSList *
318color_filter_list_clone(GSList *cfl)
319{
320 GSList *new_list = NULL((void*)0);
321
322 g_slist_foreach(cfl, color_filter_list_clone_cb, &new_list);
323
324 return new_list;
325}
326
327static bool_Bool
328color_filters_get(char** err_msg, color_filter_add_cb_func add_cb, const char* app_env_var_prefix)
329{
330 char *path;
331 FILE *f;
332 int ret;
333
334 /* start the list with the temporary colorizing rules */
335 color_filters_add_tmp(&color_filter_list);
336
337 /*
338 * Try to get the user's legacy filters first, for backward
339 * compatibility.
340 *
341 * Get the path for the file that would have their filters, and
342 * try to open it.
343 */
344 path = get_persconffile_path(COLORFILTERS_FILE_NAME"colorfilters", true1, app_env_var_prefix);
345 if ((f = ws_fopenfopen(path, "r")) == NULL((void*)0)) {
2
Assuming that the 1st argument to 'fopen' is not NULL
3
Taking false branch
346 if (errno(*__errno_location ()) != ENOENT2) {
347 /* Error trying to open the file; give up. */
348 *err_msg = ws_strdup_printf("Could not open filter file\n\"%s\": %s.", path,wmem_strdup_printf(((void*)0), "Could not open filter file\n\"%s\": %s."
, path, g_strerror((*__errno_location ())))
349 g_strerror(errno))wmem_strdup_printf(((void*)0), "Could not open filter file\n\"%s\": %s."
, path, g_strerror((*__errno_location ())))
;
350 g_free(path)(__builtin_object_size ((path), 0) != ((size_t) - 1)) ? g_free_sized
(path, __builtin_object_size ((path), 0)) : (g_free) (path)
;
351 return false0;
352 }
353 g_free(path)(__builtin_object_size ((path), 0) != ((size_t) - 1)) ? g_free_sized
(path, __builtin_object_size ((path), 0)) : (g_free) (path)
;
354 /* Try the personal coloring_rules.jsonc below. */
355 } else {
356 ret = read_legacy_filters_file(path, f, &color_filter_list, add_cb);
357 fclose(f);
358 g_free(path)(__builtin_object_size ((path), 0) != ((size_t) - 1)) ? g_free_sized
(path, __builtin_object_size ((path), 0)) : (g_free) (path)
;
4
Assuming the condition is false
5
'?' condition is false
6
Memory is released
359 if (ret != 0) {
7
Assuming 'ret' is not equal to 0
8
Taking true branch
360 *err_msg = ws_strdup_printf("Error reading filter file\n\"%s\": %s.",wmem_strdup_printf(((void*)0), "Error reading filter file\n\"%s\": %s."
, path, g_strerror((*__errno_location ())))
9
Use of memory after it is released
361 path, g_strerror(errno))wmem_strdup_printf(((void*)0), "Error reading filter file\n\"%s\": %s."
, path, g_strerror((*__errno_location ())))
;
362 return false0;
363 }
364 }
365
366 /*
367 * Now try to read the user's coloring rules, in the current JSONC
368 * format. Legacy rules with the same name will be replaced.
369 */
370 path = get_persconffile_path(COLORING_RULES_FILE_NAME"coloring_rules.jsonc", true1, app_env_var_prefix);
371 if ((f = ws_fopenfopen(path, "r")) == NULL((void*)0)) {
372 if (errno(*__errno_location ()) != ENOENT2) {
373 *err_msg = ws_strdup_printf("Could not open coloring rules file\n\"%s\": %s.", path,wmem_strdup_printf(((void*)0), "Could not open coloring rules file\n\"%s\": %s."
, path, g_strerror((*__errno_location ())))
374 g_strerror(errno))wmem_strdup_printf(((void*)0), "Could not open coloring rules file\n\"%s\": %s."
, path, g_strerror((*__errno_location ())))
;
375 g_free(path)(__builtin_object_size ((path), 0) != ((size_t) - 1)) ? g_free_sized
(path, __builtin_object_size ((path), 0)) : (g_free) (path)
;
376 return false0;
377 }
378 /* They don't have any filters; try to read the global filters */
379 g_free(path)(__builtin_object_size ((path), 0) != ((size_t) - 1)) ? g_free_sized
(path, __builtin_object_size ((path), 0)) : (g_free) (path)
;
380 return color_filters_read_globals(&color_filter_list, err_msg, add_cb, app_env_var_prefix);
381 }
382
383 ret = read_filters_file(path, f, &color_filter_list, add_cb);
384 fclose(f);
385 if (ret != 0) {
386 *err_msg = ws_strdup_printf("Error reading coloring rules file\n\"%s\": %s.",wmem_strdup_printf(((void*)0), "Error reading coloring rules file\n\"%s\": %s."
, path, g_strerror((*__errno_location ())))
387 path, g_strerror(errno))wmem_strdup_printf(((void*)0), "Error reading coloring rules file\n\"%s\": %s."
, path, g_strerror((*__errno_location ())))
;
388 g_free(path)(__builtin_object_size ((path), 0) != ((size_t) - 1)) ? g_free_sized
(path, __builtin_object_size ((path), 0)) : (g_free) (path)
;
389 return false0;
390 }
391
392 g_free(path)(__builtin_object_size ((path), 0) != ((size_t) - 1)) ? g_free_sized
(path, __builtin_object_size ((path), 0)) : (g_free) (path)
;
393 return true1;
394}
395
396/* Initialize the filter structures (reading from file) for general running, including app startup */
397bool_Bool
398color_filters_init(char** err_msg, color_filter_add_cb_func add_cb, const char* app_env_var_prefix)
399{
400 /* delete all currently existing filters */
401 color_filter_list_delete(&color_filter_list);
402
403 /* now try to construct the filters list */
404 bool_Bool result = color_filters_get(err_msg, add_cb, app_env_var_prefix);
405
406 /* Load paused filters from profile after loading color filters */
407 if (result) {
408 color_filter_read_paused(app_env_var_prefix);
409 }
410
411 return result;
412}
413
414bool_Bool
415color_filters_reload(char** err_msg, color_filter_add_cb_func add_cb, const char* app_env_var_prefix)
416{
417 /* "move" old entries to the deleted list
418 * we must keep them until the dissection no longer needs them */
419 color_filter_deleted_list = g_slist_concat(color_filter_deleted_list, color_filter_list);
420 color_filter_list = NULL((void*)0);
421
422 /* now try to construct the filters list */
423 return color_filters_get(err_msg, add_cb, app_env_var_prefix);
1
Calling 'color_filters_get'
424}
425
426void
427color_filters_cleanup(void)
428{
429 /* delete the previously deleted filters */
430 color_filter_list_delete(&color_filter_deleted_list);
431
432 if (session_disabled_filters) {
433 g_hash_table_destroy(session_disabled_filters);
434 session_disabled_filters = NULL((void*)0);
435 }
436}
437
438typedef struct _color_clone
439{
440 void *user_data;
441 color_filter_add_cb_func add_cb;
442} color_clone_t;
443
444static void
445color_filters_clone_cb(void *filter_arg, void *user_data)
446{
447 color_clone_t* clone_data = (color_clone_t*)user_data;
448 color_filter_t * new_colorf = color_filter_clone((color_filter_t *)filter_arg);
449
450 clone_data->add_cb (new_colorf, clone_data->user_data);
451}
452
453void
454color_filters_clone(void *user_data, color_filter_add_cb_func add_cb)
455{
456 color_clone_t clone_data;
457
458 clone_data.user_data = user_data;
459 clone_data.add_cb = add_cb;
460 g_slist_foreach(color_filter_list, color_filters_clone_cb, &clone_data);
461}
462
463
464static void
465color_filter_compile_cb(void *filter_arg, void *err)
466{
467 color_filter_t *colorf = (color_filter_t *)filter_arg;
468 char **err_msg = (char**)err;
469 df_error_t *df_err = NULL((void*)0);
470
471 ws_assert(colorf->c_colorfilter == NULL)do { if ((1) && !(colorf->c_colorfilter == ((void*
)0))) ws_log_fatal_full("Epan", LOG_LEVEL_ERROR, "epan/color_filters.c"
, 471, __func__, "assertion failed: %s", "colorf->c_colorfilter == ((void*)0)"
); } while (0)
;
472
473 /* If the filter is disabled it doesn't matter if it compiles or not. */
474 if (!colorf->enabled) return;
475
476 if (!dfilter_compile(colorf->filter_text, &colorf->c_colorfilter, &df_err)dfilter_compile_full(colorf->filter_text, &colorf->
c_colorfilter, &df_err, (1U << 1)|(1U << 2), __func__
)
) {
477 *err_msg = ws_strdup_printf("Could not compile color filter name: \"%s\" text: \"%s\".\n%s",wmem_strdup_printf(((void*)0), "Could not compile color filter name: \"%s\" text: \"%s\".\n%s"
, colorf->filter_name, colorf->filter_text, df_err->
msg)
478 colorf->filter_name, colorf->filter_text, df_err->msg)wmem_strdup_printf(((void*)0), "Could not compile color filter name: \"%s\" text: \"%s\".\n%s"
, colorf->filter_name, colorf->filter_text, df_err->
msg)
;
479 df_error_free(&df_err);
480 /* this filter was compilable before, so this should never happen */
481 /* except if the OK button of the parent window has been clicked */
482 /* so don't use ws_assert_not_reached() but check the filters again */
483 }
484}
485
486static void
487color_filter_validate_cb(void *filter_arg, void *err)
488{
489 color_filter_t *colorf = (color_filter_t *)filter_arg;
490 char **err_msg = (char**)err;
491 df_error_t *df_err = NULL((void*)0);
492
493 ws_assert(colorf->c_colorfilter == NULL)do { if ((1) && !(colorf->c_colorfilter == ((void*
)0))) ws_log_fatal_full("Epan", LOG_LEVEL_ERROR, "epan/color_filters.c"
, 493, __func__, "assertion failed: %s", "colorf->c_colorfilter == ((void*)0)"
); } while (0)
;
494
495 /* If the filter is disabled it doesn't matter if it compiles or not. */
496 if (!colorf->enabled) return;
497
498 if (!dfilter_compile(colorf->filter_text, &colorf->c_colorfilter, &df_err)dfilter_compile_full(colorf->filter_text, &colorf->
c_colorfilter, &df_err, (1U << 1)|(1U << 2), __func__
)
) {
499 *err_msg = ws_strdup_printf("Disabling color filter name: \"%s\" filter: \"%s\".\n%s",wmem_strdup_printf(((void*)0), "Disabling color filter name: \"%s\" filter: \"%s\".\n%s"
, colorf->filter_name, colorf->filter_text, df_err->
msg)
500 colorf->filter_name, colorf->filter_text, df_err->msg)wmem_strdup_printf(((void*)0), "Disabling color filter name: \"%s\" filter: \"%s\".\n%s"
, colorf->filter_name, colorf->filter_text, df_err->
msg)
;
501 df_error_free(&df_err);
502
503 /* Disable the color filter in the list of color filters. */
504 colorf->enabled = false0;
505 }
506
507 /* XXX: What if the color filter tests "frame.coloring_rule.name" or
508 * "frame.coloring_rule.string"?
509 */
510}
511
512/* apply changes from the edit list */
513bool_Bool
514color_filters_apply(GSList *tmp_cfl, GSList *edit_cfl, char** err_msg)
515{
516 bool_Bool ret = true1;
517
518 *err_msg = NULL((void*)0);
519
520 /* "move" old entries to the deleted list
521 * we must keep them until the dissection no longer needs them */
522 color_filter_deleted_list = g_slist_concat(color_filter_deleted_list, color_filter_list);
523 color_filter_list = NULL((void*)0);
524
525 /* clone all list entries from tmp/edit to normal list */
526 color_filter_list_delete(&color_filter_valid_list);
527 color_filter_valid_list = color_filter_list_clone(tmp_cfl);
528 color_filter_valid_list = g_slist_concat(color_filter_valid_list,
529 color_filter_list_clone(edit_cfl) );
530
531 /* compile all filter */
532 g_slist_foreach(color_filter_valid_list, color_filter_validate_cb, err_msg);
533 if (*err_msg != NULL((void*)0)) {
534 ret = false0;
535 }
536
537 /* clone all list entries from tmp/edit to normal list */
538 color_filter_list = color_filter_list_clone(color_filter_valid_list);
539
540 /* compile all filter */
541 g_slist_foreach(color_filter_list, color_filter_compile_cb, err_msg);
542 if (*err_msg != NULL((void*)0)) {
543 ret = false0;
544 }
545
546 return ret;
547}
548
549bool_Bool
550color_filters_used(void)
551{
552 return color_filter_list != NULL((void*)0) && filters_enabled;
553}
554
555bool_Bool
556tmp_color_filters_used(void)
557{
558 return tmp_colors_set;
559}
560
561/* prepare the epan_dissect_t for the filter */
562static void
563prime_edt(void *data, void *user_data)
564{
565 color_filter_t *colorf = (color_filter_t *)data;
566 epan_dissect_t *edt = (epan_dissect_t *)user_data;
567
568 if (colorf->c_colorfilter != NULL((void*)0))
569 epan_dissect_prime_with_dfilter(edt, colorf->c_colorfilter);
570}
571
572/* Prime the epan_dissect_t with all the compiler
573 * color filters in 'color_filter_list'. */
574void
575color_filters_prime_edt(epan_dissect_t *edt)
576{
577 if (color_filters_used())
578 g_slist_foreach(color_filter_list, prime_edt, edt);
579}
580
581static int
582find_hfid(const void *data, const void *user_data)
583{
584 color_filter_t *colorf = (color_filter_t *)data;
585 int hfid = GPOINTER_TO_INT(user_data)((gint) (glong) (user_data));
586
587 if ((colorf->enabled) && colorf->c_colorfilter != NULL((void*)0)) {
588 if (dfilter_interested_in_field(colorf->c_colorfilter, hfid)) {
589 return 0;
590 }
591 }
592 return -1;
593}
594
595bool_Bool
596color_filters_use_hfid(int hfid)
597{
598 GSList *item = NULL((void*)0);
599 if (color_filters_used())
600 item = g_slist_find_custom(color_filter_list, GINT_TO_POINTER(hfid)((gpointer) (glong) (hfid)), find_hfid);
601 return (item != NULL((void*)0));
602}
603
604static int
605find_proto(const void *data, const void *user_data)
606{
607 color_filter_t *colorf = (color_filter_t *)data;
608 int proto_id = GPOINTER_TO_INT(user_data)((gint) (glong) (user_data));
609
610 if ((colorf->enabled) && colorf->c_colorfilter != NULL((void*)0)) {
611 if (dfilter_interested_in_proto(colorf->c_colorfilter, proto_id)) {
612 return 0;
613 }
614 }
615 return -1;
616}
617
618bool_Bool
619color_filters_use_proto(int proto_id)
620{
621 GSList *item = NULL((void*)0);
622 if (color_filters_used())
623 item = g_slist_find_custom(color_filter_list, GINT_TO_POINTER(proto_id)((gpointer) (glong) (proto_id)), find_proto);
624 return (item != NULL((void*)0));
625}
626
627/* * Return the color_t for later use */
628const color_filter_t *
629color_filters_colorize_packet(epan_dissect_t *edt)
630{
631 GSList *curr;
632 color_filter_t *colorf;
633
634 /* If we have color filters, "search" for the matching one. */
635 if ((edt->tree != NULL((void*)0)) && (color_filters_used())) {
636 curr = color_filter_list;
637
638 while(curr != NULL((void*)0)) {
639 colorf = (color_filter_t *)curr->data;
640 if ( (colorf->enabled) &&
641 (colorf->c_colorfilter != NULL((void*)0)) &&
642 dfilter_apply_edt(colorf->c_colorfilter, edt) &&
643 !color_filter_is_session_disabled(colorf->filter_name)) {
644 return colorf;
645 }
646 curr = g_slist_next(curr)((curr) ? (((GSList *)(curr))->next) : ((void*)0));
647 }
648 }
649
650 return NULL((void*)0);
651}
652
653const color_filter_t *
654color_filters_colorize_packet_all(epan_dissect_t *edt,
655 wmem_allocator_t *scope, wmem_list_t **matches)
656{
657 const color_filter_t *first_match = NULL((void*)0);
658
659 if (matches) {
660 *matches = NULL((void*)0);
661 }
662
663 /* If we have color filters, collect ALL matching ones. */
664 if ((edt->tree != NULL((void*)0)) && (color_filters_used())) {
665 for (GSList *curr = color_filter_list; curr != NULL((void*)0); curr = g_slist_next(curr)((curr) ? (((GSList *)(curr))->next) : ((void*)0))) {
666 color_filter_t *colorf = (color_filter_t *)curr->data;
667 if ((colorf->enabled) &&
668 (colorf->c_colorfilter != NULL((void*)0)) &&
669 dfilter_apply_edt(colorf->c_colorfilter, edt)) {
670
671 bool_Bool is_session_disabled = color_filter_is_session_disabled(colorf->filter_name);
672
673 /* Add to matches list even if paused (for Frame tree display) */
674 if (matches) {
675 if (*matches == NULL((void*)0)) {
676 *matches = wmem_list_new(scope);
677 }
678 wmem_list_append(*matches, colorf);
679 }
680
681 /* Only use non-paused filters for first_match */
682 if (!first_match && !is_session_disabled) {
683 first_match = colorf; /* Backward compatibility */
684 }
685 }
686 }
687 }
688
689 return first_match;
690}
691
692void
693color_filter_set_session_disabled(const char *filter_name, bool_Bool disabled)
694{
695 if (!filter_name) return;
696
697 if (!session_disabled_filters) {
698 session_disabled_filters = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL((void*)0));
699 }
700
701 if (disabled) {
702 g_hash_table_insert(session_disabled_filters, g_strdup(filter_name)g_strdup_inline (filter_name), GINT_TO_POINTER(1)((gpointer) (glong) (1)));
703 } else {
704 g_hash_table_remove(session_disabled_filters, filter_name);
705 }
706
707 /* Auto-save to profile directory after every change */
708 color_filter_write_paused(NULL((void*)0)); /* NULL uses default env prefix */
709}
710
711bool_Bool
712color_filter_is_session_disabled(const char *filter_name)
713{
714 if (!filter_name || !session_disabled_filters) {
715 return false0;
716 }
717 return g_hash_table_contains(session_disabled_filters, filter_name);
718}
719
720void
721color_filter_clear_session_disabled(void)
722{
723 if (session_disabled_filters) {
724 g_hash_table_remove_all(session_disabled_filters);
725 }
726 /* Restore from profile directory after clearing (workaround for rescan) */
727 color_filter_read_paused(NULL((void*)0));
728}
729
730#define PAUSED_FILTERS_FILE"paused_filters" "paused_filters"
731
732/* Get profile-specific paused filters file path */
733static char *
734get_paused_filters_path(const char *app_env_var_prefix)
735{
736 /* Use profile-specific path (true = use profile directory) */
737 return get_persconffile_path(PAUSED_FILTERS_FILE"paused_filters", true1, app_env_var_prefix);
738}
739
740/* Save paused filters to profile directory */
741void
742color_filter_write_paused(const char *app_env_var_prefix)
743{
744 if (!session_disabled_filters) return;
745
746 char *path = get_paused_filters_path(app_env_var_prefix);
747 if (!path) return;
748
749 FILE *f = ws_fopenfopen(path, "w");
750 if (!f) {
751 g_free(path)(__builtin_object_size ((path), 0) != ((size_t) - 1)) ? g_free_sized
(path, __builtin_object_size ((path), 0)) : (g_free) (path)
;
752 return;
753 }
754
755 /* Write each paused filter name, one per line */
756 GHashTableIter iter;
757 gpointer key, value;
758 g_hash_table_iter_init(&iter, session_disabled_filters);
759 while (g_hash_table_iter_next(&iter, &key, &value)) {
760 fprintf(f, "%s\n", (const char*)key);
761 }
762
763 fclose(f);
764 g_free(path)(__builtin_object_size ((path), 0) != ((size_t) - 1)) ? g_free_sized
(path, __builtin_object_size ((path), 0)) : (g_free) (path)
;
765}
766
767/* Read paused filters from profile directory */
768void
769color_filter_read_paused(const char *app_env_var_prefix)
770{
771 char *path = get_paused_filters_path(app_env_var_prefix);
772 if (!path) return;
773
774 FILE *f = ws_fopenfopen(path, "r");
775 g_free(path)(__builtin_object_size ((path), 0) != ((size_t) - 1)) ? g_free_sized
(path, __builtin_object_size ((path), 0)) : (g_free) (path)
;
776
777 if (!f) return; /* File doesn't exist yet - that's OK */
778
779 char line[1024];
780 while (fgets(line, sizeof(line), f)) {
781 /* Remove trailing newline/whitespace */
782 line[strcspn(line, "\r\n")] = 0;
783 g_strstrip(line)g_strchomp (g_strchug (line)); /* GLib function to trim whitespace */
784
785 if (line[0] && line[0] != '#') { /* Skip empty lines and comments */
786 /* Add to session_disabled_filters without triggering auto-save */
787 if (!session_disabled_filters) {
788 session_disabled_filters = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL((void*)0));
789 }
790 g_hash_table_insert(session_disabled_filters, g_strdup(line)g_strdup_inline (line), GINT_TO_POINTER(1)((gpointer) (glong) (1)));
791 }
792 }
793
794 fclose(f);
795}
796
797/* Resume all paused filters (clears all and saves empty file) */
798void
799color_filter_resume_all(const char *app_env_var_prefix)
800{
801 /* Clear the hash table */
802 if (session_disabled_filters) {
803 g_hash_table_remove_all(session_disabled_filters);
804 }
805
806 /* Write empty file to profile directory */
807 char *path = get_paused_filters_path(app_env_var_prefix);
808 if (!path) return;
809
810 FILE *f = ws_fopenfopen(path, "w");
811 if (f) {
812 /* Write empty file (or just close it) */
813 fclose(f);
814 }
815 g_free(path)(__builtin_object_size ((path), 0) != ((size_t) - 1)) ? g_free_sized
(path, __builtin_object_size ((path), 0)) : (g_free) (path)
;
816}
817
818/* Parse a "#rrggbb" or "rrggbb" string into a color_t. Returns false (and
819 * leaves *color untouched) if str is NULL or not a valid hex triplet. */
820static bool_Bool
821hex_str_to_color(const char *str, color_t *color)
822{
823 unsigned long cval;
824
825 if (str == NULL((void*)0)) {
826 return false0;
827 }
828 if (str[0] == '#') {
829 str++;
830 }
831 if (strlen(str) != 6 || strspn(str, "0123456789abcdefABCDEF") != 6) {
832 return false0;
833 }
834
835 cval = strtoul(str, NULL((void*)0), 16);
836 color->red = RED_COMPONENT(cval)(uint16_t) (((((cval) >> 16) & 0xff) * 65535 / 255)
)
;
837 color->green = GREEN_COMPONENT(cval)(uint16_t) (((((cval) >> 8) & 0xff) * 65535 / 255));
838 color->blue = BLUE_COMPONENT(cval)(uint16_t) ( (((cval) & 0xff) * 65535 / 255));
839 return true1;
840}
841
842static int
843read_filters_file(const char *path, FILE *f, void *user_data, color_filter_add_cb_func add_cb)
844{
845 long size;
846 char *buf;
847 size_t nread;
848 int num_tokens;
849 jsmntok_t *tokens;
850 int array_len;
851 jsmntok_t *element;
852
853 if (fseek(f, 0, SEEK_END2) == -1) {
854 return errno(*__errno_location ());
855 }
856 size = ftell(f);
857 if (size < 0) {
858 return errno(*__errno_location ());
859 }
860 if (fseek(f, 0, SEEK_SET0) == -1) {
861 return errno(*__errno_location ());
862 }
863 if (size == 0) {
864 return 0;
865 }
866
867 buf = (char *)g_malloc(size + 1);
868 nread = fread(buf, 1, size, f);
869 if (ferror(f)) {
870 int ret = errno(*__errno_location ());
871 g_free(buf)(__builtin_object_size ((buf), 0) != ((size_t) - 1)) ? g_free_sized
(buf, __builtin_object_size ((buf), 0)) : (g_free) (buf)
;
872 return ret;
873 }
874 buf[nread] = '\0';
875
876 if (!json_strip_jsonc_comments(buf)) {
877 report_warning("Error parsing coloring rules file\n\"%s\": unterminated block comment.", path);
878 g_free(buf)(__builtin_object_size ((buf), 0) != ((size_t) - 1)) ? g_free_sized
(buf, __builtin_object_size ((buf), 0)) : (g_free) (buf)
;
879 return 0;
880 }
881
882 num_tokens = json_parse(buf, NULL((void*)0), 0);
883 if (num_tokens <= 0) {
884 /* All comments/whitespace) or invalid. Just treat it as empty. */
885 g_free(buf)(__builtin_object_size ((buf), 0) != ((size_t) - 1)) ? g_free_sized
(buf, __builtin_object_size ((buf), 0)) : (g_free) (buf)
;
886 return 0;
887 }
888
889 tokens = (jsmntok_t *)g_malloc(num_tokens * sizeof(jsmntok_t));
890 if (json_parse(buf, tokens, num_tokens) <= 0 || tokens[0].type != JSMN_ARRAY) {
891 report_warning("Error parsing coloring rules file\n\"%s\": invalid JSON.", path);
892 g_free(tokens)(__builtin_object_size ((tokens), 0) != ((size_t) - 1)) ? g_free_sized
(tokens, __builtin_object_size ((tokens), 0)) : (g_free) (tokens
)
;
893 g_free(buf)(__builtin_object_size ((buf), 0) != ((size_t) - 1)) ? g_free_sized
(buf, __builtin_object_size ((buf), 0)) : (g_free) (buf)
;
894 return 0;
895 }
896
897 array_len = json_get_array_len(&tokens[0]);
898 element = json_get_array_index(&tokens[0], 0);
899
900 for (int i = 0; i < array_len; i++, element = json_get_next_object(element)) {
901 char *name, *filter_str, *fg_str, *bg_str;
902 bool_Bool enabled = true1;
903 color_t fg_color = { 0, 0, 0 }, bg_color = { 0, 0, 0 };
904 dfilter_t *temp_dfilter = NULL((void*)0);
905 df_error_t *df_err = NULL((void*)0);
906 const char *filter_exp;
907 color_filter_t *colorf;
908 jsmntok_t *name_tok, *properties_tok;
909
910 if (element->type != JSMN_OBJECT || element->size != 1) {
911 continue;
912 }
913
914 /* "<filter name>": { ... } */
915 name_tok = element + 1;
916 properties_tok = name_tok + 1;
917 if (name_tok->type != JSMN_STRING || properties_tok->type != JSMN_OBJECT) {
918 continue;
919 }
920
921 name = g_strndup(&buf[name_tok->start], (gsize)(name_tok->end - name_tok->start));
922 if (!json_decode_string_inplace(name)) {
923 g_free(name)(__builtin_object_size ((name), 0) != ((size_t) - 1)) ? g_free_sized
(name, __builtin_object_size ((name), 0)) : (g_free) (name)
;
924 continue;
925 }
926
927 filter_str = json_get_string(buf, properties_tok, "filter");
928 fg_str = json_get_string(buf, properties_tok, "foreground");
929 bg_str = json_get_string(buf, properties_tok, "background");
930 json_get_boolean(buf, properties_tok, "enabled", &enabled);
931
932 hex_str_to_color(fg_str, &fg_color);
933 hex_str_to_color(bg_str, &bg_color);
934
935 filter_exp = filter_str ? filter_str : "";
936
937 if (enabled && !dfilter_compile(filter_exp, &temp_dfilter, &df_err)dfilter_compile_full(filter_exp, &temp_dfilter, &df_err
, (1U << 1)|(1U << 2), __func__)
) {
938 report_warning("Disabling color filter: Could not compile \"%s\" in coloring rules file \"%s\".\n%s",
939 name, path, df_err->msg);
940 df_error_free(&df_err);
941 enabled = false0;
942 }
943
944 colorf = color_filter_new(name, filter_exp, &bg_color, &fg_color, enabled);
945 g_free(name)(__builtin_object_size ((name), 0) != ((size_t) - 1)) ? g_free_sized
(name, __builtin_object_size ((name), 0)) : (g_free) (name)
;
946
947 if (user_data == &color_filter_list) {
948 GSList **cfl = (GSList **)user_data;
949 GSList *existing;
950
951 /* internal call */
952 colorf->c_colorfilter = temp_dfilter;
953
954 /* A JSONC rule with the same name as an already-loaded
955 * (legacy) rule replaces it, rather than duplicating it. */
956 existing = g_slist_find_custom(*cfl, colorf->filter_name, color_filters_find_by_name_cb);
957 if (existing != NULL((void*)0)) {
958 color_filter_delete((color_filter_t *)existing->data);
959 existing->data = colorf;
960 } else {
961 *cfl = g_slist_append(*cfl, colorf);
962 }
963 } else {
964 /* external call */
965 /* just editing, don't need the compiled filter */
966 dfilter_free(temp_dfilter);
967 add_cb(colorf, user_data);
968 }
969 }
970
971 g_free(tokens)(__builtin_object_size ((tokens), 0) != ((size_t) - 1)) ? g_free_sized
(tokens, __builtin_object_size ((tokens), 0)) : (g_free) (tokens
)
;
972 g_free(buf)(__builtin_object_size ((buf), 0) != ((size_t) - 1)) ? g_free_sized
(buf, __builtin_object_size ((buf), 0)) : (g_free) (buf)
;
973 return 0;
974}
975
976/* Read legacy (@-delimited) filters from the given file */
977/* XXX At what point do we drop support? */
978/* XXX Would it make more sense to use GStrings here instead of reallocing
979 our buffers? */
980static int
981read_legacy_filters_file(const char *path, FILE *f, void *user_data, color_filter_add_cb_func add_cb)
982{
983#define INIT_BUF_SIZE128 128
984 char *name;
985 char *filter_exp;
986 uint32_t name_len = INIT_BUF_SIZE128;
987 uint32_t filter_exp_len = INIT_BUF_SIZE128;
988 uint32_t i = 0;
989 int c;
990 uint16_t fg_r, fg_g, fg_b, bg_r, bg_g, bg_b;
991 bool_Bool disabled = false0;
992 bool_Bool skip_end_of_line = false0;
993 int ret = 0;
994
995 name = (char *)g_malloc(name_len + 1);
996 filter_exp = (char *)g_malloc(filter_exp_len + 1);
997
998 while (1) {
999
1000 if (skip_end_of_line) {
1001 do {
1002 c = ws_getc_unlockedgetc_unlocked(f);
1003 } while (c != EOF(-1) && c != '\n');
1004 if (c == EOF(-1))
1005 break;
1006 disabled = false0;
1007 skip_end_of_line = false0;
1008 }
1009
1010 while ((c = ws_getc_unlockedgetc_unlocked(f)) != EOF(-1) && g_ascii_isspace(c)((g_ascii_table[(guchar) (c)] & G_ASCII_SPACE) != 0)) {
1011 if (c == '\n') {
1012 continue;
1013 }
1014 }
1015
1016 if (c == EOF(-1))
1017 break;
1018
1019 if (c == '!') {
1020 disabled = true1;
1021 continue;
1022 }
1023
1024 /* skip # comments and invalid lines */
1025 if (c != '@') {
1026 skip_end_of_line = true1;
1027 continue;
1028 }
1029
1030 /* we get the @ delimiter.
1031 * Format is:
1032 * @name@filter expression@[background r,g,b][foreground r,g,b]
1033 */
1034
1035 /* retrieve name */
1036 i = 0;
1037 while (1) {
1038 c = ws_getc_unlockedgetc_unlocked(f);
1039 if (c == EOF(-1) || c == '@')
1040 break;
1041 if (i >= name_len) {
1042 /* buffer isn't long enough; double its length.*/
1043 name_len *= 2;
1044 name = (char *)g_realloc(name, name_len + 1);
1045 }
1046 name[i++] = c;
1047 }
1048 name[i] = '\0';
1049
1050 if (c == EOF(-1)) {
1051 break;
1052 } else if (i == 0) {
1053 skip_end_of_line = true1;
1054 continue;
1055 }
1056
1057 /* retrieve filter expression */
1058 i = 0;
1059 while (1) {
1060 c = ws_getc_unlockedgetc_unlocked(f);
1061 if (c == EOF(-1) || c == '@')
1062 break;
1063 if (i >= filter_exp_len) {
1064 /* buffer isn't long enough; double its length.*/
1065 filter_exp_len *= 2;
1066 filter_exp = (char *)g_realloc(filter_exp, filter_exp_len + 1);
1067 }
1068 filter_exp[i++] = c;
1069 }
1070 filter_exp[i] = '\0';
1071
1072 if (c == EOF(-1)) {
1073 break;
1074 } else if (i == 0) {
1075 skip_end_of_line = true1;
1076 continue;
1077 }
1078
1079 /* retrieve background and foreground colors */
1080 if (fscanf(f,"[%hu,%hu,%hu][%hu,%hu,%hu]",
1081 &bg_r, &bg_g, &bg_b, &fg_r, &fg_g, &fg_b) == 6) {
1082
1083 /* we got a complete color filter */
1084
1085 color_t bg_color, fg_color;
1086 color_filter_t *colorf;
1087 dfilter_t *temp_dfilter = NULL((void*)0);
1088 df_error_t *df_err = NULL((void*)0);
1089
1090 if (!disabled && !dfilter_compile(filter_exp, &temp_dfilter, &df_err)dfilter_compile_full(filter_exp, &temp_dfilter, &df_err
, (1U << 1)|(1U << 2), __func__)
) {
1091 report_warning("Disabling color filter: Could not compile \"%s\" in colorfilters file \"%s\".\n%s", name, path, df_err->msg);
1092 df_error_free(&df_err);
1093
1094 /* skip_end_of_line = true; */
1095 disabled = true1;
1096 }
1097
1098 fg_color.red = fg_r;
1099 fg_color.green = fg_g;
1100 fg_color.blue = fg_b;
1101
1102 bg_color.red = bg_r;
1103 bg_color.green = bg_g;
1104 bg_color.blue = bg_b;
1105
1106 colorf = color_filter_new(name, filter_exp, &bg_color,
1107 &fg_color, !disabled);
1108 if(user_data == &color_filter_list) {
1109 GSList **cfl = (GSList **)user_data;
1110
1111 /* internal call */
1112 colorf->c_colorfilter = temp_dfilter;
1113 *cfl = g_slist_append(*cfl, colorf);
1114 } else {
1115 /* external call */
1116 /* just editing, don't need the compiled filter */
1117 dfilter_free(temp_dfilter);
1118 add_cb(colorf, user_data);
1119 }
1120 } /* if sscanf */
1121
1122 skip_end_of_line = true1;
1123 }
1124
1125 if (ferror(f))
1126 ret = errno(*__errno_location ());
1127
1128 g_free(name)(__builtin_object_size ((name), 0) != ((size_t) - 1)) ? g_free_sized
(name, __builtin_object_size ((name), 0)) : (g_free) (name)
;
1129 g_free(filter_exp)(__builtin_object_size ((filter_exp), 0) != ((size_t) - 1)) ?
g_free_sized (filter_exp, __builtin_object_size ((filter_exp
), 0)) : (g_free) (filter_exp)
;
1130 return ret;
1131}
1132
1133/* read filters from the filter file */
1134bool_Bool
1135color_filters_read_globals(void *user_data, char** err_msg, color_filter_add_cb_func add_cb, const char* app_env_var_prefix)
1136{
1137 char *path;
1138 FILE *f;
1139 int ret;
1140
1141 /*
1142 * Try to get the global filters.
1143 *
1144 * Get the path for the file that would have the global filters, and
1145 * try to open it.
1146 */
1147 path = get_datafile_path(COLORING_RULES_FILE_NAME"coloring_rules.jsonc", app_env_var_prefix);
1148 if ((f = ws_fopenfopen(path, "r")) == NULL((void*)0)) {
1149 if (errno(*__errno_location ()) != ENOENT2) {
1150 /* Error trying to open the file; give up. */
1151 *err_msg = ws_strdup_printf("Could not open global coloring rules file\n\"%s\": %s.", path,wmem_strdup_printf(((void*)0), "Could not open global coloring rules file\n\"%s\": %s."
, path, g_strerror((*__errno_location ())))
1152 g_strerror(errno))wmem_strdup_printf(((void*)0), "Could not open global coloring rules file\n\"%s\": %s."
, path, g_strerror((*__errno_location ())))
;
1153 g_free(path)(__builtin_object_size ((path), 0) != ((size_t) - 1)) ? g_free_sized
(path, __builtin_object_size ((path), 0)) : (g_free) (path)
;
1154 return false0;
1155 }
1156
1157 /*
1158 * There is no global rules file; treat that as equivalent to
1159 * that file existing bug being empty, and say we succeeded.
1160 */
1161 g_free(path)(__builtin_object_size ((path), 0) != ((size_t) - 1)) ? g_free_sized
(path, __builtin_object_size ((path), 0)) : (g_free) (path)
;
1162 return true1;
1163 }
1164
1165 /* Don't try to read the legacy colorfilters file, unlike the personal config. */
1166
1167 ret = read_filters_file(path, f, user_data, add_cb);
1168 fclose(f);
1169 if (ret != 0) {
1170 *err_msg = ws_strdup_printf("Error reading global coloring rules file\n\"%s\": %s.",wmem_strdup_printf(((void*)0), "Error reading global coloring rules file\n\"%s\": %s."
, path, g_strerror((*__errno_location ())))
1171 path, g_strerror(errno))wmem_strdup_printf(((void*)0), "Error reading global coloring rules file\n\"%s\": %s."
, path, g_strerror((*__errno_location ())))
;
1172 g_free(path)(__builtin_object_size ((path), 0) != ((size_t) - 1)) ? g_free_sized
(path, __builtin_object_size ((path), 0)) : (g_free) (path)
;
1173 return false0;
1174 }
1175
1176 g_free(path)(__builtin_object_size ((path), 0) != ((size_t) - 1)) ? g_free_sized
(path, __builtin_object_size ((path), 0)) : (g_free) (path)
;
1177 return true1;
1178}
1179
1180/* read filters from some other filter file (import) */
1181bool_Bool
1182color_filters_import(const char *path, void *user_data, char **err_msg, color_filter_add_cb_func add_cb)
1183{
1184 FILE *f;
1185 int ret;
1186
1187 if ((f = ws_fopenfopen(path, "r")) == NULL((void*)0)) {
1188 *err_msg = ws_strdup_printf("Could not open filter file\n%s\nfor reading: %s.",wmem_strdup_printf(((void*)0), "Could not open filter file\n%s\nfor reading: %s."
, path, g_strerror((*__errno_location ())))
1189 path, g_strerror(errno))wmem_strdup_printf(((void*)0), "Could not open filter file\n%s\nfor reading: %s."
, path, g_strerror((*__errno_location ())))
;
1190 return false0;
1191 }
1192
1193 /*
1194 * The file being imported might be in the legacy @-delimited
1195 * format or the newer JSONC format. Try the legacy format first,
1196 * then the JSONC format. Each parser simply finds no filters if
1197 * the file isn't in the expected format.
1198 */
1199 ret = read_legacy_filters_file(path, f, user_data, add_cb);
1200 if (ret != 0) {
1201 *err_msg = ws_strdup_printf("Error reading filter file\n\"%s\": %s.",wmem_strdup_printf(((void*)0), "Error reading filter file\n\"%s\": %s."
, path, g_strerror((*__errno_location ())))
1202 path, g_strerror(errno))wmem_strdup_printf(((void*)0), "Error reading filter file\n\"%s\": %s."
, path, g_strerror((*__errno_location ())))
;
1203 fclose(f);
1204 return false0;
1205 }
1206
1207 /* read_filters_file() seeks to the start of the file. */
1208 ret = read_filters_file(path, f, user_data, add_cb);
1209 fclose(f);
1210 if (ret != 0) {
1211 *err_msg = ws_strdup_printf("Error reading filter file\n\"%s\": %s.",wmem_strdup_printf(((void*)0), "Error reading filter file\n\"%s\": %s."
, path, g_strerror((*__errno_location ())))
1212 path, g_strerror(errno))wmem_strdup_printf(((void*)0), "Error reading filter file\n\"%s\": %s."
, path, g_strerror((*__errno_location ())))
;
1213 return false0;
1214 }
1215
1216 return true1;
1217}
1218
1219struct write_filter_data
1220{
1221 FILE *f;
1222 bool_Bool only_selected;
1223};
1224
1225/* save a single filter, in the legacy @-delimited format */
1226static void
1227write_legacy_filter(void *filter_arg, void *data_arg)
1228{
1229 struct write_filter_data *data = (struct write_filter_data *)data_arg;
1230 color_filter_t *colorf = (color_filter_t *)filter_arg;
1231 FILE *f = data->f;
1232
1233 if ( (!data->only_selected) &&
1234 (strstr(colorf->filter_name,CONVERSATION_COLOR_PREFIX"___conversation_color_filter___")==NULL((void*)0)) ) {
1235 fprintf(f,"%s@%s@%s@[%u,%u,%u][%u,%u,%u]\n",
1236 colorf->enabled ? "" : "!",
1237 colorf->filter_name,
1238 colorf->filter_text,
1239 colorf->bg_color.red,
1240 colorf->bg_color.green,
1241 colorf->bg_color.blue,
1242 colorf->fg_color.red,
1243 colorf->fg_color.green,
1244 colorf->fg_color.blue);
1245 }
1246}
1247
1248/* save filters in a filter file, in the legacy @-delimited format */
1249static bool_Bool
1250write_legacy_filters_file(GSList *cfl, FILE *f, bool_Bool only_selected, const char* app_name)
1251{
1252 struct write_filter_data data;
1253
1254 data.f = f;
1255 data.only_selected = only_selected;
1256
1257 fprintf(f,"# This file was created by %s. Edit with care.\n", app_name);
1258 g_slist_foreach(cfl, write_legacy_filter, &data);
1259 return true1;
1260}
1261
1262struct write_filter_jsonc_data
1263{
1264 json_dumper *dumper;
1265 bool_Bool only_selected;
1266};
1267
1268/* save a single filter, in JSONC format */
1269static void
1270write_jsonc_filter(void *filter_arg, void *data_arg)
1271{
1272 struct write_filter_jsonc_data *data = (struct write_filter_jsonc_data *)data_arg;
1273 color_filter_t *colorf = (color_filter_t *)filter_arg;
1274 json_dumper *dumper = data->dumper;
1275 char *fg_str, *bg_str;
1276
1277 if ( (!data->only_selected) &&
1278 (strstr(colorf->filter_name,CONVERSATION_COLOR_PREFIX"___conversation_color_filter___")==NULL((void*)0)) ) {
1279 fg_str = ws_strdup_printf("#%06x", color_t_to_rgb(&colorf->fg_color))wmem_strdup_printf(((void*)0), "#%06x", color_t_to_rgb(&colorf
->fg_color))
;
1280 bg_str = ws_strdup_printf("#%06x", color_t_to_rgb(&colorf->bg_color))wmem_strdup_printf(((void*)0), "#%06x", color_t_to_rgb(&colorf
->bg_color))
;
1281
1282 json_dumper_begin_object(dumper);
1283 json_dumper_set_member_name(dumper, colorf->filter_name);
1284 json_dumper_begin_object(dumper);
1285 json_dumper_set_member_name_const(dumper, "filter")json_dumper_set_member_name_noesc(dumper, "filter", sizeof("filter"
) - 1)
;
1286 json_dumper_value_string(dumper, colorf->filter_text);
1287 json_dumper_set_member_name_const(dumper, "foreground")json_dumper_set_member_name_noesc(dumper, "foreground", sizeof
("foreground") - 1)
;
1288 json_dumper_value_string(dumper, fg_str);
1289 json_dumper_set_member_name_const(dumper, "background")json_dumper_set_member_name_noesc(dumper, "background", sizeof
("background") - 1)
;
1290 json_dumper_value_string(dumper, bg_str);
1291 json_dumper_set_member_name_const(dumper, "enabled")json_dumper_set_member_name_noesc(dumper, "enabled", sizeof("enabled"
) - 1)
;
1292 json_dumper_value_anyf(dumper, "%s", colorf->enabled ? "true" : "false");
1293 json_dumper_end_object(dumper);
1294 json_dumper_end_object(dumper);
1295
1296 g_free(fg_str)(__builtin_object_size ((fg_str), 0) != ((size_t) - 1)) ? g_free_sized
(fg_str, __builtin_object_size ((fg_str), 0)) : (g_free) (fg_str
)
;
1297 g_free(bg_str)(__builtin_object_size ((bg_str), 0) != ((size_t) - 1)) ? g_free_sized
(bg_str, __builtin_object_size ((bg_str), 0)) : (g_free) (bg_str
)
;
1298 }
1299}
1300
1301/* save filters in a coloring rules file, in JSONC format */
1302static bool_Bool
1303write_filters_file(GSList *cfl, FILE *f, bool_Bool only_selected, const char* app_name)
1304{
1305 struct write_filter_jsonc_data data;
1306 json_dumper dumper = {
1307 .output_file = f,
1308 .flags = JSON_DUMPER_FLAGS_PRETTY_PRINT(1 << 0),
1309 };
1310
1311 data.dumper = &dumper;
1312 data.only_selected = only_selected;
1313
1314 fprintf(f, "// This file was created by %s. Edit with care.\n", app_name);
1315
1316 json_dumper_begin_array(&dumper);
1317 g_slist_foreach(cfl, write_jsonc_filter, &data);
1318 json_dumper_end_array(&dumper);
1319
1320 return json_dumper_finish(&dumper);
1321}
1322
1323/* save filters in users filter file(s) */
1324bool_Bool
1325color_filters_write(GSList *cfl, const char* app_name, const char* app_env_var_prefix, char** err_msg)
1326{
1327 char *pf_dir_path;
1328 char *path;
1329 FILE *f;
1330
1331 /* Create the directory that holds personal configuration files,
1332 if necessary. */
1333 if (create_persconffile_dir(app_env_var_prefix, &pf_dir_path) == -1) {
1334 *err_msg = ws_strdup_printf("Can't create directory\n\"%s\"\nfor color files: %s.",wmem_strdup_printf(((void*)0), "Can't create directory\n\"%s\"\nfor color files: %s."
, pf_dir_path, g_strerror((*__errno_location ())))
1335 pf_dir_path, g_strerror(errno))wmem_strdup_printf(((void*)0), "Can't create directory\n\"%s\"\nfor color files: %s."
, pf_dir_path, g_strerror((*__errno_location ())))
;
1336 g_free(pf_dir_path)(__builtin_object_size ((pf_dir_path), 0) != ((size_t) - 1)) ?
g_free_sized (pf_dir_path, __builtin_object_size ((pf_dir_path
), 0)) : (g_free) (pf_dir_path)
;
1337 return false0;
1338 }
1339
1340 /* Only write the legacy colorfilters file if it already exists. */
1341 path = get_persconffile_path(COLORFILTERS_FILE_NAME"colorfilters", true1, app_env_var_prefix);
1342 if (g_file_test(path, G_FILE_TEST_EXISTS)) {
1343 if ((f = ws_fopenfopen(path, "w+")) == NULL((void*)0)) {
1344 *err_msg = ws_strdup_printf("Could not open\n%s\nfor writing: %s.",wmem_strdup_printf(((void*)0), "Could not open\n%s\nfor writing: %s."
, path, g_strerror((*__errno_location ())))
1345 path, g_strerror(errno))wmem_strdup_printf(((void*)0), "Could not open\n%s\nfor writing: %s."
, path, g_strerror((*__errno_location ())))
;
1346 g_free(path)(__builtin_object_size ((path), 0) != ((size_t) - 1)) ? g_free_sized
(path, __builtin_object_size ((path), 0)) : (g_free) (path)
;
1347 return false0;
1348 }
1349 write_legacy_filters_file(cfl, f, false0, app_name);
1350 fclose(f);
1351 }
1352 g_free(path)(__builtin_object_size ((path), 0) != ((size_t) - 1)) ? g_free_sized
(path, __builtin_object_size ((path), 0)) : (g_free) (path)
;
1353
1354 path = get_persconffile_path(COLORING_RULES_FILE_NAME"coloring_rules.jsonc", true1, app_env_var_prefix);
1355 if ((f = ws_fopenfopen(path, "w+")) == NULL((void*)0)) {
1356 *err_msg = ws_strdup_printf("Could not open\n%s\nfor writing: %s.",wmem_strdup_printf(((void*)0), "Could not open\n%s\nfor writing: %s."
, path, g_strerror((*__errno_location ())))
1357 path, g_strerror(errno))wmem_strdup_printf(((void*)0), "Could not open\n%s\nfor writing: %s."
, path, g_strerror((*__errno_location ())))
;
1358 g_free(path)(__builtin_object_size ((path), 0) != ((size_t) - 1)) ? g_free_sized
(path, __builtin_object_size ((path), 0)) : (g_free) (path)
;
1359 return false0;
1360 }
1361 write_filters_file(cfl, f, false0, app_name);
1362 fclose(f);
1363 g_free(path)(__builtin_object_size ((path), 0) != ((size_t) - 1)) ? g_free_sized
(path, __builtin_object_size ((path), 0)) : (g_free) (path)
;
1364 return true1;
1365}
1366
1367/* Save filters in the specified file (export) */
1368bool_Bool
1369color_filters_export(const char *path, GSList *cfl, bool_Bool only_marked, const char* app_name, char** err_msg)
1370{
1371 FILE *f;
1372
1373 if ((f = ws_fopenfopen(path, "w+")) == NULL((void*)0)) {
1374 *err_msg = ws_strdup_printf("Could not open\n%s\nfor writing: %s.",wmem_strdup_printf(((void*)0), "Could not open\n%s\nfor writing: %s."
, path, g_strerror((*__errno_location ())))
1375 path, g_strerror(errno))wmem_strdup_printf(((void*)0), "Could not open\n%s\nfor writing: %s."
, path, g_strerror((*__errno_location ())))
;
1376 return false0;
1377 }
1378 write_filters_file(cfl, f, only_marked, app_name);
1379 fclose(f);
1380 return true1;
1381}
1382
1383/*
1384 * Editor modelines - https://www.wireshark.org/tools/modelines.html
1385 *
1386 * Local variables:
1387 * c-basic-offset: 4
1388 * tab-width: 8
1389 * indent-tabs-mode: nil
1390 * End:
1391 *
1392 * vi: set shiftwidth=4 tabstop=8 expandtab:
1393 * :indentSize=4:tabSize=8:noTabs=true:
1394 */