1 /*********************************************************************************************************
2 * Software License Agreement (BSD License) *
3 * Author: Sebastien Decugis <sdecugis@freediameter.net> *
4 * *
5 * Copyright (c) 2017, WIDE Project and NICT *
6 * All rights reserved. *
7 * *
8 * Redistribution and use of this software in source and binary forms, with or without modification, are *
9 * permitted provided that the following conditions are met: *
10 * *
11 * * Redistributions of source code must retain the above *
12 * copyright notice, this list of conditions and the *
13 * following disclaimer. *
14 * *
15 * * Redistributions in binary form must reproduce the above *
16 * copyright notice, this list of conditions and the *
17 * following disclaimer in the documentation and/or other *
18 * materials provided with the distribution. *
19 * *
20 * * Neither the name of the WIDE Project or NICT nor the *
21 * names of its contributors may be used to endorse or *
22 * promote products derived from this software without *
23 * specific prior written permission of WIDE Project and *
24 * NICT. *
25 * *
26 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
27 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
28 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
29 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT *
30 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS *
31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
32 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF *
33 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
34 *********************************************************************************************************/
35
36 #include "dict_lxml.h"
37
38 /*
39 The internal freeDiameter dictionary has strong dependency relations between
40 the different objects, as follow:
41
42 vendor
43 / \
44 application \
45 / \ |
46 command \ |
47 | type |
48 | / \ |
49 \ enumval \ |
50 \ avp
51 \ _____/
52 \ /
53 rule
54
55 It means an AVP cannot be defined unless the parent TYPE has already been defined,
56 in turn depending on parent APPLICATION, etc. (top-to-bottom dependencies on the graph)
57
58 On the other hand, the hierarchy of the XML format described in draft-frascone-xml-dictionary-00
59 does not enforce most of these dependencies, the structure is as follows:
60
61 vendor application
62 / | \
63 command | avp
64 / type \
65 rule enumval
66
67 (in addition if DTD validation was performed, command and avp refer to vendor, avp refers to type,
68 but we do not do it for larger compatibility -- we just report when errors are found)
69
70 As a consequence of this difference, it is impossible to parse the XML tree and create the dictionary objects in freeDiameter
71 in only 1 pass. To avoid parsing the tree several times, we use a temporary structure in memory to contain all the data
72 from the XML file, and when the parsing is complete we store all the objects in the dictionary.
73 */
74
75 /* We use the SAX interface of libxml2 (from GNOME) to parse the XML file. */
76 #include <libxml/parser.h>
77
78 /*******************************************/
79 /* Helper functions */
xmltoint(xmlChar * xmlinteger,uint32_t * conv)80 static int xmltoint(xmlChar * xmlinteger, uint32_t * conv) {
81 TRACE_ENTRY("%p %p", xmlinteger, conv);
82
83 /* Attempt at converting the string to an integer */
84 if (sscanf((char *)xmlinteger, "%u", conv) != 1) {
85 TRACE_DEBUG(INFO, "Unable to convert '%s' to integer.", (char *)xmlinteger)
86 return EINVAL;
87 }
88
89 return 0;
90 }
91
92
93 /*******************************************
94 The temporary structure that is being built when the XML file is parsed
95 *******************************************/
96
97 /* VENDOR */
98 struct t_vend {
99 struct fd_list chain; /* link in the t_dictionary->vendors */
100 uint32_t id;
101 uint8_t * name;
102 };
103
new_vendor(struct fd_list * parent,xmlChar * xmlid,xmlChar * xmlname)104 static int new_vendor(struct fd_list * parent, xmlChar * xmlid, xmlChar * xmlname) {
105 struct t_vend * new;
106 uint32_t id = 0;
107
108 TRACE_ENTRY("%p %p %p", parent, xmlid, xmlname);
109 CHECK_PARAMS( parent && xmlid && xmlname );
110
111 CHECK_FCT( xmltoint(xmlid, &id) );
112
113 CHECK_MALLOC( new = malloc(sizeof(struct t_vend)) );
114 memset(new, 0, sizeof(struct t_vend));
115 fd_list_init(&new->chain, NULL);
116 new->id = id;
117 CHECK_MALLOC( new->name = (uint8_t *)strdup((char *)xmlname) );
118
119 fd_list_insert_before(parent, &new->chain);
120
121 return 0;
122 }
123
dump_vendor(struct t_vend * v)124 static void dump_vendor(struct t_vend * v) {
125 fd_log_debug(" Vendor %d:'%s'", v->id, (char *)v->name);
126 }
127
del_vendor_contents(struct t_vend * v)128 static void del_vendor_contents(struct t_vend * v) {
129 TRACE_ENTRY("%p", v);
130 free(v->name);
131 }
132
133
134 /* RULE */
135 struct t_rule {
136 struct fd_list chain; /* link in either t_cmd or t_avp */
137 uint8_t * avpname;
138 int max;
139 int min;
140 };
141
new_rule(struct fd_list * parent,xmlChar * xmlname,xmlChar * xmlmaximum,xmlChar * xmlminimum)142 static int new_rule(struct fd_list * parent, xmlChar * xmlname, /* position is never used */ xmlChar * xmlmaximum, xmlChar * xmlminimum) {
143 struct t_rule * new;
144 uint32_t min, max;
145
146 TRACE_ENTRY("%p %p %p %p", parent, xmlname, xmlmaximum, xmlminimum);
147 CHECK_PARAMS( parent && xmlname );
148
149 CHECK_MALLOC( new = malloc(sizeof(struct t_rule)) );
150 memset(new, 0, sizeof(struct t_rule));
151 fd_list_init(&new->chain, NULL);
152 if (xmlminimum) {
153 CHECK_FCT( xmltoint(xmlminimum, &min) );
154 new->min = (int) min;
155 } else {
156 new->min = -1;
157 }
158 if (xmlmaximum) {
159 CHECK_FCT( xmltoint(xmlmaximum, &max) );
160 new->max = (int) max;
161 } else {
162 new->max = -1;
163 }
164 CHECK_MALLOC( new->avpname = (uint8_t *)strdup((char *)xmlname) );
165
166 fd_list_insert_before(parent, &new->chain);
167
168 return 0;
169 }
170
dump_rule(struct t_rule * r,char * prefix)171 static void dump_rule(struct t_rule * r, char * prefix) {
172 fd_log_debug("%s ", prefix);
173 if (r->min != -1)
174 fd_log_debug("m:%d ", r->min);
175 if (r->max != -1)
176 fd_log_debug("M:%d ", r->max);
177 fd_log_debug("%s", (char *)r->avpname);
178 }
179
del_rule_contents(struct t_rule * r)180 static void del_rule_contents(struct t_rule * r) {
181 TRACE_ENTRY("%p",r);
182 free(r->avpname);
183 }
184
185
186 /* COMMAND */
187 struct t_cmd {
188 struct fd_list chain; /* link in t_appl->commands */
189 uint32_t code;
190 uint8_t * name;
191 uint8_t flags;
192 uint8_t fmask;
193 struct fd_list reqrules_fixed; /* list of t_rule */
194 struct fd_list reqrules_required; /* list of t_rule */
195 struct fd_list reqrules_optional; /* list of t_rule */
196 struct fd_list ansrules_fixed; /* list of t_rule */
197 struct fd_list ansrules_required; /* list of t_rule */
198 struct fd_list ansrules_optional; /* list of t_rule */
199 };
200
new_cmd(struct fd_list * parent,xmlChar * xmlcode,xmlChar * xmlname,xmlChar * xmlpbit,struct t_cmd ** ret)201 static int new_cmd(struct fd_list * parent, xmlChar * xmlcode, xmlChar * xmlname /*, ignore the vendor id because we don't use it */, xmlChar * xmlpbit, struct t_cmd **ret) {
202 struct t_cmd * new;
203 uint32_t code;
204 uint32_t flag = 0;
205 uint32_t fmask = 0;
206
207 TRACE_ENTRY("%p %p %p %p", parent, xmlcode, xmlname, xmlpbit);
208 CHECK_PARAMS( parent && xmlcode && xmlname );
209
210 CHECK_FCT( xmltoint(xmlcode, &code) );
211
212 if (xmlpbit) {
213 uint32_t val;
214 CHECK_FCT( xmltoint(xmlpbit, &val) );
215 fmask |= CMD_FLAG_PROXIABLE;
216 if (val)
217 flag |= CMD_FLAG_PROXIABLE;
218 }
219
220 CHECK_MALLOC( new = malloc(sizeof(struct t_cmd)) );
221 memset(new, 0, sizeof(struct t_cmd));
222 fd_list_init(&new->chain, NULL);
223 new->code = code;
224 CHECK_MALLOC( new->name = (uint8_t *)strdup((char *)xmlname) );
225 new->flags = flag;
226 new->fmask = fmask;
227 fd_list_init(&new->reqrules_fixed, NULL);
228 fd_list_init(&new->reqrules_required, NULL);
229 fd_list_init(&new->reqrules_optional, NULL);
230 fd_list_init(&new->ansrules_fixed, NULL);
231 fd_list_init(&new->ansrules_required, NULL);
232 fd_list_init(&new->ansrules_optional, NULL);
233
234 fd_list_insert_before(parent, &new->chain);
235
236 *ret = new;
237
238 return 0;
239 }
240
dump_cmd(struct t_cmd * c)241 static void dump_cmd(struct t_cmd * c) {
242 struct fd_list * li;
243 fd_log_debug(" Command %d %s: %s", c->code,
244 c->fmask ? ( c->flags ? "[P=1]" : "[P=0]") : "", c->name);
245 for (li = c->reqrules_fixed.next; li != &c->reqrules_fixed; li = li->next)
246 dump_rule((struct t_rule *)li, " Request fixed AVP:");
247 for (li = c->reqrules_required.next; li != &c->reqrules_required; li = li->next)
248 dump_rule((struct t_rule *)li, " Request required AVP:");
249 for (li = c->reqrules_optional.next; li != &c->reqrules_optional; li = li->next)
250 dump_rule((struct t_rule *)li, " Request optional AVP:");
251 for (li = c->ansrules_fixed.next; li != &c->ansrules_fixed; li = li->next)
252 dump_rule((struct t_rule *)li, " Answer fixed AVP:");
253 for (li = c->ansrules_required.next; li != &c->ansrules_required; li = li->next)
254 dump_rule((struct t_rule *)li, " Answer required AVP:");
255 for (li = c->ansrules_optional.next; li != &c->ansrules_optional; li = li->next)
256 dump_rule((struct t_rule *)li, " Answer optional AVP:");
257 }
258
del_cmd_contents(struct t_cmd * c)259 static void del_cmd_contents(struct t_cmd * c) {
260 TRACE_ENTRY("%p", c);
261 free(c->name);
262 while (!FD_IS_LIST_EMPTY(&c->reqrules_fixed)) {
263 struct fd_list * li = c->reqrules_fixed.next;
264 fd_list_unlink(li);
265 del_rule_contents((struct t_rule *)li);
266 free(li);
267 }
268 while (!FD_IS_LIST_EMPTY(&c->reqrules_required)) {
269 struct fd_list * li = c->reqrules_required.next;
270 fd_list_unlink(li);
271 del_rule_contents((struct t_rule *)li);
272 free(li);
273 }
274 while (!FD_IS_LIST_EMPTY(&c->reqrules_optional)) {
275 struct fd_list * li = c->reqrules_optional.next;
276 fd_list_unlink(li);
277 del_rule_contents((struct t_rule *)li);
278 free(li);
279 }
280 while (!FD_IS_LIST_EMPTY(&c->ansrules_fixed)) {
281 struct fd_list * li = c->ansrules_fixed.next;
282 fd_list_unlink(li);
283 del_rule_contents((struct t_rule *)li);
284 free(li);
285 }
286 while (!FD_IS_LIST_EMPTY(&c->ansrules_required)) {
287 struct fd_list * li = c->ansrules_required.next;
288 fd_list_unlink(li);
289 del_rule_contents((struct t_rule *)li);
290 free(li);
291 }
292 while (!FD_IS_LIST_EMPTY(&c->ansrules_optional)) {
293 struct fd_list * li = c->ansrules_optional.next;
294 fd_list_unlink(li);
295 del_rule_contents((struct t_rule *)li);
296 free(li);
297 }
298 }
299
300 /* TYPE */
301 struct t_typedefn {
302 struct fd_list chain; /* link in t_appl->types */
303 uint8_t * name;
304 uint8_t * parent_name;
305 };
306
new_type(struct fd_list * parent,xmlChar * xmlname,xmlChar * xmlparent)307 static int new_type(struct fd_list * parent, xmlChar * xmlname, xmlChar * xmlparent /*, xmlChar * xmldescription -- ignore */) {
308 struct t_typedefn * new;
309
310 TRACE_ENTRY("%p %p %p", parent, xmlname, xmlparent);
311 CHECK_PARAMS( parent && xmlname );
312
313 CHECK_MALLOC( new = malloc(sizeof(struct t_typedefn)) );
314 memset(new, 0, sizeof(struct t_typedefn));
315 fd_list_init(&new->chain, NULL);
316 CHECK_MALLOC( new->name = (uint8_t *)strdup((char *)xmlname) );
317 if (xmlparent) {
318 CHECK_MALLOC( new->parent_name = (uint8_t *)strdup((char *)xmlparent) );
319 }
320
321 fd_list_insert_before(parent, &new->chain);
322
323 return 0;
324 }
325
dump_type(struct t_typedefn * t)326 static void dump_type(struct t_typedefn * t) {
327 fd_log_debug(" Type %s%s%s%s", (char *)t->name,
328 t->parent_name ? "(parent: " : "",
329 t->parent_name ? (char *)t->parent_name : "",
330 t->parent_name ? ")" : "");
331 }
332
del_type_contents(struct t_typedefn * t)333 static void del_type_contents(struct t_typedefn * t) {
334 TRACE_ENTRY("%p", t);
335 free(t->name);
336 free(t->parent_name);
337 }
338
339
340 /* TYPE INSIDE AVP */
341 struct t_avptype {
342 struct fd_list chain; /* link in t_avp->type */
343 uint8_t * type_name;
344 };
345
new_avptype(struct fd_list * parent,xmlChar * xmlname)346 static int new_avptype(struct fd_list * parent, xmlChar * xmlname) {
347 struct t_avptype * new;
348
349 TRACE_ENTRY("%p %p", parent, xmlname);
350 CHECK_PARAMS( parent && xmlname );
351
352 CHECK_MALLOC( new = malloc(sizeof(struct t_avptype)) );
353 memset(new, 0, sizeof(struct t_avptype));
354 fd_list_init(&new->chain, NULL);
355 CHECK_MALLOC( new->type_name = (uint8_t *)strdup((char *)xmlname) );
356
357 fd_list_insert_before(parent, &new->chain);
358
359 return 0;
360 }
361
dump_avptype(struct t_avptype * t)362 static void dump_avptype(struct t_avptype * t) {
363 fd_log_debug(" data type: %s", t->type_name);
364 }
365
del_avptype_contents(struct t_avptype * t)366 static void del_avptype_contents(struct t_avptype * t) {
367 TRACE_ENTRY("%p", t);
368 free(t->type_name);
369 }
370
371
372 /* ENUM */
373 struct t_enum {
374 struct fd_list chain; /* link in t_avp->enums */
375 uint32_t code;
376 uint8_t * name;
377 };
378
new_enum(struct fd_list * parent,xmlChar * xmlcode,xmlChar * xmlname)379 static int new_enum(struct fd_list * parent, xmlChar * xmlcode, xmlChar * xmlname) {
380 struct t_enum * new;
381 uint32_t code = 0;
382
383 TRACE_ENTRY("%p %p %p", parent, xmlcode, xmlname);
384 CHECK_PARAMS( parent && xmlcode && xmlname );
385
386 CHECK_FCT( xmltoint(xmlcode, &code) );
387
388 CHECK_MALLOC( new = malloc(sizeof(struct t_enum)) );
389 memset(new, 0, sizeof(struct t_enum));
390 fd_list_init(&new->chain, NULL);
391 new->code = code;
392 CHECK_MALLOC( new->name = (uint8_t *)strdup((char *)xmlname) );
393
394 fd_list_insert_before(parent, &new->chain);
395
396 return 0;
397 }
398
dump_enum(struct t_enum * e)399 static void dump_enum(struct t_enum * e) {
400 fd_log_debug(" Value: %d == %s", e->code, e->name);
401 }
402
del_enum_contents(struct t_enum * e)403 static void del_enum_contents(struct t_enum * e) {
404 TRACE_ENTRY("%p", e);
405 free(e->name);
406 }
407
408 /* AVP */
409 struct t_avp {
410 struct fd_list chain; /* link in t_appl->avps */
411 uint32_t code;
412 uint8_t * name;
413 uint8_t flags;
414 uint8_t fmask;
415 uint32_t vendor;
416 struct fd_list type; /* list of t_avptype -- there must be at max 1 item in the list */
417 struct fd_list enums; /* list of t_enum */
418 struct fd_list grouped_fixed; /* list of t_rule */
419 struct fd_list grouped_required; /* list of t_rule */
420 struct fd_list grouped_optional; /* list of t_rule */
421 };
422
new_avp(struct fd_list * parent,xmlChar * xmlcode,xmlChar * xmlname,xmlChar * xmlmandatory,xmlChar * xmlvendor,struct t_avp ** ret)423 static int new_avp(struct fd_list * parent, xmlChar * xmlcode, xmlChar * xmlname, xmlChar * xmlmandatory, xmlChar * xmlvendor, struct t_avp **ret) {
424 /* we ignore description, may-encrypt, protected, ... */
425 struct t_avp * new;
426 uint32_t code;
427 uint32_t vendor = 0;
428 uint32_t flag = 0;
429 uint32_t fmask = 0;
430
431 TRACE_ENTRY("%p %p %p %p %p", parent, xmlcode, xmlname, xmlmandatory, xmlvendor);
432 CHECK_PARAMS( parent && xmlcode && xmlname );
433
434 CHECK_FCT( xmltoint(xmlcode, &code) );
435
436 if (xmlmandatory && !strcasecmp((char *)xmlmandatory, "must")) {
437 flag |= AVP_FLAG_MANDATORY;
438 fmask |= AVP_FLAG_MANDATORY;
439 }
440
441 if (xmlvendor) {
442 CHECK_FCT( xmltoint(xmlvendor, &vendor) );
443 if (vendor)
444 flag |= AVP_FLAG_VENDOR;
445 fmask |= AVP_FLAG_VENDOR;
446 }
447
448 CHECK_MALLOC( new = malloc(sizeof(struct t_avp)) );
449 memset(new, 0, sizeof(struct t_avp));
450 fd_list_init(&new->chain, NULL);
451 new->code = code;
452 CHECK_MALLOC( new->name = (uint8_t *)strdup((char *)xmlname) );
453 new->flags = flag;
454 new->fmask = fmask;
455 new->vendor= vendor;
456 fd_list_init(&new->type, NULL);
457 fd_list_init(&new->enums, NULL);
458 fd_list_init(&new->grouped_fixed, NULL);
459 fd_list_init(&new->grouped_required, NULL);
460 fd_list_init(&new->grouped_optional, NULL);
461
462 fd_list_insert_before(parent, &new->chain);
463
464 *ret = new;
465
466 return 0;
467 }
468
dump_avp(struct t_avp * a)469 static void dump_avp(struct t_avp * a) {
470 struct fd_list * li;
471 fd_log_debug(" AVP %d %s%s: %s", a->code,
472 a->fmask & AVP_FLAG_MANDATORY ? ( a->flags & AVP_FLAG_MANDATORY ? "[M=1]" : "[M=0]") : "",
473 a->fmask & AVP_FLAG_VENDOR ? ( a->flags & AVP_FLAG_VENDOR ? "[V=1]" : "[V=0]") : "",
474 a->name);
475 if (a->fmask & AVP_FLAG_VENDOR)
476 fd_log_debug(" vendor: %d", a->vendor);
477 for (li = a->type.next; li != &a->type; li = li->next)
478 dump_avptype((struct t_avptype *)li);
479 for (li = a->enums.next; li != &a->enums; li = li->next)
480 dump_enum((struct t_enum *)li);
481 for (li = a->grouped_fixed.next; li != &a->grouped_fixed; li = li->next)
482 dump_rule((struct t_rule *)li, " Grouped, fixed AVP:");
483 for (li = a->grouped_required.next; li != &a->grouped_required; li = li->next)
484 dump_rule((struct t_rule *)li, " Grouped, required AVP:");
485 for (li = a->grouped_optional.next; li != &a->grouped_optional; li = li->next)
486 dump_rule((struct t_rule *)li, " Grouped, optional AVP:");
487 }
488
del_avp_contents(struct t_avp * a)489 static void del_avp_contents(struct t_avp * a) {
490 TRACE_ENTRY("%p", a);
491 free(a->name);
492 while (!FD_IS_LIST_EMPTY(&a->type)) {
493 struct fd_list * li = a->type.next;
494 fd_list_unlink(li);
495 del_avptype_contents((struct t_avptype *)li);
496 free(li);
497 }
498 while (!FD_IS_LIST_EMPTY(&a->enums)) {
499 struct fd_list * li = a->enums.next;
500 fd_list_unlink(li);
501 del_enum_contents((struct t_enum *)li);
502 free(li);
503 }
504 while (!FD_IS_LIST_EMPTY(&a->grouped_fixed)) {
505 struct fd_list * li = a->grouped_fixed.next;
506 fd_list_unlink(li);
507 del_rule_contents((struct t_rule *)li);
508 free(li);
509 }
510 while (!FD_IS_LIST_EMPTY(&a->grouped_required)) {
511 struct fd_list * li = a->grouped_required.next;
512 fd_list_unlink(li);
513 del_rule_contents((struct t_rule *)li);
514 free(li);
515 }
516 while (!FD_IS_LIST_EMPTY(&a->grouped_optional)) {
517 struct fd_list * li = a->grouped_optional.next;
518 fd_list_unlink(li);
519 del_rule_contents((struct t_rule *)li);
520 free(li);
521 }
522 }
523
524
525 /* APPLICATION */
526 struct t_appl {
527 struct fd_list chain; /* link in the t_dictionary->base_and_applications, the sentinel corresponds to "base" */
528 uint32_t id;
529 uint8_t * name;
530 struct fd_list commands; /* list of t_cmd */
531 struct fd_list types; /* list of t_typedefn */
532 struct fd_list avps; /* list of t_avp */
533 };
534
new_appl(struct fd_list * parent,xmlChar * xmlid,xmlChar * xmlname,struct t_appl ** ret)535 static int new_appl(struct fd_list * parent, xmlChar * xmlid, xmlChar * xmlname /* We ignore the URI */, struct t_appl **ret) {
536 struct t_appl * new;
537 uint32_t id = 0;
538
539 TRACE_ENTRY("%p %p %p", parent, xmlid, xmlname);
540 CHECK_PARAMS( parent && xmlid && xmlname );
541
542 CHECK_FCT( xmltoint(xmlid, &id) );
543
544 CHECK_MALLOC( new = malloc(sizeof(struct t_appl)) );
545 memset(new, 0, sizeof(struct t_appl));
546 fd_list_init(&new->chain, NULL);
547 new->id = id;
548 CHECK_MALLOC( new->name = (uint8_t *)strdup((char *)xmlname) );
549
550 fd_list_init(&new->commands, NULL);
551 fd_list_init(&new->types, NULL);
552 fd_list_init(&new->avps, NULL);
553
554 fd_list_insert_before(parent, &new->chain);
555
556 *ret = new;
557
558 return 0;
559 }
560
dump_appl(struct t_appl * a)561 static void dump_appl(struct t_appl * a) {
562 struct fd_list * li;
563 fd_log_debug(" Application %d: %s", a->id, a->name);
564 for (li = a->commands.next; li != &a->commands; li = li->next)
565 dump_cmd((struct t_cmd *)li);
566 for (li = a->types.next; li != &a->types; li = li->next)
567 dump_type((struct t_typedefn *)li);
568 for (li = a->avps.next; li != &a->avps; li = li->next)
569 dump_avp((struct t_avp *)li);
570 }
571
del_appl_contents(struct t_appl * a)572 static void del_appl_contents(struct t_appl * a) {
573 TRACE_ENTRY("%p", a);
574 free(a->name);
575 while (!FD_IS_LIST_EMPTY(&a->commands)) {
576 struct fd_list * li = a->commands.next;
577 fd_list_unlink(li);
578 del_cmd_contents((struct t_cmd *)li);
579 free(li);
580 }
581 while (!FD_IS_LIST_EMPTY(&a->types)) {
582 struct fd_list * li = a->types.next;
583 fd_list_unlink(li);
584 del_type_contents((struct t_typedefn *)li);
585 free(li);
586 }
587 while (!FD_IS_LIST_EMPTY(&a->avps)) {
588 struct fd_list * li = a->avps.next;
589 fd_list_unlink(li);
590 del_avp_contents((struct t_avp *)li);
591 free(li);
592 }
593 }
594
595 /* DICTIONARY */
596 struct t_dictionary {
597 struct fd_list vendors;
598 struct t_appl base_and_applications;
599 };
600
dump_dict(struct t_dictionary * d)601 static void dump_dict(struct t_dictionary * d) {
602 struct fd_list * li;
603 for (li = d->vendors.next; li != &d->vendors; li = li->next)
604 dump_vendor((struct t_vend *)li);
605 dump_appl(&d->base_and_applications);
606 for (li = d->base_and_applications.chain.next; li != &d->base_and_applications.chain; li = li->next)
607 dump_appl((struct t_appl *)li);
608 }
609
del_dict_contents(struct t_dictionary * d)610 static void del_dict_contents(struct t_dictionary * d) {
611 TRACE_ENTRY("%p", d);
612 while (!FD_IS_LIST_EMPTY(&d->vendors)) {
613 struct fd_list * li = d->vendors.next;
614 fd_list_unlink(li);
615 del_vendor_contents((struct t_vend *)li);
616 free(li);
617 }
618 while (!FD_IS_LIST_EMPTY(&d->base_and_applications.chain)) {
619 struct fd_list * li = d->base_and_applications.chain.next;
620 fd_list_unlink(li);
621 del_appl_contents((struct t_appl *)li);
622 free(li);
623 }
624 d->base_and_applications.name = NULL;
625 del_appl_contents(&d->base_and_applications);
626 }
627
628 /*********************************************/
629
630 /* The states for the SAX parser, corresponding roughly to the expected structure of the XML file.
631 We use the states mostly to validate the XML file. */
632 enum state {
633 INIT = 0,
634 START, /* In "dictionary" */
635 IN_VENDOR,
636 IN_APPLICATION, /* note that "base" is equivalent to "application" for our state machine */
637 IN_COMMAND,
638 IN_REQRULES,
639 IN_REQRULES_FIXED,
640 IN_REQRULES_REQUIRED,
641 IN_REQRULES_OPTIONAL,
642 IN_ANSRULES,
643 IN_ANSRULES_FIXED,
644 IN_ANSRULES_REQUIRED,
645 IN_ANSRULES_OPTIONAL,
646 IN_TYPEDEFN,
647 IN_AVP,
648 IN_AVP_TYPE,
649 IN_AVP_ENUM,
650 IN_AVP_GROUPED,
651 IN_AVP_GROUPED_FIXED,
652 IN_AVP_GROUPED_REQUIRED,
653 IN_AVP_GROUPED_OPTIONAL
654 };
655
656
657 /* The context passed to the SAX parser */
658 struct parser_ctx {
659 enum state state; /* the current state */
660 int error_depth; /* if non 0, we are in an unexpected element, wait until the count goes back to 0 to resume normal parsing. */
661 struct t_dictionary dict; /* The dictionary being built */
662 struct t_appl * cur_app;
663 struct t_cmd * cur_cmd;
664 struct t_avp * cur_avp;
665 char * xmlfilename; /* Name of the file, for error messages */
666 };
667
668 /* Find an attribute with given name in the list */
get_attr(const xmlChar ** atts_array,const char * attr_name,xmlChar ** attr_val)669 static void get_attr(const xmlChar ** atts_array, const char * attr_name, xmlChar ** attr_val) {
670 int i;
671 *attr_val = NULL;
672 if (atts_array == NULL)
673 return;
674 for (i=0; atts_array[i] != NULL; i+=2) {
675 if (!strcasecmp((char *)atts_array[i], attr_name)) {
676 /* found */
677 *attr_val = (xmlChar *)atts_array[i+1];
678 return;
679 }
680 }
681 /* not found */
682 return;
683 }
684
685 /* The following macro avoids duplicating a lot of code in the state machine */
686 #define ADD_RULE( _parent_list ) { \
687 xmlChar *xname, *xmin, *xmax; \
688 /* We are expecting an <avprule> tag at this point */ \
689 if (strcasecmp((char *)name, "avprule")) \
690 goto xml_tree_error; \
691 /* Search the expected attributes */ \
692 get_attr(atts, "name", &xname); \
693 get_attr(atts, "maximum", &xmax); \
694 get_attr(atts, "minimum", &xmin); \
695 /* Check the mandatory name is here */ \
696 CHECK_PARAMS_DO(xname, \
697 { TRACE_DEBUG(INFO, "Invalid 'avprule' tag found without 'name' attribute."); goto xml_tree_error; } ); \
698 /* Create the rule and add into the parent list */ \
699 CHECK_FCT_DO( new_rule((_parent_list), xname, xmax, xmin),\
700 { TRACE_DEBUG(INFO, "An error occurred while parsing an avprule tag. Entry ignored."); goto xml_tree_error; } ); \
701 /* Done. we don't change the state */ \
702 }
703
704
705 /* The function called on each XML element start tag (startElementSAXFunc) */
SAXstartelem(void * ctx,const xmlChar * name,const xmlChar ** atts)706 static void SAXstartelem (void * ctx, const xmlChar * name, const xmlChar ** atts)
707 {
708 struct parser_ctx * data = ctx;
709 TRACE_ENTRY("%p %p %p", ctx, name, atts);
710 CHECK_PARAMS_DO( ctx && name, { return; } );
711
712 TRACE_DEBUG(CALL, "Tag: <%s>", (char *)name);
713
714 if (data->error_depth) /* we are in an unknown element, just skip until it is closed */
715 goto xml_tree_error;
716
717 switch (data->state) {
718 case INIT: /* we are just starting. We only expect a <dictionary> tag, reject anything else. */
719 if (strcasecmp((char *)name, "dictionary"))
720 goto xml_tree_error;
721
722 data->state = START;
723 break;
724
725 case START:
726 /* We are in <dictionary>
727 Valid tags are: <vendor>, <base>, <application> */
728 if (!strcasecmp((char *)name, "vendor")) {
729 xmlChar *xid, *xname;
730
731 get_attr(atts, "id", &xid);
732 get_attr(atts, "name", &xname);
733
734 /* id and name are required */
735 CHECK_PARAMS_DO(xid && xname,
736 { TRACE_DEBUG(INFO, "Invalid 'vendor' tag found without 'id' or 'name' attribute."); goto xml_tree_error; } );
737
738
739 CHECK_FCT_DO( new_vendor(&data->dict.vendors, xid, xname),
740 { TRACE_DEBUG(INFO, "An error occurred while parsing a vendor tag. Entry ignored."); goto xml_tree_error; } )
741
742 data->state = IN_VENDOR;
743 break;
744 }
745
746 if (!strcasecmp((char *)name, "base")) {
747 /* we don't care for the 'uri' attribute */
748 data->cur_app = &data->dict.base_and_applications;
749 data->state = IN_APPLICATION;
750 break;
751 }
752
753 if (!strcasecmp((char *)name, "application")) {
754 /* we don't care for the 'uri' attribute */
755 xmlChar *xid, *xname;
756 char buf[50];
757
758 get_attr(atts, "id", &xid);
759 get_attr(atts, "name", &xname);
760
761 CHECK_PARAMS_DO(xid,
762 { TRACE_DEBUG(INFO, "Invalid 'application' tag found without 'id' attribute."); goto xml_tree_error; } );
763
764 /* Name is optional, if not provided we create a name */
765 if (!xname) {
766 snprintf(buf, sizeof(buf), "Application %s", xid);
767 xname = (xmlChar *)buf;
768 }
769
770 CHECK_FCT_DO( new_appl(&data->dict.base_and_applications.chain, xid, xname, &data->cur_app),
771 { TRACE_DEBUG(INFO, "An error occurred while parsing an application tag. Entry ignored."); goto xml_tree_error; } )
772
773 data->state = IN_APPLICATION;
774 break;
775 }
776
777 /* Other tags are errors */
778 goto xml_tree_error;
779
780
781 case IN_VENDOR: /* nothing is allowed inside <vendor> */
782 goto xml_tree_error;
783
784 case IN_APPLICATION:
785 /* We are in <base> or <application>
786 Valid tags are: <command>, <typedefn>, <avp> */
787 if (!strcasecmp((char *)name, "command")) {
788 /* we don't care for the 'vendor-id' attribute. */
789 xmlChar *xcode, *xname, *xpbit;
790
791 get_attr(atts, "code", &xcode);
792 get_attr(atts, "name", &xname);
793 get_attr(atts, "pbit", &xpbit);
794
795 /* code and name are required */
796 CHECK_PARAMS_DO(xcode && xname,
797 { TRACE_DEBUG(INFO, "Invalid 'command' tag found without 'code' or 'name' attribute."); goto xml_tree_error; } );
798
799 CHECK_FCT_DO( new_cmd( &data->cur_app->commands, xcode, xname, xpbit, &data->cur_cmd),
800 { TRACE_DEBUG(INFO, "An error occurred while parsing a command tag. Entry ignored."); goto xml_tree_error; } )
801
802 data->state = IN_COMMAND;
803 break;
804 }
805
806 if (!strcasecmp((char *)name, "typedefn")) {
807 /* we don't care for the 'description' attribute. */
808 xmlChar *xname, *xparent;
809
810 get_attr(atts, "type-name", &xname);
811 get_attr(atts, "type-parent", &xparent);
812
813 /* name is required */
814 CHECK_PARAMS_DO(xname,
815 { TRACE_DEBUG(INFO, "Invalid 'typedefn' tag found without 'name' attribute."); goto xml_tree_error; } );
816
817 CHECK_FCT_DO( new_type( &data->cur_app->types, xname, xparent),
818 { TRACE_DEBUG(INFO, "An error occurred while parsing a typedefn tag. Entry ignored."); goto xml_tree_error; } )
819
820 data->state = IN_TYPEDEFN;
821 break;
822 }
823
824 if (!strcasecmp((char *)name, "avp")) {
825 /* we don't care for the description, may-encrypt, and protected attributes */
826 xmlChar *xname, *xcode, *xmandatory, *xvendor;
827
828 get_attr(atts, "name", &xname);
829 get_attr(atts, "code", &xcode);
830 get_attr(atts, "mandatory", &xmandatory);
831 get_attr(atts, "vendor-id", &xvendor);
832
833 /* code and name are required */
834 CHECK_PARAMS_DO(xcode && xname,
835 { TRACE_DEBUG(INFO, "Invalid 'avp' tag found without 'code' or 'name' attribute."); goto xml_tree_error; } );
836
837 CHECK_FCT_DO( new_avp(&data->cur_app->avps, xcode, xname, xmandatory, xvendor, &data->cur_avp),
838 { TRACE_DEBUG(INFO, "An error occurred while parsing an avp tag. Entry ignored."); goto xml_tree_error; } )
839
840 data->state = IN_AVP;
841 break;
842 }
843 /* Other tags are errors */
844 goto xml_tree_error;
845
846
847 case IN_COMMAND:
848 /* We are in <command>
849 Valid tags are: <requestrules>, <answerrules> */
850 if (!strcasecmp((char *)name, "requestrules")) {
851 data->state = IN_REQRULES;
852 break;
853 }
854 if (!strcasecmp((char *)name, "answerrules")) {
855 data->state = IN_ANSRULES;
856 break;
857 }
858 /* Other tags are errors */
859 goto xml_tree_error;
860
861 case IN_REQRULES:
862 /* We are in <requestrules>
863 Valid tags are: <fixed>, <required>, <optional> */
864 if (!strcasecmp((char *)name, "fixed")) {
865 data->state = IN_REQRULES_FIXED;
866 break;
867 }
868 if (!strcasecmp((char *)name, "required")) {
869 data->state = IN_REQRULES_REQUIRED;
870 break;
871 }
872 if (!strcasecmp((char *)name, "optional")) {
873 data->state = IN_REQRULES_OPTIONAL;
874 break;
875 }
876 /* Other tags are errors */
877 goto xml_tree_error;
878
879 case IN_ANSRULES:
880 /* We are in <answerrules>
881 Valid tags are: <fixed>, <required>, <optional> */
882 if (!strcasecmp((char *)name, "fixed")) {
883 data->state = IN_ANSRULES_FIXED;
884 break;
885 }
886 if (!strcasecmp((char *)name, "required")) {
887 data->state = IN_ANSRULES_REQUIRED;
888 break;
889 }
890 if (!strcasecmp((char *)name, "optional")) {
891 data->state = IN_ANSRULES_OPTIONAL;
892 break;
893 }
894 /* Other tags are errors */
895 goto xml_tree_error;
896
897 case IN_REQRULES_FIXED:
898 /* We are in <command><answerrules><fixed>
899 Valid tags are: <avprule> */
900 ADD_RULE( &data->cur_cmd->reqrules_fixed );
901 break;
902 case IN_REQRULES_REQUIRED:
903 ADD_RULE( &data->cur_cmd->reqrules_required );
904 break;
905 case IN_REQRULES_OPTIONAL:
906 ADD_RULE( &data->cur_cmd->reqrules_optional );
907 break;
908 case IN_ANSRULES_FIXED:
909 ADD_RULE( &data->cur_cmd->ansrules_fixed );
910 break;
911 case IN_ANSRULES_REQUIRED:
912 ADD_RULE( &data->cur_cmd->ansrules_required );
913 break;
914 case IN_ANSRULES_OPTIONAL:
915 ADD_RULE( &data->cur_cmd->ansrules_optional );
916 break;
917
918
919 case IN_TYPEDEFN: /* nothing is allowed inside <typedefn> */
920 goto xml_tree_error;
921
922
923 case IN_AVP:
924 /* We are in <avp>
925 Valid tags are: <type>, <enum>, <grouped> */
926 if (!strcasecmp((char *)name, "type")) {
927 xmlChar *xname;
928
929 get_attr(atts, "type-name", &xname);
930
931 /* name is required */
932 CHECK_PARAMS_DO(xname,
933 { TRACE_DEBUG(INFO, "Invalid 'type' tag found without 'name' attribute."); goto xml_tree_error; } );
934
935 /* Check there is only 1 type */
936 if (!FD_IS_LIST_EMPTY(&data->cur_avp->type)) {
937 TRACE_DEBUG(INFO, "Multiple 'type' tags found for AVP.");
938 goto xml_tree_error;
939 }
940
941 /* Add the new type */
942 CHECK_FCT_DO( new_avptype(&data->cur_avp->type, xname),
943 { TRACE_DEBUG(INFO, "An error occurred while parsing a type tag. Entry ignored."); goto xml_tree_error; } )
944
945 data->state = IN_AVP_TYPE;
946 break;
947 }
948 if (!strcasecmp((char *)name, "enum")) {
949 xmlChar *xcode, *xname;
950
951 get_attr(atts, "code", &xcode);
952 get_attr(atts, "name", &xname);
953
954 /* code and name are required */
955 CHECK_PARAMS_DO(xcode && xname,
956 { TRACE_DEBUG(INFO, "Invalid 'enum' tag found without 'code' or 'name' attribute."); goto xml_tree_error; } );
957
958 CHECK_FCT_DO( new_enum(&data->cur_avp->enums, xcode, xname),
959 { TRACE_DEBUG(INFO, "An error occurred while parsing a command tag. Entry ignored."); goto xml_tree_error; } )
960
961 data->state = IN_AVP_ENUM;
962 break;
963 }
964 if (!strcasecmp((char *)name, "grouped")) {
965 /* no attribute for this one */
966 data->state = IN_AVP_GROUPED;
967 break;
968 }
969 /* Other tags are errors */
970 goto xml_tree_error;
971
972 case IN_AVP_TYPE: /* nothing is allowed inside <type> */
973 goto xml_tree_error;
974
975 case IN_AVP_ENUM: /* nothing is allowed inside <enum> */
976 goto xml_tree_error;
977
978 case IN_AVP_GROUPED:
979 /* We are in <avp><grouped>
980 Valid tags are: <fixed>, <required>, <optional> */
981 if (!strcasecmp((char *)name, "fixed")) {
982 data->state = IN_AVP_GROUPED_FIXED;
983 break;
984 }
985 if (!strcasecmp((char *)name, "required")) {
986 data->state = IN_AVP_GROUPED_REQUIRED;
987 break;
988 }
989 if (!strcasecmp((char *)name, "optional")) {
990 data->state = IN_AVP_GROUPED_OPTIONAL;
991 break;
992 }
993 /* Other tags are errors */
994 goto xml_tree_error;
995
996 case IN_AVP_GROUPED_FIXED:
997 /* We are in <avp><grouped><fixed>
998 Valid tags are: <avprule> */
999 ADD_RULE( &data->cur_avp->grouped_fixed );
1000 break;
1001 case IN_AVP_GROUPED_REQUIRED:
1002 ADD_RULE( &data->cur_avp->grouped_required );
1003 break;
1004 case IN_AVP_GROUPED_OPTIONAL:
1005 ADD_RULE( &data->cur_avp->grouped_optional );
1006 break;
1007
1008
1009 default:
1010 TRACE_DEBUG(INFO, "Internal parsing error, unexpected state %d.", data->state);
1011 }
1012
1013 return;
1014
1015 xml_tree_error:
1016 if (!data->error_depth) {
1017 TRACE_DEBUG(INFO, "Unexpected XML element found: '%s'. Ignoring...", name);
1018 }
1019 data->error_depth += 1;
1020 if (data->cur_app || data->cur_cmd || data->cur_avp) {
1021 TRACE_DEBUG(INFO, "Error encountered while parsing tag of:");
1022 if (data->cur_app)
1023 fd_log_debug(" Application: '%s'", data->cur_app->name);
1024 if (data->cur_cmd)
1025 fd_log_debug(" Command : '%s'", data->cur_cmd->name);
1026 if (data->cur_avp)
1027 fd_log_debug(" AVP : '%s'", data->cur_avp->name);
1028 }
1029 return;
1030 }
1031
1032 /* The function called on each XML element end tag (endElementSAXFunc) */
SAXendelem(void * ctx,const xmlChar * name)1033 static void SAXendelem (void * ctx, const xmlChar * name)
1034 {
1035 struct parser_ctx * data = ctx;
1036 TRACE_ENTRY("%p %p", ctx, name);
1037 CHECK_PARAMS_DO( ctx && name, { return; } );
1038
1039 TRACE_DEBUG(CALL, "Tag: </%s>", (char *)name);
1040
1041 if (data->error_depth) {
1042 /* we are recovering from an erroneous element */
1043 data->error_depth -= 1;
1044 return;
1045 }
1046
1047 switch (data->state) {
1048 case INIT:
1049 goto state_machine_error;
1050
1051 case START:
1052 if (strcasecmp((char *)name, "dictionary"))
1053 goto state_machine_error;
1054
1055 data->state = 0;
1056 break;
1057
1058 case IN_VENDOR:
1059 if (strcasecmp((char *)name, "vendor"))
1060 goto state_machine_error;
1061
1062 data->state = START;
1063 break;
1064
1065 case IN_APPLICATION:
1066 if (strcasecmp((char *)name, "base") && strcasecmp((char *)name, "application"))
1067 goto state_machine_error;
1068
1069 data->cur_app = NULL;
1070 data->state = START;
1071 break;
1072
1073 case IN_COMMAND:
1074 if (strcasecmp((char *)name, "command"))
1075 goto state_machine_error;
1076
1077 data->cur_cmd = NULL;
1078 data->state = IN_APPLICATION;
1079 break;
1080
1081 case IN_REQRULES:
1082 if (strcasecmp((char *)name, "requestrules"))
1083 goto state_machine_error;
1084
1085 data->state = IN_COMMAND;
1086 break;
1087
1088 case IN_REQRULES_FIXED:
1089 if (!strcasecmp((char *)name, "avprule"))
1090 /* we don't have a special state for these, just ignore */
1091 return;
1092 if (strcasecmp((char *)name, "fixed"))
1093 goto state_machine_error;
1094 data->state = IN_REQRULES;
1095 break;
1096 case IN_REQRULES_REQUIRED:
1097 if (!strcasecmp((char *)name, "avprule"))
1098 /* we don't have a special state for these, just ignore */
1099 return;
1100 if (strcasecmp((char *)name, "required"))
1101 goto state_machine_error;
1102 data->state = IN_REQRULES;
1103 break;
1104 case IN_REQRULES_OPTIONAL:
1105 if (!strcasecmp((char *)name, "avprule"))
1106 /* we don't have a special state for these, just ignore */
1107 return;
1108 if (strcasecmp((char *)name, "optional"))
1109 goto state_machine_error;
1110 data->state = IN_REQRULES;
1111 break;
1112
1113 case IN_ANSRULES:
1114 if (strcasecmp((char *)name, "answerrules"))
1115 goto state_machine_error;
1116
1117 data->state = IN_COMMAND;
1118 break;
1119 case IN_ANSRULES_FIXED:
1120 if (!strcasecmp((char *)name, "avprule"))
1121 /* we don't have a special state for these, just ignore */
1122 return;
1123 if (strcasecmp((char *)name, "fixed"))
1124 goto state_machine_error;
1125 data->state = IN_ANSRULES;
1126 break;
1127 case IN_ANSRULES_REQUIRED:
1128 if (!strcasecmp((char *)name, "avprule"))
1129 /* we don't have a special state for these, just ignore */
1130 return;
1131 if (strcasecmp((char *)name, "required"))
1132 goto state_machine_error;
1133 data->state = IN_ANSRULES;
1134 break;
1135 case IN_ANSRULES_OPTIONAL:
1136 if (!strcasecmp((char *)name, "avprule"))
1137 /* we don't have a special state for these, just ignore */
1138 return;
1139 if (strcasecmp((char *)name, "optional"))
1140 goto state_machine_error;
1141 data->state = IN_ANSRULES;
1142 break;
1143
1144
1145 case IN_TYPEDEFN:
1146 if (strcasecmp((char *)name, "typedefn"))
1147 goto state_machine_error;
1148
1149 data->state = IN_APPLICATION;
1150 break;
1151
1152 case IN_AVP:
1153 if (strcasecmp((char *)name, "avp"))
1154 goto state_machine_error;
1155
1156 data->cur_avp = NULL;
1157 data->state = IN_APPLICATION;
1158 break;
1159
1160 case IN_AVP_TYPE:
1161 if (strcasecmp((char *)name, "type"))
1162 goto state_machine_error;
1163
1164 data->state = IN_AVP;
1165 break;
1166
1167 case IN_AVP_ENUM:
1168 if (strcasecmp((char *)name, "enum"))
1169 goto state_machine_error;
1170
1171 data->state = IN_AVP;
1172 break;
1173
1174 case IN_AVP_GROUPED:
1175 if (strcasecmp((char *)name, "grouped"))
1176 goto state_machine_error;
1177
1178 data->state = IN_AVP;
1179 break;
1180
1181 case IN_AVP_GROUPED_FIXED:
1182 if (!strcasecmp((char *)name, "avprule"))
1183 /* we don't have a special state for these, just ignore */
1184 return;
1185 if (strcasecmp((char *)name, "fixed"))
1186 goto state_machine_error;
1187 data->state = IN_AVP_GROUPED;
1188 break;
1189 case IN_AVP_GROUPED_REQUIRED:
1190 if (!strcasecmp((char *)name, "avprule"))
1191 return;
1192 if (strcasecmp((char *)name, "required"))
1193 goto state_machine_error;
1194 data->state = IN_AVP_GROUPED;
1195 break;
1196 case IN_AVP_GROUPED_OPTIONAL:
1197 if (!strcasecmp((char *)name, "avprule"))
1198 return;
1199 if (strcasecmp((char *)name, "optional"))
1200 goto state_machine_error;
1201 data->state = IN_AVP_GROUPED;
1202 break;
1203
1204 default:
1205 TRACE_DEBUG(INFO, "Internal parsing error, unexpected state %d.", data->state);
1206 }
1207
1208 return;
1209
1210 state_machine_error:
1211 TRACE_DEBUG(INFO, "Internal parsing error, ignored [state %d, closing tag '%s'].", data->state, name);
1212 return;
1213 }
1214
1215 /* The SAX parser sends a warning, error, fatalerror -- do we need these ?
1216 static void SAXwarning (void * ctx, const char * msg, ...)
1217 {
1218
1219 }
1220 static void SAXerror (void * ctx, const char * msg, ...)
1221 {
1222
1223 }
1224 static void SAXfatal (void * ctx, const char * msg, ...)
1225 {
1226
1227 }
1228 */
1229
1230
1231
1232
1233 /*********************************************/
1234 /* 2nd pass: from memory to fD dictionary */
1235 /*********************************************/
1236
1237 /* Find or create a vendor */
vend_to_fD(struct t_vend * v,struct dictionary * fD_dict,struct dict_object ** fd_v,int * nb_added)1238 static int vend_to_fD(struct t_vend * v, struct dictionary * fD_dict, struct dict_object ** fd_v, int * nb_added)
1239 {
1240 int ret;
1241 struct dict_object * prev = NULL;
1242 struct dict_vendor_data vd;
1243
1244 TRACE_ENTRY("%p %p %p %p", v, fD_dict, fd_v, nb_added);
1245
1246 CHECK_PARAMS(v && fD_dict);
1247
1248 /* Prepare the data in fD's format */
1249 memset(&vd, 0, sizeof(vd));
1250 vd.vendor_id = v->id;
1251 vd.vendor_name = (char *)v->name;
1252
1253 /* Create or search in the dictionary */
1254 ret = fd_dict_new ( fD_dict, DICT_VENDOR, &vd, NULL, &prev );
1255 if (fd_v)
1256 *fd_v = prev;
1257 if (ret == EEXIST) {
1258 /* Conflict with existing entry */
1259 CHECK_FCT( fd_dict_getval(prev, &vd) );
1260 TRACE_DEBUG(INFO, "[dict_legacy_xml] Warning: Conflicting entry.");
1261 TRACE_DEBUG(INFO, "[dict_legacy_xml] New entry (ignored): %u - '%s'", v->id, (char *)v->name);
1262 TRACE_DEBUG(INFO, "[dict_legacy_xml] Old entry : %u - '%s'", vd.vendor_id, vd.vendor_name);
1263 return 0;
1264 } else {
1265 /* other errors are stoppers */
1266 CHECK_FCT(ret);
1267 }
1268
1269 /* Update count */
1270 if (nb_added)
1271 *nb_added += 1;
1272
1273 /* Done */
1274 return 0;
1275 }
1276
1277 /* Find the base fD type from a type name */
resolve_base_type(struct dictionary * fD_dict,uint8_t * type_name,enum dict_avp_basetype * basetype,struct dict_object ** type)1278 static int resolve_base_type(struct dictionary * fD_dict, uint8_t * type_name, enum dict_avp_basetype * basetype, struct dict_object **type)
1279 {
1280 int ret;
1281 struct dict_type_data td;
1282 struct dict_object *t;
1283
1284 TRACE_ENTRY("%p, %p %p", fD_dict, type_name, basetype);
1285 CHECK_PARAMS( fD_dict && type_name && basetype );
1286
1287 /* First, check if the type is already in the dictionary */
1288 ret = fd_dict_search ( fD_dict, DICT_TYPE, TYPE_BY_NAME, type_name, &t, ENOENT);
1289 switch (ret) {
1290 case 0: /* the type is already in the dictionary */
1291 CHECK_FCT( fd_dict_getval(t, &td) );
1292 *basetype = td.type_base;
1293 if (type)
1294 *type = t;
1295 return 0;
1296
1297 case ENOENT: /* We did not find it, it is maybe normal */
1298 break;
1299
1300 default:
1301 /* An unexpected error occurred */
1302 CHECK_FCT(ret);
1303 }
1304
1305 /* at this point we did not find the type in the dictionary */
1306 #define PREDEF_TYPES( _typename_, _basetype_ ) \
1307 if (!strcasecmp((char *)type_name, (_typename_))) { \
1308 *basetype = (_basetype_); \
1309 return 0; \
1310 }
1311
1312 PREDEF_TYPES( "OctetString", AVP_TYPE_OCTETSTRING );
1313 PREDEF_TYPES( "Integer32", AVP_TYPE_INTEGER32 );
1314 PREDEF_TYPES( "Integer64", AVP_TYPE_INTEGER64 );
1315 PREDEF_TYPES( "Unsigned32", AVP_TYPE_UNSIGNED32 );
1316 PREDEF_TYPES( "Enumerated", AVP_TYPE_INTEGER32 );
1317 PREDEF_TYPES( "Unsigned64", AVP_TYPE_UNSIGNED64 );
1318 PREDEF_TYPES( "Float32", AVP_TYPE_FLOAT32 );
1319 PREDEF_TYPES( "Float64", AVP_TYPE_FLOAT64 );
1320
1321 /* When we reach this point, we have not yet found this type anywhere. */
1322 TODO("Type not found. Maybe search in whole xmldictionary if it is defined later?");
1323 TRACE_DEBUG(INFO, "The type '%s' could not be resolved. Please check it is defined before use.", type_name);
1324 return ENOENT;
1325 }
1326
1327 /* Find or create a type. */
typdefn_to_fD(struct t_typedefn * t,struct dictionary * fD_dict,struct dict_object * fd_appl,struct dict_object ** fd_t,int * nb_added)1328 static int typdefn_to_fD(struct t_typedefn * t, struct dictionary * fD_dict, struct dict_object * fd_appl, struct dict_object ** fd_t, int * nb_added)
1329 {
1330 int ret;
1331 struct dict_object * prev = NULL;
1332 struct dict_type_data td;
1333
1334 TRACE_ENTRY("%p %p %p %p %p", t, fD_dict, fd_appl, fd_t, nb_added);
1335
1336 CHECK_PARAMS(t && fD_dict);
1337
1338 /* Prepare the data in fD's format */
1339 memset(&td, 0, sizeof(td));
1340 td.type_name = (char *)t->name;
1341
1342 /* infer td.type_base from t->parent_name */
1343 CHECK_FCT( resolve_base_type(fD_dict, t->parent_name, &td.type_base, NULL) );
1344
1345 /* Create or search in the dictionary */
1346 ret = fd_dict_new ( fD_dict, DICT_TYPE, &td, fd_appl, &prev );
1347 if (fd_t)
1348 *fd_t = prev;
1349 if (ret == EEXIST) {
1350 /* Conflict with existing entry */
1351 enum dict_avp_basetype xmlbt = td.type_base;
1352 extern const char * type_base_name[]; /* in libfreeDiameter/dictionary.c */
1353 CHECK_FCT( fd_dict_getval(prev, &td) );
1354 TRACE_DEBUG(INFO, "[dict_legacy_xml] Warning: Conflicting entry.");
1355 TRACE_DEBUG(INFO, "[dict_legacy_xml] New entry (ignored): '%s' (%d - %s)", t->name, xmlbt, type_base_name[xmlbt] );
1356 TRACE_DEBUG(INFO, "[dict_legacy_xml] Old entry : '%s' (%d - %s)", td.type_name, td.type_base, type_base_name[td.type_base]);
1357 return 0;
1358 } else {
1359 /* other errors are stoppers */
1360 CHECK_FCT(ret);
1361 }
1362
1363 /* Update count */
1364 if (nb_added)
1365 *nb_added += 1;
1366
1367 /* Done */
1368 return 0;
1369 }
1370
1371 /* Process one list of rules */
rules_to_fD_onelist(struct dictionary * fD_dict,struct dict_object * parent,enum rule_position position,struct fd_list * list,int * nb_added)1372 static int rules_to_fD_onelist(struct dictionary * fD_dict, struct dict_object * parent, enum rule_position position, struct fd_list * list, int * nb_added)
1373 {
1374 struct dict_rule_data rd;
1375 struct fd_list * li;
1376 int order = 0;
1377 int ret;
1378
1379 TRACE_ENTRY("%p %p %d %p %p", fD_dict, parent, position, list, nb_added);
1380
1381 CHECK_PARAMS(fD_dict && parent && position && list);
1382
1383 for (li = list->next; li != list; li = li->next) {
1384 struct t_rule * r = (struct t_rule *)li;
1385
1386 /* The [AVP] rule in all ABNF definitions is implicit in freeDiameter, skip it */
1387 if (!strcmp((char *)r->avpname, "AVP"))
1388 continue;
1389
1390 /* Prepare rule data */
1391 memset(&rd, 0, sizeof(rd));
1392 rd.rule_position = position;
1393 rd.rule_order = ++order; /* actually only used for fixed rules, but no harm for others */
1394 rd.rule_min = r->min;
1395 rd.rule_max = r->max;
1396
1397 /* Resolve the AVP */
1398 ret = fd_dict_search(fD_dict, DICT_AVP, AVP_BY_NAME_ALL_VENDORS, r->avpname, &rd.rule_avp, ENOENT);
1399 if (ret == ENOENT) {
1400 TRACE_DEBUG(INFO, "[dict_legacy_xml] Error: AVP '%s' used in a rule before being defined.", r->avpname);
1401 }
1402 CHECK_FCT(ret);
1403
1404 /* Now create the new rule */
1405 CHECK_FCT_DO( ret = fd_dict_new ( fD_dict, DICT_RULE, &rd, parent, NULL ),
1406 { TRACE_DEBUG(INFO, "Error creating rule for sub-AVP '%s'", r->avpname); return ret; } );
1407 if (nb_added)
1408 *nb_added += 1;
1409 }
1410
1411 return 0;
1412 }
1413
1414 /* Process lists of rules */
rules_to_fD(struct dictionary * fD_dict,struct dict_object * parent,struct fd_list * fixed,struct fd_list * required,struct fd_list * optional,int * nb_added)1415 static int rules_to_fD(struct dictionary * fD_dict, struct dict_object * parent, struct fd_list * fixed, struct fd_list * required, struct fd_list * optional, int * nb_added)
1416 {
1417 int ret;
1418
1419 TRACE_ENTRY("%p %p %p %p %p %p", fD_dict, parent, fixed, required, optional, nb_added);
1420
1421 /* Process the rules */
1422 CHECK_FCT_DO( ret = rules_to_fD_onelist(fD_dict, parent, RULE_FIXED_HEAD, fixed, nb_added),
1423 { TRACE_DEBUG(INFO, "Error processing FIXED rules"); return ret; } );
1424 CHECK_FCT_DO( ret = rules_to_fD_onelist(fD_dict, parent, RULE_REQUIRED, required, nb_added),
1425 { TRACE_DEBUG(INFO, "Error processing REQUIRED rules"); return ret; } );
1426 CHECK_FCT_DO( ret = rules_to_fD_onelist(fD_dict, parent, RULE_OPTIONAL, optional, nb_added),
1427 { TRACE_DEBUG(INFO, "Error processing OPTIONAL rules"); return ret; } );
1428
1429 return 0;
1430 }
1431
1432 /* Find or create an AVP (and dependent objects) */
avp_to_fD(struct t_avp * a,struct dictionary * fD_dict,struct dict_object * fd_appl,struct dict_object ** fd_a,int * nb_added)1433 static int avp_to_fD(struct t_avp * a, struct dictionary * fD_dict, struct dict_object * fd_appl, struct dict_object ** fd_a, int * nb_added)
1434 {
1435 int ret;
1436 struct dict_object * prev = NULL, *type = NULL;
1437 struct dict_avp_data ad;
1438 struct fd_list * li;
1439
1440 TRACE_ENTRY("%p %p %p %p %p", a, fD_dict, fd_appl, fd_a, nb_added);
1441
1442 CHECK_PARAMS(a && fD_dict);
1443
1444 /* Prepare the data in fD's format */
1445 memset(&ad, 0, sizeof(ad));
1446 ad.avp_code = a->code;
1447 ad.avp_vendor = a->vendor;
1448 ad.avp_name = (char *)a->name;
1449 ad.avp_flag_mask = a->fmask | AVP_FLAG_VENDOR;
1450 ad.avp_flag_val = a->flags;
1451
1452 if (!FD_IS_LIST_EMPTY(&a->type)) {
1453 /* special exception: we use per-AVP enumerated types in fD */
1454 if (!strcasecmp("Enumerated", (char *)((struct t_avptype *)a->type.next)->type_name))
1455 goto enumerated;
1456 /* Let's allow "Integer32" instead of "Enumerated" also... */
1457 if ((!FD_IS_LIST_EMPTY(&a->enums)) && (!strcasecmp("Integer32", (char *)((struct t_avptype *)a->type.next)->type_name)))
1458 goto enumerated;
1459
1460 /* The type was explicitly specified, resolve it */
1461 CHECK_FCT( resolve_base_type(fD_dict, ((struct t_avptype *)a->type.next)->type_name, &ad.avp_basetype, &type) );
1462 } else {
1463 /* The type was not specified, try to infer it from provided data */
1464 if ( !FD_IS_LIST_EMPTY(&a->grouped_optional)
1465 || !FD_IS_LIST_EMPTY(&a->grouped_required)
1466 || !FD_IS_LIST_EMPTY(&a->grouped_fixed) ) {
1467 /* The AVP has rules, it is a grouped AVP */
1468 CHECK_PARAMS_DO( FD_IS_LIST_EMPTY(&a->enums),
1469 { TRACE_DEBUG(INFO, "Conflict: The AVP '%s' has both enum values and rules.", ad.avp_name); return EINVAL; } );
1470 ad.avp_basetype = AVP_TYPE_GROUPED;
1471 } else {
1472 /* It should be an enumerated AVP... */
1473 if (FD_IS_LIST_EMPTY(&a->enums)) {
1474 TRACE_DEBUG(INFO, "Error: Missing type information for AVP '%s'", ad.avp_name);
1475 return EINVAL;
1476 } else {
1477 /* We create a new type to hold the enumerated values -- fD specifics */
1478 char typename[256];
1479 struct dict_type_data tdata;
1480
1481 enumerated:
1482 snprintf(typename, sizeof(typename), "Enumerated(%s)", ad.avp_name);
1483 memset(&tdata, 0, sizeof(tdata));
1484 tdata.type_base = AVP_TYPE_INTEGER32;
1485 tdata.type_name = &typename[0];
1486 CHECK_FCT( fd_dict_new ( fD_dict, DICT_TYPE, &tdata, fd_appl, &type ) );
1487 if (nb_added)
1488 *nb_added += 1;
1489
1490 ad.avp_basetype = AVP_TYPE_INTEGER32;
1491 }
1492 }
1493 }
1494
1495 /* At this point, ad.avp_basetype is defined and type might also be */
1496
1497 /* Create or search in the dictionary */
1498 ret = fd_dict_new ( fD_dict, DICT_AVP, &ad, type, &prev );
1499 if (fd_a)
1500 *fd_a = prev;
1501 if (ret == EEXIST) {
1502 /* Conflict with existing entry */
1503 CHECK_FCT( fd_dict_getval(prev, &ad) );
1504 TRACE_DEBUG(INFO, "[dict_legacy_xml] Warning: Conflicting entry.");
1505 TRACE_DEBUG(INFO, "[dict_legacy_xml] New entry (ignored): %u - '%s'", a->code, (char *)a->name);
1506 TRACE_DEBUG(INFO, "[dict_legacy_xml] Old entry : %u - '%s'", ad.avp_code, ad.avp_name);
1507 goto inside;
1508 } else {
1509 /* other errors are stoppers */
1510 CHECK_FCT(ret);
1511 }
1512
1513 /* Update count */
1514 if (nb_added)
1515 *nb_added += 1;
1516
1517 inside:
1518 /* Now, the inner elements, if any */
1519
1520 if ( (!FD_IS_LIST_EMPTY(&a->enums)) && (ad.avp_basetype != AVP_TYPE_UNSIGNED32)) {
1521 TRACE_DEBUG(INFO, "AVP '%s' type is not an Unsigned32 but it has enum values (invalid in this extension).", ad.avp_name);
1522 return EINVAL;
1523 }
1524
1525 /* In case of enumeration, define the enum values */
1526 for (li = a->enums.next; li != &a->enums; li = li->next) {
1527 struct t_enum * e = (struct t_enum *)li;
1528 struct dict_enumval_data ed;
1529
1530 memset(&ed, 0, sizeof(ed));
1531 ed.enum_name = (char *)e->name;
1532 ed.enum_value.u32 = e->code;
1533
1534 CHECK_FCT_DO( ret = fd_dict_new ( fD_dict, DICT_ENUMVAL, &ed, type, NULL ),
1535 {
1536 TRACE_DEBUG(INFO, "Error defining constant value '%s' for AVP '%s': %s", ed.enum_name, ad.avp_name, strerror(ret));
1537 return ret;
1538 } );
1539 if (nb_added)
1540 *nb_added += 1;
1541 }
1542
1543 /* In case of grouped AVP, check the type is really grouped */
1544 if ( !FD_IS_LIST_EMPTY(&a->grouped_optional)
1545 || !FD_IS_LIST_EMPTY(&a->grouped_required)
1546 || !FD_IS_LIST_EMPTY(&a->grouped_fixed) ) {
1547 CHECK_PARAMS_DO( ad.avp_basetype == AVP_TYPE_GROUPED,
1548 { TRACE_DEBUG(INFO, "Got rules for non-grouped AVP '%s'", ad.avp_name); return EINVAL;} );
1549 CHECK_FCT_DO( ret = rules_to_fD(fD_dict, prev, &a->grouped_fixed, &a->grouped_required, &a->grouped_optional, nb_added),
1550 { TRACE_DEBUG(INFO, "Error processing rules for AVP '%s': %s", ad.avp_name, strerror(ret)); return ret; } );
1551 }
1552
1553 /* done! */
1554 return 0;
1555 }
1556
1557 /* Find or create a command. */
cmd_to_fD(struct t_cmd * c,struct dictionary * fD_dict,struct dict_object * fd_appl,struct dict_object ** fd_req,int * nb_added)1558 static int cmd_to_fD(struct t_cmd * c, struct dictionary * fD_dict, struct dict_object * fd_appl, struct dict_object ** fd_req, int * nb_added)
1559 {
1560 int ret;
1561 struct dict_object * req = NULL, *ans = NULL;
1562 struct dict_cmd_data cd;
1563 char cmdname[512];
1564
1565 TRACE_ENTRY("%p %p %p %p %p", c, fD_dict, fd_appl, fd_req, nb_added);
1566
1567 CHECK_PARAMS(c && fD_dict);
1568
1569 /* Prepare the request data in fD's format */
1570 memset(&cd, 0, sizeof(cd));
1571 cd.cmd_code = c->code;
1572 snprintf(cmdname, sizeof(cmdname), "%s-Request", (char *)c->name);
1573 cd.cmd_name = &cmdname[0];
1574 cd.cmd_flag_mask = c->fmask | CMD_FLAG_REQUEST | CMD_FLAG_ERROR;
1575 cd.cmd_flag_val = c->flags | CMD_FLAG_REQUEST;
1576
1577 /* Create or search in the dictionary */
1578 ret = fd_dict_new ( fD_dict, DICT_COMMAND, &cd, fd_appl, &req );
1579 if (fd_req)
1580 *fd_req = req;
1581 if (ret == EEXIST) {
1582 struct dict_cmd_data prevcd;
1583 /* Conflict with existing entry */
1584 CHECK_FCT( fd_dict_getval(req, &prevcd) );
1585 TRACE_DEBUG(INFO, "[dict_legacy_xml] Warning: Conflicting entry.");
1586 TRACE_DEBUG(INFO, "[dict_legacy_xml] New entry (ignored): %u - '%s'", cd.cmd_code, cd.cmd_name);
1587 TRACE_DEBUG(INFO, "[dict_legacy_xml] Old entry : %u - '%s'", prevcd.cmd_code, prevcd.cmd_name);
1588 goto answer;
1589 } else {
1590 /* other errors are stoppers */
1591 CHECK_FCT(ret);
1592 }
1593
1594 /* Update count */
1595 if (nb_added)
1596 *nb_added += 1;
1597
1598 answer:
1599 /* update data for the answer */
1600 snprintf(cmdname, sizeof(cmdname), "%s-Answer", (char *)c->name);
1601 cd.cmd_flag_val &= ~CMD_FLAG_REQUEST;
1602 cd.cmd_flag_mask &= ~CMD_FLAG_ERROR;
1603
1604 ret = fd_dict_new ( fD_dict, DICT_COMMAND, &cd, fd_appl, &ans );
1605 if (ret == EEXIST) {
1606 struct dict_cmd_data prevcd;
1607 /* Conflict with existing entry */
1608 CHECK_FCT( fd_dict_getval(ans, &prevcd) );
1609 TRACE_DEBUG(INFO, "[dict_legacy_xml] Warning: Conflicting entry.");
1610 TRACE_DEBUG(INFO, "[dict_legacy_xml] New entry (ignored): %u - '%s'", cd.cmd_code, cd.cmd_name);
1611 TRACE_DEBUG(INFO, "[dict_legacy_xml] Old entry : %u - '%s'", prevcd.cmd_code, prevcd.cmd_name);
1612 goto rules;
1613 } else {
1614 /* other errors are stoppers */
1615 CHECK_FCT(ret);
1616 }
1617
1618 /* Update count */
1619 if (nb_added)
1620 *nb_added += 1;
1621
1622 rules:
1623 /* Now process the rules inside the command */
1624 CHECK_FCT_DO( ret = rules_to_fD(fD_dict, req, &c->reqrules_fixed, &c->reqrules_required, &c->reqrules_optional, nb_added),
1625 {
1626 TRACE_DEBUG(INFO, "Error converting data from request rules: %s", strerror(ret));
1627 return ret;
1628 } );
1629 CHECK_FCT_DO( ret = rules_to_fD(fD_dict, ans, &c->ansrules_fixed, &c->ansrules_required, &c->ansrules_optional, nb_added),
1630 {
1631 TRACE_DEBUG(INFO, "Error converting data from answer rules: %s", strerror(ret));
1632 return ret;
1633 } );
1634
1635 /* Done */
1636 return 0;
1637 }
1638
1639 /* Find or create an application (and dependent objects) */
appl_to_fD(struct t_appl * a,struct dictionary * fD_dict,struct dict_object ** fd_a,int * nb_added)1640 static int appl_to_fD(struct t_appl * a, struct dictionary * fD_dict, struct dict_object ** fd_a, int * nb_added)
1641 {
1642 int ret;
1643 struct dict_object * prev = NULL;
1644 struct dict_application_data ad;
1645 struct fd_list * li;
1646
1647 TRACE_ENTRY("%p %p %p %p", a, fD_dict, fd_a, nb_added);
1648
1649 CHECK_PARAMS(a && fD_dict);
1650
1651 if (a->id) { /* skip app 0 */
1652
1653 /* Prepare the data in fD's format */
1654 memset(&ad, 0, sizeof(ad));
1655 ad.application_id = a->id;
1656 ad.application_name = (char *)a->name;
1657
1658 /* Create or search in the dictionary */
1659 ret = fd_dict_new ( fD_dict,
1660 DICT_APPLICATION,
1661 &ad,
1662 NULL /* we don't have a parent vendor in XML files, so currently everything links to no vendor */,
1663 &prev );
1664 if (fd_a)
1665 *fd_a = prev;
1666 if (ret == EEXIST) {
1667 /* Conflict with existing entry */
1668 CHECK_FCT( fd_dict_getval(prev, &ad) );
1669 TRACE_DEBUG(INFO, "[dict_legacy_xml] Warning: Conflicting entry.");
1670 TRACE_DEBUG(INFO, "[dict_legacy_xml] New entry (ignored): %u - '%s'", a->id, (char *)a->name);
1671 TRACE_DEBUG(INFO, "[dict_legacy_xml] Old entry : %u - '%s'", ad.application_id, ad.application_name);
1672 goto inside;
1673 } else {
1674 /* other errors are stoppers */
1675 CHECK_FCT(ret);
1676 }
1677
1678 /* Update count */
1679 if (nb_added)
1680 *nb_added += 1;
1681 }
1682
1683 inside:
1684 /* Now, the inner elements */
1685
1686 /* First, define all the types */
1687 for (li = a->types.next; li != &a->types; li = li->next) {
1688 CHECK_FCT_DO( ret = typdefn_to_fD((struct t_typedefn *)li, fD_dict, prev, NULL, nb_added),
1689 {
1690 TRACE_DEBUG(INFO, "Error converting data from typedefn '%s': %s", ((struct t_typedefn *)li)->name, strerror(ret));
1691 return ret;
1692 } );
1693 }
1694
1695 /* Then, AVPs, enums, and grouped AVP rules */
1696 for (li = a->avps.next; li != &a->avps; li = li->next) {
1697 CHECK_FCT_DO( ret = avp_to_fD((struct t_avp *)li, fD_dict, prev, NULL, nb_added),
1698 {
1699 TRACE_DEBUG(INFO, "Error converting data from AVP '%s': %s", ((struct t_avp *)li)->name, strerror(ret));
1700 return ret;
1701 } );
1702 }
1703
1704 /* Finally, the commands and rules */
1705 for (li = a->commands.next; li != &a->commands; li = li->next) {
1706 CHECK_FCT_DO( ret = cmd_to_fD((struct t_cmd *)li, fD_dict, prev, NULL, nb_added),
1707 {
1708 TRACE_DEBUG(INFO, "Error converting data from command '%s': %s", ((struct t_cmd *)li)->name, strerror(ret));
1709 return ret;
1710 } );
1711 }
1712
1713 /* done! */
1714 return 0;
1715 }
1716
1717
dict_to_fD(struct dictionary * fD_dict,struct t_dictionary * xmldict,int * nb_added)1718 static int dict_to_fD(struct dictionary * fD_dict, struct t_dictionary * xmldict, int * nb_added)
1719 {
1720 struct fd_list * li;
1721 int ret;
1722
1723 TRACE_ENTRY("%p %p %p", fD_dict, xmldict, nb_added);
1724
1725 CHECK_PARAMS(fD_dict && xmldict && nb_added);
1726
1727 *nb_added = 0;
1728
1729 /* Create all the vendors */
1730 for (li = xmldict->vendors.next; li != &xmldict->vendors; li = li->next) {
1731 CHECK_FCT_DO( ret = vend_to_fD((struct t_vend *)li, fD_dict, NULL, nb_added),
1732 {
1733 TRACE_DEBUG(INFO, "Error converting data from vendor '%s': %s", ((struct t_vend *)li)->name, strerror(ret));
1734 return ret;
1735 } );
1736 }
1737
1738 /* Now, process each application */
1739 CHECK_FCT_DO( ret = appl_to_fD(&xmldict->base_and_applications, fD_dict, NULL, nb_added),
1740 {
1741 TRACE_DEBUG(INFO, "Error converting data from Base application: %s", strerror(ret));
1742 return ret;
1743 } );
1744 for (li = xmldict->base_and_applications.chain.next; li != &xmldict->base_and_applications.chain; li = li->next) {
1745 CHECK_FCT_DO( ret = appl_to_fD((struct t_appl *) li, fD_dict, NULL, nb_added),
1746 {
1747 TRACE_DEBUG(INFO, "Error converting data from application '%s': %s", ((struct t_appl *)li)->name, strerror(ret));
1748 return ret;
1749 } );
1750 }
1751
1752 /* Complete! */
1753 return 0;
1754 }
1755
1756
1757
1758
1759
1760 /*********************************************/
1761
dict_lxml_parse(char * xmlfilename)1762 int dict_lxml_parse(char * xmlfilename)
1763 {
1764 xmlSAXHandler handler;
1765 struct parser_ctx data;
1766 int ret;
1767
1768 TRACE_ENTRY("%p", xmlfilename);
1769
1770 CHECK_PARAMS_DO(xmlfilename, { return -1; } );
1771
1772 TRACE_DEBUG(FULL, "Parsing next XML file: %s...", xmlfilename);
1773
1774 /* Initialize the parser */
1775 memset(&handler, 0, sizeof(handler));
1776 handler.startElement = SAXstartelem;
1777 handler.endElement = SAXendelem;
1778
1779 /* Initialize the data */
1780 memset(&data, 0, sizeof(data));
1781 fd_list_init( &data.dict.vendors, NULL );
1782 fd_list_init( &data.dict.base_and_applications.chain, NULL );
1783 data.dict.base_and_applications.name = (uint8_t *)"[Diameter Base Protocol]";
1784 fd_list_init( &data.dict.base_and_applications.commands, NULL );
1785 fd_list_init( &data.dict.base_and_applications.types, NULL );
1786 fd_list_init( &data.dict.base_and_applications.avps, NULL );
1787 data.xmlfilename = xmlfilename;
1788
1789 /* Parse the file */
1790 ret = xmlSAXUserParseFile(&handler, &data, xmlfilename);
1791 if (ret < 0) {
1792 TRACE_DEBUG(INFO, "An error occurred while parsing %s, aborting.", xmlfilename);
1793 del_dict_contents(&data.dict);
1794 return -1;
1795 }
1796
1797 TRACE_DEBUG(FULL, "XML file parsing, 1st pass completed.");
1798 if (TRACE_BOOL(ANNOYING)) {
1799 dump_dict(&data.dict);
1800 }
1801
1802 /* Now, convert all the objects from the temporary tree into the freeDiameter dictionary */
1803 CHECK_FCT_DO( dict_to_fD(fd_g_config->cnf_dict, &data.dict, &ret),
1804 {
1805 TRACE_DEBUG(INFO, "Error while converting data read from file '%s'", xmlfilename);
1806 del_dict_contents(&data.dict);
1807 return -1;
1808 } );
1809
1810 TRACE_DEBUG(FULL, "Conversion from '%s' to freeDiameter internal format complete.", xmlfilename);
1811
1812 /* Done */
1813 del_dict_contents(&data.dict);
1814
1815 return ret;
1816 }
1817