Leaked source code of windows server 2003
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.
 
 
 
 
 
 

3069 lines
81 KiB

/*++
Copyright (c) 1997-2000 Microsoft Corporation
Module Name:
rndils.cpp
Abstract:
This module contains implementation of CILSDirectory.
--*/
#include "stdafx.h"
#include <limits.h>
#include "rndcommc.h"
#include "rndils.h"
#include "rndldap.h"
#include "rndcnf.h"
#include "rndcoll.h"
//
// These are the names of the attributes we have to work with
// in the ILS schema.
//
const WCHAR * const CILSDirectory::s_RTConferenceAttributes[] =
{
L"advertisingScope",
L"conferenceBlob",
L"generalDescription",
L"isEncrypted",
L"uid",
L"originator",
L"protocolId",
L"startTime",
L"stopTime",
L"subType",
L"URL"
};
//
// The following are the attributes we make use of natively for
// ILS user objects. (perhaps not all though?)
//
const WCHAR * const CILSDirectory::s_RTPersonAttributes[] =
{
L"cn",
L"telephoneNumber",
L"ipAddress"
};
// The following definitions are for NetMeeting compatibility only.
const WCHAR * const g_NMAttributeNames[] =
{
L"applicationID",
L"mimetype",
L"GUID",
L"protocolID",
L"ProtocolMimeType",
L"port",
L"ILSA39321630",
L"ILSA26214430",
L"ILSA32964638",
L"ILSA32833566"
};
const DWORD NUM_NM_ATTRIBUTES =
(sizeof g_NMAttributeNames)/(sizeof (WCHAR *));
const WCHAR * const g_NMAttributeValues[] =
{
L"ms-netmeeting", NULL,
L"text/iuls", NULL,
L"008aff194794cf118796444553540000", NULL,
L"H323", NULL,
L"text/h323", NULL,
L"1720", NULL,
L"4", NULL,
L"0", NULL,
L"1", NULL,
L"1", NULL
};
const WCHAR * const g_NMRTPersonAttributeNames[] =
{
// ZoltanS: moved this out so we can make it look nicer: L"givenName",
L"surname", // vocabulary word: this means "last name"
L"location",
L"sflags",
L"c",
L"comment",
L"ssecurity",
L"smodop",
L"mimetype",
L"GUID",
L"ProtocolMimeType",
L"port",
L"ILSA39321630",
L"ILSA26214430",
L"ILSA32964638",
L"ILSA32833566"
};
const DWORD NUM_NM_RTPERSON_ATTRIBUTES =
(sizeof g_NMRTPersonAttributeNames)/(sizeof (WCHAR *));
const WCHAR * const g_NMRTPersonAttributeValues[] =
{
// ZoltanS: we used to have this here: L"N/A", NULL,
L" ", NULL, // ZoltanS was N/A. Need the space to avoid having nothing set.
L"N/A", NULL,
L"1", NULL,
L"US", NULL,
L"Generated by TAPI3", NULL,
L"1508109", NULL,
L"0", NULL,
L"text/iuls", NULL,
L"008aff194794cf118796444553540000", NULL,
L"H323", NULL,
L"1720", NULL,
L"4", NULL,
L"0", NULL,
L"1", NULL,
L"1", NULL
};
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
HRESULT CILSDirectory::FinalConstruct(void)
{
LOG((MSP_TRACE, "CILSDirectory::FinalConstruct - enter"));
HRESULT hr = CoCreateFreeThreadedMarshaler( GetControllingUnknown(),
& m_pFTM );
if ( FAILED(hr) )
{
LOG((MSP_INFO, "CILSDirectory::FinalConstruct - "
"create FTM returned 0x%08x; exit", hr));
return hr;
}
LOG((MSP_TRACE, "CILSDirectory::FinalConstruct - exit S_OK"));
return S_OK;
}
/////////////////////////////////////////////////////////////////////////////
// ldap helper functions
/////////////////////////////////////////////////////////////////////////////
HRESULT ValidateILSServer(IN LDAP *hLdap, IN TCHAR * org)
/*++
Routine Description:
send a search to check if the RTConference schema is supported
Arguments:
hLdap - The handle to the ldap connection.
org - The default naming context.
Return Value:
HRESULT.
--*/
{
const WCHAR CN_WSTR[] = L"cn";
const WCHAR CN_RTCONF_WSTR[] = L"cn=RTConference";
const WCHAR SCHEMA[] = L",ou=admin,cn=schema";
LOG((MSP_INFO, "::ValidateILSServer - entering"));
// send a search (one level, base dn = "o=ORG,ou=admin,cn=schema",
// filter = "cn=RTConference") ask for the cn attribute without the value
PTCHAR Attributes[] = {(WCHAR *)CN_WSTR, NULL};
// determine the organization - use the org portion of the directory path
// string and the pre-cooked "ou=admin,cn=schema" string
TCHAR *SchemaDn = new TCHAR[lstrlen(org) + lstrlen(SCHEMA) + 1];
BAIL_IF_NULL(SchemaDn, E_OUTOFMEMORY);
lstrcpy(SchemaDn, org);
lstrcat(SchemaDn, SCHEMA);
// print the base dn
LDAPMessage *SearchResult;
ULONG res = DoLdapSearch(
hLdap, // ldap handle
SchemaDn, // schema dn
LDAP_SCOPE_ONELEVEL, // one level search
(WCHAR *)CN_RTCONF_WSTR, // common name is RTConference
Attributes, // array of attribute names
TRUE, // don't return the attribute values
&SearchResult // search results
);
// free the schema dn string
delete SchemaDn;
// check result of the search operation
BAIL_IF_LDAP_FAIL(res, "search for RTConference schema");
CLdapMsgPtr MessageHolder(SearchResult);
// check the number of entries returned to determine if OK
HRESULT hr = (0==ldap_count_entries(hLdap, SearchResult)) ? E_FAIL : S_OK;
LOG((MSP_INFO, "::ValidateILSServer - exiting %x", hr));
return hr;
}
/////////////////////////////////////////////////////////////////////////////
// private functions
/////////////////////////////////////////////////////////////////////////////
HRESULT CILSDirectory::Init(
IN const TCHAR * const strServerName,
IN const WORD wPort
)
/*++
Routine Description:
Connect to the ILS server on a given port.
Arguments:
strServerName - The ILS server name.
wPort - The port number.
Return Value:
HRESULT.
--*/
{
if (strServerName != NULL)
{
int cbServerName = lstrlen(strServerName);
// Make sure the adding operation don't go over the limits
if( (cbServerName+1) <= cbServerName )
{
return E_INVALIDARG;
}
m_pServerName = new TCHAR [cbServerName + 1];
if (m_pServerName == NULL)
{
return E_OUTOFMEMORY;
}
// Strange +1, lstrcpyn copies also the NULL terminator
lstrcpyn(m_pServerName, strServerName, cbServerName+1);
_ASSERTE( lstrlen(m_pServerName) == cbServerName );
}
m_wPort = wPort;
return S_OK;
}
HRESULT
CILSDirectory::TryServer(
IN WORD Port
)
/*++
Routine Description:
Try to connect to the ILS server one the given port.
Arguments:
wPort - The port number.
Return Value:
HRESULT.
--*/
{
LOG((MSP_INFO, "trying %S at port %d", m_pServerName, Port));
// associate the ldap handle with the handle holder. in case of an error
// and subsequent return (without being reset), the ldap handle is closed
CLdapPtr hLdap = ldap_init(m_pServerName, Port);
BAIL_IF_NULL((LDAP*)hLdap, HRESULT_FROM_WIN32(ERROR_BAD_NETPATH));
LDAP_TIMEVAL Timeout;
Timeout.tv_sec = REND_LDAP_TIMELIMIT;
Timeout.tv_usec = 0;
DWORD res = ldap_connect((LDAP*)hLdap, &Timeout);
BAIL_IF_LDAP_FAIL(res, "connect to the server.");
DWORD LdapVersion = 3;
res = ldap_set_option((LDAP*)hLdap, LDAP_OPT_VERSION, &LdapVersion);
BAIL_IF_LDAP_FAIL(res, "set ldap version to 3");
res = ldap_set_option((LDAP*)hLdap, LDAP_OPT_TIMELIMIT, &Timeout);
BAIL_IF_LDAP_FAIL(res, "set ldap timelimit");
ULONG ldapOptionOn = PtrToUlong(LDAP_OPT_ON);
res = ldap_set_option((LDAP*)hLdap, LDAP_OPT_AREC_EXCLUSIVE, &ldapOptionOn);
BAIL_IF_LDAP_FAIL(res, "set ldap arec exclusive");
if (m_IsSsl)
{
res = ldap_set_option(hLdap, LDAP_OPT_SSL, LDAP_OPT_ON);
BAIL_IF_LDAP_FAIL(res, "set ssl option");
}
// if no directory path is specified, query the server
// to determine the correct path
CTstr namingContext;
BAIL_IF_FAIL(
::GetNamingContext(hLdap, &namingContext),
"read naming context"
);
// ZoltanS: this copies the namingContext string.
BAIL_IF_FAIL(
ValidateILSServer(hLdap, namingContext),
"ValidateILSServer"
);
m_pContainer =
new TCHAR[lstrlen(namingContext) + lstrlen(DYNAMIC_CONTAINER) + 1];
BAIL_IF_NULL(m_pContainer, E_OUTOFMEMORY);
m_pNMContainer =
new TCHAR[lstrlen(namingContext) + lstrlen(NETMEETING_CONTAINER) + 1];
BAIL_IF_NULL(m_pNMContainer, E_OUTOFMEMORY);
m_ldap = hLdap;
lstrcpy(m_pContainer, DYNAMIC_CONTAINER);
lstrcat(m_pContainer, namingContext);
lstrcpy(m_pNMContainer, NETMEETING_CONTAINER);
lstrcat(m_pNMContainer, namingContext);
// reset the holders so that they don't release anyting.
hLdap = NULL;
// ZoltanS fix: We were leaking this string. It is copied
// everywhere above, so we should NOT reset the holder here!
// --> namingContext = NULL;
LOG((MSP_INFO, "CILSDirectory::TryServer - exiting OK"));
return S_OK;
}
HRESULT CILSDirectory::MakeConferenceDN(
IN TCHAR * pName,
OUT TCHAR ** ppDN
)
/*++
Routine Description:
Construct the DN for a conference based on the name of the conference.
Arguments:
pName - The name of the conference.
ppDN - The DN of the conference returned.
Return Value:
HRESULT.
--*/
{
DWORD dwLen =
lstrlen(m_pContainer) + lstrlen(ILS_CONF_DN_FORMAT)
+ lstrlen(pName) + 1;
*ppDN = new TCHAR [dwLen];
BAIL_IF_NULL(*ppDN, E_OUTOFMEMORY);
wsprintf(*ppDN, ILS_CONF_DN_FORMAT, pName, m_pContainer);
return S_OK;
}
HRESULT CILSDirectory::MakeUserCN(
IN TCHAR * pName,
IN TCHAR * pAddress,
OUT TCHAR ** ppCN,
OUT DWORD * pdwIP
)
/*++
Routine Description:
Construct a User's CN based on username and machine name. The machine
name is resolved first the get the fully qualified DNS name. The CN is
in the following format: email\DNSname.
Arguments:
pName - The name of the user.
pAddress - The machine name.
ppCN - The CN returned.
pdwIP - The resolved IP address of the machine. Used later
for Netmeeting. If this is NULL, then we don't care
about the IP.
Return Value:
HRESULT.
--*/
{
char *pchFullDNSName;
if ( pdwIP == NULL )
{
BAIL_IF_FAIL(ResolveHostName(0, pAddress, &pchFullDNSName, NULL),
"can't resolve host name");
}
else
{
// we care about the IP, so we must be publishing a user object
// as opposed to refreshing or deleting. Make sure we use the
// same IP as the interface that's used to reach the ILS server.
BAIL_IF_FAIL(ResolveHostName(m_dwInterfaceAddress, pAddress, &pchFullDNSName, pdwIP),
"can't resolve host name (matching interface address)");
}
DWORD dwLen = lstrlen(DYNAMIC_USER_CN_FORMAT)
+ lstrlen(pName) + lstrlenA(pchFullDNSName);
*ppCN = new TCHAR [dwLen + 1];
BAIL_IF_NULL(*ppCN, E_OUTOFMEMORY);
wsprintf(*ppCN, DYNAMIC_USER_CN_FORMAT, pName, pchFullDNSName);
return S_OK;
}
HRESULT CILSDirectory::MakeUserDN(
IN TCHAR * pCN,
OUT TCHAR ** ppDNRTPerson,
OUT TCHAR ** ppDNNMPerson
)
/*++
Routine Description:
Construct the DNs for a user used in the Dynamic container and
the Netmeeting's container.
Arguments:
pCN - the CN of a user.
ppDNRTPerson - The DN of the user in the dynamic container.
ppDNNMPerson - The DN of the user in NetMeeting's container.
Return Value:
HRESULT.
--*/
{
// construct the DN for RTPerson.
DWORD dwLen = lstrlen(m_pContainer)
+ lstrlen(DYNAMIC_USER_DN_FORMAT) + lstrlen(pCN);
*ppDNRTPerson = new TCHAR [dwLen + 1];
BAIL_IF_NULL(*ppDNRTPerson, E_OUTOFMEMORY);
wsprintf(*ppDNRTPerson, DYNAMIC_USER_DN_FORMAT, pCN, m_pContainer);
// construct the DN for NetMeeting's RTApplicationUser.
dwLen = lstrlen(m_pNMContainer)
+ lstrlen(DYNAMIC_USER_DN_FORMAT) + lstrlen(pCN);
*ppDNNMPerson = new TCHAR [dwLen + 1];
if (*ppDNNMPerson == NULL)
{
delete *ppDNRTPerson;
*ppDNRTPerson = NULL;
return E_OUTOFMEMORY;
}
wsprintf(*ppDNNMPerson, DYNAMIC_USER_DN_FORMAT, pCN, m_pNMContainer);
return S_OK;
}
HRESULT CILSDirectory::AddConferenceComplete(BOOL fModify,
LDAP * ldap,
TCHAR ** ppDN,
LDAPMod ** mods,
DWORD TTL1,
DWORD TTL2)
{
if (fModify)
{
// Call the modify function to modify the object.
BAIL_IF_LDAP_FAIL(DoLdapModify(FALSE, ldap, *ppDN, mods),
"modify conference");
}
else
{
// Call the add function to create the object.
BAIL_IF_LDAP_FAIL(DoLdapAdd(ldap, *ppDN, mods), "add conference");
// next set the TTL value for this object
BAIL_IF_FAIL(::SetTTL(ldap, *ppDN, (TTL1 == 0) ? TTL2 : TTL1),
"Set ttl for conference");
}
return S_OK;
}
HRESULT CILSDirectory::AddConference(
IN ITDirectoryObject *pDirectoryObject,
IN BOOL fModify
)
/*++
Routine Description:
Add a new conference to the ILS server.
Arguments:
pDirectoryObject - a pointer to the conference.
fModify - true if called by MofifyDirectoryObject
false if called by AddDirectoryObject
Return Values:
Value Where defined What it means
----- ------------- -------------
HRESULT from ATL QueryInterface
HRESULT from pDirectoryObject->get_Name
HRESULT from MakeConferenceDN
HRESULT from pObjectPrivate->GetAttribute(MA_PROTOCOL)
HRESULT from pObjectPrivate->GetAttribute(MA_CONFERENCE_BLOB)
HRESULT from GetSecurityDescriptor
HRESULT from
--*/
{
// first query the private interface for attributes.
CComPtr <ITDirectoryObjectPrivate> pObjectPrivate;
BAIL_IF_FAIL(
pDirectoryObject->QueryInterface(
IID_ITDirectoryObjectPrivate,
(void **)&pObjectPrivate
),
"can't get the private directory object interface");
// Get the name of the conference.
CBstr bName;
BAIL_IF_FAIL(pDirectoryObject->get_Name(&bName),
"get conference name");
// Construct the DN of the object.
CTstr pDN;
BAIL_IF_FAIL(
MakeConferenceDN(bName, &pDN), "construct DN for conference"
);
// Get the protocol and the blob.
CBstr bProtocol, bBlob;
BAIL_IF_FAIL(pObjectPrivate->GetAttribute(MA_PROTOCOL, &bProtocol),
"get conference protocol");
BAIL_IF_FAIL(pObjectPrivate->GetAttribute(MA_CONFERENCE_BLOB, &bBlob),
"get conference Blob");
// Get the Security Descriptor. The pointer pSD is just a copy of a pointer
// in the Conference object; the conference object retains ownership of the
// data and we must be careful not to delete or modify this data.
char * pSD;
DWORD dwSDSize;
BAIL_IF_FAIL(pObjectPrivate->GetConvertedSecurityDescriptor(&pSD, &dwSDSize),
"get conference security descriptor");
// Get the TTL setting.
DWORD dwTTL;
BAIL_IF_FAIL(pObjectPrivate->GetTTL(&dwTTL), "get conference TTL");
// 4 attributes needs to be published.
static const DWORD DWATTRIBUTES = 4;
// Fist fill the modify structures required by LDAP.
LDAPMod mod[DWATTRIBUTES];
LDAPMod* mods[DWATTRIBUTES + 1];
DWORD dwCount = 0;
// The objectclass attribute.
TCHAR * objectClass[] =
{(WCHAR *)ILS_RTCONFERENCE, (WCHAR *)DYNAMICOBJECT, NULL};
if (!fModify)
{
mod[dwCount].mod_values = objectClass;
mod[dwCount].mod_op = LDAP_MOD_REPLACE;
mod[dwCount].mod_type = (WCHAR *)OBJECTCLASS;
dwCount ++;
}
// The protocol attribute.
TCHAR * protocol[] = {(WCHAR *)bProtocol, NULL};
mod[dwCount].mod_values = protocol;
mod[dwCount].mod_op = LDAP_MOD_REPLACE;
mod[dwCount].mod_type = (WCHAR *)RTConferenceAttributeName(MA_PROTOCOL);
dwCount ++;
// The blob attribute.
TCHAR * blob[] = {(WCHAR *)bBlob, NULL};
mod[dwCount].mod_values = blob;
mod[dwCount].mod_op = LDAP_MOD_REPLACE;
mod[dwCount].mod_type =
(WCHAR *)RTConferenceAttributeName(MA_CONFERENCE_BLOB);
dwCount ++;
// ZoltanS fix: these locals should not be within the "if" below... if
// they are, they might be deallocated before the function returns.
// This has been broken for a long, long time (unrelated to IsModified
// stuff below).
berval BerVal;
berval *sd[] = {&BerVal, NULL};
HRESULT hr;
//
// If there is a security descriptor on the local object, perhaps send it
// to the server.
//
if ( (char*)pSD != NULL )
{
BOOL fSendIt = FALSE;
if ( ! fModify )
{
//
// We are trying to add the conference, so we definitely need
// to send the security descriptor. Note that we even want
// to send it if it hasn't changed, as we may be sending it to
// some new server other than where we got it (if this conference
// object was retrieved from a server in the first place).
//
fSendIt = TRUE;
}
else
{
//
// We are trying to modify the conference, so we send the
// security descriptor if it has changed.
//
VARIANT_BOOL fChanged;
hr = pObjectPrivate->get_SecurityDescriptorIsModified( &fChanged );
if ( SUCCEEDED( hr ) && ( fChanged == VARIANT_TRUE ) )
{
fSendIt = TRUE;
}
}
if ( fSendIt )
{
//
// We've decided to send the ACL. Fail before adding / modifying
// the conference if the ACL is unsafe (would prevent us from
// later deleting or modifying our own conference).
//
hr = TestAclSafety(pSD, dwSDSize);
if ( FAILED(hr) )
{
LOG((MSP_ERROR, "CILSDirectory::AddConference - "
"ACL appears unsafe -- exit 0x%08x", hr));
return hr;
}
//
// We know we want to send the ACL and we can safely send the ACL.
// Fill in the mods to send it.
//
BerVal.bv_len = dwSDSize;
BerVal.bv_val = (char*)pSD;
mod[dwCount].mod_bvalues = sd;
mod[dwCount].mod_op = LDAP_MOD_REPLACE | LDAP_MOD_BVALUES;
mod[dwCount].mod_type = (WCHAR *)NT_SECURITY_DESCRIPTOR;
dwCount ++;
}
}
//
// All done with the mods. Package them up and write to the server.
//
DWORD i;
for (i = 0; i < dwCount; i ++)
{
mods[i] = &mod[i];
}
mods[i] = NULL;
LOG((MSP_INFO, "%S %S", fModify ? _T("modifying") : _T("adding"), pDN));
hr = AddConferenceComplete(fModify, m_ldap, &pDN, mods, dwTTL, m_TTL);
if ( SUCCEEDED(hr) )
{
pObjectPrivate->put_SecurityDescriptorIsModified( VARIANT_FALSE );
}
return hr;
}
//////////////////////////////////////////////////////////////////////////////
//
// This method tests if the given ACL (security descriptor) is "safe".
// "Safe" is defined as allowing the creator to modify and delete the
// object; modification is tested via the TTL field.
//
// This test is needed to prevent leaving "ghost" objects on the server
// when modifying the TTL of a newly-created conference fails because of
// insfufficient access rights. This will happen if a user messes up
// ACL creation or if the server does not understand the domain trustees
// in the ACL (e.g., server machine is not in a domain).
//
// The test is performed by creating a "dummy" test object with a random
// name in the container normally used for conferences. The object will
// not show up in a normal conference or user enumeration because it does
// not have the required attributes.
//
//
HRESULT CILSDirectory::TestAclSafety(
IN char * pSD,
IN DWORD dwSDSize
)
{
LOG((MSP_TRACE, "CILSDirectory::TestACLSafety - enter"));
//
// First fill in the modify structures required by LDAP.
// We use only the object class and the security descriptor.
// Therefore this will not show up as a valid conference
// during an enumeration.
//
static const DWORD DWATTRIBUTES = 2;
LDAPMod mod[DWATTRIBUTES];
LDAPMod* mods[DWATTRIBUTES + 1];
DWORD dwCount = 0;
TCHAR * objectClass[] =
{(WCHAR *)ILS_RTCONFERENCE, (WCHAR *)DYNAMICOBJECT, NULL};
mod[dwCount].mod_values = objectClass;
mod[dwCount].mod_op = LDAP_MOD_REPLACE;
mod[dwCount].mod_type = (WCHAR *)OBJECTCLASS;
dwCount ++;
berval BerVal;
berval *sd[] = {&BerVal, NULL};
BerVal.bv_len = dwSDSize;
BerVal.bv_val = (char*)pSD;
mod[dwCount].mod_bvalues = sd;
mod[dwCount].mod_op = LDAP_MOD_REPLACE | LDAP_MOD_BVALUES;
mod[dwCount].mod_type = (WCHAR *)NT_SECURITY_DESCRIPTOR;
dwCount ++;
DWORD i;
for (i = 0; i < dwCount; i ++)
{
mods[i] = &mod[i];
}
mods[i] = NULL;
//
// Try to add an object in the dynamic container with the above mods.
// Use a name composed of a random number printed to a string. If the
// names happen to conflict with another such dummy conference, then
// try again in a loop.
//
// Randomization is apparently per-DLL -- Sdpblb.dll does srand() but
// it doesn't seem to affect rand() calls within rend.dll. We therefore do
// srand( time( NULL ) ) on DLL_PROCESS_ATTACH.
//
HRESULT hr;
int iRandomNumber;
TCHAR ptszRandomNumber[30];
TCHAR * pDN = NULL;
do
{
if ( pDN != NULL )
{
delete pDN;
}
iRandomNumber = rand();
wsprintf(ptszRandomNumber, _T("%d"), iRandomNumber);
hr = CILSDirectory::MakeConferenceDN(ptszRandomNumber, &pDN);
if ( FAILED(hr) )
{
LOG((MSP_ERROR, "CILSDirectory::TestACLSafety - "
"test DN construction failed - exit 0x%08x", hr));
return hr;
}
LOG((MSP_TRACE, "CILSDirectory::TestACLSafety - "
"trying to create test object DN %S", pDN));
hr = GetLdapHResultIfFailed( DoLdapAdd(m_ldap, pDN, mods) );
}
while ( hr == GetLdapHResultIfFailed( LDAP_ALREADY_EXISTS ) );
if ( FAILED(hr) )
{
LOG((MSP_ERROR, "CILSDirectory::TestACLSafety - "
"test addition failed and not duplicate - exit 0x%08x", hr));
delete pDN;
return hr;
}
//
// Now that we have the test object, try modifying it.
//
LOG((MSP_TRACE, "CILSDirectory::TestACLSafety - "
"trying to modify test object..."));
HRESULT hrModify = ::SetTTL( m_ldap, pDN, MINIMUM_TTL );
//
// Now delete it. We do this even if we already know that the ACL is bad
// because the modify failed; we want to get rid of the object if
// possible.
//
LOG((MSP_TRACE, "CILSDirectory::TestACLSafety - "
"trying to delete test object..."));
hr = GetLdapHResultIfFailed( DoLdapDelete(m_ldap, pDN) );
delete pDN;
//
// Now determine the verdict and return.
//
if ( FAILED(hr) )
{
LOG((MSP_ERROR, "CILSDirectory::TestACLSafety - "
"test deletion (+ modification?) failed - ACL unsafe - "
"exit 0x%08x", hr));
return hr;
}
if ( FAILED( hrModify ) )
{
LOG((MSP_ERROR, "CILSDirectory::TestACLSafety - "
"test deletion ok; test modification failed - ACL unsafe - "
"exit 0x%08x", hrModify));
return hrModify;
}
LOG((MSP_TRACE, "CILSDirectory::TestACLSafety - exit S_OK"));
return S_OK;
}
HRESULT CILSDirectory::PublishRTPerson(
IN TCHAR * pCN,
IN TCHAR * pDN,
IN TCHAR * pIPAddress,
IN DWORD dwTTL,
IN BOOL fModify,
IN char * pSD,
IN DWORD dwSDSize
)
/*++
Routine Description:
Create a RTPerson object in the dynamic container.
Arguments:
pCN - The cn of the user.
pDN - The dn of the user.
pIPAddress - The ip address of the machine.
dwTTL - The ttl of this object.
fModify - modify or add.
Return Value:
HRESULT.
--*/
{
// UPDATE THIS whenever you add an explicitly set attribute below
// (ie, not one of those with default values in the array associated
// with NUM_NM_RTPERSON_ATTRIBUTES).
static const DWORD DWCOREATTRIBUTES = 5;
// Fist create the object.
LDAPMod mod[NUM_NM_RTPERSON_ATTRIBUTES + DWCOREATTRIBUTES];
DWORD dwCount = 0;
//
// We are not allowed to modify the object class. Therefore we only mention
// this if we are adding the object to the server, not modifying it.
//
// Fix: this is allocated on the stack, so we must do it here; if we stick
// it inside the if below, it gets deallocated immediately.
TCHAR * objectClass[] = {(WCHAR *)ILS_RTPERSON, (WCHAR *)DYNAMICOBJECT, NULL};
if ( ! fModify )
{
// Object class.
// only need this attribute if not modifying.
mod[dwCount].mod_values = objectClass;
mod[dwCount].mod_op = LDAP_MOD_REPLACE;
mod[dwCount].mod_type = (WCHAR *)OBJECTCLASS;
dwCount ++;
}
// IP address.
TCHAR * IPPhone[] = {(WCHAR *)pIPAddress, NULL};
mod[dwCount].mod_values = IPPhone;
mod[dwCount].mod_op = LDAP_MOD_REPLACE;
mod[dwCount].mod_type = (WCHAR *)ILS_IPADDRESS;
dwCount ++;
//
// Make pUserName the user portion of the CN (user]machine).
//
CTstr pUserName = new TCHAR[ lstrlen(pCN) + 1 ];
if ( pUserName == NULL )
{
return E_OUTOFMEMORY;
}
lstrcpy( pUserName, pCN );
WCHAR * pCloseBracket = wcschr( pUserName, CLOSE_BRACKET_CHARACTER );
if ( pCloseBracket == NULL )
{
// this is not the format generated by us -- very strange indeed!
return E_UNEXPECTED;
}
*pCloseBracket = NULL_CHARACTER;
//
// We now have pUserName containing just the user name.
// No need to delete it explicitly, as it is contained in a smart pointer.
//
// email: the user name
TCHAR * Email[] = { (WCHAR *) ( (TCHAR *) pUserName ), NULL };
mod[dwCount].mod_values = Email;
mod[dwCount].mod_op = LDAP_MOD_REPLACE;
mod[dwCount].mod_type = (WCHAR *)EMAIL;
dwCount ++;
// given name: the same as email (surname is just a space)
mod[dwCount].mod_values = Email;
mod[dwCount].mod_op = LDAP_MOD_REPLACE;
mod[dwCount].mod_type = (WCHAR *)GIVEN_NAME;
dwCount ++;
DWORD i;
for (i = 0; i < NUM_NM_RTPERSON_ATTRIBUTES; i ++)
{
mod[dwCount].mod_op = LDAP_MOD_REPLACE;
mod[dwCount].mod_type = (WCHAR *)g_NMRTPersonAttributeNames[i];
mod[dwCount].mod_values = (WCHAR **)&g_NMRTPersonAttributeValues[i * 2];
dwCount ++;
}
// these locals should not be within the "if" below... if
// they are, they might be deallocated before the function returns.
berval BerVal;
berval *sd[] = {&BerVal, NULL};
// The security descriptor attribute.
if ((char*)pSD != NULL)
{
BerVal.bv_len = dwSDSize;
BerVal.bv_val = (char*)pSD;
mod[dwCount].mod_bvalues = sd;
mod[dwCount].mod_op = LDAP_MOD_REPLACE | LDAP_MOD_BVALUES;
mod[dwCount].mod_type = (WCHAR *)NT_SECURITY_DESCRIPTOR;
dwCount ++;
}
LDAPMod* mods[NUM_NM_RTPERSON_ATTRIBUTES + DWCOREATTRIBUTES + 1];
for (i = 0; i < dwCount; i ++)
{
mods[i] = &mod[i];
}
mods[i] = NULL;
if (fModify)
{
LOG((MSP_INFO, "modifying %S", pDN));
// Call the modify function to modify the object.
BAIL_IF_LDAP_FAIL(DoLdapModify(FALSE, m_ldap, pDN, mods), "modify RTPerson");
}
else
{
LOG((MSP_INFO, "adding %S", pDN));
// Call the add function to create the object.
BAIL_IF_LDAP_FAIL(DoLdapAdd(m_ldap, pDN, mods), "add RTPerson");
// next set the TTL value for this object
BAIL_IF_FAIL(::SetTTL(m_ldap, pDN, (dwTTL == 0) ? m_TTL : dwTTL),
"Set ttl for RTPerson");
}
return S_OK;
}
HRESULT CILSDirectory::PublishNMPerson(
IN TCHAR * pCN,
IN TCHAR * pDN,
IN TCHAR * pDNRTPerson,
IN DWORD dwTTL,
IN BOOL fModify,
IN char * pSD,
IN DWORD dwSDSize
)
/*++
Routine Description:
Create a applicationUser object in the netmeeting's container.
Arguments:
pCN - The cn of the user.
pDN - The dn of the applicationUser object.
pDNRTPerson - The dn of the RTPerson object in the dynamic container.
dwTTL - The ttl of this object.
fModify - modify or add.
Return Value:
HRESULT.
--*/
{
// UPDATE THIS whenever you add an explicitly set attribute below
// (ie, not one of those with default values in the array associated
// with NUM_NM_ATTRIBUTES).
static const DWORD DWCOREATTRIBUTES = 3;
// Fist create the object.
LDAPMod mod[NUM_NM_ATTRIBUTES + DWCOREATTRIBUTES]; // The modify sturctures used by LDAP
DWORD dwCount = 0;
//
// We are not allowed to modify the object class. Therefore we only mention
// this if we are adding the object to the server, not modifying it.
//
// Fix: this is allocated on the stack, so we must do it here; if we stick
// it inside the if below, it gets deallocated immediately.
TCHAR * objectClass[] =
{(WCHAR *)RTAPPLICATIONUSER, (WCHAR *)DYNAMICOBJECT, NULL};
if ( ! fModify )
{
// Object class.
mod[dwCount].mod_values = objectClass;
mod[dwCount].mod_op = LDAP_MOD_REPLACE;
mod[dwCount].mod_type = (WCHAR *)OBJECTCLASS;
dwCount ++;
}
// IP address.
TCHAR * rtPerson[] = {(WCHAR *)pDNRTPerson, NULL};
mod[dwCount].mod_values = rtPerson;
mod[dwCount].mod_op = LDAP_MOD_REPLACE;
mod[dwCount].mod_type = (WCHAR *)USEROBJECT;
dwCount ++;
for (DWORD i = 0; i < NUM_NM_ATTRIBUTES; i ++)
{
mod[dwCount].mod_op = LDAP_MOD_REPLACE;
mod[dwCount].mod_type = (WCHAR *)g_NMAttributeNames[i];
mod[dwCount].mod_values = (WCHAR **)&g_NMAttributeValues[i * 2];
dwCount ++;
}
// these locals should not be within the "if" below... if
// they are, they might be deallocated before the function returns.
berval BerVal;
berval *sd[] = {&BerVal, NULL};
// The security descriptor attribute.
if ((char*)pSD != NULL)
{
BerVal.bv_len = dwSDSize;
BerVal.bv_val = (char*)pSD;
mod[dwCount].mod_bvalues = sd;
mod[dwCount].mod_op = LDAP_MOD_REPLACE | LDAP_MOD_BVALUES;
mod[dwCount].mod_type = (WCHAR *)NT_SECURITY_DESCRIPTOR;
dwCount ++;
}
LDAPMod* mods[NUM_NM_ATTRIBUTES + DWCOREATTRIBUTES + 1];
for (i = 0; i < dwCount; i ++)
{
mods[i] = &mod[i];
}
mods[i] = NULL;
if (fModify)
{
LOG((MSP_INFO, "modifying %S", pDN));
// Call the modify function to modify the object.
BAIL_IF_LDAP_FAIL(DoLdapModify(FALSE, m_ldap, pDN, mods), "modify NMPerson");
}
else
{
LOG((MSP_INFO, "adding %S", pDN));
// Call the add function to create the object.
BAIL_IF_LDAP_FAIL(DoLdapAdd(m_ldap, pDN, mods), "add NMPerson");
// next set the TTL value for this object
BAIL_IF_FAIL(::SetTTL(m_ldap, pDN, (dwTTL == 0) ? m_TTL : dwTTL),
"Set ttl for NMPerson");
}
return S_OK;
}
HRESULT CILSDirectory::AddObjectToRefresh(
IN WCHAR *pDN,
IN long TTL
)
{
//
// Add a refresh table entry to the refresh table. The refresh table's add
// method does an element-by-element copy of the entry that it is given.
// This just copies the string pointer, so we need to allocate and copy
// the string here.
//
RefreshTableEntry entry;
entry.dwTTL = TTL;
entry.pDN = new WCHAR[ wcslen(pDN) + 1 ];
if ( entry.pDN == NULL )
{
LOG((MSP_ERROR, "Cannot allocate string for adding to refresh table"));
return E_OUTOFMEMORY;
}
wcscpy( entry.pDN, pDN );
//
// Now add it to the refresh table.
//
BOOL fSuccess = m_RefreshTable.add(entry);
if ( ! fSuccess )
{
LOG((MSP_ERROR, "Cannot add object to the refresh table"));
return E_OUTOFMEMORY;
}
return S_OK;
}
HRESULT CILSDirectory::RemoveObjectToRefresh(
IN WCHAR *pDN
)
{
//
// For each item in our refresh table
//
for ( DWORD i = 0; i < m_RefreshTable.size(); i++ )
{
//
// If the desired DN matches the one in this item
// then remove it and return success
//
if ( ! _wcsicmp( m_RefreshTable[i].pDN, pDN ) )
{
//
// We new'ed the string when we added the entry.
//
delete m_RefreshTable[i].pDN;
m_RefreshTable.removeAt(i);
return S_OK;
}
}
//
// If we get here then there was no matching item
//
LOG((MSP_ERROR, "Cannot remove object from the refresh table"));
return E_FAIL;
}
HRESULT CILSDirectory::AddUser(
IN ITDirectoryObject *pDirectoryObject,
IN BOOL fModify
)
/*++
Routine Description:
Publish a new user object.
Arguments:
pDirectoryObject - The object to be published.
Return Value:
HRESULT.
--*/
{
// First find the interface for attributes.
CComPtr <ITDirectoryObjectPrivate> pObjectPrivate;
BAIL_IF_FAIL(
pDirectoryObject->QueryInterface(
IID_ITDirectoryObjectPrivate,
(void **)&pObjectPrivate
),
"can't get the private directory object interface");
// Get the user name.
CBstr bName;
BAIL_IF_FAIL(pDirectoryObject->get_Name(&bName),
"get user name");
// Get the user's machine name.
CBstr bIPPhone;
BAIL_IF_FAIL(pObjectPrivate->GetAttribute(UA_IPPHONE_PRIMARY, &bIPPhone),
"get IPPhone");
// Resolve the machine name and construct the CN for the user.
CTstr pCN;
DWORD dwIP;
BAIL_IF_FAIL(
MakeUserCN(bName, bIPPhone, &pCN, &dwIP),
"construct CN for user"
);
// Construct the DNs for the RTPerson object and applicationuser object.
CTstr pDNRTPerson, pDNNMPerson;
BAIL_IF_FAIL(
MakeUserDN(pCN, &pDNRTPerson, &pDNNMPerson),
"construct DN for user"
);
// convert the IP address to a string.
// This convertion is because of NetMeeting.
TCHAR IPAddress[80];
wsprintf(IPAddress, _T("%u"), dwIP);
DWORD dwTTL;
BAIL_IF_FAIL(pObjectPrivate->GetTTL(&dwTTL), "get User TTL");
// Get the Security Descriptor. The pointer pSD is just a copy of a pointer
// in the Conference object; the conference object retains ownership of the
// data and we must be careful not to delete or modify this data.
char * pSD;
DWORD dwSDSize;
BAIL_IF_FAIL(pObjectPrivate->GetConvertedSecurityDescriptor(&pSD, &dwSDSize),
"get user object security descriptor");
VARIANT_BOOL fChanged;
if ( SUCCEEDED( pObjectPrivate->
get_SecurityDescriptorIsModified( &fChanged ) ) )
{
if ( fChanged == VARIANT_FALSE )
{
pSD = NULL; // do NOT delete the string (see above)
dwSDSize = 0;
}
}
// Create a rtApplicationUser under netmeeting's container. If we are adding
// and this returns already exists, then bailing is fine.
BAIL_IF_FAIL(PublishNMPerson(pCN, pDNNMPerson, pDNRTPerson, dwTTL, fModify, pSD, dwSDSize),
"Can't publish a NMPerson");
// Create the RTPerson in the dynamic container. According to Sun Shaw,
// this succeeds even if the object already exists, due to NetMeeting's
// requirements. (Yet another case of ILS being designed around NM.)
// We don't depend on the existence of that bug, however.
HRESULT hr = PublishRTPerson(pCN, pDNRTPerson, IPAddress, dwTTL, fModify, pSD, dwSDSize);
if (FAILED(hr))
{
LOG((MSP_ERROR, "Can't publish a RTPerson, hr:%x", hr));
if (!fModify)
{
// Call the delete function to delete the NMPerson object.
// This is needed to avoid leaving a phantom NMPerson when
// the NMPerson creation succeeds but we fail.
DoLdapDelete(m_ldap, pDNNMPerson);
}
return hr;
}
if (m_fAutoRefresh)
{
AddObjectToRefresh(pDNRTPerson, m_TTL);
AddObjectToRefresh(pDNNMPerson, m_TTL);
}
return S_OK;
}
HRESULT CILSDirectory::DeleteConference(
IN ITDirectoryObject *pDirectoryObject
)
/*++
Routine Description:
Delete a conference from the ILS server.
Arguments:
pDirectoryObject - The object to be deleted.
Return Value:
HRESULT.
--*/
{
// Get the name.
CBstr bName;
BAIL_IF_FAIL(pDirectoryObject->get_Name(&bName),
"get conference name");
// construct the DN
CTstr pDN;
BAIL_IF_FAIL(
MakeConferenceDN(bName, &pDN), "construct DN for conference"
);
LOG((MSP_INFO, "deleting %S", pDN));
// Call the add function to create the object.
BAIL_IF_LDAP_FAIL(DoLdapDelete(m_ldap, pDN), "delete conference");
return S_OK;
}
HRESULT CILSDirectory::DeleteUser(
IN ITDirectoryObject *pDirectoryObject
)
/*++
Routine Description:
Delete a user from the ILS server.
Arguments:
pDirectoryObject - The object to be deleted.
Return Value:
HRESULT.
--*/
{
// First find the interface for attributes.
CComPtr <ITDirectoryObjectPrivate> pObjectPrivate;
BAIL_IF_FAIL(
pDirectoryObject->QueryInterface(
IID_ITDirectoryObjectPrivate,
(void **)&pObjectPrivate
),
"can't get the private directory object interface");
// Get the user name.
CBstr bName;
BAIL_IF_FAIL(pDirectoryObject->get_Name(&bName),
"get user name");
// Get the user's machine name.
CBstr bIPPhone;
BAIL_IF_FAIL(pObjectPrivate->GetAttribute(UA_IPPHONE_PRIMARY, &bIPPhone),
"get IPPhone");
// Resolve the machine name and construct the CN for the user.
CTstr pCN;
BAIL_IF_FAIL(
MakeUserCN(bName, bIPPhone, &pCN, NULL),
"construct CN for user"
);
// Construct the DNs for the RTPerson object and applicationuser object.
CTstr pDNRTPerson, pDNNMPerson;
BAIL_IF_FAIL(
MakeUserDN(pCN, &pDNRTPerson, &pDNNMPerson),
"construct DN for user"
);
//
// Now delete the TWO objects from the server.
//
HRESULT hrFinal = S_OK;
HRESULT hr;
ULONG ulResult;
// Call the delete function to delete the RTPerson object, but keep
// going if it fails, noting the error code.
LOG((MSP_INFO, "deleting %S", pDNRTPerson));
ulResult = DoLdapDelete(m_ldap, pDNRTPerson);
hr = LogAndGetLdapHResult(ulResult, _T("delete RTPerson"));
if (FAILED(hr)) { hrFinal = hr; }
// Call the delete function to delete the applicationuser object, but
// keep going if it fails, noting the error code.
LOG((MSP_INFO, "deleting %S", pDNNMPerson));
ulResult = DoLdapDelete(m_ldap, pDNNMPerson);
hr = LogAndGetLdapHResult(ulResult, _T("delete NMPerson"));
if (FAILED(hr)) { hrFinal = hr; }
// ZoltanS: we now always remove the refresh objects, even if removal failed.
if (m_fAutoRefresh)
{
RemoveObjectToRefresh(pDNRTPerson);
RemoveObjectToRefresh(pDNNMPerson);
}
return hrFinal;
}
HRESULT CILSDirectory::RefreshUser(
IN ITDirectoryObject *pDirectoryObject
)
/*++
Routine Description:
Refresh a user's TTL on the ILS server.
Arguments:
pDirectoryObject - The object to be refreshed.
Return Value:
HRESULT.
--*/
{
// First find the interface for attributes.
CComPtr <ITDirectoryObjectPrivate> pObjectPrivate;
BAIL_IF_FAIL(
pDirectoryObject->QueryInterface(
IID_ITDirectoryObjectPrivate,
(void **)&pObjectPrivate
),
"can't get the private directory object interface");
// Get the user name.
CBstr bName;
BAIL_IF_FAIL(pDirectoryObject->get_Name(&bName),
"get user name");
// Get the user's machine name.
CBstr bIPPhone;
BAIL_IF_FAIL(pObjectPrivate->GetAttribute(UA_IPPHONE_PRIMARY, &bIPPhone),
"get IPPhone");
// Resolve the machine name and construct the CN for the user.
CTstr pCN;
BAIL_IF_FAIL(
MakeUserCN(bName, bIPPhone, &pCN, NULL),
"construct CN for user"
);
// Construct the DNs for the RTPerson object and applicationuser object.
CTstr pDNRTPerson, pDNNMPerson;
BAIL_IF_FAIL(
MakeUserDN(pCN, &pDNRTPerson, &pDNNMPerson),
"construct DN for user"
);
// ZoltanS: this is fine; if one of them doesn't exist, then we're in trouble,
// so return the error.
// set ttl for the RTPerson object.
BAIL_IF_LDAP_FAIL(SetTTL(m_ldap, pDNRTPerson, m_TTL), "set ttl for RTPerson");
// set ttl for the NMPerson object.
BAIL_IF_LDAP_FAIL(SetTTL(m_ldap, pDNNMPerson, m_TTL), "set ttl for NMPerson");
//
// ZoltanS: If the app has enabled auto-refresh but does not add or modify its
// user object, but rather, just refreshes it (because the object is still around
// from a previous instance of the app, and "add" would fail), we still need to
// autorefresh it, as that's what the app wants.
//
if (m_fAutoRefresh)
{
AddObjectToRefresh(pDNRTPerson, m_TTL);
AddObjectToRefresh(pDNNMPerson, m_TTL);
}
return S_OK;
}
HRESULT CILSDirectory::CreateConference(
IN LDAPMessage * pEntry,
OUT ITDirectoryObject ** ppObject
)
/*++
Routine Description:
Create a conference object from the result of the LDAP search.
Arguments:
pEntry - The search result.
ppObject - The object to be created.
Return Value:
HRESULT.
--*/
{
CBstr bName, bProtocol, bBlob;
// Get the name of the conference.
BAIL_IF_FAIL(
::GetAttributeValue(
m_ldap,
pEntry,
RTConferenceAttributeName(MA_MEETINGNAME),
&bName
),
"get the conference name"
);
// Get the protocol ID of the conference.
BAIL_IF_FAIL(
::GetAttributeValue(
m_ldap,
pEntry,
RTConferenceAttributeName(MA_PROTOCOL),
&bProtocol
),
"get the conference protocol"
);
// Get the conference blob of the conference.
BAIL_IF_FAIL(
::GetAttributeValue(
m_ldap,
pEntry,
RTConferenceAttributeName(MA_CONFERENCE_BLOB),
&bBlob
),
"get the conference blob"
);
char * pSD = NULL;
DWORD dwSDSize = 0;
::GetAttributeValueBer(
m_ldap,
pEntry,
NT_SECURITY_DESCRIPTOR,
&pSD,
&dwSDSize
);
// Create an empty conference.
HRESULT hr = ::CreateConferenceWithBlob(bName,
bProtocol,
bBlob,
pSD,
dwSDSize,
ppObject);
if ( FAILED(hr) )
{
LOG((MSP_ERROR, "CILSDirectory::CreateConference - "
"CreateConferenceWithBlob failed 0x%08x", hr));
delete pSD;
return hr;
}
//
// If the above succeeded then the conference object has taken ownership
// of pSD.
//
return S_OK;
}
HRESULT CILSDirectory::CreateUser(
IN LDAPMessage * pEntry,
IN ITDirectoryObject ** ppObject
)
/*++
Routine Description:
Create a user object from the result of the LDAP search.
Arguments:
pEntry - The search result.
ppObject - The object to be created.
Return Value:
HRESULT.
--*/
{
CBstr bName;
// Get the name of the user.
BAIL_IF_FAIL(
::GetAttributeValue(
m_ldap,
pEntry,
RTPersonAttributeName(UA_USERNAME),
&bName
),
"get the user name"
);
CBstr bAddress;
// Grab the machine name from the name of the object. This can fail if
// we didn't publish the object (ie, it's a NetMeeting object). Also,
// check if the hostname isn't resolvable. In that case we also have
// to fall back on the IP address in the ipAddress attribute.
HRESULT hr;
hr = ParseUserName(bName, &bAddress);
if ( SUCCEEDED(hr) )
{
// Make sure we can get an IP from this name, at least for the moment.
// If not, release the name and indicate failure so we do our backup
// plan.
hr = ResolveHostName(0, bAddress, NULL, NULL);
if ( FAILED(hr) )
{
SysFreeString(bAddress);
}
}
if ( FAILED(hr) )
{
// In order to compatible with NetMeeting, we have to use IP address field.
CBstr bUglyIP;
BAIL_IF_FAIL(
::GetAttributeValue(
m_ldap,
pEntry,
RTPersonAttributeName(UA_IPPHONE_PRIMARY),
&bUglyIP
),
"get the user's IP address"
);
// We have to use NM's ugly format for the IP address. The IP address
// we got from netmeeting is a decimal string whose value is the dword
// value of the IP address in network order.
BAIL_IF_FAIL(UglyIPtoIP(bUglyIP, &bAddress), "Convert IP address");
}
// Create an empty user object.
CComPtr<ITDirectoryObject> pObject;
BAIL_IF_FAIL(::CreateEmptyUser(bName, &pObject), "CreateEmptyUser");
CComPtr <ITDirectoryObjectPrivate> pObjectPrivate;
BAIL_IF_FAIL(
pObject->QueryInterface(
IID_ITDirectoryObjectPrivate,
(void **)&pObjectPrivate
),
"can't get the private directory object interface");
// Set the user attributes.
BAIL_IF_FAIL(pObjectPrivate->SetAttribute(UA_IPPHONE_PRIMARY, bAddress),
"set ipAddress");
//
// Set the security descriptor on the object.
//
char * pSD = NULL;
DWORD dwSDSize = 0;
::GetAttributeValueBer(
m_ldap,
pEntry,
NT_SECURITY_DESCRIPTOR,
&pSD,
&dwSDSize
);
if ( pSD != NULL )
{
//
// Set the security descriptor in its "converted" (server) form.
//
hr = pObjectPrivate->PutConvertedSecurityDescriptor(pSD,
dwSDSize);
if ( FAILED(hr) )
{
LOG((MSP_ERROR, "PutConvertedSecurityDescriptor failed: %x", hr));
return hr;
}
}
*ppObject = pObject;
(*ppObject)->AddRef();
return S_OK;
}
HRESULT CILSDirectory::SearchObjects(
IN DIRECTORY_OBJECT_TYPE DirectoryObjectType,
IN BSTR pName,
OUT ITDirectoryObject *** pppDirectoryObject,
OUT DWORD * pdwSize
)
/*++
Routine Description:
Search the ILS server for given type of objects.
Arguments:
DirectoryObjectType - The type of the object.
pName - The name to search for.
pppDirectoryObject - The returned array of objects.
pdwSize - The size of the array.
Return Value:
HRESULT.
--*/
{
TCHAR *pRDN;
TCHAR *Attributes[NUM_MEETING_ATTRIBUTES + 1]; // This is big enough now.
// Fill the attributes want to be returned.
switch (DirectoryObjectType)
{
case OT_CONFERENCE:
pRDN = (WCHAR*)ILS_UIDEQUALS;
Attributes[0] = (WCHAR *)RTConferenceAttributeName(MA_MEETINGNAME);
Attributes[1] = (WCHAR *)RTConferenceAttributeName(MA_PROTOCOL);
Attributes[2] = (WCHAR *)RTConferenceAttributeName(MA_CONFERENCE_BLOB);
Attributes[3] = (WCHAR *)NT_SECURITY_DESCRIPTOR;
Attributes[4] = NULL;
break;
case OT_USER:
pRDN = (WCHAR*)CNEQUALS;
Attributes[0] = (WCHAR *)RTPersonAttributeName(UA_USERNAME);
Attributes[1] = (WCHAR *)RTPersonAttributeName(UA_IPPHONE_PRIMARY);
Attributes[2] = (WCHAR *)NT_SECURITY_DESCRIPTOR;
Attributes[3] = NULL;
break;
default:
return E_FAIL;
}
// Construct the filter of the search.
int cbRDN = lstrlen(pRDN);
int cbName = lstrlen(pName);
if( (( cbRDN + cbName + 2)<= cbRDN) ||
((cbRDN + cbName + 2) <= cbName) )
{
return E_FAIL;
}
CTstr pFilter = new TCHAR [cbRDN + cbName + 2];
BAIL_IF_NULL((TCHAR*)pFilter, E_OUTOFMEMORY);
lstrcpyn(pFilter, pRDN, cbRDN+1);
_ASSERTE( lstrlen(pFilter) == cbRDN );
lstrcpyn(pFilter+cbRDN, pName, cbName+1);
_ASSERTE( lstrlen(pFilter) == cbRDN+cbName );
if (pName[lstrlen(pName) - 1] != _T('*'))
{
lstrcat(pFilter, _T("*"));
}
// Search them.
CLdapMsgPtr pLdapMsg; // auto release message.
ULONG res = DoLdapSearch(
m_ldap, // ldap handle
m_pContainer, // schema dn
LDAP_SCOPE_ONELEVEL, // one level search
pFilter, // cn=Name or uid=Name
Attributes, // array of attribute names
FALSE, // return the attribute values
&pLdapMsg // search results
);
BAIL_IF_LDAP_FAIL(res, "search for objects");
// count the returned entries.
DWORD dwEntries = ldap_count_entries(m_ldap, pLdapMsg);
ITDirectoryObject ** pObjects = new PDIRECTORYOBJECT [dwEntries];
BAIL_IF_NULL(pObjects, E_OUTOFMEMORY);
// Create objects.
DWORD dwCount = 0;
LDAPMessage *pEntry = ldap_first_entry(m_ldap, pLdapMsg);
while (pEntry != NULL)
{
HRESULT hr;
switch (DirectoryObjectType)
{
case OT_CONFERENCE:
hr = CreateConference(pEntry, &pObjects[dwCount]);
break;
case OT_USER:
hr = CreateUser(pEntry, &pObjects[dwCount]);
break;
}
if (SUCCEEDED(hr))
{
dwCount ++;
}
// Get next entry.
pEntry = ldap_next_entry(m_ldap, pEntry);
}
*pppDirectoryObject = pObjects;
*pdwSize = dwCount;
return S_OK;
}
/////////////////////////////////////////////////////////////////////////////
// ILS Directory implementation
/////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CILSDirectory::get_DirectoryType (
OUT DIRECTORY_TYPE * pDirectoryType
)
// Get the type of the directory.
{
if ( IsBadWritePtr(pDirectoryType, sizeof(DIRECTORY_TYPE) ) )
{
LOG((MSP_ERROR, "Directory.GetType, invalid pointer"));
return E_POINTER;
}
CLock Lock(m_lock);
*pDirectoryType = m_Type;
return S_OK;
}
STDMETHODIMP CILSDirectory::get_DisplayName (
OUT BSTR *ppServerName
)
// Get the display name of the directory.
{
BAIL_IF_BAD_WRITE_PTR(ppServerName, E_POINTER);
CLock Lock(m_lock);
if (m_pServerName == NULL)
{
*ppServerName = SysAllocString(L"");
}
else
{
*ppServerName = SysAllocString(m_pServerName);
}
if (*ppServerName == NULL)
{
LOG((MSP_ERROR, "get_DisplayName: out of memory."));
return E_OUTOFMEMORY;
}
return S_OK;
}
STDMETHODIMP CILSDirectory::get_IsDynamic(
OUT VARIANT_BOOL *pfDynamic
)
// find out if the directory is a dynamic one, meaning the object will be
// deleted after TTL runs out.
{
if ( IsBadWritePtr( pfDynamic, sizeof(VARIANT_BOOL) ) )
{
LOG((MSP_ERROR, "Directory.get_IsDynamic, invalid pointer"));
return E_POINTER;
}
*pfDynamic = VARIANT_TRUE;
return S_OK;
}
STDMETHODIMP CILSDirectory::get_DefaultObjectTTL(
OUT long *pTTL // in seconds
)
// The default TTL for object created. It is used when the object doesn't set
// a TTL. Conference object always has a TTL based on the stoptime.
{
if ( IsBadWritePtr( pTTL, sizeof(long) ) )
{
LOG((MSP_ERROR, "Directory.get_default objec TTL, invalid pointer"));
return E_POINTER;
}
CLock Lock(m_lock);
*pTTL = m_TTL;
return S_OK;
}
STDMETHODIMP CILSDirectory::put_DefaultObjectTTL(
IN long TTL // in sechods
)
// Change the default TTL, must be bigger then five minutes.
{
CLock Lock(m_lock);
if (TTL < MINIMUM_TTL)
{
return E_INVALIDARG;
}
m_TTL = TTL;
return S_OK;
}
STDMETHODIMP CILSDirectory::EnableAutoRefresh(
IN VARIANT_BOOL fEnable
)
// Enable auto refresh. Add this directory to the work threads that
// will notify the directory to update its objects.
{
HRESULT hr;
// ZoltanS: either VARIANT_TRUE or TRUE will work
// in case the caller doesn't know better
if (fEnable)
{
// Add this directory to the notify list of the work thread.
if (FAILED(hr = g_RendThread.AddDirectory(this)))
{
LOG((MSP_ERROR,
"Can not add this directory to the thread, %x", hr));
return hr;
}
}
else
{
// Remove this directory from the notify list of the work thread.
if (FAILED(hr = g_RendThread.RemoveDirectory(this)))
{
LOG((MSP_ERROR,
"Can not remove this directory from the thread, %x", hr));
return hr;
}
}
// ZoltanS: either VARIANT_TRUE or TRUE will work
// in case the caller doesn't know better
m_lock.Lock();
m_fAutoRefresh = ( fEnable ? VARIANT_TRUE : VARIANT_FALSE );
m_lock.Unlock();
return S_OK;
}
STDMETHODIMP CILSDirectory::Connect(
IN VARIANT_BOOL fSecure
)
// Connect to the server, using secure port or normal port.
{
CLock Lock(m_lock);
if (m_ldap != NULL)
{
LOG((MSP_ERROR, "already connected."));
return RND_ALREADY_CONNECTED;
}
if (m_pServerName == NULL)
{
LOG((MSP_ERROR, "No server specified."));
return RND_NULL_SERVER_NAME;
}
// ZoltanS: either VARIANT_TRUE or TRUE will work
// in case the caller doesn't know better
if (fSecure)
{
// the port is flipped from regular port to ssl port.
m_wPort = GetOtherPort(m_wPort);
m_IsSsl = TRUE;
}
HRESULT hr = TryServer(m_wPort);
// We support legacy servers by also trying the normal LDAP port if
// the normal ILS port fails. If the user specified a special port,
// then we don't do anything behind their back!
if ( FAILED(hr) && (hr != E_OUTOFMEMORY) && (m_wPort == ILS_PORT))
{
m_wPort = LDAP_PORT;
hr = TryServer(m_wPort);
}
if ( SUCCEEDED(hr) )
{
// find out which interface we use to reach this server, to make sure
// we use that interface whenever we publish our own IP address
// If this fails we will not be able to publish anything, so fail!
hr = DiscoverInterface();
}
if( FAILED(hr))
{
if( m_ldap)
{
ldap_unbind(m_ldap);
m_ldap = NULL;
}
if( fSecure == VARIANT_TRUE )
{
hr = E_INVALIDARG;
}
}
return hr;
}
HRESULT CILSDirectory::DiscoverInterface(void)
{
LOG((MSP_INFO, "CILSDirectory::DiscoverInterface - enter"));
//
// Winsock must be initialized at this point.
//
//
// Get the IP address of the server we're using
//
DWORD dwIP; // The IP address of the destination ILS server
HRESULT hr = ResolveHostName(0, m_pServerName, NULL, &dwIP);
if ( FAILED(hr) )
{
LOG((MSP_ERROR, "CILSDirectory::DiscoverInterface - "
"can't resolve host name - "
"strange, because we could connect! - exit 0x%08x", hr));
return hr;
}
//
// allocate a "fake" control socket
//
SOCKET hSocket = WSASocket(AF_INET, // af
SOCK_DGRAM, // type
IPPROTO_IP, // protocol
NULL, // lpProtocolInfo
0, // g
0 // dwFlags
);
if ( hSocket == INVALID_SOCKET )
{
hr = HRESULT_FROM_ERROR_CODE(WSAGetLastError());
LOG((MSP_ERROR, "CILSDirectory::DiscoverInterface - "
"WSASocket gave an invalid socket - exit 0x%08x", hr));
return hr;
}
//
// Query for the interface address based on destination.
//
SOCKADDR_IN DestAddr;
DestAddr.sin_family = AF_INET;
DestAddr.sin_port = 0;
DestAddr.sin_addr.s_addr = dwIP;
SOCKADDR_IN LocAddr;
DWORD dwStatus;
DWORD dwLocAddrSize = sizeof(SOCKADDR_IN);
DWORD dwNumBytesReturned = 0;
dwStatus = WSAIoctl(
hSocket, // SOCKET s
SIO_ROUTING_INTERFACE_QUERY, // DWORD dwIoControlCode
&DestAddr, // LPVOID lpvInBuffer
sizeof(SOCKADDR_IN), // DWORD cbInBuffer
&LocAddr, // LPVOID lpvOUTBuffer
dwLocAddrSize, // DWORD cbOUTBuffer
&dwNumBytesReturned, // LPDWORD lpcbBytesReturned
NULL, // LPWSAOVERLAPPED lpOverlapped
NULL // LPWSAOVERLAPPED_COMPLETION_ROUTINE lpComplROUTINE
);
//
// Don't close the socket yet, because the closesocket() call will
// overwrite the WSAGetLastError value in the failure case!
//
// Check for error and then close the socket.
//
if ( dwStatus == SOCKET_ERROR )
{
hr = HRESULT_FROM_ERROR_CODE(WSAGetLastError());
LOG((MSP_ERROR, "CILSDirectory::DiscoverInterface - "
"WSAIoctl failed - exit 0x%08x", hr));
closesocket(hSocket);
return hr;
}
closesocket(hSocket);
//
// Success - save the returned address in our member variable.
// Stored in network byte order.
//
m_dwInterfaceAddress = LocAddr.sin_addr.s_addr;
LOG((MSP_INFO, "CILSDirectory::DiscoverInterface - exit S_OK"));
return S_OK;
}
//
// ITDirectory::Bind
//
// Bind to the server.
//
// Currently recognized flags:
//
// RENDBIND_AUTHENTICATE 0x00000001
// RENDBIND_DEFAULTDOMAINNAME 0x00000002
// RENDBIND_DEFAULTUSERNAME 0x00000004
// RENDBIND_DEFAULTPASSWORD 0x00000008
//
// "Meta-flags" for convenience:
// RENDBIND_DEFAULTCREDENTIALS 0x0000000e
//
//
// All of this together means that the following three
// forms are all equivalent:
//
// BSTR es = SysAllocString(L"");
// hr = pITDirectory->Bind(es, es, es, RENDBIND_AUTHENTICATE |
// RENDBIND_DEFAULTCREDENTIALS);
// SysFreeString(es);
//
//
// BSTR es = SysAllocString(L"");
// hr = pITDirectory->Bind(es, es, es, RENDBIND_AUTHENTICATE |
// RENDBIND_DEFAULTDOMAINNAME |
// RENDBIND_DEFAULTUSERNAME |
// RENDBIND_DEFAULTPASSWORD);
// SysFreeString(es);
//
//
// hr = pITDirectory->Bind(NULL, NULL, NULL, RENDBIND_AUTHENTICATE);
//
//
STDMETHODIMP CILSDirectory::Bind (
IN BSTR pDomainName,
IN BSTR pUserName,
IN BSTR pPassword,
IN long lFlags
)
{
LOG((MSP_TRACE, "CILSDirectory Bind - enter"));
//
// Determine if we should authenticate.
//
BOOL fAuthenticate = FALSE;
if ( lFlags & RENDBIND_AUTHENTICATE )
{
fAuthenticate = TRUE;
}
//
// For scripting compatibility, force string parameters to NULL based
// on flags.
//
if ( lFlags & RENDBIND_DEFAULTDOMAINNAME )
{
pDomainName = NULL;
}
if ( lFlags & RENDBIND_DEFAULTUSERNAME )
{
pUserName = NULL;
}
if ( lFlags & RENDBIND_DEFAULTPASSWORD )
{
pPassword = NULL;
}
LOG((MSP_INFO, "Bind parameters: domain: `%S' user: `%S' "
"authenticate: %S)",
(pDomainName) ? pDomainName : L"<null>",
(pUserName) ? pUserName : L"<null>",
(fAuthenticate) ? L"yes" : L"no"));
//
// All flags processed -- lock and proceed with bind if connected.
//
CLock Lock(m_lock);
if (m_ldap == NULL)
{
LOG((MSP_ERROR, "not connected."));
return RND_NOT_CONNECTED;
}
//
// ZoltanS: check the arguments. NULL has meaning in each case, so they are
// OK for now. In each case we want to check any length string, so we
// specify (UINT) -1 as the length.
//
if ( (pDomainName != NULL) && IsBadStringPtr(pDomainName, (UINT) -1 ) )
{
LOG((MSP_ERROR, "CNTDirectory::Bind: bad non-NULL pDomainName argument"));
return E_POINTER;
}
if ( (pUserName != NULL) && IsBadStringPtr(pUserName, (UINT) -1 ) )
{
LOG((MSP_ERROR, "CNTDirectory::Bind: bad non-NULL pUserName argument"));
return E_POINTER;
}
if ( (pPassword != NULL) && IsBadStringPtr(pPassword, (UINT) -1 ) )
{
LOG((MSP_ERROR, "CNTDirectory::Bind: bad non-NULL pPassword argument"));
return E_POINTER;
}
ULONG res;
if ( m_IsSsl || (!fAuthenticate) )
{
// if encrypted or no secure authentication is required,
// simple bind is sufficient
// ldap_simple_bind_s does not use sspi to get default credentials. We are
// just specifying what we will actually pass on the wire.
if (pPassword == NULL)
{
LOG((MSP_ERROR, "invalid Bind parameters: no password specified"));
return E_INVALIDARG;
}
WCHAR * wszFullName;
if ( (pDomainName == NULL) && (pUserName == NULL) )
{
// No domain / user doesn't make sense.
LOG((MSP_ERROR, "invalid Bind paramters: domain and user not specified"));
return E_INVALIDARG;
}
else if (pDomainName == NULL)
{
// username only is okay
wszFullName = pUserName;
}
else if (pUserName == NULL)
{
// It doesn't make sense to specify domain but not user...
LOG((MSP_ERROR, "invalid Bind paramters: domain specified but not user"));
return E_INVALIDARG;
}
else
{
// We need domain\user. Allocate a string and sprintf into it.
// The + 2 is for the "\" and for the null termination.
wszFullName = new WCHAR[wcslen(pDomainName) + wcslen(pUserName) + 2];
BAIL_IF_NULL(wszFullName, E_OUTOFMEMORY);
wsprintf(wszFullName, L"%s\\%s", pDomainName, pUserName);
}
//
// Do the simple bind.
//
res = ldap_simple_bind_s(m_ldap, wszFullName, pPassword);
//
// If we constructed the full name string, we now need to delete it.
//
if (wszFullName != pUserName)
{
delete wszFullName;
}
//
// Bail if the simple bind failed.
//
BAIL_IF_LDAP_FAIL(res, "ldap simple bind");
}
else // try an SSPI bind
{
// if the domain name, user name or the password is non-null
if ( pDomainName || pUserName || pPassword )
{
// fill the credential structure
SEC_WINNT_AUTH_IDENTITY AuthI;
AuthI.User = (PTCHAR)pUserName;
AuthI.UserLength = (pUserName == NULL)? 0: wcslen(pUserName);
AuthI.Domain = (PTCHAR)pDomainName;
AuthI.DomainLength = (pDomainName == NULL)? 0: wcslen(pDomainName);
AuthI.Password = (PTCHAR)pPassword;
AuthI.PasswordLength = (pPassword == NULL)? 0: wcslen(pPassword);
AuthI.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
res = ldap_bind_s(m_ldap, NULL, (TCHAR*)(&AuthI), LDAP_AUTH_NTLM);
BAIL_IF_LDAP_FAIL(res, "bind with authtication");
}
else
{
// Otherwise we've come in with NULL, NULL, NULL -
// pass in NULL, NULL. The reason do this is that ldap bind code
// does not process NULL, NULL, NULL in the
// SEC_WINNT_AUTH_IDENTITY blob !!!
ULONG res = ldap_bind_s(m_ldap, NULL, NULL, LDAP_AUTH_NTLM);
BAIL_IF_LDAP_FAIL(res, "bind with NULL NULL NULL");
}
}
LOG((MSP_TRACE, "CILSDirectory::Bind - exiting OK"));
return S_OK;
}
//////////////////////////////////////////////////////////////////////
// ITDirectory::AddDirectoryObject
//
// Return values:
// Value Where defined What it means
// ----- ------------- -------------
// RND_NOT_CONNECTED .\rnderr.h ::Connect not yet called
// E_POINTER sdk\inc\winerror.h pDirectoryObject is a bad pointer
// other from AddConference
// other from AddUser
//
STDMETHODIMP CILSDirectory::AddDirectoryObject (
IN ITDirectoryObject *pDirectoryObject
)
// publish a new object to the server.
{
if ( IsBadReadPtr(pDirectoryObject, sizeof(ITDirectoryObject) ) )
{
LOG((MSP_ERROR, "CILSDirectory::AddDirectoryObject - "
"bad directory object pointer - returning E_POINTER"));
return E_POINTER;
}
CLock Lock(m_lock);
if (m_ldap == NULL)
{
LOG((MSP_ERROR, "CILSDirectory::AddDirectoryObject - not connected."));
return RND_NOT_CONNECTED;
}
HRESULT hr;
DIRECTORY_OBJECT_TYPE type;
hr = pDirectoryObject->get_ObjectType(&type);
if ( FAILED(hr) )
{
LOG((MSP_ERROR, "CILSDirectory::AddDirectoryObject - "
"can't get object type; returning 0x%08x", hr));
return hr;
}
switch (type)
{
case OT_CONFERENCE:
hr = AddConference(pDirectoryObject, FALSE);
break;
case OT_USER:
hr = AddUser(pDirectoryObject, FALSE);
break;
}
return hr;
}
STDMETHODIMP CILSDirectory::ModifyDirectoryObject (
IN ITDirectoryObject *pDirectoryObject
)
// modify an object on the server.
{
if ( IsBadReadPtr(pDirectoryObject, sizeof(ITDirectoryObject) ) )
{
LOG((MSP_ERROR, "CILSDirectory::ModifyDirectoryObject - "
"bad directory object pointer - returning E_POINTER"));
return E_POINTER;
}
CLock Lock(m_lock);
if (m_ldap == NULL)
{
LOG((MSP_ERROR, "CILSDirectory::ModifyDirectoryObject - not connected."));
return RND_NOT_CONNECTED;
}
HRESULT hr;
DIRECTORY_OBJECT_TYPE type;
hr = pDirectoryObject->get_ObjectType(&type);
if ( FAILED(hr) )
{
LOG((MSP_ERROR, "CILSDirectory::ModifyDirectoryObject - "
"can't get object type; returning 0x%08x", hr));
return hr;
}
switch (type)
{
case OT_CONFERENCE:
hr = AddConference(pDirectoryObject, TRUE);
break;
case OT_USER:
hr = AddUser(pDirectoryObject, TRUE);
break;
}
return hr;
}
STDMETHODIMP CILSDirectory::RefreshDirectoryObject (
IN ITDirectoryObject *pDirectoryObject
)
// Refresh the TTL for the object and add the object to the refresh list
// if the autorefresh is enabled.
{
if ( IsBadReadPtr(pDirectoryObject, sizeof(ITDirectoryObject) ) )
{
LOG((MSP_ERROR, "CILSDirectory::RefreshDirectoryObject - "
"bad directory object pointer - returning E_POINTER"));
return E_POINTER;
}
CLock Lock(m_lock);
if (m_ldap == NULL)
{
LOG((MSP_ERROR, "CILSDirectory::RefreshDirectoryObject - not connected."));
return RND_NOT_CONNECTED;
}
HRESULT hr;
DIRECTORY_OBJECT_TYPE type;
hr = pDirectoryObject->get_ObjectType(&type);
if ( FAILED(hr) )
{
LOG((MSP_ERROR, "CILSDirectory::RefreshDirectoryObject - "
"can't get object type; returning 0x%08x", hr));
return hr;
}
switch (type)
{
case OT_CONFERENCE:
return S_OK; // conferences do not need refresh.
case OT_USER:
hr = RefreshUser(pDirectoryObject);
break;
}
return hr;
}
STDMETHODIMP CILSDirectory::DeleteDirectoryObject (
IN ITDirectoryObject *pDirectoryObject
)
// delete an object on the server.
{
if ( IsBadReadPtr(pDirectoryObject, sizeof(ITDirectoryObject) ) )
{
LOG((MSP_ERROR, "CILSDirectory::DeleteDirectoryObject - "
"bad directory object pointer - returning E_POINTER"));
return E_POINTER;
}
CLock Lock(m_lock);
if (m_ldap == NULL)
{
LOG((MSP_ERROR, "CILSDirectory::DeleteDirectoryObject - not connected."));
return RND_NOT_CONNECTED;
}
HRESULT hr;
DIRECTORY_OBJECT_TYPE type;
hr = pDirectoryObject->get_ObjectType(&type);
if ( FAILED(hr) )
{
LOG((MSP_ERROR, "CILSDirectory::DeleteDirectoryObject - "
"can't get object type; returning 0x%08x", hr));
return hr;
}
switch (type)
{
case OT_CONFERENCE:
hr = DeleteConference(pDirectoryObject);
break;
case OT_USER:
hr = DeleteUser(pDirectoryObject);
break;
}
return hr;
}
STDMETHODIMP CILSDirectory::get_DirectoryObjects (
IN DIRECTORY_OBJECT_TYPE DirectoryObjectType,
IN BSTR pName,
OUT VARIANT * pVariant
)
// search for objects on the server.
{
BAIL_IF_BAD_READ_PTR(pName, E_POINTER);
BAIL_IF_BAD_WRITE_PTR(pVariant, E_POINTER);
CLock Lock(m_lock);
if (m_ldap == NULL)
{
LOG((MSP_ERROR, "not connected."));
return RND_NOT_CONNECTED;
}
HRESULT hr;
ITDirectoryObject **pObjects;
DWORD dwSize;
// search and create objects.
hr = SearchObjects(DirectoryObjectType, pName, &pObjects, &dwSize);
BAIL_IF_FAIL(hr, "Search for objects");
// create a collection object that contains the objects.
hr = CreateInterfaceCollection(dwSize, // count
&pObjects[0], // begin ptr
&pObjects[dwSize], // end ptr
pVariant); // return value
for (DWORD i = 0; i < dwSize; i ++)
{
pObjects[i]->Release();
}
delete pObjects;
BAIL_IF_FAIL(hr, "Create collection of directory objects");
return hr;
}
STDMETHODIMP CILSDirectory::EnumerateDirectoryObjects (
IN DIRECTORY_OBJECT_TYPE DirectoryObjectType,
IN BSTR pName,
OUT IEnumDirectoryObject ** ppEnumObject
)
// search for the objects on the server.
{
BAIL_IF_BAD_READ_PTR(pName, E_POINTER);
BAIL_IF_BAD_WRITE_PTR(ppEnumObject, E_POINTER);
CLock Lock(m_lock);
if (m_ldap == NULL)
{
LOG((MSP_ERROR, "not connected."));
return RND_NOT_CONNECTED;
}
HRESULT hr;
ITDirectoryObject **pObjects;
DWORD dwSize;
// search and create objects.
hr = SearchObjects(DirectoryObjectType, pName, &pObjects, &dwSize);
BAIL_IF_FAIL(hr, "Search for objects");
// create a enumerator object that contains the objects.
hr = ::CreateDirectoryObjectEnumerator(
&pObjects[0],
&pObjects[dwSize],
ppEnumObject
);
for (DWORD i = 0; i < dwSize; i ++)
{
pObjects[i]->Release();
}
delete pObjects;
BAIL_IF_FAIL(hr, "Create enumerator of directory objects");
return hr;
}
/////////////////////////////////////////////////////////////////////////////
// ILSConfig implementation
/////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CILSDirectory::get_Port (
OUT long *pPort
)
// get the current port used in Ldap connection.
{
if ( IsBadWritePtr(pPort, sizeof(long) ) )
{
LOG((MSP_ERROR, "Directory.get_Port, invalid pointer"));
return E_POINTER;
}
CLock Lock(m_lock);
*pPort = (long)m_wPort;
return S_OK;
}
STDMETHODIMP CILSDirectory::put_Port (
IN long Port
)
// set the port the user wants to use.
{
CLock Lock(m_lock);
if (m_ldap != NULL)
{
LOG((MSP_ERROR, "already connected."));
return RND_ALREADY_CONNECTED;
}
if (Port <= USHRT_MAX)
{
m_wPort = (WORD)Port;
return S_OK;
}
return E_INVALIDARG;
}
/////////////////////////////////////////////////////////////////////////////
// ITDynamic interface
/////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CILSDirectory::Update(DWORD dwSecondsPassed)
// Update the TTL for object created in this directory. The worker thread
// sends a tick every minute.
{
if ( ! m_lock.TryLock() )
{
return S_OK;
}
LOG((MSP_TRACE, "CILSDirectory::Update is called, delta: %d", dwSecondsPassed));
//
// Go through the table to see if anyone needs refresh.
//
for ( DWORD i = 0; i < m_RefreshTable.size(); i++ )
{
WCHAR * pDN = m_RefreshTable[i].pDN;
DWORD dwTTL = m_RefreshTable[i].dwTTL;
LOG((MSP_TRACE, "\tExamining user object: %S", pDN ));
LOG((MSP_TRACE, "\t\tTime remaining: %d", dwTTL ));
if ( dwTTL <= ( 2 * dwSecondsPassed ) )
{
//
// refresh it if the TTL is going to expire within the next
// two clicks
//
LOG((MSP_TRACE, "\t\t\tREFRESHING"));
if ( SUCCEEDED( SetTTL( m_ldap, pDN, m_TTL) ) )
{
m_RefreshTable[i].dwTTL = m_TTL;
}
else
{
LOG((MSP_WARN, "\t\t\t\tRefresh failed; will try again next time"));
}
}
else
{
//
// Not about to expire right now so just keep track of the time before
// it expires
//
LOG((MSP_TRACE, "\t\t\tdecrementing"));
m_RefreshTable[i].dwTTL -= dwSecondsPassed;
}
}
m_lock.Unlock();
LOG((MSP_TRACE, "CILSDirectory::Update exit S_OK"));
return S_OK;
}
typedef IDispatchImpl<ITDirectoryVtbl<CILSDirectory>, &IID_ITDirectory, &LIBID_RENDLib> CTDirectory;
typedef IDispatchImpl<ITILSConfigVtbl<CILSDirectory>, &IID_ITILSConfig, &LIBID_RENDLib> CTILSConfig;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++=
//
// CILSDirectory::GetIDsOfNames
//
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++=
STDMETHODIMP CILSDirectory::GetIDsOfNames(REFIID riid,
LPOLESTR* rgszNames,
UINT cNames,
LCID lcid,
DISPID* rgdispid
)
{
LOG((MSP_TRACE, "CILSDirectory::GetIDsOfNames[%p] - enter. Name [%S]",this, *rgszNames));
HRESULT hr = DISP_E_UNKNOWNNAME;
//
// See if the requsted method belongs to the default interface
//
hr = CTDirectory::GetIDsOfNames(riid, rgszNames, cNames, lcid, rgdispid);
if (SUCCEEDED(hr))
{
LOG((MSP_TRACE, "CILSDirectory::GetIDsOfNames - found %S on CTDirectory", *rgszNames));
rgdispid[0] |= IDISPDIRECTORY;
return hr;
}
//
// If not, then try the ITILSConfig base class
//
hr = CTILSConfig::GetIDsOfNames(riid, rgszNames, cNames, lcid, rgdispid);
if (SUCCEEDED(hr))
{
LOG((MSP_TRACE, "CILSDirectory::GetIDsOfNames - found %S on CTILSConfig", *rgszNames));
rgdispid[0] |= IDISPILSCONFIG;
return hr;
}
LOG((MSP_ERROR, "CILSDirectory::GetIDsOfNames[%p] - finish. didn't find %S on our iterfaces",*rgszNames));
return hr;
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++=
//
// CILSDirectory::Invoke
//
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++=
STDMETHODIMP CILSDirectory::Invoke(DISPID dispidMember,
REFIID riid,
LCID lcid,
WORD wFlags,
DISPPARAMS* pdispparams,
VARIANT* pvarResult,
EXCEPINFO* pexcepinfo,
UINT* puArgErr
)
{
LOG((MSP_TRACE, "CILSDirectory::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 IDISPDIRECTORY:
{
hr = CTDirectory::Invoke(dispidMember,
riid,
lcid,
wFlags,
pdispparams,
pvarResult,
pexcepinfo,
puArgErr
);
LOG((MSP_TRACE, "CILSDirectory::Invoke - ITDirectory"));
break;
}
case IDISPILSCONFIG:
{
hr = CTILSConfig::Invoke(dispidMember,
riid,
lcid,
wFlags,
pdispparams,
pvarResult,
pexcepinfo,
puArgErr
);
LOG((MSP_TRACE, "CILSDirectory::Invoke - ITILSConfig"));
break;
}
} // end switch (dwInterface)
LOG((MSP_TRACE, "CILSDirectory::Invoke[%p] - finish. hr = %lx", hr));
return hr;
}