//-------------------------------------------------------------------- // Copyright (C)1998 Microsoft Corporation, All Rights Reserved. // // bftp.cpp // // Author // // Edward Reus (EdwardR) 02-26-98 Initial Coding. // //-------------------------------------------------------------------- #include "precomp.h" #include 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. }; //-------------------------------------------------------------------- // 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 5 WORD wValue = 0; CHAR szTemp[MAX_VALUE_STR_LEN]; if (dwLength < MAX_VALUE_STR_LEN-1) { // // only handle 4 digits for year // ZeroMemory(szTemp,sizeof(szTemp)); memcpy(szTemp,pValue,dwLength); wValue = (WORD)atoi(szTemp); } return wValue; } //-------------------------------------------------------------------- // 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*)( FIELD_OFFSET(BFTP_ATTRIBUTE,Type) + 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); #ifdef DBG_IO DbgPrint("CSCEP_CONNECTION::ParseBftp(): Processing %d attributes\n",usNumAttr); #endif for (i=0; i dwBftpDataSize) { #ifdef DBG_ERROR DbgPrint("ParseBftp(): Data too small to hold length and attribute %d\n",dwBftpDataSize); #endif return ERROR_BFTP_INVALID_PROTOCOL; } #ifdef LITTLE_ENDIAN pAttr->Length = ByteSwapLong( pAttr->Length ); #endif // // this just needs to look at the name and length fields in the first 8 bytes of // the struct // pNextAttr = ParseBftpAttributeName( pAttr, &dwAttrSize, &dwWhichAttr ); if (dwWhichAttr == INVALID_ATTR) { #ifdef DBG_ERROR DbgPrint("ParseBftp(): bad attribute\n"); #endif return ERROR_BFTP_INVALID_PROTOCOL; } if (dwWhichAttr != BDY0) { // // won't do this check for the body as the data may be fragmented // if ((ULONG)(ULONG_PTR)((UCHAR *)pAttr - pBftpData) + FIELD_OFFSET(BFTP_ATTRIBUTE,Type) + pAttr->Length > dwBftpDataSize) { #ifdef DBG_ERROR DbgPrint("ParseBftp(): Data too small to hold attribute, attrib=%d total len=%d\n",pAttr->Length,dwBftpDataSize); #endif 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 / sizeof(WCHAR)); // 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) { StringCbCat (m_pwszFileName, dwWcLength, WSZ_UPF); // UPF file. } else { StringCbCat (m_pwszFileName, dwWcLength, 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 / sizeof(WCHAR)); // 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) { StringCbCat(m_pwszFileName,dwWcLength,WSZ_UPF); } else { StringCbCat(m_pwszFileName,dwWcLength,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; } // for 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; if (dwPutDataSize < UPF_TOTAL_HEADER_SIZE) { #ifdef DBG_ERROR DbgPrint("ParseUpfHeaders(): size smaller than header %d\n",dwPutDataSize); #endif return ERROR_BFTP_INVALID_PROTOCOL; } 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 DBG_IO DbgPrint("CSCEP_CONNECTION::ParseUpfHeaders(): JpegOffset: %d JpegSize: %d\n",*pdwJpegOffset,*pdwJpegSize); #endif #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; }