Bug Summary

File:builds/wireshark/wireshark/epan/color_filters.c
Warning:line 1081, column 13
Read function called when stream is in EOF state. Function has no effect

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 <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 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)) {
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->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) {
3
Loop condition is true. Entering loop body
37
Loop condition is true. Entering loop body
1000
1001 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
1002 do {
41
Loop condition is false. Exiting loop
1003 c = ws_getc_unlockedgetc_unlocked(f);
1004 } while (c != EOF(-1) && c != '\n');
39
Assuming the condition is true
40
Assuming the condition is false
1005 if (c == EOF(-1))
42
Taking false branch
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)) {
4
Assuming the condition is true
5
Loop condition is false. Execution continues on line 1017
43
Assuming the condition is true
44
Loop condition is false. Execution continues on line 1017
1012 if (c == '\n') {
1013 continue;
1014 }
1015 }
1016
1017 if (c == EOF(-1))
6
Taking false branch
45
Taking false branch
1018 break;
1019
1020 if (c == '!') {
7
Assuming the condition is false
8
Taking false branch
46
Assuming the condition is false
47
Taking false branch
1021 disabled = true1;
1022 continue;
1023 }
1024
1025 /* skip # comments and invalid lines */
1026 if (c != '@') {
9
Assuming the condition is false
10
Taking false branch
48
Assuming the condition is false
49
Taking false branch
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) {
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
1039 c = ws_getc_unlockedgetc_unlocked(f);
1040 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
1041 break;
20
Execution continues on line 1049
59
Execution continues on line 1049
1042 if (i
14.1
'i' is < 'name_len'
53.1
'i' is < 'name_len'
>= name_len) {
15
Taking false branch
54
Taking false branch
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)) {
21
Taking false branch
60
Taking false branch
1052 break;
1053 } 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
1054 skip_end_of_line = true1;
1055 continue;
1056 }
1057
1058 /* retrieve filter expression */
1059 i = 0;
1060 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
1061 c = ws_getc_unlockedgetc_unlocked(f);
1062 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
1063 break;
32
Execution continues on line 1071
71
Execution continues on line 1071
1064 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
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)) {
33
Taking false branch
72
Taking false branch
1074 break;
1075 } 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
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]",
35
Assuming stream reaches end-of-file here
36
Taking false branch
74
Read function called when stream is in EOF state. Function has no effect
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)) {
1
Taking false branch
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);
2
Calling 'read_legacy_filters_file'
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 */