/////////////////////////////////////////////////////////////////////////////// // // 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 #include #include #include #include #include #include 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(&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; }