### Bug Summary

File: | builds/wireshark/wireshark/plugins/epan/mate/mate_util.c
Warning: | line 1522, column 22 File position of the stream might be 'indeterminate' after a failed operation. Can cause undefined behavior

### Annotated Source Code

Press '?' to see keyboard shortcuts

Show analyzer invocation
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 mate_util.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-21/lib/clang/21 -isystem /usr/include/glib-2.0 -isystem /usr/lib/x86_64-linux-gnu/glib-2.0/include -D G_DISABLE_DEPRECATED -D G_DISABLE_SINGLE_INCLUDES -D PLUGIN_VERSION="1.0.1" -D WS_DEBUG -D WS_DEBUG_UTF_8 -D mate_EXPORTS -I /builds/wireshark/wireshark/build -I /builds/wireshark/wireshark -I /builds/wireshark/wireshark/include -I /builds/wireshark/wireshark/plugins/epan/mate -I /builds/wireshark/wireshark/build/plugins/epan/mate -D _GLIBCXX_ASSERTIONS -internal-isystem /usr/lib/llvm-21/lib/clang/21/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../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=gnu11 -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 -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /builds/wireshark/wireshark/sbout/2026-05-06-100446-3641-1 -x c /builds/wireshark/wireshark/plugins/epan/mate/mate_util.c

Keyboard shortcuts:

- Use 'j/k' keys for keyboard navigation
- Use 'Shift+S' to show/hide relevant lines
- Use '?' to toggle this window

Close
Show only relevant lines Show control flow arrows

1 | /* mate_util.c
2 | * MATE -- Meta Analysis Tracing Engine
3 | * Utility Library: Single Copy Strings and Attribute Value Pairs
4 | *
5 | * Copyright 2004, Luis E. Garcia Ontanon <luis@ontanon.org>
6 | *
7 | * Wireshark - Network traffic analyzer
8 | * By Gerald Combs <gerald@wireshark.org>
9 | * Copyright 1998 Gerald Combs
10 | *
11 | * SPDX-License-Identifier: GPL-2.0-or-later
12 | */
13 |
14 | #include "config.h"
15 |
16 | #include "mate.h"
17 | #include "mate_util.h"
18 |
19 | #include <errno(*__errno_location ()).h>
20 | #include <wsutil/file_util.h>
21 |
22 |
23 | /***************************************************************************
24 | * dbg_print
25 | ***************************************************************************
26 | * This is the debug facility of the thing.
27 | ***************************************************************************/
28 |
29 | /* dbg_print:
30 | * which: a pointer to the current level of debugging for a feature
31 | * how: the level over which this message should be printed out
32 | * where: the file on which to print (ws_message if null)
33 | * fmt, ...: what to print
34 | */
35 |
36 | void dbg_print(const int* which, int how, FILE* where, const char* fmt, ... ) {
37 | static char debug_buffer[DEBUG_BUFFER_SIZE4096];
38 | va_list list;
39 |
40 | if ( ! which || *which < how ) return;
41 |
42 | va_start( list, fmt )__builtin_va_start(list, fmt);
43 | vsnprintf(debug_buffer,DEBUG_BUFFER_SIZE4096,fmt,list);
44 | va_end( list )__builtin_va_end(list);
45 |
46 | if (! where) {
47 | ws_message("%s", debug_buffer)do { if (1) { ws_log_full("MATE", LOG_LEVEL_MESSAGE, ((void*) 0), -1, ((void*)0), "%s", debug_buffer); } } while (0);
48 | } else {
49 | fputs(debug_buffer,where);
50 | fputs("\n",where);
51 | }
52 |
53 | }
54 |
55 | /***************************************************************************
56 | * single copy strings
57 | ***************************************************************************
58 | * Strings repeat more often than don't. In order to save memory
59 | * we'll keep only one copy of each as key to a hash with a count of
60 | * subscribers as value. Each string is truncated to be no more than
61 | * SCS_HUGE_SIZE.
62 | * XXX - GLib 2.58 adds reference counted strings, use that?
63 | ***************************************************************************/
64 |
65 | /**
66 | * scs_init:
67 | *
68 | * Initializes the scs hash.
69 | **/
70 |
71 | struct _scs_collection {
72 | GHashTable* hash; /* key: a string value: unsigned number of subscribers */
73 | };
74 |
75 | /*
76 | * XXX: AFAICT destroy_scs_collection() might be called only when reading a
77 | * mate config file. Since reading a new config file can apparently currently
78 | * only be done once after starting Wireshark, in theory this fcn
79 | * currently should never be called since there will never be an existing
80 | * scs_collection to be destroyed.
81 | */
82 | static void destroy_scs_collection(SCS_collection* c) {
83 | if (c->hash) g_hash_table_destroy(c->hash);
84 | }
85 |
86 | /* The same djb2 hash used in g_str_hash, except that it stops after
87 | * SCS_HUGE_SIZE bytes. */
88 | static unsigned scs_hash(const void *v)
89 | {
90 | const signed char *p;
91 | uint32_t h = 5381;
92 | size_t i = 0;
93 |
94 | for (p = v; *p != '\0' && i++ < SCS_HUGE_SIZE65535; p++) {
95 | h = (h << 5) + h + *p;
96 | }
97 |
98 | return h;
99 | }
100 |
101 | /* Compare the first SCS_HUGE_SIZE bytes for equality. */
102 | static gboolean scs_equal(const void *a, const void *b)
103 | {
104 | const char *str1 = a;
105 | const char *str2 = b;
106 |
107 | return strncmp(str1, str2, SCS_HUGE_SIZE65535) == 0;
108 | }
109 |
110 | static SCS_collection* scs_init(void) {
111 | SCS_collection* c = g_new(SCS_collection, 1)((SCS_collection *) g_malloc_n ((1), sizeof (SCS_collection)) );
112 |
113 | c->hash = g_hash_table_new_full(scs_hash, scs_equal, g_free, g_free);
114 |
115 | return c;
116 | }
117 |
118 |
119 | /**
120 | * subscribe:
121 | * @param c the scs hash
122 | * @param s a string
123 | *
124 | * Checks if the given string exists already and if so it increases the count of
125 | * subscribers and returns a pointer to the stored string. If not it will copy
126 | * the given string, store it in the hash, and return the pointer to the copy.
127 | * Remember, containment is handled internally, take care of your own strings.
128 | *
129 | * Return value: a pointer to the subscribed string.
130 | **/
131 | char* scs_subscribe(SCS_collection* c, const char* s) {
132 | char* orig = NULL((void*)0);
133 | unsigned* ip = NULL((void*)0);
134 | size_t len = 0;
135 |
136 | if (g_hash_table_lookup_extended(c->hash,(const void *)s,(void * *)&orig,(void * *)&ip)) {
137 | (*ip)++;
138 | } else {
139 | ip = g_new0(unsigned, 1)((unsigned *) g_malloc0_n ((1), sizeof (unsigned)));
140 |
141 | len = strlen(s);
142 |
143 | if(G_LIKELY(len <= SCS_HUGE_SIZE)(len <= 65535)) {
144 | orig = g_strdup(s)g_strdup_inline (s);
145 | } else {
146 | ws_warning("mate SCS: string truncated due to huge size")do { if (1) { ws_log_full("MATE", LOG_LEVEL_WARNING, "plugins/epan/mate/mate_util.c" , 146, __func__, "mate SCS: string truncated due to huge size" ); } } while (0);
147 | orig = g_strndup(s, SCS_HUGE_SIZE65535);
148 | }
149 |
150 | g_hash_table_insert(c->hash, orig, ip);
151 | }
152 |
153 | return orig;
154 | }
155 |
156 | /**
157 | * unsubscribe:
158 | * @param c the scs hash
159 | * @param s a string.
160 | *
161 | * decreases the count of subscribers, if zero frees the internal copy of
162 | * the string.
163 | **/
164 | void scs_unsubscribe(SCS_collection* c, char* s) {
165 | char* orig = NULL((void*)0);
166 | unsigned* ip = NULL((void*)0);
167 |
168 | if (g_hash_table_lookup_extended(c->hash,(const void *)s,(void **)&orig,(void **)&ip)) {
169 | if (*ip == 0) {
170 | g_hash_table_remove(c->hash,orig);
171 | }
172 | else {
173 | (*ip)--;
174 | }
175 | } else {
176 | ws_warning("mate SCS: not subscribed")do { if (1) { ws_log_full("MATE", LOG_LEVEL_WARNING, "plugins/epan/mate/mate_util.c" , 176, __func__, "mate SCS: not subscribed"); } } while (0);
177 | }
178 | }
179 |
180 | /**
181 | * scs_subscribe_printf:
182 | * @param fmt a format string ...
183 | *
184 | * Formats the input and subscribes it.
185 | *
186 | * Return value: the stored copy of the formatted string.
187 | *
188 | **/
189 | char* scs_subscribe_printf(SCS_collection* c, char* fmt, ...) {
190 | va_list list;
191 | char *buf, *ret;
192 |
193 | va_start( list, fmt )__builtin_va_start(list, fmt);
194 | buf = g_strdup_vprintf(fmt, list);
195 | va_end( list )__builtin_va_end(list);
196 |
197 | ret = scs_subscribe(c, buf);
198 | g_free(buf);
199 |
200 | return ret;
201 | }
202 |
203 | /***************************************************************************
204 | * AVPs & Co.
205 | ***************************************************************************
206 | * The Thing operates mainly on avps, avpls and loals
207 | * - attribute value pairs (two strings: the name and the value and an operator)
208 | * - avp lists a somehow sorted list of avps
209 | * - loal (list of avp lists) an arbitrarily sorted list of avpls
210 | *
211 | *
212 | ***************************************************************************/
213 |
214 |
215 | typedef union _any_avp_type {
216 | AVP avp;
217 | AVPN avpn;
218 | AVPL avpl;
219 | LoAL loal;
220 | LoALnode loaln;
221 | } any_avp_type;
222 |
223 |
224 | static SCS_collection* avp_strings;
225 |
226 | #ifdef _AVP_DEBUGGING
227 | static FILE* dbg_fp;
228 |
229 | static int dbg_level;
230 | static int* dbg = &dbg_level;
231 |
232 | static int dbg_avp_level;
233 | static int* dbg_avp = &dbg_avp_level;
234 |
235 | static int dbg_avp_op_level;
236 | static int* dbg_avp_op = &dbg_avp_op_level;
237 |
238 | static int dbg_avpl_level;
239 | static int* dbg_avpl = &dbg_avpl_level;
240 |
241 | static int dbg_avpl_op_level;
242 | static int* dbg_avpl_op = &dbg_avpl_op_level;
243 |
244 | /**
245 | * setup_avp_debug:
246 | * @param fp the file in which to send debugging output.
247 | * @param general a pointer to the level of debugging of facility "general"
248 | * @param avp a pointer to the level of debugging of facility "avp"
249 | * @param avp_op a pointer to the level of debugging of facility "avp_op"
250 | * @param avpl a pointer to the level of debugging of facility "avpl"
251 | * @param avpl_op a pointer to the level of debugging of facility "avpl_op"
252 | *
253 | * If enabled sets up the debug facilities for the avp library.
254 | *
255 | **/
256 | extern void setup_avp_debug(FILE* fp, int* general, int* avp, int* avp_op, int* avpl, int* avpl_op) {
257 | dbg_fp = fp;
258 | dbg = general;
259 | dbg_avp = avp;
260 | dbg_avp_op = avp_op;
261 | dbg_avpl = avpl;
262 | dbg_avpl_op = avpl_op;
263 | }
264 |
265 | #endif /* _AVP_DEBUGGING */
266 |
267 | /**
268 | * avp_init:
269 | *
270 | * (Re)Initializes the avp library.
271 | *
272 | **/
273 | extern void avp_init(void) {
274 |
275 | if (avp_strings) destroy_scs_collection(avp_strings);
276 | avp_strings = scs_init();
277 |
278 | }
279 |
280 | /**
281 | * new_avp_from_finfo:
282 | * @param name the name the avp will have.
283 | * @param finfo the field_info from which to fetch the data.
284 | *
285 | * Creates an avp from a field_info record.
286 | *
287 | * Return value: a pointer to the newly created avp.
288 | *
289 | **/
290 | extern AVP* new_avp_from_finfo(const char* name, field_info* finfo) {
291 | AVP* new_avp_val = (AVP*)g_slice_new(any_avp_type)((any_avp_type*) g_slice_alloc (sizeof (any_avp_type)));
292 | char* value;
293 | char* repr;
294 |
295 | new_avp_val->n = scs_subscribe(avp_strings, name);
296 |
297 | repr = fvalue_to_string_repr(NULL((void*)0), finfo->value, FTREPR_DISPLAY, finfo->hfinfo->display);
298 |
299 | if (repr) {
300 | value = scs_subscribe(avp_strings, repr);
301 | wmem_free(NULL((void*)0), repr);
302 | #ifdef _AVP_DEBUGGING
303 | dbg_print (dbg_avp,2,dbg_fp,"new_avp_from_finfo: from string: %s",value);
304 | #endif
305 | } else {
306 | #ifdef _AVP_DEBUGGING
307 | dbg_print (dbg_avp,2,dbg_fp,"new_avp_from_finfo: a proto: %s",finfo->hfinfo->abbrev);
308 | #endif
309 | value = scs_subscribe(avp_strings, "");
310 | }
311 |
312 | new_avp_val->v = value;
313 |
314 | new_avp_val->o = '=';
315 |
316 | #ifdef _AVP_DEBUGGING
317 | dbg_print (dbg_avp,1,dbg_fp,"new_avp_from_finfo: %p %s%c%s;",new_avp_val,new_avp_val->n,new_avp_val->o,new_avp_val->v);
318 | #endif
319 |
320 | return new_avp_val;
321 | }
322 |
323 |
324 | /**
325 | * new_avp:
326 | * @param name the name the avp will have.
327 | * @param value the value the avp will have.
328 | * @param o the operator of this avp.
329 | *
330 | * Creates an avp given every parameter.
331 | *
332 | * Return value: a pointer to the newly created avp.
333 | *
334 | **/
335 | extern AVP* new_avp(const char* name, const char* value, char o) {
336 | AVP* new_avp_val = (AVP*)g_slice_new(any_avp_type)((any_avp_type*) g_slice_alloc (sizeof (any_avp_type)));
337 |
338 | new_avp_val->n = scs_subscribe(avp_strings, name);
339 | new_avp_val->v = scs_subscribe(avp_strings, value);
340 | new_avp_val->o = o;
341 |
342 | #ifdef _AVP_DEBUGGING
343 | dbg_print(dbg_avp,1,dbg_fp,"new_avp_val: %p %s%c%s;",new_avp_val,new_avp_val->n,new_avp_val->o,new_avp_val->v);
344 | #endif
345 | return new_avp_val;
346 | }
347 |
348 |
349 | /**
350 | * delete_avp:
351 | * @param avp the avp to delete.
352 | *
353 | * Destroys an avp and releases the resources it uses.
354 | *
355 | **/
356 | extern void delete_avp(AVP* avp) {
357 | #ifdef _AVP_DEBUGGING
358 | dbg_print(dbg_avp,1,dbg_fp,"delete_avp: %p %s%c%s;",avp,avp->n,avp->o,avp->v);
359 | #endif
360 |
361 | scs_unsubscribe(avp_strings, avp->n);
362 | scs_unsubscribe(avp_strings, avp->v);
363 | g_slice_free(any_avp_type,(any_avp_type*)avp)do { if (1) g_slice_free1 (sizeof (any_avp_type), ((any_avp_type *)avp)); else (void) ((any_avp_type*) 0 == ((any_avp_type*)avp )); } while (0);
364 | }
365 |
366 |
367 | /**
368 | * avp_copy:
369 | * @param from the avp to be copied.
370 | *
371 | * Creates an avp whose name op and value are copies of the given one.
372 | *
373 | * Return value: a pointer to the newly created avp.
374 | *
375 | **/
376 | extern AVP* avp_copy(AVP* from) {
377 | AVP* new_avp_val = (AVP*)g_slice_new(any_avp_type)((any_avp_type*) g_slice_alloc (sizeof (any_avp_type)));
378 |
379 | new_avp_val->n = scs_subscribe(avp_strings, from->n);
380 | new_avp_val->v = scs_subscribe(avp_strings, from->v);
381 | new_avp_val->o = from->o;
382 |
383 | #ifdef _AVP_DEBUGGING
384 | dbg_print(dbg_avp,1,dbg_fp,"copy_avp: %p %s%c%s;",new_avp_val,new_avp_val->n,new_avp_val->o,new_avp_val->v);
385 | #endif
386 |
387 | return new_avp_val;
388 | }
389 |
390 | /**
391 | * new_avpl:
392 | * @param name the name the avpl will have.
393 | *
394 | * Creates an empty avpl.
395 | *
396 | * Return value: a pointer to the newly created avpl.
397 | *
398 | **/
399 | extern AVPL* new_avpl(const char* name) {
400 | AVPL* new_avpl_p = (AVPL*)g_slice_new(any_avp_type)((any_avp_type*) g_slice_alloc (sizeof (any_avp_type)));
401 |
402 | #ifdef _AVP_DEBUGGING
403 | dbg_print(dbg_avpl_op,7,dbg_fp,"new_avpl_p: %p name=%s",new_avpl_p,name);
404 | #endif
405 |
406 | new_avpl_p->name = name ? scs_subscribe(avp_strings, name) : scs_subscribe(avp_strings, "");
407 | new_avpl_p->len = 0;
408 | new_avpl_p->null.avp = NULL((void*)0);
409 | new_avpl_p->null.next = &new_avpl_p->null;
410 | new_avpl_p->null.prev = &new_avpl_p->null;
411 |
412 |
413 | return new_avpl_p;
414 | }
415 |
416 | extern void rename_avpl(AVPL* avpl, char* name) {
417 | scs_unsubscribe(avp_strings,avpl->name);
418 | avpl->name = scs_subscribe(avp_strings,name);
419 | }
420 |
421 | /**
422 | * insert_avp_before_node:
423 | * @param avpl the avpl in which to insert.
424 | * @param next_node the next node before which the new avpn has to be inserted.
425 | * @param avp the avp to be inserted.
426 | * @param copy_avp whether the original AVP or a copy thereof must be inserted.
427 | *
428 | * Pre-condition: the avp is sorted before before_avp and does not already exist
429 | * in the avpl.
430 | */
431 | static void insert_avp_before_node(AVPL* avpl, AVPN* next_node, AVP *avp, bool_Bool copy_avp) {
432 | AVPN* new_avp_val = (AVPN*)g_slice_new(any_avp_type)((any_avp_type*) g_slice_alloc (sizeof (any_avp_type)));
433 |
434 | new_avp_val->avp = copy_avp ? avp_copy(avp) : avp;
435 |
436 | #ifdef _AVP_DEBUGGING
437 | dbg_print(dbg_avpl_op,7,dbg_fp,"new_avpn: %p",new_avp_val);
438 | dbg_print(dbg_avpl,5,dbg_fp,"insert_avp: inserting %p in %p before %p;",avp,avpl,next_node);
439 | #endif
440 |
441 | new_avp_val->next = next_node;
442 | new_avp_val->prev = next_node->prev;
443 | next_node->prev->next = new_avp_val;
444 | next_node->prev = new_avp_val;
445 |
446 | avpl->len++;
447 |
448 | #ifdef _AVP_DEBUGGING
449 | dbg_print(dbg_avpl,4,dbg_fp,"avpl: %p new len: %i",avpl,avpl->len);
450 | #endif
451 | }
452 |
453 | /**
454 | * insert_avp:
455 | * @param avpl the avpl in which to insert.
456 | * @param avp the avp to be inserted.
457 | *
458 | * Inserts the given AVP into the given AVPL if an identical one isn't yet there.
459 | *
460 | * Return value: whether it was inserted or not.
461 | *
462 | * BEWARE: Check the return value, you might need to delete the avp if
463 | * it is not inserted.
464 | **/
465 | extern bool_Bool insert_avp(AVPL* avpl, AVP* avp) {
466 | AVPN* c;
467 |
468 | #ifdef _AVP_DEBUGGING
469 | dbg_print(dbg_avpl_op,4,dbg_fp,"insert_avp: %p %p %s%c%s;",avpl,avp,avp->n,avp->o,avp->v);
470 | #endif
471 |
472 | /* get to the insertion point */
473 | for (c=avpl->null.next; c->avp; c = c->next) {
474 | int name_diff = strcmp(avp->n, c->avp->n);
475 |
476 | if (name_diff == 0) {
477 | int value_diff = strcmp(avp->v, c->avp->v);
478 |
479 | if (value_diff < 0) {
480 | break;
481 | }
482 |
483 | if (value_diff == 0) {
484 | // ignore duplicate values, prevents (a=1, a=1)
485 | // note that this is also used to insert
486 | // conditions AVPs, so really check if the name,
487 | // value and operator are all equal.
488 | if (c->avp->o == avp->o && avp->o == AVP_OP_EQUAL'=') {
489 | return false0;
490 | }
491 | }
492 | }
493 |
494 | if (name_diff < 0) {
495 | break;
496 | }
497 | }
498 |
499 | insert_avp_before_node(avpl, c, avp, false0);
500 |
501 | return true1;
502 | }
503 |
504 | /**
505 | * get_avp_by_name:
506 | * @param avpl the avpl from which to try to get the avp.
507 | * @param name the name of the avp we are looking for.
508 | * @param cookie variable in which to store the state between calls.
509 | *
510 | * Gets pointer to the next avp whose name is given; uses cookie to store its
511 | * state between calls.
512 | *
513 | * Return value: a pointer to the next matching avp if there's one, else NULL.
514 | *
515 | **/
516 | extern AVP* get_avp_by_name(AVPL* avpl, char* name, void** cookie) {
517 | AVPN* curr;
518 | AVPN* start = (AVPN*) *cookie;
519 |
520 | #ifdef _AVP_DEBUGGING
521 | dbg_print(dbg_avpl_op,7,dbg_fp,"get_avp_by_name: entering: %p %s %p",avpl,name,*cookie);
522 | #endif
523 |
524 | name = scs_subscribe(avp_strings, name);
525 |
526 | if (!start) start = avpl->null.next;
527 |
528 | for ( curr = start; curr->avp; curr = curr->next ) {
529 | if ( curr->avp->n == name ) {
530 | break;
531 | }
532 | }
533 |
534 | *cookie = curr;
535 |
536 | #ifdef _AVP_DEBUGGING
537 | dbg_print(dbg_avpl_op,5,dbg_fp,"get_avp_by_name: got avp: %p",curr);
538 | #endif
539 |
540 | scs_unsubscribe(avp_strings, name);
541 |
542 | return curr->avp;
543 | }
544 |
545 | /**
546 | * extract_avp_by_name:
547 | * @param avpl the avpl from which to try to extract the avp.
548 | * @param name the name of the avp we are looking for.
549 | *
550 | * Extracts from the avpl the next avp whose name is given;
551 | *
552 | * Return value: a pointer to extracted avp if there's one, else NULL.
553 | *
554 | **/
555 | extern AVP* extract_avp_by_name(AVPL* avpl, char* name) {
556 | AVPN* curr;
557 | AVP* avp = NULL((void*)0);
558 |
559 | #ifdef _AVP_DEBUGGING
560 | dbg_print(dbg_avpl_op,7,dbg_fp,"extract_avp_by_name: entering: %p %s",avpl,name);
561 | #endif
562 |
563 | name = scs_subscribe(avp_strings, name);
564 |
565 | for ( curr = avpl->null.next; curr->avp; curr = curr->next ) {
566 | if ( curr->avp->n == name ) {
567 | break;
568 | }
569 | }
570 |
571 | scs_unsubscribe(avp_strings, name);
572 |
573 | if( ! curr->avp ) return NULL((void*)0);
574 |
575 | curr->next->prev = curr->prev;
576 | curr->prev->next = curr->next;
577 |
578 | avp = curr->avp;
579 |
580 | g_slice_free(any_avp_type,(any_avp_type*)curr)do { if (1) g_slice_free1 (sizeof (any_avp_type), ((any_avp_type *)curr)); else (void) ((any_avp_type*) 0 == ((any_avp_type*)curr )); } while (0);
581 |
582 | (avpl->len)--;
583 |
584 | #ifdef _AVP_DEBUGGING
585 | dbg_print(dbg_avpl,4,dbg_fp,"avpl: %p new len: %i",avpl,avpl->len);
586 | #endif
587 |
588 | #ifdef _AVP_DEBUGGING
589 | dbg_print(dbg_avpl_op,5,dbg_fp,"extract_avp_by_name: got avp: %p",avp);
590 | #endif
591 |
592 | return avp;
593 | }
594 |
595 |
596 | /**
597 | * extract_first_avp:
598 | * @param avpl the avpl from which to try to extract the avp.
599 | *
600 | * Extracts the first avp from the avpl.
601 | *
602 | * Return value: a pointer to extracted avp if there's one, else NULL.
603 | *
604 | **/
605 | extern AVP* extract_first_avp(AVPL* avpl) {
606 | AVP* avp;
607 | AVPN* node;
608 |
609 | #ifdef _AVP_DEBUGGING
610 | dbg_print(dbg_avpl_op,7,dbg_fp,"extract_first_avp: %p",avpl);
611 | #endif
612 |
613 | node = avpl->null.next;
614 |
615 | avpl->null.next->prev = &avpl->null;
616 | avpl->null.next = node->next;
617 |
618 | avp = node->avp;
619 |
620 | if (avp) {
621 | g_slice_free(any_avp_type,(any_avp_type*)node)do { if (1) g_slice_free1 (sizeof (any_avp_type), ((any_avp_type *)node)); else (void) ((any_avp_type*) 0 == ((any_avp_type*)node )); } while (0);
622 | (avpl->len)--;
623 | #ifdef _AVP_DEBUGGING
624 | dbg_print(dbg_avpl,4,dbg_fp,"avpl: %p new len: %i",avpl,avpl->len);
625 | #endif
626 | }
627 |
628 | #ifdef _AVP_DEBUGGING
629 | dbg_print(dbg_avpl_op,5,dbg_fp,"extract_first_avp: got avp: %p",avp);
630 | #endif
631 |
632 | return avp;
633 |
634 | }
635 |
636 |
637 | /**
638 | * extract_last_avp:
639 | * @param avpl the avpl from which to try to extract the avp.
640 | *
641 | * Extracts the last avp from the avpl.
642 | *
643 | * Return value: a pointer to extracted avp if there's one, else NULL.
644 | *
645 | **/
646 | extern AVP* extract_last_avp(AVPL* avpl) {
647 | AVP* avp;
648 | AVPN* node;
649 |
650 | node = avpl->null.prev;
651 |
652 | avpl->null.prev->next = &avpl->null;
653 | avpl->null.prev = node->prev;
654 |
655 | avp = node->avp;
656 |
657 | if (avp) {
658 | g_slice_free(any_avp_type,(any_avp_type*)node)do { if (1) g_slice_free1 (sizeof (any_avp_type), ((any_avp_type *)node)); else (void) ((any_avp_type*) 0 == ((any_avp_type*)node )); } while (0);
659 | (avpl->len)--;
660 | #ifdef _AVP_DEBUGGING
661 | dbg_print(dbg_avpl,4,dbg_fp,"avpl: %p new len: %i",avpl,avpl->len);
662 | #endif
663 | }
664 |
665 | #ifdef _AVP_DEBUGGING
666 | dbg_print(dbg_avpl_op,5,dbg_fp,"extract_last_avp: got avp: %p",avp);
667 | #endif
668 |
669 | return avp;
670 |
671 | }
672 |
673 |
674 | /**
675 | * delete_avpl:
676 | * @param avpl the avpl from which to try to extract the avp.
677 | * @param avps_too whether or not it should delete the avps as well.
678 | *
679 | * Destroys an avpl and releases the resources it uses. If told to do
680 | * so releases the avps as well.
681 | *
682 | **/
683 | extern void delete_avpl(AVPL* avpl, bool_Bool avps_too) {
684 | AVP* avp;
685 | #ifdef _AVP_DEBUGGING
686 | dbg_print(dbg_avpl,3,dbg_fp,"delete_avpl: %p",avpl);
687 | #endif
688 |
689 | while(( avp = extract_last_avp(avpl))) {
690 | if (avps_too) {
691 | delete_avp(avp);
692 | }
693 | }
694 |
695 | scs_unsubscribe(avp_strings,avpl->name);
696 | g_slice_free(any_avp_type,(any_avp_type*)avpl)do { if (1) g_slice_free1 (sizeof (any_avp_type), ((any_avp_type *)avpl)); else (void) ((any_avp_type*) 0 == ((any_avp_type*)avpl )); } while (0);
697 | }
698 |
699 |
700 |
701 | /**
702 | * get_next_avp:
703 | * @param avpl the avpl from which to try to get the avps.
704 | * @param cookie variable in which to store the state between calls.
705 | *
706 | * Iterates on an avpl to get its avps.
707 | *
708 | * Return value: a pointer to the next avp if there's one, else NULL.
709 | *
710 | **/
711 | extern AVP* get_next_avp(AVPL* avpl, void** cookie) {
712 | AVPN* node;
713 |
714 | #ifdef _AVP_DEBUGGING
715 | dbg_print(dbg_avpl_op,5,dbg_fp,"get_next_avp: avpl: %p avpn: %p",avpl,*cookie);
716 | #endif
717 |
718 | if (*cookie) {
719 | node = (AVPN*) *cookie;
720 | } else {
721 | node = avpl->null.next;
722 | }
723 |
724 | *cookie = node->next;
725 |
726 | #ifdef _AVP_DEBUGGING
727 | dbg_print(dbg_avpl_op,5,dbg_fp,"extract_last_avp: got avp: %p",node->avp);
728 | #endif
729 |
730 | return node->avp;
731 | }
732 |
733 | /**
734 | * avpl_to_str:
735 | * @param avpl the avpl to represent.
736 | *
737 | * Creates a newly allocated string containing a representation of an avpl.
738 | *
739 | * Return value: a pointer to the newly allocated string.
740 | *
741 | **/
742 | char* avpl_to_str(AVPL* avpl) {
743 | AVPN* c;
744 | GString* s = g_string_new("");
745 | char* avp_s;
746 | char* r;
747 |
748 | for(c=avpl->null.next; c->avp; c = c->next) {
749 | avp_s = avp_to_str(c->avp)(wmem_strdup_printf(((void*)0), "%s%c%s",c->avp->n,c-> avp->o,c->avp->v));
750 | g_string_append_printf(s," %s;",avp_s);
751 | g_free(avp_s);
752 | }
753 |
754 | r = g_string_free(s,FALSE)(__builtin_constant_p ((0)) ? (((0)) ? (g_string_free) ((s), ( (0))) : g_string_free_and_steal (s)) : (g_string_free) ((s), ( (0))));
755 |
756 | /* g_strchug(r); ? */
757 | return r;
758 | }
759 |
760 | extern char* avpl_to_dotstr(AVPL* avpl) {
761 | AVPN* c;
762 | GString* s = g_string_new("");
763 | char* avp_s;
764 | char* r;
765 |
766 | for(c=avpl->null.next; c->avp; c = c->next) {
767 | avp_s = avp_to_str(c->avp)(wmem_strdup_printf(((void*)0), "%s%c%s",c->avp->n,c-> avp->o,c->avp->v));
768 | g_string_append_printf(s," .%s;",avp_s);
769 | g_free(avp_s);
770 | }
771 |
772 | r = g_string_free(s,FALSE)(__builtin_constant_p ((0)) ? (((0)) ? (g_string_free) ((s), ( (0))) : g_string_free_and_steal (s)) : (g_string_free) ((s), ( (0))));
773 |
774 | /* g_strchug(r); ? */
775 | return r;
776 | }
777 |
778 | /**
779 | * merge_avpl:
780 | * @param dst the avpl in which to merge the avps.
781 | * @param src the avpl from which to get the avps.
782 | * @param copy_avps whether avps should be copied instead of referenced.
783 | *
784 | * Adds the avps of src that are not existent in dst into dst.
785 | *
786 | **/
787 | extern void merge_avpl(AVPL* dst, AVPL* src, bool_Bool copy_avps) {
788 | AVPN* cd = NULL((void*)0);
789 | AVPN* cs = NULL((void*)0);
790 |
791 | #ifdef _AVP_DEBUGGING
792 | dbg_print(dbg_avpl_op,3,dbg_fp,"merge_avpl: %p %p",dst,src);
793 | #endif
794 |
795 | cs = src->null.next;
796 | cd = dst->null.next;
797 |
798 | while (cs->avp && cd->avp) {
799 |
800 | int name_diff = strcmp(cd->avp->n, cs->avp->n);
801 |
802 | if (name_diff < 0) {
803 | // dest < source, advance dest to find a better place to insert
804 | cd = cd->next;
805 | } else if (name_diff > 0) {
806 | // dest > source, so it can be definitely inserted here.
807 | insert_avp_before_node(dst, cd, cs->avp, copy_avps);
808 | cs = cs->next;
809 | } else {
810 | // attribute names are equal. Ignore duplicate values but ensure that other values are sorted.
811 | int value_diff = strcmp(cd->avp->v, cs->avp->v);
812 |
813 | if (value_diff < 0) {
814 | // dest < source, do not insert it yet
815 | cd = cd->next;
816 | } else if (value_diff > 0) {
817 | // dest > source, insert AVP before the current dest AVP
818 | insert_avp_before_node(dst, cd, cs->avp, copy_avps);
819 | cs = cs->next;
820 | } else {
821 | // identical AVPs, do not create a duplicate.
822 | cs = cs->next;
823 | }
824 | }
825 | }
826 |
827 | // if there are remaining source AVPs while there are no more destination
828 | // AVPs (cd now represents the NULL item, after the last item), append
829 | // all remaining source AVPs to the end
830 | while (cs->avp) {
831 | insert_avp_before_node(dst, cd, cs->avp, copy_avps);
832 | cs = cs->next;
833 | }
834 |
835 | #ifdef _AVP_DEBUGGING
836 | dbg_print(dbg_avpl_op,8,dbg_fp,"merge_avpl: done");
837 | #endif
838 |
839 | return;
840 | }
841 |
842 |
843 | /**
844 | * new_avpl_from_avpl:
845 | * @param name the name of the new avpl.
846 | * @param avpl the avpl from which to get the avps.
847 | * @param copy_avps whether avps should be copied instead of referenced.
848 | *
849 | * Creates a new avpl containing the same avps as the given avpl
850 | * It will either reference or copy the avps.
851 | *
852 | * Return value: a pointer to the newly allocated string.
853 | *
854 | **/
855 | extern AVPL* new_avpl_from_avpl(const char* name, AVPL* avpl, bool_Bool copy_avps) {
856 | AVPL* newavpl = new_avpl(name);
857 | void* cookie = NULL((void*)0);
858 | AVP* avp;
859 | AVP* copy;
860 |
861 | #ifdef _AVP_DEBUGGING
862 | dbg_print(dbg_avpl_op,3,dbg_fp,"new_avpl_from_avpl: %p from=%p name='%s'",newavpl,avpl,name);
863 | #endif
864 |
865 | while(( avp = get_next_avp(avpl,&cookie) )) {
866 | if (copy_avps) {
867 | copy = avp_copy(avp);
868 | if ( ! insert_avp(newavpl,copy) ) {
869 | delete_avp(copy);
870 | }
871 | } else {
872 | insert_avp(newavpl,avp);
873 | }
874 | }
875 |
876 | #ifdef _AVP_DEBUGGING
877 | dbg_print(dbg_avpl_op,8,dbg_fp,"new_avpl_from_avpl: done");
878 | #endif
879 |
880 | return newavpl;
881 | }
882 |
883 | /**
884 | * match_avp:
885 | * @param src an src to be compared against an "op" avp
886 | * @param op the "op" avp that will be matched against the src avp
887 | *
888 | * Checks whether or not two avp's match.
889 | *
890 | * Return value: a pointer to the src avp if there's a match.
891 | *
892 | **/
893 | extern AVP* match_avp(AVP* src, AVP* op) {
894 | char** splited;
895 | int i;
896 | char* p;
897 | unsigned ls;
898 | unsigned lo;
899 | double fs = 0.0;
900 | double fo = 0.0;
901 | bool_Bool lower = false0;
902 |
903 | #ifdef _AVP_DEBUGGING
904 | dbg_print(dbg_avpl_op,3,dbg_fp,"match_avp: %s%c%s; vs. %s%c%s;",src->n,src->o,src->v,op->n,op->o,op->v);
905 | #endif
906 |
907 | if ( src->n != op->n ) {
908 | return NULL((void*)0);
909 | }
910 |
911 | switch (op->o) {
912 | case AVP_OP_EXISTS'?':
913 | return src;
914 | case AVP_OP_EQUAL'=':
915 | return src->v == op->v ? src : NULL((void*)0);
916 | case AVP_OP_NOTEQUAL'!':
917 | return !( src->v == op->v) ? src : NULL((void*)0);
918 | case AVP_OP_STARTS'^':
919 | return strncmp(src->v,op->v,strlen(op->v)) == 0 ? src : NULL((void*)0);
920 | case AVP_OP_ONEOFF'|':
921 | splited = g_strsplit(op->v,"|",0);
922 | if (splited) {
923 | for (i=0;splited[i];i++) {
924 | if(g_str_equal(splited[i],src->v)(strcmp ((const char *) (splited[i]), (const char *) (src-> v)) == 0)) {
925 | g_strfreev(splited);
926 | return src;
927 | }
928 | }
929 | g_strfreev(splited);
930 | }
931 | return NULL((void*)0);
932 |
933 | case AVP_OP_LOWER'<':
934 | lower = true1;
935 | /* FALLTHRU */
936 | case AVP_OP_HIGHER'>':
937 |
938 | fs = g_ascii_strtod(src->v, NULL((void*)0));
939 | fo = g_ascii_strtod(op->v, NULL((void*)0));
940 |
941 | if (lower) {
942 | if (fs<fo) return src;
943 | else return NULL((void*)0);
944 | } else {
945 | if (fs>fo) return src;
946 | else return NULL((void*)0);
947 | }
948 | case AVP_OP_ENDS'$':
949 | /* does this work? */
950 | ls = (unsigned) strlen(src->v);
951 | lo = (unsigned) strlen(op->v);
952 |
953 | if ( ls < lo ) {
954 | return NULL((void*)0);
955 | } else {
956 | p = src->v + ( ls - lo );
957 | return g_str_equal(p,op->v)(strcmp ((const char *) (p), (const char *) (op->v)) == 0) ? src : NULL((void*)0);
958 | }
959 |
960 | /* case AVP_OP_TRANSF: */
961 | /* return do_transform(src,op); */
962 | case AVP_OP_CONTAINS'~':
963 | return g_strrstr(src->v, op->v) ? src : NULL((void*)0);
964 | }
965 | /* will never get here */
966 | return NULL((void*)0);
967 | }
968 |
969 |
970 |
971 | /**
972 | * new_avpl_loose_match:
973 | * @param name the name of the resulting avpl
974 | * @param src the data AVPL to be matched against a condition AVPL
975 | * @param op the conditions AVPL that will be matched against the data AVPL
976 | * @param copy_avps whether the avps in the resulting avpl should be copied
977 | *
978 | * Creates a new AVP list containing all data AVPs that matched any of the
979 | * conditions AVPs. If there are no matches, an empty list will be returned.
980 | *
981 | * Note: Loose will always be considered a successful match, it matches zero or
982 | * more conditions.
983 | */
984 | extern AVPL* new_avpl_loose_match(const char* name,
985 | AVPL* src,
986 | AVPL* op,
987 | bool_Bool copy_avps) {
988 |
989 | AVPL* newavpl = new_avpl(scs_subscribe(avp_strings, name));
990 | AVPN* co = NULL((void*)0);
991 | AVPN* cs = NULL((void*)0);
992 |
993 | #ifdef _AVP_DEBUGGING
994 | dbg_print(dbg_avpl_op,3,dbg_fp,"new_avpl_loose_match: %p src=%p op=%p name='%s'",newavpl,src,op,name);
995 | #endif
996 |
997 |
998 | cs = src->null.next;
999 | co = op->null.next;
1000 | while (cs->avp && co->avp) {
1001 | int name_diff = strcmp(co->avp->n, cs->avp->n);
1002 |
1003 | if (name_diff < 0) {
1004 | // op < source, op is not matching
1005 | co = co->next;
1006 | } else if (name_diff > 0) {
1007 | // op > source, source is not matching
1008 | cs = cs->next;
1009 | } else {
1010 | // attribute match found, let's see if there is any condition (op) that accepts this data AVP.
1011 | AVPN *cond = co;
1012 | do {
1013 | if (match_avp(cs->avp, cond->avp)) {
1014 | insert_avp_before_node(newavpl, newavpl->null.prev->next, cs->avp, copy_avps);
1015 | break;
1016 | }
1017 | cond = cond->next;
1018 | } while (cond->avp && cond->avp->n == cs->avp->n);
1019 | cs = cs->next;
1020 | }
1021 | }
1022 |
1023 | // return matches (possible none)
1024 | return newavpl;
1025 | }
1026 |
1027 | /**
1028 | * new_avpl_pairs_match:
1029 | * @param name the name of the resulting avpl
1030 | * @param src the data AVPL to be matched against a condition AVPL
1031 | * @param op the conditions AVPL that will be matched against the data AVPL
1032 | * @param strict true if every condition must have a matching data AVP, false if
1033 | * it is also acceptable that only one of the condition AVPs for the same
1034 | * attribute is matching.
1035 | * @param copy_avps whether the avps in the resulting avpl should be copied
1036 | *
1037 | * Creates an AVP list by matching pairs of conditions and data AVPs, returning
1038 | * the data AVPs. If strict is true, then each condition must be paired with a
1039 | * matching data AVP. If strict is false, then some conditions are allowed to
1040 | * fail when other conditions for the same attribute do have a match. Note that
1041 | * if the condition AVPL is empty, the result will be a match (an empty list).
1042 | *
1043 | * Return value: a pointer to the newly created avpl containing the
1044 | * matching avps or NULL if there is no match.
1045 | */
1046 | extern AVPL* new_avpl_pairs_match(const char* name, AVPL* src, AVPL* op, bool_Bool strict, bool_Bool copy_avps) {
1047 | AVPL* newavpl;
1048 | AVPN* co = NULL((void*)0);
1049 | AVPN* cs = NULL((void*)0);
1050 | const char *last_match = NULL((void*)0);
1051 | bool_Bool matched = true1;
1052 |
1053 | newavpl = new_avpl(scs_subscribe(avp_strings, name));
1054 |
1055 | #ifdef _AVP_DEBUGGING
1056 | dbg_print(dbg_avpl_op,3,dbg_fp,"%s: %p src=%p op=%p name='%s'",G_STRFUNC((const char*) (__func__)),newavpl,src,op,name);
1057 | #endif
1058 |
1059 | cs = src->null.next;
1060 | co = op->null.next;
1061 | while (cs->avp && co->avp) {
1062 | int name_diff = g_strcmp0(co->avp->n, cs->avp->n);
1063 | const char *failed_match = NULL((void*)0);
1064 |
1065 | if (name_diff < 0) {
1066 | // op < source, op has no data avp with same attribute.
1067 | failed_match = co->avp->n;
1068 | co = co->next;
1069 | } else if (name_diff > 0) {
1070 | // op > source, the source avp is not matched by any condition
1071 | cs = cs->next;
1072 | } else {
1073 | // Matching attributes found, now try to find a matching data AVP for the condition.
1074 | if (match_avp(cs->avp, co->avp)) {
1075 | insert_avp_before_node(newavpl, newavpl->null.prev->next, cs->avp, copy_avps);
1076 | last_match = co->avp->n;
1077 | cs = cs->next;
1078 | } else {
1079 | failed_match = co->avp->n;
1080 | }
1081 | co = co->next;
1082 | }
1083 |
1084 | // condition did not match, check if we can continue matching.
1085 | if (failed_match) {
1086 | if (strict) {
1087 | matched = false0;
1088 | break;
1089 | } else if (last_match != failed_match) {
1090 | // None of the conditions so far matched the attribute, check for other candidates
1091 | if (!co->avp || co->avp->n != last_match) {
1092 | matched = false0;
1093 | break;
1094 | }
1095 | }
1096 | }
1097 | }
1098 |
1099 | // if there are any conditions remaining, then those could not be matched
1100 | if (matched && strict && co->avp) {
1101 | matched = false0;
1102 | }
1103 |
1104 | if (matched) {
1105 | // there was a match, accept it
1106 | return newavpl;
1107 | } else {
1108 | // no match, only delete AVPs too if they were copied
1109 | delete_avpl(newavpl, copy_avps);
1110 | return NULL((void*)0);
1111 | }
1112 | }
1113 |
1114 |
1115 | /**
1116 | * new_avpl_from_match:
1117 | * @param mode The matching method, one of AVPL_STRICT, AVPL_LOOSE, AVPL_EVERY.
1118 | * @param name the name of the resulting avpl
1119 | * @param src the data AVPL to be matched against a condition AVPL
1120 | * @param op the conditions AVPL that will be matched against the data AVPL
1121 | *
1122 | * Matches the conditions AVPL against the original AVPL according to the mode.
1123 | * If there is no match, NULL is returned. If there is actually a match, then
1124 | * the matching AVPs (a subset of the data) are returned.
1125 | */
1126 | extern AVPL* new_avpl_from_match(avpl_match_mode mode, const char* name,AVPL* src, AVPL* op, bool_Bool copy_avps) {
1127 | AVPL* avpl = NULL((void*)0);
1128 |
1129 | switch (mode) {
1130 | case AVPL_STRICT:
1131 | avpl = new_avpl_pairs_match(name, src, op, true1, copy_avps);
1132 | break;
1133 | case AVPL_LOOSE:
1134 | avpl = new_avpl_loose_match(name,src,op,copy_avps);
1135 | break;
1136 | case AVPL_EVERY:
1137 | avpl = new_avpl_pairs_match(name, src, op, false0, copy_avps);
1138 | break;
1139 | case AVPL_NO_MATCH:
1140 | // XXX this seems unused
1141 | avpl = new_avpl_from_avpl(name,src,copy_avps);
1142 | merge_avpl(avpl, op, copy_avps);
1143 | break;
1144 | }
1145 |
1146 | return avpl;
1147 | }
1148 |
1149 | /**
1150 | * delete_avpl_transform:
1151 | * @param op a pointer to the avpl transformation object
1152 | *
1153 | * Destroys an avpl transformation object and releases all the resources it
1154 | * uses.
1155 | *
1156 | **/
1157 | extern void delete_avpl_transform(AVPL_Transf* op) {
1158 | AVPL_Transf* next;
1159 |
1160 | for (; op ; op = next) {
1161 | next = op->next;
1162 |
1163 | g_free(op->name);
1164 |
1165 | if (op->match) {
1166 | delete_avpl(op->match,true1);
1167 | }
1168 |
1169 | if (op->replace) {
1170 | delete_avpl(op->replace,true1);
1171 | }
1172 |
1173 | g_free(op);
1174 | }
1175 |
1176 | }
1177 |
1178 |
1179 | /**
1180 | * avpl_transform:
1181 | * @param src the source avpl for the transform operation.
1182 | * @param op a pointer to the avpl transformation object to apply.
1183 | *
1184 | * Applies the "op" transformation to an avpl, matches it and eventually
1185 | * replaces or inserts the transformed avps.
1186 | *
1187 | * Return value: whether the transformation was performed or not.
1188 | **/
1189 | extern void avpl_transform(AVPL* src, AVPL_Transf* op) {
1190 | AVPL* avpl = NULL((void*)0);
1191 | AVPN* cs;
1192 | AVPN* cm;
1193 | AVPN* n;
1194 |
1195 | #ifdef _AVP_DEBUGGING
1196 | dbg_print(dbg_avpl_op,3,dbg_fp,"avpl_transform: src=%p op=%p",src,op);
1197 | #endif
1198 |
1199 | for ( ; op ; op = op->next) {
1200 |
1201 | avpl = new_avpl_from_match(op->match_mode, src->name,src, op->match, true1);
1202 |
1203 | if (avpl) {
1204 | switch (op->replace_mode) {
1205 | case AVPL_NO_REPLACE:
1206 | delete_avpl(avpl,true1);
1207 | return;
1208 | case AVPL_INSERT:
1209 | merge_avpl(src,op->replace,true1);
1210 | delete_avpl(avpl,true1);
1211 | return;
1212 | case AVPL_REPLACE:
1213 | cs = src->null.next;
1214 | cm = avpl->null.next;
1215 | // Removes AVPs from the source which are in the matched data.
1216 | // Assume that the matched set is a subset of the source.
1217 | while (cs->avp && cm->avp) {
1218 | if (cs->avp->n == cm->avp->n && cs->avp->v == cm->avp->v) {
1219 | n = cs->next;
1220 |
1221 | cs->prev->next = cs->next;
1222 | cs->next->prev = cs->prev;
1223 |
1224 | if (cs->avp)
1225 | delete_avp(cs->avp);
1226 | g_slice_free(any_avp_type,(any_avp_type*)cs)do { if (1) g_slice_free1 (sizeof (any_avp_type), ((any_avp_type *)cs)); else (void) ((any_avp_type*) 0 == ((any_avp_type*)cs) ); } while (0);
1227 |
1228 | cs = n;
1229 | cm = cm->next;
1230 | } else {
1231 | // Current matched AVP is not equal to the current
1232 | // source AVP. Since there must be a source AVP for
1233 | // each matched AVP, advance current source and not
1234 | // the match AVP.
1235 | cs = cs->next;
1236 | }
1237 | }
1238 |
1239 | merge_avpl(src,op->replace,true1);
1240 | delete_avpl(avpl,true1);
1241 | return;
1242 | }
1243 | }
1244 | }
1245 | }
1246 |
1247 |
1248 | /**
1249 | * new_loal:
1250 | * @param name the name the loal will take.
1251 | *
1252 | * Creates an empty list of avp lists.
1253 | *
1254 | * Return value: a pointer to the newly created loal.
1255 | **/
1256 | extern LoAL* new_loal(const char* name) {
1257 | LoAL* new_loal_p = (LoAL*)g_slice_new(any_avp_type)((any_avp_type*) g_slice_alloc (sizeof (any_avp_type)));
1258 |
1259 | if (! name) {
1260 | name = "anonymous";
1261 | }
1262 |
1263 | #ifdef _AVP_DEBUGGING
1264 | dbg_print(dbg_avpl_op,3,dbg_fp,"new_loal_p: %p name=%s",new_loal_p,name);
1265 | #endif
1266 |
1267 | new_loal_p->name = scs_subscribe(avp_strings,name);
1268 | new_loal_p->null.avpl = NULL((void*)0);
1269 | new_loal_p->null.next = &new_loal_p->null;
1270 | new_loal_p->null.prev = &new_loal_p->null;
1271 | new_loal_p->len = 0;
1272 | return new_loal_p;
1273 | }
1274 |
1275 | /**
1276 | * loal_append:
1277 | * @param loal the loal on which to operate.
1278 | * @param avpl the avpl to append.
1279 | *
1280 | * Appends an avpl to a loal.
1281 | *
1282 | **/
1283 | extern void loal_append(LoAL* loal, AVPL* avpl) {
1284 | LoALnode* node = (LoALnode*)g_slice_new(any_avp_type)((any_avp_type*) g_slice_alloc (sizeof (any_avp_type)));
1285 |
1286 | #ifdef _AVP_DEBUGGING
1287 | dbg_print(dbg_avpl_op,3,dbg_fp,"new_loal_node: %p",node);
1288 | #endif
1289 |
1290 | node->avpl = avpl;
1291 | node->next = &loal->null;
1292 | node->prev = loal->null.prev;
1293 |
1294 | loal->null.prev->next = node;
1295 | loal->null.prev = node;
1296 | loal->len++;
1297 | }
1298 |
1299 |
1300 | /**
1301 | * extract_first_avpl:
1302 | * @param loal the loal on which to operate.
1303 | *
1304 | * Extracts the first avpl contained in a loal.
1305 | *
1306 | * Return value: a pointer to the extracted avpl.
1307 | *
1308 | **/
1309 | extern AVPL* extract_first_avpl(LoAL* loal) {
1310 | LoALnode* node;
1311 | AVPL* avpl;
1312 |
1313 | #ifdef _AVP_DEBUGGING
1314 | dbg_print(dbg_avpl_op,3,dbg_fp,"extract_first_avpl: from: %s",loal->name);
1315 | #endif
1316 |
1317 | node = loal->null.next;
1318 |
1319 | loal->null.next->next->prev = &loal->null;
1320 | loal->null.next = node->next;
1321 |
1322 | loal->len--;
1323 |
1324 | avpl = node->avpl;
1325 |
1326 | if ( avpl ) {
1327 | g_slice_free(any_avp_type,(any_avp_type*)node)do { if (1) g_slice_free1 (sizeof (any_avp_type), ((any_avp_type *)node)); else (void) ((any_avp_type*) 0 == ((any_avp_type*)node )); } while (0);
1328 |
1329 | #ifdef _AVP_DEBUGGING
1330 | dbg_print(dbg_avpl_op,3,dbg_fp,"extract_first_avpl: got %s",avpl->name);
1331 | dbg_print(dbg_avpl_op,3,dbg_fp,"delete_loal_node: %p",node);
1332 | #endif
1333 | }
1334 |
1335 | return avpl;
1336 | }
1337 |
1338 | /**
1339 | * extract_first_avpl:
1340 | * @param loal the loal on which to operate.
1341 | *
1342 | * Extracts the last avpl contained in a loal.
1343 | *
1344 | * Return value: a pointer to the extracted avpl.
1345 | *
1346 | **/
1347 | extern AVPL* extract_last_avpl(LoAL* loal){
1348 | LoALnode* node;
1349 | AVPL* avpl;
1350 |
1351 | node = loal->null.prev;
1352 |
1353 | loal->null.prev->prev->next = &loal->null;
1354 | loal->null.prev = node->prev;
1355 |
1356 | loal->len--;
1357 |
1358 | avpl = node->avpl;
1359 |
1360 | if ( avpl ) {
1361 | g_slice_free(any_avp_type,(any_avp_type*)node)do { if (1) g_slice_free1 (sizeof (any_avp_type), ((any_avp_type *)node)); else (void) ((any_avp_type*) 0 == ((any_avp_type*)node )); } while (0);
1362 | #ifdef _AVP_DEBUGGING
1363 | dbg_print(dbg_avpl_op,3,dbg_fp,"delete_loal_node: %p",node);
1364 | #endif
1365 | }
1366 |
1367 | return avpl;
1368 | }
1369 |
1370 | /**
1371 | * extract_first_avpl:
1372 | * @param loal the loal on which to operate.
1373 | * @param cookie pointer to the pointer variable to contain the state between calls
1374 | *
1375 | * At each call will return the following avpl from a loal. The given cookie
1376 | * will be used to manatain the state between calls.
1377 | *
1378 | * Return value: a pointer to the next avpl.
1379 | *
1380 | **/
1381 | extern AVPL* get_next_avpl(LoAL* loal,void** cookie) {
1382 | LoALnode* node;
1383 |
1384 | #ifdef _AVP_DEBUGGING
1385 | dbg_print(dbg_avpl_op,3,dbg_fp,"get_next_avpl: loal=%p node=%p",loal,*cookie);
1386 | #endif
1387 |
1388 | if (*cookie) {
1389 | node = (LoALnode*) *cookie;
1390 | } else {
1391 | node = loal->null.next;
1392 | }
1393 |
1394 | *cookie = node->next;
1395 |
1396 | return node->avpl;
1397 | }
1398 |
1399 | /**
1400 | * delete_loal:
1401 | * @param loal the loal to be deleted.
1402 | * @param avpls_too whether avpls contained by the loal should be deleted as well
1403 | * @param avps_too whether avps contained by the avpls should be also deleted
1404 | *
1405 | * Destroys a loal and eventually destroys avpls and avps.
1406 | *
1407 | **/
1408 | extern void delete_loal(LoAL* loal, bool_Bool avpls_too, bool_Bool avps_too) {
1409 | AVPL* avpl;
1410 |
1411 | #ifdef _AVP_DEBUGGING
1412 | dbg_print(dbg_avpl_op,3,dbg_fp,"delete_loal: %p",loal);
1413 | #endif
1414 |
1415 | while(( avpl = extract_last_avpl(loal) )) {
1416 | if (avpls_too) {
1417 | delete_avpl(avpl,avps_too);
1418 | }
1419 | }
1420 |
1421 | scs_unsubscribe(avp_strings,loal->name);
1422 | g_slice_free(any_avp_type,(any_avp_type*)loal)do { if (1) g_slice_free1 (sizeof (any_avp_type), ((any_avp_type *)loal)); else (void) ((any_avp_type*) 0 == ((any_avp_type*)loal )); } while (0);
1423 | }
1424 |
1425 |
1426 |
1427 | /****************************************************************************
1428 | ******************* the following are used in load_loal_from_file
1429 | ****************************************************************************/
1430 |
1431 | /**
1432 | * load_loal_error:
1433 | * Used by loal_from_file to handle errors while loading.
1434 | **/
1435 | static LoAL* load_loal_error(FILE* fp, LoAL* loal, AVPL* curr, int linenum, const char* fmt, ...) {
1436 | va_list list;
1437 | char* desc;
1438 | LoAL* ret = NULL((void*)0);
1439 | char* err;
1440 |
1441 | va_start( list, fmt )__builtin_va_start(list, fmt);
1442 | desc = ws_strdup_vprintf(fmt, list)wmem_strdup_vprintf(((void*)0), fmt, list);
1443 | va_end( list )__builtin_va_end(list);
1444 |
1445 | if (loal) {
1446 | err = ws_strdup_printf("Error Loading LoAL from file: in %s at line: %i, %s",loal->name,linenum,desc)wmem_strdup_printf(((void*)0), "Error Loading LoAL from file: in %s at line: %i, %s" ,loal->name,linenum,desc);
1447 | } else {
1448 | err = ws_strdup_printf("Error Loading LoAL at line: %i, %s",linenum,desc)wmem_strdup_printf(((void*)0), "Error Loading LoAL at line: %i, %s" ,linenum,desc);
1449 | }
1450 | ret = new_loal(err);
1451 |
1452 | g_free(desc);
1453 | g_free(err);
1454 |
1455 | if (fp) fclose(fp);
1456 | if (loal) delete_loal(loal,true1,true1);
1457 | if (curr) delete_avpl(curr,true1);
1458 |
1459 | return ret;
1460 | }
1461 |
1462 |
1463 | /* the maximum length allowed for a line */
1464 | #define MAX_ITEM_LEN8192 8192
1465 |
1466 | /* this two ugly things are used for tokenizing */
1467 | #define AVP_OP_CHAR'=': case '^': case '$': case '~': case '<': case '>': case '?': case '|': case '&' : case '!' '=': case '^': case '$': case '~': case '<': case '>': case '?': case '|': case '&' : case '!'
1468 |
1469 | #define AVP_NAME_CHAR'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G' : case 'H': case 'I': case 'J':case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T' :case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': case 'a': case 'b': case 'c': case 'd':case 'e': case 'f': case 'g' : case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x':case 'y': case 'z' : case '_': case '0': case '1': case '2': case '3': case '4': case '5': case '6':case '7': case '8': case '9': case '.' 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J':\
1470 | case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T':\
1471 | case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': case 'a': case 'b': case 'c': case 'd':\
1472 | case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':\
1473 | case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x':\
1474 | case 'y': case 'z': case '_': case '0': case '1': case '2': case '3': case '4': case '5': case '6':\
1475 | case '7': case '8': case '9': case '.'
1476 |
1477 |
1478 | /**
1479 | * loal_from_file:
1480 | * @param filename the file containing a loals text representation.
1481 | *
1482 | * Given a filename it will attempt to load a loal containing a copy of
1483 | * the avpls represented in the file.
1484 | *
1485 | * Return value: if successful a pointer to the new populated loal, else NULL.
1486 | *
1487 | **/
1488 | extern LoAL* loal_from_file(char* filename) {
1489 | FILE *fp = NULL((void*)0);
1490 | char c;
1491 | int i = 0;
1492 | uint32_t linenum = 1;
1493 | GString *linenum_buf;
1494 | char *name;
1495 | char *value;
1496 | char op = '?';
1497 | LoAL *loal_error, *loal = new_loal(filename);
1498 | AVPL* curr = NULL((void*)0);
1499 | AVP* avp;
1500 |
1501 | enum _load_loal_states {
1502 | START,
1503 | BEFORE_NAME,
1504 | IN_NAME,
1505 | IN_VALUE,
1506 | MY_IGNORE
1507 | } state;
1508 |
1509 | linenum_buf = g_string_new(NULL((void*)0));
1510 | name = (char*)g_malloc(MAX_ITEM_LEN8192);
1511 | value = (char*)g_malloc(MAX_ITEM_LEN8192);
1512 | #ifndef _WIN32
1513 | if (! getuid()) {
1 | Assuming the condition is false | →
2 | ← | Taking false branch | →
1514 | loal_error = load_loal_error(fp,loal,curr,linenum,"MATE Will not run as root");
1515 | goto error;
1516 | }
1517 | #endif
1518 |
1519 | state = START;
1520 |
1521 | if (( fp = ws_fopenfopen(filename,"r") )) {
3 | ← | Assuming 'fp' is non-null | →
4 | ← | Taking true branch | →
1522 | while(( c = (char) fgetc(fp) )){
5 | ← | Loop condition is true. Entering loop body | →
12 | ← | Execution continues on line 1522 | →
13 | ← | Assuming this stream operation fails | →
14 | ← | Loop condition is true. Entering loop body | →
20 | ← | Execution continues on line 1522 | →
21 | ← | File position of the stream might be 'indeterminate' after a failed operation. Can cause undefined behavior
1523 |
1524 | if ( feof(fp) ) {
6 | ← | Taking false branch | →
15 | ← | Taking false branch | →
1525 | if ( ferror(fp) ) {
1526 | report_read_failure(filename,errno(*__errno_location ()));
1527 | loal_error = load_loal_error(fp,loal,curr,linenum,"Error while reading '%f'",filename);
1528 | goto error;
1529 | }
1530 | break;
1531 | }
1532 |
1533 | if ( c == '\n' ) {
7 | ← | Assuming the condition is false | →
8 | ← | Taking false branch | →
16 | ← | Taking false branch | →
1534 | linenum++;
1535 | }
1536 |
1537 | if ( i >= MAX_ITEM_LEN8192 - 1 ) {
9 | ← | Taking false branch | →
17 | ← | Taking false branch | →
1538 | loal_error = load_loal_error(fp,loal,curr,linenum,"Maximum item length exceeded");
1539 | goto error;
1540 | }
1541 |
1542 | switch(state) {
10 | ← | Control jumps to 'case START:' at line 1552 | →
18 | ← | Control jumps to 'case MY_IGNORE:' at line 1543 | →
1543 | case MY_IGNORE:
1544 | switch (c) {
19 | ← | Control jumps to the 'default' case at line 1549 | →
1545 | case '\n':
1546 | state = START;
1547 | i = 0;
1548 | continue;
1549 | default:
1550 | continue;
1551 | }
1552 | case START:
1553 | switch (c) {
11 | ← | Control jumps to 'case 35:' at line 1570 | →
1554 | case ' ': case '\t':
1555 | /* ignore whitespace at line start */
1556 | continue;
1557 | case '\n':
1558 | /* ignore empty lines */
1559 | i = 0;
1560 | continue;
1561 | case AVP_NAME_CHAR'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G' : case 'H': case 'I': case 'J':case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T' :case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': case 'a': case 'b': case 'c': case 'd':case 'e': case 'f': case 'g' : case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x':case 'y': case 'z' : case '_': case '0': case '1': case '2': case '3': case '4': case '5': case '6':case '7': case '8': case '9': case '.':
1562 | state = IN_NAME;
1563 | i = 0;
1564 | name[i++] = c;
1565 | name[i] = '\0';
1566 | g_string_append_printf(linenum_buf, "%s:%u", filename, linenum);
1567 | curr = new_avpl(linenum_buf->str);
1568 | g_string_set_size(linenum_buf, 0);
1569 | continue;
1570 | case '#':
1571 | state = MY_IGNORE;
1572 | continue;
1573 | default:
1574 | loal_error = load_loal_error(fp,loal,curr,linenum,"expecting name got: '%c'",c);
1575 | goto error;
1576 | }
1577 | case BEFORE_NAME:
1578 | i = 0;
1579 | name[0] = '\0';
1580 | switch (c) {
1581 | case '\\':
1582 | c = (char) fgetc(fp);
1583 | if (c != '\n') ungetc(c,fp);
1584 | continue;
1585 | case ' ':
1586 | case '\t':
1587 | continue;
1588 | case AVP_NAME_CHAR'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G' : case 'H': case 'I': case 'J':case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T' :case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': case 'a': case 'b': case 'c': case 'd':case 'e': case 'f': case 'g' : case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x':case 'y': case 'z' : case '_': case '0': case '1': case '2': case '3': case '4': case '5': case '6':case '7': case '8': case '9': case '.':
1589 | state = IN_NAME;
1590 |
1591 | name[i++] = c;
1592 | name[i] = '\0';
1593 | continue;
1594 | case '\n':
1595 | loal_append(loal,curr);
1596 | curr = NULL((void*)0);
1597 | state = START;
1598 | continue;
1599 | default:
1600 | loal_error = load_loal_error(fp,loal,curr,linenum,"expecting name got: '%c'",c);
1601 | goto error;
1602 | }
1603 | case IN_NAME:
1604 | switch (c) {
1605 | case ';':
1606 | state = BEFORE_NAME;
1607 |
1608 | op = '?';
1609 | name[i] = '\0';
1610 | value[0] = '\0';
1611 | i = 0;
1612 |
1613 | avp = new_avp(name,value,op);
1614 |
1615 | if (! insert_avp(curr,avp) ) {
1616 | delete_avp(avp);
1617 | }
1618 |
1619 | continue;
1620 | case AVP_OP_CHAR'=': case '^': case '$': case '~': case '<': case '>': case '?': case '|': case '&' : case '!':
1621 | name[i] = '\0';
1622 | i = 0;
1623 | op = c;
1624 | state = IN_VALUE;
1625 | continue;
1626 | case AVP_NAME_CHAR'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G' : case 'H': case 'I': case 'J':case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T' :case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': case 'a': case 'b': case 'c': case 'd':case 'e': case 'f': case 'g' : case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x':case 'y': case 'z' : case '_': case '0': case '1': case '2': case '3': case '4': case '5': case '6':case '7': case '8': case '9': case '.':
1627 | name[i++] = c;
1628 | continue;
1629 | case '\n':
1630 | loal_error = load_loal_error(fp,loal,curr,linenum,"operator expected found new line");
1631 | goto error;
1632 | default:
1633 | loal_error = load_loal_error(fp,loal,curr,linenum,"name or match operator expected found '%c'",c);
1634 | goto error;
1635 | }
1636 | case IN_VALUE:
1637 | switch (c) {
1638 | case '\\':
1639 | value[i++] = (char) fgetc(fp);
1640 | continue;
1641 | case ';':
1642 | state = BEFORE_NAME;
1643 |
1644 | value[i] = '\0';
1645 | i = 0;
1646 |
1647 | avp = new_avp(name,value,op);
1648 |
1649 | if (! insert_avp(curr,avp) ) {
1650 | delete_avp(avp);
1651 | }
1652 | continue;
1653 | case '\n':
1654 | loal_error = load_loal_error(fp,loal,curr,linenum,"';' expected found new line");
1655 | goto error;
1656 | default:
1657 | value[i++] = c;
1658 | continue;
1659 | }
1660 | }
1661 | }
1662 | fclose (fp);
1663 |
1664 | if (curr) {
1665 | // Premature EOF? It could just be a file that doesn't
1666 | // end in a newline, but hard to say without checking
1667 | // state. Error, discard, add to existing loal?
1668 | delete_avpl(curr,true1);
1669 | }
1670 |
1671 | g_string_free(linenum_buf, true)(__builtin_constant_p (1) ? ((1) ? (g_string_free) ((linenum_buf ), (1)) : g_string_free_and_steal (linenum_buf)) : (g_string_free ) ((linenum_buf), (1)));
1672 | g_free(name);
1673 | g_free(value);
1674 |
1675 | return loal;
1676 |
1677 | } else {
1678 | report_open_failure(filename,errno(*__errno_location ()),false0);
1679 | loal_error = load_loal_error(NULL((void*)0),loal,NULL((void*)0),0,"Cannot Open file '%s'",filename);
1680 | }
1681 |
1682 | error:
1683 | g_string_free(linenum_buf, true)(__builtin_constant_p (1) ? ((1) ? (g_string_free) ((linenum_buf ), (1)) : g_string_free_and_steal (linenum_buf)) : (g_string_free ) ((linenum_buf), (1)));
1684 | g_free(name);
1685 | g_free(value);
1686 |
1687 | return loal_error;
1688 | }