Updated license
[anna.git] / source / diameter / stack / Dictionary.cpp
1 // ANNA - Anna is Not Nothingness Anymore
2 //
3 // (c) Copyright 2005-2014 Eduardo Ramos Testillano & Francisco Ruiz Rayo
4 //
5 // https://bitbucket.org/testillano/anna
6 //
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions
9 // are met:
10 //
11 //     * Redistributions of source code must retain the above copyright
12 // notice, this list of conditions and the following disclaimer.
13 //     * Redistributions in binary form must reproduce the above
14 // copyright notice, this list of conditions and the following disclaimer
15 // in the documentation and/or other materials provided with the
16 // distribution.
17 //     * Neither the name of Google Inc. nor the names of its
18 // contributors may be used to endorse or promote products derived from
19 // this software without specific prior written permission.
20 //
21 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 //
33 // Authors: eduardo.ramos.testillano@gmail.com
34 //          cisco.tierra@gmail.com
35
36
37 // Local
38 #include <anna/diameter/stack/Dictionary.hpp>
39 #include <anna/diameter/stack/Engine.hpp>
40
41 #include <anna/config/defines.hpp>
42 #include <anna/core/tracing/Logger.hpp>
43 #include <anna/xml/xml.hpp>
44 #include <anna/core/functions.hpp>
45
46 #include <anna/config/defines.hpp>
47 #include <anna/core/functions.hpp>
48 #include <anna/diameter/codec/Format.hpp>
49
50 // STL
51 #include <string>
52
53
54 using namespace anna::diameter::stack;
55 using namespace anna;
56
57
58 //------------------------------------------------------------------------------
59 //---------------------------------------------------------------------- #define
60 //------------------------------------------------------------------------------
61 #define ITEM_OVERWRITE(item_n,item,pool,poolNames)\
62 LOGNOTICE(\
63    std::string trace = "\n   Updated ";\
64    trace += item_n;\
65    trace += ":\n";\
66    trace += anna::functions::tab(found->asString(), 6);\
67    trace += "\n   New content:\n";\
68    trace += anna::functions::tab(item.asString(), 6);\
69    trace += "\n";\
70    anna::Logger::notice(trace, ANNA_FILE_LOCATION);\
71 );\
72 pool.erase(pool.find(found->getId()));\
73 poolNames.erase(poolNames.find(found->getName()));
74
75
76 //------------------------------------------------------------------------------
77 //----------------------------------------------------- Dictionary::Dictionary()
78 //------------------------------------------------------------------------------
79 Dictionary::Dictionary(void) {
80   a_dtd = (Engine::instantiate()).getDictionariesDTD();
81   a_allowUpdates = false;
82   initialize();
83 }
84
85 //------------------------------------------------------------------------------
86 //----------------------------------------------------- Dictionary::initialize()
87 //------------------------------------------------------------------------------
88 void Dictionary::initialize() throw() {
89   a_formats.clear();
90   a_vendors.clear();
91   a_avps.clear();
92   a_commands.clear();
93   a_vendorNames.clear();
94   a_avpNames.clear();
95   a_commandNames.clear();
96   a_dtd = (Engine::instantiate()).getDictionariesDTD();
97   // RFC3588 Diameter Formats harcoding:
98   // Basic diameter types
99   Format OctetString(this), Integer32(this), Integer64(this), Unsigned32(this), Unsigned64(this), Float32(this), Float64(this), Grouped(this);
100   OctetString.setRFC3588(codec::Format::OctetString);
101   Integer32.setRFC3588(codec::Format::Integer32);
102   Integer64.setRFC3588(codec::Format::Integer64);
103   Unsigned32.setRFC3588(codec::Format::Unsigned32);
104   Unsigned64.setRFC3588(codec::Format::Unsigned64);
105   Float32.setRFC3588(codec::Format::Float32);
106   Float64.setRFC3588(codec::Format::Float64);
107   Grouped.setRFC3588(codec::Format::Grouped);
108   addFormat(OctetString);
109   addFormat(Integer32);
110   addFormat(Integer64);
111   addFormat(Unsigned32);
112   addFormat(Unsigned64);
113   addFormat(Float32);
114   addFormat(Float64);
115   addFormat(Grouped);
116   // Derived diameter types
117   Format Address(this), Time(this), UTF8String(this), DiameterIdentity(this), DiameterURI(this), Enumerated(this), IPFilterRule(this), QoSFilterRule(this);
118   Address.setRFC3588(codec::Format::Address);                     Address.setParentName(OctetString.getName());
119   Time.setRFC3588(codec::Format::Time);                           Time.setParentName(OctetString.getName());
120   UTF8String.setRFC3588(codec::Format::UTF8String);               UTF8String.setParentName(OctetString.getName());
121   DiameterIdentity.setRFC3588(codec::Format::DiameterIdentity);   DiameterIdentity.setParentName(OctetString.getName());
122   DiameterURI.setRFC3588(codec::Format::DiameterURI);             DiameterURI.setParentName(OctetString.getName());
123   Enumerated.setRFC3588(codec::Format::Enumerated);               Enumerated.setParentName(Integer32.getName());
124   IPFilterRule.setRFC3588(codec::Format::IPFilterRule);           IPFilterRule.setParentName(OctetString.getName());
125   QoSFilterRule.setRFC3588(codec::Format::QoSFilterRule);         QoSFilterRule.setParentName(OctetString.getName());
126   addFormat(Address);
127   addFormat(Time);
128   addFormat(UTF8String);
129   addFormat(DiameterIdentity);
130   addFormat(DiameterURI);
131   addFormat(Enumerated);
132   addFormat(IPFilterRule);
133   addFormat(QoSFilterRule);
134   // Generic AVP hardcoding:
135   Avp genericAvp(this);
136   genericAvp.setCode(0);
137   genericAvp.setVendorId(0/*Vendor::Code::Ietf*/);
138   genericAvp.setName("AVP");
139   Format Any(this);
140   Any.setRFC3588(codec::Format::Any);
141   addFormat(Any, true /*reserved*/);
142   genericAvp.setFormatName(Any.getName());
143   genericAvp.setVbit(Avp::FlagRule::mustnot);
144   genericAvp.setMbit(Avp::FlagRule::may);
145   genericAvp.setPbit(Avp::FlagRule::may);
146   genericAvp.setMayEncrypt(false);
147   addAvp(genericAvp);
148 }
149
150
151 //------------------------------------------------------------------------------
152 //------------------------------------------------------ Dictionary::addFormat()
153 //------------------------------------------------------------------------------
154 void Dictionary::addFormat(const Format & format, bool reserved) throw(anna::RuntimeException) {
155   if(!reserved && format.isReserved()) {
156     std::string s_ex = anna::functions::asString("Format type '%s' is reserved for internal use", format.getName().c_str());
157     throw anna::RuntimeException(s_ex, ANNA_FILE_LOCATION);
158   }
159
160   const Format * found;
161
162   if(found = getFormat(format.getName())) {
163     if(!a_allowUpdates) {
164       std::string s_ex = "Cannot add a format with an existing type name: ";
165       //s_ex += format.getName();
166       s_ex += format.asString();
167       s_ex += "'";
168       throw anna::RuntimeException(s_ex, ANNA_FILE_LOCATION);
169     }
170
171     LOGNOTICE(
172       std::string trace = "\n   Updated format:\n";
173       trace += anna::functions::tab(found->asString(), 6);
174       trace += "\n   New content:\n";
175       trace += anna::functions::tab(format.asString(), 6);
176       trace += "\n";
177       anna::Logger::notice(trace, ANNA_FILE_LOCATION);
178     );
179   }
180
181   a_formats[format.getName()] = format;
182 }
183
184
185 //------------------------------------------------------------------------------
186 //------------------------------------------------------ Dictionary::addVendor()
187 //------------------------------------------------------------------------------
188 void Dictionary::addVendor(const Vendor & vendor) throw(anna::RuntimeException) {
189   const Vendor * found;
190
191   if(found = getVendor(vendor.getId())) {
192     if(!a_allowUpdates) {
193       std::string s_ex = "Cannot add a vendor with an existing code: ";
194       s_ex += vendor.asString();
195       throw anna::RuntimeException(s_ex, ANNA_FILE_LOCATION);
196     }
197
198     ITEM_OVERWRITE("vendor", vendor, a_vendors, a_vendorNames);
199   }
200
201   if(found = getVendor(vendor.getName())) {
202     if(!a_allowUpdates) {
203       std::string s_ex = "Cannot add a vendor with an existing name: ";
204       s_ex += vendor.asString();
205       throw anna::RuntimeException(s_ex, ANNA_FILE_LOCATION);
206     }
207
208     ITEM_OVERWRITE("vendor", vendor, a_vendors, a_vendorNames);
209   }
210
211   a_vendors[vendor.getId()] = vendor;
212   a_vendorNames[vendor.getName()] = getVendor(vendor.getId());
213 }
214
215
216 //------------------------------------------------------------------------------
217 //--------------------------------------------------------- Dictionary::addAvp()
218 //------------------------------------------------------------------------------
219 void Dictionary::addAvp(const Avp & avp) throw(anna::RuntimeException) {
220   const Avp * found;
221
222   if(found = getAvp(avp.getId())) {
223     if(!a_allowUpdates) {
224       std::string s_ex = "Cannot add an avp with an existing identifier (code,vendor):\n";
225       s_ex += avp.asString();
226       throw anna::RuntimeException(s_ex, ANNA_FILE_LOCATION);
227     }
228
229     ITEM_OVERWRITE("avp", avp, a_avps, a_avpNames);
230   }
231
232   if(found = getAvp(avp.getName())) {
233     if(!a_allowUpdates) {
234       std::string s_ex = "Cannot add an avp with an existing name:\n";
235       s_ex += avp.asString();
236       throw anna::RuntimeException(s_ex, ANNA_FILE_LOCATION);
237     }
238
239     ITEM_OVERWRITE("avp", avp, a_avps, a_avpNames);
240   }
241
242   a_avps[avp.getId()] = avp;
243   a_avpNames[avp.getName()] = getAvp(avp.getId());
244 }
245
246
247 //------------------------------------------------------------------------------
248 //----------------------------------------------------- Dictionary::addCommand()
249 //------------------------------------------------------------------------------
250 void Dictionary::addCommand(const Command & command) throw(anna::RuntimeException) {
251   const Command * found;
252
253   if(found = getCommand(command.getId())) {
254     if(!a_allowUpdates) {
255       std::string s_ex = "Cannot add a command with an existing identifier (code,request):\n";
256       s_ex += command.asString();
257       throw anna::RuntimeException(s_ex, ANNA_FILE_LOCATION);
258     }
259
260     ITEM_OVERWRITE("command", command, a_commands, a_commandNames);
261   }
262
263   if(found = getCommand(command.getName())) {
264     if(!a_allowUpdates) {
265       std::string s_ex = "Cannot add a command with an existing name:\n";
266       s_ex += command.asString();
267       throw anna::RuntimeException(s_ex, ANNA_FILE_LOCATION);
268     }
269
270     ITEM_OVERWRITE("command", command, a_commands, a_commandNames);
271   }
272
273   a_commands[command.getId()] = command;
274   a_commandNames[command.getName()] = getCommand(command.getId());
275 }
276
277
278 // public
279
280 //------------------------------------------------------------------------------
281 //------------------------------------------------------ Dictionary::getFormat()
282 //------------------------------------------------------------------------------
283 const Format * Dictionary::getFormat(const std::string & formatName) const throw() {
284   const_format_iterator it = a_formats.find(formatName);
285
286   if(it != format_end()) return ((const Format *) & ((*it).second));
287
288   return (NULL);
289 }
290
291
292 //------------------------------------------------------------------------------
293 //------------------------------------------------------ Dictionary::getVendor()
294 //------------------------------------------------------------------------------
295 const Vendor * Dictionary::getVendor(S32 vendorId) const throw() {
296   const_vendor_iterator it = a_vendors.find(vendorId);
297
298   if(it != vendor_end()) return ((const Vendor *) & ((*it).second));
299
300   return (NULL);
301 }
302
303
304 //------------------------------------------------------------------------------
305 //------------------------------------------------------ Dictionary::getVendor()
306 //------------------------------------------------------------------------------
307 const Vendor * Dictionary::getVendor(const std::string & vendorName) const throw() {
308   const_vendorNames_iterator v_it = a_vendorNames.find(vendorName);
309
310   if(v_it != a_vendorNames.end()) return ((*v_it).second);
311
312   return (NULL);
313 }
314
315
316 //------------------------------------------------------------------------------
317 //--------------------------------------------------------- Dictionary::getAvp()
318 //------------------------------------------------------------------------------
319 const Avp * Dictionary::getAvp(const AvpId & avpId) const throw() {
320   const_avp_iterator it = a_avps.find(avpId);
321
322   if(it != avp_end()) return ((const Avp *) & ((*it).second));
323
324   return (NULL);
325 }
326
327
328 //------------------------------------------------------------------------------
329 //--------------------------------------------------------- Dictionary::getAvp()
330 //------------------------------------------------------------------------------
331 const Avp * Dictionary::getAvp(const std::string & avpName) const throw() {
332   const_avpNames_iterator a_it = a_avpNames.find(avpName);
333
334   if(a_it != a_avpNames.end()) return ((*a_it).second);
335
336   return (NULL);
337 }
338
339
340 //------------------------------------------------------------------------------
341 //----------------------------------------------------- Dictionary::getCommand()
342 //------------------------------------------------------------------------------
343 const Command * Dictionary::getCommand(const CommandId & commandId) const throw() {
344   const_command_iterator it = a_commands.find(commandId);
345
346   if(it != command_end()) return ((const Command *) & ((*it).second));
347
348   return (NULL);
349 }
350
351
352 //------------------------------------------------------------------------------
353 //----------------------------------------------------- Dictionary::getCommand()
354 //------------------------------------------------------------------------------
355 const Command * Dictionary::getCommand(const std::string & commandName) const throw() {
356   const_commandNames_iterator c_it = a_commandNames.find(commandName);
357
358   if(c_it != a_commandNames.end()) return ((*c_it).second);
359
360   return (NULL);
361 }
362
363
364 //------------------------------------------------------------------------------
365 //------------------------------------------------------- Dictionary::asString()
366 //------------------------------------------------------------------------------
367 std::string Dictionary::asString(void) const throw() {
368   std::string trace, title;
369   trace += "\n";
370   anna::functions::TextHighlightMode::_v thm = anna::functions::TextHighlightMode::LeftAndRightline;
371   trace += anna::functions::highlight("Name", thm);
372   trace += a_name; trace += "\n";
373   const_format_iterator f_it;
374   const_vendor_iterator v_it;
375   const_avp_iterator a_it;
376   const_command_iterator c_it;
377   trace += "\n";
378   // Formats
379   title = "Formats";
380   //title = "Formats (";
381   //title += anna::functions::entriesAsString(format_size());
382   //title += ")";
383   trace += anna::functions::highlight(title, thm);
384
385   for(f_it = format_begin(); f_it != format_end(); f_it++) {
386     if((*f_it).second.isReserved()) continue;
387
388     if((*f_it).second.isRFC3588())
389       trace += "(Diameter RFC-3588 type)    ";
390     else
391       trace += "(Application-specific type) ";
392
393     trace += (*f_it).second.asString();
394     trace += "\n";
395   }
396
397   trace += "\n";
398   // Vendors
399   title = "Vendors (";
400   title += anna::functions::entriesAsString(vendor_size());
401   title += ")";
402   trace += anna::functions::highlight(title, thm);
403
404   for(v_it = vendor_begin(); v_it != vendor_end(); v_it++) {
405     trace += (*v_it).second.asString();
406     trace += "\n";
407   }
408
409   trace += "\n";
410   // Avps
411   title = "Avps (";
412   title += anna::functions::entriesAsString(avp_size());
413   title += ")";
414   trace += anna::functions::highlight(title, thm);
415
416   for(a_it = avp_begin(); a_it != avp_end(); a_it++) {
417     trace += (*a_it).second.asString();
418     trace += "\n";
419   }
420
421   trace += "\n";
422   // Commands
423   title = "Commands (";
424   title += anna::functions::entriesAsString(command_size());
425   title += ")";
426   trace += anna::functions::highlight(title, thm);
427
428   for(c_it = command_begin(); c_it != command_end(); c_it++) {
429     trace += (*c_it).second.asString();
430     trace += "\n";
431   }
432
433   trace += "\n";
434   return (trace);
435 }
436
437
438 //------------------------------------------------------------------------------
439 //---------------------------------------------------------- Dictionary::asXML()
440 //------------------------------------------------------------------------------
441 anna::xml::Node* Dictionary::asXML(anna::xml::Node* parent) const throw() {
442 //   <!ELEMENT dictionary (format*, vendor*, avp*, command*)>
443 //   <!ATTLIST dictionary name CDATA #REQUIRED>
444   anna::xml::Node* result = parent->createChild("dictionary");
445   result->createAttribute("name", a_name);
446
447   // Formats
448   for(const_format_iterator it = format_begin(); it != format_end(); it++) {
449     if((*it).second.isReserved()) continue;
450
451     if((*it).second.isRFC3588()) continue;
452
453     (*it).second.asXML(result);
454   }
455
456   // Vendors
457   for(const_vendor_iterator it = vendor_begin(); it != vendor_end(); it++)
458     (*it).second.asXML(result);
459
460   // Avps
461   for(const_avp_iterator it = avp_begin(); it != avp_end(); it++) {
462     if((*it).second.getFormat()->isAny()) continue;  // Generic AVP not shown
463
464     (*it).second.asXML(result);
465   }
466
467   // Commands
468   for(const_command_iterator it = command_begin(); it != command_end(); it++)
469     (*it).second.asXML(result);
470
471   return result;
472 }
473
474
475
476 //------------------------------------------------------------------------------
477 //---------------------------------------------------- Dictionary::asXMLString()
478 //------------------------------------------------------------------------------
479 std::string Dictionary::asXMLString() const throw() {
480   anna::xml::Node root("root");
481   return anna::xml::Compiler().apply(asXML(&root));
482 }
483
484
485
486 ////------------------------------------------------------------------------------
487 ////----------------------------------------- Dictionary::checkUniqueIdentifiers()
488 ////------------------------------------------------------------------------------
489 //void Dictionary::checkUniqueIdentifiers(const anna::xml::Node *rootNode) const throw(anna::RuntimeException) {
490 //   std::map < std::string/*xml ref*/, int/*dummy*/ > formats;
491 //   std::map < std::string/*xml ref*/, int/*dummy*/ > vendors;
492 //   std::map < std::string/*xml ref*/, int/*dummy*/ > avps;
493 //   std::map < std::string/*xml ref*/, int/*dummy*/ > commands;
494 //   std::string nodeName, ref;
495 //
496 //   for (anna::xml::Node::const_child_iterator it = rootNode->child_begin(); it != rootNode->child_end(); it++) {
497 //      nodeName = (*it)->getName();
498 //
499 //      if (nodeName == "format") {
500 //         ref = (*it)->getAttribute("name")->getValue();
501 //         int refs_i = formats.size(); formats[ref] = 0;
502 //
503 //         if (formats.size() == refs_i) {
504 //            std::string s_ex = anna::functions::asString("Repeated format name reference '%s' not allowed within the same xml dictionary", ref.c_str());
505 //            throw anna::RuntimeException(s_ex, ANNA_FILE_LOCATION);
506 //         }
507 //      } else if (nodeName == "vendor") {
508 //         ref = (*it)->getAttribute("name")->getValue();
509 //         int refs_i = vendors.size(); vendors[ref] = 0;
510 //
511 //         if (vendors.size() == refs_i) {
512 //            std::string s_ex = anna::functions::asString("Repeated vendor name reference '%s' not allowed within the same xml dictionary", ref.c_str());
513 //            throw anna::RuntimeException(s_ex, ANNA_FILE_LOCATION);
514 //         }
515 //      } else if (nodeName == "avp") {
516 //         ref = (*it)->getAttribute("name")->getValue();
517 //         int refs_i = avps.size(); avps[ref] = 0;
518 //
519 //         if (avps.size() == refs_i) {
520 //            std::string s_ex = anna::functions::asString("Repeated avp name reference '%s' not allowed within the same xml dictionary", ref.c_str());
521 //            throw anna::RuntimeException(s_ex, ANNA_FILE_LOCATION);
522 //         }
523 //      } else if (nodeName == "command") {
524 //         ref = (*it)->getAttribute("name")->getValue();
525 //         int refs_i = commands.size(); commands[ref] = 0;
526 //
527 //         if (commands.size() == refs_i) {
528 //            std::string s_ex = anna::functions::asString("Repeated command name reference '%s' not allowed within the same xml dictionary", ref.c_str());
529 //            throw anna::RuntimeException(s_ex, ANNA_FILE_LOCATION);
530 //         }
531 //      }
532 //   }
533 //}
534
535
536 //------------------------------------------------------------------------------
537 //------------------------------------------------- Dictionary::extractFormats()
538 //------------------------------------------------------------------------------
539 void Dictionary::extractFormats(const anna::xml::Node *rootNode) throw(anna::RuntimeException) {
540   Format aux;
541
542   for(anna::xml::Node::const_child_iterator it = rootNode->child_begin(); it != rootNode->child_end(); it++) {
543     std::string nodeName = (*it)->getName();
544
545     if(nodeName == "format") {
546       // Reset:
547       aux.initialize(this);
548       // Attributes:
549       const char * type = (*it)->getAttribute("name")->getCStringValue(); // mandatory
550       const char * parentType = (*it)->getAttribute("parent-type")->getCStringValue(); // mandatory
551       // Assignments:
552       aux.setName(type);
553       aux.setParentName(parentType /* never empty, basics registered at initialization */);
554       // New entry:
555       addFormat(aux);
556     }
557   }
558 }
559
560
561 //------------------------------------------------------------------------------
562 //------------------------------------------------- Dictionary::extractVendors()
563 //------------------------------------------------------------------------------
564 void Dictionary::extractVendors(const anna::xml::Node *rootNode) throw(anna::RuntimeException) {
565   Vendor aux;
566
567   for(anna::xml::Node::const_child_iterator it = rootNode->child_begin(); it != rootNode->child_end(); it++) {
568     std::string nodeName = (*it)->getName();
569
570     if(nodeName == "vendor") {
571       // Attributes:
572       const char * name = (*it)->getAttribute("name")->getCStringValue(); // mandatory
573       S32 code = (*it)->getAttribute("code")->getIntegerValue(); // mandatory
574       // Assignments:
575       aux.setId(code);
576       aux.setName(name);
577       // New entry:
578       addVendor(aux);
579     }
580   }
581 }
582
583
584 //------------------------------------------------------------------------------
585 //---------------------------------------------------- Dictionary::extractAvps()
586 //------------------------------------------------------------------------------
587 void Dictionary::extractAvps(const anna::xml::Node *rootNode) throw(anna::RuntimeException) {
588   Avp auxAvp;
589   const anna::xml::Node *singleNode, *groupedNode;
590
591   for(anna::xml::Node::const_child_iterator it = rootNode->child_begin(); it != rootNode->child_end(); it++) {
592     std::string nodeName = (*it)->getName();
593
594     if(nodeName == "avp") {
595       // Reset:
596       auxAvp.initialize(this);
597       // Attributes:
598       const char * name = (*it)->getAttribute("name")->getCStringValue(); // mandatory
599       S32 code = (*it)->getAttribute("code")->getIntegerValue(); // mandatory
600       const anna::xml::Attribute *vendor_name = (*it)->getAttribute("vendor-name", anna::Exception::Mode::Ignore); // implied
601       S32 vendorCode = 0; /* IETF by default */
602
603       if(vendor_name) {
604         const char * c_name = vendor_name->getCStringValue();
605         auxAvp.setVendorName(c_name);
606         const_vendorNames_iterator v_it = a_vendorNames.find(c_name);
607
608         if(v_it == a_vendorNames.end()) {
609           std::string s_ex = anna::functions::asString("Vendor '%s', referenced at '%s' avp definition, not found at xml", c_name, name);
610           throw anna::RuntimeException(s_ex, ANNA_FILE_LOCATION);
611         }
612
613         vendorCode = ((*v_it).second)->getId();
614       }
615
616       // Assignments:
617       auxAvp.setCode(code);
618       auxAvp.setVendorId(vendorCode);
619       auxAvp.setName(name);
620       // libxml2 doesn't support default attribute value retrieving. All default values will be replaced by #IMPLIED sintax:
621       const anna::xml::Attribute *v_bit, *m_bit, *p_bit, *may_encript;
622       v_bit = (*it)->getAttribute("v-bit", anna::Exception::Mode::Ignore);
623       m_bit = (*it)->getAttribute("m-bit", anna::Exception::Mode::Ignore);
624       p_bit = (*it)->getAttribute("p-bit", anna::Exception::Mode::Ignore);
625       may_encript = (*it)->getAttribute("may-encrypt", anna::Exception::Mode::Ignore);
626       auxAvp.setVbit(v_bit ? (Avp::FlagRule::asEnum(v_bit->getCStringValue())) : (Avp::FlagRule::mustnot));
627       auxAvp.setMbit(m_bit ? (Avp::FlagRule::asEnum(m_bit->getCStringValue())) : (Avp::FlagRule::may));
628       auxAvp.setPbit(p_bit ? (Avp::FlagRule::asEnum(p_bit->getCStringValue())) : (Avp::FlagRule::may));
629       auxAvp.setMayEncrypt(may_encript ? ((may_encript->getValue()) == "yes") : false);
630
631       // Check vendor specific bit:
632       if(vendorCode && (auxAvp.getVbit() == Avp::FlagRule::mustnot)) {
633         std::string s_ex = anna::functions::asString("Flag rules for vendor specific bit (mustnot) at '%s' avp definicion, are incompatible with non-zeroed vendor id %s", name, getVendor(vendorCode)->asString().c_str());
634         throw anna::RuntimeException(s_ex, ANNA_FILE_LOCATION);
635       }
636
637       if(!vendorCode && (auxAvp.getVbit() == Avp::FlagRule::must)) {
638         std::string s_ex = anna::functions::asString("Flag rules for vendor specific bit (must) at '%s' avp definicion, are incompatible with zeroed vendor id %s", name, getVendor(vendorCode)->asString().c_str());
639         throw anna::RuntimeException(s_ex, ANNA_FILE_LOCATION);
640       }
641
642       // Single or grouped:
643       singleNode = (*it)->find("single", anna::Exception::Mode::Ignore);
644       groupedNode = (*it)->find("grouped", anna::Exception::Mode::Ignore);
645
646       if(singleNode) {
647         // Attributes:
648         const char * formatName = singleNode->getAttribute("format-name")->getCStringValue(); // mandatory
649         const anna::xml::Attribute *_enum = singleNode->getAttribute("enum", anna::Exception::Mode::Ignore); // implied
650         // Assignments:
651         const Format *format = getFormat(formatName);
652
653         if(!format) {
654           std::string s_ex = anna::functions::asString("Format '%s', referenced at '%s' avp definition, not found at dictionary (neither xml nor RFC 3588 diameter format types)", formatName, name);
655           throw anna::RuntimeException(s_ex, ANNA_FILE_LOCATION);
656         }
657
658         auxAvp.setFormatName(formatName);
659
660         if(_enum) {
661           const char *enums = _enum->getCStringValue();
662
663           if(!format->isEnumerated()) {
664             std::string s_ex = anna::functions::asString("Enumerated literal '%s' is not allowed for '%s' avp format", enums, formatName);
665             throw anna::RuntimeException(s_ex, ANNA_FILE_LOCATION);
666           }
667
668           auxAvp.setEnums(enums);
669         }
670
671         for(anna::xml::Node::const_child_iterator l_it = singleNode->child_begin(); l_it != singleNode->child_end(); l_it++) {
672           // Attributes:
673           std::string data = (*l_it)->getAttribute("data")->getValue(); // mandatory
674           std::string alias = (*l_it)->getAttribute("alias")->getValue(); // mandatory
675           // Assignment:
676           auxAvp.addLabel(data, alias);
677         }
678       } else { // grouped
679         // Assignments:
680         auxAvp.setFormatName(codec::Format::asText(codec::Format::Grouped));
681         // Wait for avprule insertion, because we need complete avp reference pool (*)
682       }
683
684       // New entry:
685       addAvp(auxAvp);
686     } // end if
687   } // end for
688
689   // (*) Avp rules adding:
690   for(anna::xml::Node::const_child_iterator it = rootNode->child_begin(); it != rootNode->child_end(); it++) {
691     std::string nodeName = (*it)->getName();
692
693     if(nodeName == "avp") {
694       // Attributes:
695       const char * gid = (*it)->getAttribute("name")->getCStringValue(); // mandatory
696       // Avp:
697       const_avpNames_iterator a_it = a_avpNames.find(gid);
698       Avp * gavp = (Avp *)((*a_it).second);
699
700       if(!gavp) continue;  // it could be mising (a redefinition could have removed it)
701
702       const Format *format = gavp->getFormat();
703
704       // Avprule updating:
705       if(format->isGrouped()) {
706         AvpRule auxAvpRule(this);
707         groupedNode = (*it)->find("grouped");
708
709         for(anna::xml::Node::const_child_iterator r_it = groupedNode->child_begin(); r_it != groupedNode->child_end(); r_it++) {
710           // Attributes:
711           std::string id = (*r_it)->getAttribute("id")->getValue(); // mandatory
712           std::string type = (*r_it)->getAttribute("type")->getValue(); // mandatory
713           const anna::xml::Attribute *qual = (*r_it)->getAttribute("qual", anna::Exception::Mode::Ignore); // implied
714           // Assignment:
715           const Avp * avp = getAvp(id);
716
717           if(avp == NULL) {
718             std::string s_ex = anna::functions::asString("Avp '%s', referenced at avp rule definition within grouped avp '%s', not found at xml", id.c_str(), gid);
719             throw anna::RuntimeException(s_ex, ANNA_FILE_LOCATION);
720           }
721
722           auxAvpRule.setAvpName(id);
723           auxAvpRule.setPresence(AvpRule::Presence::asEnum(type));
724           auxAvpRule.setQual(qual ? (qual->getValue()) : "");
725           gavp->addAvpRule(auxAvpRule);
726         }
727       } // grouped
728     } // end if
729   } // end for
730
731   // Check avp loops between grouped avps:
732
733   // In order to avoid loops, we could force to define grouped avps which children
734   //  had been previously defined at xml file. In this way, is imposible to get a loop:
735   //   C = ...
736   //   D = ...
737   //   A = grouped of B,C,D -> error, B unknown
738   //   B = grouped of A,F -> with former definition, would become a loop
739   //
740   // But this supposes a restriction at xml configuration (specific order).
741   // The other way is an internal check: a grouped AVP won't have descendants within
742   //  its ascendants. Then we will check all grouped avps in this way:
743   //
744   // 1. Searching for another grouped avps which are parents for this avp.
745   // 2. If these are children (even this avp(*)) at avp definition, then a loop is detected.
746   //
747   // Example 1: (1) Analyzing 'A', found parent 'B' / (2) 'B' is already children of 'A'
748   //       A -> B
749   //            C
750   //            D
751   //       ...
752   //       B -> A -> loop !!
753   //            F
754   //
755   // (*) Example 2: (1) Analyzing 'A', found parent 'A' / (2) 'A' is already children of 'A'
756   //       A -> B
757   //            C
758   //            D
759   //            A -> loop !!
760   //
761   for(const_avp_iterator it = avp_begin(); it != avp_end(); it++) {
762     const Avp & avp = (*it).second;
763
764     if(!((avp.getFormat())->isGrouped())) continue;
765
766     for(const_avp_iterator it_p = avp_begin(); it_p != avp_end(); it_p++) {
767       const Avp & avp_p = (*it_p).second;
768
769       if(!((avp_p.getFormat())->isGrouped())) continue;
770
771       if(avp_p.isChild(avp.getId())) {
772         if(avp.isChild(avp_p.getId())) {
773           std::string s_ex;
774
775           if(it != it_p)
776             s_ex = anna::functions::asString("Loop detected between grouped avps '%s' and '%s'", avp.getName().c_str(), avp_p.getName().c_str());
777           else
778             s_ex = anna::functions::asString("Loop within grouped avp '%s': cannot contain itself !!", avp.getName().c_str());
779
780           throw anna::RuntimeException(s_ex, ANNA_FILE_LOCATION);
781         } // parent is children of (ref): loop !
782       } // parent found
783     } // search parents
784   } // search grouped avps (ref)
785 }
786
787
788 //------------------------------------------------------------------------------
789 //------------------------------------------------ Dictionary::extractCommands()
790 //------------------------------------------------------------------------------
791 void Dictionary::extractCommands(const anna::xml::Node *rootNode) throw(anna::RuntimeException) {
792   Command auxCommand;
793
794   // (*) Avp rules adding:
795   for(anna::xml::Node::const_child_iterator it = rootNode->child_begin(); it != rootNode->child_end(); it++) {
796     std::string nodeName = (*it)->getName();
797
798     if(nodeName == "command") {
799       // Reset:
800       auxCommand.initialize(this);
801       // Attributes:
802       const char * name = (*it)->getAttribute("name")->getCStringValue(); // mandatory
803       U24 code = (*it)->getAttribute("code")->getIntegerValue(); // mandatory
804       std::string type = (*it)->getAttribute("type")->getValue(); // mandatory
805       // Assignment:
806       auxCommand.setCode(code);
807       auxCommand.setName(name);
808       auxCommand.setRequest(type == "Request");
809       AvpRule auxAvpRule(this);
810
811       for(anna::xml::Node::const_child_iterator r_it = (*it)->child_begin(); r_it != (*it)->child_end(); r_it++) {
812         // Attributes:
813         std::string id = (*r_it)->getAttribute("id")->getValue(); // mandatory
814         std::string type = (*r_it)->getAttribute("type")->getValue(); // mandatory
815         const anna::xml::Attribute *qual = (*r_it)->getAttribute("qual", anna::Exception::Mode::Ignore); // implied
816         // Assignment:
817         const Avp * avp = getAvp(id);
818
819         if(avp == NULL) {
820           std::string s_ex = anna::functions::asString("Avp '%s', referenced at avp rule definition within command '%s', not found at xml", id.c_str(), name);
821           throw anna::RuntimeException(s_ex, ANNA_FILE_LOCATION);
822         }
823
824         auxAvpRule.setAvpName(id);
825         auxAvpRule.setPresence(AvpRule::Presence::asEnum(type));
826         auxAvpRule.setQual(qual ? (qual->getValue()) : "");
827         auxCommand.addAvpRule(auxAvpRule);
828       }
829
830       // New entry:
831       addCommand(auxCommand);
832     } // end if
833   } // end for
834 }
835
836
837 //------------------------------------------------------------------------------
838 //----------------------------------------------------------- Dictionary::load()
839 //------------------------------------------------------------------------------
840 void Dictionary::load(const std::string & xmlPathFile) throw(anna::RuntimeException) {
841   if(xmlPathFile == "")
842     throw anna::RuntimeException("Empty xml path file provided", ANNA_FILE_LOCATION);
843
844   LOGDEBUG(
845     std::string trace = "Loading diameter dictionary from '";
846     trace += xmlPathFile;
847     trace += "' ...";
848     anna::Logger::debug(trace, ANNA_FILE_LOCATION);
849   );
850
851   try {
852     anna::xml::DocumentFile xmlDocument; // has private copy constructor defined but not implemented to avoid inhenrit/copy (is very heavy)
853     const anna::xml::Node *rootNode;
854     xmlDocument.initialize(xmlPathFile.c_str()); // fail here is i/o error
855     rootNode = xmlDocument.parse(*a_dtd); // Parsing: fail here if xml violates dtd
856     a_name = rootNode->getAttribute("name")->getValue();
857     //checkUniqueIdentifiers(rootNode);   // Check unique id within xml, for vendor, avp and command nodes:
858     extractFormats(rootNode);           // Add formats:
859     extractVendors(rootNode);           // Add vendors:
860     extractAvps(rootNode);              // Add avps:
861     extractCommands(rootNode);          // Add commands:
862   } catch(anna::RuntimeException& ex) {
863     ////ex.trace();
864     throw;
865   }
866
867   // Trace:
868   LOGDEBUG(anna::Logger::debug(asString(), ANNA_FILE_LOCATION));
869 }
870