### Bug Summary

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