text: /*
   *    Copyright 2007 Baylor University
   * 
   *    Licensed under the Apache License, Version 2.0 (the "License");
   *    you may not use this file except in compliance with the License.
   *    You may obtain a copy of the License at
   * 
   *        http://www.apache.org/licenses/LICENSE-2.0
   * 
   *    Unless required by applicable law or agreed to in writing, software
   *    distributed under the License is distributed on an "AS IS" BASIS,
   *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   *    See the License for the specific language governing permissions and
   *    limitations under the License.
   */
  
  #ifndef _PROPHET_TLV_H_
  #define _PROPHET_TLV_H_
  
  #include <string>
  #include <list>
  #include "BaseTLV.h"
  
  namespace prophet {
  
  class ProphetTLV 
  {
  public:
      typedef std::list<BaseTLV*> List;
      typedef std::list<BaseTLV*>::iterator iterator;
      typedef std::list<BaseTLV*>::const_iterator const_iterator;
  
      /**
       * Header Definition
       * p. 21, 4.2
       */
      struct ProphetHeader {
          u_int8_t version; ///< This version of the PRoPHET Protocol = 1.
          u_int8_t flags; ///< TBD
          /**
           * Field  that is used to indicate whether a response is required
           * to the request message if the outcome is successful.  A value of
           * "NoSuccessAck" indicates that the request message does not
           * expect a response if the outcome is successful, and a value of
           * "AckAll" indicates that a response is expected if the outcome is
           * successful.  In both cases a failure response MUST be generated
           * if the request fails.<br><br>
           * In a response message, the result field can have two values:
           * "Success," and "Failure".  The "Success" results indicates a
           * success response.  All messages that belong to the same success
           * response will have the same Transaction Identifier.  The
           * "Success" result indicates a success response that may be
           * contained in a single message or the final message of a success
           * response spanning multiple messages.<br><br>
           * ReturnReceipt is a result field used to indicate that an
           * acknowledgement is required for the message.  The default for
           * Messages is that the controller will not acknowledge responses.
           * In the case where an acknowledgement is required, it will set
           * the Result Field to ReturnReceipt in the header of the Message.
           * <br><br>
           * The encoding of the result field is:<br>
           * <br>
           *            NoSuccessAck:       Result = 1<br>
           *            AckAll:             Result = 2<br>
           *            Success:            Result = 3<br>
           *            Failure:            Result = 4<br>
           *            ReturnReceipt       Result = 5<br>
           * <br>
           */
          u_int8_t result;
          /**
           * Field gives further information concerning the result in a
           * response message.  It is mostly used to pass an error code in a
           * failure response but can also be used to give further
           * information in a success response message or an event message.
           * In a request message, the code field is not used and is set to
           * zero.<br><br>
           * If the Code field indicates that the Error TLV is included in
           * the message, further information on the error will be found in
           * the Error TLV, which MUST be the the first TLV after the header.
           * <br><br>
           * The encoding is:<br>
           * <br>
           *     PRoPHET Error messages       0x000 - 0x099<br>
           *     Reserved                     0x0A0 - 0x0FE<br>
           *     Error TLV in message             0x0FF<br>
           * 
           */
          u_int8_t code;
          /**
           * For messages during the Hello phase with the Hello SYN, Hello
           * SYNACK, and Hello ACK functions, it is the sender's instance
           * number for the link.  It is used to detect when the link comes
           * back up after going down or when the identity of the entity at
           * the other end of the link changes.  The instance number is a 16-
           * bit number that is guaranteed to be unique within the recent
           * past and to change when the link or node comes back up after
           * going down.  Zero is not a valid instance number.  For the
           * RSTACK function, the Sender Instance field is set to the value
           * of the Receiver Instance field from the incoming message that
           * caused the RSTACK function to be generated.  Messages sent after
           * the Hello phase is completed should use the sender's instance
           * number for the link.
           */
          u_int16_t sender_instance;
          /**
           * For messages during the Hello phase with the Hello SYN, Hello
           * SYNACK, and Hello ACK functions, is what the sender believes is
           * the current instance number for the link, allocated by the
           * entity at the far end of the link.  If the sender of the message
           * does not know the current instance number at the far end of the
           * link, this field SHOULD be set to zero.  For the RSTACK message,
           * the Receiver Instance field is set to the value of the Sender
           * Instance field from the incoming message that caused the RSTACK
           * message to be generated.  Messages sent after the Hello phase is
           * completed should use what the sender believes is the current
           * instance number for the link, allocated by the entity at the far
           * end of the link.
           */
          u_int16_t receiver_instance;
          /**
           * Used to associate a message with its response message.  This
           * should be set in request messages to a value that is unique for
           * the sending host within the recent past.  Reply messages contain
           * the Transaction Indentifier of the request they are responding to.
           */
          u_int32_t transaction_id;
          /**
           * If S is set then the SubMessage Number field indicates the total
           * number of SubMessage segments that compose the entire message.
           * If it is not set then the SubMessage  Number field indicates the
           * sequence number of this SubMessage segment within the whole
           * message. the S field will only be set in the first sub-message
           * of a sequence.
           */
          u_int16_t submessage_flag:1;
          /**
           * When a message is segmented because it exceeds the MTU of the
           * link layer, each segment will include a submessage number to
           * indicate its position.  Alternatively, if it is the first
           * submessage in a sequence of submessages, the S flag will be set
           * and this field will contain the total count of submessage
           * segments.
           */
          u_int16_t submessage_num:15;
          /**
           * Length in octets of this message including headers and message
           * body.  If the message is fragmented, this field contains the
           * length of this submessage.
           */
          u_int16_t length;
      } __attribute__((packed));
  
      /**
       * Legal values for ProphetHeader.result field
       * p. 22, 4.2
       */
      typedef enum {
          UnknownResult = 0x0,
          NoSuccessAck  = 0x1,
          AckAll        = 0x2,
          Success       = 0x3,
          Failure       = 0x4,
          ReturnReceipt = 0x5
      } header_result_t;
  
      static const size_t ProphetHeaderSize = sizeof(struct ProphetHeader);
  
      /**
       * Pretty print function for header_result_t.
       */
      static const char* result_to_str(header_result_t hr)
      {
          switch(hr) {
              case NoSuccessAck:  return "NoSuccessAck";
              case AckAll:        return "AckAll";
              case Success:       return "Success";
              case Failure:       return "Failure";
              case ReturnReceipt: return "ReturnReceipt";
              case UnknownResult:
              default:            return "Unknown header result";
          }
      }
  
      /**
       * Constructor
       */
      ProphetTLV(const std::string& src,
                 const std::string& dst,
                 header_result_t result,
                 u_int16_t local_instance,
                 u_int16_t remote_instance,
                 u_int32_t tid);
  
      /**
       * Copy constructor
       */
      ProphetTLV(const ProphetTLV& tlv);
  
      /**
       * Destructor
       */
      virtual ~ProphetTLV();
  
      /**
       * Write ProphetTLV out to no more than len bytes of buffer; return
       * bytes written
       */
      size_t serialize(u_char* bp, size_t len) const;
  
      /**
       * Read ProphetTLV in from no more than len bytes of buffer; return
       * bytes read
       */
      static ProphetTLV* deserialize(const std::string& src,
                                     const std::string& dst,
                                     const u_char* bp, size_t len);
  
      /**
       * Place TLV on list for serialization into next outbound ProphetTLV.
       * ProphetTLV assumes ownership of memory on submitted pointer,
       * on success.
       */
      bool add_tlv(BaseTLV* tlv);
  
      /**
       * Remove next TLV from list. Caller assumes ownership of memory
       * pointed to by returned pointer (if non-NULL).
       */
      BaseTLV* get_tlv();
  
      ///@{ Accessors
      const std::string& source()         const { return src_; }
      const std::string& destination()    const { return dst_; }
      header_result_t result()            const { return result_; }
      u_int16_t       sender_instance()   const { return sender_instance_; }
      u_int16_t       receiver_instance() const { return receiver_instance_; }
      u_int32_t       transaction_id()    const { return tid_; }
      u_int16_t       length()            const { return length_; }
      size_t          size()              const { return list_.size(); }
      const List&     list()              const { return list_; }
      ///@}
  
  protected:
      ProphetTLV();
  
      std::string src_; ///< destination id for TLV source (from Bundle metadata)
      std::string dst_; ///< destination id for TLV destination (from Bundle ")
      header_result_t result_; ///< Disposition of this Prophet TLV 
      u_int16_t sender_instance_; ///< Local node's index for this encounter
      u_int16_t receiver_instance_; ///< Peer's index for this encounter
      u_int32_t tid_; ///< Transaction ID for this TLV
      mutable u_int16_t length_; ///< Serialized length of this TLV
      List list_; ///< Linked list of TLVs embedded in this Prophet TLV
  
  }; // class ProphetTLV
  
  }; // namespace prophet
  #endif //  _PROPHET_TLV_H_
  