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