Bug Summary

File:builds/wireshark/wireshark/epan/color_filters.c
Warning:line 1080, column 13
File position of the stream might be 'indeterminate' after a failed operation. Can cause undefined behavior

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-10-02-100859-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 <gerald@wireshark.org>
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)) {
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)
;
359 if (ret != 0) {
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 ())))
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);
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) {
3
Loop condition is true. Entering loop body
37
Loop condition is true. Entering loop body
999
1000 if (skip_end_of_line
3.1
'skip_end_of_line' is false
37.1
'skip_end_of_line' is true
) {
38
Taking true branch
1001 do {
41
Loop condition is false. Exiting loop
1002 c = ws_getc_unlockedgetc_unlocked(f);
1003 } while (c != EOF(-1) && c != '\n');
39
Assuming the condition is true
40
Assuming the condition is false
1004 if (c == EOF(-1))
42
Taking false branch
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)) {
4
Assuming the condition is true
5
Loop condition is false. Execution continues on line 1016
43
Assuming the condition is true
44
Loop condition is false. Execution continues on line 1016
1011 if (c == '\n') {
1012 continue;
1013 }
1014 }
1015
1016 if (c == EOF(-1))
6
Taking false branch
45
Taking false branch
1017 break;
1018
1019 if (c == '!') {
7
Assuming the condition is false
8
Taking false branch
46
Assuming the condition is false
47
Taking false branch
1020 disabled = true1;
1021 continue;
1022 }
1023
1024 /* skip # comments and invalid lines */
1025 if (c != '@') {
9
Assuming the condition is false
10
Taking false branch
48
Assuming the condition is false
49
Taking false branch
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) {
11
Loop condition is true. Entering loop body
16
Loop condition is true. Entering loop body
50
Loop condition is true. Entering loop body
55
Loop condition is true. Entering loop body
1038 c = ws_getc_unlockedgetc_unlocked(f);
1039 if (c == EOF(-1) || c == '@')
12
Assuming the condition is false
13
Assuming the condition is false
14
Taking false branch
17
Assuming the condition is false
18
Assuming the condition is true
19
Taking true branch
51
Assuming the condition is false
52
Assuming the condition is false
53
Taking false branch
56
Assuming the condition is false
57
Assuming the condition is true
58
Taking true branch
1040 break;
20
Execution continues on line 1048
59
Execution continues on line 1048
1041 if (i
14.1
'i' is < 'name_len'
53.1
'i' is < 'name_len'
>= name_len) {
15
Taking false branch
54
Taking false branch
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)) {
21
Taking false branch
60
Taking false branch
1051 break;
1052 } else if (i
21.1
'i' is not equal to 0
60.1
'i' is not equal to 0
== 0) {
22
Taking false branch
61
Taking false branch
1053 skip_end_of_line = true1;
1054 continue;
1055 }
1056
1057 /* retrieve filter expression */
1058 i = 0;
1059 while (1) {
23
Loop condition is true. Entering loop body
28
Loop condition is true. Entering loop body
62
Loop condition is true. Entering loop body
67
Loop condition is true. Entering loop body
1060 c = ws_getc_unlockedgetc_unlocked(f);
1061 if (c == EOF(-1) || c == '@')
24
Assuming the condition is false
25
Assuming the condition is false
26
Taking false branch
29
Assuming the condition is false
30
Assuming the condition is true
31
Taking true branch
63
Assuming the condition is false
64
Assuming the condition is false
65
Taking false branch
68
Assuming the condition is false
69
Assuming the condition is true
70
Taking true branch
1062 break;
32
Execution continues on line 1070
71
Execution continues on line 1070
1063 if (i
26.1
'i' is < 'filter_exp_len'
65.1
'i' is < 'filter_exp_len'
>= filter_exp_len) {
27
Taking false branch
66
Taking false branch
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)) {
33
Taking false branch
72
Taking false branch
1073 break;
1074 } else if (i
33.1
'i' is not equal to 0
72.1
'i' is not equal to 0
== 0) {
34
Taking false branch
73
Taking false branch
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]",
35
Assuming this stream operation fails
36
Taking false branch
74
File position of the stream might be 'indeterminate' after a failed operation. Can cause undefined behavior
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)) {
1
Taking false branch
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);
2
Calling 'read_legacy_filters_file'
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 */