3a583ce8603f5f904c2b5389db44327ab6027858
[anna.git] / include / anna / diameter / codec / Message.hpp
1 // ANNA - Anna is Not Nothingness Anymore                                                         //
2 //                                                                                                //
3 // (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo                         //
4 //                                                                                                //
5 // See project site at http://redmine.teslayout.com/projects/anna-suite                           //
6 // See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE //
7
8
9 #ifndef anna_diameter_codec_Message_hpp
10 #define anna_diameter_codec_Message_hpp
11
12
13 // Local
14 #include <anna/config/defines.hpp>
15 #include <anna/diameter/defines.hpp>
16 #include <anna/diameter/codec/Avp.hpp>
17 #include <anna/diameter/helpers/base/defines.hpp>
18
19 #include <anna/core/DataBlock.hpp>
20 #include <anna/core/RuntimeException.hpp>
21
22 // STL
23 #include <string>
24
25 //------------------------------------------------------------------------------
26 //---------------------------------------------------------------------- #define
27 //------------------------------------------------------------------------------
28
29 namespace anna {
30 class Node;
31 }
32
33 namespace anna {
34
35 namespace diameter {
36
37 namespace stack {
38 class Dictionary;
39 class Format;
40 class Command;
41 }
42
43 namespace codec {
44
45 class Avp;
46 class Engine;
47
48 /**
49 * Diameter message generic container
50 * <pre>
51 *    RFC 3588                Diameter Based Protocol           September 2003
52 *    3.  Diameter Header
53 *
54 *       A summary of the Diameter header format is shown below.  The fields
55 *       are transmitted in network byte order.
56 *
57 *        0                   1                   2                   3
58 *        0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
59 *       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
60 *       |    Version    |                 Message Length                |
61 *       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
62 *       | command flags |                  Command-Code                 |
63 *       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
64 *       |                         Application-ID                        |
65 *       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
66 *       |                      Hop-by-Hop Identifier                    |
67 *       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
68 *       |                      End-to-End Identifier                    |
69 *       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
70 *       |  AVPs ...
71 *       +-+-+-+-+-+-+-+-+-+-+-+-+-
72 * </pre>
73 */
74 class Message {
75
76   U8 a_version;
77   CommandId a_id;          // code and request indicator
78   U8 a_flags;
79   U32 a_applicationId;
80   U32 a_hopByHop;
81   U32 a_endToEnd;
82   avp_container a_avps; // childrens
83   find_container a_finds; // fast access for message first-level avps
84
85   // auxiliary
86   int a_insertionPositionForChilds; // used with childrens
87   anna::DataBlock a_forCode;
88
89   const Avp* _getAvp(const char *name, int ocurrence, anna::Exception::Mode::_v emode) const throw(anna::RuntimeException);
90
91   // --- Developer notes ---
92   // 'AVP Length' does not include posible data padding. Thanks to this, 'Data Length'
93   // is the difference between 'AVP Length' and sum of code, length, flags and
94   // optionally the vendor-ID (all of them are 32-bit boundary), that is to say:
95   // 8 or 12 (vendor-specific avps).
96   //
97   // Grouped avps 'AVP Length' includes own headers plus the total length of all
98   //  underlying AVPs, including their headers and padding, then 'AVP Length' is
99   //  always multiple of 4 (library will check this), and smae for 'Data Length'
100   //  which is an 'whole avp Length with padding' itself.
101
102
103   // Children helpers
104
105   // Own
106   avp_iterator avp_begin() throw() { return a_avps.begin(); }
107   avp_iterator avp_end() throw() { return a_avps.end(); }
108   const_avp_iterator avp_begin() const throw() { return a_avps.begin(); }
109   const_avp_iterator avp_end() const throw() { return a_avps.end(); }
110
111   /**
112   * Gets avp total message length.
113   */
114   U24 getLength() const throw();
115
116
117   // Internal
118   bool flagsOK(int &rc) const throw(); // flags coherence regarding dictionary. Only must be called when Message is identified at the dictionary.
119   int addChild(Avp *avp) throw() { return Avp::addChild(a_avps, a_insertionPositionForChilds, avp); }
120   const anna::diameter::stack::Command *getStackCommand(CommandId id) const throw(anna::RuntimeException);
121
122   void setFailedAvp(const parent_t &parent, AvpId wrong, const char *wrongName = NULL) throw(anna::RuntimeException);
123   // During message decoding and validation, the first wrong avp is stored and all the tracking is managed to find out its
124   //  nested path for the case of grouped avps with wrong avps inside. Remember the RFC 6733, section 7.5:
125   //
126   //           In the case where the offending AVP is embedded within a Grouped AVP,
127   //           the Failed-AVP MAY contain the grouped AVP, which in turn contains
128   //           the single offending AVP.  The same method MAY be employed if the
129   //           grouped AVP itself is embedded in yet another grouped AVP and so on.
130   //           In this case, the Failed-AVP MAY contain the grouped AVP hierarchy up
131   //           to the single offending AVP.  This enables the recipient to detect
132   //           the location of the offending AVP when embedded in a group.
133   //
134   // The first wrong avp found will set the final result code, as the RFC recommends:
135   //
136   //           The value of the Result-Code AVP will provide information on the reason
137   //           for the Failed-AVP AVP.  A Diameter answer message SHOULD contain an
138   //           instance of the Failed-AVP AVP that corresponds to the error
139   //           indicated by the Result-Code AVP.  For practical purposes, this
140   //           Failed-AVP would typically refer to the first AVP processing error
141   //           that a Diameter node encounters.
142   //
143   // The message keeps the list (reverse order) of avps hierarchy (in case of grouping) for the final Failed-AVP construction,
144   // which is done at the end of decoding or validation, and only the first wrong avp is stored with its corresponding path.
145
146
147 protected:
148
149   /** Codec Engine */
150   mutable Engine *a_engine;
151
152   /** Codec Engine getter: avoids have to create base engine when using its child */
153   virtual Engine * getEngine() const throw(anna::RuntimeException);
154
155   /**
156   * Initializes Message class information.
157   * Any reimplementation must first invoke base class method.
158   */
159   virtual void initialize() throw();
160
161
162 public:
163
164   /**
165   * Default constructor
166   * @param engine Codec engine used
167   */
168   Message(Engine *engine = NULL);
169
170   /**
171   * Identified constructor
172   * @param id Command identifier as pair (code,request-indicator).
173   * @param engine Codec engine used
174   */
175   Message(CommandId id, Engine *engine = NULL);
176
177
178   /**
179   * Sets the codec engine.
180   *
181   * Once assigned (here or at constructor), this method SHALL NOT be used anymore.
182   * Also, the associated dictionary SHOULD NOT BE CHANGED through the engine,
183   * unless you know what are you doing.
184   * Setting a new different engine with different stack, even same engine where the
185   * stack has been dynamically changed, could cause a bad behaviour depending on the
186   * changes: in general, if the dictionary grows, nothing bad will happen, but if
187   * you remove or modified some elements which were processed with a certain format,
188   * will be interpreted as 'unknown' with the new dictionary, and then some problems
189   * may occur. If you add elements (vendors, avps, messages) is not a problem.
190   *
191   * IMPORTANT NOTES:
192   * 1) if you want to reuse the message, as a recommendation, you should #clear the
193   * message. In that way, next operation will adjust automatically the needed engine.
194   * 2) if you have dedicated message objects for each interface (application id), then
195   * you could set the corresponding engine on constructor (or setEngine), and forget
196   * about #clear. The needed cleanup will be done automatically from decoding and xml
197   * loading procedures, and initialized engine will be kept along message operations.
198   */
199   void setEngine(Engine *engine) throw();
200
201
202   // Length references
203   static const int HeaderLength;
204
205
206   //    Command Flags
207   //    +-+-+-+-+-+-+-+-+
208   //    |R P E T r r r r|
209   //    +-+-+-+-+-+-+-+-+
210   //
211   //      R(equest)
212   //      P(roxiable)
213   //      E(rror)
214   //      T(Potentially re-transmitted message)
215   //      r(eserved)  - these flag bits are reserved for future use, and
216   //                    MUST be set to zero, and ignored by the receiver.
217   static const U8 RBitMask;
218   static const U8 PBitMask;
219   static const U8 EBitMask;
220   static const U8 TBitMask;
221
222
223   /**
224   * Destructor
225   */
226   virtual ~Message();
227
228   // Virtual destructors are useful when you can delete an instance of a derived class through a pointer to base class:
229   // This destructor is not virtual, then a pointer to base class (even pointing to a children one) will invoke this destructor, not the derived one.
230   // My current solution: virtualizing method 'clear'
231   //
232   // Recommendation:
233   // To sum up, always make base classes' destructors virtual when they're meant to be manipulated polymorphically.
234
235   // setters
236
237   /**
238      Sets the command identifier and clear the former content.
239
240      @param id Command identifier as pair (code,request-indicator).
241   */
242   void setId(CommandId id) throw(anna::RuntimeException);
243
244   /**
245      Same as #setId but providing dictionary logical name for Avp searched
246   */
247   void setId(const char *name) throw(anna::RuntimeException);
248
249   /**
250      Sets the command version. By default, messages initializes with value 1.
251
252      @param version Version provided
253   */
254   void setVersion(U8 version) throw() { a_version = version; }
255
256   /**
257      Sets/unsets P bit activation.
258      Application should not have to use this because dictionary information is used in order to configure flags when Message identifier is stored.
259
260      @param activate Activates/deactivates the bit. True by default.
261   */
262   void setProxiableBit(bool activate = true) throw() { if(activate) a_flags |= PBitMask; else a_flags &= (~PBitMask); }
263
264   /**
265      Sets/unsets E bit activation.
266      Application should not have to use this because dictionary information is used in order to configure flags when Message identifier is stored.
267      This flag MUST NOT be set in request messages (in this case, it will be ignored).
268
269      @param activate Activates/deactivates the bit. True by default.
270   */
271   void setErrorBit(bool activate = true) throw() { if(isRequest()) return; if(activate) a_flags |= EBitMask; else a_flags &= (~EBitMask); }
272
273   /**
274      Sets/unsets T bit activation.
275      Application should not have to use this because dictionary information is used in order to configure flags when Message identifier is stored.
276      This flag MUST NOT be set in answer messages (in this case, it will be ignored).
277
278      @param activate Activates/deactivates the bit. True by default.
279   */
280   void setPotentiallyReTransmittedMessageBit(bool activate = true) throw() { if(isAnswer()) return; if(activate) a_flags |= TBitMask; else a_flags &= (~TBitMask); }
281
282   /**
283      Sets the message application id.
284
285      The codec engine could be configured to force a stack selection based in this field value: see #selectStackWithApplicationId.
286      In multistack applications (in case of being monothread), you only have to take care about how to apply this method: the thing
287      is that you must not interleave message builds which belongs to different stacks. For example, you could think about setting the
288      message header for message A using stack A. Then, start to add the message header fields for a second message B using another stack B.
289      Following you would add the message A avps, but then, the stack is not going to be automatically changed (this is only done through this
290      method). The result could be unexpected when adding/encoding messages with a dictionary which does not correspond.
291
292      @warning do not interleave build/encode operations between different messages which uses different stacks over the same codec engine.
293      It seems common sense, but it is not bad to advice about this.
294
295      @param aid Application-id.
296   */
297   void setApplicationId(U32 aid) throw(anna::RuntimeException);
298
299   /**
300      Sets the message hop-by-hop
301      @param hbh Hop-by-hop identifier.
302   */
303   void setHopByHop(U32 hbh) throw() { a_hopByHop = hbh; }
304
305   /**
306      Sets the message end-to-end
307      @param ete End-to-end identifier.
308   */
309   void setEndToEnd(U32 ete) throw() { a_endToEnd = ete; }
310
311
312   /**
313      Sets header to be an answer regarding provided request message code.
314      Internally, updates command identifier (indeed request flag), promotes version, application identifier, hop-by-hop and end-to-end fields.
315
316      @param request Message to be answered.
317
318      @warning Request provided must be a request, in other case method do nothing.
319   */
320   void setHeaderToAnswer(const Message &request) throw(anna::RuntimeException) {
321     if(!request.getId().second) return;
322
323     a_engine = request.getEngine(); // we know this will be
324
325     setId(CommandId(request.getId().first, !request.getId().second));
326     setVersion(request.getVersion());
327     setApplicationId(request.getApplicationId());
328     setHopByHop(request.getHopByHop()); // The same Hop-by-Hop Identifier in the request is used in the answer (RFC 6733 Section 6.2).
329     setEndToEnd(request.getEndToEnd()); // The same End-to-End Identifier in the request is used in the answer (RFC 6733 Section 6.2).
330     setProxiableBit(request.proxiableBit()); // The 'P' bit is set to the same value as the one in the request (RFC 6733 Section 6.2).
331   }
332
333
334   /**
335     Standard minimum-answer building from requests. Adds Session-Id (mirrored from request if present), Origin-Host and Origin-Realm
336     (which could be configured, extracted from optional Destination AVPs, etc.), and all the Proxy-Info AVPs (added in same order as
337     appear on the request). Of course, answer message header is built from request information through #setHeaderToAnswer. Finally,
338     message is fixed regarding dictionary elements order (#fix).
339
340     Summing up, as RFC 6733 Section 6.2, says:
341
342     <pre>
343
344         6.2.  Diameter Answer Processing
345
346            When a request is locally processed, the following procedures MUST be
347            applied to create the associated answer, in addition to any
348            additional procedures that MAY be discussed in the Diameter
349            application defining the command:
350
351            o  The same Hop-by-Hop Identifier in the request is used in the
352               answer.
353
354            o  The local host's identity is encoded in the Origin-Host AVP.
355
356            o  The Destination-Host and Destination-Realm AVPs MUST NOT be
357               present in the answer message.
358
359            o  The Result-Code AVP is added with its value indicating success or
360               failure.
361
362            o  If the Session-Id is present in the request, it MUST be included
363               in the answer.
364
365            o  Any Proxy-Info AVPs in the request MUST be added to the answer
366               message, in the same order they were present in the request.
367
368            o  The 'P' bit is set to the same value as the one in the request.
369
370            o  The same End-to-End identifier in the request is used in the
371               answer.
372
373            Note that the error messages (see Section 7) are also subjected to
374            the above processing rules.
375
376    Regarding errors, is recommended to use this over automatic answer built at #decode and/or #valid procedures, which would had added
377    Result-Code and/or Failed-AVP AVPs if proceed, but be aware of DIAMETER_COMMAND_UNSUPPORTED Result-Code, because becomes impossible
378    to fix (Session-Id SHOULD appear immediately following the Diameter header, and #fix do this manually even if no information about
379    the command structure is known, but perhaps another fixed AVPs could not comply... use #getResultCode to find out this situation before
380    using #setStandardToAnswer). Anyway, application could add another Failed-AVP content no detected internally, for example:
381    DIAMETER_CONTRADICTING_AVPS or DIAMETER_INVALID_AVP_BIT_COMBO). Also, application could add more Failed-AVP avps with other
382    wrong avps, or accumulate wrong avps inside the one and only Failed-AVP managed by the stack. The standard is open to add multiple
383    avps inside Failed-AVP or multiple Failed-AVP avps with single or multiple avps inside. This depends on application criteria regarding
384    other nodes. However, internally the Anna::diameter stack only provides one Failed-AVP with the first wrong avp found, as RFC 6733 says
385    in section 7.5.
386
387    If application decoding and/or validation operations are ok, user may search for other problems and put the appropiate Result-Code.
388    For example, DIAMETER_TOO_BUSY (3004) depends on congestion issues at business layer and cannot be decided with the only message
389    information automatically (not all the Result-Code values are taken into account, only those which correspond to anomalies managed
390    by anna::diameter::codec). Application Result-Codes could be provided in this prototype, being DIAMETER_SUCCESS the default value if missing.
391
392    </pre>
393    @param request Message to be answered.
394    @param originHost Mandatory Origin-Host diameter identity value provided by application. If answer has already an Origin-Host, this will be ignored.
395    @param originRealm Mandatory Origin-Realm diameter identity value provided by application. If answer has already an Origin-Realm, this will be ignored.
396    @param resultCode Result-Code value assigned by application. If non-success value is already assigned, this will be ignored. DIAMETER_SUCCESS is provided by default.
397
398    @warning Request provided must be a request, in other case method do nothing.
399   */
400   void setStandardToAnswer(const Message &request, const std::string &originHost, const std::string &originRealm, int resultCode = helpers::base::AVPVALUES__Result_Code::DIAMETER_SUCCESS) throw(anna::RuntimeException);
401
402
403   /**
404      Sets a Result-Code AVP over an answer message (for requests, do nothing).
405      If Result-Code AVP doesn't exists, is added and then filled with the value provided.
406      If Result-Code AVP already exists, value detected is replaced if was DIAMETER_SUCCESS (non success codes are unchanged).
407      When provided value corresponds to an protocol error, that is to say within range [3001,3010], message (E)rror bit is
408      automatically activated.
409
410      This method is internally used during #decode and/or #valid procedures in order to build automatic answers, but application
411      could call this for set another Result-Code no detected by these methods within its category or for other one (application
412      layer). These are the Result-Codes implemented (detected) by ANNA::diameter::codec:
413
414      <pre>
415         Protocol Errors:
416
417            DIAMETER_COMMAND_UNSUPPORTED
418            DIAMETER_INVALID_HDR_BITS
419            DIAMETER_INVALID_AVP_BITS
420
421         Permanent Failures:
422
423            DIAMETER_AVP_UNSUPPORTED (F)
424            DIAMETER_INVALID_AVP_VALUE (F)
425            DIAMETER_MISSING_AVP (F)
426            DIAMETER_AVP_NOT_ALLOWED (F)
427            DIAMETER_AVP_OCCURS_TOO_MANY_TIMES (F)
428            DIAMETER_INVALID_BIT_IN_HEADER
429            DIAMETER_INVALID_AVP_LENGTH (F)
430            DIAMETER_INVALID_MESSAGE_LENGTH
431
432            (F) Generates Failed-AVP (also DIAMETER_CONTRADICTING_AVPS and DIAMETER_INVALID_AVP_BIT_COMBO
433                                      values does, but these are not managed by anna::diameter::codec).
434      </pre>
435
436      @param rc Result-Code value. DIAMETER_SUCCESS by default.
437   */
438   void setResultCode(int rc = helpers::base::AVPVALUES__Result_Code::DIAMETER_SUCCESS) throw(anna::RuntimeException);
439
440
441   /**
442      Gets the Result-Code AVP value from an answer message (for requests, returns -1).
443      If missing, -1 value is returned.
444
445      @return Result-Code value for answers, -1 for request and answers without Result-Code AVP inside
446   */
447   int getResultCode() const throw();
448
449
450   /**
451      Adds an avp child providing its identifier and reserve internal memory it.
452
453      @param id Avp identifier as pair (code,vendor-id).
454
455      @return Pointer to the new created avp.
456   */
457   Avp * addAvp(AvpId id) throw(anna::RuntimeException) { return Avp::addAvp(a_avps, a_insertionPositionForChilds, id, getEngine()); }
458
459
460   /**
461      Same as #addAvp but providing dictionary logical name for Avp searched
462   */
463   Avp * addAvp(const char *name) throw(anna::RuntimeException);
464
465
466   /**
467      Adds an avp child providing a persistent pointer (must be maintained by application).
468      It is not allowed to add an avp with no codec engine configured, neither if the engine
469      is not the same.
470
471      @param avp Avp external pointer. If NULL provided, nothing is done and NULL returned.
472      Also NULL returned for bad engine configuration.
473
474      @return Pointer to the added avp (again).
475   */
476   Avp * addAvp(Avp * avp) throw();
477
478
479   /**
480      Removes an Avp within message (first level) and free resources.
481
482      @param id Avp identifier (pair code + vendor-id).
483      @param ocurrence Order of appearance for the searched avp. Zero value means remove all avps with provided identifier at first level (no recursiveness would be allowed in the API in order to avoid unexpected behaviour).
484      Negative values could be used to reverse access positions: i.e. -1 is the last ocurrence, -2 is the second to last (penultimate), etc.
485
486      @return Returns true if something was removed. False in other cases (including i.e. when this message is empty).
487   */
488   bool removeAvp(AvpId id, int ocurrence = 1) throw(anna::RuntimeException) { return Avp::removeAvp(a_avps, (find_container&)a_finds, id, ocurrence, getEngine()); }
489
490
491   /**
492      Same as #removeAvp but providing dictionary logical name for Avp searched
493   */
494   bool removeAvp(const char *name, int ocurrence = 1) throw(anna::RuntimeException);
495
496
497   /**
498   * Clears and initializes Message class information.
499   * Application must clear auxiliary message objects before adding Avps in a new context if the same object is reused.
500   * Application don't need to clear a message object before decode operation (decode invokes #clear before any other task).
501   * Any reimplementation must first invoke base class method.
502   *
503   * @param resetEngine Sets to NULL the codec engine (true, default) or respect its current value (false). If you are going
504   * to reuse the message instance it is better to clear all the information (default) to manage different stacks, because if
505   * you don't initialize the engine to NULL, the second use of the message will keep the same engine deduced from the first
506   * decoding/loading operation, which could be wrong if the second message belongs to a different application identifier.
507   */
508   virtual void clear(bool resetEngine = true) throw(anna::RuntimeException);
509
510   /**
511      Decodes buffer provided over class content. If an error ocurred, decoding will stop launching exception (fatal error) or a warning trace (perhaps the achieved
512      message is valid against all odds then validation will go on). In case that validation is enabled (codec::Engine::ValidationMode) an exception will be launched
513      in a moment which depends on validation depth (codec::Engine::ValidationDepth).
514
515      You could decode multiple times over the same object. A basic cleanup is done respecting the codec engine.
516
517      @param db buffer data block processed. Before decoding, the whole message instance will be cleared (no need to invoke #clear before #decode).
518      @param ptrAnswer Answer set by application (could be empty or not), who is responsible for its memory reservation,
519      and automatically built regarding standard. If message analyzed realizes to be an answer, internal reference becomes
520      NULL because no answer is built for answers. By default, automatic answer is not built.
521   */
522   void decode(const anna::DataBlock &db, Message *ptrAnswer = NULL) throw(anna::RuntimeException);
523
524   /**
525     Fix childrens content regarding dictionary avp positions.
526     Message could remain invalid because of possible fixed/mandatory avps.
527     This is useful to give flexibility to the application during message construction before encoding or representing the data.
528     Is not recommended to fix a recently decoded message because possible validation problems will be hidden.
529   */
530   void fix() throw();
531
532   /**
533      Validates the message regarding dictionary rules like enumerated range, flags coherence, mandatory and fixed types, cardinality qualifiers, etc.
534      @return Boolean indicating validation result
535      @param ptrAnswer Answer set by application (could be empty or not), who is responsible for its memory reservation,
536      and automatically built regarding standard. If message analyzed realizes to be an answer, internal reference becomes
537      NULL because no answer is built for answers. By default, automatic answer is not built.
538   */
539   bool valid(Message *ptrAnswer = NULL) const throw(anna::RuntimeException);
540
541   /**
542      Interpret xml data in order to dump over the class content.
543      You could apply this multiple times over the same object. A basic cleanup is done respecting the codec engine.
544      \param messageNode Message root node obtained from @functions::xmlFileTo
545   */
546   void fromXML(const anna::xml::Node* messageNode) throw(anna::RuntimeException);
547
548   /**
549    * Interpret a xml file in order to create a diameter message
550    * You could apply this multiple times over the same object. A basic cleanup is done respecting the codec engine.
551    *
552    * @see functions::messageXmlDocumentFromXmlFile
553    * @see fromXML
554    *
555    * @param xmlPathFile Complete path file to the xml document which represents the diameter message
556    */
557   void loadXMLFile(const std::string &xmlPathFile) throw(anna::RuntimeException);
558
559   /**
560    * Interpret a xml string in order to create a diameter message
561    * You could apply this multiple times over the same object. A basic cleanup is done respecting the codec engine.
562    *
563    * @see functions::messageXmlDocumentFromXmlString
564    * @see fromXML
565    *
566    * @param xmlString xml representation of the diameter message
567    */
568   void loadXMLString(const std::string &xmlString) throw(anna::RuntimeException);
569
570
571   // getters
572
573   /**
574      Gets Message identifier as pair (code, request indicator).
575   */
576   const CommandId & getId() const throw() { return a_id; }
577
578   /**
579      Gets the command version. By default, messages initializes with value 1.
580
581      @return version Message version
582   */
583   U8 getVersion() const throw() { return a_version; }
584
585   /**
586      Gets Message request indicator.
587   */
588   bool isRequest() const throw() { return a_id.second; }
589
590   /**
591      Gets Message answer indicator.
592   */
593   bool isAnswer() const throw() { return !isRequest(); }
594
595   /**
596      Gets the message application id
597      @return aid Application-id.
598   */
599   const U32 & getApplicationId() const throw() { return a_applicationId; }
600
601   /**
602      Gets the message hop-by-hop
603      @return hbh Hop-by-hop identifier.
604   */
605   const U32 & getHopByHop() const throw() { return a_hopByHop; }
606
607   /**
608      Gets the message end-to-end
609      @return ete End-to-end identifier.
610   */
611   const U32 & getEndToEnd() const throw() { return a_endToEnd; }
612
613   /**
614      Gets stack command (dictionary command reference).
615   */
616   const anna::diameter::stack::Command *getStackCommand() const throw(anna::RuntimeException) { return getStackCommand(a_id); }
617
618   /** Returns R bit activation state */
619   bool requestBit() const throw() { return ((a_flags & RBitMask) != 0x00); }
620
621   /** Returns P bit activation state */
622   bool proxiableBit() const throw() { return ((a_flags & PBitMask) != 0x00); }
623
624   /** Returns E bit activation state */
625   bool errorBit() const throw() { return ((a_flags & EBitMask) != 0x00); }
626
627   /** Returns T bit activation state */
628   bool potentiallyReTransmittedMessageBit() const throw() { return ((a_flags & TBitMask) != 0x00); }
629
630
631   /**
632      Access content for internal Avps. Exception mode allows different combinations like cascade access:
633      <pre>
634
635         try {
636            message->getAvp(anna::diameter::helpers::base::AVP__Multiple_Services_Credit_Control, anna::Exception::Mode::Throw)
637                   ->getAvp(anna::diameter::helpers::base::AVP__Rating_Group, anna::Exception::Mode::Throw);
638         }
639         catch(anna::RuntimeException) {;}
640      </pre>
641
642      Or step access:
643
644      <pre>
645         const Avp *mscc = message->getAvp(anna::diameter::helpers::base::AVP__Multiple_Services_Credit_Control);
646         const Avp *rg;
647         if (mscc) rg = mscc->getAvp(anna::diameter::helpers::base::AVP__Rating_Group);
648      </pre>
649
650      Replacing procedures becomes easy because an Avp can be searched and its pointer reconfigured by mean #setId and data part setters.
651      Deleting procedures must use #removeAvp.
652      Access is internally cached to speed up the search operations. This cache is reset after calling #fix or #removeAvp methods.
653
654      @param id Avp identifier (pair code + vendor-id).
655      @param ocurrence Order of appearance for the searched avp. Zero position is rejected, but negative values could be used to reverse
656      access positions: i.e. -1 is the last ocurrence, -2 is the second to last (penultimate), etc.
657      @param emode Excepcion mode handling: Ignore (no action is taken), Throw (excepcion when missing avp), Trace (trace situation as warning).
658   */
659   const Avp* getAvp(AvpId id, int ocurrence = 1, anna::Exception::Mode::_v emode = anna::Exception::Mode::Throw) const throw(anna::RuntimeException) {
660     return Avp::getAvp(a_avps, (find_container&)a_finds, id, ocurrence, getEngine(), emode);
661   }
662
663   Avp* getAvp(AvpId id, int ocurrence = 1, anna::Exception::Mode::_v emode = anna::Exception::Mode::Throw) throw(anna::RuntimeException) {
664     return const_cast<Avp*>(Avp::getAvp(a_avps, (find_container&)a_finds, id, ocurrence, getEngine(), emode));
665   }
666
667
668   /**
669      Same as #getAvp but providing dictionary logical name for Avp searched
670   */
671   const Avp* getAvp(const char *name, int ocurrence = 1, anna::Exception::Mode::_v emode = anna::Exception::Mode::Throw) const throw(anna::RuntimeException) {
672     return _getAvp(name, ocurrence, emode);
673   }
674
675   Avp* getAvp(const char *name, int ocurrence = 1, anna::Exception::Mode::_v emode = anna::Exception::Mode::Throw) throw(anna::RuntimeException) {
676     return const_cast<Avp*>(_getAvp(name, ocurrence, emode));
677   }
678
679 // Helpers
680
681   /**
682      Counts the number of ocurrences of Avps (first level) with the identifier provided
683
684      @param id Avp identifier (pair code + vendor-id).
685   */
686   int countAvp(AvpId id) const throw() { return Avp::countAvp(a_avps, id); }
687
688   /**
689      Same as #countAvp but providing dictionary logical name for Avp searched
690   */
691   int countAvp(const char *name) const throw(anna::RuntimeException);
692
693   /**
694      Counts the number of children
695
696      @param id Avp identifier (pair code + vendor-id).
697   */
698   int countChilds() const throw() { return Avp::countChilds(a_avps); }
699
700   /**
701      Encodes datablock with the class content. In case that validation is enabled (codec::Engine::ValidationMode) an exception will be launched
702      in a moment which depends on validation depth (codec::Engine::ValidationDepth). If you want to see validation errors but go on with encoding,
703      you should try/catch #valid() procedure out of #code.
704
705      @return DataBlock encoded (internal memory used)
706   */
707   const anna::DataBlock & code() throw(anna::RuntimeException);
708
709   /**
710      Class xml representation
711      \param parent Parent XML node on which hold this instance information.
712      \return XML document with relevant information for this instance.
713   */
714   anna::xml::Node* asXML(anna::xml::Node* parent) const throw();
715
716   /**
717      Class xml string representation
718      @param sortAttributes Optional normalization used to match xml representation with regexps
719
720      \return XML string representation with relevant information for this instance.
721   */
722   std::string asXMLString(bool sortAttributes = false) const throw();
723
724   /**
725      Comparison operator by mean serialization
726
727      @param m1 Instance 1 for Message class
728      @param m2 Instance 2 for Message class
729
730      @return Comparison result
731   */
732   friend bool operator == (const Message & m1, const Message & m2) throw() { return (m1.asXMLString() == m2.asXMLString()); }
733
734   /**
735      Match a regular expression (string pattern) regarding xml string serialization for this message.
736      Using a complex pattern (many avps, grouped ones) it could be necessary to fix the message before
737      using the method in order to perform a more controlled comparison. In the same way, flags could be
738      ignored to simplify message xml presentation.
739      This powerful tool could be used to program traffic analysis and decide future behaviour (routing,
740      traslation, etc.).
741
742      <pre>
743      Examples:
744
745      The pattern '<avp name="Service-Context-Id" data="(.)*32251@3gpp.org"/>' detects PS charging contexts
746      because of data suffix specification '32251@3gpp.org' for that AVP.
747
748      The pattern '<message version="1" name="Capabilities-Exchange-Request"' detects a CER message.
749
750      The pattern (string including carriage returns):
751
752      '<avp name="Subscription-Id">
753         <avp name="Subscription-Id-Type" data="0" alias="END_USER_E164"/>
754         <avp name="Subscription-Id-Data" data="606000106"/>
755      </avp>'
756
757      detects MSISDN (not IMSI) equal to 606000106
758
759      It would seems strange or 'creative' to use regular expressions within an hex string representation,
760      but anyway you could also do such kind of things to check non-printable data parts within the message:
761      for example, the pattern '<avp name="Framed-IP-Address" hex-data="0a[A-Fa-f0-9][A-Fa-f0-9]0a0a"/>'
762      matchs IP addresses for '10.x.10.10' where x = [0..255].
763
764      Note that string pattern could also be generated via #loadXMLFile/#loadXMLString and then #asXML, thus, you
765      could get patterns through xml files which act as conditional triggers over message. In that case,
766      it is not possible to specify regular expressions within xml 'hex-data' fields because parser will fail
767      during hexadecimal read. Normally only printable 'data' fields are used for matching issues.
768
769      For example, imagine a 'pattern.xml' file like:
770      <message version="1" name="Credit-Control-Request" application-id="16777236" hop-by-hop-id="0" end-to-end-id="0">
771         <avp name="Subscription-Id">
772            <avp name="Subscription-Id-Type" data="0" alias="END_USER_E164"/>
773            <avp name="Subscription-Id-Data" data="616[0-9]{6,6}"/>
774         </avp>
775      </message>
776
777      Then you could do:
778
779      anna::diameter::codec::Message patternMessage;
780      patternMessage.loadXMLFile("pattern.xml");
781      std::string pattern = patternMessage.getAvp("Subscription-Id")->getAvp("Subscription-Id-Type")->asXMLString();
782      // Former is '<avp name="Subscription-Id-Data" data="616[0-9]{6,6}"/>'
783      bool match = incomingMessage.isLike(pattern);
784
785      Then, messages having MSISDN numbers starting with '616' will match the pattern.
786      Note, that any other message codes (and not only Credit-Control-Request ones), could pass the test...
787      You could also build that string manually:
788
789      Example 1:
790      std::string pattern = "<avp name=\"Subscription-Id\">\n";
791      pattern += std::string(ANNA_XML_INDENTATION_SPACES, ' '); pattern += "<avp name=\"Subscription-Id-Type\" data=\"0\" alias=\"END_USER_E164\"/>\n"
792      pattern += std::string(ANNA_XML_INDENTATION_SPACES, ' '); pattern += "<avp name=\"Subscription-Id-Data\" data=\"616[0-9]{6,6}\"/>"
793
794      Example 2:
795      std::string pattern = "name=\"Subscription-Id\"(.)*name=\"Subscription-Id-Type\" data=\"0\"(.)*name=\"Subscription-Id-Data\" data=\"616[0-9]{6,6}\"";
796      </pre>
797
798      \return Returns the match result
799   */
800   bool isLike(const std::string &pattern) const throw();
801
802
803 //friend class Engine;
804   friend class Avp;
805 };
806
807 }
808 }
809 }
810
811
812 #endif
813