Coverage Report

Created: 2026-08-14 06:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/epan/funnel.c
Line
Count
Source
1
/*
2
 *  funnel.c
3
 *
4
 * EPAN's GUI mini-API
5
 *
6
 * (c) 2006, Luis E. Garcia Ontanon <luis@ontanon.org>
7
 *
8
 * Wireshark - Network traffic analyzer
9
 * By Gerald Combs <gerald@wireshark.org>
10
 * Copyright 1998 Gerald Combs
11
 *
12
 * SPDX-License-Identifier: GPL-2.0-or-later
13
 */
14
15
#include "config.h"
16
17
#include <epan/funnel.h>
18
#include <wsutil/glib-compat.h>
19
20
typedef struct _funnel_menu_t {
21
    char *name;
22
    register_stat_group_t group;
23
    funnel_menu_callback callback;
24
    void *callback_data;
25
    funnel_menu_callback_data_free callback_data_free;
26
    bool retap;
27
} funnel_menu_t;
28
29
typedef struct _console_menu {
30
    char *name;
31
    funnel_console_eval_cb_t eval_cb;
32
    funnel_console_open_cb_t open_cb;
33
    funnel_console_close_cb_t close_cb;
34
    void *user_data;
35
    funnel_console_data_free_cb_t data_free_cb;
36
} funnel_console_menu_t;
37
38
/**
39
 * Represents a single packet menu entry and callback
40
 */
41
typedef struct _funnel_packet_menu_t {
42
    char *name;                           /**< Name to display in the GUI */
43
    char *required_fields;                /**< comma-separated list of fields
44
                                               that must be present for the
45
                                               packet menu to be displayed */
46
    funnel_packet_menu_callback callback; /**< Lua function to be called on
47
                                               menu item selection. */
48
    void *callback_data;                  /**< Lua state for the callback
49
                                               function */
50
    bool retap;                           /**< Whether or not to rescan the
51
                                               capture file's packets */
52
} funnel_packet_menu_t;
53
54
55
/* XXX This assumes one main window and one capture file. */
56
static const funnel_ops_t* ops;
57
static GSList* registered_menus;
58
static GSList* added_menus;
59
static GSList* removed_menus;
60
static bool menus_registered;
61
62
/*
63
 * List of all registered funnel_packet_menu_t's
64
 */
65
static GSList* registered_packet_menus;
66
static GSList* registered_console_menus;
67
68
/*
69
 * true if the packet menus were modified since the last registration
70
 */
71
static bool packet_menus_modified;
72
73
static void free_funnel_packet_menu(gpointer data, gpointer user_data);
74
75
const funnel_ops_t* funnel_get_funnel_ops(void)
76
0
{
77
0
    return ops;
78
0
}
79
80
void funnel_set_funnel_ops(const funnel_ops_t *o)
81
0
{
82
0
    ops = o;
83
0
}
84
85
static void free_funnel_menu(gpointer data, gpointer user_data _U_)
86
0
{
87
0
    funnel_menu_t* m = (funnel_menu_t*)data;
88
89
0
    if (m->callback_data_free) {
90
0
        m->callback_data_free(m->callback_data);
91
0
    }
92
0
    g_free(m->name);
93
0
    g_free(m);
94
0
}
95
96
static void funnel_remove_menu(GSList** menu_list, funnel_menu_t *menu)
97
0
{
98
0
    GSList* current = *menu_list;
99
100
0
    while (current != NULL) {
101
0
        GSList* next = current->next; // Store the next pointer BEFORE potentially removing current
102
0
        funnel_menu_t* m = (funnel_menu_t*)current->data;
103
0
        if (m->callback == menu->callback) {
104
0
            *menu_list = g_slist_remove(*menu_list, m);
105
0
            free_funnel_menu(m, NULL);
106
0
        }
107
108
0
        current = next; // Move to the stored next pointer
109
0
    }
110
0
}
111
112
static void funnel_clear_menu(GSList** menu_list, GFunc free_func)
113
0
{
114
0
    g_slist_foreach(*menu_list, free_func, NULL);
115
0
    g_slist_free(*menu_list);
116
117
0
    *menu_list = NULL;
118
0
}
119
120
void funnel_register_menu(const char *name,
121
                          register_stat_group_t group,
122
                          funnel_menu_callback callback,
123
                          void *callback_data,
124
                          funnel_menu_callback_data_free callback_data_free,
125
                          bool retap)
126
0
{
127
0
    funnel_menu_t* m = g_new(funnel_menu_t, 1);
128
129
0
    m->name = g_strdup(name);
130
0
    m->group = group;
131
0
    m->callback = callback;
132
0
    m->callback_data = callback_data;
133
0
    m->callback_data_free = callback_data_free;
134
0
    m->retap = retap;
135
136
0
    registered_menus = g_slist_append(registered_menus, m);
137
138
0
    if (menus_registered) {
139
0
        funnel_menu_t* m_r = (funnel_menu_t *)g_memdup2(m, sizeof *m);
140
0
        m_r->name = g_strdup(name);
141
        /* This temporary copy shares the callback data; do not free it. */
142
0
        m_r->callback_data_free = NULL;
143
0
        added_menus = g_slist_append(added_menus, m_r);
144
0
    }
145
0
}
146
147
void funnel_deregister_menus(funnel_menu_callback callback)
148
0
{
149
0
    funnel_menu_t* m = g_new0(funnel_menu_t, 1);
150
151
0
    m->callback = callback;
152
153
0
    funnel_remove_menu(&registered_menus, m);
154
0
    removed_menus = g_slist_append(removed_menus, m);
155
156
    // Clear and free memory of packet menus
157
0
    funnel_clear_menu(&registered_packet_menus, free_funnel_packet_menu);
158
0
    packet_menus_modified = true;
159
0
}
160
161
static void funnel_register_all_menus(funnel_registration_cb_t r_cb)
162
0
{
163
0
    if (r_cb == NULL) {
164
0
        return;
165
0
    }
166
167
0
    for (GSList* l = registered_menus; l; l = l->next) {
168
0
        funnel_menu_t* c = (funnel_menu_t*)l->data;
169
0
        r_cb(c->name,c->group,c->callback,c->callback_data,c->retap);
170
0
    }
171
0
}
172
173
void funnel_reload_menus(funnel_deregistration_cb_t d_cb,
174
                         funnel_registration_cb_t r_cb)
175
0
{
176
0
    for (GSList* l = removed_menus; l; l = l->next) {
177
0
        funnel_menu_t* c = (funnel_menu_t*)l->data;
178
0
        d_cb(c->callback);
179
0
    }
180
181
0
    for (GSList* l = added_menus; l; l = l->next) {
182
0
        funnel_menu_t* c = (funnel_menu_t*)l->data;
183
0
        r_cb(c->name,c->group,c->callback,c->callback_data,c->retap);
184
0
    }
185
186
0
    funnel_clear_menu(&removed_menus, free_funnel_menu);
187
0
    funnel_clear_menu(&added_menus, free_funnel_menu);
188
0
}
189
190
/**
191
 * Entry point for Lua code to register a packet menu
192
 *
193
 * Stores the menu name and callback from the Lua code
194
 * into registered_packet_menus so that the
195
 * Wireshark GUI code can retrieve it with
196
 * funnel_register_all_packet_menus().
197
 */
198
void funnel_register_packet_menu(const char *name,
199
                                 const char *required_fields,
200
                                 funnel_packet_menu_callback callback,
201
                                 void *callback_data,
202
                                 bool retap)
203
0
{
204
0
    funnel_packet_menu_t* m = g_new0(funnel_packet_menu_t, 1);
205
206
0
    m->name = g_strdup(name);
207
0
    m->required_fields = g_strdup(required_fields);
208
0
    m->callback = callback;
209
0
    m->callback_data = callback_data;
210
0
    m->retap = retap;
211
212
0
    registered_packet_menus = g_slist_append(registered_packet_menus, m);
213
214
0
    packet_menus_modified = true;
215
0
}
216
217
static void free_funnel_packet_menu(gpointer data, gpointer user_data _U_)
218
0
{
219
0
    funnel_packet_menu_t* m = (funnel_packet_menu_t*)data;
220
221
0
    g_free(m->name);
222
0
    g_free(m->required_fields);
223
0
    if (m->callback_data) {
224
0
        g_free(m->callback_data);
225
0
    }
226
0
    g_free(m);
227
0
}
228
229
/**
230
 * Entry point for Wireshark GUI to obtain all registered packet menus
231
 *
232
 * Calls the supplied callback for each packet menu registered with
233
 * funnel_register_packet_menu().
234
 *
235
 * @param r_cb the callback function to call with each registered packet menu
236
 */
237
void funnel_register_all_packet_menus(funnel_registration_packet_cb_t r_cb)
238
0
{
239
0
    for (GSList* l = registered_packet_menus; l; l = l->next) {
240
0
        funnel_packet_menu_t* c = (funnel_packet_menu_t*)l->data;
241
0
        r_cb(c->name, c->required_fields, c->callback, c->callback_data, c->retap);
242
0
    }
243
244
0
    packet_menus_modified = false;
245
0
}
246
247
/**
248
 * Returns whether the packet menus have been modified since they were last registered
249
 *
250
 * @return true if the packet menus were modified since the last registration
251
 */
252
bool funnel_packet_menus_modified(void)
253
0
{
254
0
    return packet_menus_modified;
255
0
}
256
257
/**
258
 * Entry point for code to register a console menu
259
 */
260
void funnel_register_console_menu(const char *name,
261
                                  funnel_console_eval_cb_t eval_cb,
262
                                  funnel_console_open_cb_t open_cb,
263
                                  funnel_console_close_cb_t close_cb,
264
                                  void *callback_data,
265
                                  funnel_console_data_free_cb_t free_data)
266
0
{
267
0
    funnel_console_menu_t* m = g_new0(funnel_console_menu_t, 1);
268
269
0
    m->name = g_strdup(name);
270
0
    m->eval_cb = eval_cb;
271
0
    m->open_cb = open_cb;
272
0
    m->close_cb = close_cb;
273
0
    m->user_data = callback_data;
274
0
    m->data_free_cb = free_data;
275
276
0
    registered_console_menus = g_slist_prepend(registered_console_menus, m);
277
0
}
278
279
static void funnel_register_all_console_menus(funnel_registration_console_cb_t r_cb)
280
0
{
281
0
    if (r_cb == NULL) {
282
0
        return;
283
0
    }
284
285
0
    for (GSList* l = registered_console_menus; l != NULL; l = l->next) {
286
0
        funnel_console_menu_t *m = l->data;
287
0
        r_cb(m->name, m->eval_cb, m->open_cb, m->close_cb, m->user_data);
288
0
    }
289
0
}
290
291
static void free_funnel_console_menu(gpointer data, gpointer user_data _U_)
292
0
{
293
0
    funnel_console_menu_t* m = data;
294
295
0
    g_free(m->name);
296
0
    if (m->data_free_cb) {
297
0
        m->data_free_cb(m->user_data);
298
0
    }
299
0
    g_free(m);
300
0
}
301
302
void funnel_ops_init(funnel_registration_cb_t r_cb, funnel_registration_console_cb_t rconsole_cb)
303
0
{
304
0
    funnel_register_all_menus(r_cb);
305
0
    funnel_register_all_console_menus(rconsole_cb);
306
0
    menus_registered = true;
307
0
}
308
309
bool funnel_menu_registered(void)
310
0
{
311
0
    return menus_registered;
312
0
}
313
314
void funnel_cleanup(void)
315
0
{
316
0
    funnel_clear_menu(&registered_menus, free_funnel_menu);
317
0
    funnel_clear_menu(&registered_packet_menus, free_funnel_packet_menu);
318
0
    funnel_clear_menu(&registered_console_menus, free_funnel_console_menu);
319
0
}
320
321
/*
322
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
323
 *
324
 * Local variables:
325
 * c-basic-offset: 4
326
 * tab-width: 8
327
 * indent-tabs-mode: nil
328
 * End:
329
 *
330
 * vi: set shiftwidth=4 tabstop=8 expandtab:
331
 * :indentSize=4:tabSize=8:noTabs=true:
332
 */