/***********************************************************************
 * 
 *  Copyright (C) 2006 Novell, Inc. All Rights Reserved.
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; version 2.1
 *  of the License.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Library Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, Novell, Inc.
 * 
 *  To contact Novell about this file by physical or electronic mail, 
 *  you may find current contact information at www.novell.com.
 * 
 *  Author: Juan Carlos Luciani <jluciani@novell.com>
 *
 ***********************************************************************/


//===[ Include files ]=====================================================

#define __STDC_LIMIT_MACROS
#include "ipcint.h"

//===[ External data ]=====================================================

//===[ External prototypes ]===============================================

//===[ Manifest constants ]================================================

//===[ Type definitions ]==================================================

//===[ Function prototypes ]===============================================

//===[ Global variables ]==================================================

// Channel Packet Types
string DataCarrierTypeTemplate = "TypeXX";
string ReqDataCarrierType =      "Type01";
string ReqErrorCarrierType =     "Type02";

// Channel Packet Headers
string ReqIdHdr = "ReqIdHdr =";
string PayloadLengthHdr = "PayloadLength =";

// Req Data Pkt Hdr Template
string ReqDataPktHdrTemplate =   "Type01\r\nReqIdHdr =XXXXXXXX\r\nPayloadLength =XXXXXXXX\r\n\r\n";

// Req Error Pkt Hdr Template
string ReqErrorPktHdrTemplate =  "Type02\r\nReqIdHdr =XXXXXXXX\r\nPayloadLength =XXXXXXXX\r\n\r\n";


//++=======================================================================
int
ChannelProto::buildReqDataPktHdr(
   uint32_t reqId,
   uint32_t payloadLength,
   char *pPktHdr)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes: pPktHdr must point to a buffer of size ReqDataPktHdrTemple.length().
//
// L2
//=======================================================================--
{
   int   retStatus = -1;

   DbgTrace(1, "ChannelProto::buildReqDataPktHdr- Start\n", 0);

   try {
      // - Req Data Packet Header Format -
      //
      // ReqDataCarrierType
      // ReqIdHdr value (value format=%08X)
      // PayloadLengthHdr value (value format=%08X)
      //

      // Setup the necessary value strings
      char wrkBuffer[10];

      sprintf(wrkBuffer, "%08X", reqId);
      string reqIdValue = wrkBuffer;
      sprintf(wrkBuffer, "%08X", payloadLength);
      string payloadLengthValue = wrkBuffer;

      // Format the header.
      char* pCurr = pPktHdr;

      memcpy(pCurr, ReqDataCarrierType.c_str(), ReqDataCarrierType.length());
      pCurr += ReqDataCarrierType.length();
      memcpy(pCurr, "\r\n", 2);
      pCurr += 2;

      memcpy(pCurr, ReqIdHdr.c_str(), ReqIdHdr.length());
      pCurr += ReqIdHdr.length();
      memcpy(pCurr, reqIdValue.c_str(), reqIdValue.length());
      pCurr += reqIdValue.length();
      memcpy(pCurr, "\r\n", 2);
      pCurr += 2;

      memcpy(pCurr, PayloadLengthHdr.c_str(), PayloadLengthHdr.length());
      pCurr += PayloadLengthHdr.length();
      memcpy(pCurr, payloadLengthValue.c_str(), payloadLengthValue.length());
      pCurr += payloadLengthValue.length();
      memcpy(pCurr, "\r\n\r\n", 4);

      // Success
      retStatus = 0;
   }
   catch (...) {
      DbgTrace(0, "ChannelProto::buildReqDataPktHdr- Exception caught while creating header\n", 0);
   }

   DbgTrace(1, "ChannelProto::buildReqDataPktHdr- End, retStatus = %0X\n", retStatus);

   return retStatus;

}  /*-- ChannelProto::buildReqDataPktHdr() --*/


//++=======================================================================
int
ChannelProto::buildReqErrorPktHdr(
   uint32_t reqId,
   uint32_t payloadLength,
   char *pPktHdr)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes: pPktHdr must point to a buffer of size ReqErrorPktHdrTemple.length().
//
// L2
//=======================================================================--
{
   int   retStatus = -1;

   DbgTrace(1, "ChannelProto::buildReqErrorPktHdr- Start\n", 0);

   try {
      // - Req Error Packet Header Format -
      //
      // ReqErrorCarrierType
      // ReqIdHdr value (value format=%08X)
      // PayloadLengthHdr value (value format=%08X)
      //

      // Setup the necessary value strings
      char wrkBuffer[10];

      sprintf(wrkBuffer, "%08X", reqId);
      string reqIdValue = wrkBuffer;
      sprintf(wrkBuffer, "%08X", payloadLength);
      string payloadLengthValue = wrkBuffer;

      // Format the header.
      char* pCurr = pPktHdr;

      memcpy(pCurr, ReqErrorCarrierType.c_str(), ReqErrorCarrierType.length());
      pCurr += ReqErrorCarrierType.length();
      memcpy(pCurr, "\r\n", 2);
      pCurr += 2;

      memcpy(pCurr, ReqIdHdr.c_str(), ReqIdHdr.length());
      pCurr += ReqIdHdr.length();
      memcpy(pCurr, reqIdValue.c_str(), reqIdValue.length());
      pCurr += reqIdValue.length();
      memcpy(pCurr, "\r\n", 2);
      pCurr += 2;

      memcpy(pCurr, PayloadLengthHdr.c_str(), PayloadLengthHdr.length());
      pCurr += PayloadLengthHdr.length();
      memcpy(pCurr, payloadLengthValue.c_str(), payloadLengthValue.length());
      pCurr += payloadLengthValue.length();
      memcpy(pCurr, "\r\n\r\n", 4);

      // Success
      retStatus = 0;
   }
   catch (...) {
      DbgTrace(0, "ChannelProto::buildReqErrorPktHdr- Exception caught while creating header\n", 0);
   }

   DbgTrace(1, "ChannelProto::buildReqErrorPktHdr- End, retStatus = %0X\n", retStatus);

   return retStatus;

}  /*-- ChannelProto::buildReqErrorPktHdr() --*/


//++=======================================================================
ChannelProto::PacketTypes
ChannelProto::getPktType(
   char &buff,
   int hdrLength)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L2
//=======================================================================--
{
   PacketTypes packetType = UnknownPacketType;

   DbgTrace(1, "ChannelProto::getPktType- Start\n", 0);

   // Find the end of the Channel Packet Type
   char *pCurr = &buff;
   int bytesLeft = hdrLength;
   bool endFound = false;
   while (bytesLeft)
   {
      if (*pCurr == '\r')
      {
         endFound = true;
         break;
      }

      pCurr ++;
      bytesLeft --;
   }

   if (endFound)
   {
      // Found the end of the Channel Packet Type, now
      // calculate its length.
      int channelPktTypeLength = pCurr - &buff;

      // Now start comparing
      if (channelPktTypeLength == ReqDataCarrierType.length()
          && !memcmp(&buff, ReqDataCarrierType.c_str(), channelPktTypeLength))
      {
         // The type is Channel Req Data Carrier
         packetType = ReqDataCarrierPacketType;
      }
      else if (channelPktTypeLength == ReqErrorCarrierType.length()
               && !memcmp(&buff, ReqErrorCarrierType.c_str(), channelPktTypeLength))
      {
         // The type is Channel Req Error Carrier
         packetType = ReqErrorCarrierPacketType;
      }
      else
      {
         DbgTrace(0, "ChannelProto::getPktType- No match found\n", 0);
      }
   }
   else
   {
      DbgTrace(0, "ChannelProto::getPktType- Invalid header\n", 0);
   }

   DbgTrace(1, "ChannelProto::getPktType- End, type = %d\n", packetType);

   return packetType;

}  /*-- ChannelProto::getPktType() --*/


//++=======================================================================
bool
ChannelProto::getReqIdAndPayloadLength(
   char *pBuff,
   int hdrLength,
   uint32_t *pReqId,
   uint32_t *pPayloadLength)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L2
//=======================================================================--
{
   bool  reqIdObtained = false;
   bool  payloadLengthObtained = false;

   DbgTrace(1, "ChannelProto::getReqIdAndPayloadLength- Start\n", 0);

   char *pCurr = pBuff;
   char *pChannelHdr = NULL;
   int bytesLeft = hdrLength;

   // Skip the Channel Packet Type which should always
   // be the first header.
   while (bytesLeft >= 2)
   {
      if (*pCurr == '\r'
          && *(pCurr+1) == '\n')
      {
         // Found the end of the channel packet type
         pCurr += 2;
         bytesLeft -= 2;
         break;
      }
      else
      {
         pCurr ++;
         bytesLeft --;
      }
   }

   // Start processing Channel Packet Headers
   pChannelHdr = pCurr;
   while (bytesLeft >= 2
          && (!reqIdObtained || !payloadLengthObtained))
   {
      if (*pCurr == '\r'
          && *(pCurr+1) == '\n')
      {
         // Found the end of the current channel header
         pCurr += 2;
         bytesLeft -= 2;

         // Check if the line is empty or if it contained a
         // channel header.
         if ((pCurr - pChannelHdr) == 2)
         {
            // This was an empty line, which means that
            // we reached the end of the channel packet header.
            break;
         }
         else
         {
            // Check if the header is the Req Id Hdr
            if (!reqIdObtained && (pCurr - pChannelHdr) > ReqIdHdr.length()
                && !memcmp(pChannelHdr, ReqIdHdr.c_str(), ReqIdHdr.length()))
            {
               // We found the Req Id Hdr, get the value.
               char *pValue = pChannelHdr + ReqIdHdr.length();

               // Temporarily NULL terminate the value
               *(pCurr-2) = '\0';

               // Convert the value to hex
               errno = 0;
               unsigned long int value = strtoul(pValue, NULL, 16);
               if (errno != 0
                   || value > UINT32_MAX)
               {
                  DbgTrace(0, "ChannelProto::getReqIdAndPayloadLength- Invalid reqId value, %s\n", pValue);
                  break;
               }

               // Use the value
               *pReqId = (uint32_t) value;

               // Undo the damage that we did
               *(pCurr-2) = '\r';

               // Remember that the Req Id was obtained
               reqIdObtained = true;
            }
            // Check if the header is the Payload Length Hdr
            else if ((pCurr - pChannelHdr) > PayloadLengthHdr.length()
                     && !memcmp(pChannelHdr, PayloadLengthHdr.c_str(), PayloadLengthHdr.length()))
            {
               // We found the Payload Length Hdr, get the value.
               char *pValue = pChannelHdr + PayloadLengthHdr.length();

               // Temporarily NULL terminate the value
               *(pCurr-2) = '\0';

               // Convert the value to hex
               errno = 0;
               unsigned long int value = strtoul(pValue, NULL, 16);
               if (errno != 0
                   || value > UINT32_MAX)
               {
                  DbgTrace(0, "ChannelProto::getReqIdAndPayloadLength- Invalid payloadLength value, %s\n", pValue);
                  break;
               }

               // Use the value
               *pPayloadLength = (uint32_t) value;

               // Undo the damage that we did
               *(pCurr-2) = '\r';

               // Remember that the Payload Lenght was obtained
               payloadLengthObtained = true;
            }

            // Get set to process the next header
            pChannelHdr = pCurr;
         }
      }
      else
      {
         pCurr ++;
         bytesLeft --;
      }
   }

   DbgTrace(1,
            "ChannelProto::getReqIdAndPayloadLength- End, retStatus = %0X\n",
            reqIdObtained && payloadLengthObtained);

   return reqIdObtained && payloadLengthObtained;

}  /*-- ChannelProto::getReqIdAndPayloadLength() --*/


//=========================================================================
//=========================================================================