Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

1771 lines
45 KiB

//+-------------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (C) Microsoft Corporation, 1997 - 1999
//
// File: obex.cxx
//
//--------------------------------------------------------------------------
#include "precomp.h"
#include "iso8601.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
SzCpyW( wszNameOnly, wszObj );
StripPathW( wszNameOnly );
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 )
{
// strip off all but the last directory
SzCpyW( wsz, wszPath );
if( wsz[CchWsz(wsz)-1] == cBACKSLASH )
wsz[CchWsz(wsz)-1] = (WCHAR)cNIL;
StripPathW( wsz );
}
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 );
}