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.
1429 lines
35 KiB
1429 lines
35 KiB
/*++
|
|
|
|
Copyright (c) 1997-2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
rndcnf.cpp
|
|
|
|
Abstract:
|
|
|
|
This module contains implementation of CConference object.
|
|
|
|
--*/
|
|
|
|
#include "stdafx.h"
|
|
#include <winsock2.h>
|
|
|
|
#include "rndcnf.h"
|
|
|
|
// These are the attribute names according to the schema in NTDS.
|
|
const WCHAR *const MeetingAttributeNames[] =
|
|
{
|
|
L"meetingAdvertisingScope",
|
|
L"meetingBlob",
|
|
L"meetingDescription",
|
|
L"meetingIsEncrypted",
|
|
L"meetingName",
|
|
L"meetingOriginator",
|
|
L"meetingProtocol",
|
|
L"meetingStartTime",
|
|
L"meetingStopTime",
|
|
L"meetingType",
|
|
L"meetingURL"
|
|
};
|
|
|
|
#define INC_ACCESS_ACL_SIZE(_SIZE_, _SID_) \
|
|
_SIZE_ += (sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) + GetLengthSid(_SID_));
|
|
|
|
#define BAIL_ON_BOOLFAIL(_FN_) \
|
|
if ( !_FN_ ) \
|
|
{ \
|
|
hr = HRESULT_FROM_WIN32(GetLastError()); \
|
|
goto failed; \
|
|
}
|
|
|
|
#define ACCESS_READ 0x10
|
|
#define ACCESS_WRITE 0x20
|
|
#define ACCESS_MODIFY (ACCESS_WRITE | WRITE_DAC)
|
|
#define ACCESS_DELETE DELETE
|
|
|
|
#define ACCESS_ALL (ACCESS_READ | ACCESS_MODIFY | ACCESS_DELETE)
|
|
|
|
|
|
|
|
HRESULT
|
|
ConvertStringToSid(
|
|
IN PWSTR string,
|
|
OUT PSID *sid,
|
|
OUT PDWORD pdwSidSize,
|
|
OUT PWSTR *end
|
|
);
|
|
|
|
HRESULT
|
|
ConvertACLToVariant(
|
|
PACL pACL,
|
|
LPVARIANT pvarACL
|
|
);
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// non-interface class methods
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
HRESULT
|
|
CConference::FinalConstruct()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Create the SDPBlob object that is aggregated with the conference object.
|
|
Also query my own Notification interface which will be given to the
|
|
SDPBlob object. To avoid circlar ref count, this interface is realeased
|
|
as soon as it is queried.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
HRESULT.
|
|
|
|
--*/
|
|
{
|
|
// Create the conference Blob
|
|
CComPtr <IUnknown> pIUnkConfBlob;
|
|
|
|
HRESULT hr = CoCreateInstance(
|
|
CLSID_SdpConferenceBlob,
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_IUnknown,
|
|
(void **)&pIUnkConfBlob
|
|
);
|
|
BAIL_IF_FAIL(hr, "Create SdpConferenceBlob");
|
|
|
|
|
|
// Get the ITConfBlobPrivate interface from the Blob object.
|
|
CComPtr <ITConfBlobPrivate> pITConfBlobPrivate;
|
|
|
|
hr = pIUnkConfBlob->QueryInterface(
|
|
IID_ITConfBlobPrivate,
|
|
(void **)&pITConfBlobPrivate
|
|
);
|
|
|
|
BAIL_IF_FAIL(hr, "Query ITConfBlobPrivate interface");
|
|
|
|
|
|
// query the conf blob instance for the conf blob i/f
|
|
CComPtr <ITConferenceBlob> pITConfBlob;
|
|
|
|
hr = pIUnkConfBlob->QueryInterface(
|
|
IID_ITConferenceBlob,
|
|
(void **)&pITConfBlob
|
|
);
|
|
|
|
BAIL_IF_FAIL(hr, "Query ITConferenceBlob");
|
|
|
|
// keep the interface pointers.
|
|
m_pIUnkConfBlob = pIUnkConfBlob;
|
|
m_pIUnkConfBlob->AddRef();
|
|
|
|
m_pITConfBlob = pITConfBlob;
|
|
m_pITConfBlob->AddRef();
|
|
|
|
m_pITConfBlobPrivate = pITConfBlobPrivate;
|
|
m_pITConfBlobPrivate->AddRef();
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
HRESULT CConference::Init(BSTR bName)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Init this conference with only a name. This is called when an empty
|
|
conference is created. A default SDP blob will be created. The format
|
|
is in the registry. See sdpblb code.
|
|
|
|
Arguments:
|
|
|
|
bName - The name of the conference.
|
|
|
|
Return Value:
|
|
|
|
HRESULT.
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = SetDefaultValue(
|
|
g_ConfInstInfoArray,
|
|
g_ContInstInfoArraySize
|
|
);
|
|
BAIL_IF_FAIL(hr, "set default attribute value");
|
|
|
|
hr = SetSingleValue(MA_MEETINGNAME, bName);
|
|
BAIL_IF_FAIL(hr, "can't set meeting name");
|
|
|
|
hr = m_pITConfBlob->Init(bName, BCS_UTF8, NULL);
|
|
BAIL_IF_FAIL(hr, "Init the conference Blob");
|
|
|
|
hr = SetDefaultSD();
|
|
BAIL_IF_FAIL(hr, "Init the security descriptor");
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CConference::Init(BSTR bName, BSTR bProtocol, BSTR bBlob)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Init this conference with only the conference name and also a conference
|
|
blob whose protocol should be IP conference. The blob is parsed by the
|
|
SDP Blob object and the information in the blob will be notified back
|
|
to this conference object through the notification interface.
|
|
|
|
Arguments:
|
|
|
|
bName - The name of the conference.
|
|
|
|
bProtocol - The protocol used by the blob. should be SDP now.
|
|
|
|
bBlob - The opaque data blob that describes this conference.
|
|
|
|
Return Value:
|
|
|
|
HRESULT.
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = SetSingleValue(MA_MEETINGNAME, bName);
|
|
BAIL_IF_FAIL(hr, "can't set meeting name");
|
|
|
|
// Check to make sure protocol is IP Conference
|
|
if ( wcscmp(bProtocol, L"IP Conference" ))
|
|
{
|
|
LOG((MSP_ERROR, "Protocol must be IP Conference"));
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
hr = SetSingleValue(MA_PROTOCOL, bProtocol);
|
|
BAIL_IF_FAIL(hr, "can't set meeting protocol");
|
|
|
|
hr = SetSingleValue(MA_CONFERENCE_BLOB, bBlob);
|
|
BAIL_IF_FAIL(hr, "can't set meeting blob");
|
|
|
|
hr = m_pITConfBlob->Init(NULL, BCS_UTF8, bBlob);
|
|
BAIL_IF_FAIL(hr, "Init the conference Blob object");
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
void
|
|
CConference::FinalRelease()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Clean up the SDP blob contained in this object.
|
|
clean up the security object for this object.
|
|
clean up all the attributes.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
CLock Lock(m_lock);
|
|
|
|
// do base class first
|
|
CDirectoryObject::FinalRelease();
|
|
|
|
if ( NULL != m_pIUnkConfBlob )
|
|
{
|
|
m_pIUnkConfBlob->Release();
|
|
m_pIUnkConfBlob = NULL;
|
|
}
|
|
|
|
if ( NULL != m_pITConfBlobPrivate )
|
|
{
|
|
m_pITConfBlobPrivate->Release();
|
|
m_pITConfBlobPrivate = NULL;
|
|
}
|
|
|
|
if ( NULL != m_pITConfBlob )
|
|
{
|
|
m_pITConfBlob->Release();
|
|
m_pITConfBlob = NULL;
|
|
}
|
|
|
|
// MuHan + ZoltanS fix 3-19-98 -- these are member smart pointers,
|
|
// should not be deleted!!!
|
|
// for (DWORD i = 0; i < NUM_MEETING_ATTRIBUTES; i ++)
|
|
// {
|
|
// delete m_Attributes[i];
|
|
// }
|
|
|
|
// the interface pointer to this instance's ITNotification i/f is NOT
|
|
// released because it was already released in FinalConstruct but the
|
|
// pointer was held to be passed onto the aggregated conf blob instance
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CConference::UpdateConferenceBlob(
|
|
IN IUnknown *pIUnkConfBlob
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
update the blob attribute if the blob object has been changed.
|
|
|
|
Arguments:
|
|
|
|
pIUnkConfBlob - pointer to the SDPBlob object.
|
|
|
|
Return Value:
|
|
|
|
HRESULT.
|
|
|
|
--*/
|
|
{
|
|
// check if the sdp blob has been modified since the component was created
|
|
VARIANT_BOOL BlobIsModified;
|
|
BAIL_IF_FAIL(m_pITConfBlobPrivate->get_IsModified(&BlobIsModified),
|
|
"UpdateConferenceBlob.get_IsModified");
|
|
|
|
// if not, return
|
|
if ( BlobIsModified == VARIANT_FALSE )
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
// get the blob and
|
|
CBstr SdpBlobBstr;
|
|
BAIL_IF_FAIL(m_pITConfBlob->get_ConferenceBlob(&SdpBlobBstr),
|
|
"UpdateConferenceBlob.get_ConfrenceBlob");
|
|
|
|
// set the conference blob attribute for the conference
|
|
BAIL_IF_FAIL(SetSingleValue(MA_CONFERENCE_BLOB, SdpBlobBstr),
|
|
"UpdateConferenceBlob.Setblob");
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CConference::SetDefaultValue(
|
|
IN REG_INFO RegInfo[],
|
|
IN DWORD dwItems
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Set attributes to the default value got from the registry.
|
|
|
|
Arguments:
|
|
|
|
RegInfo - {attribute, value} array.
|
|
|
|
dwItems - The number of items in the array.
|
|
|
|
Return Value:
|
|
|
|
HRESULT.
|
|
|
|
--*/
|
|
{
|
|
for (DWORD i=0; i < dwItems ; i++)
|
|
{
|
|
HRESULT hr;
|
|
hr = SetSingleValue(RegInfo[i].Attribute, RegInfo[i].wstrValue);
|
|
BAIL_IF_FAIL(hr, "set value");
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Set the right security descriptor for the conference.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
HRESULT.
|
|
|
|
--*/
|
|
HRESULT
|
|
CConference::SetDefaultSD()
|
|
{
|
|
LOG((MSP_INFO, "CConference::SetDefaultSD - entered"));
|
|
|
|
//
|
|
// The security descriptor
|
|
//
|
|
|
|
IADsSecurityDescriptor* pSecDesc = NULL;
|
|
|
|
HRESULT hr = S_OK;
|
|
bool bOwner = false, bWorld = false;
|
|
PACL pACL = NULL;
|
|
PSID pSidWorld = NULL;
|
|
DWORD dwAclSize = sizeof(ACL), dwTemp;
|
|
BSTR bstrTemp = NULL;
|
|
LPWSTR pszTemp = NULL;
|
|
|
|
HANDLE hToken;
|
|
UCHAR *pInfoBuffer = NULL;
|
|
DWORD cbInfoBuffer = 512;
|
|
|
|
//
|
|
// Try to get the thread or process token
|
|
//
|
|
|
|
if( !OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &hToken) )
|
|
{
|
|
//
|
|
// If there was a sever error we exit
|
|
//
|
|
|
|
if( GetLastError() != ERROR_NO_TOKEN )
|
|
{
|
|
LOG((MSP_ERROR, "CConference::SetDefaultSD - exit E_FAIL "
|
|
"OpenThreadToken failed!"));
|
|
return E_FAIL;
|
|
}
|
|
|
|
//
|
|
// Attempt to open the process token, since no thread token exists
|
|
//
|
|
|
|
if( !OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken) )
|
|
{
|
|
LOG((MSP_ERROR, "CConference::SetDefaultSD - exit E_FAIL "
|
|
"OpenProcessToken failed"));
|
|
return E_FAIL;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Loop until we have a large enough structure
|
|
//
|
|
|
|
while ( (pInfoBuffer = new UCHAR[cbInfoBuffer]) != NULL )
|
|
{
|
|
if ( !GetTokenInformation(hToken, TokenUser, pInfoBuffer, cbInfoBuffer, &cbInfoBuffer) )
|
|
{
|
|
delete pInfoBuffer;
|
|
pInfoBuffer = NULL;
|
|
|
|
if ( GetLastError() != ERROR_INSUFFICIENT_BUFFER )
|
|
return E_FAIL;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
CloseHandle(hToken);
|
|
|
|
//
|
|
// Did we get the owner ACL?
|
|
//
|
|
|
|
if ( pInfoBuffer )
|
|
{
|
|
INC_ACCESS_ACL_SIZE( dwAclSize, ((PTOKEN_USER) pInfoBuffer)->User.Sid );
|
|
bOwner = true;
|
|
}
|
|
|
|
//
|
|
// Make SID for "Everyone"
|
|
//
|
|
|
|
SysReAllocString( &bstrTemp, L"S-1-1-0" );
|
|
hr = ConvertStringToSid( bstrTemp, &pSidWorld, &dwTemp, &pszTemp );
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
INC_ACCESS_ACL_SIZE( dwAclSize, pSidWorld );
|
|
bWorld = true;
|
|
}
|
|
|
|
//
|
|
// Create a security descriptor
|
|
//
|
|
|
|
hr = CoCreateInstance(
|
|
CLSID_SecurityDescriptor,
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_IADsSecurityDescriptor,
|
|
(void **)&pSecDesc
|
|
);
|
|
if( FAILED(hr) )
|
|
{
|
|
|
|
LOG((MSP_ERROR, "CConference::SetDefaultSD - exit 0x%08x "
|
|
"Create security descriptor failed!", hr));
|
|
|
|
goto failed;
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Create the ACL containing the Owner and World ACEs
|
|
//
|
|
|
|
pACL = (PACL) new BYTE[dwAclSize];
|
|
if ( pACL )
|
|
{
|
|
BAIL_ON_BOOLFAIL( InitializeAcl(pACL, dwAclSize, ACL_REVISION) );
|
|
|
|
// Add World Rights
|
|
if ( bWorld )
|
|
{
|
|
if ( bOwner )
|
|
{
|
|
BAIL_ON_BOOLFAIL( AddAccessAllowedAce(pACL, ACL_REVISION, ACCESS_READ, pSidWorld) );
|
|
}
|
|
else
|
|
{
|
|
BAIL_ON_BOOLFAIL( AddAccessAllowedAce(pACL, ACL_REVISION, ACCESS_ALL , pSidWorld) );
|
|
}
|
|
}
|
|
|
|
// Add Creator rights
|
|
if ( bOwner )
|
|
BAIL_ON_BOOLFAIL( AddAccessAllowedAce(pACL, ACL_REVISION, ACCESS_ALL, ((PTOKEN_USER) pInfoBuffer)->User.Sid) );
|
|
|
|
|
|
// Set the DACL onto our security descriptor
|
|
VARIANT varDACL;
|
|
VariantInit( &varDACL );
|
|
if ( SUCCEEDED(hr = ConvertACLToVariant((PACL) pACL, &varDACL)) )
|
|
{
|
|
if ( SUCCEEDED(hr = pSecDesc->put_DaclDefaulted(FALSE)) )
|
|
{
|
|
hr = pSecDesc->put_DiscretionaryAcl( V_DISPATCH(&varDACL) );
|
|
if( SUCCEEDED(hr) )
|
|
{
|
|
hr = put_SecurityDescriptor((IDispatch*)pSecDesc);
|
|
if( SUCCEEDED(hr) )
|
|
{
|
|
hr = this->put_SecurityDescriptorIsModified(TRUE);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
VariantClear( &varDACL );
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
// Clean up
|
|
failed:
|
|
SysFreeString( bstrTemp );
|
|
if ( pACL ) delete pACL;
|
|
if ( pSidWorld ) delete pSidWorld;
|
|
if ( pInfoBuffer ) delete pInfoBuffer;
|
|
if( pSecDesc ) pSecDesc->Release();
|
|
|
|
LOG((MSP_INFO, "CConference::SetDefaultSD - exit 0x%08x", hr));
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CConference::GetSingleValueBstr(
|
|
IN OBJECT_ATTRIBUTE Attribute,
|
|
OUT BSTR * AttributeValue
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get the value of an attribute and create a BSTR to return.
|
|
|
|
Arguments:
|
|
|
|
Attribute - the attribute id as defined in rend.idl
|
|
|
|
AttributeValue - a pointer to a BSTR that points to the returned value.
|
|
|
|
Return Value:
|
|
|
|
HRESULT.
|
|
|
|
--*/
|
|
{
|
|
LOG((MSP_INFO, "CConference::GetSingleValueBstr - entered"));
|
|
|
|
BAIL_IF_BAD_WRITE_PTR(AttributeValue, E_POINTER);
|
|
|
|
// Check to see if the attribute is valid.
|
|
if (!ValidMeetingAttribute(Attribute))
|
|
{
|
|
LOG((MSP_ERROR, "Invalid Attribute, %d", Attribute));
|
|
return E_FAIL;
|
|
}
|
|
|
|
CLock Lock(m_lock);
|
|
|
|
// check to see if I have this attribute.
|
|
if(!m_Attributes[MeetingAttrIndex(Attribute)])
|
|
{
|
|
LOG((MSP_ERROR, "Attribute %S is not found",
|
|
MeetingAttributeName(Attribute)));
|
|
return E_FAIL;
|
|
}
|
|
|
|
// allocate a BSTR to return.
|
|
*AttributeValue =
|
|
SysAllocString(m_Attributes[MeetingAttrIndex(Attribute)]);
|
|
if (*AttributeValue == NULL)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CConference::GetSingleValueWstr(
|
|
IN OBJECT_ATTRIBUTE Attribute,
|
|
IN DWORD dwSize,
|
|
OUT WCHAR * AttributeValue
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get the value of an attribute. copy the value to the buffer provided.
|
|
|
|
Arguments:
|
|
|
|
Attribute - the attribute id as defined in rend.idl
|
|
|
|
AttributeValue - a pointer to a buffer where the value will be copied to.
|
|
|
|
Return Value:
|
|
|
|
HRESULT.
|
|
|
|
--*/
|
|
{
|
|
LOG((MSP_INFO, "CConference::GetSingleValueWstr - entered"));
|
|
|
|
_ASSERTE(NULL != AttributeValue);
|
|
_ASSERTE(ValidMeetingAttribute(Attribute));
|
|
|
|
CLock Lock(m_lock);
|
|
if(!m_Attributes[MeetingAttrIndex(Attribute)])
|
|
{
|
|
LOG((MSP_ERROR, "Attribute %S is not found",
|
|
MeetingAttributeName(Attribute)));
|
|
return E_FAIL;
|
|
}
|
|
|
|
// copy the attribute value
|
|
lstrcpynW(
|
|
AttributeValue,
|
|
m_Attributes[MeetingAttrIndex(Attribute)],
|
|
dwSize
|
|
);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CConference::SetSingleValue(
|
|
IN OBJECT_ATTRIBUTE Attribute,
|
|
IN WCHAR * AttributeValue
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Set the attribute value to a new string.
|
|
|
|
Arguments:
|
|
|
|
Attribute - the attribute id as defined in rend.idl
|
|
|
|
AttributeValue - the new value. If it is null, the value is deleted.
|
|
|
|
Return Value:
|
|
|
|
HRESULT.
|
|
|
|
--*/
|
|
{
|
|
LOG((MSP_INFO, "CConference::SetSingleValue - entered"));
|
|
|
|
if (!ValidMeetingAttribute(Attribute))
|
|
{
|
|
LOG((MSP_ERROR, "Invalid Attribute, %d", Attribute));
|
|
return E_FAIL;
|
|
}
|
|
|
|
if (AttributeValue != NULL)
|
|
{
|
|
BAIL_IF_BAD_READ_PTR(AttributeValue, E_POINTER);
|
|
}
|
|
|
|
CLock Lock(m_lock);
|
|
// if AttributeValue is NULL, the attribute is deleted.
|
|
if (!m_Attributes[MeetingAttrIndex(Attribute)].set(AttributeValue))
|
|
{
|
|
LOG((MSP_ERROR, "Can not add attribute %S",
|
|
MeetingAttributeName(Attribute)));
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// ITConference interface
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
STDMETHODIMP CConference::get_Name(BSTR * ppVal)
|
|
{
|
|
return m_pITConfBlobPrivate->GetName(ppVal);
|
|
}
|
|
|
|
STDMETHODIMP CConference::put_Name(BSTR pVal)
|
|
{
|
|
return m_pITConfBlobPrivate->SetName(pVal);
|
|
}
|
|
|
|
STDMETHODIMP CConference::get_Protocol(BSTR * ppVal)
|
|
{
|
|
return GetSingleValueBstr(MA_PROTOCOL, ppVal);
|
|
}
|
|
|
|
/*
|
|
|
|
*
|
|
* I've removed the following method from the interface because I don't plan
|
|
* to ever implement it.
|
|
*
|
|
|
|
STDMETHODIMP CConference::put_Protocol(BSTR newVal)
|
|
{
|
|
// currently not allowed to change this
|
|
// only IP Conference is supported
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
*
|
|
* The following are implemented but don't work. Again these are useless
|
|
* methods! Removed from the interface.
|
|
*
|
|
|
|
STDMETHODIMP CConference::get_ConferenceType(BSTR * ppVal)
|
|
{
|
|
return GetSingleValueBstr(MA_TYPE, ppVal);
|
|
}
|
|
|
|
STDMETHODIMP CConference::put_ConferenceType(BSTR newVal)
|
|
{
|
|
return SetSingleValue(MA_TYPE, newVal);
|
|
}
|
|
|
|
*/
|
|
|
|
STDMETHODIMP CConference::get_Originator(BSTR * ppVal)
|
|
{
|
|
return m_pITConfBlobPrivate->GetOriginator(ppVal);
|
|
}
|
|
|
|
STDMETHODIMP CConference::put_Originator(BSTR newVal)
|
|
{
|
|
return m_pITConfBlobPrivate->SetOriginator(newVal);
|
|
}
|
|
|
|
STDMETHODIMP CConference::get_AdvertisingScope(
|
|
RND_ADVERTISING_SCOPE *pAdvertisingScope
|
|
)
|
|
{
|
|
return m_pITConfBlobPrivate->GetAdvertisingScope(pAdvertisingScope);
|
|
}
|
|
|
|
STDMETHODIMP CConference::put_AdvertisingScope(
|
|
RND_ADVERTISING_SCOPE AdvertisingScope
|
|
)
|
|
{
|
|
return m_pITConfBlobPrivate->SetAdvertisingScope(AdvertisingScope);
|
|
}
|
|
|
|
STDMETHODIMP CConference::get_Url(BSTR * ppVal)
|
|
{
|
|
return m_pITConfBlobPrivate->GetUrl(ppVal);
|
|
}
|
|
|
|
STDMETHODIMP CConference::put_Url(BSTR newVal)
|
|
{
|
|
return m_pITConfBlobPrivate->SetUrl(newVal);
|
|
}
|
|
|
|
STDMETHODIMP CConference::get_Description(BSTR * ppVal)
|
|
{
|
|
return m_pITConfBlobPrivate->GetDescription(ppVal);
|
|
}
|
|
|
|
STDMETHODIMP CConference::put_Description(BSTR newVal)
|
|
{
|
|
return m_pITConfBlobPrivate->SetDescription(newVal);
|
|
}
|
|
|
|
STDMETHODIMP CConference::get_IsEncrypted(VARIANT_BOOL *pfEncrypted)
|
|
{
|
|
if ( IsBadWritePtr(pfEncrypted, sizeof(VARIANT_BOOL)) )
|
|
{
|
|
LOG((MSP_ERROR, "CConference::get_IsEncrypted : bad pointer passed in"));
|
|
return E_POINTER;
|
|
}
|
|
|
|
// We don't support encrypted streaming at all.
|
|
|
|
*pfEncrypted = VARIANT_FALSE;
|
|
|
|
return S_OK;
|
|
|
|
// ILS server (NT 5 beta 1) has IsEncrypted as a dword, this will be modified
|
|
// to a string afterwards at that time the code below should replace
|
|
// the current implementation
|
|
// return GetSingleValueBstr(MA_IS_ENCRYPTED, pVal);
|
|
}
|
|
|
|
STDMETHODIMP CConference::put_IsEncrypted(VARIANT_BOOL fEncrypted)
|
|
{
|
|
// We don't allow changes to this. See get_IsEncrypted.
|
|
|
|
return E_NOTIMPL;
|
|
|
|
// ILS server (NT 5 beta 1) has IsEncrypted as a dword, this will be modified
|
|
// to a string afterwards at that time the code below should replace the
|
|
// current implementation no need to notify the conference blob of the change
|
|
// return SetSingleValue(MA_IS_ENCRYPTED, newVal);
|
|
}
|
|
|
|
|
|
inline
|
|
DWORD_PTR TimetToNtpTime(IN time_t TimetVal)
|
|
{
|
|
return TimetVal + NTP_OFFSET;
|
|
}
|
|
|
|
|
|
inline
|
|
time_t NtpTimeToTimet(IN DWORD_PTR NtpTime)
|
|
{
|
|
return NtpTime - NTP_OFFSET;
|
|
}
|
|
|
|
|
|
inline HRESULT
|
|
SystemTimeToNtpTime(
|
|
IN SYSTEMTIME &Time,
|
|
OUT DWORD &NtpDword
|
|
)
|
|
{
|
|
_ASSERTE(FIRST_POSSIBLE_YEAR <= Time.wYear);
|
|
|
|
// fill in a tm struct with the values
|
|
tm NtpTmStruct;
|
|
NtpTmStruct.tm_isdst = -1; // no info available about daylight savings time
|
|
NtpTmStruct.tm_year = (int)Time.wYear - 1900;
|
|
NtpTmStruct.tm_mon = (int)Time.wMonth - 1; // months since january
|
|
NtpTmStruct.tm_mday = (int)Time.wDay;
|
|
NtpTmStruct.tm_wday = (int)Time.wDayOfWeek;
|
|
NtpTmStruct.tm_hour = (int)Time.wHour;
|
|
NtpTmStruct.tm_min = (int)Time.wMinute;
|
|
NtpTmStruct.tm_sec = (int)Time.wSecond;
|
|
|
|
// try to convert into a time_t value
|
|
time_t TimetVal = mktime(&NtpTmStruct);
|
|
if ( -1 == TimetVal )
|
|
{
|
|
return HRESULT_FROM_ERROR_CODE(RND_INVALID_TIME);
|
|
}
|
|
|
|
// convert the time_t value into an NTP value
|
|
NtpDword = (DWORD) TimetToNtpTime(TimetVal);
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
inline
|
|
HRESULT
|
|
NtpTimeToSystemTime(
|
|
IN DWORD dwNtpTime,
|
|
OUT SYSTEMTIME &Time
|
|
)
|
|
{
|
|
// if the gen time is WSTR_GEN_TIME_ZERO then,
|
|
// all the out parameters should be set to 0
|
|
if (dwNtpTime == 0)
|
|
{
|
|
memset(&Time, 0, sizeof(SYSTEMTIME));
|
|
return S_OK;
|
|
}
|
|
|
|
time_t Timet = NtpTimeToTimet(dwNtpTime);
|
|
|
|
// get the local tm struct for this time value
|
|
tm* pTimet = localtime(&Timet);
|
|
if( IsBadReadPtr(pTimet, sizeof(tm) ))
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
tm LocalTm = *pTimet;
|
|
|
|
//
|
|
// win64: added casts below
|
|
//
|
|
|
|
// set the ref parameters to the tm struct values
|
|
Time.wYear = (WORD) ( LocalTm.tm_year + 1900 ); // years since 1900
|
|
Time.wMonth = (WORD) ( LocalTm.tm_mon + 1 ); // months SINCE january (0,11)
|
|
Time.wDay = (WORD) LocalTm.tm_mday;
|
|
Time.wDayOfWeek = (WORD) LocalTm.tm_wday;
|
|
Time.wHour = (WORD) LocalTm.tm_hour;
|
|
Time.wMinute = (WORD) LocalTm.tm_min;
|
|
Time.wSecond = (WORD) LocalTm.tm_sec;
|
|
Time.wMilliseconds = (WORD) 0;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CConference::get_StartTime(DATE *pDate)
|
|
{
|
|
LOG((MSP_INFO, "CConference::get_StartTime - enter"));
|
|
|
|
BAIL_IF_BAD_WRITE_PTR(pDate, E_POINTER);
|
|
|
|
DWORD dwStartTime;
|
|
|
|
HRESULT hr = m_pITConfBlobPrivate->GetStartTime(&dwStartTime);
|
|
|
|
BAIL_IF_FAIL(hr, "GetStartTime from blob");
|
|
|
|
// special case for permanent / unbounded conferences
|
|
// return the variant time zero. In the put_ methods this
|
|
// is also considered a special value. We will never
|
|
// actually use zero as a valid time because it is so
|
|
// far in the past.
|
|
|
|
if ( dwStartTime == 0 )
|
|
{
|
|
*pDate = 0; // special "unbounded" value
|
|
|
|
LOG((MSP_INFO, "CConference::get_StartTime - unbounded/permanent "
|
|
"- exit S_OK"));
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
// break the generalized time entry into the year,
|
|
// month, day, hour and minute (local values)
|
|
SYSTEMTIME Time;
|
|
hr = NtpTimeToSystemTime(dwStartTime, Time);
|
|
if( FAILED(hr) )
|
|
{
|
|
return HRESULT_FROM_ERROR_CODE(RND_INVALID_TIME);
|
|
}
|
|
|
|
DOUBLE vtime;
|
|
if (SystemTimeToVariantTime(&Time, &vtime) == FALSE)
|
|
{
|
|
return HRESULT_FROM_ERROR_CODE(RND_INVALID_TIME);
|
|
}
|
|
|
|
*pDate = vtime;
|
|
|
|
LOG((MSP_INFO, "CConference::get_StartTime - exit S_OK"));
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CConference::put_StartTime(DATE Date)
|
|
{
|
|
SYSTEMTIME Time;
|
|
if (VariantTimeToSystemTime(Date, &Time) == FALSE)
|
|
{
|
|
return HRESULT_FROM_ERROR_CODE(RND_INVALID_TIME);
|
|
}
|
|
|
|
DWORD dwNtpStartTime;
|
|
if (Date == 0)
|
|
{
|
|
// unbounded start time
|
|
dwNtpStartTime = 0;
|
|
}
|
|
else if ( FIRST_POSSIBLE_YEAR > Time.wYear )
|
|
{
|
|
// cannot handle years less than FIRST_POSSIBLE_YEAR
|
|
return HRESULT_FROM_ERROR_CODE(RND_INVALID_TIME);
|
|
}
|
|
else
|
|
{
|
|
BAIL_IF_FAIL(
|
|
SystemTimeToNtpTime(Time, dwNtpStartTime),
|
|
"getNtpDword"
|
|
);
|
|
}
|
|
|
|
// notify the conference blob of the change
|
|
HRESULT hr = m_pITConfBlobPrivate->SetStartTime(dwNtpStartTime);
|
|
BAIL_IF_FAIL(hr, "SetStartTime from to blob");
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CConference::get_StopTime(DATE *pDate)
|
|
{
|
|
LOG((MSP_INFO, "CConference::get_StopTime - enter"));
|
|
|
|
BAIL_IF_BAD_WRITE_PTR(pDate, E_POINTER);
|
|
|
|
DWORD dwStopTime;
|
|
|
|
HRESULT hr = m_pITConfBlobPrivate->GetStopTime(&dwStopTime);
|
|
|
|
BAIL_IF_FAIL(hr, "GetStopTime from blob");
|
|
|
|
// special case for permanent / unbounded conferences
|
|
// return the variant time zero. In the put_ methods this
|
|
// is also considered a special value. We will never
|
|
// actually use zero as a valid time because it is so
|
|
// far in the past.
|
|
|
|
if ( dwStopTime == 0 )
|
|
{
|
|
*pDate = 0; // special "unbounded" value
|
|
|
|
LOG((MSP_INFO, "CConference::get_StopTime - unbounded/permanent "
|
|
"- exit S_OK"));
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
// break the generalized time entry into the year,
|
|
// month, day, hour and minute (local values)
|
|
SYSTEMTIME Time;
|
|
hr =NtpTimeToSystemTime(dwStopTime, Time);
|
|
if( FAILED(hr) )
|
|
{
|
|
return HRESULT_FROM_ERROR_CODE(RND_INVALID_TIME);
|
|
}
|
|
|
|
DOUBLE vtime;
|
|
if (SystemTimeToVariantTime(&Time, &vtime) == FALSE)
|
|
{
|
|
return HRESULT_FROM_ERROR_CODE(RND_INVALID_TIME);
|
|
}
|
|
|
|
*pDate = vtime;
|
|
|
|
LOG((MSP_INFO, "CConference::get_StopTime - exit S_OK"));
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CConference::put_StopTime(DATE Date)
|
|
{
|
|
SYSTEMTIME Time;
|
|
if (VariantTimeToSystemTime(Date, &Time) == FALSE)
|
|
{
|
|
return HRESULT_FROM_ERROR_CODE(RND_INVALID_TIME);
|
|
}
|
|
|
|
DWORD dwNtpStopTime;
|
|
if (Date == 0)
|
|
{
|
|
// unbounded start time
|
|
dwNtpStopTime = 0;
|
|
}
|
|
else if ( FIRST_POSSIBLE_YEAR > Time.wYear )
|
|
{
|
|
// cannot handle years less than FIRST_POSSIBLE_YEAR
|
|
return HRESULT_FROM_ERROR_CODE(RND_INVALID_TIME);
|
|
}
|
|
else
|
|
{
|
|
BAIL_IF_FAIL(
|
|
SystemTimeToNtpTime(Time, dwNtpStopTime),
|
|
"getNtpDword"
|
|
);
|
|
|
|
// determine current time
|
|
time_t CurrentTime = time(NULL);
|
|
if (dwNtpStopTime <= TimetToNtpTime(CurrentTime))
|
|
{
|
|
return HRESULT_FROM_ERROR_CODE(RND_INVALID_TIME);
|
|
}
|
|
}
|
|
|
|
// notify the conference blob of the change
|
|
HRESULT hr = m_pITConfBlobPrivate->SetStopTime(dwNtpStopTime);
|
|
BAIL_IF_FAIL(hr, "SetStopTime from to blob");
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// ITDirectoryObject
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
STDMETHODIMP CConference::get_DialableAddrs(
|
|
IN long dwAddressTypes, //defined in tapi.h
|
|
OUT VARIANT * pVariant
|
|
)
|
|
{
|
|
BAIL_IF_BAD_WRITE_PTR(pVariant, E_POINTER);
|
|
|
|
BSTR *Addresses = new BSTR[1]; // only one for now.
|
|
BAIL_IF_NULL(Addresses, E_OUTOFMEMORY);
|
|
|
|
HRESULT hr;
|
|
|
|
switch (dwAddressTypes) // ZoltanS fix
|
|
{
|
|
case LINEADDRESSTYPE_SDP:
|
|
|
|
hr = UpdateConferenceBlob(m_pIUnkConfBlob);
|
|
|
|
if ( FAILED(hr) )
|
|
{
|
|
LOG((MSP_ERROR, "CConference::get_DialableAddrs - "
|
|
"failed to update blob attribute from the blob object"));
|
|
}
|
|
else
|
|
{
|
|
hr = GetSingleValueBstr(MA_CONFERENCE_BLOB, &Addresses[0]);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
hr = E_FAIL; // just return 0 addresses
|
|
break;
|
|
}
|
|
|
|
DWORD dwCount = (FAILED(hr)) ? 0 : 1;
|
|
|
|
hr = ::CreateBstrCollection(dwCount, // count
|
|
&Addresses[0], // begin pointer
|
|
&Addresses[dwCount], // end pointer
|
|
pVariant, // return value
|
|
AtlFlagTakeOwnership); // flags
|
|
|
|
// the collection will destroy the Addresses array eventually.
|
|
// no need to free anything here. Even if we tell it to hand
|
|
// out zero objects, it will delete the array on construction.
|
|
// (ZoltanS verified.)
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CConference::EnumerateDialableAddrs(
|
|
IN DWORD dwAddressTypes, //defined in tapi.h
|
|
OUT IEnumDialableAddrs ** ppEnumDialableAddrs
|
|
)
|
|
{
|
|
BAIL_IF_BAD_WRITE_PTR(ppEnumDialableAddrs, E_POINTER);
|
|
|
|
BSTR *Addresses = new BSTR[1]; // only one for now.
|
|
BAIL_IF_NULL(Addresses, E_OUTOFMEMORY);
|
|
|
|
HRESULT hr;
|
|
|
|
switch (dwAddressTypes) // ZoltanS fix
|
|
{
|
|
case LINEADDRESSTYPE_SDP:
|
|
|
|
hr = UpdateConferenceBlob(m_pIUnkConfBlob);
|
|
|
|
if ( FAILED(hr) )
|
|
{
|
|
LOG((MSP_ERROR, "CConference::EnumerateDialableAddrs - "
|
|
"failed to update blob attribute from the blob object"));
|
|
}
|
|
else
|
|
{
|
|
hr = GetSingleValueBstr(MA_CONFERENCE_BLOB, &Addresses[0]);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
hr = E_FAIL; // just return 0 addresses
|
|
break;
|
|
}
|
|
|
|
DWORD dwCount = (FAILED(hr)) ? 0 : 1;
|
|
|
|
hr = ::CreateDialableAddressEnumerator(
|
|
&Addresses[0],
|
|
&Addresses[dwCount],
|
|
ppEnumDialableAddrs
|
|
);
|
|
|
|
// the enumerator will destroy the Addresses array eventually,
|
|
// so no need to free anything here. Even if we tell it to hand
|
|
// out zero objects, it will delete the array on destruction.
|
|
// (ZoltanS verified.)
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CConference::GetAttribute(
|
|
IN OBJECT_ATTRIBUTE Attribute,
|
|
OUT BSTR * ppAttributeValue
|
|
)
|
|
{
|
|
if (Attribute == MA_CONFERENCE_BLOB)
|
|
{
|
|
BAIL_IF_FAIL(
|
|
UpdateConferenceBlob(m_pIUnkConfBlob),
|
|
"update blob attribute from the blob object"
|
|
);
|
|
}
|
|
return GetSingleValueBstr(Attribute, ppAttributeValue);
|
|
}
|
|
|
|
STDMETHODIMP CConference::SetAttribute(
|
|
IN OBJECT_ATTRIBUTE Attribute,
|
|
IN BSTR pAttributeValue
|
|
)
|
|
{
|
|
// this function is never called in the current implementation.
|
|
// However, it might be useful in the future.
|
|
return SetSingleValue(Attribute, pAttributeValue);
|
|
}
|
|
|
|
STDMETHODIMP CConference::GetTTL(
|
|
OUT DWORD * pdwTTL
|
|
)
|
|
{
|
|
LOG((MSP_INFO, "CConference::GetTTL - enter"));
|
|
|
|
//
|
|
// Check arguments.
|
|
//
|
|
|
|
BAIL_IF_BAD_WRITE_PTR(pdwTTL, E_POINTER);
|
|
|
|
//
|
|
// Get the stop time from the conference blob.
|
|
//
|
|
|
|
DWORD dwStopTime;
|
|
HRESULT hr = m_pITConfBlobPrivate->GetStopTime(&dwStopTime);
|
|
|
|
BAIL_IF_FAIL(hr, "GetStopTime from blob");
|
|
|
|
//
|
|
// If the blob has zero as the stop time, then this conference does not
|
|
// have an explicit end time. The RFC calls this an "unbounded" session
|
|
// (or a "permanent" session if the start time is also zero. In this
|
|
// case we use some very large value (MAX_TTL) as the TTL.
|
|
//
|
|
|
|
if ( dwStopTime == 0 )
|
|
{
|
|
*pdwTTL = MAX_TTL;
|
|
|
|
LOG((MSP_INFO, "CConference::GetTTL - unbounded or permanent "
|
|
"conference - exit S_OK"));
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//
|
|
// Determine the current NTP time.
|
|
//
|
|
|
|
time_t CurrentTime = time(NULL);
|
|
DWORD dwCurrentTime = (DWORD) TimetToNtpTime(CurrentTime);
|
|
|
|
//
|
|
// Error if the current time is later than the conference stop time.
|
|
//
|
|
|
|
if ( dwStopTime <= dwCurrentTime )
|
|
{
|
|
LOG((MSP_ERROR, "CConference::GetTTL - bounded conference - "
|
|
"current time is later than start time - "
|
|
"exit RND_INVALID_TIME"));
|
|
|
|
return HRESULT_FROM_ERROR_CODE(RND_INVALID_TIME);
|
|
}
|
|
|
|
//
|
|
// Return how much time from now until the conference expires.
|
|
//
|
|
|
|
*pdwTTL = dwStopTime - (DWORD) TimetToNtpTime(CurrentTime);
|
|
|
|
LOG((MSP_INFO, "CConference::GetTTL - bounded conference - "
|
|
"exit S_OK"));
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
typedef IDispatchImpl<ITDirectoryObjectConferenceVtbl<CConference>, &IID_ITDirectoryObjectConference, &LIBID_RENDLib> CTDirObjConference;
|
|
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++=
|
|
//
|
|
// CConference::GetIDsOfNames
|
|
//
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++=
|
|
STDMETHODIMP CConference::GetIDsOfNames(REFIID riid,
|
|
LPOLESTR* rgszNames,
|
|
UINT cNames,
|
|
LCID lcid,
|
|
DISPID* rgdispid
|
|
)
|
|
{
|
|
LOG((MSP_TRACE, "CConference::GetIDsOfNames[%p] - enter. Name [%S]",this, *rgszNames));
|
|
|
|
|
|
HRESULT hr = DISP_E_UNKNOWNNAME;
|
|
|
|
|
|
|
|
//
|
|
// See if the requsted method belongs to the default interface
|
|
//
|
|
|
|
hr = CTDirObjConference::GetIDsOfNames(riid, rgszNames, cNames, lcid, rgdispid);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LOG((MSP_TRACE, "CConference:GetIDsOfNames - found %S on CTDirObjConference", *rgszNames));
|
|
rgdispid[0] |= IDISPDIROBJCONFERENCE;
|
|
return hr;
|
|
}
|
|
|
|
|
|
//
|
|
// If not, then try the CDirectoryObject base class
|
|
//
|
|
|
|
hr = CDirectoryObject::GetIDsOfNames(riid, rgszNames, cNames, lcid, rgdispid);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LOG((MSP_TRACE, "CConference::GetIDsOfNames - found %S on CDirectoryObject", *rgszNames));
|
|
rgdispid[0] |= IDISPDIROBJECT;
|
|
return hr;
|
|
}
|
|
|
|
LOG((MSP_ERROR, "CConference::GetIDsOfNames[%p] - finish. didn't find %S on our iterfaces",*rgszNames));
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++=
|
|
//
|
|
// CConference::Invoke
|
|
//
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++=
|
|
STDMETHODIMP CConference::Invoke(DISPID dispidMember,
|
|
REFIID riid,
|
|
LCID lcid,
|
|
WORD wFlags,
|
|
DISPPARAMS* pdispparams,
|
|
VARIANT* pvarResult,
|
|
EXCEPINFO* pexcepinfo,
|
|
UINT* puArgErr
|
|
)
|
|
{
|
|
LOG((MSP_TRACE, "CConference::Invoke[%p] - enter. dispidMember %lx",this, dispidMember));
|
|
|
|
HRESULT hr = DISP_E_MEMBERNOTFOUND;
|
|
DWORD dwInterface = (dispidMember & INTERFACEMASK);
|
|
|
|
|
|
//
|
|
// Call invoke for the required interface
|
|
//
|
|
|
|
switch (dwInterface)
|
|
{
|
|
case IDISPDIROBJCONFERENCE:
|
|
{
|
|
hr = CTDirObjConference::Invoke(dispidMember,
|
|
riid,
|
|
lcid,
|
|
wFlags,
|
|
pdispparams,
|
|
pvarResult,
|
|
pexcepinfo,
|
|
puArgErr
|
|
);
|
|
|
|
LOG((MSP_TRACE, "CConference::Invoke - ITDirectoryObjectConference"));
|
|
|
|
break;
|
|
}
|
|
|
|
case IDISPDIROBJECT:
|
|
{
|
|
hr = CDirectoryObject::Invoke(dispidMember,
|
|
riid,
|
|
lcid,
|
|
wFlags,
|
|
pdispparams,
|
|
pvarResult,
|
|
pexcepinfo,
|
|
puArgErr
|
|
);
|
|
|
|
LOG((MSP_TRACE, "CConference::Invoke - ITDirectoryObject"));
|
|
|
|
break;
|
|
}
|
|
|
|
} // end switch (dwInterface)
|
|
|
|
|
|
LOG((MSP_TRACE, "CConference::Invoke[%p] - finish. hr = %lx", hr));
|
|
|
|
return hr;
|
|
}
|
|
|
|
// eof
|