Internal clear respecting codec engine
[anna.git] / include / anna / diameter / codec / Message.hpp
index f4834e0..27ee6da 100644 (file)
@@ -1,37 +1,9 @@
-// ANNA - Anna is Not Nothingness Anymore
-//
-// (c) Copyright 2005-2014 Eduardo Ramos Testillano & Francisco Ruiz Rayo
-//
-// https://bitbucket.org/testillano/anna
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions
-// are met:
-//
-//     * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-//     * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-//     * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-//
-// Authors: eduardo.ramos.testillano@gmail.com
-//          cisco.tierra@gmail.com
+// ANNA - Anna is Not Nothingness Anymore                                                         //
+//                                                                                                //
+// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo                         //
+//                                                                                                //
+// See project site at http://redmine.teslayout.com/projects/anna-suite                           //
+// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE //
 
 
 #ifndef anna_diameter_codec_Message_hpp
@@ -146,7 +118,31 @@ class Message {
   bool flagsOK(int &rc) const throw(); // flags coherence regarding dictionary. Only must be called when Message is identified at the dictionary.
   int addChild(Avp *avp) throw() { return Avp::addChild(a_avps, a_insertionPositionForChilds, avp); }
   const anna::diameter::stack::Command *getStackCommand(CommandId id) const throw(anna::RuntimeException);
-  Avp * addFailedAVP() throw(); // returns Failed-AVP if exists, creates it when missing
+
+  void setFailedAvp(const parent_t &parent, AvpId wrong, const char *wrongName = NULL) throw(anna::RuntimeException);
+  // During message decoding and validation, the first wrong avp is stored and all the tracking is managed to find out its
+  //  nested path for the case of grouped avps with wrong avps inside. Remember the RFC 6733, section 7.5:
+  //
+  //           In the case where the offending AVP is embedded within a Grouped AVP,
+  //           the Failed-AVP MAY contain the grouped AVP, which in turn contains
+  //           the single offending AVP.  The same method MAY be employed if the
+  //           grouped AVP itself is embedded in yet another grouped AVP and so on.
+  //           In this case, the Failed-AVP MAY contain the grouped AVP hierarchy up
+  //           to the single offending AVP.  This enables the recipient to detect
+  //           the location of the offending AVP when embedded in a group.
+  //
+  // The first wrong avp found will set the final result code, as the RFC recommends:
+  //
+  //           The value of the Result-Code AVP will provide information on the reason
+  //           for the Failed-AVP AVP.  A Diameter answer message SHOULD contain an
+  //           instance of the Failed-AVP AVP that corresponds to the error
+  //           indicated by the Result-Code AVP.  For practical purposes, this
+  //           Failed-AVP would typically refer to the first AVP processing error
+  //           that a Diameter node encounters.
+  //
+  // The message keeps the list (reverse order) of avps hierarchy (in case of grouping) for the final Failed-AVP construction,
+  // which is done at the end of decoding or validation, and only the first wrong avp is stored with its corresponding path.
+
 
 protected:
 
@@ -167,14 +163,43 @@ public:
 
   /**
   * Default constructor
+  * @param engine Codec engine used
   */
-  Message();
+  Message(Engine *engine = NULL);
 
   /**
   * Identified constructor
   * @param id Command identifier as pair (code,request-indicator).
-  */
-  Message(CommandId id);
+  * @param engine Codec engine used
+  */
+  Message(CommandId id, Engine *engine = NULL);
+
+
+  /**
+  * Sets the codec engine.
+  *
+  * Once assigned (here or at constructor), this method SHALL NOT be used anymore.
+  * Also, the associated dictionary SHOULD NOT BE CHANGED through the engine,
+  * unless you know what are you doing.
+  * Setting a new different engine with different stack, even same engine where the
+  * stack has been dynamically changed, could cause a bad behaviour depending on the
+  * changes: in general, if the dictionary grows, nothing bad will happen, but if
+  * you remove or modified some elements which were processed with a certain format,
+  * will be interpreted as 'unknown' with the new dictionary, and then some problems
+  * may occur. If you add elements (vendors, avps, messages) is not a problem.
+  *
+  * IMPORTANT NOTES:
+  * 1) if you want to reuse the message, as a recommendation, you should set engine to
+  * NULL or use #clear. In that way, next operation will adjust automatically the needed
+  * engine because it would not be configured in such stage.
+  * 2) if you want to pre-configure the engine you will need to set the engine to NULL
+  * previously and then you could change to the new one without warning/ignoring.
+  * 3) if you have dedicated message objects for each interface (application id), then
+  * you could set the corresponding engine on constructor (or setEngine), and forget
+  * about #clear. The needed cleanup will be done automatically from decoding and xml
+  * loading procedures, and initialized engine will be kept along message operations.
+  */
+  void setEngine(Engine *engine) throw();
 
 
   // Length references
@@ -215,9 +240,8 @@ public:
      Sets the command identifier and clear the former content.
 
      @param id Command identifier as pair (code,request-indicator).
-     @param _clear Message will be cleared when updating the command identifier (default behaviour).
   */
-  void setId(CommandId id, bool _clear = true) throw(anna::RuntimeException);
+  void setId(CommandId id) throw(anna::RuntimeException);
 
   /**
      Same as #setId but providing dictionary logical name for Avp searched
@@ -242,24 +266,37 @@ public:
   /**
      Sets/unsets E bit activation.
      Application should not have to use this because dictionary information is used in order to configure flags when Message identifier is stored.
+     This flag MUST NOT be set in request messages (in this case, it will be ignored).
 
      @param activate Activates/deactivates the bit. True by default.
   */
-  void setErrorBit(bool activate = true) throw() { if(activate) a_flags |= EBitMask; else a_flags &= (~EBitMask); }
+  void setErrorBit(bool activate = true) throw() { if(isRequest()) return; if(activate) a_flags |= EBitMask; else a_flags &= (~EBitMask); }
 
   /**
      Sets/unsets T bit activation.
      Application should not have to use this because dictionary information is used in order to configure flags when Message identifier is stored.
+     This flag MUST NOT be set in answer messages (in this case, it will be ignored).
 
      @param activate Activates/deactivates the bit. True by default.
   */
-  void setPotentiallyReTransmittedMessageBit(bool activate = true) throw() { if(activate) a_flags |= TBitMask; else a_flags &= (~TBitMask); }
+  void setPotentiallyReTransmittedMessageBit(bool activate = true) throw() { if(isAnswer()) return; if(activate) a_flags |= TBitMask; else a_flags &= (~TBitMask); }
 
   /**
-     Sets the message application id
+     Sets the message application id.
+
+     The codec engine could be configured to force a stack selection based in this field value: see #selectStackWithApplicationId.
+     In multistack applications (in case of being monothread), you only have to take care about how to apply this method: the thing
+     is that you must not interleave message builds which belongs to different stacks. For example, you could think about setting the
+     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.
+     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
+     method). The result could be unexpected when adding/encoding messages with a dictionary which does not correspond.
+
+     @warning do not interleave build/encode operations between different messages which uses different stacks over the same codec engine.
+     It seems common sense, but it is not bad to advice about this.
+
      @param aid Application-id.
   */
-  void setApplicationId(U32 aid) throw() { a_applicationId = aid; }
+  void setApplicationId(U32 aid) throw(anna::RuntimeException);
 
   /**
      Sets the message hop-by-hop
@@ -282,10 +319,12 @@ public:
 
      @warning Request provided must be a request, in other case method do nothing.
   */
-  void setHeaderToAnswer(const Message &request) throw() {
+  void setHeaderToAnswer(const Message &request) throw(anna::RuntimeException) {
     if(!request.getId().second) return;
 
-    setId(CommandId(request.getId().first, !request.getId().second), false /* don't clear */);
+    a_engine = request.getEngine(); // we know this will be
+
+    setId(CommandId(request.getId().first, !request.getId().second));
     setVersion(request.getVersion());
     setApplicationId(request.getApplicationId());
     setHopByHop(request.getHopByHop()); // The same Hop-by-Hop Identifier in the request is used in the answer (RFC 6733 Section 6.2).
@@ -340,7 +379,12 @@ public:
    Result-Code and/or Failed-AVP AVPs if proceed, but be aware of DIAMETER_COMMAND_UNSUPPORTED Result-Code, because becomes impossible
    to fix (Session-Id SHOULD appear immediately following the Diameter header, and #fix do this manually even if no information about
    the command structure is known, but perhaps another fixed AVPs could not comply... use #getResultCode to find out this situation before
-   using #setStandardToAnswer).
+   using #setStandardToAnswer). Anyway, application could add another Failed-AVP content no detected internally, for example:
+   DIAMETER_CONTRADICTING_AVPS or DIAMETER_INVALID_AVP_BIT_COMBO). Also, application could add more Failed-AVP avps with other
+   wrong avps, or accumulate wrong avps inside the one and only Failed-AVP managed by the stack. The standard is open to add multiple
+   avps inside Failed-AVP or multiple Failed-AVP avps with single or multiple avps inside. This depends on application criteria regarding
+   other nodes. However, internally the Anna::diameter stack only provides one Failed-AVP with the first wrong avp found, as RFC 6733 says
+   in section 7.5.
 
    If application decoding and/or validation operations are ok, user may search for other problems and put the appropiate Result-Code.
    For example, DIAMETER_TOO_BUSY (3004) depends on congestion issues at business layer and cannot be decided with the only message
@@ -355,7 +399,7 @@ public:
 
    @warning Request provided must be a request, in other case method do nothing.
   */
-  void setStandardToAnswer(const Message &request, const std::string &originHost, const std::string &originRealm, int resultCode = helpers::base::AVPVALUES__Result_Code::DIAMETER_SUCCESS) throw();
+  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);
 
 
   /**
@@ -405,35 +449,6 @@ public:
   int getResultCode() const throw();
 
 
-  /**
-     Adds a new AVP within a Failed-AVP over an answer message (for requests, do nothing).
-     If Failed-AVP AVP doesn't exists, is added and then filled (added within) with the value provided (empty AVP id representantion).
-     If Failed-AVP AVP already exists, is filled (added within) with the value provided (empty AVP id representantion).
-
-     This method is internally used during #decode and/or #valid procedures in order to build automatic answers.
-
-     @param id Avp identifier as pair (code,vendor-id).
-
-     @return Pointer to the new AVP added within Failed-AVP, to make easy data-part accessif needed.
-  */
-  Avp * setNewFailedAvp(AvpId id) throw(anna::RuntimeException) { if(isRequest()) return NULL; return (addFailedAVP()->addAvp(id)); }
-
-  /**
-     Adds a new AVP within a Failed-AVP over an answer message (for requests, do nothing).
-     If Failed-AVP AVP doesn't exists, is added and then filled (added within) with the value provided (empty AVP id representantion).
-     If Failed-AVP AVP already exists, is filled (added within) with the value provided (empty AVP id representantion).
-
-     This method is internally used during #decode and/or #valid procedures in order to build automatic answers, but application
-     could call this for set another Failed-AVP content no detected by these methods, for example: DIAMETER_CONTRADICTING_AVPS or
-     DIAMETER_INVALID_AVP_BIT_COMBO).
-
-     @param id Avp identifier as pair (code,vendor-id).
-
-     @return Pointer to the new AVP added within Failed-AVP, to make easy data-part accessif needed.
-  */
-  Avp * setNewFailedAvp(Avp *avp) throw() { if(!avp || isRequest()) return NULL; return (addFailedAVP()->addAvp(avp)); }
-
-
   /**
      Adds an avp child providing its identifier and reserve internal memory it.
 
@@ -452,12 +467,15 @@ public:
 
   /**
      Adds an avp child providing a persistent pointer (must be maintained by application).
+     It is not allowed to add an avp with no codec engine configured, neither if the engine
+     is not the same.
 
      @param avp Avp external pointer. If NULL provided, nothing is done and NULL returned.
+     Also NULL returned for bad engine configuration.
 
      @return Pointer to the added avp (again).
   */
-  Avp * addAvp(Avp * avp) throw() { if(!avp) return NULL; addChild(avp); return avp; }
+  Avp * addAvp(Avp * avp) throw();
 
 
   /**
@@ -480,11 +498,16 @@ public:
 
   /**
   * Clears and initializes Message class information.
-  * Application must clear auxiliary message objects before adding Avps in a new context.
+  * Application must clear auxiliary message objects before adding Avps in a new context if the same object is reused.
   * Application don't need to clear a message object before decode operation (decode invokes #clear before any other task).
   * Any reimplementation must first invoke base class method.
+  *
+  * @param resetEngine Sets to NULL the codec engine (true, default) or respect its current value (false). If you are going
+  * to reuse the message instance it is better to clear all the information (default) to manage different stacks, because if
+  * you don't initialize the engine to NULL, the second use of the message will keep the same engine deduced from the first
+  * decoding/loading operation, which could be wrong if the second message belongs to a different application identifier.
   */
-  virtual void clear() throw(anna::RuntimeException);
+  virtual void clear(bool resetEngine = true) throw(anna::RuntimeException);
 
   /**
      Decodes buffer provided over class content. If an error ocurred, decoding will stop launching exception (fatal error) or a warning trace (perhaps the achieved
@@ -515,87 +538,21 @@ public:
   */
   bool valid(Message *ptrAnswer = NULL) const throw(anna::RuntimeException);
 
-
   /**
      Interpret xml data in order to dump over the class content.
-     \param messageNode Message root node
+     \param messageNode Message root node obtained from @functions::xmlFileTo
   */
   void fromXML(const anna::xml::Node* messageNode) throw(anna::RuntimeException);
 
   /**
-     Interpret xml string representation in order to dump over the class content.
-     DTD validation is used in the same way that #loadXML does.
-     \param xmlString XML string representation with relevant information for this instance
-  */
-  void fromXMLString(const std::string &xmlString) throw(anna::RuntimeException);
-
-  /**
-     Loads an xml file based on this message DTD (could be accumulative, no initialization will be performed by this method).
-
-     <pre>
-     <!ELEMENT message (avp*)>
-     <!ELEMENT avp (avp*)>
-
-     <!ATTLIST message version CDATA #IMPLIED name CDATA #IMPLIED code CDATA #IMPLIED flags CDATA #IMPLIED application-id CDATA #REQUIRED hop-by-hop-id CDATA #IMPLIED end-by-end-id CDATA #IMPLIED>
-     <!--
-        version: Diameter version. Sets '1' by default
-        name:    Command name within working stack (dictionary identifier)
-
-        In order to get more coding capabilities, command code and flags could be established instead of former command name,
-         but neither of them are allowed if 'name' is provided (and vice versa):
-
-        code:    Command code
-        flags:   Command flags byte value (0-255) where standard bit set for flags is 'RPET rrrr': (R)equest, (P)roxiable, (E)rror, Potentially re-(T)ransmitted message and (r)eserved
-
-
-        application-id:   Message application id
-        hop-by-hop-id:    Message hop by hop id. Sets '0' by default
-        end-by-end-id:    Message end by end id. Sets '0' by default
-     -->
-
-     <!ATTLIST avp name CDATA #IMPLIED code CDATA #IMPLIED vendor-code CDATA #IMPLIED flags CDATA #IMPLIED data CDATA #IMPLIED hex-data CDATA #IMPLIED>
-     <!--
-        name:   Avp name within working stack (dictionary identifier)
-
-        In order to get more coding capabilities, avp code, vendor-id and flags could be established instead of former avp name,
-         but neither of them are allowed if 'name' is provided (and vice versa):
-
-        code:          Avp code
-        vendor-code:   Avp vendor code
-        flags:         Avp flags byte value (0-255) where standard bit set for flags is 'VMPr rrrr': (V)endor-specific, (M)andatory, end to end encry(P)tion and r(eserved)
-
-
-        data:          Natural string representation for avp data. Specially applicable with numbers and printable strings, but also
-                        useful for certain formats which could be easily understandable in such friendly/smart representation. We will
-                        achieve different human-readable strings depending on data format:
-
-                          [ OctetString ] (if printable, but not recommended)
-                          [ Integer32, Integer64, Unsigned32, Unsigned64, Float32, Float64 ] (normal number representation)
-                          [ Time ] (NTP timestamp, normal number representation)
-                          [ Address ] (auto detects IPv4 or IPv6 address version, then only ip address is specified: IPv4 with dots, IPv6 with colons)
-                          [ UTF8String, DiameterIdentity, DiameterURI ] (printable)
-                          [ IPFilterRule, QoSFilterRule ] (uses ASCII charset, printable)
-
-                          New application formats must define specific natural representation for internal raw data
-
-        hex-data:      Hexadecimal octet sequence representation (i.e. 'af012fb3', with even number of digits). Suitable for whatever kind
-                        of diameter format, but mandatory for non printable information. OctetString usually transport non human-readable
-                        data and should better be encoded within this field although being printable. Unknown avps (which fails identifying
-                        provided name or code/vendor-code) must always use this representation.
-
-        Xml representation for decoded messages shows natural content except for 'OctetString' format and unknown avps. Anyway, when printable,
-         OctetString could show such information at data field apart from hex-data, because many implementations use this format to transport
-         readable-string data. In general, one of the data fields is mandatory except for 'Grouped' type (its data is another level of avps).
-        Application-specific formats must decide the way to represent its contents, being recommended to use a natural representation if possible,
-         because xml is read by humans with testing and monitoring purposes.
-     -->
-     </pre>
-
-     @param xmlPathFile Complete path file to the xml document which represents the diameter message
-     @see fromXMLString
-  */
-  void loadXML(const std::string & xmlPathFile) throw(anna::RuntimeException);
-
+   * Interpret a xml file in order to create a diameter message
+   *
+   * @see functions::messageXmlDocumentFromXmlFile
+   * @see fromXML
+   *
+   * @param xmlPathFile Complete path file to the xml document which represents the diameter message
+   */
+  void loadXML(const std::string &xmlPathFile) throw(anna::RuntimeException);
 
 
   // getters
@@ -750,7 +707,7 @@ public:
   std::string asXMLString() const throw();
 
   /**
-     Comparison operator
+     Comparison operator by mean serialization
 
      @param m1 Instance 1 for Message class
      @param m2 Instance 2 for Message class