Source code of Windows XP (NT5)
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.

262 lines
5.8 KiB

  1. ///////////////////////////////////////////////////////////////////////////////
  2. //
  3. // FILE
  4. //
  5. // pollseg.cpp
  6. //
  7. // SYNOPSIS
  8. //
  9. // Defines the class PollSegment.
  10. //
  11. // MODIFICATION HISTORY
  12. //
  13. // 06/10/1998 Original version.
  14. // 06/23/1998 Use constants from ntldap.
  15. // 07/13/1998 Clean up header file dependencies.
  16. //
  17. ///////////////////////////////////////////////////////////////////////////////
  18. #include <ias.h>
  19. #include <pollseg.h>
  20. #include <winldap.h>
  21. #include <ntldap.h>
  22. #include <ldapcache.h>
  23. #include <ldapcxn.h>
  24. #include <ntglobal.h>
  25. const LDAPControlW INCLUDE_TOMBSTONES =
  26. {
  27. LDAP_SERVER_SHOW_DELETED_OID_W,
  28. { 0, NULL },
  29. TRUE
  30. };
  31. PollSegment::PollSegment() throw ()
  32. : server(NULL),
  33. base(NULL),
  34. scope(LDAP_SCOPE_BASE),
  35. highestUsnSeen(0)
  36. { }
  37. PollSegment::~PollSegment() throw ()
  38. {
  39. finalize();
  40. }
  41. DWORD PollSegment::initialize(PCWSTR pszHostName,
  42. PCWSTR pszBaseDN,
  43. ULONG ulScope) throw ()
  44. {
  45. // If we already have a server, don't allow initialization.
  46. if (server) { return ERROR_ALREADY_INITIALIZED; }
  47. DWORD error;
  48. // Initialize the NTDS API. This is a ref. counted call, so there's little
  49. // overhead in doing it for every PollSegment instance.
  50. error = IASNtdsInitialize();
  51. if (error != NO_ERROR) { goto ntds_init_failed; }
  52. // Retrieve the server object from the cache.
  53. error = theServers.getServer(pszHostName, &server);
  54. if (error != NO_ERROR) { goto get_server_failed; }
  55. // Store the base DN.
  56. if (pszBaseDN && !(base = _wcsdup(pszBaseDN)))
  57. {
  58. goto copy_base_failed;
  59. }
  60. // Store the scope.
  61. scope = ulScope;
  62. // Read the current state of the server.
  63. // We don't care if this is successful.
  64. LDAPConnection* cxn;
  65. if (server->getConnection(&cxn) == NO_ERROR)
  66. {
  67. highestUsnSeen = readHighestCommittedUsn(cxn);
  68. cxn->Release();
  69. }
  70. return NO_ERROR;
  71. copy_base_failed:
  72. server->Release();
  73. server = NULL;
  74. get_server_failed:
  75. IASNtdsUninitialize();
  76. ntds_init_failed:
  77. return error;
  78. }
  79. void PollSegment::finalize() throw ()
  80. {
  81. // server will be non-zero iff we were successfully initialized.
  82. if (server)
  83. {
  84. free(base);
  85. base = NULL;
  86. server->Release();
  87. server = NULL;
  88. IASNtdsUninitialize();
  89. }
  90. }
  91. BOOL PollSegment::hasChanged() throw ()
  92. {
  93. // Open a connection to the server.
  94. LDAPConnection* cxn;
  95. ULONG ldapError = server->getConnection(&cxn);
  96. if (ldapError != LDAP_SUCCESS) { return TRUE; }
  97. // Default is TRUE.
  98. BOOL retval = TRUE;
  99. // We've got the same server, so we can do a search for recently
  100. // modified objects. We must first query the USN to avoid race
  101. // conditions.
  102. DWORDLONG newUsn = readHighestCommittedUsn(cxn);
  103. if (newUsn)
  104. {
  105. // We successfully read the USN.
  106. if (newUsn == highestUsnSeen)
  107. {
  108. // The server hasn't performed any updates since the last poll.
  109. retval = FALSE;
  110. }
  111. else
  112. {
  113. // The USN has changed since last time, so poll the segment.
  114. ldapError = conductPoll(cxn);
  115. switch (ldapError)
  116. {
  117. case LDAP_NO_SUCH_OBJECT:
  118. // We found no objects, so the segement hasn't changed.
  119. retval = FALSE;
  120. // Fall through.
  121. case LDAP_SUCCESS:
  122. case LDAP_SIZELIMIT_EXCEEDED:
  123. // We successfully polled the server, so update USN.
  124. highestUsnSeen = newUsn;
  125. }
  126. }
  127. }
  128. cxn->Release();
  129. return retval;
  130. }
  131. ULONG PollSegment::conductPoll(LDAPConnection* cxn)
  132. {
  133. // Unsigned 64-bit integer requires max. of 20 characters.
  134. WCHAR filter[35];
  135. // Format the search filter.
  136. swprintf(filter, L"(uSNChanged>=%I64u)", highestUsnSeen + 1);
  137. // We don't need any attributes.
  138. PWCHAR attrs[] = { NULL };
  139. PLDAPControlW serverControls[] =
  140. {
  141. const_cast<PLDAPControlW>(&INCLUDE_TOMBSTONES),
  142. NULL
  143. };
  144. LDAPMessage* res;
  145. ULONG ldapError = ldap_search_ext_sW(
  146. *cxn,
  147. base,
  148. scope,
  149. filter,
  150. attrs,
  151. TRUE,
  152. serverControls,
  153. NULL,
  154. NULL,
  155. 1, // SizeLimit
  156. &res
  157. );
  158. if (ldapError != LDAP_SUCCESS)
  159. {
  160. cxn->disable();
  161. }
  162. else
  163. {
  164. if (res->lm_returncode == LDAP_SUCCESS &&
  165. ldap_count_entries(*cxn, res) == 0)
  166. {
  167. ldapError = LDAP_NO_SUCH_OBJECT;
  168. }
  169. else
  170. {
  171. ldapError = res->lm_returncode;
  172. }
  173. ldap_msgfree(res);
  174. }
  175. return ldapError;
  176. }
  177. DWORDLONG PollSegment::readHighestCommittedUsn(LDAPConnection* cxn)
  178. {
  179. static WCHAR HIGHEST_COMMITTED_USN[] = LDAP_OPATT_HIGHEST_COMMITTED_USN_W;
  180. DWORDLONG retval = 0;
  181. PWCHAR attrs[] = { HIGHEST_COMMITTED_USN, NULL };
  182. LDAPMessage* res;
  183. ULONG ldapError = ldap_search_sW(
  184. *cxn,
  185. L"",
  186. LDAP_SCOPE_BASE,
  187. L"(objectclass=*)",
  188. attrs,
  189. 0,
  190. &res
  191. );
  192. if (ldapError != LDAP_SUCCESS)
  193. {
  194. cxn->disable();
  195. }
  196. else
  197. {
  198. if (res->lm_returncode == LDAP_SUCCESS)
  199. {
  200. LDAPMessage* entry = ldap_first_entry(*cxn, res);
  201. PWCHAR* vals = ldap_get_valuesW(
  202. *cxn,
  203. entry,
  204. HIGHEST_COMMITTED_USN
  205. );
  206. if (vals && *vals)
  207. {
  208. retval = _wtoi64(*vals);
  209. }
  210. ldap_value_freeW(vals);
  211. }
  212. ldap_msgfree(res);
  213. }
  214. return retval;
  215. }