Ensures normalization on waitfe/fc-xml operations
[anna.git] / include / anna / diameter / codec / Message.hpp
index 27ee6da..2ec338b 100644 (file)
@@ -189,12 +189,9 @@ public:
   * 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
+  * 1) if you want to reuse the message, as a recommendation, you should #clear the
+  * message. In that way, next operation will adjust automatically the needed engine.
+  * 2) 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.
@@ -226,7 +223,8 @@ public:
   /**
   * Destructor
   */
-  ~Message();
+  virtual ~Message();
+
   // Virtual destructors are useful when you can delete an instance of a derived class through a pointer to base class:
   // 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.
   // My current solution: virtualizing method 'clear'
@@ -514,6 +512,8 @@ public:
      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
      in a moment which depends on validation depth (codec::Engine::ValidationDepth).
 
+     You could decode multiple times over the same object. A basic cleanup is done respecting the codec engine.
+
      @param db buffer data block processed. Before decoding, the whole message instance will be cleared (no need to invoke #clear before #decode).
      @param ptrAnswer Answer set by application (could be empty or not), who is responsible for its memory reservation,
      and automatically built regarding standard. If message analyzed realizes to be an answer, internal reference becomes
@@ -540,19 +540,32 @@ public:
 
   /**
      Interpret xml data in order to dump over the class content.
+     You could apply this multiple times over the same object. A basic cleanup is done respecting the codec engine.
      \param messageNode Message root node obtained from @functions::xmlFileTo
   */
   void fromXML(const anna::xml::Node* messageNode) throw(anna::RuntimeException);
 
   /**
    * Interpret a xml file in order to create a diameter message
+   * You could apply this multiple times over the same object. A basic cleanup is done respecting the codec engine.
    *
    * @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);
+  void loadXMLFile(const std::string &xmlPathFile) throw(anna::RuntimeException);
+
+  /**
+   * Interpret a xml string in order to create a diameter message
+   * You could apply this multiple times over the same object. A basic cleanup is done respecting the codec engine.
+   *
+   * @see functions::messageXmlDocumentFromXmlString
+   * @see fromXML
+   *
+   * @param xmlString xml representation of the diameter message
+   */
+  void loadXMLString(const std::string &xmlString) throw(anna::RuntimeException);
 
 
   // getters
@@ -702,9 +715,12 @@ public:
 
   /**
      Class xml string representation
+     @param normalize Optional normalization which sorts attribute names and removes
+     newlines in the xml representation in order to ease regexp matching.
+
      \return XML string representation with relevant information for this instance.
   */
-  std::string asXMLString() const throw();
+  std::string asXMLString(bool normalize = false) const throw();
 
   /**
      Comparison operator by mean serialization
@@ -717,69 +733,59 @@ public:
   friend bool operator == (const Message & m1, const Message & m2) throw() { return (m1.asXMLString() == m2.asXMLString()); }
 
   /**
-     Match a regular expression (string pattern) regarding xml string serialization for this message.
-     Using a complex pattern (many avps, grouped ones) it could be necessary to fix the message before
-     using the method in order to perform a more controlled comparison. In the same way, flags could be
-     ignored to simplify message xml presentation.
-     This powerful tool could be used to program traffic analysis and decide future behaviour (routing,
-     traslation, etc.).
+     Matchs a regular expression (string pattern) regarding xml string serialization for this message.
+     The message xml representation is internally normalized (attribute names are sort and newlines
+     are removed) in order to ease regexp matching.
 
-     <pre>
-     Examples:
+     You could use simple regular expressions.
+     For example, the pattern '<avp data="(.)*32251@3gpp.org" name="Service-Context-Id"/>' detects
+     PS charging contexts because of data suffix specification '32251@3gpp.org' for that AVP.
+     The pattern '<message(.)* name="Capabilities-Exchange-Request"' detects a CER message. And so on.
 
-     The pattern '<avp name="Service-Context-Id" data="(.)*32251@3gpp.org"/>' detects PS charging contexts
-     because of data suffix specification '32251@3gpp.org' for that AVP.
+     It would seems strange or 'creative' to use regular expressions within an hex string representation,
+     but anyway you could also do such kind of things to check non-printable data parts within the message:
+     for example, the pattern '<avp hex-data="0a[A-Fa-f0-9]{2}0a0a" name="Framed-IP-Address"/>'
+     matchs IP addresses for '10.x.10.10' where x = [0..255].
 
-     The pattern '<message version="1" name="Capabilities-Exchange-Request"' detects a CER message.
+     Normally only printable 'data' fields are used for matching issues.
 
-     The pattern (string including carriage returns):
+     Now imagine 'message.xml' containing this avp:
 
-     '<avp name="Subscription-Id">
-        <avp name="Subscription-Id-Type" data="0" alias="END_USER_E164"/>
-        <avp name="Subscription-Id-Data" data="606000106"/>
+     <pre>
+     ...
+     <avp name="Subscription-Id">
+        <avp alias="END_USER_E164" data="0" name="Subscription-Id-Type"/>
+        <avp data="616[0-9]{6}" name="Subscription-Id-Data"/>
      </avp>'
+     ...
+     </pre>
 
-     detects MSISDN (not IMSI) equal to 606000106
+     You could also extract AVP xml normalized representation in this way:
 
-     It would seems strange or 'creative' to use regular expressions within an hex string representation,
-     but anyway you could also do such kind of things to check non-printable data parts within the message:
-     for example, the pattern '<avp name="Framed-IP-Address" hex-data="0a[A-Fa-f0-9][A-Fa-f0-9]0a0a"/>'
-     matchs IP addresses for '10.x.10.10' where x = [0..255].
+     <pre>
+     anna::diameter::codec::Message myMessage;
+     myMessage.loadXMLFile("message.xml");
+     std::string subscriptionId = myMessage.getAvp("Subscription-Id")->getAvp("Subscription-Id-Type")->asXMLString(true);
+     // Former is '<avp data="616[0-9]{6}" name="Subscription-Id-Data"/>'
+     </pre>
 
-     Note that string pattern could also be generated via #loadXML and then #asXML, that is to say, you
-     could get patterns through xml files which act as conditional triggers over message. In that case,
-     it is not possible to specify regular expressions within xml 'hex-data' fields because parser will fail
-     during hexadecimal read. Normally only printable 'data' fields are used for matching issues.
-
-     For example, imagine a 'pattern.xml' file like:
-     <message version="1" name="Credit-Control-Request" application-id="16777236" hop-by-hop-id="0" end-by-end-id="0">
-        <avp name="Subscription-Id">
-           <avp name="Subscription-Id-Type" data="0" alias="END_USER_E164"/>
-           <avp name="Subscription-Id-Data" data="616[0-9]{6,6}"/>
-        </avp>
-     </message>
-
-     Then you could do:
-
-     anna::diameter::codec::Message patternMessage;
-     patternMessage.loadXML("pattern.xml");
-     std::string pattern = patternMessage.getAvp("Subscription-Id")->getAvp("Subscription-Id-Type")->asXMLString();
-     // Former is '<avp name="Subscription-Id-Data" data="616[0-9]{6,6}"/>'
-     bool match = incomingMessage.isLike(pattern);
-
-     Then, messages having MSISDN numbers starting with '616' will match the pattern.
-     Note, that any other message codes (and not only Credit-Control-Request ones), could pass the test...
-     You could also build that string manually:
-
-     Example 1:
-     std::string pattern = "<avp name=\"Subscription-Id\">\n";
-     pattern += ANNA_XML_COMPILER_TAB; pattern += "<avp name=\"Subscription-Id-Type\" data=\"0\" alias=\"END_USER_E164\"/>\n"
-     pattern += ANNA_XML_COMPILER_TAB; pattern += "<avp name=\"Subscription-Id-Data\" data=\"616[0-9]{6,6}\"/>"
-
-     Example 2:
-     std::string pattern = "name=\"Subscription-Id\"(.)*name=\"Subscription-Id-Type\" data=\"0\"(.)*name=\"Subscription-Id-Data\" data=\"616[0-9]{6,6}\"";
+     And then use to match incoming messages:
+
+     <pre>
+     bool match = incomingMessage.isLike(subscriptionId);
      </pre>
 
+     Using a complex pattern (many avps, grouped ones) is possible, indeed testing ADML engine supports 'waitfe/fc-xml'
+     operations which load entire diameter messages to be used as a whole regular expression (hop-by-hop, end-to-end and
+     Origin-State-Id avp is automatically replaced by '[0-9]+' to make possible the comparison).
+
+     Those operations makes all the work, but if you use the API, you may take into account:
+
+     - Respect indentation for inner Message xml representation (normally 3 spaces).
+     - Sort alphabetically the attribute names in every xml node.
+     - Remove all the newlines in the xml representation as normalization stage.
+     - Ignore flags and set the fix mode for the message.
+
      \return Returns the match result
   */
   bool isLike(const std::string &pattern) const throw();
@@ -795,3 +801,4 @@ public:
 
 
 #endif
+