You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1780 lines
47 KiB
1780 lines
47 KiB
//+-------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
//
|
|
// Copyright (C) Microsoft Corporation, 1997 - 1999
|
|
//
|
|
// File: obex.cxx
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
#include "precomp.h"
|
|
#include "iso8601.h"
|
|
#include <irmonftp.h>
|
|
|
|
#define OBEX_VERSION 0x10
|
|
|
|
#define OBEX_OPCODE_FINALBIT 0x80
|
|
#define OBEX_OPCODE_CONNECT ( 0x00 | OBEX_OPCODE_FINALBIT )
|
|
#define OBEX_OPCODE_DISCONNECT ( 0x01 | OBEX_OPCODE_FINALBIT )
|
|
#define OBEX_OPCODE_PUT 0x02
|
|
#define OBEX_OPCODE_PUT_FINAL ( 0x02 | OBEX_OPCODE_FINALBIT )
|
|
#define OBEX_OPCODE_SETPATH ( 0x05 | OBEX_OPCODE_FINALBIT )
|
|
#define OBEX_OPCODE_ABORT 0xFF
|
|
#define OBEX_OPCODE_LEN 1
|
|
#define OBEX_OPCODE_VALID 1
|
|
#define OBEX_OPCODE_NOTIMP 0
|
|
#define OBEX_OPCODE_INVALID -1
|
|
|
|
#define OBEX_REPLY_CONTINUE ( 0x10 | OBEX_OPCODE_FINALBIT )
|
|
#define OBEX_REPLY_SUCCESS ( 0x20 | OBEX_OPCODE_FINALBIT )
|
|
#define OBEX_REPLY_FAIL_BADREQUEST ( 0x40 | OBEX_OPCODE_FINALBIT )
|
|
#define OBEX_REPLY_FAIL_FORBIDDEN ( 0x43 | OBEX_OPCODE_FINALBIT )
|
|
#define OBEX_REPLY_FAIL_NOTFOUND ( 0x44 | OBEX_OPCODE_FINALBIT )
|
|
#define OBEX_REPLY_FAIL_TOOBIG ( 0x4D | OBEX_OPCODE_FINALBIT )
|
|
#define OBEX_REPLY_FAIL_NOTIMP ( 0x61 | OBEX_OPCODE_FINALBIT )
|
|
#define OBEX_REPLY_FAIL_UNAVAILABLE ( 0x63 | OBEX_OPCODE_FINALBIT )
|
|
|
|
#define CONNECT_PKTLEN 0x0007
|
|
#define CONNECT_FLAGS 0x00
|
|
|
|
#define DISCONNECT_PKTLEN 0x0003
|
|
|
|
#define ABORT_PKTLEN 0x0008
|
|
|
|
#define SETPATH_FLAGS 0x00
|
|
#define SETPATH_CONSTANTS 0x00
|
|
#define SETPATH_UPALEVEL 0x01
|
|
|
|
#define REPLY_PKTLEN 0x0003
|
|
|
|
#define REPLY_TIMEOUT TIMEOUT_INFINITE
|
|
#define CONNECT_TIMEOUT 60000
|
|
#define SETPATH_TIMEOUT 60000
|
|
#define DISCONN_TIMEOUT 15000
|
|
#define MAX_UPDATE_DELAY 5000
|
|
|
|
|
|
#define dwBUFFER_INC 512L
|
|
#define MIN_PACKET_SIZE 3 // 1 (opcode/status) + 2 (packet len)
|
|
|
|
|
|
//#ifdef DBG
|
|
#if 1
|
|
LPSTR _szState[] = { "IDLE", "CONNECTED", "FILE" };
|
|
#endif
|
|
|
|
UUID DIALECT_ID_NT5 = /* b9c7fd98-e5f8-11d1-bfce-0000f8753890 */
|
|
{
|
|
0xb9c7fd98,
|
|
0xe5f8,
|
|
0x11d1,
|
|
{0xbf, 0xce, 0x00, 0x00, 0xf8, 0x75, 0x38, 0x90}
|
|
};
|
|
|
|
|
|
|
|
BOOL FILE_TRANSFER::Obex_Init( VOID )
|
|
{
|
|
//
|
|
// initialize connection data structures
|
|
//
|
|
FillMemory( &_dataRecv, sizeof(_dataRecv), 0 );
|
|
|
|
_SetState( &_dataRecv, osIDLE );
|
|
|
|
_dataRecv.lpStore = Store_New( cbSTORE_SIZE_RECV );
|
|
if( !_dataRecv.lpStore )
|
|
goto lErr;
|
|
|
|
return TRUE;
|
|
|
|
lErr:
|
|
|
|
if( _dataRecv.lpStore )
|
|
Store_Delete( &_dataRecv.lpStore );
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
VOID FILE_TRANSFER::Obex_Reset( VOID )
|
|
{
|
|
_SetState( &_dataRecv, osIDLE );
|
|
|
|
Store_Empty( _dataRecv.lpStore );
|
|
}
|
|
|
|
|
|
BOOL FILE_TRANSFER::Obex_ReceiveData( XFER_TYPE xferType, LPVOID lpvData, DWORD dwDataSize )
|
|
{
|
|
return Store_AddData( _dataRecv.lpStore, lpvData, dwDataSize );
|
|
}
|
|
|
|
|
|
error_status_t
|
|
FILE_TRANSFER::Obex_Connect( __int64 dwTotalSize )
|
|
{
|
|
error_status_t status = ERROR_NOT_ENOUGH_MEMORY;
|
|
|
|
LPSTORE lpStore = Store_New( CONNECT_PKTLEN+30 );
|
|
|
|
DbgLog2( SEV_FUNCTION, "Obex_Connect(0x%x): dwTotalSize: %d",
|
|
OBEX_OPCODE_CONNECT, (ULONG) dwTotalSize );
|
|
|
|
ExitOnFalse( _dataRecv.state == osIDLE );
|
|
|
|
ExitOnNull( lpStore );
|
|
|
|
if (_dialect == dialNt5)
|
|
{
|
|
ExitOnFalse( Store_AddData1Byte(lpStore, OBEX_OPCODE_CONNECT) );
|
|
ExitOnFalse( Store_AddData2Byte(lpStore, CONNECT_PKTLEN+5) );
|
|
ExitOnFalse( Store_AddData1Byte(lpStore, OBEX_VERSION) );
|
|
ExitOnFalse( Store_AddData1Byte(lpStore, CONNECT_FLAGS) );
|
|
ExitOnFalse( Store_AddData2Byte(lpStore, CONNECT_MAXPKT) );
|
|
ExitOnFalse( Store_AddData1Byte(lpStore, OBEX_PARAM_LENGTH) );
|
|
ExitOnFalse( Store_AddData4Byte(lpStore, (ULONG) dwTotalSize) );
|
|
|
|
ExitOnFalse( Store_AddData1Byte(lpStore, OBEX_PARAM_WHO) );
|
|
ExitOnFalse( Store_AddData2Byte(lpStore, sizeof(UUID)+3) );
|
|
ExitOnFalse( Store_AddDataUuid(lpStore, &DIALECT_ID_NT5) );
|
|
ExitOnFalse( _PokePacketSizeIntoStore(lpStore) );
|
|
|
|
DbgLog1( SEV_INFO, "Connect (NT5) Packet Size: %d",
|
|
Store_GetDataUsed( lpStore ) );
|
|
|
|
status = _Request( lpStore, OBEX_REPLY_SUCCESS );
|
|
ExitOnErr( status );
|
|
|
|
status = _WaitForReply( CONNECT_TIMEOUT, OBEX_REPLY_SUCCESS );
|
|
}
|
|
else
|
|
{
|
|
DbgLog( SEV_INFO, "trying with Win9x Connect" );
|
|
|
|
ExitOnFalse( Store_AddData1Byte(lpStore, OBEX_OPCODE_CONNECT) );
|
|
ExitOnFalse( Store_AddData2Byte(lpStore, CONNECT_PKTLEN+5) );
|
|
ExitOnFalse( Store_AddData1Byte(lpStore, OBEX_VERSION) );
|
|
ExitOnFalse( Store_AddData1Byte(lpStore, CONNECT_FLAGS) );
|
|
ExitOnFalse( Store_AddData2Byte(lpStore, CONNECT_MAXPKT) );
|
|
ExitOnFalse( Store_AddData1Byte(lpStore, OBEX_PARAM_LENGTH) );
|
|
ExitOnFalse( Store_AddData4Byte(lpStore, (ULONG) dwTotalSize) );
|
|
|
|
status = _Request( lpStore, OBEX_REPLY_SUCCESS );
|
|
ExitOnErr( status );
|
|
|
|
status = _WaitForReply( CONNECT_TIMEOUT, OBEX_REPLY_SUCCESS );
|
|
}
|
|
|
|
DbgLog1(SEV_INFO, "wait result was %d", status);
|
|
|
|
if (ERROR_TIMEOUT == status)
|
|
{
|
|
DbgLog( SEV_FUNCTION, "Obex_Connect: no connect reply; assuming Win95 [%d]" );
|
|
|
|
// Win95 doesn't reply;
|
|
// manually fill in the values we would
|
|
// have gotten in a connect response
|
|
_dataRecv.b1Version = 0x10; // assume the lowest version, Obex 1.0
|
|
_dataRecv.b1Flags = 0;
|
|
_dataRecv.b2MaxPacket = 2000;
|
|
status = 0;
|
|
|
|
_dialect = dialWin95;
|
|
}
|
|
|
|
ExitOnErr( status );
|
|
|
|
_SetState( &_dataRecv, osCONN );
|
|
|
|
//
|
|
// See Obex_PutBody for reasoning on the -16...
|
|
// Make sure Acked <= Total; this is just an estimate, anyway.
|
|
//
|
|
|
|
_blockSize = _dataRecv.b2MaxPacket - 16;
|
|
|
|
lExit:
|
|
|
|
Store_Delete( &lpStore );
|
|
|
|
DbgLog1( SEV_FUNCTION, "Obex_Connect leave [%d]", status );
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
error_status_t
|
|
FILE_TRANSFER::Obex_Disconnect(
|
|
error_status_t error
|
|
)
|
|
{
|
|
error_status_t status = ERROR_NOT_ENOUGH_MEMORY;
|
|
LPSTORE lpStore = Store_New( DISCONNECT_PKTLEN );
|
|
|
|
DbgLog( SEV_FUNCTION, "Obex_Disconnect" );
|
|
|
|
// if we are in the middle of sending a file, abort it before disconnecting.
|
|
// this will set the state to osCONN.
|
|
if( _dataRecv.state == osFILE )
|
|
Obex_Abort(error);
|
|
|
|
ExitOnFalse( _dataRecv.state == osCONN );
|
|
|
|
ExitOnNull( lpStore );
|
|
|
|
ExitOnFalse( Store_AddData1Byte(lpStore, OBEX_OPCODE_DISCONNECT) );
|
|
ExitOnFalse( Store_AddData2Byte(lpStore, DISCONNECT_PKTLEN) );
|
|
|
|
status = _Request( lpStore, OBEX_REPLY_SUCCESS );
|
|
ExitOnErr( status );
|
|
|
|
status = _WaitForReply( DISCONN_TIMEOUT, OBEX_REPLY_SUCCESS );
|
|
|
|
lExit:
|
|
|
|
_SetState( &_dataRecv, osIDLE );
|
|
|
|
Store_Delete( &lpStore );
|
|
|
|
DbgLog1( SEV_FUNCTION, "Obex_Disconnect leave [%d]", status );
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
error_status_t
|
|
FILE_TRANSFER::Obex_Abort(
|
|
error_status_t error
|
|
)
|
|
{
|
|
error_status_t status = ERROR_NOT_ENOUGH_MEMORY;
|
|
LPSTORE lpStore = Store_New( ABORT_PKTLEN );
|
|
|
|
DbgLog( SEV_FUNCTION, "Obex_Abort" );
|
|
|
|
ExitOnFalse( _dataRecv.state == osFILE );
|
|
|
|
ExitOnNull( lpStore );
|
|
|
|
ExitOnFalse( Store_AddData1Byte(lpStore, OBEX_OPCODE_ABORT) );
|
|
ExitOnFalse( Store_AddData2Byte(lpStore, ABORT_PKTLEN) );
|
|
|
|
if (status && _dialect != dialWin95)
|
|
{
|
|
ExitOnFalse( Store_AddData1Byte(lpStore, PRIVATE_PARAM_WIN32_ERROR) );
|
|
ExitOnFalse( Store_AddData4Byte(lpStore, error) );
|
|
}
|
|
|
|
status = _Request( lpStore, OBEX_REPLY_SUCCESS );
|
|
ExitOnErr( status );
|
|
|
|
lExit:
|
|
|
|
_SetState( &_dataRecv, osCONN );
|
|
|
|
Store_Delete( &lpStore );
|
|
|
|
DbgLog1( SEV_FUNCTION, "Obex_Abort leave [%d]", status );
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
error_status_t
|
|
FILE_TRANSFER::_Put(
|
|
LPWSTR wszObj,
|
|
__int64 dwObjSize,
|
|
FILETIME * pFileTime,
|
|
LPBYTE1 pb1Data,
|
|
BYTE2 b2DataSize,
|
|
BOOL fFinal
|
|
)
|
|
{
|
|
DWORD msg;
|
|
|
|
error_status_t status = ERROR_NOT_ENOUGH_MEMORY;
|
|
BYTE1 b1Opcode = ( fFinal ? OBEX_OPCODE_PUT_FINAL : OBEX_OPCODE_PUT );
|
|
BYTE1 b1BodyParam = ( fFinal ? OBEX_PARAM_BODY_END : OBEX_PARAM_BODY );
|
|
|
|
LPSTORE lpStore = Store_New( _dataRecv.b2MaxPacket );
|
|
|
|
DbgLog1( SEV_FUNCTION, "_Put, fFinal = %s", SzBool(fFinal) );
|
|
|
|
msg = MC_IRXFER_SEND_FAILED;
|
|
|
|
ExitOnFalse( _dataRecv.state == osCONN || _dataRecv.state == osFILE );
|
|
|
|
ExitOnNull( lpStore );
|
|
|
|
ExitOnFalse( Store_AddData1Byte(lpStore, b1Opcode) );
|
|
|
|
// add buffer space for the packet length
|
|
ExitOnFalse( Store_AddData2Byte(lpStore, 0) );
|
|
|
|
if( wszObj )
|
|
{
|
|
WCHAR wszNameOnly[MAX_PATH];
|
|
|
|
//
|
|
// strip the path from the full file name
|
|
//
|
|
StripPath(wszObj, wszNameOnly,sizeof(wszNameOnly)/sizeof(WCHAR) );
|
|
|
|
ExitOnFalse( Store_AddData1Byte(lpStore, OBEX_PARAM_NAME) );
|
|
ExitOnFalse( Store_AddData2Byte(lpStore, 3+CbWsz(wszNameOnly)) );
|
|
ExitOnFalse( Store_AddDataWsz(lpStore, wszNameOnly) );
|
|
ExitOnFalse( Store_AddData1Byte(lpStore, OBEX_PARAM_LENGTH) );
|
|
ExitOnFalse( Store_AddData4Byte(lpStore, (ULONG) dwObjSize) );
|
|
|
|
if (_dialect == dialWin95)
|
|
{
|
|
DWORD UnixTime;
|
|
|
|
if( FileTimeToUnixTime(pFileTime, &UnixTime) )
|
|
{
|
|
ExitOnFalse( Store_AddData1Byte(lpStore, OBEX_PARAM_UNIX_TIME) );
|
|
ExitOnFalse( Store_AddData4Byte(lpStore, UnixTime) );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
char IsoTimeA[1+ISO_TIME_LENGTH];
|
|
WCHAR IsoTimeW[1+ISO_TIME_LENGTH];
|
|
|
|
status = FileTimeToiso8601(pFileTime, IsoTimeA);
|
|
if (status)
|
|
{
|
|
goto lExit;
|
|
}
|
|
|
|
if (0 == MultiByteToWideChar(CP_ACP, 0L, IsoTimeA, -1, IsoTimeW, 1+ISO_TIME_LENGTH))
|
|
{
|
|
status = GetLastError();
|
|
goto lExit;
|
|
}
|
|
|
|
DbgLog2(SEV_INFO, " ISO time: len %d '%S'", 3+CbWsz(IsoTimeW), IsoTimeW);
|
|
|
|
ExitOnFalse( Store_AddData1Byte( lpStore, OBEX_PARAM_ISO_TIME ));
|
|
ExitOnFalse( Store_AddData2Byte( lpStore, 3+CbWsz(IsoTimeW) ));
|
|
ExitOnFalse( Store_AddDataWsz ( lpStore, IsoTimeW ));
|
|
}
|
|
}
|
|
|
|
ExitOnFalse( Store_AddData1Byte(lpStore, b1BodyParam) );
|
|
ExitOnFalse( Store_AddData2Byte(lpStore, b2DataSize+3) );
|
|
ExitOnFalse( Store_AddData(lpStore, pb1Data, b2DataSize ) );
|
|
|
|
DbgLog1(SEV_INFO, " data size %d", b2DataSize);
|
|
|
|
ExitOnFalse( _PokePacketSizeIntoStore(lpStore) );
|
|
|
|
status = _Request( lpStore, 0 );
|
|
ExitOnErr( status );
|
|
|
|
++_blocksSent;
|
|
|
|
msg = MC_IRXFER_SEND_WAIT_FAILED;
|
|
|
|
if (fFinal) {
|
|
//
|
|
// final put
|
|
//
|
|
do {
|
|
|
|
status = _WaitForReply( REPLY_TIMEOUT, OBEX_REPLY_SUCCESS );
|
|
|
|
if (status != ERROR_TIMEOUT) {
|
|
|
|
++_blocksAcked;
|
|
}
|
|
|
|
DbgLog3(SEV_INFO, " blocks %d acks %d status %x",
|
|
_blocksSent, _blocksAcked, status );
|
|
|
|
} while ( !status && _blocksAcked < _blocksSent );
|
|
|
|
} else {
|
|
//
|
|
// non-final put
|
|
//
|
|
#if 0
|
|
if (GetTickCount() - _lastAckTime > MAX_UPDATE_DELAY) {
|
|
#endif
|
|
//
|
|
// it has been more than 5 seconds since last ack
|
|
//
|
|
status = _WaitForReply( REPLY_TIMEOUT, OBEX_REPLY_SUCCESS );
|
|
if (status != ERROR_TIMEOUT)
|
|
{
|
|
++_blocksAcked;
|
|
}
|
|
|
|
DbgLog3(SEV_INFO, " blocks %d acks %d status %x",
|
|
_blocksSent, _blocksAcked, status );
|
|
#if 0
|
|
} else {
|
|
//
|
|
// less than 5 seconds since last ack
|
|
//
|
|
status = _WaitForReply( 0, OBEX_REPLY_CONTINUE );
|
|
if (status == ERROR_TIMEOUT)
|
|
{
|
|
status = 0;
|
|
}
|
|
else
|
|
{
|
|
++_blocksAcked;
|
|
}
|
|
|
|
DbgLog3(SEV_INFO, " blocks %d acks %d status %x",
|
|
_blocksSent, _blocksAcked, status );
|
|
}
|
|
#endif
|
|
}
|
|
ExitOnErr( status );
|
|
|
|
_SetState( &_dataRecv, (fFinal ? osCONN : osFILE) );
|
|
|
|
lExit:
|
|
|
|
if (status)
|
|
{
|
|
ASSERT( msg );
|
|
|
|
DWORD dwEventStatus = 0;
|
|
EVENT_LOG EventLog(WS_EVENT_SOURCE,&dwEventStatus);
|
|
|
|
if (!dwEventStatus)
|
|
{
|
|
ReportFileError( msg, _dataFileRecv.szFileName, status );
|
|
}
|
|
}
|
|
|
|
Store_Delete( &lpStore );
|
|
|
|
DbgLog1( SEV_FUNCTION, "_Put leave [%d]", status );
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
error_status_t
|
|
FILE_TRANSFER::_WaitForReply(
|
|
DWORD dwTimeout,
|
|
BYTE1 b1NeededReply
|
|
)
|
|
{
|
|
BOOL fProcessed;
|
|
long StartTime = GetTickCount();
|
|
long EndTime = StartTime + dwTimeout;
|
|
|
|
error_status_t status = 0;
|
|
error_status_t ExpectedStatus = 0;
|
|
|
|
DbgLog1( SEV_FUNCTION, "_WaitForReply( %d )", dwTimeout );
|
|
|
|
ASSERT( b1NeededReply == OBEX_REPLY_SUCCESS ||
|
|
b1NeededReply == OBEX_REPLY_CONTINUE );
|
|
|
|
if (b1NeededReply == OBEX_REPLY_CONTINUE)
|
|
{
|
|
ExpectedStatus = ERROR_CONTINUE;
|
|
}
|
|
|
|
if (TRUE == Obex_ConsumePackets( xferSEND, &status ))
|
|
{
|
|
return status;
|
|
}
|
|
|
|
if (dwTimeout == TIMEOUT_INFINITE)
|
|
{
|
|
BOOL fProcessedPacket = FALSE;
|
|
|
|
do
|
|
{
|
|
status = Sock_CheckForReply( TIMEOUT_INFINITE );
|
|
if (status == WSAETIMEDOUT)
|
|
{
|
|
DbgLog(SEV_WARNING, " infinite wait returned WSAETIMEDOUT");
|
|
status = 0;
|
|
}
|
|
|
|
if (!status)
|
|
{
|
|
fProcessedPacket = Obex_ConsumePackets( xferSEND, &status );
|
|
}
|
|
}
|
|
while ( !fProcessedPacket && !status );
|
|
}
|
|
else
|
|
{
|
|
BOOL fProcessedPacket = FALSE;
|
|
|
|
DbgLog1(SEV_INFO, " end time = %d", EndTime);
|
|
|
|
do
|
|
{
|
|
status = Sock_CheckForReply( EndTime - GetTickCount() );
|
|
if (status == WSAETIMEDOUT)
|
|
{
|
|
status = ERROR_TIMEOUT;
|
|
}
|
|
|
|
if (!status)
|
|
{
|
|
fProcessedPacket = Obex_ConsumePackets( xferSEND, &status );
|
|
}
|
|
}
|
|
while ( !fProcessedPacket && !status && (EndTime - (long) GetTickCount() > 0) );
|
|
|
|
if (!fProcessedPacket && !status)
|
|
{
|
|
status = ERROR_TIMEOUT;
|
|
}
|
|
}
|
|
|
|
if ( status == ExpectedStatus )
|
|
{
|
|
status = 0;
|
|
}
|
|
|
|
DbgLog1( SEV_FUNCTION, "_WaitForReply leave [%d]", status );
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
BOOL
|
|
FILE_TRANSFER::Obex_ConsumePackets(
|
|
XFER_TYPE xferType,
|
|
error_status_t * pStatus
|
|
)
|
|
{
|
|
BOOL fRet = FALSE;
|
|
DWORD dwUsed;
|
|
BYTE1 b1Opcode;
|
|
BYTE2 b2Length;
|
|
LPSTORE lpStore = _dataRecv.lpStore ;
|
|
|
|
dwUsed = Store_GetDataUsed( lpStore );
|
|
|
|
DbgLog1( SEV_FUNCTION, "Obex_ConsumePackets: dwUsed: %ld", dwUsed );
|
|
|
|
// Must have at least MIN_PACKET_SIZE bytes to do anything
|
|
if( dwUsed < MIN_PACKET_SIZE )
|
|
{
|
|
DbgLog( SEV_INFO, "not enough bytes");
|
|
goto lExit;
|
|
}
|
|
|
|
// Get status/opcode
|
|
if( !Store_GetData1Byte(lpStore, &b1Opcode) )
|
|
{
|
|
DbgLog( SEV_INFO, " can't get opcode");
|
|
goto lExit;
|
|
}
|
|
|
|
// Get total packet length
|
|
if( !Store_GetData2Byte(lpStore, &b2Length) )
|
|
{
|
|
DbgLog( SEV_INFO, " can't get packet length");
|
|
goto lExit;
|
|
}
|
|
|
|
// We must have all of the packet to continue
|
|
if (dwUsed < b2Length)
|
|
{
|
|
DbgLog2( SEV_INFO, " have: %d of %d",
|
|
dwUsed, b2Length );
|
|
|
|
// Put back the 3 bytes we just read
|
|
Store_SkipData( lpStore, -3 );
|
|
goto lExit;
|
|
}
|
|
|
|
DbgLog2( SEV_INFO, " b1Opcode: 0x%x b2Length: %d",
|
|
b1Opcode, b2Length );
|
|
|
|
// if we're waiting, this must be a response packet
|
|
if( xferType == xferSEND )
|
|
{
|
|
*pStatus = _HandleResponse( b1Opcode, b2Length-3 );
|
|
}
|
|
else
|
|
{
|
|
*pStatus = _HandleRequest( b1Opcode, b2Length-3 ) ;
|
|
}
|
|
|
|
Store_RemoveData( lpStore, b2Length );
|
|
|
|
_lastAckTime = GetTickCount();
|
|
|
|
fRet = TRUE;
|
|
|
|
lExit:
|
|
|
|
DbgLog2( SEV_FUNCTION, "Obex_ConsumePackets leave [%s, %d]", SzBool(fRet), *pStatus );
|
|
|
|
return fRet;
|
|
}
|
|
|
|
|
|
INT FILE_TRANSFER::_ValidOpcode( OBEXSTATE state, BYTE1 b1Opcode )
|
|
{
|
|
switch( b1Opcode )
|
|
{
|
|
case OBEX_OPCODE_CONNECT:
|
|
if( state == osIDLE ) return OBEX_OPCODE_VALID;
|
|
break;
|
|
|
|
case OBEX_OPCODE_DISCONNECT:
|
|
if( state == osCONN || state == osFILE ) return OBEX_OPCODE_VALID;
|
|
break;
|
|
|
|
case OBEX_OPCODE_PUT:
|
|
case OBEX_OPCODE_PUT_FINAL:
|
|
if( state == osCONN || state == osFILE ) return OBEX_OPCODE_VALID;
|
|
break;
|
|
|
|
case OBEX_OPCODE_SETPATH:
|
|
if( state == osCONN ) return OBEX_OPCODE_VALID;
|
|
break;
|
|
|
|
case OBEX_OPCODE_ABORT:
|
|
if( state == osFILE ) return OBEX_OPCODE_VALID;
|
|
break;
|
|
|
|
default:
|
|
return OBEX_OPCODE_NOTIMP;
|
|
}
|
|
|
|
DbgLog1( SEV_FUNCTION, "_ValidOpcode: Invalid opcode: 0x%2.2x", b1Opcode);
|
|
|
|
return OBEX_OPCODE_INVALID;
|
|
}
|
|
|
|
|
|
error_status_t
|
|
FILE_TRANSFER::_HandleResponse(
|
|
BYTE1 b1Opcode,
|
|
BYTE2 b2Length
|
|
)
|
|
{
|
|
error_status_t status = ERROR_NOT_ENOUGH_MEMORY;
|
|
|
|
DbgLog1( SEV_FUNCTION, "_HandleResponse b2Length[0x%x]", b2Length );
|
|
|
|
if( _dataRecv.state == osIDLE )
|
|
{
|
|
BYTE2 RemoteMaxPacket;
|
|
|
|
// this should be a connect response. get the conn data
|
|
//
|
|
ExitOnFalse( Store_GetData1Byte(_dataRecv.lpStore, &_dataRecv.b1Version) );
|
|
DbgLog1( SEV_INFO, "_HandleResponse, b1Version[0x%x]", _dataRecv.b1Version );
|
|
|
|
ExitOnFalse( Store_GetData1Byte(_dataRecv.lpStore, &_dataRecv.b1Flags) );
|
|
DbgLog1( SEV_INFO, "_HandleResponse, b1Flags[0x%x]", _dataRecv.b1Flags );
|
|
|
|
ExitOnFalse( Store_GetData2Byte(_dataRecv.lpStore, &RemoteMaxPacket) );
|
|
DbgLog1( SEV_INFO, "_HandleResponse, b2MaxPacket[0x%x]", RemoteMaxPacket );
|
|
|
|
_dataRecv.b2MaxPacket = min(RemoteMaxPacket, cbSTORE_SIZE_RECV);
|
|
|
|
b2Length -= 4;
|
|
|
|
status = _ParseParams( b1Opcode, b2Length );
|
|
}
|
|
else
|
|
{
|
|
status = _ParseParams( b1Opcode, b2Length );
|
|
if (!status &&
|
|
_dataRecv.state == osFILE &&
|
|
(b1Opcode == OBEX_REPLY_SUCCESS || b1Opcode == OBEX_REPLY_CONTINUE))
|
|
{
|
|
if (_blocksAcked > 1)
|
|
{
|
|
__int64 currentFileBytesAcked = (_blocksAcked-1) * _blockSize;
|
|
if (currentFileBytesAcked > _dataXferRecv.dwFileSize)
|
|
{
|
|
currentFileBytesAcked = _dataXferRecv.dwFileSize;
|
|
}
|
|
|
|
_UpdateSendProgress( rpcBinding,
|
|
_cookie,
|
|
_dataFileRecv.szFileName,
|
|
_dataXferRecv.dwTotalSize,
|
|
_completedFilesSize + currentFileBytesAcked,
|
|
&status
|
|
);
|
|
if (status)
|
|
{
|
|
DbgLog1( SEV_WARNING, "_HandleResponse: UpdateSendProgress returned %d", status );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!status && b1Opcode != OBEX_REPLY_SUCCESS && b1Opcode != OBEX_REPLY_CONTINUE)
|
|
{
|
|
status = ObexStatusToWin32( b1Opcode );
|
|
DbgLog2( SEV_WARNING, "_HandleResponse: mapped OBEX 0x%x to Win32 %d", b1Opcode, status );
|
|
}
|
|
|
|
lExit:
|
|
|
|
return status;
|
|
}
|
|
|
|
#ifdef DBG
|
|
|
|
struct
|
|
{
|
|
BYTE1 opcode;
|
|
char * name;
|
|
}
|
|
OpcodeNames[] =
|
|
{
|
|
{ OBEX_OPCODE_CONNECT, "connect" },
|
|
{ OBEX_OPCODE_DISCONNECT, "disconnect" },
|
|
{ OBEX_OPCODE_PUT, "put" },
|
|
{ OBEX_OPCODE_ABORT, "abort" },
|
|
{ OBEX_OPCODE_SETPATH, "setpath" },
|
|
{ OBEX_OPCODE_PUT_FINAL, "putfinal" }
|
|
};
|
|
|
|
void
|
|
LogOpcode(
|
|
char * prefix,
|
|
BYTE1 opcode,
|
|
BYTE2 length
|
|
)
|
|
{
|
|
unsigned i;
|
|
|
|
for (i=0; i < sizeof(OpcodeNames)/sizeof(OpcodeNames[0]); ++i)
|
|
{
|
|
if (opcode == OpcodeNames[i].opcode)
|
|
{
|
|
DbgLog3( SEV_INFO, "%s: %s length %d", prefix, OpcodeNames[i].name, length );
|
|
return;
|
|
}
|
|
}
|
|
|
|
DbgLog3( SEV_ERROR, "%s: unknown opcode 0x%x, length %d", prefix, opcode, length );
|
|
}
|
|
|
|
#else
|
|
|
|
inline void
|
|
LogOpcode(
|
|
char * prefix,
|
|
BYTE1 opcode,
|
|
BYTE2 length
|
|
)
|
|
{
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
error_status_t
|
|
FILE_TRANSFER::_HandleRequest(
|
|
BYTE1 b1Opcode,
|
|
BYTE2 b2Length
|
|
)
|
|
{
|
|
INT nValid;
|
|
BOOL fRet;
|
|
|
|
LogOpcode( "_HandleRequest", b1Opcode, b2Length );
|
|
|
|
// only accept valid opcodes, otherwise discard the packet and move on
|
|
nValid = _ValidOpcode( _dataRecv.state, b1Opcode );
|
|
|
|
if( nValid == OBEX_OPCODE_INVALID )
|
|
{
|
|
_HandleBadRequest( b1Opcode );
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
else if( nValid == OBEX_OPCODE_NOTIMP )
|
|
{
|
|
_HandleNotImplemented( b1Opcode );
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
// opcode is valid
|
|
|
|
switch ( b1Opcode )
|
|
{
|
|
case OBEX_OPCODE_CONNECT:
|
|
_HandleConnect( b1Opcode, b2Length );
|
|
return 0;
|
|
|
|
case OBEX_OPCODE_DISCONNECT:
|
|
_HandleDisconnect( b1Opcode, b2Length );
|
|
return 0xffffffff;
|
|
|
|
case OBEX_OPCODE_PUT:
|
|
return _HandlePut( b1Opcode, b2Length, FALSE );
|
|
|
|
case OBEX_OPCODE_PUT_FINAL:
|
|
return _HandlePut( b1Opcode, b2Length, TRUE );
|
|
|
|
case OBEX_OPCODE_SETPATH:
|
|
return _HandleSetPath( b1Opcode, b2Length );
|
|
|
|
case OBEX_OPCODE_ABORT:
|
|
_HandleAbort( b1Opcode, b2Length );
|
|
return ERROR_CANCELLED;
|
|
|
|
default:
|
|
ASSERT( 0 && "IRMON: bad OBEX opcode" );
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
BOOL FILE_TRANSFER::_HandleConnect( BYTE1 b1Opcode, BYTE2 b2Length )
|
|
{
|
|
BYTE1 ReplyCode;
|
|
BYTE2 RemoteMaxPacket;
|
|
|
|
error_status_t status = ERROR_NOT_ENOUGH_MEMORY;
|
|
LPSTORE lpStore = 0;
|
|
|
|
DbgLog( SEV_FUNCTION, "_HandleConnect" );
|
|
|
|
ExitOnFalse( Store_GetData1Byte(_dataRecv.lpStore, &_dataRecv.b1Version) );
|
|
b2Length -= 1;
|
|
|
|
ExitOnFalse( Store_GetData1Byte(_dataRecv.lpStore, &_dataRecv.b1Flags) );
|
|
b2Length -= 1;
|
|
|
|
ExitOnFalse( Store_GetData2Byte(_dataRecv.lpStore, &RemoteMaxPacket) );
|
|
b2Length -= 2;
|
|
|
|
_dataRecv.b2MaxPacket = min(RemoteMaxPacket, cbSTORE_SIZE_RECV);
|
|
|
|
lpStore = Store_New( CONNECT_PKTLEN + 30);
|
|
ExitOnNull( lpStore );
|
|
|
|
status = Xfer_ConnStart();
|
|
if (!status)
|
|
{
|
|
Xfer_FileInit();
|
|
}
|
|
|
|
//
|
|
// Parse parms before checking login status so we can send richer error codes
|
|
// to an NT client.
|
|
//
|
|
if (!status)
|
|
{
|
|
status = _ParseParams( b1Opcode, b2Length );
|
|
}
|
|
|
|
if (!status)
|
|
{
|
|
if (!g_fAllowReceives || g_fShutdown)
|
|
{
|
|
// We'd like to return ERROR_NOT_LOGGED_ON but the NT5 client can't handle this error.
|
|
//
|
|
status = ERROR_ACCESS_DENIED;
|
|
}
|
|
}
|
|
|
|
DbgLog1(SEV_INFO, "status = %d", status);
|
|
|
|
ReplyCode = StatusToReplyCode(b1Opcode, status);
|
|
|
|
if (_dialect == dialNt5)
|
|
{
|
|
DbgLog(SEV_INFO, "using NT5 dialect");
|
|
}
|
|
|
|
ExitOnFalse( Store_AddData1Byte(lpStore, ReplyCode) );
|
|
ExitOnFalse( Store_AddData2Byte(lpStore, 0) ); // placeholder for length
|
|
ExitOnFalse( Store_AddData1Byte(lpStore, OBEX_VERSION) );
|
|
ExitOnFalse( Store_AddData1Byte(lpStore, CONNECT_FLAGS) );
|
|
ExitOnFalse( Store_AddData2Byte(lpStore, CONNECT_MAXPKT) );
|
|
|
|
if (status && _dialect == dialNt5)
|
|
{
|
|
ExitOnFalse( Store_AddData1Byte(lpStore, PRIVATE_PARAM_WIN32_ERROR) );
|
|
ExitOnFalse( Store_AddData4Byte(lpStore, status) );
|
|
}
|
|
else if (_dialect == dialNt5)
|
|
{
|
|
ExitOnFalse( Store_AddData1Byte(lpStore, OBEX_PARAM_WHO) );
|
|
ExitOnFalse( Store_AddData2Byte(lpStore, sizeof(UUID)+3) );
|
|
ExitOnFalse( Store_AddDataUuid(lpStore, &DIALECT_ID_NT5) );
|
|
}
|
|
|
|
ExitOnFalse( _PokePacketSizeIntoStore(lpStore) );
|
|
|
|
if (status)
|
|
{
|
|
_Respond( lpStore );
|
|
}
|
|
else
|
|
{
|
|
status = _Respond( lpStore );
|
|
}
|
|
|
|
ExitOnErr( status );
|
|
|
|
_SetState( &_dataRecv, osCONN );
|
|
|
|
status = _ReceiveInProgress(rpcBinding, _DeviceName, &_cookie, FALSE);
|
|
|
|
DbgLog3( status ? SEV_ERROR : SEV_INFO, "ReceiveInProgress [%S] returned %d, cookie %p", _DeviceName, status, (void *) _cookie );
|
|
|
|
ExitOnErr( status );
|
|
|
|
_fInUiReceiveList = TRUE;
|
|
|
|
lExit:
|
|
|
|
if( status )
|
|
Xfer_ConnEnd();
|
|
|
|
if (lpStore)
|
|
{
|
|
Store_Delete( &lpStore );
|
|
}
|
|
|
|
DbgLog2( SEV_FUNCTION, "_HandleConnect leave [%s] %d", SzBool(_dataRecv.state == osCONN), status );
|
|
|
|
return ( _dataRecv.state == osCONN );
|
|
}
|
|
|
|
#if 1
|
|
|
|
error_status_t
|
|
FILE_TRANSFER::ObexStatusToWin32(
|
|
BYTE1 ObexStatus
|
|
)
|
|
{
|
|
switch (ObexStatus)
|
|
{
|
|
case OBEX_REPLY_SUCCESS:
|
|
return 0;
|
|
|
|
case OBEX_REPLY_CONTINUE:
|
|
return ERROR_CONTINUE;
|
|
|
|
case OBEX_REPLY_FAIL_FORBIDDEN:
|
|
return ERROR_ACCESS_DENIED;
|
|
|
|
case OBEX_REPLY_FAIL_TOOBIG:
|
|
return ERROR_DISK_FULL;
|
|
|
|
case OBEX_REPLY_FAIL_UNAVAILABLE:
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
|
|
case OBEX_REPLY_FAIL_BADREQUEST:
|
|
default:
|
|
DbgLog1(SEV_ERROR, "obex error 0x%x", ObexStatus);
|
|
return ERROR_INVALID_DATA;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
BYTE1
|
|
FILE_TRANSFER::StatusToReplyCode(
|
|
BYTE1 b1Opcode,
|
|
DWORD status
|
|
)
|
|
{
|
|
if (status)
|
|
{
|
|
DbgLog1(SEV_ERROR, "win32 error 0x%x", status);
|
|
}
|
|
|
|
switch (status)
|
|
{
|
|
case 0:
|
|
|
|
if (b1Opcode == OBEX_OPCODE_PUT) {
|
|
|
|
return OBEX_REPLY_CONTINUE;
|
|
}
|
|
|
|
return OBEX_REPLY_SUCCESS;
|
|
|
|
case ERROR_NOT_LOGGED_ON:
|
|
case ERROR_ACCESS_DENIED:
|
|
case ERROR_CANCELLED:
|
|
|
|
return OBEX_REPLY_FAIL_FORBIDDEN;
|
|
|
|
case ERROR_DISK_FULL:
|
|
return OBEX_REPLY_FAIL_TOOBIG;
|
|
|
|
case ERROR_INVALID_DATA:
|
|
return OBEX_REPLY_FAIL_BADREQUEST;
|
|
|
|
case ERROR_NOT_ENOUGH_MEMORY:
|
|
return OBEX_REPLY_FAIL_UNAVAILABLE;
|
|
|
|
default:
|
|
DbgLog(SEV_WARNING, "error not mapped");
|
|
return OBEX_REPLY_FAIL_UNAVAILABLE;
|
|
}
|
|
}
|
|
|
|
|
|
BOOL
|
|
FILE_TRANSFER::SendReplyWin32(
|
|
BYTE1 b1Opcode,
|
|
DWORD status
|
|
)
|
|
{
|
|
LPSTORE lpStore;
|
|
|
|
if (status)
|
|
{
|
|
DbgLog1( SEV_WARNING, "sending Win32 error [%d]", status );
|
|
}
|
|
|
|
lpStore = Store_New( REPLY_PKTLEN+5 );
|
|
ExitOnNull( lpStore );
|
|
|
|
ExitOnFalse( Store_AddData1Byte(lpStore, StatusToReplyCode(b1Opcode, status)) );
|
|
ExitOnFalse( Store_AddData2Byte(lpStore, REPLY_PKTLEN) );
|
|
|
|
if (status && _dialect != dialWin95)
|
|
{
|
|
DbgLog(SEV_INFO, "(including WIN32_ERROR parameter)");
|
|
ExitOnFalse( Store_AddData1Byte(lpStore, PRIVATE_PARAM_WIN32_ERROR) );
|
|
ExitOnFalse( Store_AddData4Byte(lpStore, status) );
|
|
}
|
|
|
|
status = _Respond( lpStore );
|
|
|
|
lExit:
|
|
|
|
Store_Delete( &lpStore );
|
|
|
|
if (status)
|
|
{
|
|
Xfer_ConnEnd();
|
|
|
|
_SetState( &_dataRecv, osIDLE );
|
|
|
|
DbgLog1( SEV_FUNCTION, "SendReplyWin32 failed [%d]", status);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
FILE_TRANSFER::SendReplyObex(
|
|
BYTE1 ObexCode
|
|
)
|
|
{
|
|
error_status_t status = ERROR_NOT_ENOUGH_MEMORY;
|
|
LPSTORE lpStore = NULL;
|
|
|
|
if (ObexCode != OBEX_REPLY_SUCCESS)
|
|
{
|
|
DbgLog1( SEV_WARNING, "sending OBEX error [0x%x]", ObexCode );
|
|
}
|
|
|
|
lpStore = Store_New( REPLY_PKTLEN );
|
|
ExitOnNull( lpStore );
|
|
|
|
ExitOnFalse( Store_AddData1Byte(lpStore, ObexCode) );
|
|
ExitOnFalse( Store_AddData2Byte(lpStore, REPLY_PKTLEN) );
|
|
|
|
status = _Respond( lpStore );
|
|
|
|
lExit:
|
|
|
|
Store_Delete( &lpStore );
|
|
|
|
if (status)
|
|
{
|
|
Xfer_ConnEnd();
|
|
|
|
_SetState( &_dataRecv, osIDLE );
|
|
|
|
DbgLog1( SEV_FUNCTION, "SendReplyObex failed [%d]", status);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL FILE_TRANSFER::_HandleDisconnect( BYTE1 b1Opcode, BYTE2 b2Length )
|
|
{
|
|
DbgLog( SEV_FUNCTION, "_HandleDisconnect" );
|
|
|
|
SendReplyObex( OBEX_REPLY_SUCCESS );
|
|
|
|
Xfer_ConnEnd();
|
|
|
|
_SetState( &_dataRecv, osIDLE );
|
|
|
|
HandleClosure( 0 );
|
|
|
|
DbgLog( SEV_FUNCTION, "_HandleDisconnect leave [TRUE]" );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
error_status_t
|
|
FILE_TRANSFER::_HandlePut(
|
|
BYTE1 b1Opcode,
|
|
BYTE2 b2Length,
|
|
BOOL fFinal
|
|
)
|
|
{
|
|
error_status_t status = 0;
|
|
|
|
DbgLog1( SEV_FUNCTION, "_HandlePut%s", fFinal ? " (final)" : "" );
|
|
|
|
status = _ParseParams( b1Opcode, b2Length );
|
|
|
|
// if final put packet, write 0-length data packet to ensure the
|
|
// file is closed (for 0-length files).
|
|
// don't watch the error code - if the file is already closed, it
|
|
// would return FALSE.
|
|
if( fFinal )
|
|
{
|
|
if (!status)
|
|
{
|
|
status = Xfer_FileWriteBody( NULL, 0, TRUE );
|
|
}
|
|
else
|
|
{
|
|
Xfer_FileWriteBody( NULL, 0, TRUE );
|
|
}
|
|
}
|
|
|
|
if (!SendReplyWin32(b1Opcode, status))
|
|
{
|
|
DbgLog1(SEV_WARNING, "_HandlePut leave [%d]", status);
|
|
return status;
|
|
}
|
|
|
|
_SetState( &_dataRecv, (fFinal ? osCONN : osFILE) );
|
|
|
|
DbgLog1( SEV_FUNCTION, "_HandlePut leave [%d]", status );
|
|
|
|
// return ( _dataRecv.state == osCONN );
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
error_status_t
|
|
FILE_TRANSFER::_HandleSetPath(
|
|
BYTE1 b1Opcode,
|
|
BYTE2 b2Length
|
|
)
|
|
{
|
|
error_status_t status = ERROR_NOT_ENOUGH_MEMORY;
|
|
|
|
DbgLog( SEV_FUNCTION, "_HandleSetPath" );
|
|
|
|
// get the flags and constants
|
|
ExitOnFalse( Store_GetData1Byte(_dataRecv.lpStore, &_dataPath.b1Flags) );
|
|
b2Length -= 1;
|
|
|
|
ExitOnFalse( Store_GetData1Byte(_dataRecv.lpStore, &_dataPath.b1Constants) );
|
|
b2Length -= 1;
|
|
|
|
status = 0;
|
|
|
|
// pop up a level
|
|
if( _dataPath.b1Flags & SETPATH_UPALEVEL )
|
|
{
|
|
status = Xfer_SetPath( szPREVDIR );
|
|
}
|
|
|
|
if (!status)
|
|
{
|
|
status = _ParseParams( b1Opcode, b2Length );
|
|
}
|
|
|
|
SendReplyWin32(b1Opcode, status);
|
|
|
|
lExit:
|
|
|
|
DbgLog1( SEV_FUNCTION, "_HandleSetPath leave [%d]", status);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
BOOL
|
|
FILE_TRANSFER::_HandleAbort(
|
|
BYTE1 b1Opcode,
|
|
BYTE2 b2Length )
|
|
{
|
|
BOOL fRet = FALSE;
|
|
LPSTORE lpStore = NULL;
|
|
|
|
DbgLog( SEV_FUNCTION, "_HandleAbort" );
|
|
|
|
Xfer_FileAbort();
|
|
|
|
if (!SendReplyWin32(b1Opcode, 0))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
_SetState( &_dataRecv, osCONN );
|
|
|
|
DbgLog( SEV_FUNCTION, "_HandleAbort leave" );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
VOID FILE_TRANSFER::_SetState( LPDATA_CONN lpDataConn, OBEXSTATE os )
|
|
{
|
|
lpDataConn->state = os;
|
|
|
|
DbgLog2( SEV_INFO, "_SetState[%s], %s", (lpDataConn == &_dataRecv ? "SEND":"RECV"), _szState[os] );
|
|
}
|
|
|
|
|
|
error_status_t
|
|
FILE_TRANSFER::_ParseParams(
|
|
BYTE1 b1Opcode,
|
|
BYTE2 b2Length
|
|
)
|
|
{
|
|
error_status_t status = 0;
|
|
|
|
BOOL fHaveName = FALSE;
|
|
BYTE1 b1Param;
|
|
BYTE2 b2ParamLen;
|
|
LPVOID lpvData;
|
|
PSZ EndOfData = PSZ(Store_GetDataPtr( _dataRecv.lpStore )) + b2Length;
|
|
|
|
DbgLog1( SEV_FUNCTION, "_ParseParams [0x%x]", b1Opcode );
|
|
|
|
// get the data out of the buffer
|
|
while( PSZ(Store_GetDataPtr( _dataRecv.lpStore )) < EndOfData )
|
|
{
|
|
if ( !Store_GetData1Byte(_dataRecv.lpStore, &b1Param))
|
|
{
|
|
status = ERROR_INVALID_DATA;
|
|
goto lExit;
|
|
}
|
|
|
|
#ifdef DBG
|
|
Store_DumpParameter( _dataRecv.lpStore, b1Param );
|
|
#endif
|
|
|
|
switch( b1Param )
|
|
{
|
|
case OBEX_PARAM_NAME:
|
|
{
|
|
WCHAR wszName[MAX_PATH];
|
|
|
|
if ( !Store_GetData2Byte(_dataRecv.lpStore, &b2ParamLen) )
|
|
{
|
|
status = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto lExit;
|
|
}
|
|
|
|
if (b2ParamLen >= MAX_PATH)
|
|
{
|
|
status = ERROR_INVALID_DATA;
|
|
goto lExit;
|
|
}
|
|
|
|
if ( !Store_GetDataWsz(_dataRecv.lpStore, wszName, sizeof(wszName)) )
|
|
{
|
|
status = ERROR_INVALID_DATA;
|
|
goto lExit;
|
|
}
|
|
|
|
if( CchWsz(wszName) > 0 )
|
|
{
|
|
fHaveName = TRUE;
|
|
|
|
if( b1Opcode == OBEX_OPCODE_SETPATH )
|
|
status = Xfer_SetPath( wszName );
|
|
if( b1Opcode == OBEX_OPCODE_PUT || b1Opcode == OBEX_OPCODE_PUT_FINAL )
|
|
status = Xfer_FileSetName( wszName );
|
|
|
|
if (status)
|
|
{
|
|
goto lExit;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case OBEX_PARAM_LENGTH:
|
|
{
|
|
BYTE4 b4Size;
|
|
|
|
if ( !Store_GetData4Byte(_dataRecv.lpStore, &b4Size) )
|
|
{
|
|
status = ERROR_INVALID_DATA;
|
|
goto lExit;
|
|
}
|
|
|
|
if( b1Opcode == OBEX_OPCODE_CONNECT )
|
|
{
|
|
Xfer_SetSize( b4Size );
|
|
}
|
|
else if( b1Opcode == OBEX_OPCODE_PUT || b1Opcode == OBEX_OPCODE_PUT_FINAL )
|
|
{
|
|
if( !Xfer_FileSetSize(b4Size) )
|
|
{
|
|
DWORD dwEventStatus = 0;
|
|
EVENT_LOG EventLog(WS_EVENT_SOURCE,&dwEventStatus);
|
|
|
|
if (!dwEventStatus)
|
|
{
|
|
EventLog.ReportError(CAT_IRXFER, MC_IRXFER_DISK_FULL);
|
|
}
|
|
|
|
status = ERROR_DISK_FULL;
|
|
goto lExit;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case OBEX_PARAM_UNIX_TIME:
|
|
{
|
|
DWORD UnixTime;
|
|
|
|
if ( !Store_GetData4Byte(_dataRecv.lpStore, &UnixTime) )
|
|
{
|
|
status = ERROR_INVALID_DATA;
|
|
goto lExit;
|
|
}
|
|
|
|
if( b1Opcode == OBEX_OPCODE_PUT || b1Opcode == OBEX_OPCODE_PUT_FINAL )
|
|
{
|
|
if (!UnixTimeToFileTime( UnixTime, &_dataFileRecv.filetime))
|
|
{
|
|
DbgLog1(SEV_ERROR, "UnixTimeToFileTime() failed on 0x%x", UnixTime);
|
|
status = ERROR_INVALID_DATA;
|
|
goto lExit;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case OBEX_PARAM_ISO_TIME:
|
|
{
|
|
char IsoTimeA[1+ISO_TIME_LENGTH];
|
|
WCHAR IsoTimeW[1+ISO_TIME_LENGTH];
|
|
|
|
if ( !Store_GetData2Byte(_dataRecv.lpStore, &b2ParamLen) )
|
|
{
|
|
status = ERROR_INVALID_DATA;
|
|
goto lExit;
|
|
}
|
|
|
|
if (b2ParamLen > 3+(1+ISO_TIME_LENGTH)*sizeof(WCHAR))
|
|
{
|
|
DbgLog2(SEV_ERROR, "ISO time length is %d, string is '%S'", b2ParamLen, Store_GetDataPtr( _dataRecv.lpStore ));
|
|
status = ERROR_INVALID_DATA;
|
|
goto lExit;
|
|
}
|
|
|
|
ExitOnFalse( Store_GetDataWsz( _dataRecv.lpStore, IsoTimeW, sizeof(IsoTimeW)));
|
|
|
|
if( b1Opcode == OBEX_OPCODE_PUT || b1Opcode == OBEX_OPCODE_PUT_FINAL )
|
|
{
|
|
if (0 == WideCharToMultiByte(CP_ACP, 0L, IsoTimeW, -1, IsoTimeA, 1+ISO_TIME_LENGTH, NULL, NULL))
|
|
{
|
|
DbgLog2(SEV_ERROR, "WideCharToMultiByte failed %d on '%S'", GetLastError(), IsoTimeW);
|
|
status = ERROR_INVALID_DATA;
|
|
goto lExit;
|
|
}
|
|
|
|
if (0 != iso8601ToFileTime( IsoTimeA, &_dataFileRecv.filetime, TRUE, TRUE))
|
|
{
|
|
DbgLog1(SEV_ERROR, "iso8601ToFileTime() failed on '%s'", IsoTimeA);
|
|
status = ERROR_INVALID_DATA;
|
|
goto lExit;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case OBEX_PARAM_BODY:
|
|
case OBEX_PARAM_BODY_END:
|
|
{
|
|
if ( !Store_GetData2Byte(_dataRecv.lpStore, &b2ParamLen) )
|
|
{
|
|
status = ERROR_INVALID_DATA;
|
|
goto lExit;
|
|
}
|
|
|
|
lpvData = Store_GetDataPtr( _dataRecv.lpStore );
|
|
|
|
if( b1Opcode == OBEX_OPCODE_PUT || b1Opcode == OBEX_OPCODE_PUT_FINAL )
|
|
{
|
|
status = Xfer_FileWriteBody(lpvData, b2ParamLen-3, FALSE);
|
|
if (status)
|
|
{
|
|
goto lExit;
|
|
}
|
|
}
|
|
|
|
Store_SkipData( _dataRecv.lpStore, b2ParamLen-3 ); // -3 for HI and HI len
|
|
break;
|
|
}
|
|
|
|
case OBEX_PARAM_WHO:
|
|
{
|
|
BYTE2 Left;
|
|
|
|
if ( !Store_GetData2Byte(_dataRecv.lpStore, &b2ParamLen) )
|
|
{
|
|
status = ERROR_INVALID_DATA;
|
|
goto lExit;
|
|
}
|
|
|
|
Left = b2ParamLen;
|
|
|
|
Left -= 3; // -3 for HI and HI len
|
|
|
|
while (Left >= sizeof(UUID))
|
|
{
|
|
UUID dialect;
|
|
|
|
if ( !Store_GetDataUuid(_dataRecv.lpStore, &dialect) )
|
|
{
|
|
status = ERROR_INVALID_DATA;
|
|
goto lExit;
|
|
}
|
|
|
|
Left -= sizeof(UUID);
|
|
|
|
if (0 == memcmp(&dialect, &DIALECT_ID_NT5, sizeof(UUID)))
|
|
{
|
|
_dialect = dialNt5;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Skip over uninterpreted parameter data.
|
|
//
|
|
Store_SkipData( _dataRecv.lpStore, Left );
|
|
break;
|
|
}
|
|
|
|
case PRIVATE_PARAM_WIN32_ERROR:
|
|
{
|
|
if ( !Store_GetData4Byte( _dataRecv.lpStore, &status ))
|
|
{
|
|
status = ERROR_INVALID_DATA;
|
|
goto lExit;
|
|
}
|
|
|
|
if (status)
|
|
{
|
|
goto lExit;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
_SkipHeader( b1Param, _dataRecv.lpStore );
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( b1Opcode == OBEX_OPCODE_SETPATH )
|
|
{
|
|
if( !(_dataPath.b1Flags & SETPATH_UPALEVEL) && !fHaveName )
|
|
{
|
|
// go back to receive folder root
|
|
status = Xfer_SetPath( NULL );
|
|
}
|
|
}
|
|
|
|
lExit:
|
|
|
|
DbgLog1( SEV_FUNCTION, "_ParseParams leave [%d]", status );
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
BYTE2 FILE_TRANSFER::_SkipHeader( BYTE1 b1Param, LPSTORE lpStore )
|
|
{
|
|
BYTE2 b2Ret = 0;
|
|
|
|
// if it's 1 or 4 byte value, read it in and ignore, otherwise
|
|
// read the length and skip ahead the appropriate number of bytes
|
|
|
|
// NOTE: Win95 and Win98 have a bug in this routine. Their test looks like
|
|
//
|
|
// if( b1Param & OBEX_PARAM_1BYTE )
|
|
// {
|
|
// treat as 1-byte;
|
|
// }
|
|
// else if( b1Param & OBEX_PARAM_4BYTE )
|
|
// {
|
|
// treat as 4-byte;
|
|
// }
|
|
// else
|
|
// {
|
|
// treat as 2-byte length-prefixed header;
|
|
// }
|
|
//
|
|
// which, if I read it correctly, treats 1- and 4-byte headers as 1-byte,
|
|
// byte-stream headers as 4-byte, and Unicode headers as Unicode.
|
|
|
|
if( (b1Param & OBEX_PARAM_TYPE_MASK) == OBEX_PARAM_1BYTE )
|
|
{
|
|
BYTE1 b1;
|
|
ExitOnFalse( Store_GetData1Byte(lpStore, &b1) );
|
|
b2Ret = 2;
|
|
}
|
|
else if( (b1Param & OBEX_PARAM_TYPE_MASK) == OBEX_PARAM_4BYTE )
|
|
{
|
|
BYTE4 b4;
|
|
ExitOnFalse( Store_GetData4Byte(lpStore, &b4) );
|
|
b2Ret = 5;
|
|
}
|
|
else
|
|
{
|
|
ExitOnFalse( Store_GetData2Byte(lpStore, &b2Ret) );
|
|
Store_SkipData( lpStore, b2Ret-3 ); // -3 for HI and HI len
|
|
}
|
|
|
|
lExit:
|
|
|
|
return b2Ret;
|
|
}
|
|
|
|
|
|
BOOL FILE_TRANSFER::_PokePacketSizeIntoStore( LPSTORE lpStore )
|
|
{
|
|
// NOTE: cast assumes store sizes to fit into 2 bytes
|
|
BYTE2 b2PktSize = (BYTE2)Store_GetDataUsed( lpStore );
|
|
|
|
// packet size is always the 2nd and 3rd bytes in the buffer
|
|
return Store_PokeData( lpStore, 1, &b2PktSize, sizeof(b2PktSize) );
|
|
}
|
|
|
|
|
|
error_status_t
|
|
FILE_TRANSFER::_Request(
|
|
LPSTORE lpStore,
|
|
BYTE1 b1NeededReply
|
|
)
|
|
{
|
|
error_status_t status = 0;
|
|
LPBYTE1 pb1Data = (LPBYTE1) Store_GetDataPtr(lpStore);
|
|
DWORD dwUsed = Store_GetDataUsed(lpStore);
|
|
DWORD dwOffset = 0;
|
|
|
|
DbgLog( SEV_FUNCTION, "_Request" );
|
|
|
|
status = Sock_Request( pb1Data + dwOffset, dwUsed - dwOffset );
|
|
ExitOnErr( status );
|
|
|
|
#if 0
|
|
status = _WaitForReply( REPLY_TIMEOUT, b1NeededReply );
|
|
if (status == ERROR_TIMEOUT)
|
|
{
|
|
status = 0;
|
|
}
|
|
|
|
ExitOnErr( status );
|
|
#endif
|
|
|
|
lExit:
|
|
|
|
DbgLog1( SEV_FUNCTION, "_Request leave [%d]", status );
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
error_status_t
|
|
FILE_TRANSFER::_Respond(
|
|
LPSTORE lpStore
|
|
)
|
|
{
|
|
error_status_t status = 0;
|
|
LPBYTE1 pb1Data = (LPBYTE1) Store_GetDataPtr(lpStore);
|
|
DWORD dwUsed = Store_GetDataUsed(lpStore);
|
|
DWORD dwOffset = 0;
|
|
|
|
DbgLog( SEV_FUNCTION, "_Respond" );
|
|
|
|
status = Sock_Respond( pb1Data + dwOffset, dwUsed - dwOffset );
|
|
ExitOnErr( status );
|
|
|
|
lExit:
|
|
|
|
DbgLog1( SEV_FUNCTION, "_Respond leave [%d]", status );
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
error_status_t
|
|
FILE_TRANSFER::Obex_PutBegin(
|
|
LPWSTR wszObj,
|
|
__int64 dwObjSize,
|
|
FILETIME * pFileTime
|
|
)
|
|
{
|
|
_blocksSent = 0;
|
|
_blocksAcked = 0;
|
|
_lastAckTime = GetTickCount();
|
|
|
|
return _Put( wszObj, dwObjSize, pFileTime, NULL, 0, dwObjSize == 0 );
|
|
}
|
|
|
|
|
|
error_status_t
|
|
FILE_TRANSFER::Obex_PutBody(
|
|
wchar_t FileName[],
|
|
LPBYTE1 pb1Data,
|
|
BYTE2 b2DataSize,
|
|
BOOL fFinal
|
|
)
|
|
{
|
|
error_status_t status = 0;
|
|
BYTE2 b2Sent = 0;
|
|
BYTE2 b2MaxBodySize = _dataRecv.b2MaxPacket - 16; // 16 = 3 (opcode+pktlen) + 3 (param+len param) + 10 (for good measure)
|
|
|
|
// send in packets no larger than what the receiving side requested
|
|
while( b2Sent < b2DataSize )
|
|
{
|
|
BYTE2 b2SendSize = min( b2DataSize - b2Sent, b2MaxBodySize );
|
|
BOOL fLastPacket = fFinal && (b2Sent + b2SendSize == b2DataSize);
|
|
|
|
DbgLog4(SEV_INFO, " b2DataSize %d, b2Sent %d, b2MaxBodySize %d, b2MaxPacket %d",
|
|
_dataRecv.b2MaxPacket, b2DataSize, b2Sent, b2MaxBodySize );
|
|
|
|
if( _fCancelled )
|
|
{
|
|
Obex_Abort(ERROR_CANCELLED);
|
|
return ERROR_CANCELLED;
|
|
}
|
|
|
|
status = _Put( NULL, 0, 0, pb1Data + b2Sent, b2SendSize, fLastPacket );
|
|
|
|
if( status )
|
|
break;
|
|
|
|
status = 0;
|
|
b2Sent += b2SendSize;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
error_status_t
|
|
FILE_TRANSFER::Obex_SetPath(
|
|
LPWSTR wszPath
|
|
)
|
|
{
|
|
error_status_t status = ERROR_NOT_ENOUGH_MEMORY;
|
|
BOOL fUpALevel = ( !wszPath || CchWsz(wszPath) == 0 );
|
|
BYTE1 b1Flags = SETPATH_FLAGS;
|
|
WCHAR wsz[MAX_PATH];
|
|
LPSTORE lpStore = Store_New( MAX_PATH+10 );
|
|
|
|
DbgLog1( SEV_FUNCTION, "Obex_SetPath '%S'", wszPath );
|
|
|
|
if( !fUpALevel ) {
|
|
|
|
WCHAR TempPath[MAX_PATH];
|
|
|
|
StringCbCopy(TempPath, sizeof(TempPath), wszPath);
|
|
|
|
//
|
|
// strip off all but the last directory
|
|
//
|
|
if ( TempPath[CchWsz(TempPath)-1] == cBACKSLASH ) {
|
|
|
|
TempPath[CchWsz(TempPath)-1] = (WCHAR)cNIL;
|
|
}
|
|
|
|
StripPath( TempPath, wsz, sizeof(wsz)/sizeof(WCHAR) );
|
|
}
|
|
|
|
if ( _dataRecv.state != osCONN )
|
|
{
|
|
status = ERROR_NOT_CONNECTED;
|
|
goto lExit;
|
|
}
|
|
|
|
ExitOnNull( lpStore );
|
|
|
|
ExitOnFalse( Store_AddData1Byte(lpStore, OBEX_OPCODE_SETPATH) );
|
|
|
|
// add buffer space for the packet length
|
|
ExitOnFalse( Store_AddData2Byte(lpStore, 0) );
|
|
|
|
if( fUpALevel )
|
|
b1Flags |= SETPATH_UPALEVEL;
|
|
|
|
ExitOnFalse( Store_AddData1Byte(lpStore, b1Flags) );
|
|
ExitOnFalse( Store_AddData1Byte(lpStore, SETPATH_CONSTANTS) );
|
|
|
|
if( !fUpALevel )
|
|
{
|
|
ExitOnFalse( Store_AddData1Byte(lpStore, OBEX_PARAM_NAME) );
|
|
ExitOnFalse( Store_AddData2Byte(lpStore, 3+CbWsz(wsz)) );
|
|
ExitOnFalse( Store_AddDataWsz(lpStore, wsz) );
|
|
}
|
|
|
|
ExitOnFalse( _PokePacketSizeIntoStore(lpStore) );
|
|
|
|
status = _Request( lpStore, OBEX_REPLY_SUCCESS );
|
|
ExitOnErr( status );
|
|
|
|
status = _WaitForReply( SETPATH_TIMEOUT, OBEX_REPLY_SUCCESS );
|
|
|
|
ExitOnErr( status );
|
|
|
|
status = 0;
|
|
|
|
lExit:
|
|
|
|
Store_Delete( &lpStore );
|
|
|
|
DbgLog1( SEV_FUNCTION, "Obex_SetPath leave [%d]", status );
|
|
|
|
return status;
|
|
}
|
|
|
|
BOOL FILE_TRANSFER::_HandleNotImplemented( BYTE1 b1Opcode )
|
|
{
|
|
return SendReplyObex( OBEX_REPLY_FAIL_NOTIMP );
|
|
}
|
|
|
|
BOOL FILE_TRANSFER::_HandleBadRequest( BYTE1 b1Opcode )
|
|
{
|
|
return SendReplyObex( OBEX_REPLY_FAIL_BADREQUEST );
|
|
}
|