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.
657 lines
14 KiB
657 lines
14 KiB
#include "precomp.h"
|
|
#include "resource.h"
|
|
#include "ConfUtil.h"
|
|
#include "ConfPolicies.h"
|
|
|
|
// SDK includes
|
|
#include "NmEnum.h"
|
|
#include "NmConference.h"
|
|
#include "SDKInternal.h"
|
|
#include "NmMember.h"
|
|
#include "NmChannel.h"
|
|
#include "NmChannelFt.h"
|
|
#include "NmFt.h"
|
|
#include "FtHook.h"
|
|
|
|
|
|
CNmChannelFtObj::CNmChannelFtObj()
|
|
: m_bSentSinkLocalMember(false)
|
|
{
|
|
DBGENTRY(CNmChannelFtObj::CNmChannelFtObj);
|
|
|
|
DBGEXIT(CNmChannelFtObj::CNmChannelFtObj);
|
|
}
|
|
|
|
CNmChannelFtObj::~CNmChannelFtObj()
|
|
{
|
|
DBGENTRY(CNmChannelFtObj::~CNmChannelFtObj);
|
|
|
|
CFt::UnAdvise(this);
|
|
|
|
// Free our Ft objects
|
|
for(int i = 0; i < m_SDKFtObjs.GetSize(); ++i)
|
|
{
|
|
m_SDKFtObjs[i]->Release();
|
|
}
|
|
|
|
DBGEXIT(CNmChannelFtObj::~CNmChannelFtObj);
|
|
}
|
|
|
|
//
|
|
HRESULT CNmChannelFtObj::CreateInstance(CNmConferenceObj* pConfObj, INmChannel** ppChannel)
|
|
{
|
|
DBGENTRY(CNmChannelFtObj::CreateInstance);
|
|
HRESULT hr = S_OK;
|
|
|
|
typedef CNmChannel<CNmChannelFtObj, &IID_INmChannelFt, NMCH_FT> channel_type;
|
|
|
|
channel_type* p = NULL;
|
|
p = new CComObject<channel_type>(NULL);
|
|
|
|
if (p != NULL)
|
|
{
|
|
if(ppChannel)
|
|
{
|
|
p->SetVoid(NULL);
|
|
|
|
hr = p->QueryInterface(IID_INmChannel, reinterpret_cast<void**>(ppChannel));
|
|
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
// We don't have to RefCount this because our lifetime is
|
|
// contained in the CConf's lifetime
|
|
p->m_pConfObj = pConfObj;
|
|
}
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
*ppChannel = NULL;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
hr = E_POINTER;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
delete p;
|
|
}
|
|
|
|
DBGEXIT_HR(CNmChannelFtObj::CreateInstance,hr);
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
/* V A L I D A T E F I L E */
|
|
/*-------------------------------------------------------------------------
|
|
%%Function: ValidateFile
|
|
|
|
Verify that a file is a valid to send (it exists, not a folder, etc.)
|
|
-------------------------------------------------------------------------*/
|
|
HRESULT ValidateFile(LPCTSTR pszFile, DWORD *pdwSizeInBytes)
|
|
{
|
|
WIN32_FIND_DATA findData;
|
|
*pdwSizeInBytes = 0;
|
|
|
|
HRESULT hr = S_OK;
|
|
if (FEmptySz(pszFile) || (MAX_PATH < lstrlen(pszFile)))
|
|
{
|
|
TRACE_OUT(("SendFile: invalid filename"));
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
// get file information
|
|
HANDLE hFind = FindFirstFile(pszFile, &findData);
|
|
if (INVALID_HANDLE_VALUE == hFind)
|
|
{
|
|
TRACE_OUT(("SendFile: Bad Filename [%s]", pszFile));
|
|
return E_INVALIDARG;
|
|
}
|
|
FindClose(hFind);
|
|
if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
|
{
|
|
WARNING_OUT(("SendFile: [%s] is a directory", pszFile));
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
// Check whether we are allowed to send files of this size.
|
|
DWORD dwMaxSendFileSize = ConfPolicies::GetMaxSendFileSize();
|
|
|
|
if (dwMaxSendFileSize) {
|
|
DWORD dwFileSizeInK = (findData.nFileSizeLow >> 10) |
|
|
(findData.nFileSizeHigh << (sizeof(DWORD) * 8 - 10));
|
|
if ((dwFileSizeInK >= dwMaxSendFileSize) ||
|
|
((findData.nFileSizeHigh >> 10) > 0)) {
|
|
return NM_E_FILE_TOO_BIG;
|
|
}
|
|
}
|
|
|
|
*pdwSizeInBytes = findData.nFileSizeLow;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////
|
|
// INmChannelFt methods
|
|
///////////////////////////////////////////////////////////////////////////////////
|
|
STDMETHODIMP CNmChannelFtObj::SendFile(INmFt **ppFt, INmMember *pMember, BSTR bstrFile, ULONG uOptions)
|
|
{
|
|
DBGENTRY(CNmChannelFtObj::SendFile);
|
|
HRESULT hr = S_OK;
|
|
|
|
|
|
LPTSTR szName = NULL;
|
|
BSTR_to_LPTSTR (&szName, bstrFile);
|
|
|
|
LPCTSTR szFileName = szName;
|
|
DWORD dwSizeInBytes = 0;
|
|
hr = ValidateFile(szFileName,&dwSizeInBytes);
|
|
ULONG gccID = 0;
|
|
MBFTEVENTHANDLE hEvent;
|
|
MBFTFILEHANDLE hFile;
|
|
|
|
|
|
// We should never be passed NULL because of the marshalling code...
|
|
ASSERT(ppFt);
|
|
|
|
if(!GetbActive())
|
|
{
|
|
// We are not active yet!
|
|
hr = E_FAIL;
|
|
goto end;
|
|
}
|
|
|
|
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
|
|
if(pMember)
|
|
{
|
|
// Make sure that this member is valid and in the channel
|
|
if(!_MemberInChannel(pMember))
|
|
{
|
|
hr = E_INVALIDARG;
|
|
goto end;
|
|
}
|
|
|
|
hr = pMember->GetID(&gccID);
|
|
if(FAILED(hr)) goto end;
|
|
}
|
|
|
|
hr = CFt::SendFile(szFileName,
|
|
static_cast<T120NodeID>(gccID),
|
|
&hEvent,
|
|
&hFile);
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
hr = CNmFtObj::CreateInstance(
|
|
this,
|
|
hEvent,
|
|
hFile,
|
|
false, // bIsIncoming
|
|
szFileName, // FileName
|
|
dwSizeInBytes, // Size in Bytes of the file
|
|
pMember,
|
|
ppFt
|
|
);
|
|
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
(*ppFt)->AddRef();
|
|
m_SDKFtObjs.Add(*ppFt);
|
|
|
|
Fire_FtUpdate(CONFN_FT_STARTED, *ppFt);
|
|
}
|
|
}
|
|
}
|
|
|
|
end:
|
|
|
|
if(szName)
|
|
{
|
|
delete (szName);
|
|
}
|
|
|
|
DBGEXIT_HR(CNmChannelFtObj::SendFile,hr);
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CNmChannelFtObj::SetReceiveFileDir(BSTR bstrDir)
|
|
{
|
|
DBGENTRY(CNmChannelFtObj::SetReceiveFileDir);
|
|
HRESULT hr = E_INVALIDARG;
|
|
|
|
if(bstrDir)
|
|
{
|
|
|
|
LPTSTR szDir;
|
|
hr = BSTR_to_LPTSTR (&szDir, bstrDir);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if(szDir)
|
|
{
|
|
if(FEnsureDirExists(szDir))
|
|
{
|
|
hr = _ChangeRecDir(szDir);
|
|
}
|
|
else
|
|
{
|
|
hr = E_INVALIDARG;
|
|
}
|
|
}
|
|
delete (szDir);
|
|
}
|
|
}
|
|
|
|
DBGEXIT_HR(CNmChannelFtObj::SetReceiveFileDir,hr);
|
|
return hr;
|
|
}
|
|
|
|
// static
|
|
HRESULT CNmChannelFtObj::_ChangeRecDir(LPTSTR pszRecDir)
|
|
{
|
|
PSTR psz;
|
|
TCHAR szPath[MAX_PATH];
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
RegEntry reFileXfer(FILEXFER_KEY, HKEY_CURRENT_USER);
|
|
|
|
if (NULL == pszRecDir)
|
|
{
|
|
// NULL directory specified - get info from registry or use default
|
|
psz = reFileXfer.GetString(REGVAL_FILEXFER_PATH);
|
|
if (!FEmptySz(psz))
|
|
{
|
|
lstrcpyn(szPath, psz, CCHMAX(szPath));
|
|
}
|
|
else
|
|
{
|
|
TCHAR szInstallDir[MAX_PATH];
|
|
GetInstallDirectory(szInstallDir);
|
|
FLoadString1(IDS_FT_RECDIR_DEFAULT, szPath, szInstallDir);
|
|
}
|
|
|
|
pszRecDir = szPath;
|
|
}
|
|
|
|
psz = pszRecDir;
|
|
|
|
// Remove trailing backslash, if any
|
|
for (; *psz; psz = CharNext(psz))
|
|
{
|
|
if ((_T('\\') == *psz) && (_T('\0') == *CharNext(psz)) )
|
|
{
|
|
*psz = _T('\0');
|
|
break;
|
|
}
|
|
}
|
|
|
|
TRACE_OUT(("ChangeRecDir [%s]", pszRecDir));
|
|
|
|
|
|
if (!FEnsureDirExists(pszRecDir))
|
|
{
|
|
WARNING_OUT(("ChangeRecDir: FT directory is invalid [%s]", pszRecDir));
|
|
hr = E_FAIL;
|
|
}
|
|
else
|
|
{
|
|
// update the registry
|
|
reFileXfer.SetValue(REGVAL_FILEXFER_PATH, pszRecDir);
|
|
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CNmChannelFtObj::GetReceiveFileDir(BSTR *pbstrDir)
|
|
{
|
|
DBGENTRY(CNmChannelFtObj::GetReceiveFileDir);
|
|
HRESULT hr = S_OK;
|
|
|
|
if(pbstrDir)
|
|
{
|
|
RegEntry reFileXfer(FILEXFER_KEY, HKEY_CURRENT_USER);
|
|
*pbstrDir = T2BSTR(reFileXfer.GetString(REGVAL_FILEXFER_PATH));
|
|
if(!*pbstrDir)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_POINTER;
|
|
}
|
|
|
|
DBGEXIT_HR(CNmChannelFtObj::GetReceiveFileDir,hr);
|
|
return hr;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// IInternalChannelObj methods
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
STDMETHODIMP CNmChannelFtObj::GetInternalINmChannel(INmChannel** ppChannel)
|
|
{
|
|
DBGENTRY(CNmChannelFtObj::GetInternalINmChannel);
|
|
HRESULT hr = S_OK;
|
|
|
|
if(ppChannel)
|
|
{
|
|
*ppChannel = NULL;
|
|
}
|
|
else
|
|
{
|
|
hr = E_POINTER;
|
|
}
|
|
|
|
DBGEXIT_HR(CNmChannelFtObj::GetInternalINmChannel,hr);
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CNmChannelFtObj::ChannelRemoved()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
RemoveMembers();
|
|
|
|
CNmConferenceObj* pConfObj = GetConfObj();
|
|
if(pConfObj)
|
|
{
|
|
hr = pConfObj->Fire_ChannelChanged(NM_CHANNEL_REMOVED, com_cast<INmChannel>(GetUnknown()));
|
|
}
|
|
else
|
|
{
|
|
ERROR_OUT(("ChannelRemoved, but no ConfObject"));
|
|
hr = E_UNEXPECTED;
|
|
}
|
|
|
|
m_bSentSinkLocalMember = false;
|
|
|
|
|
|
return hr;
|
|
}
|
|
|
|
void CNmChannelFtObj::_OnActivate(bool bActive)
|
|
{
|
|
bActive ? CFt::Advise(this) : CFt::UnAdvise(this);
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Helpers
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CNmChannelFtObj::Fire_MemberChanged(NM_MEMBER_NOTIFY uNotify, INmMember *pMember)
|
|
{
|
|
DBGENTRY(CNmChannelFtObj::Fire_MemberChanged);
|
|
HRESULT hr = S_OK;
|
|
|
|
/////////////////////////////////////////////////////
|
|
// INmChannelNotify
|
|
/////////////////////////////////////////////////////
|
|
IConnectionPointImpl<CNmChannelFtObj, &IID_INmChannelNotify, CComDynamicUnkArray>* pCP = this;
|
|
for(int i = 0; i < pCP->m_vec.GetSize(); ++i )
|
|
{
|
|
if(NM_MEMBER_ADDED == uNotify)
|
|
{
|
|
if(!m_bSentSinkLocalMember)
|
|
{
|
|
m_bSentSinkLocalMember = true;
|
|
NotifySinksOfLocalMember();
|
|
}
|
|
}
|
|
|
|
INmChannelNotify* pNotify = reinterpret_cast<INmChannelNotify*>(pCP->m_vec.GetAt(i));
|
|
|
|
if(pNotify)
|
|
{
|
|
pNotify->MemberChanged(uNotify, pMember);
|
|
}
|
|
}
|
|
/////////////////////////////////////////////////////
|
|
// INmChannelFtNotify
|
|
/////////////////////////////////////////////////////
|
|
|
|
IConnectionPointImpl<CNmChannelFtObj, &IID_INmChannelFtNotify, CComDynamicUnkArray>* pCP2 = this;
|
|
for(i = 0; i < pCP2->m_vec.GetSize(); ++i )
|
|
{
|
|
if(NM_MEMBER_ADDED == uNotify)
|
|
{
|
|
if(!m_bSentSinkLocalMember)
|
|
{
|
|
m_bSentSinkLocalMember = true;
|
|
NotifySinksOfLocalMember();
|
|
}
|
|
}
|
|
|
|
INmChannelFtNotify* pNotify2 = reinterpret_cast<INmChannelFtNotify*>(pCP2->m_vec.GetAt(i));
|
|
|
|
if(pNotify2)
|
|
{
|
|
pNotify2->MemberChanged(uNotify, pMember);
|
|
}
|
|
}
|
|
|
|
DBGEXIT_HR(CNmChannelFtObj::Fire_MemberChanged,hr);
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CNmChannelFtObj::Fire_FtUpdate(CONFN uNotify, INmFt* pNmFt)
|
|
{
|
|
DBGENTRY(CNmChannelFtObj::Fire_FtUpdate);
|
|
HRESULT hr = S_OK;
|
|
|
|
/////////////////////////////////////////////////////
|
|
// INmChannelFtNotify
|
|
/////////////////////////////////////////////////////
|
|
|
|
IConnectionPointImpl<CNmChannelFtObj, &IID_INmChannelFtNotify, CComDynamicUnkArray>* pCP2 = this;
|
|
for(int i = 0; i < pCP2->m_vec.GetSize(); ++i )
|
|
{
|
|
INmChannelFtNotify* pNotify2 = reinterpret_cast<INmChannelFtNotify*>(pCP2->m_vec.GetAt(i));
|
|
|
|
if(pNotify2)
|
|
{
|
|
pNotify2->FtUpdate(uNotify, pNmFt);
|
|
}
|
|
}
|
|
|
|
DBGEXIT_HR(CNmChannelFtObj::Fire_FtUpdate,hr);
|
|
return hr;
|
|
}
|
|
|
|
void CNmChannelFtObj::_RemoveFt(INmFt* pFt)
|
|
{
|
|
INmFt* pRet = NULL;
|
|
|
|
for( int i = 0; i < m_SDKFtObjs.GetSize(); ++i)
|
|
{
|
|
CComPtr<INmFt> spFt = m_SDKFtObjs[i];
|
|
if(spFt.IsEqualObject(pFt))
|
|
{
|
|
m_SDKFtObjs.RemoveAt(i);
|
|
spFt.p->Release();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
INmMember* CNmChannelFtObj::GetSDKMemberFromInternalMember(INmMember* pInternalMember)
|
|
{
|
|
ASSERT(GetConfObj());
|
|
return GetConfObj()->GetSDKMemberFromInternalMember(pInternalMember);
|
|
}
|
|
|
|
HRESULT CNmChannelFtObj::_IsActive()
|
|
{
|
|
return GetbActive() ? S_OK : S_FALSE;
|
|
}
|
|
|
|
HRESULT CNmChannelFtObj::_SetActive(BOOL bActive)
|
|
{
|
|
if (GetbActive() == bActive)
|
|
return S_FALSE;
|
|
|
|
return E_FAIL;
|
|
}
|
|
|
|
|
|
// IMbftEvent Interface
|
|
STDMETHODIMP CNmChannelFtObj::OnInitializeComplete(void)
|
|
{
|
|
// This is handled by the conference object
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CNmChannelFtObj::OnPeerAdded(MBFT_PEER_INFO *pInfo)
|
|
{
|
|
// This is handled by the conference object
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CNmChannelFtObj::OnPeerRemoved(MBFT_PEER_INFO *pInfo)
|
|
{
|
|
// This is handled by the conference object
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CNmChannelFtObj::OnFileOffer(MBFT_FILE_OFFER *pOffer)
|
|
{
|
|
CNmConferenceObj* pConfObj = GetConfObj();
|
|
|
|
if(pConfObj)
|
|
{
|
|
CComPtr<INmMember> spMember;
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
if(pOffer->NodeID)
|
|
{
|
|
hr = pConfObj->GetMemberFromNodeID(pOffer->NodeID, &spMember);
|
|
}
|
|
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
RegEntry reFileXfer(FILEXFER_KEY, HKEY_CURRENT_USER);
|
|
ASSERT(1 == pOffer->uNumFiles);
|
|
|
|
CFt::AcceptFileOffer( pOffer,
|
|
reFileXfer.GetString(REGVAL_FILEXFER_PATH),
|
|
pOffer->lpFileInfoList[0].szFileName
|
|
);
|
|
|
|
CComPtr<INmFt> spFt;
|
|
|
|
hr = CNmFtObj::CreateInstance(
|
|
this,
|
|
pOffer->hEvent, // hEvent
|
|
0, // hFile is 0 for now...
|
|
true, // bIsIncoming
|
|
pOffer->lpFileInfoList[0].szFileName, // FileName
|
|
pOffer->lpFileInfoList[0].lFileSize, // Size in Bytes of the file
|
|
spMember,
|
|
&spFt
|
|
);
|
|
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
spFt.p->AddRef();
|
|
m_SDKFtObjs.Add(spFt.p);
|
|
|
|
Fire_FtUpdate(CONFN_FT_STARTED, spFt);
|
|
}
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
INmFt* CNmChannelFtObj::_GetFtFromHEvent(MBFTEVENTHANDLE hEvent)
|
|
{
|
|
for(int i = 0; i < m_SDKFtObjs.GetSize(); ++i)
|
|
{
|
|
UINT CurhEvent;
|
|
if(SUCCEEDED(com_cast<IInternalFtObj>(m_SDKFtObjs[i])->GetHEvent(&CurhEvent)) && (hEvent == CurhEvent))
|
|
{
|
|
return m_SDKFtObjs[i];
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CNmChannelFtObj::OnFileProgress(MBFT_FILE_PROGRESS *pProgress)
|
|
{
|
|
DBGENTRY(CNmChannelFtObj::OnFileProgress);
|
|
HRESULT hr = S_OK;
|
|
|
|
INmFt* pFt = _GetFtFromHEvent(pProgress->hEvent);
|
|
if(pFt)
|
|
{
|
|
com_cast<IInternalFtObj>(pFt)->OnFileProgress(pProgress->hFile, pProgress->lFileSize, pProgress->lBytesTransmitted);
|
|
Fire_FtUpdate(CONFN_FT_PROGRESS, pFt);
|
|
}
|
|
|
|
DBGEXIT_HR(CNmChannelFtObj::OnFileProgress,hr);
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CNmChannelFtObj::OnFileEnd(MBFTFILEHANDLE hFile)
|
|
{
|
|
// according to Lon, this is bogus
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
STDMETHODIMP CNmChannelFtObj::OnFileError(MBFT_EVENT_ERROR *pEvent)
|
|
{
|
|
DBGENTRY(CNmChannelFtObj::OnFileError);
|
|
HRESULT hr = S_OK;
|
|
|
|
INmFt* pFt = _GetFtFromHEvent(pEvent->hEvent);
|
|
if(pFt)
|
|
{
|
|
com_cast<IInternalFtObj>(pFt)->OnError();
|
|
}
|
|
|
|
DBGEXIT_HR(CNmChannelFtObj::OnFileError,hr);
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CNmChannelFtObj::OnFileEventEnd(MBFTEVENTHANDLE hEvent)
|
|
{
|
|
INmFt* pFt = _GetFtFromHEvent(hEvent);
|
|
|
|
if(pFt)
|
|
{
|
|
bool bHasSomeoneCanceled = (S_FALSE == com_cast<IInternalFtObj>(pFt)->FileTransferDone());
|
|
|
|
Fire_FtUpdate(bHasSomeoneCanceled ? CONFN_FT_CANCELED : CONFN_FT_COMPLETE, pFt);
|
|
|
|
_RemoveFt(pFt);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CNmChannelFtObj::OnSessionEnd(void)
|
|
{
|
|
CFt::UnAdvise(this);
|
|
return S_OK;
|
|
}
|