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-29-100358-3663-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 disabled) /* Is the filter disabled? */
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->disabled = disabled;
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, true1);
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->disabled)
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 disabled, 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->disabled = ((i!=filt_nr) ? true1 : disabled);
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), true1, 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->disabled = colorf->disabled;
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->disabled) 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->disabled) 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->disabled = true1;
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->disabled) && 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->disabled) && 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->disabled) &&
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->disabled) &&
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, disabled;
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 disabled = !enabled;
936 filter_exp = filter_str ? filter_str : "";
937
938 if (!disabled && !dfilter_compile(filter_exp, &temp_dfilter, &df_err)dfilter_compile_full(filter_exp, &temp_dfilter, &df_err
, (1U << 1)|(1U << 2), __func__)
) {
939 report_warning("Disabling color filter: Could not compile \"%s\" in coloring rules file \"%s\".\n%s",
940 name, path, df_err->msg);
941 df_error_free(&df_err);
942 disabled = true1;
943 }
944
945 colorf = color_filter_new(name, filter_exp, &bg_color, &fg_color, disabled);
946 g_free(name)(__builtin_object_size ((name), 0) != ((size_t) - 1)) ? g_free_sized
(name, __builtin_object_size ((name), 0)) : (g_free) (name)
;
947
948 if (user_data == &color_filter_list) {
949 GSList **cfl = (GSList **)user_data;
950 GSList *existing;
951
952 /* internal call */
953 colorf->c_colorfilter = temp_dfilter;
954
955 /* A JSONC rule with the same name as an already-loaded
956 * (legacy) rule replaces it, rather than duplicating it. */
957 existing = g_slist_find_custom(*cfl, colorf->filter_name, color_filters_find_by_name_cb);
958 if (existing != NULL((void*)0)) {
959 color_filter_delete((color_filter_t *)existing->data);
960 existing->data = colorf;
961 } else {
962 *cfl = g_slist_append(*cfl, colorf);
963 }
964 } else {
965 /* external call */
966 /* just editing, don't need the compiled filter */
967 dfilter_free(temp_dfilter);
968 add_cb(colorf, user_data);
969 }
970 }
971
972 g_free(tokens)(__builtin_object_size ((tokens), 0) != ((size_t) - 1)) ? g_free_sized
(tokens, __builtin_object_size ((tokens), 0)) : (g_free) (tokens
)
;
973 g_free(buf)(__builtin_object_size ((buf), 0) != ((size_t) - 1)) ? g_free_sized
(buf, __builtin_object_size ((buf), 0)) : (g_free) (buf)
;
974 return 0;
975}
976
977/* Read legacy (@-delimited) filters from the given file */
978/* XXX At what point do we drop support? */
979/* XXX Would it make more sense to use GStrings here instead of reallocing
980 our buffers? */
981static int
982read_legacy_filters_file(const char *path, FILE *f, void *user_data, color_filter_add_cb_func add_cb)
983{
984#define INIT_BUF_SIZE128 128
985 char *name;
986 char *filter_exp;
987 uint32_t name_len = INIT_BUF_SIZE128;
988 uint32_t filter_exp_len = INIT_BUF_SIZE128;
989 uint32_t i = 0;
990 int c;
991 uint16_t fg_r, fg_g, fg_b, bg_r, bg_g, bg_b;
992 bool_Bool disabled = false0;
993 bool_Bool skip_end_of_line = false0;
994 int ret = 0;
995
996 name = (char *)g_malloc(name_len + 1);
997 filter_exp = (char *)g_malloc(filter_exp_len + 1);
998
999 while (1) {
1000
1001 if (skip_end_of_line) {
1002 do {
1003 c = ws_getc_unlockedgetc_unlocked(f);
1004 } while (c != EOF(-1) && c != '\n');
1005 if (c == EOF(-1))
1006 break;
1007 disabled = false0;
1008 skip_end_of_line = false0;
1009 }
1010
1011 while ((c = ws_getc_unlockedgetc_unlocked(f)) != EOF(-1) && g_ascii_isspace(c)((g_ascii_table[(guchar) (c)] & G_ASCII_SPACE) != 0)) {
1012 if (c == '\n') {
1013 continue;
1014 }
1015 }
1016
1017 if (c == EOF(-1))
1018 break;
1019
1020 if (c == '!') {
1021 disabled = true1;
1022 continue;
1023 }
1024
1025 /* skip # comments and invalid lines */
1026 if (c != '@') {
1027 skip_end_of_line = true1;
1028 continue;
1029 }
1030
1031 /* we get the @ delimiter.
1032 * Format is:
1033 * @name@filter expression@[background r,g,b][foreground r,g,b]
1034 */
1035
1036 /* retrieve name */
1037 i = 0;
1038 while (1) {
1039 c = ws_getc_unlockedgetc_unlocked(f);
1040 if (c == EOF(-1) || c == '@')
1041 break;
1042 if (i >= name_len) {
1043 /* buffer isn't long enough; double its length.*/
1044 name_len *= 2;
1045 name = (char *)g_realloc(name, name_len + 1);
1046 }
1047 name[i++] = c;
1048 }
1049 name[i] = '\0';
1050
1051 if (c == EOF(-1)) {
1052 break;
1053 } else if (i == 0) {
1054 skip_end_of_line = true1;
1055 continue;
1056 }
1057
1058 /* retrieve filter expression */
1059 i = 0;
1060 while (1) {
1061 c = ws_getc_unlockedgetc_unlocked(f);
1062 if (c == EOF(-1) || c == '@')
1063 break;
1064 if (i >= filter_exp_len) {
1065 /* buffer isn't long enough; double its length.*/
1066 filter_exp_len *= 2;
1067 filter_exp = (char *)g_realloc(filter_exp, filter_exp_len + 1);
1068 }
1069 filter_exp[i++] = c;
1070 }
1071 filter_exp[i] = '\0';
1072
1073 if (c == EOF(-1)) {
1074 break;
1075 } else if (i == 0) {
1076 skip_end_of_line = true1;
1077 continue;
1078 }
1079
1080 /* retrieve background and foreground colors */
1081 if (fscanf(f,"[%hu,%hu,%hu][%hu,%hu,%hu]",
1082 &bg_r, &bg_g, &bg_b, &fg_r, &fg_g, &fg_b) == 6) {
1083
1084 /* we got a complete color filter */
1085
1086 color_t bg_color, fg_color;
1087 color_filter_t *colorf;
1088 dfilter_t *temp_dfilter = NULL((void*)0);
1089 df_error_t *df_err = NULL((void*)0);
1090
1091 if (!disabled && !dfilter_compile(filter_exp, &temp_dfilter, &df_err)dfilter_compile_full(filter_exp, &temp_dfilter, &df_err
, (1U << 1)|(1U << 2), __func__)
) {
1092 report_warning("Disabling color filter: Could not compile \"%s\" in colorfilters file \"%s\".\n%s", name, path, df_err->msg);
1093 df_error_free(&df_err);
1094
1095 /* skip_end_of_line = true; */
1096 disabled = true1;
1097 }
1098
1099 fg_color.red = fg_r;
1100 fg_color.green = fg_g;
1101 fg_color.blue = fg_b;
1102
1103 bg_color.red = bg_r;
1104 bg_color.green = bg_g;
1105 bg_color.blue = bg_b;
1106
1107 colorf = color_filter_new(name, filter_exp, &bg_color,
1108 &fg_color, disabled);
1109 if(user_data == &color_filter_list) {
1110 GSList **cfl = (GSList **)user_data;
1111
1112 /* internal call */
1113 colorf->c_colorfilter = temp_dfilter;
1114 *cfl = g_slist_append(*cfl, colorf);
1115 } else {
1116 /* external call */
1117 /* just editing, don't need the compiled filter */
1118 dfilter_free(temp_dfilter);
1119 add_cb(colorf, user_data);
1120 }
1121 } /* if sscanf */
1122
1123 skip_end_of_line = true1;
1124 }
1125
1126 if (ferror(f))
1127 ret = errno(*__errno_location ());
1128
1129 g_free(name)(__builtin_object_size ((name), 0) != ((size_t) - 1)) ? g_free_sized
(name, __builtin_object_size ((name), 0)) : (g_free) (name)
;
1130 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)
;
1131 return ret;
1132}
1133
1134/* read filters from the filter file */
1135bool_Bool
1136color_filters_read_globals(void *user_data, char** err_msg, color_filter_add_cb_func add_cb, const char* app_env_var_prefix)
1137{
1138 char *path;
1139 FILE *f;
1140 int ret;
1141
1142 /*
1143 * Try to get the global filters.
1144 *
1145 * Get the path for the file that would have the global filters, and
1146 * try to open it.
1147 */
1148 path = get_datafile_path(COLORING_RULES_FILE_NAME"coloring_rules.jsonc", app_env_var_prefix);
1149 if ((f = ws_fopenfopen(path, "r")) == NULL((void*)0)) {
1150 if (errno(*__errno_location ()) != ENOENT2) {
1151 /* Error trying to open the file; give up. */
1152 *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 ())))
1153 g_strerror(errno))wmem_strdup_printf(((void*)0), "Could not open global coloring rules file\n\"%s\": %s."
, path, g_strerror((*__errno_location ())))
;
1154 g_free(path)(__builtin_object_size ((path), 0) != ((size_t) - 1)) ? g_free_sized
(path, __builtin_object_size ((path), 0)) : (g_free) (path)
;
1155 return false0;
1156 }
1157
1158 /*
1159 * There is no global rules file; treat that as equivalent to
1160 * that file existing bug being empty, and say we succeeded.
1161 */
1162 g_free(path)(__builtin_object_size ((path), 0) != ((size_t) - 1)) ? g_free_sized
(path, __builtin_object_size ((path), 0)) : (g_free) (path)
;
1163 return true1;
1164 }
1165
1166 /* Don't try to read the legacy colorfilters file, unlike the personal config. */
1167
1168 ret = read_filters_file(path, f, user_data, add_cb);
1169 fclose(f);
1170 if (ret != 0) {
1171 *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 ())))
1172 path, g_strerror(errno))wmem_strdup_printf(((void*)0), "Error reading global coloring rules file\n\"%s\": %s."
, path, g_strerror((*__errno_location ())))
;
1173 g_free(path)(__builtin_object_size ((path), 0) != ((size_t) - 1)) ? g_free_sized
(path, __builtin_object_size ((path), 0)) : (g_free) (path)
;
1174 return false0;
1175 }
1176
1177 g_free(path)(__builtin_object_size ((path), 0) != ((size_t) - 1)) ? g_free_sized
(path, __builtin_object_size ((path), 0)) : (g_free) (path)
;
1178 return true1;
1179}
1180
1181/* read filters from some other filter file (import) */
1182bool_Bool
1183color_filters_import(const char *path, void *user_data, char **err_msg, color_filter_add_cb_func add_cb)
1184{
1185 FILE *f;
1186 int ret;
1187
1188 if ((f = ws_fopenfopen(path, "r")) == NULL((void*)0)) {
1189 *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 ())))
1190 path, g_strerror(errno))wmem_strdup_printf(((void*)0), "Could not open filter file\n%s\nfor reading: %s."
, path, g_strerror((*__errno_location ())))
;
1191 return false0;
1192 }
1193
1194 /*
1195 * The file being imported might be in the legacy @-delimited
1196 * format or the newer JSONC format. Try the legacy format first,
1197 * then the JSONC format. Each parser simply finds no filters if
1198 * the file isn't in the expected format.
1199 */
1200 ret = read_legacy_filters_file(path, f, user_data, add_cb);
1201 if (ret != 0) {
1202 *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 ())))
1203 path, g_strerror(errno))wmem_strdup_printf(((void*)0), "Error reading filter file\n\"%s\": %s."
, path, g_strerror((*__errno_location ())))
;
1204 fclose(f);
1205 return false0;
1206 }
1207
1208 /* read_filters_file() seeks to the start of the file. */
1209 ret = read_filters_file(path, f, user_data, add_cb);
1210 fclose(f);
1211 if (ret != 0) {
1212 *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 ())))
1213 path, g_strerror(errno))wmem_strdup_printf(((void*)0), "Error reading filter file\n\"%s\": %s."
, path, g_strerror((*__errno_location ())))
;
1214 return false0;
1215 }
1216
1217 return true1;
1218}
1219
1220struct write_filter_data
1221{
1222 FILE *f;
1223 bool_Bool only_selected;
1224};
1225
1226/* save a single filter, in the legacy @-delimited format */
1227static void
1228write_legacy_filter(void *filter_arg, void *data_arg)
1229{
1230 struct write_filter_data *data = (struct write_filter_data *)data_arg;
1231 color_filter_t *colorf = (color_filter_t *)filter_arg;
1232 FILE *f = data->f;
1233
1234 if ( (!data->only_selected) &&
1235 (strstr(colorf->filter_name,CONVERSATION_COLOR_PREFIX"___conversation_color_filter___")==NULL((void*)0)) ) {
1236 fprintf(f,"%s@%s@%s@[%u,%u,%u][%u,%u,%u]\n",
1237 colorf->disabled ? "!" : "",
1238 colorf->filter_name,
1239 colorf->filter_text,
1240 colorf->bg_color.red,
1241 colorf->bg_color.green,
1242 colorf->bg_color.blue,
1243 colorf->fg_color.red,
1244 colorf->fg_color.green,
1245 colorf->fg_color.blue);
1246 }
1247}
1248
1249/* save filters in a filter file, in the legacy @-delimited format */
1250static bool_Bool
1251write_legacy_filters_file(GSList *cfl, FILE *f, bool_Bool only_selected, const char* app_name)
1252{
1253 struct write_filter_data data;
1254
1255 data.f = f;
1256 data.only_selected = only_selected;
1257
1258 fprintf(f,"# This file was created by %s. Edit with care.\n", app_name);
1259 g_slist_foreach(cfl, write_legacy_filter, &data);
1260 return true1;
1261}
1262
1263struct write_filter_jsonc_data
1264{
1265 json_dumper *dumper;
1266 bool_Bool only_selected;
1267};
1268
1269/* save a single filter, in JSONC format */
1270static void
1271write_jsonc_filter(void *filter_arg, void *data_arg)
1272{
1273 struct write_filter_jsonc_data *data = (struct write_filter_jsonc_data *)data_arg;
1274 color_filter_t *colorf = (color_filter_t *)filter_arg;
1275 json_dumper *dumper = data->dumper;
1276 char *fg_str, *bg_str;
1277
1278 if ( (!data->only_selected) &&
1279 (strstr(colorf->filter_name,CONVERSATION_COLOR_PREFIX"___conversation_color_filter___")==NULL((void*)0)) ) {
1280 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))
;
1281 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))
;
1282
1283 json_dumper_begin_object(dumper);
1284 json_dumper_set_member_name(dumper, colorf->filter_name);
1285 json_dumper_begin_object(dumper);
1286 json_dumper_set_member_name_const(dumper, "filter")json_dumper_set_member_name_noesc(dumper, "filter", sizeof("filter"
) - 1)
;
1287 json_dumper_value_string(dumper, colorf->filter_text);
1288 json_dumper_set_member_name_const(dumper, "foreground")json_dumper_set_member_name_noesc(dumper, "foreground", sizeof
("foreground") - 1)
;
1289 json_dumper_value_string(dumper, fg_str);
1290 json_dumper_set_member_name_const(dumper, "background")json_dumper_set_member_name_noesc(dumper, "background", sizeof
("background") - 1)
;
1291 json_dumper_value_string(dumper, bg_str);
1292 json_dumper_set_member_name_const(dumper, "enabled")json_dumper_set_member_name_noesc(dumper, "enabled", sizeof("enabled"
) - 1)
;
1293 json_dumper_value_anyf(dumper, "%s", colorf->disabled ? "false" : "true");
1294 json_dumper_end_object(dumper);
1295 json_dumper_end_object(dumper);
1296
1297 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
)
;
1298 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
)
;
1299 }
1300}
1301
1302/* save filters in a coloring rules file, in JSONC format */
1303static bool_Bool
1304write_filters_file(GSList *cfl, FILE *f, bool_Bool only_selected, const char* app_name)
1305{
1306 struct write_filter_jsonc_data data;
1307 json_dumper dumper = {
1308 .output_file = f,
1309 .flags = JSON_DUMPER_FLAGS_PRETTY_PRINT(1 << 0),
1310 };
1311
1312 data.dumper = &dumper;
1313 data.only_selected = only_selected;
1314
1315 fprintf(f, "// This file was created by %s. Edit with care.\n", app_name);
1316
1317 json_dumper_begin_array(&dumper);
1318 g_slist_foreach(cfl, write_jsonc_filter, &data);
1319 json_dumper_end_array(&dumper);
1320
1321 return json_dumper_finish(&dumper);
1322}
1323
1324/* save filters in users filter file(s) */
1325bool_Bool
1326color_filters_write(GSList *cfl, const char* app_name, const char* app_env_var_prefix, char** err_msg)
1327{
1328 char *pf_dir_path;
1329 char *path;
1330 FILE *f;
1331
1332 /* Create the directory that holds personal configuration files,
1333 if necessary. */
1334 if (create_persconffile_dir(app_env_var_prefix, &pf_dir_path) == -1) {
1335 *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 ())))
1336 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 ())))
;
1337 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)
;
1338 return false0;
1339 }
1340
1341 /* Only write the legacy colorfilters file if it already exists. */
1342 path = get_persconffile_path(COLORFILTERS_FILE_NAME"colorfilters", true1, app_env_var_prefix);
1343 if (g_file_test(path, G_FILE_TEST_EXISTS)) {
1344 if ((f = ws_fopenfopen(path, "w+")) == NULL((void*)0)) {
1345 *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 ())))
1346 path, g_strerror(errno))wmem_strdup_printf(((void*)0), "Could not open\n%s\nfor writing: %s."
, path, g_strerror((*__errno_location ())))
;
1347 g_free(path)(__builtin_object_size ((path), 0) != ((size_t) - 1)) ? g_free_sized
(path, __builtin_object_size ((path), 0)) : (g_free) (path)
;
1348 return false0;
1349 }
1350 write_legacy_filters_file(cfl, f, false0, app_name);
1351 fclose(f);
1352 }
1353 g_free(path)(__builtin_object_size ((path), 0) != ((size_t) - 1)) ? g_free_sized
(path, __builtin_object_size ((path), 0)) : (g_free) (path)
;
1354
1355 path = get_persconffile_path(COLORING_RULES_FILE_NAME"coloring_rules.jsonc", true1, app_env_var_prefix);
1356 if ((f = ws_fopenfopen(path, "w+")) == NULL((void*)0)) {
1357 *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 ())))
1358 path, g_strerror(errno))wmem_strdup_printf(((void*)0), "Could not open\n%s\nfor writing: %s."
, path, g_strerror((*__errno_location ())))
;
1359 g_free(path)(__builtin_object_size ((path), 0) != ((size_t) - 1)) ? g_free_sized
(path, __builtin_object_size ((path), 0)) : (g_free) (path)
;
1360 return false0;
1361 }
1362 write_filters_file(cfl, f, false0, app_name);
1363 fclose(f);
1364 g_free(path)(__builtin_object_size ((path), 0) != ((size_t) - 1)) ? g_free_sized
(path, __builtin_object_size ((path), 0)) : (g_free) (path)
;
1365 return true1;
1366}
1367
1368/* Save filters in the specified file (export) */
1369bool_Bool
1370color_filters_export(const char *path, GSList *cfl, bool_Bool only_marked, const char* app_name, char** err_msg)
1371{
1372 FILE *f;
1373
1374 if ((f = ws_fopenfopen(path, "w+")) == NULL((void*)0)) {
1375 *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 ())))
1376 path, g_strerror(errno))wmem_strdup_printf(((void*)0), "Could not open\n%s\nfor writing: %s."
, path, g_strerror((*__errno_location ())))
;
1377 return false0;
1378 }
1379 write_filters_file(cfl, f, only_marked, app_name);
1380 fclose(f);
1381 return true1;
1382}
1383
1384/*
1385 * Editor modelines - https://www.wireshark.org/tools/modelines.html
1386 *
1387 * Local variables:
1388 * c-basic-offset: 4
1389 * tab-width: 8
1390 * indent-tabs-mode: nil
1391 * End:
1392 *
1393 * vi: set shiftwidth=4 tabstop=8 expandtab:
1394 * :indentSize=4:tabSize=8:noTabs=true:
1395 */