|
|
/******************************************************************************
Copyright (c) 2000 Microsoft Corporation
Module Name: Server.cpp
Abstract: This file contains the implementation of the MPCServer class, that controls the overall interaction between client and server.
Revision History: Davide Massarenti (Dmassare) 04/20/99 created
******************************************************************************/
#include "stdafx.h"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
MPCServer::MPCServer( /*[in]*/ MPCHttpContext* hcCallback, /*[in]*/ LPCWSTR szURL, /*[in]*/ LPCWSTR szUser ) : m_SelfCOM ( this ), m_crClientRequest ( 0 ), m_srServerResponse( UPLOAD_LIBRARY_PROTOCOL_VERSION_SRV ) // Prepare default response protocol.
{ __ULT_FUNC_ENTRY("MPCServer::MPCServer");
bool fFound;
m_szURL = SAFEWSTR( szURL ); // MPC::wstring m_szURL;
m_szUser = SAFEWSTR( szUser ); // MPC::wstring m_szUser;
m_isapiInstance = NULL; // CISAPIinstance* m_isapiInstance;
m_flLogHandle = NULL; // MPC::FileLog* m_flLogHandle;
//
m_hcCallback = hcCallback; // MPCHttpContext* m_hcCallback;
m_mpccClient = NULL; // MPCClient* m_mpccClient;
//
// UploadLibrary::ClientRequest m_crClientRequest;
// UploadLibrary::ServerResponse m_srServerResponse;
//
// MPC::Serializer_Memory m_streamResponseData;
// MPCServerCOMWrapper m_SelfCOM;
m_Session = NULL; // MPCSession* m_Session;
m_customProvider = NULL; // IULProvider* m_customProvider;
m_fTerminated = false; // bool m_fTerminated;
if(SUCCEEDED(::Config_GetInstance( m_szURL, m_isapiInstance, fFound ))) { if(fFound) { m_isapiInstance->get_LogHandle( m_flLogHandle ); } } }
MPCServer::~MPCServer() { __ULT_FUNC_ENTRY("MPCServer::~MPCServer");
ReleaseClient(); }
IULServer* MPCServer::COM() { return &m_SelfCOM; }
//////////////////////////////////////////////////////////////////////
// Methods.
//////////////////////////////////////////////////////////////////////
void MPCServer::getURL ( MPC::wstring& szURL ) { szURL = m_szURL ; } void MPCServer::getUser( MPC::wstring& szUser ) { szUser = m_szUser; }
CISAPIinstance* MPCServer::getInstance() { return m_isapiInstance; } MPC::FileLog* MPCServer::getFileLog () { return m_flLogHandle ; }
//////////////////////////////////////////////////////////////////////
HRESULT MPCServer::Process( BOOL& fKeepAlive ) { __ULT_FUNC_ENTRY("MPCServer::Process");
MPC::Serializer& streamConn = MPCSerializerHttp( m_hcCallback ); HRESULT hr;
m_fKeepAlive = TRUE;
try { #ifdef DEBUG
if(m_hcCallback->m_Debug_FIXED_POINTER_ERROR) { m_srServerResponse.dwPosition = m_hcCallback->m_Debug_FIXED_POINTER_ERROR_pos;
SetResponse( UploadLibrary::UL_RESPONSE_SKIPPED, TRUE ); __MPC_SET_ERROR_AND_EXIT(hr, S_OK); } #endif
//
// Enforce maximum request size.
//
{ DWORD dwMaximumPacketSize; DWORD dwCount;
__MPC_EXIT_IF_METHOD_FAILS(hr, ::Config_GetMaximumPacketSize( m_szURL, dwMaximumPacketSize ));
__MPC_EXIT_IF_METHOD_FAILS(hr, m_hcCallback->GetRequestSize( dwCount ));
if(dwCount > dwMaximumPacketSize) { WCHAR rgSize[16]; swprintf( rgSize, L"%d", dwCount );
(void)g_NTEvents.LogEvent( EVENTLOG_WARNING_TYPE, PCHUL_WARN_PACKET_SIZE, m_szURL.c_str(), // %1 = SERVER
rgSize , // %2 = SIZE
NULL );
if(m_flLogHandle) { m_flLogHandle->LogRecord( L"ERROR | Received a packet too large: %ld, limit %ld", dwCount, dwMaximumPacketSize ); }
SetResponse( UploadLibrary::UL_RESPONSE_BAD_REQUEST ); __MPC_SET_ERROR_AND_EXIT(hr, S_OK); } }
//
// Read request.
//
__MPC_EXIT_IF_METHOD_FAILS(hr, streamConn >> m_crClientRequest);
if(m_srServerResponse.MatchVersion( m_crClientRequest ) == false) { if(m_flLogHandle) { m_flLogHandle->LogRecord( L"ERROR | Received an invalid packet: SIG:%08lx VER:%08lx", m_crClientRequest.rhProlog.dwSignature, m_crClientRequest.rhProlog.dwVersion ); }
SetResponse( UploadLibrary::UL_RESPONSE_BAD_REQUEST ); __MPC_SET_ERROR_AND_EXIT(hr, S_OK); }
if(FAILED(hr = GrabClient())) { //
// If another process is handling the file, reply with warning BUSY.
//
if(hr == HRESULT_FROM_WIN32( ERROR_SHARING_VIOLATION )) { SetResponse( UploadLibrary::UL_RESPONSE_BUSY );
hr = S_OK; }
__ULT_FUNC_LEAVE; }
if(m_crClientRequest.dwCommand == UploadLibrary::UL_COMMAND_OPENSESSION) { hr = HandleCommand_OpenSession ( streamConn ); } else if(m_crClientRequest.dwCommand == UploadLibrary::UL_COMMAND_WRITESESSION) { hr = HandleCommand_WriteSession( streamConn ); } else { SetResponse( UploadLibrary::UL_RESPONSE_BAD_REQUEST ); } } catch(...) { __ULT_TRACE_ERROR( UPLOADLIBID, "Upload Server raised an exception. Gracefully exiting..." );
MPC::wstring szID;
if(m_mpccClient) { (void)m_mpccClient->FormatID( szID ); } else { szID = L"<UNKNOWN>"; }
(void)g_NTEvents.LogEvent( EVENTLOG_ERROR_TYPE, PCHUL_ERR_EXCEPTION, m_szURL.c_str(), // %1 = SERVER
szID .c_str(), // %2 = CLIENT
NULL );
//
// Something ugly happened, reply with SERVER_BUSY...
//
SetResponse( UploadLibrary::UL_RESPONSE_BUSY ); }
__ULT_FUNC_CLEANUP;
if(hr != S_FALSE && hr != E_PENDING ) { MPC::Serializer_Memory streamRes;
streamRes << m_srServerResponse;
m_hcCallback->Write( streamRes .GetData(), streamRes .GetSize() ); m_hcCallback->Write( m_streamResponseData.GetData(), m_streamResponseData.GetSize() ); }
//
// Never return a real failure!
//
if(hr != E_PENDING) hr = S_OK;
ReleaseClient();
fKeepAlive = m_fKeepAlive;
__ULT_FUNC_EXIT(hr); }
//////////////////////////////////////////////////////////////////////
// Helpers.
//////////////////////////////////////////////////////////////////////
HRESULT MPCServer::GrabClient() { __ULT_FUNC_ENTRY("MPCServer::GrabClient");
HRESULT hr;
if(m_mpccClient) { if(*m_mpccClient == m_crClientRequest.sigClient) { //
// It's for the same client, dont' do anything...
//
__MPC_SET_ERROR_AND_EXIT(hr, S_OK); }
__MPC_EXIT_IF_METHOD_FAILS(hr, ReleaseClient()); }
//
// Get instance's settings and create client object.
//
m_mpccClient = new MPCClient( this, m_crClientRequest.sigClient );
//
// Check authenticity of ID.
//
if(m_mpccClient->CheckSignature() == false) { SetResponse( UploadLibrary::UL_RESPONSE_DENIED ); __MPC_SET_ERROR_AND_EXIT(hr, S_OK); }
if(FAILED(hr = m_mpccClient->InitFromDisk( true ))) { if(hr == HRESULT_FROM_WIN32( ERROR_DISK_FULL )) { SetResponse( UploadLibrary::UL_RESPONSE_QUOTA_EXCEEDED );
hr = S_OK; }
__ULT_FUNC_LEAVE; }
hr = S_OK;
__ULT_FUNC_CLEANUP;
__ULT_FUNC_EXIT(hr); }
HRESULT MPCServer::ReleaseClient() { __ULT_FUNC_ENTRY("MPCServer::ReleaseClient");
HRESULT hr;
(void)CustomProvider_Release();
if(m_mpccClient) { hr = m_mpccClient->SyncToDisk();
delete m_mpccClient; m_mpccClient = NULL;
if(FAILED(hr)) __ULT_FUNC_LEAVE; }
hr = S_OK;
__ULT_FUNC_CLEANUP;
__ULT_FUNC_EXIT(hr); }
/////////////////////////////////////////////////////////////////////////////
HRESULT MPCServer::HandleCommand_OpenSession( /*[in] */ MPC::Serializer& streamConn ) { __ULT_FUNC_ENTRY("MPCServer::HandleCommand_OpenSession");
UploadLibrary::ClientRequest_OpenSession crosReq( 0 ); MPCClient::Iter it; HRESULT hr; bool fServerBusy; bool fAccessDenied; bool fExceeded;
crosReq.crHeader = m_crClientRequest;
__MPC_EXIT_IF_METHOD_FAILS(hr, streamConn >> crosReq );
#ifdef DEBUG
if(m_hcCallback->m_Debug_NO_RESPONSE_TO_OPEN) { __MPC_SET_ERROR_AND_EXIT(hr, S_FALSE); }
if(m_hcCallback->m_Debug_RESPONSE_TO_OPEN) { m_srServerResponse.dwPosition = m_hcCallback->m_Debug_RESPONSE_TO_OPEN_position; m_srServerResponse.rhProlog.dwVersion = m_hcCallback->m_Debug_RESPONSE_TO_OPEN_protocol;
SetResponse( m_hcCallback->m_Debug_RESPONSE_TO_OPEN_response ); __MPC_SET_ERROR_AND_EXIT(hr, S_OK); } #endif
//
// Reject any request whose length is zero.
//
if(crosReq.dwSize == 0 || crosReq.dwSizeOriginal == 0 ) { SetResponse( UploadLibrary::UL_RESPONSE_BAD_REQUEST ); __MPC_SET_ERROR_AND_EXIT(hr, S_OK); }
if(m_mpccClient->Find( crosReq.szJobID, it )) { if(it->get_Committed() == true) { if(it->MatchRequest( crosReq ) == true) { SetResponse( UploadLibrary::UL_RESPONSE_COMMITTED, TRUE ); } else { SetResponse( UploadLibrary::UL_RESPONSE_EXISTS ); } } else { SetResponse( UploadLibrary::UL_RESPONSE_SKIPPED, TRUE ); } } else { bool fPassed;
if(m_flLogHandle) { m_flLogHandle->LogRecord( L"PROGRESS | Created new session: '%s' (%s)", crosReq.szJobID.c_str(), crosReq.szProviderID.c_str() ); }
it = m_mpccClient->NewSession( crosReq );
if(SUCCEEDED(hr = it->Validate( false, fPassed )) && fPassed) { if(SUCCEEDED(hr = m_mpccClient->CheckQuotas( *it, fServerBusy, fAccessDenied, fExceeded ))) { if(fServerBusy == true) { SetResponse( UploadLibrary::UL_RESPONSE_BUSY ); } else if(fAccessDenied == true) { SetResponse( UploadLibrary::UL_RESPONSE_DENIED ); } else if(fExceeded == true) { SetResponse( UploadLibrary::UL_RESPONSE_QUOTA_EXCEEDED ); } else { SetResponse( UploadLibrary::UL_RESPONSE_SUCCESS, TRUE ); } } }
if(FAILED(hr) || fPassed == false || fExceeded == true ) { m_mpccClient->Erase( it );
__MPC_SET_ERROR_AND_EXIT(hr, S_OK); } }
__MPC_EXIT_IF_METHOD_FAILS(hr, CustomProvider_Create( *it ));
__MPC_EXIT_IF_METHOD_FAILS(hr, CustomProvider_ValidateClient()); if(m_fTerminated) __MPC_SET_ERROR_AND_EXIT(hr, S_OK);
it->get_CurrentSize( m_srServerResponse.dwPosition ); hr = S_OK;
__ULT_FUNC_CLEANUP;
__ULT_FUNC_EXIT(hr); }
HRESULT MPCServer::HandleCommand_WriteSession( /*[in] */ MPC::Serializer& streamConn ) { __ULT_FUNC_ENTRY("MPCServer::HandleCommand_WriteSession");
HRESULT hr; UploadLibrary::ClientRequest_WriteSession crwsReq( 0 ); MPCClient::Iter it; DWORD dwCurrentSize; DWORD dwTotalSize; bool fServerBusy; bool fAccessDenied; bool fExceeded; bool fAvailable;
crwsReq.crHeader = m_crClientRequest;
__MPC_EXIT_IF_METHOD_FAILS(hr, streamConn >> crwsReq);
#ifdef DEBUG
if(m_hcCallback->m_Debug_NO_RESPONSE_TO_WRITE) { __MPC_SET_ERROR_AND_EXIT(hr, S_FALSE); }
if(m_hcCallback->m_Debug_RESPONSE_TO_WRITE) { m_srServerResponse.dwPosition = m_hcCallback->m_Debug_RESPONSE_TO_WRITE_position; m_srServerResponse.rhProlog.dwVersion = m_hcCallback->m_Debug_RESPONSE_TO_WRITE_protocol;
SetResponse( m_hcCallback->m_Debug_RESPONSE_TO_WRITE_response ); __MPC_SET_ERROR_AND_EXIT(hr, S_OK); } #endif
//
// Session couldn't be found, reply with error NOTACTIVE.
//
if(m_mpccClient->Find( crwsReq.szJobID, it ) == false) { SetResponse( UploadLibrary::UL_RESPONSE_NOTACTIVE ); __MPC_SET_ERROR_AND_EXIT(hr, S_OK); }
__MPC_EXIT_IF_METHOD_FAILS(hr, CustomProvider_Create( *it ));
__MPC_EXIT_IF_METHOD_FAILS(hr, CustomProvider_ValidateClient()); if(m_fTerminated) __MPC_SET_ERROR_AND_EXIT(hr, S_OK);
if(SUCCEEDED(hr = m_mpccClient->CheckQuotas( *it, fServerBusy, fAccessDenied, fExceeded ))) { if(fServerBusy == true) { SetResponse( UploadLibrary::UL_RESPONSE_BUSY ); __MPC_SET_ERROR_AND_EXIT(hr, S_OK); }
if(fAccessDenied == true) { SetResponse( UploadLibrary::UL_RESPONSE_DENIED ); __MPC_SET_ERROR_AND_EXIT(hr, S_OK); }
if(fExceeded == true) { SetResponse( UploadLibrary::UL_RESPONSE_QUOTA_EXCEEDED ); __MPC_SET_ERROR_AND_EXIT(hr, S_OK); } }
//
// Session has already being finished, reply with warning COMMITTED.
//
if(it->get_Committed()) { SetResponse( UploadLibrary::UL_RESPONSE_COMMITTED, TRUE ); __MPC_SET_ERROR_AND_EXIT(hr, S_OK); }
#ifdef DEBUG
if(m_hcCallback->m_Debug_RANDOM_POINTER_ERROR) { double pick = (double)rand() / (double)RAND_MAX;
m_srServerResponse.dwPosition = m_hcCallback->m_Debug_RANDOM_POINTER_ERROR_pos_low + (m_hcCallback->m_Debug_RANDOM_POINTER_ERROR_pos_high - m_hcCallback->m_Debug_RANDOM_POINTER_ERROR_pos_low) * pick;
SetResponse( UploadLibrary::UL_RESPONSE_SKIPPED, TRUE ); __MPC_SET_ERROR_AND_EXIT(hr, S_OK); } #endif
it->get_CurrentSize( dwCurrentSize ); it->get_TotalSize ( dwTotalSize );
//
// If request offset and file size don't match, reply with warning SKIPPED.
//
if(dwCurrentSize != crwsReq.dwOffset) { if(m_flLogHandle) { m_flLogHandle->LogRecord( L"WARN | Resync the client to %ld", dwCurrentSize ); }
m_srServerResponse.dwPosition = dwCurrentSize;
SetResponse( UploadLibrary::UL_RESPONSE_SKIPPED, TRUE ); __MPC_SET_ERROR_AND_EXIT(hr, S_OK); }
//
// Trim request size (don't overwrite past the declared file size).
//
crwsReq.dwSize = min( dwTotalSize - dwCurrentSize, crwsReq.dwSize );
//
// If data is not all available, wait.
//
__MPC_EXIT_IF_METHOD_FAILS(hr, m_hcCallback->CheckDataAvailable( crwsReq.dwSize, fAvailable )); if(fAvailable == false) { __MPC_SET_ERROR_AND_EXIT(hr, E_PENDING); }
if(m_flLogHandle) { m_flLogHandle->LogRecord( L"PROGRESS | Writing chunk: %ld bytes at %ld", crwsReq.dwSize, crwsReq.dwOffset ); }
//
// Try to add the chunk to the file. If it fails due to low free disk space, reply with QUOTA_EXCEEDED.
//
{ MPC::Serializer_Text streamText( streamConn ); MPC::Serializer* pstream = UploadLibrary::SelectStream( streamConn, streamText );
if(FAILED(hr = m_mpccClient->AppendData( *it, *pstream, crwsReq.dwSize ))) { if(hr == HRESULT_FROM_WIN32( ERROR_DISK_FULL )) { SetResponse( UploadLibrary::UL_RESPONSE_QUOTA_EXCEEDED );
hr = S_OK; }
__ULT_FUNC_LEAVE; }
__MPC_EXIT_IF_METHOD_FAILS(hr, CustomProvider_DataAvailable()); if(m_fTerminated) __MPC_SET_ERROR_AND_EXIT(hr, S_OK); }
//
// Check for end of transmission.
//
it->get_CurrentSize( dwCurrentSize ); if(dwCurrentSize >= dwTotalSize) { bool fMatch;
__MPC_EXIT_IF_METHOD_FAILS(hr, it->CompareCRC( fMatch ));
if(fMatch == false) { if(m_flLogHandle) { m_flLogHandle->LogRecord( L"WARN | Wrong CRC, restarting..." ); }
//
// The CRC is wrong, so remove the session completely...
//
(void)it->RemoveFile();
m_mpccClient->Erase( it );
SetResponse( UploadLibrary::UL_RESPONSE_BADCRC ); __MPC_SET_ERROR_AND_EXIT(hr, S_OK); } else { if(m_flLogHandle) { m_flLogHandle->LogRecord( L"PROGRESS | Transfer complete" ); }
__MPC_EXIT_IF_METHOD_FAILS(hr, CustomProvider_TransferComplete()); if(m_fTerminated) __MPC_SET_ERROR_AND_EXIT(hr, S_OK); } } else { SetResponse( UploadLibrary::UL_RESPONSE_SUCCESS, TRUE ); }
it->get_CurrentSize( m_srServerResponse.dwPosition ); hr = S_OK;
__ULT_FUNC_CLEANUP;
__ULT_FUNC_EXIT(hr); }
void MPCServer::SetResponse( /*[in]*/ DWORD fResponse, /*[in]*/ BOOL fKeepAlive ) { m_srServerResponse.fResponse = fResponse; m_fKeepAlive = fKeepAlive; }
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
HRESULT MPCServer::CustomProvider_Create( /*[in]*/ MPCSession& mpcsSession ) { __ULT_FUNC_ENTRY("MPCServer::CustomProvider_Create");
HRESULT hr;
if(m_customProvider == NULL) { CISAPIprovider* isapiProvider; bool fFound;
__MPC_EXIT_IF_METHOD_FAILS(hr, mpcsSession.GetProvider( isapiProvider, fFound )); if(fFound) { MPC::wstring szProviderGUID; CLSID guid;
__MPC_EXIT_IF_METHOD_FAILS(hr, isapiProvider->get_ProviderGUID( szProviderGUID ));
if(szProviderGUID.size() && SUCCEEDED(::CLSIDFromString( (LPOLESTR)szProviderGUID.c_str(), &guid ))) { hr = ::CoCreateInstance( guid, NULL, CLSCTX_INPROC_SERVER, IID_IULProvider, (void**)&m_customProvider );
if(FAILED(hr)) { m_customProvider = NULL; } } } }
m_Session = &mpcsSession; hr = S_OK;
__ULT_FUNC_CLEANUP;
__ULT_FUNC_EXIT(hr); }
HRESULT MPCServer::CustomProvider_ValidateClient() { __ULT_FUNC_ENTRY("MPCServer::CustomProvider_ValidateClient");
HRESULT hr; bool fMatch;
//
// Before doing anything, check client identity.
//
__MPC_EXIT_IF_METHOD_FAILS(hr, m_Session->CheckUser( m_szUser, fMatch )) if(fMatch == false) { SetResponse( UploadLibrary::UL_RESPONSE_NOT_AUTHORIZED ); __MPC_SET_ERROR_AND_EXIT(hr, S_OK); }
if(m_customProvider) { hr = m_customProvider->ValidateClient( COM(), m_Session->COM() );
if(FAILED(hr) && hr != E_NOTIMPL) __ULT_FUNC_LEAVE; }
hr = S_OK;
__ULT_FUNC_CLEANUP;
__ULT_FUNC_EXIT(hr); }
HRESULT MPCServer::CustomProvider_DataAvailable() { __ULT_FUNC_ENTRY("MPCServer::CustomProvider_DataAvailable");
HRESULT hr;
if(m_customProvider) { hr = m_customProvider->DataAvailable( COM(), m_Session->COM() );
if(FAILED(hr) && hr != E_NOTIMPL) __ULT_FUNC_LEAVE; }
hr = S_OK;
__ULT_FUNC_CLEANUP;
__ULT_FUNC_EXIT(hr); }
HRESULT MPCServer::CustomProvider_TransferComplete() { __ULT_FUNC_ENTRY("MPCServer::CustomProvider_TransferComplete");
HRESULT hr;
//
// Set the commit flag, but only move the file if we don't have a custom provider.
//
if(FAILED(hr = m_Session->put_Committed( true, m_customProvider ? false : true ))) { if(hr == HRESULT_FROM_WIN32( ERROR_DISK_FULL )) { SetResponse( UploadLibrary::UL_RESPONSE_QUOTA_EXCEEDED );
hr = S_OK; }
__ULT_FUNC_LEAVE; }
SetResponse( UploadLibrary::UL_RESPONSE_COMMITTED, TRUE );
if(m_customProvider) { hr = m_customProvider->TransferComplete( COM(), m_Session->COM() );
if(FAILED(hr) && hr != E_NOTIMPL) __ULT_FUNC_LEAVE; }
hr = S_OK;
__ULT_FUNC_CLEANUP;
__ULT_FUNC_EXIT(hr); }
HRESULT MPCServer::CustomProvider_SetResponse( /*[in]*/ IStream* data ) { __ULT_FUNC_ENTRY("MPCServer::CustomProvider_SetResponse");
HRESULT hr;
//
// Set the commit flag, but only move the file if we don't have a custom provider.
//
if(FAILED(hr = m_Session->put_Committed( true, m_customProvider ? false : true ))) { if(hr == HRESULT_FROM_WIN32( ERROR_DISK_FULL )) { SetResponse( UploadLibrary::UL_RESPONSE_QUOTA_EXCEEDED );
hr = S_OK; }
__ULT_FUNC_LEAVE; }
SetResponse( UploadLibrary::UL_RESPONSE_COMMITTED, TRUE );
if(data) { BYTE buf[512]; DWORD dwRead;
while(1) { __MPC_EXIT_IF_METHOD_FAILS(hr, data->Read( buf, sizeof(buf), &dwRead ));
if(dwRead == 0) break;
__MPC_EXIT_IF_METHOD_FAILS(hr, m_streamResponseData.write( buf, dwRead )); } }
hr = S_OK;
__ULT_FUNC_CLEANUP;
__ULT_FUNC_EXIT(hr); }
HRESULT MPCServer::CustomProvider_Release() { __ULT_FUNC_ENTRY("MPCServer::CustomProvider_Release");
HRESULT hr;
if(m_customProvider) { if(m_fTerminated) { if(m_Session) m_Session->RemoveFile(); }
m_customProvider->Release(); m_customProvider = NULL; }
m_Session = NULL;
hr = S_OK;
__ULT_FUNC_EXIT(hr); }
|