windows-nt/Source/XPSP1/NT/net/ias/providers/ntuser/ldap/pollseg.cpp

263 lines
5.8 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
///////////////////////////////////////////////////////////////////////////////
//
// FILE
//
// pollseg.cpp
//
// SYNOPSIS
//
// Defines the class PollSegment.
//
// MODIFICATION HISTORY
//
// 06/10/1998 Original version.
// 06/23/1998 Use constants from ntldap.
// 07/13/1998 Clean up header file dependencies.
//
///////////////////////////////////////////////////////////////////////////////
#include <ias.h>
#include <pollseg.h>
#include <winldap.h>
#include <ntldap.h>
#include <ldapcache.h>
#include <ldapcxn.h>
#include <ntglobal.h>
const LDAPControlW INCLUDE_TOMBSTONES =
{
LDAP_SERVER_SHOW_DELETED_OID_W,
{ 0, NULL },
TRUE
};
PollSegment::PollSegment() throw ()
: server(NULL),
base(NULL),
scope(LDAP_SCOPE_BASE),
highestUsnSeen(0)
{ }
PollSegment::~PollSegment() throw ()
{
finalize();
}
DWORD PollSegment::initialize(PCWSTR pszHostName,
PCWSTR pszBaseDN,
ULONG ulScope) throw ()
{
// If we already have a server, don't allow initialization.
if (server) { return ERROR_ALREADY_INITIALIZED; }
DWORD error;
// Initialize the NTDS API. This is a ref. counted call, so there's little
// overhead in doing it for every PollSegment instance.
error = IASNtdsInitialize();
if (error != NO_ERROR) { goto ntds_init_failed; }
// Retrieve the server object from the cache.
error = theServers.getServer(pszHostName, &server);
if (error != NO_ERROR) { goto get_server_failed; }
// Store the base DN.
if (pszBaseDN && !(base = _wcsdup(pszBaseDN)))
{
goto copy_base_failed;
}
// Store the scope.
scope = ulScope;
// Read the current state of the server.
// We don't care if this is successful.
LDAPConnection* cxn;
if (server->getConnection(&cxn) == NO_ERROR)
{
highestUsnSeen = readHighestCommittedUsn(cxn);
cxn->Release();
}
return NO_ERROR;
copy_base_failed:
server->Release();
server = NULL;
get_server_failed:
IASNtdsUninitialize();
ntds_init_failed:
return error;
}
void PollSegment::finalize() throw ()
{
// server will be non-zero iff we were successfully initialized.
if (server)
{
free(base);
base = NULL;
server->Release();
server = NULL;
IASNtdsUninitialize();
}
}
BOOL PollSegment::hasChanged() throw ()
{
// Open a connection to the server.
LDAPConnection* cxn;
ULONG ldapError = server->getConnection(&cxn);
if (ldapError != LDAP_SUCCESS) { return TRUE; }
// Default is TRUE.
BOOL retval = TRUE;
// We've got the same server, so we can do a search for recently
// modified objects. We must first query the USN to avoid race
// conditions.
DWORDLONG newUsn = readHighestCommittedUsn(cxn);
if (newUsn)
{
// We successfully read the USN.
if (newUsn == highestUsnSeen)
{
// The server hasn't performed any updates since the last poll.
retval = FALSE;
}
else
{
// The USN has changed since last time, so poll the segment.
ldapError = conductPoll(cxn);
switch (ldapError)
{
case LDAP_NO_SUCH_OBJECT:
// We found no objects, so the segement hasn't changed.
retval = FALSE;
// Fall through.
case LDAP_SUCCESS:
case LDAP_SIZELIMIT_EXCEEDED:
// We successfully polled the server, so update USN.
highestUsnSeen = newUsn;
}
}
}
cxn->Release();
return retval;
}
ULONG PollSegment::conductPoll(LDAPConnection* cxn)
{
// Unsigned 64-bit integer requires max. of 20 characters.
WCHAR filter[35];
// Format the search filter.
swprintf(filter, L"(uSNChanged>=%I64u)", highestUsnSeen + 1);
// We don't need any attributes.
PWCHAR attrs[] = { NULL };
PLDAPControlW serverControls[] =
{
const_cast<PLDAPControlW>(&INCLUDE_TOMBSTONES),
NULL
};
LDAPMessage* res;
ULONG ldapError = ldap_search_ext_sW(
*cxn,
base,
scope,
filter,
attrs,
TRUE,
serverControls,
NULL,
NULL,
1, // SizeLimit
&res
);
if (ldapError != LDAP_SUCCESS)
{
cxn->disable();
}
else
{
if (res->lm_returncode == LDAP_SUCCESS &&
ldap_count_entries(*cxn, res) == 0)
{
ldapError = LDAP_NO_SUCH_OBJECT;
}
else
{
ldapError = res->lm_returncode;
}
ldap_msgfree(res);
}
return ldapError;
}
DWORDLONG PollSegment::readHighestCommittedUsn(LDAPConnection* cxn)
{
static WCHAR HIGHEST_COMMITTED_USN[] = LDAP_OPATT_HIGHEST_COMMITTED_USN_W;
DWORDLONG retval = 0;
PWCHAR attrs[] = { HIGHEST_COMMITTED_USN, NULL };
LDAPMessage* res;
ULONG ldapError = ldap_search_sW(
*cxn,
L"",
LDAP_SCOPE_BASE,
L"(objectclass=*)",
attrs,
0,
&res
);
if (ldapError != LDAP_SUCCESS)
{
cxn->disable();
}
else
{
if (res->lm_returncode == LDAP_SUCCESS)
{
LDAPMessage* entry = ldap_first_entry(*cxn, res);
PWCHAR* vals = ldap_get_valuesW(
*cxn,
entry,
HIGHEST_COMMITTED_USN
);
if (vals && *vals)
{
retval = _wtoi64(*vals);
}
ldap_value_freeW(vals);
}
ldap_msgfree(res);
}
return retval;
}