//--------------------------------------------------------------------
// Copyright (C)1998 Microsoft Corporation, All Rights Reserved.
//
// bftp.cpp
//
// Author
//
//   Edward Reus (EdwardR)  02-26-98   Initial Coding.
//
//--------------------------------------------------------------------

#include "precomp.h"
#include <stdlib.h>

static BFTP_ATTRIBUTE_MAP_ENTRY Attributes[] = {
    //Attr   Name    Type
    { FIL0, "FIL0",  ATTR_TYPE_CHAR },   // ASCII 8.3 File Name.
    { LFL0, "LFL0",  ATTR_TYPE_CHAR },   // SJIS or ISO8859-1 Long File Name.
    { TIM0, "TIM0",  ATTR_TYPE_TIME },   // File create/modify time.
    { TYP0, "TYP0",  ATTR_TYPE_BINARY }, // File or Thumbnail Information.
    { TMB0, "TMB0",  ATTR_TYPE_BINARY }, // The scaled down image.
    { BDY0, "BDY0",  ATTR_TYPE_BINARY }, // (?).
    { CMD0, "CMD0",  ATTR_TYPE_BINARY }, // Command Name (?).
    { WHT0, "WHT0",  ATTR_TYPE_CHAR },   // Category Data.
    { ERR0, "ERR0",  ATTR_TYPE_BINARY }, // Error code.
    { RPL0, "RPL0",  ATTR_TYPE_CHAR },   // Result: Stored File Name.
    { INVALID_ATTR,  0,      0 }
    };

//
// This is the bFTP for an RIMG query by the camera:
//
#define BFTP_RIMG_ATTR_VALUE_SIZE         14
#define BFTP_RIMG_RESP_SIZE               12 + BFTP_RIMG_ATTR_VALUE_SIZE

static UCHAR BftpRimgRespAttrValue[BFTP_RIMG_ATTR_VALUE_SIZE] =
    {
    0x00, 0xff, 0xff,                   // Pixel aspect ratio (any).
    0x02, 0x01, 0xff, 0xff, 0xff, 0xff, // Accept image size (any).
    0x05, 0xff, 0xff, 0xff, 0xff        // Accept file size (any).
    };

//
// This is the bFTP for an RINF query by the camera:
//
#define BFTP_RINF_ATTR_VALUE_SIZE          3
#define BFTP_RINF_RESP_SIZE               12 + BFTP_RINF_ATTR_VALUE_SIZE

static UCHAR BftpRinfRespAttrValue[BFTP_RINF_ATTR_VALUE_SIZE] =
    {
    0x10, 0xff, 0xff                    // Memory available (lots).
    };

//
// This is the bFTP for an RCMD query by the camera:
//
#define BFTP_RCMD_ATTR_VALUE_SIZE          5
#define BFTP_RCMD_RESP_SIZE               12 + BFTP_RCMD_ATTR_VALUE_SIZE

static UCHAR BftpRcmdRespAttrValue[BFTP_RCMD_ATTR_VALUE_SIZE] =
    {
    0x20, 0x00, 0xff, 0x00, 0x01        // Accept up to 255 puts/connect.
    };

//
// Map bFTP error codes:
static DWORD dwBftpErrorCodeMap[][2] =
    {
    { ERROR_PUT_UNDEFINED_ERROR,    ERROR_BFTP_INVALID_PROTOCOL },
    { ERROR_PUT_ILLEGAL_DATA,       ERROR_BFTP_INVALID_PROTOCOL },
    { ERROR_PUT_UNSUPPORTED_PID,    ERROR_BFTP_INVALID_PROTOCOL },
    { ERROR_PUT_ILLEGAL_ATTRIBUTE,  ERROR_BFTP_INVALID_PROTOCOL },
    { ERROR_PUT_UNSUPPORTED_CMD,    ERROR_BFTP_INVALID_PROTOCOL },
    { ERROR_PUT_FILE_SYSTEM_FULL,   ERROR_IRTRANP_DISK_FULL },
    { ERROR_PUT_NO_FILE_OR_DIR,     ERROR_BFTP_INVALID_PROTOCOL },
    { ERROR_PUT_LOW_BATTERY,        ERROR_BFTP_INVALID_PROTOCOL },
    { ERROR_PUT_ABORT_EXECUTION,    ERROR_SCEP_ABORT },
    { ERROR_PUT_NO_ERROR,           NO_ERROR }
    };

//--------------------------------------------------------------------
//  CharToValue()
//
//  Used in parsing the bFTP date string. In this case the maximum
//  value to parse is the year (YYYY).
//--------------------------------------------------------------------
static WORD CharToValue( IN UCHAR *pValue,
                         IN DWORD  dwLength )
    {
    #define MAX_VALUE_STR_LEN    4
    WORD    wValue = 0;
    CHAR    szTemp[MAX_VALUE_STR_LEN];

    if (dwLength < MAX_VALUE_STR_LEN)
        {
        memcpy(szTemp,pValue,dwLength);
        szTemp[dwLength] = 0;
        wValue =  (WORD)atoi(szTemp);
        }

    return wValue;
    }

//--------------------------------------------------------------------
// MapBftpErrorCode()
//
//--------------------------------------------------------------------
DWORD  MapBftpErrorCode( IN DWORD dwBftpErrorCode )
    {
    DWORD  dwErrorCode = NO_ERROR;
    DWORD  dwNumCodes = sizeof(dwBftpErrorCodeMap)/(2*sizeof(DWORD));

    for (DWORD i=0; i<dwNumCodes; i++)
        {
        if (dwBftpErrorCode == dwBftpErrorCodeMap[i][0])
            {
            dwErrorCode = dwBftpErrorCodeMap[i][1];
            break;
            }
        }

    return dwErrorCode;
    }

//--------------------------------------------------------------------
// CSCEP_CONNECTION::ParseBftpAttributeName()
//
//--------------------------------------------------------------------
BFTP_ATTRIBUTE *CSCEP_CONNECTION::ParseBftpAttributeName(
                                     IN BFTP_ATTRIBUTE *pAttr,
                                     IN OUT DWORD      *pdwSize,
                                     OUT    DWORD      *pdwWhichAttr )
    {
    BFTP_ATTRIBUTE_MAP_ENTRY *pAttrMapEntry = Attributes;

    *pdwWhichAttr = INVALID_ATTR;

    #ifdef DBG_IO
    DbgPrint("ParseBftpAttributeName(): pAttr->Name: %c%c%c%c\n",
             pAttr->Name[0], pAttr->Name[1], pAttr->Name[2], pAttr->Name[3] );
    #endif

    while (pAttrMapEntry->pName)
       {
       if (Match4(pAttr->Name,pAttrMapEntry->pName))
           {
           *pdwWhichAttr = pAttrMapEntry->dwWhichAttr;

           break;
           }

       pAttrMapEntry++;
       }

    // Note: that the Length paramter is 8 bytes in from the start
    // of pAttr, hence the extra 8 (bytes) below:
    *pdwSize = *pdwSize - 8UL - pAttr->Length;
    pAttr = (BFTP_ATTRIBUTE*)( 8UL + pAttr->Length + (UCHAR*)pAttr );

    return pAttr;
    }

//--------------------------------------------------------------------
// CSCEP_CONNECTION::SaveBftpCreateDate()
//
// The bFTP create date/time is a character array of the form:
// YYYYMMDDHHMMSS (not zero terminated).
//
// If it was specifed then we want to use it as the create date
// of the picture file that we save the JPEG to.
//--------------------------------------------------------------------
DWORD CSCEP_CONNECTION::SaveBftpCreateDate( IN UCHAR  *pDate,
                                            IN DWORD   dwDateLength )
    {
    DWORD  dwStatus = NO_ERROR;
    SYSTEMTIME     SystemTime;
    FILETIME       LocalTime;
    FILETIME       FileTime;

    memset(&SystemTime,0,sizeof(SystemTime));

    if (dwDateLength == BFTP_DATE_TIME_SIZE)
        {
        //
        // Note that system time is in UTC, we will need to convert
        // this to local time...
        //
        SystemTime.wYear = CharToValue( pDate, 4 );
        SystemTime.wMonth = CharToValue( &(pDate[4]), 2 );
        SystemTime.wDay = CharToValue( &(pDate[6]), 2 );
        SystemTime.wHour = CharToValue( &(pDate[8]), 2 );
        SystemTime.wMinute = CharToValue( &(pDate[10]), 2 );
        SystemTime.wSecond = CharToValue( &(pDate[12]), 2 );

        if (SystemTimeToFileTime(&SystemTime,&LocalTime))
            {
            //
            // Before we use the time zone, we need to convert it to
            // UTC (its currently in "local time". Note that:
            //
            if (LocalFileTimeToFileTime(&LocalTime,&FileTime))
                {
                m_CreateTime = FileTime;

                #ifdef DBG_DATE
                DbgPrint("IrTranP: SaveBftpCreateDate: Date: %d/%d/%d %d:%d:%d\n",
                     SystemTime.wYear,
                     SystemTime.wMonth,
                     SystemTime.wDay,
                     SystemTime.wHour,
                     SystemTime.wMinute,
                     SystemTime.wSecond );
                #endif
                }
            else
                {
                #ifdef DBG_DATE
                DbgPrint("IrTranP: SaveBftpCreateDate(): LocalFileTimeToFileTime() Failed: %d\n",GetLastError());
                #endif
                }
            }
        else
            {
            dwStatus = GetLastError();
            #ifdef DBG_ERROR
            DbgPrint("IrTranP: SaveBftpCreateDate(): SystemTimeToFileTime(): Failed: %d\n", dwStatus );
            #endif
            }
        }

    return dwStatus;
    }

//--------------------------------------------------------------------
// CSCEP_CONNECTION::ParseBftp()
//
//--------------------------------------------------------------------
DWORD CSCEP_CONNECTION::ParseBftp( IN  UCHAR  *pBftpData,
                                   IN  DWORD   dwBftpDataSize,
                                   IN  BOOL    fSaveAsUPF,
                                   OUT DWORD  *pdwBftpOp,
                                   OUT UCHAR **ppPutData,
                                   OUT DWORD  *pdwPutDataSize )
    {
    DWORD   i;
    DWORD   dwStatus = NO_ERROR;
    DWORD   dwAttrSize;
    DWORD   dwWhichAttr;
    DWORD   dwLength;
    DWORD   dwWcLength;
    USHORT  usNumAttr;
    char   *pszTemp;
    BFTP_ATTRIBUTE *pAttr;
    BFTP_ATTRIBUTE *pNextAttr;

    *ppPutData = 0;
    *pdwPutDataSize = 0;

    #ifdef LITTLE_ENDIAN
    usNumAttr = ByteSwapShort( *((USHORT*)pBftpData) );
    #endif

    pAttr = (BFTP_ATTRIBUTE*)(pBftpData + sizeof(USHORT));
    dwAttrSize = dwBftpDataSize - sizeof(USHORT);

    for (i=0; i<usNumAttr; i++)
        {
        #ifdef LITTLE_ENDIAN
        pAttr->Length = ByteSwapLong( pAttr->Length );
        #endif

        pNextAttr = ParseBftpAttributeName( pAttr,
                                            &dwAttrSize,
                                            &dwWhichAttr );

        if (dwWhichAttr == INVALID_ATTR)
            {
            return ERROR_BFTP_INVALID_PROTOCOL;
            }

        if (dwWhichAttr == CMD0)
            {
            if (pAttr->Length == 2+sizeof(DWORD))
                {
                #ifdef LITTLE_ENDIAN
                *((DWORD*)(pAttr->Value)) = ByteSwapLong( *((DWORD*)(pAttr->Value)) );
                #endif
                }

            // Expect Value == 0x00010040 for a Query "WHT0" Request.
            //        Value == 0x00000000 for a Put Request.
            if ( *((DWORD*)(pAttr->Value)) == 0x00010040 )
                {
                *pdwBftpOp = BFTP_QUERY_RIMG;
                }
            else if ( *((DWORD*)(pAttr->Value)) == 0 )
                {
                *pdwBftpOp = BFTP_PUT;
                }
            else
                {
                *pdwBftpOp = BFTP_UNKNOWN;
                }
            }
        else if (dwWhichAttr == WHT0)
            {
            if (Match4("RIMG",pAttr->Value))
                {
                dwWhichAttr = RIMG;
                *pdwBftpOp = BFTP_QUERY_RIMG;
                }
            else if (Match4("RINF",pAttr->Value))
                {
                dwWhichAttr = RINF;
                *pdwBftpOp = BFTP_QUERY_RINF;
                }
            else if (Match4("RCMD",pAttr->Value))
                {
                dwWhichAttr = RCMD;
                *pdwBftpOp = BFTP_QUERY_RCMD;
                }
            else
                {
                dwWhichAttr = INVALID_ATTR;
                *pdwBftpOp = BFTP_UNKNOWN;
                return ERROR_BFTP_INVALID_PROTOCOL;
                }
            }
        //
        // Short (8.3) file name:
        //
        else if (dwWhichAttr == FIL0)
            {
            // Note: That the specification limits the file
            //       name to 8.3...
            dwLength = BftpValueLength(pAttr->Length);
            if (dwLength > FILE_NAME_SIZE)
                {
                dwLength = FILE_NAME_SIZE;
                }

            if (m_pszFileName)
                {
                FreeMemory(m_pszFileName);
                }

            m_pszFileName = (CHAR*)AllocateMemory(1+dwLength);
            if (!m_pszFileName)
                {
                return ERROR_IRTRANP_OUT_OF_MEMORY;
                }

            memcpy(m_pszFileName,pAttr->Value,dwLength);
            m_pszFileName[dwLength] = 0;

            if (m_pwszFileName)
                {
                FreeMemory(m_pwszFileName);
                }

            dwWcLength = sizeof(WCHAR)*(1+dwLength) + sizeof(WSZ_JPEG);
            m_pwszFileName = (WCHAR*)AllocateMemory(dwWcLength);
            if (!m_pwszFileName)
                {
                return ERROR_IRTRANP_OUT_OF_MEMORY;
                }

            //
            // Convert file name (~\XXXXXX.JPG) to unicode.
            //
            MultiByteToWideChar( CP_ACP,
                                 MB_PRECOMPOSED,
                                 m_pszFileName,
                                 1+dwLength,
                                 m_pwszFileName,
                                 dwWcLength );

            // File name is now XXXXXX.UPF. Change to
            // XXXXXX.JPG or XXXXXX.UPF as appropriate:
            WCHAR *pwsz = wcsrchr(m_pwszFileName,PERIOD);
            if (pwsz)
                {
                *pwsz = 0;  // Remove old suffix.

                if (fSaveAsUPF)
                    {
                    wcscat(m_pwszFileName,WSZ_UPF);    // UPF file.
                    }
                else
                    {
                    wcscat(m_pwszFileName,WSZ_JPEG);   // JPG file.
                    }
                }
            }
        //
        // UPF body: headers + thumbnail + jpeg image ...
        //
        else if (dwWhichAttr == BDY0)
            {
            // This is a PUT.
            ASSERT(*pdwBftpOp == BFTP_PUT);
            *ppPutData = pAttr->Value;
            *pdwPutDataSize = dwBftpDataSize - (DWORD)(pAttr->Value - pBftpData);
            }
        //
        // Long file name:
        //
        else if (dwWhichAttr == LFL0)
            {
            if (m_pszLongFileName)
                {
                FreeMemory(m_pszLongFileName);
                }

            dwLength = BftpValueLength(pAttr->Length);
            m_pszLongFileName = (CHAR*)AllocateMemory(1+dwLength);
            if (!m_pszLongFileName)
                {
                return ERROR_IRTRANP_OUT_OF_MEMORY;
                }

            memcpy(m_pszLongFileName,pAttr->Value,dwLength);
            m_pszLongFileName[dwLength] = 0;

            #ifdef DBG
            DbgPrint("CSCEP_CONNECTION::ParseBftp(): LFL0: %s\n",
                     m_pszLongFileName);
            #endif

            CHAR *pszLongFileName = strrchr(m_pszLongFileName,'\\');
            if (pszLongFileName)
                {
                pszLongFileName++;  // Skip over the file separator...
                }
            else
                {
                pszLongFileName = m_pszLongFileName;
                }

            dwLength = strlen(pszLongFileName);

            if (m_pwszFileName)
                {
                FreeMemory(m_pwszFileName);
                }

            dwWcLength = sizeof(WCHAR)*(1+dwLength) + sizeof(WSZ_JPEG);
            m_pwszFileName = (WCHAR*)AllocateMemory(dwWcLength);
            if (!m_pwszFileName)
                {
                return ERROR_IRTRANP_OUT_OF_MEMORY;
                }

            MultiByteToWideChar( CP_ACP,
                                 MB_PRECOMPOSED,
                                 pszLongFileName,
                                 1+dwLength,
                                 m_pwszFileName,
                                 dwWcLength );

            // File name is now XXXXXX.JPG. Change to
            // XXXXXX.JPEG or XXXXXX.UPF as appropriate:
            WCHAR *pwsz = wcsrchr(m_pwszFileName,PERIOD);
            if (pwsz)
                {
                *pwsz = 0;

                if (fSaveAsUPF)
                    {
                    wcscat(m_pwszFileName,WSZ_UPF);
                    }
                else
                    {
                    wcscat(m_pwszFileName,WSZ_JPEG);
                    }
                }

            #ifdef DBG_IO
            DbgPrint("CSCEP_CONNECTION::ParseBftp(): File: %S\n",
                     m_pwszFileName);
            #endif
            }
        //
        // Create Date/Time:
        //
        else if (dwWhichAttr == TIM0)
            {
            dwLength = BftpValueLength(pAttr->Length);

            SaveBftpCreateDate(pAttr->Value,dwLength);

            #ifdef DBG_DATE
            pszTemp = (char*)AllocateMemory(1+dwLength);
            if (pszTemp)
                {
                memcpy(pszTemp,pAttr->Value,dwLength);
                pszTemp[dwLength] = 0;
                DbgPrint("CSCEP_CONNECTION::ParseBftp(): TIM0: %s\n",pszTemp);
                FreeMemory(pszTemp);
                }
            #endif
            }
        //
        // Camera sent back a bFTP error code:
        //
        else if (dwWhichAttr == ERR0)
            {
            *pdwBftpOp = BFTP_ERROR;

            *ppPutData = pAttr->Value;
            *pdwPutDataSize = BftpValueLength(pAttr->Length);

            dwStatus = ByteSwapShort( *((USHORT*)(pAttr->Value)) );

            #ifdef DBG_ERROR
            DbgPrint("CSCEP_CONNECTION::ParseBftp(): ERR0: %d\n",dwStatus);
            #endif
            }

        // Note: We may need to byte swap other attributes as well if/when
        // the protocol is extended...

        pAttr = pNextAttr;
        }

    return dwStatus;
    }

//--------------------------------------------------------------------
// CSCEP_CONNECTION::ParseUpfHeaders()
//
//--------------------------------------------------------------------
DWORD CSCEP_CONNECTION::ParseUpfHeaders( IN  UCHAR  *pPutData,
                                         IN  DWORD   dwPutDataSize,
                                         OUT DWORD  *pdwJpegOffset,
                                         OUT DWORD  *pdwJpegSize )
    {
    DWORD   dwStatus = NO_ERROR;
    DWORD   dwStartAddress;
    DWORD   dwDataSize;
    INT     iGmtOffset = 0;
    WORD    wYear;
    WORD    wMonth;
    WORD    wDay;
    SYSTEMTIME  SystemTime;
    FILETIME    LocalTime;
    FILETIME    FileTime;
    UPF_HEADER *pUpfHeader;
    UPF_ENTRY  *pUpfEntry1;
    UPF_ENTRY  *pUpfEntry2;
    PICTURE_INFORMATION_DATA *pThumbnailInfo = 0;
    PICTURE_INFORMATION_DATA *pPictureInfo = 0;

    ASSERT(dwPutDataSize >= UPF_TOTAL_HEADER_SIZE);

    pUpfHeader = (UPF_HEADER*)pPutData;

    pUpfEntry1 = (UPF_ENTRY*)(UPF_HEADER_SIZE + (UCHAR*)pUpfHeader);
    pUpfEntry2 = (UPF_ENTRY*)(UPF_ENTRY_SIZE + (UCHAR*)pUpfEntry1);

    dwStartAddress = ByteSwapLong(pUpfEntry2->dwStartAddress);

    dwDataSize = ByteSwapLong(pUpfEntry2->dwDataSize);

    #ifdef DBG_PROPERTIES
    DbgPrint("CSCEP_CONNECTION::ParseUpfHeaders(): NumTables: %d\n",
             pUpfHeader->NumTables );

    pPictureInfo = (PICTURE_INFORMATION_DATA*)pUpfEntry2->InformationData;

    DbgPrint("CSCEP_CONNECTION::ParseUpfHeaders(): Rotation: %d\n",
             pPictureInfo->RotationSet );
    #endif

    #ifdef DBG_IO
    DbgPrint("CSCEP_CONNECTION::ParseUpfHeaders(): StartAddress: %d Size: %d\n",
             dwStartAddress, dwDataSize );
    #endif

    *pdwJpegOffset = UPF_HEADER_SIZE + 4*UPF_ENTRY_SIZE + dwStartAddress;
    *pdwJpegSize = dwDataSize;

    #ifdef UPF_FILES
    *pdwJpegOffset = 0;
    *pdwJpegSize = 0;
    #endif

    // Ok, now parse the picture creation date/time, if one is
    // defined.
    //
    // Note that the date/time is local time, with a GMT offset.
    // Since we will use local system time conversions, we will
    // not need the GMT offset.
    if (pUpfHeader->CreateDate[UPF_GMT_OFFSET] != 0x80)
        {
        iGmtOffset = (pUpfHeader->CreateDate[UPF_GMT_OFFSET])/4;
        #ifdef DBG_DATE
        DbgPrint("IrTranP: ParseUpfHeaders: GMT Offset: %d (0x%x)\n",
                 iGmtOffset, pUpfHeader->CreateDate[UPF_GMT_OFFSET] );
        #endif
        }

    memcpy(&wYear,&(pUpfHeader->CreateDate[UPF_YEAR]),sizeof(SHORT) );
    wYear = ByteSwapShort(wYear);

    wMonth = pUpfHeader->CreateDate[UPF_MONTH];
    wDay = pUpfHeader->CreateDate[UPF_DAY];

    // At least the Year/Month/Day must be specified, else we
    // won't use the date. If the Hour/Minute/Second are known,
    // then we will use them as well.
    if ((wYear != 0xffff) && (wMonth != 0xff) && (wDay != 0xff))
        {
        memset(&SystemTime,0,sizeof(SystemTime));
        SystemTime.wYear = wYear;
        SystemTime.wMonth = wMonth;
        SystemTime.wDay = wDay;
        if (pUpfHeader->CreateDate[UPF_HOUR] != 0xff)
            {
            SystemTime.wHour = pUpfHeader->CreateDate[UPF_HOUR];

            if (pUpfHeader->CreateDate[UPF_MINUTE] != 0xff)
                {
                SystemTime.wMinute = pUpfHeader->CreateDate[UPF_MINUTE];

                if (pUpfHeader->CreateDate[UPF_SECOND] != 0xff)
                    {
                    SystemTime.wSecond = pUpfHeader->CreateDate[UPF_SECOND];
                    }
                }
            }


        if (SystemTimeToFileTime(&SystemTime,&LocalTime))
            {
            // 
            // Before we save the date/time, we need to convert it to
            // UTC (its currently in "local time". Note that:
            //
            if (LocalFileTimeToFileTime(&LocalTime,&FileTime))
                { 
                m_CreateTime = FileTime;

                #ifdef DBG_DATE
                DbgPrint("IrTranP: ParseUpfHeaders: Date/Time: %d/%d/%d %d:%d:%d\n",
                     SystemTime.wYear,
                     SystemTime.wMonth,
                     SystemTime.wDay,
                     SystemTime.wHour,
                     SystemTime.wMinute,
                     SystemTime.wSecond );
                #endif
                }
            else
                {
                #ifdef DBG_DATE
                DbgPrint("IrTranP: SaveBftpCreateDate(): LocalFileTimeToFileTime() Failed: %d\n",GetLastError());
                #endif
                }
            }
        else
            {
            #ifdef DBG_ERROR
            dwStatus = GetLastError();
            DbgPrint("IrTranP: ParseUpfHeaders(): Invalid Picture Create Date/Time. Status: %d\n", dwStatus );
            DbgPrint("         Date: 0x%2.2x %2.2x%2.2x %2.2x %2.2x %2.2x %2.2x %2.2x\n",
                     pUpfHeader->CreateDate[UPF_GMT_OFFSET],
                     pUpfHeader->CreateDate[UPF_YEAR],
                     pUpfHeader->CreateDate[UPF_YEAR+1],
                     pUpfHeader->CreateDate[UPF_MONTH],
                     pUpfHeader->CreateDate[UPF_DAY],
                     pUpfHeader->CreateDate[UPF_HOUR],
                     pUpfHeader->CreateDate[UPF_MINUTE],
                     pUpfHeader->CreateDate[UPF_SECOND] );
            dwStatus = NO_ERROR;
            #endif
            }
        }
    else
        {
        #ifdef DBG_DATE
        DbgPrint("IrTranP: ParseUpfHeaders(): No Picture Create Date/Time.\n");
        #endif
        }

    return dwStatus;
    }

//--------------------------------------------------------------------
// CSCEP_CONNECTION::BuildBftpWht0RinfPdu()
//
//--------------------------------------------------------------------
DWORD CSCEP_CONNECTION::BuildBftpWht0RinfPdu(
                             OUT SCEP_HEADER          **ppPdu,
                             OUT DWORD                 *pdwPduSize,
                             OUT SCEP_REQ_HEADER_LONG **ppCommand,
                             OUT COMMAND_HEADER       **ppCommandHeader )
    {
    DWORD  dwStatus = NO_ERROR;
    SCEP_HEADER          *pHeader;
    SCEP_REQ_HEADER_LONG *pCommand;
    COMMAND_HEADER       *pCommandHeader;
    UCHAR                *pUserData;
    USHORT               *pwNumAttributes;
    BFTP_ATTRIBUTE       *pAttrib;

    *ppPdu = 0;
    *pdwPduSize = 0;
    *ppCommand = 0;
    *ppCommandHeader = 0;

    pHeader = NewPdu();  // Size is MAX_PDU_SIZE by default...
    if (!pHeader)
        {
        return ERROR_IRTRANP_OUT_OF_MEMORY;
        }

    memset(pHeader,0,MAX_PDU_SIZE);

    // This is the total size of the PDU that we will construct:
    DWORD  dwPduSize = sizeof(SCEP_HEADER)
                     + sizeof(SCEP_REQ_HEADER_LONG)
                     + sizeof(USHORT)        // Num Attributes
                     + sizeof(BFTP_ATTRIBUTE)
                     + sizeof(DWORD)
                     + sizeof(BFTP_ATTRIBUTE)
                     + WHT0_ATTRIB_SIZE;

    // Length2 is the total size of the PDU minus the offset+size
    // of Length2:
    USHORT wLength2 = (USHORT)dwPduSize - 6;

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

    pCommand = (SCEP_REQ_HEADER_LONG*)(pHeader->Rest);
    pCommand->InfType = INF_TYPE_USER_DATA;
    pCommand->Length1 = USE_LENGTH2;           // 0xff
    pCommand->Length2 = wLength2;
    pCommand->InfVersion = INF_VERSION;
    pCommand->DFlag = DFLAG_SINGLE_PDU;
    pCommand->Length3 = pCommand->Length2 - 4; //

    pCommandHeader = (COMMAND_HEADER*)(pCommand->CommandHeader);
    pCommandHeader->Marker58h = 0x58;
    pCommandHeader->PduType = PDU_TYPE_REQUEST;
    pCommandHeader->Length4 = pCommand->Length2 - 10;
    pCommandHeader->DestPid = m_SrcPid;
    pCommandHeader->SrcPid = m_DestPid;
    pCommandHeader->CommandId = (USHORT)m_dwCommandId;

    memcpy( pCommandHeader->DestMachineId,
            m_pPrimaryMachineId,
            MACHINE_ID_SIZE );

    memcpy( pCommandHeader->SrcMachineId,
            m_pSecondaryMachineId,
            MACHINE_ID_SIZE );

    #ifdef LITTLE_ENDIAN
    pCommand->Length2 = ByteSwapShort(pCommand->Length2);
    pCommand->Length3 = ByteSwapShort(pCommand->Length3);
    ByteSwapCommandHeader(pCommandHeader);
    #endif

    // Setup the bFTP:
    pUserData = pCommand->UserData;
    pwNumAttributes = (USHORT*)pUserData;

    *pwNumAttributes = 2;     // Two bFTP attributes.
    #ifdef LITTLE_ENDIAN
    *pwNumAttributes = ByteSwapShort(*pwNumAttributes);
    #endif
    pUserData += sizeof(*pwNumAttributes);

    // First attribute is CMD0:
    DWORD  dwCmd0AttrValue = CMD0_ATTR_VALUE; // Fixed constant!
    pAttrib = (BFTP_ATTRIBUTE*)pUserData;
    memcpy( pAttrib->Name, Attributes[CMD0].pName, BFTP_NAME_SIZE );
    pAttrib->Length = sizeof(pAttrib->Type)
                    + sizeof(pAttrib->Flag)
                    + sizeof(dwCmd0AttrValue);
    pAttrib->Type = ATTR_TYPE_BINARY;   // 0x00
    pAttrib->Flag = ATTR_FLAG_DEFAULT;  // 0x00
    memcpy( pAttrib->Value, &dwCmd0AttrValue, sizeof(dwCmd0AttrValue) );

    #ifdef LITTLE_ENDIAN
    pAttrib->Length = ByteSwapLong(pAttrib->Length);
    #endif

    // Second attribute is WHT0:RINF
    pAttrib = (BFTP_ATTRIBUTE*)(pUserData
                                + sizeof(BFTP_ATTRIBUTE)
                                + sizeof(dwCmd0AttrValue));
    memcpy( pAttrib->Name, Attributes[WHT0].pName, BFTP_NAME_SIZE );
    pAttrib->Length = sizeof(pAttrib->Type)
                    + sizeof(pAttrib->Flag)
                    + WHT0_ATTRIB_SIZE;
    pAttrib->Type = ATTR_TYPE_CHAR;     // 0x00
    pAttrib->Flag = ATTR_FLAG_DEFAULT;  // 0x00
    memcpy( pAttrib->Value, SZ_RINF, WHT0_ATTRIB_SIZE );

    #ifdef LITTLE_ENDIAN
    pAttrib->Length = ByteSwapLong(pAttrib->Length);
    #endif


    // Done.
    *ppPdu = pHeader;
    *pdwPduSize = dwPduSize;
    *ppCommand = pCommand;
    *ppCommandHeader = pCommandHeader;

    return dwStatus;
    }

//--------------------------------------------------------------------
// CSCEP_CONNECTION::BuildBftpPutPdu()
//
// The PUT command will span multiple PDUs, this function builds the
// Nth fragment. Note that the first will also hold the attributes
// for the UPF file to be sent (in addition to the SCEP header stuff).
//
// Each PDU will also contain (MAX_PDU_SIZE - *pdwPduSize) bytes
// of the UPF file, but that isn't added in here. You add that
// yourself in the PDU starting at *ppCommand->UserData[].
//
// On success, return NO_ERROR, else return a non-zero error code.
//
// dwUpfFileSize   -- The total UPF file size.
//
// pszUpfFile      -- The 8.3 name of the UPF file.
//
// pdwFragNo       -- The fragment number that was built, cycle this
//                    back into each successive call to BuildBftpPutPdu().
//                    Initialize *pdwFragNo to zero before the first
//                    iteration, then leave it alone.
//--------------------------------------------------------------------
DWORD CSCEP_CONNECTION::BuildBftpPutPdu(
                             IN  DWORD             dwUpfFileSize,
                             IN  CHAR             *pszUpfFileName,
                             IN OUT DWORD         *pdwFragNo,
                             OUT SCEP_HEADER     **ppPdu,
                             OUT DWORD            *pdwHeaderSize,
                             OUT SCEP_REQ_HEADER_LONG_FRAG **ppCommand )
    {
    DWORD            dwStatus = NO_ERROR;
    SCEP_HEADER     *pHeader;
    SCEP_REQ_HEADER_LONG_FRAG *pCommand;
    COMMAND_HEADER  *pCommandHeader;
    UCHAR           *pUserData;
    USHORT          *pwNumAttributes;
    BFTP_ATTRIBUTE  *pAttrib;
    DWORD            dwUpfFileNameLength = strlen(pszUpfFileName);


    *ppPdu = 0;
    *pdwHeaderSize = 0;
    *ppCommand = 0;

    pHeader = NewPdu();  // Size is MAX_PDU_SIZE by default...
    if (!pHeader)
        {
        return ERROR_IRTRANP_OUT_OF_MEMORY;
        }

    memset(pHeader,0,MAX_PDU_SIZE);

    // This is the size of the SCEP (and bFTP) headers part of the
    // PDU that we will construct. dwHeaderSize1 is the header size for
    // the first PDU, dwHeaderSizeN is the header size for the rest of
    // the PDUs. Note that the Nth (N>1) header does not include the
    // COMMAN_HEADER (28 bytes).
    DWORD  dwHeaderSize;
    DWORD  dwHeaderSize1 = sizeof(SCEP_HEADER)
                         + sizeof(SCEP_REQ_HEADER_LONG_FRAG)
                         + sizeof(USHORT)          // Num Attributes
                         + sizeof(BFTP_ATTRIBUTE)  // For CMD0
                         + sizeof(DWORD)
                         + sizeof(BFTP_ATTRIBUTE)  // For FIL0
                         + dwUpfFileNameLength
                         + sizeof(BFTP_ATTRIBUTE); // For BDY0

    DWORD  dwHeaderSizeN = sizeof(SCEP_HEADER)
                         + FIELD_OFFSET(SCEP_REQ_HEADER_LONG_FRAG,CommandHeader);

    DWORD  dwSpace1;       // Space left after the header in PDU #1.
    DWORD  dwSpaceN;       // Space left after the header in the Nth PDU.
    DWORD  dwFileSizeLeft; // File Size minus what will fit in the
                           // first PDU.
    DWORD  dwNumFullPdus;  // Number of "full" PDUs after PDU #1.
    DWORD  dwLastPdu;      // = 1 iff the last PDU is partially full.
    DWORD  dwNumPdus;      // Total number of fragments to hold the file.

    // Figure out which fragment we are on:
    if (*pdwFragNo == 0)
        {
        dwHeaderSize = dwHeaderSize1;
        m_Fragmented = TRUE;
        m_DFlag = DFLAG_FIRST_FRAGMENT;

        // The space in the PDU left after the first and Nth headers:
        dwSpace1 = MAX_PDU_SIZE - dwHeaderSize1;
        dwSpaceN = MAX_PDU_SIZE - dwHeaderSizeN;

        // The number of full PDUs following the first PDU:
        dwFileSizeLeft = dwUpfFileSize - dwSpace1;
        dwNumFullPdus = dwFileSizeLeft / dwSpaceN;

        // See if there is a trailer PDU with remaining data:
        dwLastPdu = ((dwFileSizeLeft % dwSpaceN) > 0)? 1 : 0;

        dwNumPdus = 1 + dwNumFullPdus + dwLastPdu;

        *pdwFragNo = 1;
        m_dwSequenceNo = 0;     // First Seq.No. is 0.
        m_dwRestNo = dwNumPdus; // Rest starts at Total Num. Fragments.
        }
    else
        {
        dwHeaderSize = dwHeaderSizeN;

        *pdwFragNo++;
        m_dwSequenceNo++;
        m_dwRestNo--;

        if (m_dwRestNo == 0)
            {
            return ERROR_BFTP_NO_MORE_FRAGMENTS; // Called to many times...
            }
        else if (m_dwRestNo == 1)
            {
            m_DFlag = DFLAG_LAST_FRAGMENT;
            }
        else
            {
            m_DFlag = DFLAG_FRAGMENT;
            }
        }

    // Length2 is the total size of the PDU minus the offset+size
    // of Length2:
    USHORT wLength2 = (USHORT)(MAX_PDU_SIZE - 6);
    DWORD  dwLength4 = dwUpfFileSize + 22 + 48;
    DWORD  dwBdy0Length = dwUpfFileSize + 2;

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

    pCommand = (SCEP_REQ_HEADER_LONG_FRAG*)(pHeader->Rest);
    pCommand->InfType = INF_TYPE_USER_DATA;
    pCommand->Length1 = USE_LENGTH2;           // 0xff
    pCommand->Length2 = wLength2;
    pCommand->InfVersion = INF_VERSION;
    pCommand->DFlag = m_DFlag;
    pCommand->Length3 = pCommand->Length2 - 12; //
    pCommand->SequenceNo = m_dwSequenceNo;
    pCommand->RestNo = m_dwRestNo;

    #ifdef LITTLE_ENDIAN
    pCommand->Length2 = ByteSwapShort(pCommand->Length2);
    pCommand->Length3 = ByteSwapShort(pCommand->Length3);
    pCommand->SequenceNo = ByteSwapLong(pCommand->SequenceNo);
    pCommand->RestNo = ByteSwapLong(pCommand->RestNo);
    #endif

    // Note that there is a COMMAND_HEADER in the SCEP header only
    // for the first fragment.
    if (m_DFlag == DFLAG_FIRST_FRAGMENT)
        {
        pCommandHeader = (COMMAND_HEADER*)(pCommand->CommandHeader);
        pCommandHeader->Marker58h = 0x58;
        pCommandHeader->PduType = PDU_TYPE_REQUEST;
        pCommandHeader->Length4 = dwLength4;
        pCommandHeader->DestPid = m_SrcPid;
        pCommandHeader->SrcPid = m_DestPid;
        pCommandHeader->CommandId = (USHORT)m_dwCommandId;

        memcpy( pCommandHeader->DestMachineId,
                m_pPrimaryMachineId,
                MACHINE_ID_SIZE );

        memcpy( pCommandHeader->SrcMachineId,
                m_pSecondaryMachineId,
                MACHINE_ID_SIZE );

        #ifdef LITTLE_ENDIAN
        ByteSwapCommandHeader(pCommandHeader);
        #endif

        // Setup the bFTP:
        pUserData = pCommand->UserData;
        pwNumAttributes = (USHORT*)pUserData;

        *pwNumAttributes = 3;     // Three bFTP attributes.
        #ifdef LITTLE_ENDIAN
        *pwNumAttributes = ByteSwapShort(*pwNumAttributes);
        #endif
        pUserData += sizeof(*pwNumAttributes);

        // First attribute is CMD0:
        DWORD  dwCmd0AttrValue = 0x00000000;
        pAttrib = (BFTP_ATTRIBUTE*)pUserData;
        memcpy( pAttrib->Name, Attributes[CMD0].pName, BFTP_NAME_SIZE );
        pAttrib->Length = sizeof(pAttrib->Type)
                        + sizeof(pAttrib->Flag)
                        + sizeof(ULONG);
        pAttrib->Type = ATTR_TYPE_BINARY;   // 0x00
        pAttrib->Flag = ATTR_FLAG_DEFAULT;  // 0x00
        memcpy( pAttrib->Value, &dwCmd0AttrValue, sizeof(dwCmd0AttrValue) );

        #ifdef LITTLE_ENDIAN
        pAttrib->Length = ByteSwapLong(pAttrib->Length);
        #endif

        // Second attribute is FIL0 (with the 8.3 UPF file name):
        pAttrib = (BFTP_ATTRIBUTE*)(pUserData
                                    + sizeof(BFTP_ATTRIBUTE)
                                    + sizeof(dwCmd0AttrValue));
        memcpy( pAttrib->Name, Attributes[FIL0].pName, BFTP_NAME_SIZE );
        pAttrib->Length = sizeof(pAttrib->Type)
                        + sizeof(pAttrib->Flag)
                        + dwUpfFileNameLength;
        pAttrib->Type = ATTR_TYPE_CHAR;     // 0x01
        pAttrib->Flag = ATTR_FLAG_DEFAULT;  // 0x00
        memcpy( pAttrib->Value, pszUpfFileName, dwUpfFileNameLength );

        #ifdef LITTLE_ENDIAN
        pAttrib->Length = ByteSwapLong(pAttrib->Length);
        #endif

        // Third attribute is BDY0 (with the value being the whole UPF file):
        pAttrib = (BFTP_ATTRIBUTE*)( (char*)pAttrib
                                   + sizeof(BFTP_ATTRIBUTE)
                                   + dwUpfFileNameLength );
        memcpy( pAttrib->Name, Attributes[BDY0].pName, BFTP_NAME_SIZE );
        pAttrib->Length = dwBdy0Length;
        pAttrib->Type = ATTR_TYPE_BINARY;   // 0x00
        pAttrib->Flag = ATTR_FLAG_DEFAULT;  // 0x00
        // pAttrib->Value is not copied in (its the entire UPF file).

        #ifdef LITTLE_ENDIAN
        pAttrib->Length = ByteSwapLong(pAttrib->Length);
        #endif
        }

    // Done.
    *ppPdu = pHeader;
    *pdwHeaderSize = dwHeaderSize;
    *ppCommand = pCommand;

    return dwStatus;
    }

//--------------------------------------------------------------------
// CSCEP_CONNECTION::BuildBftpRespPdu()
//
//--------------------------------------------------------------------
DWORD CSCEP_CONNECTION::BuildBftpRespPdu(
                             IN  DWORD            dwPduSize,
                             OUT SCEP_HEADER    **ppPdu,
                             OUT SCEP_REQ_HEADER_SHORT **ppCommand,
                             OUT COMMAND_HEADER **ppCommandHeader )
    {
    DWORD  dwStatus = NO_ERROR;
    SCEP_HEADER           *pHeader;
    SCEP_REQ_HEADER_SHORT *pCommand;
    COMMAND_HEADER        *pCommandHeader;

    *ppPdu = 0;
    *ppCommand = 0;
    *ppCommandHeader = 0;

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

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

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

    pCommand = (SCEP_REQ_HEADER_SHORT*)(pHeader->Rest);
    pCommand->InfType = INF_TYPE_USER_DATA;
    pCommand->Length1 = (UCHAR)dwPduSize - 4;  // Four bytes from the start.
    pCommand->InfVersion = INF_VERSION;
    pCommand->DFlag = DFLAG_SINGLE_PDU;
    pCommand->Length3 = (USHORT)dwPduSize - 8; // Eight bytes from the start.

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

    pCommandHeader = (COMMAND_HEADER*)(pCommand->CommandHeader);
    pCommandHeader->Marker58h = 0x58;
    pCommandHeader->PduType = PDU_TYPE_REPLY_ACK;
    pCommandHeader->Length4 = dwPduSize - 14;  // Twelve bytes from the start.
    pCommandHeader->DestPid = m_SrcPid;
    pCommandHeader->SrcPid = m_DestPid;
    pCommandHeader->CommandId = (USHORT)m_dwCommandId;

    memcpy( pCommandHeader->DestMachineId,
            m_pPrimaryMachineId,
            MACHINE_ID_SIZE );

    memcpy( pCommandHeader->SrcMachineId,
            m_pSecondaryMachineId,
            MACHINE_ID_SIZE );

    #ifdef LITTLE_ENDIAN
    ByteSwapCommandHeader(pCommandHeader);
    #endif

    *ppPdu = pHeader;
    *ppCommand = pCommand;
    *ppCommandHeader = pCommandHeader;

    return dwStatus;
    }

//--------------------------------------------------------------------
// CSCEP_CONNECTION::BuildWht0RespPdu()
//
//--------------------------------------------------------------------
DWORD CSCEP_CONNECTION::BuildWht0RespPdu( IN  DWORD         dwWht0Type,
                                          OUT SCEP_HEADER **ppPdu,
                                          OUT DWORD        *pdwPduSize )
    {
    DWORD  dwStatus = NO_ERROR;
    DWORD  dwPduSize;
    DWORD  dwRespSize;
    DWORD  dwAttrValueSize;
    SCEP_HEADER           *pHeader;
    SCEP_REQ_HEADER_SHORT *pCommand;
    COMMAND_HEADER        *pCommandHeader;
    UCHAR                 *pQueryResp;
    USHORT                *pUShort;
    BFTP_ATTRIBUTE        *pAttr;
    UCHAR                 *pAttrValue;

    *ppPdu = 0;
    *pdwPduSize = 0;

    if (dwWht0Type == BFTP_QUERY_RIMG)
        {
        dwRespSize = BFTP_RIMG_RESP_SIZE;
        dwAttrValueSize = BFTP_RIMG_ATTR_VALUE_SIZE;
        pAttrValue = BftpRimgRespAttrValue;
        #ifdef DBG_IO
        DbgPrint("BuildWht0RespPdu(): RIMG Response\n");
        #endif
        }
    else if (dwWht0Type == BFTP_QUERY_RINF)
        {
        dwRespSize = BFTP_RINF_RESP_SIZE;
        dwAttrValueSize = BFTP_RINF_ATTR_VALUE_SIZE;
        pAttrValue = BftpRinfRespAttrValue;
        #ifdef DBG_IO
        DbgPrint("BuildWht0RespPdu(): RINF Response\n");
        #endif
        }
    else if (dwWht0Type == BFTP_QUERY_RCMD)
        {
        dwRespSize = BFTP_RCMD_RESP_SIZE;
        dwAttrValueSize = BFTP_RCMD_ATTR_VALUE_SIZE;
        pAttrValue = BftpRcmdRespAttrValue;
        #ifdef DBG_IO
        DbgPrint("BuildWht0RespPdu(): RCMD Response\n");
        #endif
        }
    else
        {
        return ERROR_BFTP_INVALID_PROTOCOL;
        }

    dwPduSize = SCEP_HEADER_SIZE
                + SCEP_REQ_HEADER_SHORT_SIZE
                + dwRespSize;

    dwStatus = BuildBftpRespPdu( dwPduSize,
                                 &pHeader,
                                 &pCommand,
                                 &pCommandHeader );

    if (dwStatus == NO_ERROR)
        {
        pQueryResp = pCommand->UserData;

        // Set the number of bFTP attributes:
        pUShort = (USHORT*)pQueryResp;
        *pUShort = 1;
        #ifdef LITTLE_ENDIAN
        *pUShort = ByteSwapShort(*pUShort);
        #endif

        // Set the BDY0 for the query response:
        pAttr = (BFTP_ATTRIBUTE*)(sizeof(USHORT)+pQueryResp);
        memcpy(pAttr->Name,Attributes[BDY0].pName,BFTP_NAME_SIZE);
        pAttr->Length = 2 + dwAttrValueSize;
        pAttr->Type = ATTR_TYPE_BINARY;
        pAttr->Flag = ATTR_FLAG_DEFAULT;
        memcpy(pAttr->Value,pAttrValue,dwAttrValueSize);

        #ifdef LITTLE_ENDIAN
        pAttr->Length = ByteSwapLong(pAttr->Length);
        #endif

        *ppPdu = pHeader;
        *pdwPduSize = dwPduSize;
        }

    return dwStatus;
    }

//--------------------------------------------------------------------
// CSCEP_CONNECTION::BuildPutRespPdu()
//
//--------------------------------------------------------------------
DWORD CSCEP_CONNECTION::BuildPutRespPdu( IN  DWORD         dwPduAckOrNack,
                                         IN  USHORT        usErrorCode,
                                         OUT SCEP_HEADER **ppPdu,
                                         OUT DWORD        *pdwPduSize )
    {
    DWORD  dwStatus = NO_ERROR;
    DWORD  dwPduSize;
    DWORD  dwRespSize;
    DWORD  dwFileNameLen;
    SCEP_HEADER           *pHeader;
    SCEP_REQ_HEADER_SHORT *pCommand;
    COMMAND_HEADER        *pCommandHeader;
    UCHAR                 *pQueryResp;
    USHORT                *pUShort;
    BFTP_ATTRIBUTE        *pAttr;
    UCHAR                 *pAttrValue;

    *ppPdu = 0;
    *pdwPduSize = 0;

    if (dwPduAckOrNack == PDU_TYPE_REPLY_ACK)
        {
        if (!m_pszFileName)
            {
            return ERROR_BFTP_INVALID_PROTOCOL;
            }

        dwFileNameLen = strlen( (const char *)m_pszFileName );
        dwRespSize = sizeof(USHORT) + sizeof(BFTP_ATTRIBUTE) + dwFileNameLen;
        }
    else
        {
        dwRespSize = sizeof(USHORT) + sizeof(BFTP_ATTRIBUTE) + sizeof(USHORT);
        }

    dwPduSize = SCEP_HEADER_SIZE
                + SCEP_REQ_HEADER_SHORT_SIZE
                + dwRespSize;

    dwStatus = BuildBftpRespPdu( dwPduSize,
                                 &pHeader,
                                 &pCommand,
                                 &pCommandHeader );

    if (dwStatus == NO_ERROR)
        {
        pQueryResp = pCommand->UserData;

        // Set the number of bFTP attributes:
        pUShort = (USHORT*)pQueryResp;
        *pUShort = 1;
        #ifdef LITTLE_ENDIAN
        *pUShort = ByteSwapShort(*pUShort);
        #endif

        pAttr = (BFTP_ATTRIBUTE*)(sizeof(USHORT)+pQueryResp);

        if (dwPduAckOrNack == PDU_TYPE_REPLY_ACK)
            {
            // Set the RPL0 for the put response (ACK):
            memcpy(pAttr->Name,Attributes[RPL0].pName,BFTP_NAME_SIZE);
            pAttr->Length = 2 + dwFileNameLen;
            pAttr->Type = ATTR_TYPE_CHAR;
            pAttr->Flag = ATTR_FLAG_DEFAULT;
            memcpy(pAttr->Value,m_pszFileName,dwFileNameLen);
            }
        else
            {
            // Nack the PUT:
            pCommandHeader->PduType = PDU_TYPE_REPLY_NACK;

            // Set the ERR0 for the put response (NACK):
            memcpy(pAttr->Name,Attributes[RPL0].pName,BFTP_NAME_SIZE);
            pAttr->Length = 2 + sizeof(USHORT);
            pAttr->Type = ATTR_TYPE_BINARY;
            pAttr->Flag = ATTR_FLAG_DEFAULT;

            #ifdef LITTLE_ENDIAN
            usErrorCode = ByteSwapShort(usErrorCode);
            #endif
            memcpy(pAttr->Value,&usErrorCode,sizeof(USHORT));
            }

        #ifdef LITTLE_ENDIAN
        pAttr->Length = ByteSwapLong(pAttr->Length);
        #endif

        *ppPdu = pHeader;
        *pdwPduSize = dwPduSize;
        }

    return dwStatus;
    }
