//--------------------------------------------------------------------
//  Copyright (C)1998 Microsoft Corporation, All Rights Reserved.
//
//  scep.cpp
//
//  This file holds most of the implementation of CSCEP_CONNECTION
//  objects. Each active connection to a camera is represented by
//  a separate CSCEP_CONNECTION object. The CSCEP_CONNECTION is then
//  destroyed when the connection (socket) to the camera is closed.
//
//  Author:
//
//    Edward Reus (edwardr)     02-24-98   Initial coding.
//
//--------------------------------------------------------------------

#include "precomp.h"

typedef struct _ATTRIBUTE_TOKEN
    {
      DWORD  dwTokenType;
      UCHAR *pChars;
      DWORD  dwSize;
    } ATTRIBUTE_TOKEN;

#define ATTRIBUTE_NAME_SIZE        2

#define COLON                     ':'
#define ONE                       '1'
#define SPACE                     ' '
#define TAB                       '\t'
#define CR                        0x0d
#define LF                        0x0a

#define ATTRIBUTE_NAME             0
#define ATTRIBUTE_COLON            1
#define ATTRIBUTE_VALUE            2
#define ATTRIBUTE_CRLF             3

#define ATTR_PDU_SIZE              0
#define ATTR_PRODUCT_ID            1
#define ATTR_USER_NAME             2
#define ATTR_PASSWORD              3

//--------------------------------------------------------------------
// Globals:
//--------------------------------------------------------------------

static  DWORD g_adwPduSizes[] 
              = { PDU_SIZE_1, PDU_SIZE_2, PDU_SIZE_3, PDU_SIZE_4 };

#ifdef DBG_MEM
static  LONG g_lCScepConnectionCount = 0;
#endif

//--------------------------------------------------------------------
// SkipBlanks()
//
//--------------------------------------------------------------------
void SkipBlanks( IN OUT UCHAR **ppAttributes,
                 IN OUT DWORD  *pdwAttributeSize )
    {
    while ( (*pdwAttributeSize > 0)
          && ((**ppAttributes == SPACE)||(**ppAttributes == TAB)) )
        {
        (*ppAttributes)++;
        (*pdwAttributeSize)--;
        }
    }

//--------------------------------------------------------------------
// NextToken()
//
//--------------------------------------------------------------------
ATTRIBUTE_TOKEN *NextToken( IN     DWORD   dwTokenType,
                            IN OUT UCHAR **ppAttributes,
                            IN OUT DWORD  *pdwAttributeSize )
    {
    ATTRIBUTE_TOKEN *pToken = 0;

    SkipBlanks(ppAttributes,pdwAttributeSize);

    if ((!*ppAttributes) || (*pdwAttributeSize == 0))
        {
        return 0;
        }

    pToken = (ATTRIBUTE_TOKEN*)AllocateMemory(sizeof(ATTRIBUTE_TOKEN));
    if (!pToken)
        {
        return 0;
        }

    pToken->dwTokenType = dwTokenType;

    switch (dwTokenType)
        {
        case ATTRIBUTE_NAME:
            if (*pdwAttributeSize < ATTRIBUTE_NAME_SIZE)
                {
                FreeMemory(pToken);
                pToken = 0;
                break;
                }

            pToken->pChars = *ppAttributes;
            pToken->dwSize = ATTRIBUTE_NAME_SIZE;
            *ppAttributes += ATTRIBUTE_NAME_SIZE;
            *pdwAttributeSize -= ATTRIBUTE_NAME_SIZE;
            break;

        case ATTRIBUTE_COLON:
            if (**ppAttributes == COLON)
                {
                pToken->pChars = *ppAttributes;
                pToken->dwSize = 1;
                *ppAttributes += 1;
                *pdwAttributeSize -= 1;
                }
            break;

        case ATTRIBUTE_VALUE:
            pToken->pChars = *ppAttributes;
            pToken->dwSize = 0;
            while ((**ppAttributes != CR) && (*pdwAttributeSize > 0))
                {
                (*ppAttributes)++;
                (*pdwAttributeSize)--;
                (pToken->dwSize)++;
                }
            break;

        case ATTRIBUTE_CRLF:
            pToken->pChars = *ppAttributes;
            pToken->dwSize = 2;
            *ppAttributes += 2;
            *pdwAttributeSize -= 2;
            if ((pToken->pChars[0] != CR)||(pToken->pChars[1] != LF))
                {
                FreeMemory(pToken);
                pToken = 0;
                }
            break;

        default:
            FreeMemory(pToken);
            pToken = 0;
            break;
        }

    return pToken;
    }

//--------------------------------------------------------------------
// IsAttributeName()
//
//--------------------------------------------------------------------
BOOL IsAttributeName( ATTRIBUTE_TOKEN *pToken,
                      int        *piAttribute )
    {
    BOOL fIsName = FALSE;

    if ((pToken->pChars[0] == 'f')&&(pToken->pChars[1] == 'r'))
        {
        fIsName = TRUE;
        *piAttribute = ATTR_PDU_SIZE;
        }
    else
    if ((pToken->pChars[0] == 'i')&&(pToken->pChars[1] == 'd'))
        {
        fIsName = TRUE;
        *piAttribute = ATTR_PRODUCT_ID;
        }
    else
    if ((pToken->pChars[0] == 'n')&&(pToken->pChars[1] == 'm'))
        {
        fIsName = TRUE;
        *piAttribute = ATTR_USER_NAME;
        }
    else
    if ((pToken->pChars[0] == 'p')&&(pToken->pChars[1] == 'w'))
        {
        fIsName = TRUE;
        *piAttribute = ATTR_PASSWORD;
        }

    return fIsName;
    }

//--------------------------------------------------------------------
// NewTokenString()
//
//--------------------------------------------------------------------
UCHAR *NewTokenString( IN  ATTRIBUTE_TOKEN *pToken,
                       OUT DWORD           *pdwStatus )
    {
    UCHAR *pszNewStr = (UCHAR*)AllocateMemory(1+pToken->dwSize);

    if (!pszNewStr)
        {
        *pdwStatus = ERROR_IRTRANP_OUT_OF_MEMORY;
        return 0;
        }

    memcpy(pszNewStr,pToken->pChars,pToken->dwSize);
    pszNewStr[pToken->dwSize] = 0;

    return pszNewStr;
    }
                      
//--------------------------------------------------------------------
// CSCEP_CONNECTION::CSCEP_CONNECTION()
//
//--------------------------------------------------------------------
CSCEP_CONNECTION::CSCEP_CONNECTION()
    {
    m_dwConnectionState = STATE_CLOSED;
    m_dwPduSendSize = PDU_SIZE_1;    // default is 512 bytes.
    m_dwPduReceiveSize = PDU_SIZE_4;
    m_CFlag = 0;
    m_pPrimaryMachineId = 0;
    m_pSecondaryMachineId = 0;
    m_DestPid = DEFAULT_PID;
    m_SrcPid = DEFAULT_PID;
    m_pszUserName = 0;
    m_pszPassword = 0;

    m_pAssembleBuffer = 0;
    m_dwAssembleBufferSize = 0;
    m_dwMaxAssembleBufferSize = 0;
    m_fDidByteSwap = FALSE;

    m_Fragmented = FALSE;
    m_DFlag = 0;
    m_dwSequenceNo = 0;
    m_dwRestNo = 0;
    m_dwCommandId = 0;
    m_pCommandHeader = 0;

    m_pszFileName = 0;
    m_pszLongFileName = 0;
    m_pwszFileName = 0;

    m_CreateTime.dwLowDateTime = 0;   // Picture create date/time.
    m_CreateTime.dwHighDateTime = 0;
    }

//--------------------------------------------------------------------
// CSCEP_CONNECTION::~CSCEP_CONNECTION()
//
//--------------------------------------------------------------------
CSCEP_CONNECTION::~CSCEP_CONNECTION()
    {
    if (m_pPrimaryMachineId)
        {
        FreeMemory(m_pPrimaryMachineId);
        }

    if (m_pSecondaryMachineId)
        {
        FreeMemory(m_pSecondaryMachineId);
        }

    if (m_pszUserName)
        {
        FreeMemory(m_pszUserName);
        }

    if (m_pszPassword)
        {
        FreeMemory(m_pszPassword);
        }

    if (m_pAssembleBuffer)
        {
        FreeMemory(m_pAssembleBuffer);
        }

    if (m_pCommandHeader)
        {
        FreeMemory(m_pCommandHeader);
        }

    if (m_pszFileName)
        {
        FreeMemory(m_pszFileName);
        }

    if (m_pszLongFileName)
        {
        FreeMemory(m_pszLongFileName);
        }

    if (m_pwszFileName)
        {
        FreeMemory(m_pwszFileName);
        }
    }

//------------------------------------------------------------------------
//  CSCEP_CONNECTION::operator new()
//
//------------------------------------------------------------------------
void *CSCEP_CONNECTION::operator new( IN size_t Size )
    {
    void *pObj = AllocateMemory(Size);

    #ifdef DBG_MEM
    if (pObj)
        {
        InterlockedIncrement(&g_lCScepConnectionCount);
        }
    #endif

    return pObj;
    }

//------------------------------------------------------------------------
//  CSCEP_CONNECTION::operator delete()
//
//------------------------------------------------------------------------
void CSCEP_CONNECTION::operator delete( IN void *pObj,
                                        IN size_t Size )
    {
    if (pObj)
        {
        DWORD dwStatus = FreeMemory(pObj);

        #ifdef DBG_MEM
        if (dwStatus)
            {
            DbgPrint("IrXfer: IrTran-P: CSCEP_CONNECTION::delete: FreeMemory() Failed: %d\n"dwStatus);
            }

        InterlockedDecrement(&g_lCScepConnectionCount);

        if (g_lCScepConnectionCount < 0)
            {
            DbgPrint("IrXfer: IrTran-P: CSCEP_CONNECTION::delete: Count: %d\n",
                     g_lCScepConnectionCount);
            }
        #endif
        }
    }


//--------------------------------------------------------------------
// CSCEP_CONNECTION::AssemblePdu()
//
// Take in bits of data as its read in. When a complete SCEP PDU has
// been read and assembled return it.
//
//   pInputData      - This is the data that just came in.
//
//   dwInputDataSize - Size in bytes of pInputData.
//
//   ppPdu           - Returns a complete SCEP PDU when this function
//                     returns NO_ERROR, otherwise set to 0.
//
//   pdwPduSize      - Size of the returned PDU.
//
// Return values:
//
//   NO_ERROR         - A new SCEP PDU is complete and ready.
//   ERROR_CONTINUE   - Data read so far is Ok, still waiting for more.
//   ERROR_SCEP_INVALID_PROTOCOL
//   ERROR_IRTRANP_OUT_OF_MEMORY
//
//--------------------------------------------------------------------
DWORD CSCEP_CONNECTION::AssemblePdu( IN  void         *pInputData,
                                     IN  DWORD         dwInputDataSize,
                                     OUT SCEP_HEADER **ppPdu,
                                     OUT DWORD        *pdwPduSize )
    {
    DWORD  dwStatus = ERROR_CONTINUE;
    UCHAR *pEnd;

    ASSERT(dwInputDataSize <= MAX_PDU_SIZE);

    *ppPdu = 0;
    *pdwPduSize = 0;

    if (dwInputDataSize > 0)
        {
        if (!m_pAssembleBuffer)
           {
           m_dwMaxAssembleBufferSize = 2*MAX_PDU_SIZE;
           m_pAssembleBuffer 
                   = (UCHAR*)AllocateMemory(m_dwMaxAssembleBufferSize);
           if (!m_pAssembleBuffer)
               {
               return ERROR_IRTRANP_OUT_OF_MEMORY;
               }

            memcpy(m_pAssembleBuffer,pInputData,dwInputDataSize);
            m_dwAssembleBufferSize = dwInputDataSize;
            #ifdef DBG_ASSEMBLE
            DbgPrint("Assemble: Start: %d\n",m_dwAssembleBufferSize);
            #endif
            }
        else
            {
            if (m_dwAssembleBufferSize+dwInputDataSize >= m_dwMaxAssembleBufferSize)
                {
                #ifdef DBG_ERROR
                DbgPrint("CSCEP_CONNECTION::AssemblePdu(): Buffer Overrun!\n");
                #endif
                }
            pEnd = &(m_pAssembleBuffer[m_dwAssembleBufferSize]);
            memcpy(pEnd,pInputData,dwInputDataSize);
            m_dwAssembleBufferSize += dwInputDataSize;
            #ifdef DBG_ASSEMBLE
            DbgPrint("Assemble: Add: %d NewSize: %d\n",
                     dwInputDataSize,m_dwAssembleBufferSize);
            #endif
            }
        }

    // Check to see if enough data has come in for a complete PDU.
    dwStatus = CheckPdu(ppPdu,pdwPduSize);

    return dwStatus;
    }

//--------------------------------------------------------------------
// CSCEP_CONNECTION::CheckPdu()
//
// Run through the "current" PDU and see if its complete. If its
// not yet complete, return ERROR_CONTINUE. If it is complete then
// return NO_ERROR.
//
// Return values:
//
//   NO_ERROR         - The current SCEP PDU is complete and ready.
//   ERROR_CONTINUE   - Data read so far is Ok, still waiting for more.
//   ERROR_SCEP_INVALID_PROTOCOL
//   ERROR_IRTRANP_OUT_OF_MEMORY
//
//--------------------------------------------------------------------
DWORD CSCEP_CONNECTION::CheckPdu( OUT SCEP_HEADER **ppPdu,
                                  OUT DWORD        *pdwPduSize )
    {
    DWORD  dwStatus;
    DWORD  dwSize;
    SCEP_NEGOTIATION *pInfNegotiation;

    if (m_dwAssembleBufferSize < 2)
        {
        return ERROR_CONTINUE;
        }

    switch ( ((SCEP_HEADER*)m_pAssembleBuffer)->MsgType )
        {
        case MSG_TYPE_CONNECT_REQ:
            dwStatus = CheckConnectPdu(ppPdu,pdwPduSize);
            break;

        case MSG_TYPE_CONNECT_RESP:
            dwStatus = CheckConnectRespPdu(ppPdu,pdwPduSize);
            break;

        case MSG_TYPE_DATA:
            dwStatus = CheckDataPdu(ppPdu,pdwPduSize);
            break;

        case MSG_TYPE_DISCONNECT:
            dwStatus = CheckDisconnectPdu(ppPdu,pdwPduSize);
            break;

        default:
            #ifdef DBG_ERROR
            DbgPrint("CheckPdu(): Invalid Msgtype: %d\n",
                     ((SCEP_HEADER*)m_pAssembleBuffer)->MsgType );
            #endif
            dwStatus = ERROR_SCEP_INVALID_PROTOCOL;
            break;
        }
    
    return dwStatus;
    }

//--------------------------------------------------------------------
// CSCEP_CONNECTION::CheckConnectPdu()
//
//--------------------------------------------------------------------
DWORD CSCEP_CONNECTION::CheckConnectPdu( OUT SCEP_HEADER **ppPdu,
                                         OUT DWORD        *pdwPduSize )
    {
    DWORD  dwStatus;
    DWORD  dwSize;
    SCEP_VERSION     *pInfVersion;
    SCEP_NEGOTIATION *pInfNegotiation;
    SCEP_EXTEND      *pInfExtend;


    ASSERT( ((SCEP_HEADER*)m_pAssembleBuffer)->MsgType 
            == MSG_TYPE_CONNECT_REQ);

    if (m_dwAssembleBufferSize < MIN_PDU_SIZE_CONNECT)
        {
        return ERROR_CONTINUE;
        }

    if (m_dwAssembleBufferSize > MAX_PDU_SIZE_CONNECT)
        {
        return ERROR_SCEP_INVALID_PROTOCOL;
        }

    pInfVersion = (SCEP_VERSION*)(((SCEP_HEADER*)m_pAssembleBuffer)->Rest);

    pInfNegotiation = (SCEP_NEGOTIATION*)( sizeof(SCEP_VERSION)
                                           + (char*)pInfVersion );

    pInfExtend = (SCEP_EXTEND*)( FIELD_OFFSET(SCEP_NEGOTIATION,InfVersion)
                                 + pInfNegotiation->Length
                                 + (char*)pInfNegotiation );

    // Check to see if we have a complete connect PDU size-wise:
    dwSize = 10 + pInfNegotiation->Length;
    if (m_dwAssembleBufferSize == dwSize)
        {
        // Have a complete PDU.
        dwStatus = NO_ERROR;
        }
    else if (m_dwAssembleBufferSize < dwSize)
        {
        // Need to wait for more data to arrive
        dwStatus = ERROR_CONTINUE;
        }
    else
        {
        // Too much data...
        dwStatus = ERROR_SCEP_INVALID_PROTOCOL;
        }

    if (dwStatus == NO_ERROR)
        {
        // Check to make sure the contents of the PDU "look" Ok:

        if ( (pInfVersion->InfType != INF_TYPE_VERSION)
           || (pInfVersion->Version != PROTOCOL_VERSION) )
            {
            dwStatus = ERROR_SCEP_INVALID_PROTOCOL;
            }

        if ( (pInfNegotiation->InfType != INF_TYPE_NEGOTIATION)
           || (pInfNegotiation->InfVersion < INF_VERSION) )
            {
            dwStatus = ERROR_SCEP_INVALID_PROTOCOL;
            }

        if ( (pInfExtend->InfType != INF_TYPE_EXTEND)
           || (pInfExtend->Length != (sizeof(pInfExtend->Parameter1)
                                      +sizeof(pInfExtend->Parameter2)))
           || (pInfExtend->Parameter1 != 0)
           || (pInfExtend->Parameter2 != 0) )
            {
            dwStatus = ERROR_SCEP_INVALID_PROTOCOL;
            }
        }

    if (dwStatus == NO_ERROR)
        {
        *ppPdu = NewPdu();
        if (!*ppPdu)
            {
            #ifdef DBG_ERROR
            DbgPrint("CSCEP_CONNECTION::CheckConnectPdu(): Out of memory.\n");
            #endif
            dwStatus = ERROR_IRTRANP_OUT_OF_MEMORY;
            }
        else
            {
            *pdwPduSize = m_dwAssembleBufferSize;
            memcpy(*ppPdu,m_pAssembleBuffer,m_dwAssembleBufferSize);
            m_dwAssembleBufferSize = 0;
            #ifdef DBG_ASSEMBLE
            DbgPrint("Assemble: PDU: %d Reset: %d\n",
                     *pdwPduSize, m_dwAssembleBufferSize );
            #endif
            }
        }

    return dwStatus;
    }

//--------------------------------------------------------------------
//  CSCEP_CONNECTION::CheckConnectRespPdu()                     CLIENT
//
//  A connect response from the IrTran-P server is either a ACK or
//  NACK PDU. If we get here then it's an ACK. We'll make sure the
//  entire PDU is here and that it is formatted correctly. There is
//  a specific message type for ACK PDUs, the NACK is just a special
//  case of MSG_TYPE_DATA and is handled elsewere (CheckDataPdu()).
//
//--------------------------------------------------------------------
DWORD CSCEP_CONNECTION::CheckConnectRespPdu( OUT SCEP_HEADER **ppPdu,
                                             OUT DWORD        *pdwPduSize )
    {
    DWORD  dwStatus;
    DWORD  dwSize;
    SCEP_HEADER      *pHeader;
    SCEP_NEGOTIATION *pInfNegotiation;


    ASSERT( ((SCEP_HEADER*)m_pAssembleBuffer)->MsgType 
            == MSG_TYPE_CONNECT_RESP);

    if (m_dwAssembleBufferSize < MIN_PDU_SIZE_CONNECT_RESP)
        {
        return ERROR_CONTINUE;
        }

    if (m_dwAssembleBufferSize > MAX_PDU_SIZE_CONNECT_RESP)
        {
        return ERROR_SCEP_INVALID_PROTOCOL;
        }

    pHeader = (SCEP_HEADER*)m_pAssembleBuffer;

    pInfNegotiation = (SCEP_NEGOTIATION*)(pHeader->Rest);

    // Check to see if we have a complete connect PDU size-wise:
    dwSize = sizeof(SCEP_HEADER)
             + FIELD_OFFSET(SCEP_NEGOTIATION,InfVersion)
             + pInfNegotiation->Length;

    if (m_dwAssembleBufferSize == dwSize)
        {
        // Have a complete PDU.
        dwStatus = NO_ERROR;
        }
    else if (m_dwAssembleBufferSize < dwSize)
        {
        // Need to wait for more data to arrive
        dwStatus = ERROR_CONTINUE;
        }
    else
        {
        // Too much data...
        dwStatus = ERROR_SCEP_INVALID_PROTOCOL;
        }

    if (dwStatus == NO_ERROR)
        {
        // Check to make sure the contents of the PDU "look" Ok:

        if ( (pInfNegotiation->InfType != INF_TYPE_NEGOTIATION)
           || (pInfNegotiation->InfVersion < INF_VERSION) )
            {
            dwStatus = ERROR_SCEP_INVALID_PROTOCOL;
            }
        }

    if (dwStatus == NO_ERROR)
        {
        *ppPdu = NewPdu();
        if (!*ppPdu)
            {
            #ifdef DBG_ERROR
            DbgPrint("CSCEP_CONNECTION::CheckConnectRespPdu(): Out of memory.\n");
            #endif
            dwStatus = ERROR_IRTRANP_OUT_OF_MEMORY;
            }
        else
            {
            *pdwPduSize = m_dwAssembleBufferSize;
            memcpy(*ppPdu,m_pAssembleBuffer,m_dwAssembleBufferSize);
            m_dwAssembleBufferSize = 0;
            #ifdef DBG_ASSEMBLE
            DbgPrint("Assemble: PDU: %d Reset: %d\n",
                     *pdwPduSize, m_dwAssembleBufferSize );
            #endif
            }
        }

    return dwStatus;
    }

//--------------------------------------------------------------------
// CSCEP_CONNECTION::CheckDisconnectPdu()
//
//--------------------------------------------------------------------
DWORD CSCEP_CONNECTION::CheckDisconnectPdu( OUT SCEP_HEADER **ppPdu,
                                            OUT DWORD        *pdwPduSize )
    {
    DWORD  dwStatus = NO_ERROR;
    DWORD  dwSize;
    SCEP_DISCONNECT  *pDisconnect;


    ASSERT( ((SCEP_HEADER*)m_pAssembleBuffer)->MsgType 
            == MSG_TYPE_DISCONNECT);

    if (m_dwAssembleBufferSize < MIN_PDU_SIZE_DISCONNECT)
        {
        return ERROR_CONTINUE;
        }

    pDisconnect = (SCEP_DISCONNECT*)(((SCEP_HEADER*)m_pAssembleBuffer)->Rest);

    // Check to make sure the contents of the PDU "look" Ok:

    if (pDisconnect->InfType != INF_TYPE_REASON)
        {
        dwStatus = ERROR_SCEP_INVALID_PROTOCOL;
        }

    if (pDisconnect->Length1 != sizeof(USHORT))
        {
        dwStatus = ERROR_SCEP_INVALID_PROTOCOL;
        }

    if (dwStatus == NO_ERROR)
        {
        *ppPdu = NewPdu();
        if (!*ppPdu)
            {
            #ifdef DBG_ERROR
            DbgPrint("CSCEP_CONNECTION::CheckDisonnectPdu(): Out of memory.\n");
            #endif
            dwStatus = ERROR_IRTRANP_OUT_OF_MEMORY;
            }
        else
            {
            *pdwPduSize = sizeof(SCEP_HEADER) + 2 + pDisconnect->Length1;
            memcpy(*ppPdu,m_pAssembleBuffer,*pdwPduSize);
            m_dwAssembleBufferSize -= *pdwPduSize;
            #ifdef DBG_ASSEMBLE
            DbgPrint("Assemble: PDU: %d Reset: %d\n",
                     *pdwPduSize, m_dwAssembleBufferSize );
            #endif
            }
        }

    return dwStatus;
    }

//--------------------------------------------------------------------
// CSCEP_CONNECTION::CheckDataPdu()
//
// The goal here is to check to see if we have a complete formatted
// PDU, if yes the return NO_ERROR, if the PDU looks ok so far, but
// isn't complete (we need to read more), then return ERROR_CONTINUE.
//
// Also if this is a little-endian machine, byteswap the header
// fields accordingly.
//--------------------------------------------------------------------
DWORD CSCEP_CONNECTION::CheckDataPdu( OUT SCEP_HEADER **ppPdu,
                                      OUT DWORD        *pdwPduSize )
    {
    DWORD  dwStatus;
    DWORD  dwExpectedPduSize;
    UCHAR *pEnd;
    SCEP_REQ_HEADER_SHORT *pReqHeaderShort;
    SCEP_REQ_HEADER_LONG  *pReqHeaderLong;

    ASSERT( ((SCEP_HEADER*)m_pAssembleBuffer)->MsgType == MSG_TYPE_DATA);

    if (m_dwAssembleBufferSize < MIN_PDU_SIZE_DATA)
        {
        return ERROR_CONTINUE;
        }

    // Get the length out of the PDU and see if we have a
    // complete PDU:

    pReqHeaderShort = (SCEP_REQ_HEADER_SHORT*)
                             (((SCEP_HEADER*)m_pAssembleBuffer)->Rest);
    if (pReqHeaderShort->Length1 == USE_LENGTH2)
        {
        // We have a long PDU:

        pReqHeaderLong = (SCEP_REQ_HEADER_LONG*)(pReqHeaderShort);

        #ifdef LITTLE_ENDIAN
        if (!m_fDidByteSwap)
            {
            ByteSwapReqHeaderLong(pReqHeaderLong);
            m_fDidByteSwap = TRUE;
            }
        #endif

        dwExpectedPduSize = sizeof(SCEP_HEADER)
                            + FIELD_OFFSET(SCEP_REQ_HEADER_LONG,InfVersion)
                            + pReqHeaderLong->Length2;
        }
    else
        {
        // We have a short PDU:

        #ifdef LITTLE_ENDIAN
        if (!m_fDidByteSwap)
            {
            ByteSwapReqHeaderShort(pReqHeaderShort);
            m_fDidByteSwap = TRUE;
            }
        #endif

        dwExpectedPduSize = sizeof(SCEP_HEADER)
                            + FIELD_OFFSET(SCEP_REQ_HEADER_SHORT,InfVersion)
                            + pReqHeaderShort->Length1;
        }

    // Ok, see if we have a complete PDU:
    if (m_dwAssembleBufferSize == dwExpectedPduSize)
        {
        *ppPdu = NewPdu();
        if (!*ppPdu)
            {
            #ifdef DBG_ERROR
            DbgPrint("CSCEP_CONNECTION::CheckDataPdu(): Out of memory.\n");
            #endif
            dwStatus = ERROR_IRTRANP_OUT_OF_MEMORY;
            }
        else
            {
            *pdwPduSize = dwExpectedPduSize;
            memcpy(*ppPdu,m_pAssembleBuffer,dwExpectedPduSize);
            m_dwAssembleBufferSize = 0;
            m_fDidByteSwap = FALSE;
            #ifdef DBG_ASSEMBLE
            DbgPrint("Assemble: PDU: %d Reset: %d\n",
                     *pdwPduSize, m_dwAssembleBufferSize );
            #endif
            dwStatus = NO_ERROR;
            }
        }
    else if (m_dwAssembleBufferSize > dwExpectedPduSize)
        {
        *ppPdu = NewPdu();
        if (!*ppPdu)
            {
            #ifdef DBG_ERROR
            DbgPrint("CSCEP_CONNECTION::CheckDataPdu(): Out of memory.\n");
            #endif
            dwStatus = ERROR_IRTRANP_OUT_OF_MEMORY;
            }
        else
            {
            *pdwPduSize = dwExpectedPduSize;
            memcpy(*ppPdu,m_pAssembleBuffer,dwExpectedPduSize);
            pEnd = dwExpectedPduSize + (UCHAR*)m_pAssembleBuffer;
            m_dwAssembleBufferSize -= dwExpectedPduSize;
            m_fDidByteSwap = FALSE;
            memcpy(m_pAssembleBuffer,pEnd,m_dwAssembleBufferSize);
            #ifdef DBG_ASSEMBLE
            DbgPrint("Assemble: PDU: %d Reset: %d\n",
                     *pdwPduSize, m_dwAssembleBufferSize );
            #endif
            dwStatus = NO_ERROR;
            }
        }
    else
        {
        dwStatus = ERROR_CONTINUE;
        }

    return dwStatus;
    }

//--------------------------------------------------------------------
// CSCEP_CONNECTION::ParseConnectPdu()
//
// AssemblePDU() already did basic integrity checking of the PDU
// (via CheckConnectPdu()), so at this point we'll assume everything
// is Ok.
//
// NOTE: The Connect PDU is limited to 256 bytes in total length,
// so it will never be fragmented.
//--------------------------------------------------------------------
DWORD CSCEP_CONNECTION::ParseConnectPdu( IN SCEP_HEADER *pPdu,
                                         IN DWORD        dwInputDataSize )
    {
    DWORD  dwStatus;
    DWORD  dwLength;

    if (dwInputDataSize > MAX_PDU_SIZE_CONNECT)
        {
        return ERROR_SCEP_INVALID_PROTOCOL;
        }

    SCEP_VERSION     *pInfVersion;
    SCEP_NEGOTIATION *pInfNegotiation;
    SCEP_EXTEND      *pInfExtend;

    pInfVersion = (SCEP_VERSION*)pPdu->Rest;

    pInfNegotiation = (SCEP_NEGOTIATION*)( sizeof(SCEP_VERSION)
                                           + (char*)pInfVersion );

    pInfExtend = (SCEP_EXTEND*)( FIELD_OFFSET(SCEP_NEGOTIATION,InfVersion)
                                 + pInfNegotiation->Length
                                 + (char*)pInfNegotiation );

    // 
    m_CFlag = pInfNegotiation->CFlag;
    
    m_pSecondaryMachineId = (UCHAR*)AllocateMemory(MACHINE_ID_SIZE);
    if (!m_pSecondaryMachineId)
        {
        return ERROR_IRTRANP_OUT_OF_MEMORY;
        }

    memcpy( m_pSecondaryMachineId,
            pInfNegotiation->SecondaryMachineId,
            MACHINE_ID_SIZE );
    
    m_pPrimaryMachineId = (UCHAR*)AllocateMemory(MACHINE_ID_SIZE);
    if (!m_pPrimaryMachineId)
        {
        FreeMemory(m_pSecondaryMachineId);
        return ERROR_IRTRANP_OUT_OF_MEMORY;
        }

    memcpy( m_pPrimaryMachineId,
            pInfNegotiation->PrimaryMachineId,
            MACHINE_ID_SIZE );

    // NOTE: The size of the negotiaion "text" is 18 bytes less than
    // the length in the SCEP_NEGOTIATION record:
    dwLength = pInfNegotiation->Length
             - ( sizeof(pInfNegotiation->InfVersion)
               + sizeof(pInfNegotiation->CFlag)
               + sizeof(pInfNegotiation->SecondaryMachineId)
               + sizeof(pInfNegotiation->PrimaryMachineId));

    dwStatus = ParseNegotiation( pInfNegotiation->Negotiation, dwLength );

    return dwStatus;
    }

//--------------------------------------------------------------------
// CSCEP_CONNECTION::ParseConnectRespPdu()
//
// AssemblePDU() already did basic integrity checking of the PDU
// (via CheckConnectRespPdu()), so at this point we'll assume 
// everything is Ok.
//
// NOTE: The Connect Response PDU is limited to 255 bytes in total
// length, so it will never be fragmented.
//--------------------------------------------------------------------
DWORD CSCEP_CONNECTION::ParseConnectRespPdu( IN SCEP_HEADER *pPdu,
                                             IN DWORD        dwPduSize )
    {
    DWORD  dwStatus;
    DWORD  dwLength;
    SCEP_NEGOTIATION *pInfNegotiation;


    if (dwPduSize > MAX_PDU_SIZE_CONNECT)
        {
        return ERROR_SCEP_INVALID_PROTOCOL;
        }

    pInfNegotiation = (SCEP_NEGOTIATION*)( sizeof(SCEP_HEADER)
                                           + (char*)pPdu );

    // This is the CFlag sent by the other machine.
    m_CFlag = pInfNegotiation->CFlag;

    m_pSecondaryMachineId = (UCHAR*)AllocateMemory(MACHINE_ID_SIZE);
    if (!m_pSecondaryMachineId)
        {
        return ERROR_IRTRANP_OUT_OF_MEMORY;
        }

    memcpy( m_pSecondaryMachineId,
            pInfNegotiation->SecondaryMachineId,
            MACHINE_ID_SIZE );
    
    m_pPrimaryMachineId = (UCHAR*)AllocateMemory(MACHINE_ID_SIZE);
    if (!m_pPrimaryMachineId)
        {
        FreeMemory(m_pSecondaryMachineId);
        return ERROR_IRTRANP_OUT_OF_MEMORY;
        }

    memcpy( m_pPrimaryMachineId,
            pInfNegotiation->PrimaryMachineId,
            MACHINE_ID_SIZE );

    // NOTE: The size of the negotiaion "text" is 18 bytes less than
    // the length in the SCEP_NEGOTIATION record:
    dwLength = pInfNegotiation->Length
             - ( sizeof(pInfNegotiation->InfVersion)
               + sizeof(pInfNegotiation->CFlag)
               + sizeof(pInfNegotiation->SecondaryMachineId)
               + sizeof(pInfNegotiation->PrimaryMachineId));

    dwStatus = ParseNegotiation( pInfNegotiation->Negotiation, dwLength );

    return dwStatus;
    }

//--------------------------------------------------------------------
// CSCEP_CONNECTION::ParseNegotiation()
//
//--------------------------------------------------------------------
DWORD CSCEP_CONNECTION::ParseNegotiation( IN UCHAR *pNegotiation,
                                          IN DWORD  dwNegotiationSize )
    {
    DWORD  dwStatus = NO_ERROR;
    UCHAR *pNext = pNegotiation;
    DWORD  dwSize = dwNegotiationSize;

    if (dwNegotiationSize <= 1)
        {
        return NO_ERROR;
        }

    if (*(pNext++) < NEGOTIATION_VERSION)
        {
        return ERROR_SCEP_INVALID_PROTOCOL;
        }

    dwSize--;

    while (pNext=ParseAttribute(pNext,
                                &dwSize,
                                &dwStatus))
       {
       if (dwStatus != NO_ERROR)
           {
           break;
           }
       }

    return dwStatus;
    }

//--------------------------------------------------------------------
// CSCEP_CONNECTION::ParseAttribute()
//
// Attributes are of the form:
//  
// Attr      <- AttrName Colon AttrValue CrLf
//
// AttrName  <- Two byte attribute name.
//
// Colon     <- ':'
//
// AttrValue <- Character string (bytes > 0x1f and < 0x8f).
//
// CrLf      <- 0x0d 0x0a
//
//--------------------------------------------------------------------
UCHAR *CSCEP_CONNECTION::ParseAttribute( IN  UCHAR *pAttributes,
                                         IN  DWORD *pdwAttributeSize,
                                         OUT DWORD *pdwStatus )
    {
    int  iAttribute;
    int  iPduSize;
    ATTRIBUTE_TOKEN  *pToken1 = 0;
    ATTRIBUTE_TOKEN  *pToken2 = 0;
    ATTRIBUTE_TOKEN  *pToken3 = 0;
    ATTRIBUTE_TOKEN  *pToken4 = 0;

    *pdwStatus = NO_ERROR;

    if (  (pToken1=NextToken(ATTRIBUTE_NAME,&pAttributes,pdwAttributeSize))
       && (IsAttributeName(pToken1,&iAttribute))
       && (pToken2=NextToken(ATTRIBUTE_COLON,&pAttributes,pdwAttributeSize))
       && (pToken3=NextToken(ATTRIBUTE_VALUE,&pAttributes,pdwAttributeSize))
       && (pToken4=NextToken(ATTRIBUTE_CRLF,&pAttributes,pdwAttributeSize)) )
        {
        if (iAttribute == ATTR_PDU_SIZE)
            {
            iPduSize = pToken3->pChars[0] - ONE;
            if ((pToken3->dwSize == 1)&&(iPduSize >= 1)&&(iPduSize <= 4))
                {
                m_dwPduSendSize = g_adwPduSizes[iPduSize];
                #ifdef DBG_IO
                DbgPrint("ParseAttribute(): PduSendSize: %d\n",m_dwPduSendSize);
                #endif
                }
            }
        else
        if (iAttribute == ATTR_PRODUCT_ID)
            {
            m_pszProductId = NewTokenString(pToken3,pdwStatus);
            if (!m_pszProductId)
                {
                pAttributes = 0;
                #ifdef DBG_IO
                DbgPrint("ParseAttribute(): Product: %s\n",m_pszProductId);
                #endif
                }
            }
        else
        if (iAttribute == ATTR_USER_NAME)
            {
            m_pszUserName = NewTokenString(pToken3,pdwStatus);
            if (!m_pszUserName)
                {
                pAttributes = 0;
                }
            }
        else
        if (iAttribute == ATTR_PASSWORD)
            {
            m_pszPassword = NewTokenString(pToken3,pdwStatus);
            if (!m_pszPassword)
                {
                pAttributes = 0;
                }
            }
        }
    else
        {
        if (*pdwAttributeSize > 0)
            {
            *pdwStatus = ERROR_SCEP_INVALID_PROTOCOL;
            }
        pAttributes = 0;
        }

    if (pToken1) FreeMemory(pToken1);
    if (pToken2) FreeMemory(pToken2);
    if (pToken3) FreeMemory(pToken3);
    if (pToken4) FreeMemory(pToken4);

    return pAttributes;
    }

//--------------------------------------------------------------------
// CSCEP_CONNECTION::ParseDataPdu()
//
// AssemblePDU() already did basic integrity checking of the PDU
// (via CheckConnectPdu()), so at this point we'll assume everything
// is Ok.
//
// NOTE: The Data PDU is limited to m_dwPduReceiveSize bytes in total
// length, if data is longer then you will get the fragmented versions
// of the Data PDU.
//--------------------------------------------------------------------
DWORD CSCEP_CONNECTION::ParseDataPdu( IN  SCEP_HEADER     *pPdu,
                                      IN  DWORD            dwPduSize,
                                      OUT COMMAND_HEADER **ppCommand,
                                      OUT UCHAR          **ppUserData,
                                      OUT DWORD           *pdwUserDataSize )
    {
    DWORD  dwStatus = NO_ERROR;
    DWORD  dwLengthOffset1;
    DWORD  dwLengthOffset3;

    // There are four cases of Data PDUs, single (unfragmented)
    // "short" and "long" PDUs, and fragmented "short" and
    // "long" PDUs:
    SCEP_REQ_HEADER_SHORT *pReqHeaderShort;
    SCEP_REQ_HEADER_LONG  *pReqHeaderLong;
    SCEP_REQ_HEADER_SHORT_FRAG *pReqHeaderShortFrag;
    SCEP_REQ_HEADER_LONG_FRAG  *pReqHeaderLongFrag;

    *ppCommand = 0;

    // Make sure the packet length makes sense...
    if (dwPduSize > m_dwPduReceiveSize)
        {
        return ERROR_SCEP_INVALID_PROTOCOL;
        }

    pReqHeaderShort = (SCEP_REQ_HEADER_SHORT*)(pPdu->Rest);

    if (pReqHeaderShort->InfType != INF_TYPE_USER_DATA)
        {
        return ERROR_SCEP_INVALID_PROTOCOL;
        }

    //
    // See if we have a short or long PDU:
    //
    if (pReqHeaderShort->Length1 != USE_LENGTH2)
        {
        // This is a short PDU (use Length1).

        m_DFlag = pReqHeaderShort->DFlag;

        if ( (pReqHeaderShort->DFlag == DFLAG_SINGLE_PDU)
           || (pReqHeaderShort->DFlag == DFLAG_CONNECT_REJECT))
            {
            //
            // This is a short unfragmented PDU.
            //

            // Make sure that a command header is present:
            if (pReqHeaderShort->Length1 > 4)
                {
                *ppCommand = (COMMAND_HEADER*)(pReqHeaderShort->CommandHeader);
                m_SrcPid = (*ppCommand)->SrcPid;
                m_DestPid = (*ppCommand)->DestPid;
                m_dwCommandId = (*ppCommand)->CommandId;
                }
            else
                {
                *ppCommand = 0;
                }

            *ppUserData = COMMAND_HEADER_SIZE + pReqHeaderShort->CommandHeader;
            *pdwUserDataSize = pReqHeaderShort->Length3 - COMMAND_HEADER_SIZE;

            m_Fragmented = FALSE;
            m_dwSequenceNo = 0;
            m_dwRestNo = 0;

            // In this case, there are two different lengths
            // in the PDU that must add up to dwPduSize...
            //
            // Note: Not currently testing Length1 for consistency...
            //
            dwLengthOffset3 = sizeof(SCEP_HEADER)
                        + FIELD_OFFSET(SCEP_REQ_HEADER_SHORT,CommandHeader);

            if (dwPduSize != dwLengthOffset3+pReqHeaderShort->Length3)
                {
                dwStatus = ERROR_SCEP_INVALID_PROTOCOL;
                }
            }
        else if (pReqHeaderShort->DFlag == DFLAG_FIRST_FRAGMENT)
            {
            //
            // This is a short fragmented PDU, and is the first 
            // fragment, so it will contain a COMMAND_HEADER.
            //
            // In practice, this should probably never show up...

            pReqHeaderShortFrag = (SCEP_REQ_HEADER_SHORT_FRAG*)pReqHeaderShort;

            // The command header is present only on the first fragment
            // of a multi-fragment PDU:
            if (pReqHeaderShortFrag->SequenceNo == 0)
                {
                *ppCommand 
                    = (COMMAND_HEADER*)(pReqHeaderShortFrag->CommandHeader);

                m_SrcPid = (*ppCommand)->SrcPid;
                m_DestPid = (*ppCommand)->DestPid;
                m_dwCommandId = (*ppCommand)->CommandId;
                }
            else
                {
                *ppCommand = 0;
                }

            *ppUserData 
                = COMMAND_HEADER_SIZE + pReqHeaderShortFrag->CommandHeader;
            *pdwUserDataSize 
                = pReqHeaderShortFrag->Length3 - COMMAND_HEADER_SIZE;

            m_Fragmented = TRUE;
            m_dwSequenceNo = pReqHeaderShortFrag->SequenceNo;
            m_dwRestNo = pReqHeaderShortFrag->RestNo;

            // Check the two length fields for consistency:
            dwLengthOffset1 = sizeof(SCEP_HEADER)
                        + FIELD_OFFSET(SCEP_REQ_HEADER_SHORT_FRAG,InfVersion);
            dwLengthOffset3 = sizeof(SCEP_HEADER)
                        + FIELD_OFFSET(SCEP_REQ_HEADER_SHORT_FRAG,SequenceNo);

            if ( (dwPduSize != dwLengthOffset1+pReqHeaderShortFrag->Length1)
               || (dwPduSize != dwLengthOffset3+pReqHeaderShortFrag->Length3) )
                {
                dwStatus = ERROR_SCEP_INVALID_PROTOCOL;
                }
            }
        else if (  (pReqHeaderShort->DFlag == DFLAG_FRAGMENT)
                || (pReqHeaderShort->DFlag == DFLAG_LAST_FRAGMENT))
            {
            //
            // This is a short fragmented PDU.
            //
            // The 2nd through last fragmented PDUs don't contain a
            // COMMAND_HEADER, just data after Length3.
            pReqHeaderShortFrag = (SCEP_REQ_HEADER_SHORT_FRAG*)pReqHeaderShort;

            // The command header is present only on the first fragment
            // of a multi-fragment PDU:
            if (pReqHeaderShortFrag->SequenceNo == 0)
                {
                *ppCommand 
                    = (COMMAND_HEADER*)(pReqHeaderShortFrag->CommandHeader);

                m_SrcPid = (*ppCommand)->SrcPid;
                m_DestPid = (*ppCommand)->DestPid;
                m_dwCommandId = (*ppCommand)->CommandId;
                }
            else
                {
                *ppCommand = 0;
                }

            *ppUserData = pReqHeaderShortFrag->CommandHeader;
            *pdwUserDataSize = pReqHeaderShortFrag->Length3;

            m_Fragmented = TRUE;
            m_dwSequenceNo = pReqHeaderShortFrag->SequenceNo;
            m_dwRestNo = pReqHeaderShortFrag->RestNo;

            // Check the two length fields for consistency:
            dwLengthOffset1 = sizeof(SCEP_HEADER)
                        + FIELD_OFFSET(SCEP_REQ_HEADER_SHORT_FRAG,InfVersion);
            dwLengthOffset3 = sizeof(SCEP_HEADER)
                        + FIELD_OFFSET(SCEP_REQ_HEADER_SHORT_FRAG,SequenceNo);

            if ( (dwPduSize != dwLengthOffset1+pReqHeaderShortFrag->Length1)
               || (dwPduSize != dwLengthOffset3+pReqHeaderShortFrag->Length3) )
                {
                dwStatus = ERROR_SCEP_INVALID_PROTOCOL;
                }
            }
        else
            {
            // Undefined DFlag, we've got a problem...
            dwStatus = ERROR_SCEP_INVALID_PROTOCOL;
            }
        }
    else
        {
        // We have a long PDU.

        pReqHeaderLong = (SCEP_REQ_HEADER_LONG*)pReqHeaderShort;

        m_DFlag = pReqHeaderLong->DFlag;

        if ( (pReqHeaderLong->DFlag == DFLAG_SINGLE_PDU)
           || (pReqHeaderLong->DFlag == DFLAG_CONNECT_REJECT))
            {
            //
            // This is a long unfragmented PDU.
            //
            *ppCommand = (COMMAND_HEADER*)(pReqHeaderLong->CommandHeader);
            *ppUserData = COMMAND_HEADER_SIZE + pReqHeaderLong->CommandHeader;
            *pdwUserDataSize = pReqHeaderLong->Length3 - COMMAND_HEADER_SIZE;

            m_Fragmented = FALSE;
            m_dwSequenceNo = 0;
            m_dwRestNo = 0;
            m_SrcPid = (*ppCommand)->SrcPid;
            m_DestPid = (*ppCommand)->DestPid;
            m_dwCommandId = (*ppCommand)->CommandId;

            // In this case, there are two different lengths
            // in the PDU that must add up to dwPduSize...
            if ( (dwPduSize != 6UL+pReqHeaderLong->Length2)
               || (dwPduSize != 10UL+pReqHeaderLong->Length3))
                {
                dwStatus = ERROR_SCEP_INVALID_PROTOCOL;
                }
            }
        else if (pReqHeaderLong->DFlag == DFLAG_FIRST_FRAGMENT)
            {
            //
            // This is the first fragment of a long fragmented PDU.
            //
            pReqHeaderLongFrag = (SCEP_REQ_HEADER_LONG_FRAG*)pReqHeaderLong;

            m_pCommandHeader = (COMMAND_HEADER*)AllocateMemory(sizeof(COMMAND_HEADER));
            if (!m_pCommandHeader)
                {
                dwStatus = ERROR_IRTRANP_OUT_OF_MEMORY;
                }
            else
                {
                memcpy(m_pCommandHeader,
                       pReqHeaderLongFrag->CommandHeader,
                       COMMAND_HEADER_SIZE );

                *ppCommand = m_pCommandHeader;
                }

            *ppUserData = COMMAND_HEADER_SIZE + pReqHeaderLongFrag->CommandHeader;
            *pdwUserDataSize = pReqHeaderLongFrag->Length3 - COMMAND_HEADER_SIZE;

            m_Fragmented = TRUE;
            m_dwSequenceNo = pReqHeaderLongFrag->SequenceNo;
            m_dwRestNo = pReqHeaderLongFrag->RestNo;
            if (*ppCommand)
                {
                m_dwCommandId = (*ppCommand)->CommandId;
                }
            else
                {
                m_dwCommandId = 0;
                }

            // Check the two length fields for consistency:
            if ( (dwPduSize != (DWORD)6+pReqHeaderLongFrag->Length2)
               || (dwPduSize != (DWORD)18+pReqHeaderLongFrag->Length3) )
                {
                dwStatus = ERROR_SCEP_INVALID_PROTOCOL;
                }
            }
        else if ( (pReqHeaderLong->DFlag == DFLAG_FRAGMENT)
                  || (pReqHeaderLong->DFlag == DFLAG_LAST_FRAGMENT) )
            {
            //
            // This is the second through last fragment of a long 
            // fragmented PDU.
            //
            // In this case the PDU doesn't contain a command
            // header, just more user data...
            //
            pReqHeaderLongFrag = (SCEP_REQ_HEADER_LONG_FRAG*)pReqHeaderLong;

            *ppCommand = m_pCommandHeader;
            *ppUserData = (UCHAR*)(pReqHeaderLongFrag->CommandHeader);
            *pdwUserDataSize = pReqHeaderLongFrag->Length3;

            m_Fragmented = TRUE;
            m_dwSequenceNo = pReqHeaderLongFrag->SequenceNo;
            m_dwRestNo = pReqHeaderLongFrag->RestNo;
            m_dwCommandId = (*ppCommand)->CommandId;

            // Check the two length fields for consistency:
            if ( (dwPduSize != (DWORD)6+pReqHeaderLongFrag->Length2)
               || (dwPduSize != (DWORD)18+pReqHeaderLongFrag->Length3) )
                {
                dwStatus = ERROR_SCEP_INVALID_PROTOCOL;
                }
            }
        else
            {
            // Undefined DFlag, we've got a problem...
            dwStatus = ERROR_SCEP_INVALID_PROTOCOL;
            }
        }

    return dwStatus;
    }

//--------------------------------------------------------------------
// CSCEP_CONNECTION::ParseDisconnectPdu()
//
// NOTE: In practice, reason codes should always be 2 bytes for
//       SCEP version 1.0.
//--------------------------------------------------------------------
DWORD CSCEP_CONNECTION::ParseDisconnectPdu( IN  SCEP_HEADER *pPdu,
                                            IN  DWORD        dwPduSize )
    {
    DWORD  dwStatus;

    SCEP_DISCONNECT *pDisconnect = (SCEP_DISCONNECT*)(pPdu->Rest);

    if ( (pDisconnect->InfType != INF_TYPE_REASON)
       || (pDisconnect->Length1 != sizeof(USHORT))
       || (pDisconnect->ReasonCode == 0) )
        {
        dwStatus = ERROR_SCEP_UNSPECIFIED_DISCONNECT;
        }
    else if (pDisconnect->ReasonCode == 1)
        {
        dwStatus = ERROR_SCEP_USER_DISCONNECT;
        }
    else if (pDisconnect->ReasonCode == 2)
        {
        dwStatus = ERROR_SCEP_PROVIDER_DISCONNECT;
        }
    else
        {
        dwStatus = ERROR_SCEP_UNSPECIFIED_DISCONNECT;
        }
    
    return dwStatus;
    }

//--------------------------------------------------------------------
// CSCEP_CONNECTION::ParsePdu()
//
//--------------------------------------------------------------------
DWORD CSCEP_CONNECTION::ParsePdu( IN  SCEP_HEADER *pPdu,
                                  IN  DWORD        dwPduSize,
                                  OUT COMMAND_HEADER **ppCommandHeader,
                                  OUT UCHAR          **ppUserData,
                                  OUT DWORD           *pdwUserDataSize )
    {
    DWORD  dwStatus = NO_ERROR;

    *ppCommandHeader = 0;
    *ppUserData = 0;
    *pdwUserDataSize = 0;

    switch (pPdu->MsgType)
        {
        case MSG_TYPE_CONNECT_REQ:
            dwStatus = ParseConnectPdu( pPdu, dwPduSize );
            break;

        case MSG_TYPE_CONNECT_RESP:
            dwStatus = ParseConnectRespPdu( pPdu, dwPduSize );
            break;

        case MSG_TYPE_DATA:
            dwStatus = ParseDataPdu( pPdu, 
                                     dwPduSize, 
                                     ppCommandHeader, 
                                     ppUserData,
                                     pdwUserDataSize );
            break;

        case MSG_TYPE_DISCONNECT:
            dwStatus = ParseDisconnectPdu( pPdu, dwPduSize );
            break;

        }

    return dwStatus;
    }

//--------------------------------------------------------------------
// CSCEP_CONNECTION::BuildConnectPdu()
//
//--------------------------------------------------------------------
DWORD CSCEP_CONNECTION::BuildConnectPdu( OUT SCEP_HEADER **ppPdu,
                                         OUT DWORD        *pdwPduSize )
    {
    DWORD  dwStatus = NO_ERROR;
    DWORD  dwPduSize;
    SCEP_HEADER       *pHeader;
    SCEP_VERSION      *pVersion;
    SCEP_NEGOTIATION  *pNegotiation;
    SCEP_EXTEND       *pExtend;

    *ppPdu = 0;
    *pdwPduSize = 0;

    // Note that the PDU size doesn't include a trailing zero, as you 
    // would think by lookin at "sizeof(CONNECT_PDU_ATTRIBUTES)" below.
    // The extra byte is for the first byte of the Negotiation string
    // (which is the Negotiation version), so the eqn below is +1-1...
    dwPduSize = sizeof(SCEP_HEADER)
                + sizeof(SCEP_VERSION)
                + sizeof(SCEP_NEGOTIATION)
                + sizeof(CONNECT_PDU_ATTRIBUTES)
                + sizeof(SCEP_EXTEND);

    pHeader = NewPdu();  // Defaulting dwPduSize to MAX_PDU_SIZE
    if (!pHeader)
        {
        return ERROR_IRTRANP_OUT_OF_MEMORY;
        }

    memset(pHeader,0,MAX_PDU_SIZE);  // MAX_PDU_SIZE since dwPduSize is defauled above.

    pHeader->Null = 0;
    pHeader->MsgType = MSG_TYPE_CONNECT_REQ;

    pVersion = (SCEP_VERSION*)(pHeader->Rest);
    pVersion->InfType = INF_TYPE_VERSION;
    pVersion->Version = PROTOCOL_VERSION;

    pNegotiation = (SCEP_NEGOTIATION*)((char*)pVersion + sizeof(SCEP_VERSION));
    pNegotiation->InfType = INF_TYPE_NEGOTIATION;
    pNegotiation->Length = 18 + sizeof(CONNECT_PDU_ATTRIBUTES);
    pNegotiation->InfVersion = INF_VERSION;
    pNegotiation->CFlag = CFLAG_ISSUE_OR_EXECUTE;
    // pNegotiation->SecondaryMachineId -- Leave set to zeros...
    // pNegotiation->PrimaryMachineId   -- Leave set to zeros...

    pNegotiation->Negotiation[0] = NEGOTIATION_VERSION;
    memcpy( &(pNegotiation->Negotiation[1]),
            CONNECT_PDU_ATTRIBUTES,
            sizeof(CONNECT_PDU_ATTRIBUTES)-1 );  // No Trailing zero...

    pExtend = (SCEP_EXTEND*)( (char*)pHeader + dwPduSize - sizeof(SCEP_EXTEND));
    pExtend->InfType = INF_TYPE_EXTEND;
    pExtend->Length = 2;
    pExtend->Parameter1 = 0;
    pExtend->Parameter2 = 0;

    *ppPdu = pHeader;
    *pdwPduSize = dwPduSize;

    return dwStatus;
    }

//--------------------------------------------------------------------
// CSCEP_CONNECTION::BuildConnectRespPdu()
//
// This is the response PDU for a connection request from a camera.
//--------------------------------------------------------------------
DWORD CSCEP_CONNECTION::BuildConnectRespPdu( OUT SCEP_HEADER **ppPdu,
                                             OUT DWORD        *pdwPduSize )
    {
    DWORD  dwStatus = NO_ERROR;
    DWORD  dwPduSize;
    SCEP_HEADER       *pHeader;
    SCEP_NEGOTIATION  *pNegotiation;

    *ppPdu = 0;
    *pdwPduSize = 0;

    // Note that the PDU size doesn't include a trailing zero, as you 
    // would think by lookin at "sizeof(RESPONSE_PDU_ATTRIBUTES)" below,
    // the extra byte in for the first byte of the Negotiation string
    // which is the Negotiation version, so the eqn below is +1-1...
    dwPduSize = sizeof(SCEP_HEADER)
                + sizeof(SCEP_NEGOTIATION)
                + sizeof(RESPONSE_PDU_ATTRIBUTES);

    pHeader = NewPdu();
    if (!pHeader)
        {
        return ERROR_IRTRANP_OUT_OF_MEMORY;
        }

    memset(pHeader,0,MAX_PDU_SIZE);  // MAX_PDU_SIZE is default arg in NewPdu().

    pHeader->Null = 0;
    pHeader->MsgType = MSG_TYPE_CONNECT_RESP;
    
    pNegotiation = (SCEP_NEGOTIATION*)(pHeader->Rest);
    pNegotiation->InfType = INF_TYPE_NEGOTIATION;
    pNegotiation->Length = 18 + sizeof(RESPONSE_PDU_ATTRIBUTES);
    pNegotiation->InfVersion = INF_VERSION;
    pNegotiation->CFlag = CFLAG_ISSUE_OR_EXECUTE;

    memcpy( pNegotiation->SecondaryMachineId,
            m_pPrimaryMachineId,
            MACHINE_ID_SIZE );

    memcpy( pNegotiation->PrimaryMachineId,
            m_pSecondaryMachineId,
            MACHINE_ID_SIZE );

    pNegotiation->Negotiation[0] = NEGOTIATION_VERSION;
    memcpy( &(pNegotiation->Negotiation[1]),
            RESPONSE_PDU_ATTRIBUTES,
            sizeof(RESPONSE_PDU_ATTRIBUTES)-1 );  // No Trailing zero...

    *ppPdu = pHeader;
    *pdwPduSize = dwPduSize;

    return dwStatus;
    }

//--------------------------------------------------------------------
// CSCEP_CONNECTION::BuildConnectNackPdu()
//
// This is the response PDU for a connection request from a camera
// when we want to reject the connection request.
//--------------------------------------------------------------------
DWORD CSCEP_CONNECTION::BuildConnectNackPdu( OUT SCEP_HEADER **ppPdu,
                                             OUT DWORD        *pdwPduSize )
    {
    DWORD  dwStatus = NO_ERROR;
    DWORD  dwPduSize;
    SCEP_HEADER           *pHeader;
    SCEP_REQ_HEADER_SHORT *pReqHeader;

    *ppPdu = 0;
    *pdwPduSize = 0;

    // A short PDU, there is now command header, so Length3 is zero...
    dwPduSize = sizeof(SCEP_HEADER)
                + sizeof(SCEP_REQ_HEADER_SHORT)
                - sizeof(COMMAND_HEADER);

    pHeader = NewPdu();
    if (!pHeader)
        {
        return ERROR_IRTRANP_OUT_OF_MEMORY;
        }

    memset(pHeader,0,MAX_PDU_SIZE);

    pHeader->Null = 0;
    pHeader->MsgType = MSG_TYPE_CONNECT_REQ;

    pReqHeader = (SCEP_REQ_HEADER_SHORT*)(pHeader->Rest);
    pReqHeader->InfType = INF_TYPE_USER_DATA;
    pReqHeader->Length1 = sizeof(pReqHeader->InfVersion)
                        + sizeof(pReqHeader->DFlag)
                        + sizeof(pReqHeader->Length3);
    pReqHeader->InfVersion = INF_VERSION;
    pReqHeader->DFlag = DFLAG_CONNECT_REJECT;
    pReqHeader->Length3 = 0;

    *ppPdu = pHeader;
    *pdwPduSize = dwPduSize;

    return dwStatus;
    }

//--------------------------------------------------------------------
// CSCEP_CONNECTION::BuildAbortPdu()
//
//--------------------------------------------------------------------
DWORD CSCEP_CONNECTION::BuildAbortPdu( OUT SCEP_HEADER **ppPdu,
                                       OUT DWORD        *pdwPduSize )
    {
    DWORD  dwStatus = NO_ERROR;
    DWORD  dwPduSize;
    SCEP_HEADER       *pHeader;
    COMMAND_HEADER    *pCommandHeader;
    SCEP_REQ_HEADER_SHORT *pReqHeader;

    *ppPdu = 0;
    *pdwPduSize = 0;

    dwPduSize = sizeof(SCEP_HEADER) + sizeof(SCEP_REQ_HEADER_SHORT);

    pHeader = NewPdu();
    if (!pHeader)
        {
        return ERROR_IRTRANP_OUT_OF_MEMORY;
        }

    memset(pHeader,0,MAX_PDU_SIZE);  // MAX_PDU_SIZE is default arg for NewPdu().

    pHeader->Null = 0;
    pHeader->MsgType = MSG_TYPE_DATA;

    pReqHeader = (SCEP_REQ_HEADER_SHORT*)(pHeader->Rest);
    pReqHeader->InfType = INF_TYPE_USER_DATA;
    pReqHeader->Length1 = 4 + sizeof(COMMAND_HEADER);
    pReqHeader->InfVersion = INF_VERSION;
    pReqHeader->DFlag = DFLAG_SINGLE_PDU;
    pReqHeader->Length3 = sizeof(COMMAND_HEADER);

    #ifdef LITTLE_ENDIAN
    pReqHeader->Length3 = ByteSwapShort(pReqHeader->Length3);
    #endif

    pCommandHeader = (COMMAND_HEADER*)(pReqHeader->CommandHeader);
    pCommandHeader->Marker58h = 0x58;
    pCommandHeader->PduType = PDU_TYPE_ABORT;
    pCommandHeader->Length4 = 22;
    pCommandHeader->DestPid = m_SrcPid;
    pCommandHeader->SrcPid = m_DestPid;
    pCommandHeader->CommandId = (USHORT)m_dwCommandId;

    #ifdef LITTLE_ENDIAN
    ByteSwapCommandHeader(pCommandHeader);
    #endif

    *ppPdu = pHeader;
    *pdwPduSize = dwPduSize;

    return dwStatus;
    }

//--------------------------------------------------------------------
// CSCEP_CONNECTION::BuildStopPdu()
//
//--------------------------------------------------------------------
DWORD CSCEP_CONNECTION::BuildStopPdu( OUT SCEP_HEADER **ppPdu,
                                      OUT DWORD        *pdwPduSize )
    {
    DWORD  dwStatus = NO_ERROR;
    DWORD  dwPduSize;
    SCEP_HEADER       *pHeader;
    SCEP_REQ_HEADER_SHORT *pReqHeader;

    *ppPdu = 0;
    *pdwPduSize = 0;

    dwPduSize = sizeof(SCEP_HEADER) 
                + sizeof(SCEP_REQ_HEADER_SHORT)
                - sizeof(COMMAND_HEADER);

    pHeader = NewPdu();
    if (!pHeader)
        {
        return ERROR_IRTRANP_OUT_OF_MEMORY;
        }

    memset(pHeader,0,MAX_PDU_SIZE);  // MAX_PDU_SIZE is default arg for NewPdu() above.

    pHeader->Null = 0;
    pHeader->MsgType = MSG_TYPE_DATA;

    pReqHeader = (SCEP_REQ_HEADER_SHORT*)(pHeader->Rest);
    pReqHeader->InfType = INF_TYPE_USER_DATA;
    pReqHeader->Length1 = 4;
    pReqHeader->InfVersion = INF_VERSION;
    pReqHeader->DFlag = DFLAG_INTERRUPT;
    pReqHeader->Length3 = 0;

    *ppPdu = pHeader;
    *pdwPduSize = dwPduSize;

    return dwStatus;
    }

//--------------------------------------------------------------------
// CSCEP_CONNECTION::BuildDisconnectPdu()
//
//--------------------------------------------------------------------
DWORD CSCEP_CONNECTION::BuildDisconnectPdu( IN  USHORT        ReasonCode,
                                            OUT SCEP_HEADER **ppPdu,
                                            OUT DWORD        *pdwPduSize )
    {
    DWORD  dwStatus = NO_ERROR;
    DWORD  dwPduSize;
    SCEP_HEADER     *pHeader;
    SCEP_DISCONNECT *pDisconnect;

    *ppPdu = 0;
    *pdwPduSize = 0;

    dwPduSize = sizeof(SCEP_HEADER) 
                + sizeof(SCEP_DISCONNECT);

    pHeader = NewPdu();
    if (!pHeader)
        {
        return ERROR_IRTRANP_OUT_OF_MEMORY;
        }

    memset(pHeader,0,MAX_PDU_SIZE);  // MAX_PDU_SIZE is default arg for NewPdu() above.

    pHeader->Null = 0;
    pHeader->MsgType = MSG_TYPE_DISCONNECT;

    pDisconnect = (SCEP_DISCONNECT*)(pHeader->Rest);
    pDisconnect->InfType = INF_TYPE_REASON;
    pDisconnect->Length1 = sizeof(pDisconnect->ReasonCode);
    pDisconnect->ReasonCode = ReasonCode;

    #ifdef LITTLE_ENDIAN
    pDisconnect->ReasonCode = ByteSwapShort(pDisconnect->ReasonCode);
    #endif

    *ppPdu = pHeader;
    *pdwPduSize = dwPduSize;

    return dwStatus;
    }

//--------------------------------------------------------------------
// CSCEP_CONNECTION::SetScepLength()
//
// Update the length fields in a PDU to reflect the total length
// of a PDU.
//
// WARNING: Currently only supports long fragmented PDUs.
//--------------------------------------------------------------------
DWORD CSCEP_CONNECTION::SetScepLength( IN SCEP_HEADER *pPdu,
                                       IN DWORD        dwTotalPduSize )
    {
    DWORD  dwStatus = NO_ERROR;
    SCEP_REQ_HEADER_LONG_FRAG *pScepHeader;

    if (dwTotalPduSize > MAX_PDU_SIZE)
        {
        dwStatus = ERROR_SCEP_PDU_TOO_LARGE;
        }
    else
        {
        pScepHeader = (SCEP_REQ_HEADER_LONG_FRAG *)(pPdu->Rest);
        pScepHeader->Length1 = USE_LENGTH2;
        pScepHeader->Length2 = (USHORT)dwTotalPduSize - 6;
        pScepHeader->Length3 = (USHORT)dwTotalPduSize - 18;

        #ifdef LITTLE_ENDIAN
        pScepHeader->Length2 = ByteSwapShort(pScepHeader->Length2);
        pScepHeader->Length3 = ByteSwapShort(pScepHeader->Length3);
        #endif
        }

    return dwStatus;
    }
