Bug Summary

File:builds/wireshark/wireshark/epan/color_filters.c
Warning:line 1095, 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-10-22-100339-3661-1 -x c /builds/wireshark/wireshark/epan/color_filters.c
1/* color_filters.c
2 * Routines for color filters
3 *
4 * Wireshark - Network traffic analyzer
5 * By Gerald Combs <[email protected]>
6 * Copyright 1998 Gerald Combs
7 *
8 * SPDX-License-Identifier: GPL-2.0-or-later
9 */
10/*
11 * Updated 1 Dec 10 jjm
12 */
13
14#include "config.h"
15#define WS_LOG_DOMAIN"Epan" LOG_DOMAIN_EPAN"Epan"
16
17#include <glib.h>
18
19#include <errno(*__errno_location ()).h>
20#include <stdio.h>
21#include <stdlib.h>
22#include <string.h>
23
24#include <wsutil/filesystem.h>
25#include <wsutil/file_util.h>
26#include <wsutil/report_message.h>
27#include <wsutil/wslog.h>
28#include <wsutil/ws_assert.h>
29#include <wsutil/wmem/wmem_list.h>
30#include <wsutil/wsjson.h>
31#include <wsutil/json_dumper.h>
32
33#include <epan/packet.h>
34#include "color_filters.h"
35#include "file.h"
36#include <epan/dfilter/dfilter.h>
37#include <epan/prefs.h>
38#include <epan/epan_dissect.h>
39
40/*
41 * The coloring_rules.jsonc file is formatted as JSONC (jsonc.org). It
42 * must contain an array of named objects with the following key:value
43 * pairs:
44 * [
45 * { "Filter name": {
46 * "filter": "<display filter string>",
47 * "foreground": "#rrggbb or rrggbb",
48 * "background": "#rrggbb or rrggbb",
49 * "enabled": "<boolean value>",
50 * }
51 * }
52 * ]
53 *
54 * Each line in the legacy colorfilters file has the following format:
55 *
56 * @<filter name>@<filter string>@[<background>][<foreground>]
57 * Background and foreground colors are 16-bit comma-separated RGB
58 * triplets. Colors are 16 bits because that's what GdkColor used.
59 * We might want to use a more standard, copy+paste-able color scheme
60 * such as #RRGGBB instead.
61 */
62
63static int read_filters_file(const char *path, FILE *f, void *user_data, color_filter_add_cb_func add_cb, bool_Bool require_json);
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 char *legacy_path;
332 FILE *f;
333 int ret;
334
335 /* start the list with the temporary colorizing rules */
336 color_filters_add_tmp(&color_filter_list);
337
338 /*
339 * Resolve both personal paths up front, although at most one file is
340 * read. get_persconffile_path() registers each name as a profile file,
341 * which makes profile copy and the Default profile reset include it.
342 * Resolving the legacy name only when it is read would let a reset
343 * remove coloring_rules.jsonc but keep a stale colorfilters, whose
344 * rules would then come back on the next start.
345 */
346 path = get_persconffile_path(COLORING_RULES_FILE_NAME"coloring_rules.jsonc", true1, app_env_var_prefix);
347 legacy_path = get_persconffile_path(COLORFILTERS_FILE_NAME"colorfilters", true1, app_env_var_prefix);
348
349 /*
350 * If the personal coloring_rules.jsonc exists, it is the user's rule
351 * list and the only file read. A legacy colorfilters file next to it
352 * is an older copy kept for older versions; reading it as well would
353 * bring back rules the user has since deleted or changed.
354 */
355 if ((f = ws_fopenfopen(path, "r")) != NULL((void*)0)) {
356 ret = read_filters_file(path, f, &color_filter_list, add_cb, true1);
357 fclose(f);
358 if (ret != 0) {
359 *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(ret))
360 path, g_strerror(ret))wmem_strdup_printf(((void*)0), "Error reading coloring rules file\n\"%s\": %s."
, path, g_strerror(ret))
;
361 g_free(legacy_path)(__builtin_object_size ((legacy_path), 0) != ((size_t) - 1)) ?
g_free_sized (legacy_path, __builtin_object_size ((legacy_path
), 0)) : (g_free) (legacy_path)
;
362 g_free(path)(__builtin_object_size ((path), 0) != ((size_t) - 1)) ? g_free_sized
(path, __builtin_object_size ((path), 0)) : (g_free) (path)
;
363 return false0;
364 }
365 g_free(legacy_path)(__builtin_object_size ((legacy_path), 0) != ((size_t) - 1)) ?
g_free_sized (legacy_path, __builtin_object_size ((legacy_path
), 0)) : (g_free) (legacy_path)
;
366 g_free(path)(__builtin_object_size ((path), 0) != ((size_t) - 1)) ? g_free_sized
(path, __builtin_object_size ((path), 0)) : (g_free) (path)
;
367 return true1;
368 }
369 if (errno(*__errno_location ()) != ENOENT2) {
370 *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 ())))
371 g_strerror(errno))wmem_strdup_printf(((void*)0), "Could not open coloring rules file\n\"%s\": %s."
, path, g_strerror((*__errno_location ())))
;
372 g_free(legacy_path)(__builtin_object_size ((legacy_path), 0) != ((size_t) - 1)) ?
g_free_sized (legacy_path, __builtin_object_size ((legacy_path
), 0)) : (g_free) (legacy_path)
;
373 g_free(path)(__builtin_object_size ((path), 0) != ((size_t) - 1)) ? g_free_sized
(path, __builtin_object_size ((path), 0)) : (g_free) (path)
;
374 return false0;
375 }
376 g_free(path)(__builtin_object_size ((path), 0) != ((size_t) - 1)) ? g_free_sized
(path, __builtin_object_size ((path), 0)) : (g_free) (path)
;
377
378 /*
379 * No personal coloring_rules.jsonc. A legacy colorfilters file from a
380 * version before the JSONC format is the complete personal rule list,
381 * so the global rules are not added to it. The next save writes it
382 * out as coloring_rules.jsonc.
383 */
384 if ((f = ws_fopenfopen(legacy_path, "r")) != NULL((void*)0)) {
385 ret = read_legacy_filters_file(legacy_path, f, &color_filter_list, add_cb);
386 fclose(f);
387 if (ret != 0) {
388 *err_msg = ws_strdup_printf("Error reading filter file\n\"%s\": %s.",wmem_strdup_printf(((void*)0), "Error reading filter file\n\"%s\": %s."
, legacy_path, g_strerror(ret))
389 legacy_path, g_strerror(ret))wmem_strdup_printf(((void*)0), "Error reading filter file\n\"%s\": %s."
, legacy_path, g_strerror(ret))
;
390 g_free(legacy_path)(__builtin_object_size ((legacy_path), 0) != ((size_t) - 1)) ?
g_free_sized (legacy_path, __builtin_object_size ((legacy_path
), 0)) : (g_free) (legacy_path)
;
391 return false0;
392 }
393 g_free(legacy_path)(__builtin_object_size ((legacy_path), 0) != ((size_t) - 1)) ?
g_free_sized (legacy_path, __builtin_object_size ((legacy_path
), 0)) : (g_free) (legacy_path)
;
394 return true1;
395 }
396 if (errno(*__errno_location ()) != ENOENT2) {
397 *err_msg = ws_strdup_printf("Could not open filter file\n\"%s\": %s.", legacy_path,wmem_strdup_printf(((void*)0), "Could not open filter file\n\"%s\": %s."
, legacy_path, g_strerror((*__errno_location ())))
398 g_strerror(errno))wmem_strdup_printf(((void*)0), "Could not open filter file\n\"%s\": %s."
, legacy_path, g_strerror((*__errno_location ())))
;
399 g_free(legacy_path)(__builtin_object_size ((legacy_path), 0) != ((size_t) - 1)) ?
g_free_sized (legacy_path, __builtin_object_size ((legacy_path
), 0)) : (g_free) (legacy_path)
;
400 return false0;
401 }
402 g_free(legacy_path)(__builtin_object_size ((legacy_path), 0) != ((size_t) - 1)) ?
g_free_sized (legacy_path, __builtin_object_size ((legacy_path
), 0)) : (g_free) (legacy_path)
;
403
404 /* They don't have any personal rules; try to read the global rules */
405 return color_filters_read_globals(&color_filter_list, err_msg, add_cb, app_env_var_prefix);
406}
407
408/* Initialize the filter structures (reading from file) for general running, including app startup */
409bool_Bool
410color_filters_init(char** err_msg, color_filter_add_cb_func add_cb, const char* app_env_var_prefix)
411{
412 /* delete all currently existing filters */
413 color_filter_list_delete(&color_filter_list);
414
415 /* now try to construct the filters list */
416 bool_Bool result = color_filters_get(err_msg, add_cb, app_env_var_prefix);
417
418 /* Load paused filters from profile after loading color filters */
419 if (result) {
420 color_filter_read_paused(app_env_var_prefix);
421 }
422
423 return result;
424}
425
426bool_Bool
427color_filters_reload(char** err_msg, color_filter_add_cb_func add_cb, const char* app_env_var_prefix)
428{
429 /* "move" old entries to the deleted list
430 * we must keep them until the dissection no longer needs them */
431 color_filter_deleted_list = g_slist_concat(color_filter_deleted_list, color_filter_list);
432 color_filter_list = NULL((void*)0);
433
434 /* now try to construct the filters list */
435 return color_filters_get(err_msg, add_cb, app_env_var_prefix);
436}
437
438void
439color_filters_cleanup(void)
440{
441 /* delete the previously deleted filters */
442 color_filter_list_delete(&color_filter_deleted_list);
443
444 if (session_disabled_filters) {
445 g_hash_table_destroy(session_disabled_filters);
446 session_disabled_filters = NULL((void*)0);
447 }
448}
449
450typedef struct _color_clone
451{
452 void *user_data;
453 color_filter_add_cb_func add_cb;
454} color_clone_t;
455
456static void
457color_filters_clone_cb(void *filter_arg, void *user_data)
458{
459 color_clone_t* clone_data = (color_clone_t*)user_data;
460 color_filter_t * new_colorf = color_filter_clone((color_filter_t *)filter_arg);
461
462 clone_data->add_cb (new_colorf, clone_data->user_data);
463}
464
465void
466color_filters_clone(void *user_data, color_filter_add_cb_func add_cb)
467{
468 color_clone_t clone_data;
469
470 clone_data.user_data = user_data;
471 clone_data.add_cb = add_cb;
472 g_slist_foreach(color_filter_list, color_filters_clone_cb, &clone_data);
473}
474
475
476static void
477color_filter_compile_cb(void *filter_arg, void *err)
478{
479 color_filter_t *colorf = (color_filter_t *)filter_arg;
480 char **err_msg = (char**)err;
481 df_error_t *df_err = NULL((void*)0);
482
483 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"
, 483, __func__, "assertion failed: %s", "colorf->c_colorfilter == ((void*)0)"
); } while (0)
;
484
485 /* If the filter is disabled it doesn't matter if it compiles or not. */
486 if (!colorf->enabled) return;
487
488 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__
)
) {
489 *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)
490 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)
;
491 df_error_free(&df_err);
492 /* this filter was compilable before, so this should never happen */
493 /* except if the OK button of the parent window has been clicked */
494 /* so don't use ws_assert_not_reached() but check the filters again */
495 }
496}
497
498static void
499color_filter_validate_cb(void *filter_arg, void *err)
500{
501 color_filter_t *colorf = (color_filter_t *)filter_arg;
502 char **err_msg = (char**)err;
503 df_error_t *df_err = NULL((void*)0);
504
505 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"
, 505, __func__, "assertion failed: %s", "colorf->c_colorfilter == ((void*)0)"
); } while (0)
;
506
507 /* If the filter is disabled it doesn't matter if it compiles or not. */
508 if (!colorf->enabled) return;
509
510 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__
)
) {
511 *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)
512 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)
;
513 df_error_free(&df_err);
514
515 /* Disable the color filter in the list of color filters. */
516 colorf->enabled = false0;
517 }
518
519 /* XXX: What if the color filter tests "frame.coloring_rule.name" or
520 * "frame.coloring_rule.string"?
521 */
522}
523
524/* apply changes from the edit list */
525bool_Bool
526color_filters_apply(GSList *tmp_cfl, GSList *edit_cfl, char** err_msg)
527{
528 bool_Bool ret = true1;
529
530 *err_msg = NULL((void*)0);
531
532 /* "move" old entries to the deleted list
533 * we must keep them until the dissection no longer needs them */
534 color_filter_deleted_list = g_slist_concat(color_filter_deleted_list, color_filter_list);
535 color_filter_list = NULL((void*)0);
536
537 /* clone all list entries from tmp/edit to normal list */
538 color_filter_list_delete(&color_filter_valid_list);
539 color_filter_valid_list = color_filter_list_clone(tmp_cfl);
540 color_filter_valid_list = g_slist_concat(color_filter_valid_list,
541 color_filter_list_clone(edit_cfl) );
542
543 /* compile all filter */
544 g_slist_foreach(color_filter_valid_list, color_filter_validate_cb, err_msg);
545 if (*err_msg != NULL((void*)0)) {
546 ret = false0;
547 }
548
549 /* clone all list entries from tmp/edit to normal list */
550 color_filter_list = color_filter_list_clone(color_filter_valid_list);
551
552 /* compile all filter */
553 g_slist_foreach(color_filter_list, color_filter_compile_cb, err_msg);
554 if (*err_msg != NULL((void*)0)) {
555 ret = false0;
556 }
557
558 return ret;
559}
560
561bool_Bool
562color_filters_used(void)
563{
564 return color_filter_list != NULL((void*)0) && filters_enabled;
565}
566
567bool_Bool
568tmp_color_filters_used(void)
569{
570 return tmp_colors_set;
571}
572
573/* prepare the epan_dissect_t for the filter */
574static void
575prime_edt(void *data, void *user_data)
576{
577 color_filter_t *colorf = (color_filter_t *)data;
578 epan_dissect_t *edt = (epan_dissect_t *)user_data;
579
580 if (colorf->c_colorfilter != NULL((void*)0))
581 epan_dissect_prime_with_dfilter(edt, colorf->c_colorfilter);
582}
583
584/* Prime the epan_dissect_t with all the compiler
585 * color filters in 'color_filter_list'. */
586void
587color_filters_prime_edt(epan_dissect_t *edt)
588{
589 if (color_filters_used())
590 g_slist_foreach(color_filter_list, prime_edt, edt);
591}
592
593static int
594find_hfid(const void *data, const void *user_data)
595{
596 color_filter_t *colorf = (color_filter_t *)data;
597 int hfid = GPOINTER_TO_INT(user_data)((gint) (glong) (user_data));
598
599 if ((colorf->enabled) && colorf->c_colorfilter != NULL((void*)0)) {
600 if (dfilter_interested_in_field(colorf->c_colorfilter, hfid)) {
601 return 0;
602 }
603 }
604 return -1;
605}
606
607bool_Bool
608color_filters_use_hfid(int hfid)
609{
610 GSList *item = NULL((void*)0);
611 if (color_filters_used())
612 item = g_slist_find_custom(color_filter_list, GINT_TO_POINTER(hfid)((gpointer) (glong) (hfid)), find_hfid);
613 return (item != NULL((void*)0));
614}
615
616static int
617find_proto(const void *data, const void *user_data)
618{
619 color_filter_t *colorf = (color_filter_t *)data;
620 int proto_id = GPOINTER_TO_INT(user_data)((gint) (glong) (user_data));
621
622 if ((colorf->enabled) && colorf->c_colorfilter != NULL((void*)0)) {
623 if (dfilter_interested_in_proto(colorf->c_colorfilter, proto_id)) {
624 return 0;
625 }
626 }
627 return -1;
628}
629
630bool_Bool
631color_filters_use_proto(int proto_id)
632{
633 GSList *item = NULL((void*)0);
634 if (color_filters_used())
635 item = g_slist_find_custom(color_filter_list, GINT_TO_POINTER(proto_id)((gpointer) (glong) (proto_id)), find_proto);
636 return (item != NULL((void*)0));
637}
638
639/* * Return the color_t for later use */
640const color_filter_t *
641color_filters_colorize_packet(epan_dissect_t *edt)
642{
643 GSList *curr;
644 color_filter_t *colorf;
645
646 /* If we have color filters, "search" for the matching one. */
647 if ((edt->tree != NULL((void*)0)) && (color_filters_used())) {
648 curr = color_filter_list;
649
650 while(curr != NULL((void*)0)) {
651 colorf = (color_filter_t *)curr->data;
652 if ( (colorf->enabled) &&
653 (colorf->c_colorfilter != NULL((void*)0)) &&
654 dfilter_apply_edt(colorf->c_colorfilter, edt) &&
655 !color_filter_is_session_disabled(colorf->filter_name)) {
656 return colorf;
657 }
658 curr = g_slist_next(curr)((curr) ? (((GSList *)(curr))->next) : ((void*)0));
659 }
660 }
661
662 return NULL((void*)0);
663}
664
665const color_filter_t *
666color_filters_colorize_packet_all(epan_dissect_t *edt,
667 wmem_allocator_t *scope, wmem_list_t **matches)
668{
669 const color_filter_t *first_match = NULL((void*)0);
670
671 if (matches) {
672 *matches = NULL((void*)0);
673 }
674
675 /* If we have color filters, collect ALL matching ones. */
676 if ((edt->tree != NULL((void*)0)) && (color_filters_used())) {
677 for (GSList *curr = color_filter_list; curr != NULL((void*)0); curr = g_slist_next(curr)((curr) ? (((GSList *)(curr))->next) : ((void*)0))) {
678 color_filter_t *colorf = (color_filter_t *)curr->data;
679 if ((colorf->enabled) &&
680 (colorf->c_colorfilter != NULL((void*)0)) &&
681 dfilter_apply_edt(colorf->c_colorfilter, edt)) {
682
683 bool_Bool is_session_disabled = color_filter_is_session_disabled(colorf->filter_name);
684
685 /* Add to matches list even if paused (for Frame tree display) */
686 if (matches) {
687 if (*matches == NULL((void*)0)) {
688 *matches = wmem_list_new(scope);
689 }
690 wmem_list_append(*matches, colorf);
691 }
692
693 /* Only use non-paused filters for first_match */
694 if (!first_match && !is_session_disabled) {
695 first_match = colorf; /* Backward compatibility */
696 }
697 }
698 }
699 }
700
701 return first_match;
702}
703
704void
705color_filter_set_session_disabled(const char *filter_name, bool_Bool disabled)
706{
707 if (!filter_name) return;
708
709 if (!session_disabled_filters) {
710 session_disabled_filters = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL((void*)0));
711 }
712
713 if (disabled) {
714 g_hash_table_insert(session_disabled_filters, g_strdup(filter_name)g_strdup_inline (filter_name), GINT_TO_POINTER(1)((gpointer) (glong) (1)));
715 } else {
716 g_hash_table_remove(session_disabled_filters, filter_name);
717 }
718
719 /* Auto-save to profile directory after every change */
720 color_filter_write_paused(NULL((void*)0)); /* NULL uses default env prefix */
721}
722
723bool_Bool
724color_filter_is_session_disabled(const char *filter_name)
725{
726 if (!filter_name || !session_disabled_filters) {
727 return false0;
728 }
729 return g_hash_table_contains(session_disabled_filters, filter_name);
730}
731
732void
733color_filter_clear_session_disabled(void)
734{
735 if (session_disabled_filters) {
736 g_hash_table_remove_all(session_disabled_filters);
737 }
738 /* Restore from profile directory after clearing (workaround for rescan) */
739 color_filter_read_paused(NULL((void*)0));
740}
741
742#define PAUSED_FILTERS_FILE"paused_filters" "paused_filters"
743
744/* Get profile-specific paused filters file path */
745static char *
746get_paused_filters_path(const char *app_env_var_prefix)
747{
748 /* Use profile-specific path (true = use profile directory) */
749 return get_persconffile_path(PAUSED_FILTERS_FILE"paused_filters", true1, app_env_var_prefix);
750}
751
752/* Save paused filters to profile directory */
753void
754color_filter_write_paused(const char *app_env_var_prefix)
755{
756 if (!session_disabled_filters) return;
757
758 char *path = get_paused_filters_path(app_env_var_prefix);
759 if (!path) return;
760
761 FILE *f = ws_fopenfopen(path, "w");
762 if (!f) {
763 g_free(path)(__builtin_object_size ((path), 0) != ((size_t) - 1)) ? g_free_sized
(path, __builtin_object_size ((path), 0)) : (g_free) (path)
;
764 return;
765 }
766
767 /* Write each paused filter name, one per line */
768 GHashTableIter iter;
769 gpointer key, value;
770 g_hash_table_iter_init(&iter, session_disabled_filters);
771 while (g_hash_table_iter_next(&iter, &key, &value)) {
772 fprintf(f, "%s\n", (const char*)key);
773 }
774
775 fclose(f);
776 g_free(path)(__builtin_object_size ((path), 0) != ((size_t) - 1)) ? g_free_sized
(path, __builtin_object_size ((path), 0)) : (g_free) (path)
;
777}
778
779/* Read paused filters from profile directory */
780void
781color_filter_read_paused(const char *app_env_var_prefix)
782{
783 char *path = get_paused_filters_path(app_env_var_prefix);
784 if (!path) return;
785
786 FILE *f = ws_fopenfopen(path, "r");
787 g_free(path)(__builtin_object_size ((path), 0) != ((size_t) - 1)) ? g_free_sized
(path, __builtin_object_size ((path), 0)) : (g_free) (path)
;
788
789 if (!f) return; /* File doesn't exist yet - that's OK */
790
791 char line[1024];
792 while (fgets(line, sizeof(line), f)) {
793 /* Remove trailing newline/whitespace */
794 line[strcspn(line, "\r\n")] = 0;
795 g_strstrip(line)g_strchomp (g_strchug (line)); /* GLib function to trim whitespace */
796
797 if (line[0] && line[0] != '#') { /* Skip empty lines and comments */
798 /* Add to session_disabled_filters without triggering auto-save */
799 if (!session_disabled_filters) {
800 session_disabled_filters = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL((void*)0));
801 }
802 g_hash_table_insert(session_disabled_filters, g_strdup(line)g_strdup_inline (line), GINT_TO_POINTER(1)((gpointer) (glong) (1)));
803 }
804 }
805
806 fclose(f);
807}
808
809/* Resume all paused filters (clears all and saves empty file) */
810void
811color_filter_resume_all(const char *app_env_var_prefix)
812{
813 /* Clear the hash table */
814 if (session_disabled_filters) {
815 g_hash_table_remove_all(session_disabled_filters);
816 }
817
818 /* Write empty file to profile directory */
819 char *path = get_paused_filters_path(app_env_var_prefix);
820 if (!path) return;
821
822 FILE *f = ws_fopenfopen(path, "w");
823 if (f) {
824 /* Write empty file (or just close it) */
825 fclose(f);
826 }
827 g_free(path)(__builtin_object_size ((path), 0) != ((size_t) - 1)) ? g_free_sized
(path, __builtin_object_size ((path), 0)) : (g_free) (path)
;
828}
829
830/* Parse a "#rrggbb" or "rrggbb" string into a color_t. Returns false (and
831 * leaves *color untouched) if str is NULL or not a valid hex triplet. */
832static bool_Bool
833hex_str_to_color(const char *str, color_t *color)
834{
835 unsigned long cval;
836
837 if (str == NULL((void*)0)) {
838 return false0;
839 }
840 if (str[0] == '#') {
841 str++;
842 }
843 if (strlen(str) != 6 || strspn(str, "0123456789abcdefABCDEF") != 6) {
844 return false0;
845 }
846
847 cval = strtoul(str, NULL((void*)0), 16);
848 color->red = RED_COMPONENT(cval)(uint16_t) (((((cval) >> 16) & 0xff) * 65535 / 255)
)
;
849 color->green = GREEN_COMPONENT(cval)(uint16_t) (((((cval) >> 8) & 0xff) * 65535 / 255));
850 color->blue = BLUE_COMPONENT(cval)(uint16_t) ( (((cval) & 0xff) * 65535 / 255));
851 return true1;
852}
853
854static int
855read_filters_file(const char *path, FILE *f, void *user_data, color_filter_add_cb_func add_cb, bool_Bool require_json)
856{
857 long size;
858 char *buf;
859 size_t nread;
860 int num_tokens;
861 jsmntok_t *tokens;
862 int array_len;
863 jsmntok_t *element;
864
865 if (fseek(f, 0, SEEK_END2) == -1) {
866 return errno(*__errno_location ());
867 }
868 size = ftell(f);
869 if (size < 0) {
870 return errno(*__errno_location ());
871 }
872 if (fseek(f, 0, SEEK_SET0) == -1) {
873 return errno(*__errno_location ());
874 }
875 if (size == 0) {
876 return 0;
877 }
878
879 buf = (char *)g_malloc(size + 1);
880 nread = fread(buf, 1, size, f);
881 if (ferror(f)) {
882 int ret = errno(*__errno_location ());
883 g_free(buf)(__builtin_object_size ((buf), 0) != ((size_t) - 1)) ? g_free_sized
(buf, __builtin_object_size ((buf), 0)) : (g_free) (buf)
;
884 return ret;
885 }
886 buf[nread] = '\0';
887
888 if (!json_strip_jsonc_comments(buf)) {
889 report_warning("Error parsing coloring rules file\n\"%s\": unterminated block comment.", path);
890 g_free(buf)(__builtin_object_size ((buf), 0) != ((size_t) - 1)) ? g_free_sized
(buf, __builtin_object_size ((buf), 0)) : (g_free) (buf)
;
891 return 0;
892 }
893
894 num_tokens = json_parse(buf, NULL((void*)0), 0);
895 if (num_tokens == 0) {
896 /* Only comments and whitespace: a valid, empty rule list. */
897 g_free(buf)(__builtin_object_size ((buf), 0) != ((size_t) - 1)) ? g_free_sized
(buf, __builtin_object_size ((buf), 0)) : (g_free) (buf)
;
898 return 0;
899 }
900 if (num_tokens < 0) {
901 /*
902 * Not JSON at all. For the personal and global rule files that is
903 * an error the user must see, since none of the file's rules are
904 * used. An imported file may be in the legacy format, which never
905 * parses as JSON, so it just yields no rules here.
906 */
907 if (require_json) {
908 report_warning("Error parsing coloring rules file\n\"%s\": invalid JSON.", path);
909 }
910 g_free(buf)(__builtin_object_size ((buf), 0) != ((size_t) - 1)) ? g_free_sized
(buf, __builtin_object_size ((buf), 0)) : (g_free) (buf)
;
911 return 0;
912 }
913
914 tokens = (jsmntok_t *)g_malloc(num_tokens * sizeof(jsmntok_t));
915 if (json_parse(buf, tokens, num_tokens) <= 0 || tokens[0].type != JSMN_ARRAY) {
916 report_warning("Error parsing coloring rules file\n\"%s\": invalid JSON.", path);
917 g_free(tokens)(__builtin_object_size ((tokens), 0) != ((size_t) - 1)) ? g_free_sized
(tokens, __builtin_object_size ((tokens), 0)) : (g_free) (tokens
)
;
918 g_free(buf)(__builtin_object_size ((buf), 0) != ((size_t) - 1)) ? g_free_sized
(buf, __builtin_object_size ((buf), 0)) : (g_free) (buf)
;
919 return 0;
920 }
921
922 array_len = json_get_array_len(&tokens[0]);
923 element = json_get_array_index(&tokens[0], 0);
924
925 for (int i = 0; i < array_len; i++, element = json_get_next_object(element)) {
926 char *name, *filter_str, *fg_str, *bg_str;
927 bool_Bool enabled = true1;
928 color_t fg_color = { 0, 0, 0 }, bg_color = { 0, 0, 0 };
929 dfilter_t *temp_dfilter = NULL((void*)0);
930 df_error_t *df_err = NULL((void*)0);
931 const char *filter_exp;
932 color_filter_t *colorf;
933 jsmntok_t *name_tok, *properties_tok;
934
935 if (element->type != JSMN_OBJECT || element->size != 1) {
936 continue;
937 }
938
939 /* "<filter name>": { ... } */
940 name_tok = element + 1;
941 properties_tok = name_tok + 1;
942 if (name_tok->type != JSMN_STRING || properties_tok->type != JSMN_OBJECT) {
943 continue;
944 }
945
946 name = g_strndup(&buf[name_tok->start], (gsize)(name_tok->end - name_tok->start));
947 if (!json_decode_string_inplace(name)) {
948 g_free(name)(__builtin_object_size ((name), 0) != ((size_t) - 1)) ? g_free_sized
(name, __builtin_object_size ((name), 0)) : (g_free) (name)
;
949 continue;
950 }
951
952 filter_str = json_get_string(buf, properties_tok, "filter");
953 fg_str = json_get_string(buf, properties_tok, "foreground");
954 bg_str = json_get_string(buf, properties_tok, "background");
955 json_get_boolean(buf, properties_tok, "enabled", &enabled);
956
957 hex_str_to_color(fg_str, &fg_color);
958 hex_str_to_color(bg_str, &bg_color);
959
960 filter_exp = filter_str ? filter_str : "";
961
962 if (enabled && !dfilter_compile(filter_exp, &temp_dfilter, &df_err)dfilter_compile_full(filter_exp, &temp_dfilter, &df_err
, (1U << 1)|(1U << 2), __func__)
) {
963 report_warning("Disabling color filter: Could not compile \"%s\" in coloring rules file \"%s\".\n%s",
964 name, path, df_err->msg);
965 df_error_free(&df_err);
966 enabled = false0;
967 }
968
969 colorf = color_filter_new(name, filter_exp, &bg_color, &fg_color, enabled);
970 g_free(name)(__builtin_object_size ((name), 0) != ((size_t) - 1)) ? g_free_sized
(name, __builtin_object_size ((name), 0)) : (g_free) (name)
;
971
972 if (user_data == &color_filter_list) {
973 GSList **cfl = (GSList **)user_data;
974
975 /* internal call */
976 colorf->c_colorfilter = temp_dfilter;
977 *cfl = g_slist_append(*cfl, colorf);
978 } else {
979 /* external call */
980 /* just editing, don't need the compiled filter */
981 dfilter_free(temp_dfilter);
982 add_cb(colorf, user_data);
983 }
984 }
985
986 g_free(tokens)(__builtin_object_size ((tokens), 0) != ((size_t) - 1)) ? g_free_sized
(tokens, __builtin_object_size ((tokens), 0)) : (g_free) (tokens
)
;
987 g_free(buf)(__builtin_object_size ((buf), 0) != ((size_t) - 1)) ? g_free_sized
(buf, __builtin_object_size ((buf), 0)) : (g_free) (buf)
;
988 return 0;
989}
990
991/* Read legacy (@-delimited) filters from the given file */
992/* XXX At what point do we drop support? */
993/* XXX Would it make more sense to use GStrings here instead of reallocing
994 our buffers? */
995static int
996read_legacy_filters_file(const char *path, FILE *f, void *user_data, color_filter_add_cb_func add_cb)
997{
998#define INIT_BUF_SIZE128 128
999 char *name;
1000 char *filter_exp;
1001 uint32_t name_len = INIT_BUF_SIZE128;
1002 uint32_t filter_exp_len = INIT_BUF_SIZE128;
1003 uint32_t i = 0;
1004 int c;
1005 uint16_t fg_r, fg_g, fg_b, bg_r, bg_g, bg_b;
1006 bool_Bool disabled = false0;
1007 bool_Bool skip_end_of_line = false0;
1008 int ret = 0;
1009
1010 name = (char *)g_malloc(name_len + 1);
1011 filter_exp = (char *)g_malloc(filter_exp_len + 1);
1012
1013 while (1) {
3
Loop condition is true. Entering loop body
37
Loop condition is true. Entering loop body
1014
1015 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
1016 do {
41
Loop condition is false. Exiting loop
1017 c = ws_getc_unlockedgetc_unlocked(f);
1018 } while (c != EOF(-1) && c != '\n');
39
Assuming the condition is true
40
Assuming the condition is false
1019 if (c == EOF(-1))
42
Taking false branch
1020 break;
1021 disabled = false0;
1022 skip_end_of_line = false0;
1023 }
1024
1025 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 1031
43
Assuming the condition is true
44
Loop condition is false. Execution continues on line 1031
1026 if (c == '\n') {
1027 continue;
1028 }
1029 }
1030
1031 if (c == EOF(-1))
6
Taking false branch
45
Taking false branch
1032 break;
1033
1034 if (c == '!') {
7
Assuming the condition is false
8
Taking false branch
46
Assuming the condition is false
47
Taking false branch
1035 disabled = true1;
1036 continue;
1037 }
1038
1039 /* skip # comments and invalid lines */
1040 if (c != '@') {
9
Assuming the condition is false
10
Taking false branch
48
Assuming the condition is false
49
Taking false branch
1041 skip_end_of_line = true1;
1042 continue;
1043 }
1044
1045 /* we get the @ delimiter.
1046 * Format is:
1047 * @name@filter expression@[background r,g,b][foreground r,g,b]
1048 */
1049
1050 /* retrieve name */
1051 i = 0;
1052 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
1053 c = ws_getc_unlockedgetc_unlocked(f);
1054 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
1055 break;
20
Execution continues on line 1063
59
Execution continues on line 1063
1056 if (i
14.1
'i' is < 'name_len'
53.1
'i' is < 'name_len'
>= name_len) {
15
Taking false branch
54
Taking false branch
1057 /* buffer isn't long enough; double its length.*/
1058 name_len *= 2;
1059 name = (char *)g_realloc(name, name_len + 1);
1060 }
1061 name[i++] = c;
1062 }
1063 name[i] = '\0';
1064
1065 if (c == EOF(-1)) {
21
Taking false branch
60
Taking false branch
1066 break;
1067 } 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
1068 skip_end_of_line = true1;
1069 continue;
1070 }
1071
1072 /* retrieve filter expression */
1073 i = 0;
1074 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
1075 c = ws_getc_unlockedgetc_unlocked(f);
1076 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
1077 break;
32
Execution continues on line 1085
71
Execution continues on line 1085
1078 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
1079 /* buffer isn't long enough; double its length.*/
1080 filter_exp_len *= 2;
1081 filter_exp = (char *)g_realloc(filter_exp, filter_exp_len + 1);
1082 }
1083 filter_exp[i++] = c;
1084 }
1085 filter_exp[i] = '\0';
1086
1087 if (c == EOF(-1)) {
33
Taking false branch
72
Taking false branch
1088 break;
1089 } 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
1090 skip_end_of_line = true1;
1091 continue;
1092 }
1093
1094 /* retrieve background and foreground colors */
1095 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
1096 &bg_r, &bg_g, &bg_b, &fg_r, &fg_g, &fg_b) == 6) {
1097
1098 /* we got a complete color filter */
1099
1100 color_t bg_color, fg_color;
1101 color_filter_t *colorf;
1102 dfilter_t *temp_dfilter = NULL((void*)0);
1103 df_error_t *df_err = NULL((void*)0);
1104
1105 if (!disabled && !dfilter_compile(filter_exp, &temp_dfilter, &df_err)dfilter_compile_full(filter_exp, &temp_dfilter, &df_err
, (1U << 1)|(1U << 2), __func__)
) {
1106 report_warning("Disabling color filter: Could not compile \"%s\" in colorfilters file \"%s\".\n%s", name, path, df_err->msg);
1107 df_error_free(&df_err);
1108
1109 /* skip_end_of_line = true; */
1110 disabled = true1;
1111 }
1112
1113 fg_color.red = fg_r;
1114 fg_color.green = fg_g;
1115 fg_color.blue = fg_b;
1116
1117 bg_color.red = bg_r;
1118 bg_color.green = bg_g;
1119 bg_color.blue = bg_b;
1120
1121 colorf = color_filter_new(name, filter_exp, &bg_color,
1122 &fg_color, !disabled);
1123 if(user_data == &color_filter_list) {
1124 GSList **cfl = (GSList **)user_data;
1125
1126 /* internal call */
1127 colorf->c_colorfilter = temp_dfilter;
1128 *cfl = g_slist_append(*cfl, colorf);
1129 } else {
1130 /* external call */
1131 /* just editing, don't need the compiled filter */
1132 dfilter_free(temp_dfilter);
1133 add_cb(colorf, user_data);
1134 }
1135 } /* if sscanf */
1136
1137 skip_end_of_line = true1;
1138 }
1139
1140 if (ferror(f))
1141 ret = errno(*__errno_location ());
1142
1143 g_free(name)(__builtin_object_size ((name), 0) != ((size_t) - 1)) ? g_free_sized
(name, __builtin_object_size ((name), 0)) : (g_free) (name)
;
1144 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)
;
1145 return ret;
1146}
1147
1148/* read filters from the filter file */
1149bool_Bool
1150color_filters_read_globals(void *user_data, char** err_msg, color_filter_add_cb_func add_cb, const char* app_env_var_prefix)
1151{
1152 char *path;
1153 FILE *f;
1154 int ret;
1155
1156 /*
1157 * Try to get the global filters.
1158 *
1159 * Get the path for the file that would have the global filters, and
1160 * try to open it.
1161 */
1162 path = get_datafile_path(COLORING_RULES_FILE_NAME"coloring_rules.jsonc", app_env_var_prefix);
1163 if ((f = ws_fopenfopen(path, "r")) == NULL((void*)0)) {
1164 if (errno(*__errno_location ()) != ENOENT2) {
1165 /* Error trying to open the file; give up. */
1166 *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 ())))
1167 g_strerror(errno))wmem_strdup_printf(((void*)0), "Could not open global coloring rules file\n\"%s\": %s."
, path, g_strerror((*__errno_location ())))
;
1168 g_free(path)(__builtin_object_size ((path), 0) != ((size_t) - 1)) ? g_free_sized
(path, __builtin_object_size ((path), 0)) : (g_free) (path)
;
1169 return false0;
1170 }
1171
1172 /*
1173 * There is no global rules file; treat that as equivalent to
1174 * that file existing bug being empty, and say we succeeded.
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 /* Don't try to read the legacy colorfilters file, unlike the personal config. */
1181
1182 ret = read_filters_file(path, f, user_data, add_cb, true1);
1183 fclose(f);
1184 if (ret != 0) {
1185 *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 ())))
1186 path, g_strerror(errno))wmem_strdup_printf(((void*)0), "Error reading global coloring rules file\n\"%s\": %s."
, path, g_strerror((*__errno_location ())))
;
1187 g_free(path)(__builtin_object_size ((path), 0) != ((size_t) - 1)) ? g_free_sized
(path, __builtin_object_size ((path), 0)) : (g_free) (path)
;
1188 return false0;
1189 }
1190
1191 g_free(path)(__builtin_object_size ((path), 0) != ((size_t) - 1)) ? g_free_sized
(path, __builtin_object_size ((path), 0)) : (g_free) (path)
;
1192 return true1;
1193}
1194
1195/* read filters from some other filter file (import) */
1196bool_Bool
1197color_filters_import(const char *path, void *user_data, char **err_msg, color_filter_add_cb_func add_cb)
1198{
1199 FILE *f;
1200 int ret;
1201
1202 if ((f = ws_fopenfopen(path, "r")) == NULL((void*)0)) {
1
Taking false branch
1203 *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 ())))
1204 path, g_strerror(errno))wmem_strdup_printf(((void*)0), "Could not open filter file\n%s\nfor reading: %s."
, path, g_strerror((*__errno_location ())))
;
1205 return false0;
1206 }
1207
1208 /*
1209 * The file being imported might be in the legacy @-delimited
1210 * format or the newer JSONC format. Try the legacy format first,
1211 * then the JSONC format. Each parser simply finds no filters if
1212 * the file isn't in the expected format.
1213 */
1214 ret = read_legacy_filters_file(path, f, user_data, add_cb);
2
Calling 'read_legacy_filters_file'
1215 if (ret != 0) {
1216 *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 ())))
1217 path, g_strerror(errno))wmem_strdup_printf(((void*)0), "Error reading filter file\n\"%s\": %s."
, path, g_strerror((*__errno_location ())))
;
1218 fclose(f);
1219 return false0;
1220 }
1221
1222 /* read_filters_file() seeks to the start of the file. */
1223 ret = read_filters_file(path, f, user_data, add_cb, false0);
1224 fclose(f);
1225 if (ret != 0) {
1226 *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 ())))
1227 path, g_strerror(errno))wmem_strdup_printf(((void*)0), "Error reading filter file\n\"%s\": %s."
, path, g_strerror((*__errno_location ())))
;
1228 return false0;
1229 }
1230
1231 return true1;
1232}
1233
1234struct write_filter_data
1235{
1236 FILE *f;
1237 bool_Bool only_selected;
1238};
1239
1240/* save a single filter, in the legacy @-delimited format */
1241static void
1242write_legacy_filter(void *filter_arg, void *data_arg)
1243{
1244 struct write_filter_data *data = (struct write_filter_data *)data_arg;
1245 color_filter_t *colorf = (color_filter_t *)filter_arg;
1246 FILE *f = data->f;
1247
1248 if ( (!data->only_selected) &&
1249 (strstr(colorf->filter_name,CONVERSATION_COLOR_PREFIX"___conversation_color_filter___")==NULL((void*)0)) ) {
1250 fprintf(f,"%s@%s@%s@[%u,%u,%u][%u,%u,%u]\n",
1251 colorf->enabled ? "" : "!",
1252 colorf->filter_name,
1253 colorf->filter_text,
1254 colorf->bg_color.red,
1255 colorf->bg_color.green,
1256 colorf->bg_color.blue,
1257 colorf->fg_color.red,
1258 colorf->fg_color.green,
1259 colorf->fg_color.blue);
1260 }
1261}
1262
1263/* save filters in a filter file, in the legacy @-delimited format */
1264static bool_Bool
1265write_legacy_filters_file(GSList *cfl, FILE *f, bool_Bool only_selected, const char* app_name)
1266{
1267 struct write_filter_data data;
1268
1269 data.f = f;
1270 data.only_selected = only_selected;
1271
1272 fprintf(f,"# This file was created by %s. Edit with care.\n", app_name);
1273 g_slist_foreach(cfl, write_legacy_filter, &data);
1274 return true1;
1275}
1276
1277struct write_filter_jsonc_data
1278{
1279 json_dumper *dumper;
1280 bool_Bool only_selected;
1281};
1282
1283/* save a single filter, in JSONC format */
1284static void
1285write_jsonc_filter(void *filter_arg, void *data_arg)
1286{
1287 struct write_filter_jsonc_data *data = (struct write_filter_jsonc_data *)data_arg;
1288 color_filter_t *colorf = (color_filter_t *)filter_arg;
1289 json_dumper *dumper = data->dumper;
1290 char *fg_str, *bg_str;
1291
1292 if ( (!data->only_selected) &&
1293 (strstr(colorf->filter_name,CONVERSATION_COLOR_PREFIX"___conversation_color_filter___")==NULL((void*)0)) ) {
1294 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))
;
1295 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))
;
1296
1297 json_dumper_begin_object(dumper);
1298 json_dumper_set_member_name(dumper, colorf->filter_name);
1299 json_dumper_begin_object(dumper);
1300 json_dumper_set_member_name_const(dumper, "filter")json_dumper_set_member_name_noesc(dumper, "filter", sizeof("filter"
) - 1)
;
1301 json_dumper_value_string(dumper, colorf->filter_text);
1302 json_dumper_set_member_name_const(dumper, "foreground")json_dumper_set_member_name_noesc(dumper, "foreground", sizeof
("foreground") - 1)
;
1303 json_dumper_value_string(dumper, fg_str);
1304 json_dumper_set_member_name_const(dumper, "background")json_dumper_set_member_name_noesc(dumper, "background", sizeof
("background") - 1)
;
1305 json_dumper_value_string(dumper, bg_str);
1306 json_dumper_set_member_name_const(dumper, "enabled")json_dumper_set_member_name_noesc(dumper, "enabled", sizeof("enabled"
) - 1)
;
1307 json_dumper_value_anyf(dumper, "%s", colorf->enabled ? "true" : "false");
1308 json_dumper_end_object(dumper);
1309 json_dumper_end_object(dumper);
1310
1311 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
)
;
1312 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
)
;
1313 }
1314}
1315
1316/* save filters in a coloring rules file, in JSONC format */
1317static bool_Bool
1318write_filters_file(GSList *cfl, FILE *f, bool_Bool only_selected, const char* app_name)
1319{
1320 struct write_filter_jsonc_data data;
1321 json_dumper dumper = {
1322 .output_file = f,
1323 .flags = JSON_DUMPER_FLAGS_PRETTY_PRINT(1 << 0),
1324 };
1325
1326 data.dumper = &dumper;
1327 data.only_selected = only_selected;
1328
1329 fprintf(f, "// This file was created by %s. Edit with care.\n", app_name);
1330
1331 json_dumper_begin_array(&dumper);
1332 g_slist_foreach(cfl, write_jsonc_filter, &data);
1333 json_dumper_end_array(&dumper);
1334
1335 return json_dumper_finish(&dumper);
1336}
1337
1338/* save filters in users filter file(s) */
1339bool_Bool
1340color_filters_write(GSList *cfl, const char* app_name, const char* app_env_var_prefix, char** err_msg)
1341{
1342 char *pf_dir_path;
1343 char *path;
1344 FILE *f;
1345
1346 /* Create the directory that holds personal configuration files,
1347 if necessary. */
1348 if (create_persconffile_dir(app_env_var_prefix, &pf_dir_path) == -1) {
1349 *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 ())))
1350 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 ())))
;
1351 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)
;
1352 return false0;
1353 }
1354
1355 /*
1356 * Keep an existing legacy colorfilters file current for older versions,
1357 * which read only that file. Never create it.
1358 */
1359 path = get_persconffile_path(COLORFILTERS_FILE_NAME"colorfilters", true1, app_env_var_prefix);
1360 if (g_file_test(path, G_FILE_TEST_EXISTS)) {
1361 if ((f = ws_fopenfopen(path, "w+")) == NULL((void*)0)) {
1362 *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 ())))
1363 path, g_strerror(errno))wmem_strdup_printf(((void*)0), "Could not open\n%s\nfor writing: %s."
, path, g_strerror((*__errno_location ())))
;
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 false0;
1366 }
1367 write_legacy_filters_file(cfl, f, false0, app_name);
1368 fclose(f);
1369 }
1370 g_free(path)(__builtin_object_size ((path), 0) != ((size_t) - 1)) ? g_free_sized
(path, __builtin_object_size ((path), 0)) : (g_free) (path)
;
1371
1372 path = get_persconffile_path(COLORING_RULES_FILE_NAME"coloring_rules.jsonc", true1, app_env_var_prefix);
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 g_free(path)(__builtin_object_size ((path), 0) != ((size_t) - 1)) ? g_free_sized
(path, __builtin_object_size ((path), 0)) : (g_free) (path)
;
1377 return false0;
1378 }
1379 write_filters_file(cfl, f, false0, app_name);
1380 fclose(f);
1381 g_free(path)(__builtin_object_size ((path), 0) != ((size_t) - 1)) ? g_free_sized
(path, __builtin_object_size ((path), 0)) : (g_free) (path)
;
1382 return true1;
1383}
1384
1385/* Save filters in the specified file (export) */
1386bool_Bool
1387color_filters_export(const char *path, GSList *cfl, bool_Bool only_marked, const char* app_name, char** err_msg)
1388{
1389 FILE *f;
1390
1391 if ((f = ws_fopenfopen(path, "w+")) == NULL((void*)0)) {
1392 *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 ())))
1393 path, g_strerror(errno))wmem_strdup_printf(((void*)0), "Could not open\n%s\nfor writing: %s."
, path, g_strerror((*__errno_location ())))
;
1394 return false0;
1395 }
1396 write_filters_file(cfl, f, only_marked, app_name);
1397 fclose(f);
1398 return true1;
1399}
1400
1401/*
1402 * Editor modelines - https://www.wireshark.org/tools/modelines.html
1403 *
1404 * Local variables:
1405 * c-basic-offset: 4
1406 * tab-width: 8
1407 * indent-tabs-mode: nil
1408 * End:
1409 *
1410 * vi: set shiftwidth=4 tabstop=8 expandtab:
1411 * :indentSize=4:tabSize=8:noTabs=true:
1412 */