/* CPPDOC_BEGIN_EXCLUDE  */
/***************************************************************************

  OxYD Socket v0.1

  File : OxYDSocketHelper.cpp

  
  Versions :
     - v0.1 (20/01/2003) : Initial release

 ---------------------------------------------------------------------------

  Copyright (C) 2003  Grégory WILMES -- Engage Security (http://www.engagesecurity.com)
  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; either
  version 2.1 of the License, or (at your option) any later version.

  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
  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, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

 ***************************************************************************/
/* CPPDOC_END_EXCLUDE  */

#include "stdafx.h"
#include "OxYDSocketHelper.h"


/**
 * The constructor. Initializes all variables to their default value.
 */
COxYDSocketHelper::COxYDSocketHelper()
{
    m_nPacketNumber = 0;
}

/**
 * The destructor.
 */
COxYDSocketHelper::~COxYDSocketHelper()
{
    // Nothing do to ?
}


/**
 * Encode data and send it.
 *
 * @param szData const LPSTR : Data to send
 * @param nDataLength int : Data length
 * @param nMessage int : Message number
 * @param nPacketNumber int : Packet number
 * @return BOOL 
 */
BOOL COxYDSocketHelper::Send(const LPSTR szData, int nDataLength, int nMessage, int nPacketNumber)
{
    CString strPacket;

    Encode(szData, nDataLength, nMessage, nPacketNumber, strPacket);
    m_nPacketNumber++;

    // Send our packet (string format)
    return(SendRaw(strPacket));
}

/**
 * Help to encode data to hexa string with extra parameters.
 *
 * @param szData const LPSTR : Data to encode
 * @param nDataLength int : Data length
 * @param nMessage int : Message number
 * @param nPacketNumber int : Packet number
 * @param strPacket CString& : Data encoded/converted to hexa string
 * @return BOOL 
 */
BOOL COxYDSocketHelper::Encode(const LPSTR szData, int nDataLength, int nMessage, int nPacketNumber, CString &strPacket)
{
    CString strTmp;

    if(nDataLength == -1)
        nDataLength = strlen(szData);

    if(nPacketNumber == -1)
        nPacketNumber = m_nPacketNumber;

    // Convert data to hexa string
    ConvDataToHex(szData, nDataLength, strTmp);

    // Packet format : [Packet number]N[Message]M[Data size]T[Data]
    strPacket.Format("%dN%dM%dT%s", nPacketNumber, nMessage, strlen((LPSTR)(LPCSTR)strTmp), strTmp);

    return TRUE;
}

/**
 * Receive hexa string and decode it.
 *
 * @param szData LPSTR : Data
 * @param nDataLength int : Data buffer length
 * @param nMessage int : Message number
 * @param nPacketNumber int : Packet number
 * @return BOOL 
 */
BOOL COxYDSocketHelper::Receive(LPSTR szData, int &nDataLength, int &nMessage, int &nPacketNumber)
{
    CString strTmp;

    if(ReceiveRaw(strTmp))
        return(Decode(strTmp, szData, nDataLength, nMessage, nPacketNumber));

    return FALSE;
}

/**
 * Help to decode hexa string with extra parameters to data.
 *
 * @param strTmp CString : Hexa string to decode
 * @param szData LPSTR : Data buffer
 * @param nDataLength int : Data buffer length
 * @param nMessage int : Message number
 * @param nPacketNumber int : Packet number
 * @return BOOL 
 */
BOOL COxYDSocketHelper::Decode(CString strTmp, LPSTR szData, int &nDataLength, int &nMessage, int &nPacketNumber)
{
    CString strPacketNumberTmp, strMessageTmp, strDataLengthTmp;
    int nPosPacketNumberTmp, nPosMessageTmp, nPosDataLengthTmp;
    int nPacketNumberTmp, nMessageTmp, nDataLengthTmp;

    ZeroMemory(szData, nDataLength);

    if(strlen(strTmp) > 0)
    {
        // Packet format : [Packet number]N[Message]M[Data size]T[Data]
        if( (nPosPacketNumberTmp = strTmp.Find('N')) >= 1 )
        {
            strPacketNumberTmp = strTmp.Left(nPosPacketNumberTmp);
            nPacketNumberTmp = atoi((LPSTR)(LPCSTR)strPacketNumberTmp);

            if( (nPosMessageTmp = strTmp.Find('M', nPosPacketNumberTmp)) >= 1 )
            {
                strMessageTmp = strTmp.Mid(nPosPacketNumberTmp + 1, nPosMessageTmp -nPosPacketNumberTmp -1);
                nMessageTmp = atoi((LPSTR)(LPCSTR)strMessageTmp);

                if( (nPosDataLengthTmp = strTmp.Find('T', nPosMessageTmp)) >= 1 )
                {
                    strDataLengthTmp = strTmp.Mid(nPosMessageTmp + 1, nPosDataLengthTmp -nPosMessageTmp -1);
                    nDataLengthTmp = atoi((LPSTR)(LPCSTR)strDataLengthTmp);

                    strTmp = strTmp.Mid(nPosDataLengthTmp + 1);

                    if( nDataLengthTmp != (int)(strlen(strTmp)) )
                    {
                        m_nLastError = ERROR__UNCOMPLETE_PACKET;
                        return FALSE;
                    }

                    if(ConvHexToData(strTmp, szData, nDataLength) > 0)
                    {
                        nDataLength = nDataLengthTmp / 2;  // Hexa...
                        nMessage = nMessageTmp;
                        nPacketNumber = nPacketNumberTmp;

                        return TRUE;
                    }
                }
            }
        }

        m_nLastError = ERROR__CORRUPTED_PACKET_HEADER;
    }
    else
        m_nLastError = ERROR__EMPTY_PACKET;

    return FALSE;
}

/**
 * Get last error string from error number.
 *
 * @param nLastError int : Error number (-1 = last error)
 * @return CString : Last error (string format)
 */
CString COxYDSocketHelper::FormatMessage(int nLastError)
{
    CString strLastError;
    int nLastErrorTmp;

    nLastErrorTmp = nLastError;

    if(nLastError == -1)
        nLastError = m_nLastError;

    if(nLastError == ERROR__UNCOMPLETE_PACKET)
        strLastError = "Uncomplete packet";
    else if(nLastError == ERROR__CORRUPTED_PACKET_HEADER)
        strLastError = "Corrupted packet header";
    else if(nLastError == ERROR__EMPTY_PACKET)
        strLastError = "Empty packet (no header and no data)";
    else if(nLastError == ERROR__NULL_POINTER)
        strLastError = "Null pointer";
    else
        strLastError = COxYDSocket::FormatMessage(nLastErrorTmp);

    return strLastError;
}


/**
 * Convert data to hexa string.
 *
 * @param szData LPSTR : Data
 * @param nDataLength int : Data length
 * @param strHex CString& : Data converted to hexa string
 * @return int 
 */
int COxYDSocketHelper::ConvDataToHex(LPSTR szData, int nDataLength, CString &strHex)
{
    CString strTmp, strHexTmp;
    int nPos;

    if(szData != NULL)
    {
        strTmp.Empty();

        for(nPos = 0; nPos < nDataLength; nPos++)
        {
            strHexTmp.Format("%02X", (unsigned char)szData[nPos]);
            strTmp += strHexTmp;
        }

        strHex = strTmp;

        return nPos;
    }

    m_nLastError = ERROR__NULL_POINTER;
    return -1;
}

/**
 * Convert hexa string to data.
 *
 * @param strHex CString : Hexa string
 * @param szData LPSTR : Hexa string converted to data
 * @param nDataLengthMax int : Data buffer length
 * @return int 
 */
int COxYDSocketHelper::ConvHexToData(CString strHex, LPSTR szData, int nDataLengthMax)
{
    CString strTmp, strTmp2;
    int nPos, nPosTmp = 0, nDataLength;
    unsigned char c;

    if(szData != NULL)
    {
        strTmp = strHex;
        nDataLength = strlen(strTmp);

        for(nPos = 0; nPos < nDataLength; nPos = nPos + 2)
        {
            if(nPosTmp == nDataLengthMax)
                break;

            strTmp2 = strTmp.Mid(nPos, 2);

            if( (strTmp2[1] >= 'a') && (strTmp2[1] <= 'f') )
                c = (strTmp2[1] -'a') + 10;
            else if( (strTmp2[1] >= 'A') && (strTmp2[1] <= 'F') )
                c = (strTmp2[1] -'A') + 10;
            else if( (strTmp2[1] >= '0') && (strTmp2[1] <= '9') )
                c = strTmp2[1] -'0';

            if( (strTmp2[0] >= 'a') && (strTmp2[0] <= 'f') )
                c += ((strTmp2[0] -'a') + 10) << 4;
            else if( (strTmp2[0] >= 'A') && (strTmp2[0] <= 'F') )
                c += ((strTmp2[0] -'A') + 10) << 4;
            else if( (strTmp2[0] >= '0') && (strTmp2[0] <= '9') )
                c += (strTmp2[0] -'0') << 4;

            szData[nPosTmp++] = c;
        }

        szData[nPosTmp] = 0;

        return nPosTmp;
    }

    m_nLastError = ERROR__NULL_POINTER;
    return -1;
}