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.

976 lines
28 KiB

  1. #include <precomp.h>
  2. #include <nt.h> // DbgPrint prototype
  3. #include <ntrtl.h> // DbgPrint
  4. #include <nturtl.h> // Needed by winbase.h
  5. #include <windef.h> // DWORD
  6. #include <winbase.h> // LocalFree
  7. #include <rpcutil.h> // GENERIC_ENUM_STRUCT
  8. #include <lmcons.h> // NET_API_STATUS
  9. #include <lmerr.h> // NetError codes
  10. #include <lmremutl.h> // SUPPORTS_RPC
  11. #include <brnames.h> // Service and interface names
  12. #include <netlib.h>
  13. #include <netdebug.h>
  14. #include <winsvc.h>
  15. #include <lmserver.h>
  16. #include <tstr.h>
  17. #include <ntddbrow.h>
  18. #include <brcommon.h> // Routines common between client & server
  19. VOID
  20. UpdateInterimServerListElement(
  21. IN PINTERIM_SERVER_LIST ServerList,
  22. IN PINTERIM_ELEMENT Element,
  23. IN ULONG Level,
  24. IN BOOLEAN NewElement
  25. );
  26. PINTERIM_ELEMENT
  27. AllocateInterimServerListEntry(
  28. IN PSERVER_INFO_101 ServerInfo,
  29. IN ULONG Level
  30. );
  31. NET_API_STATUS
  32. MergeServerList(
  33. IN OUT PINTERIM_SERVER_LIST InterimServerList,
  34. IN ULONG Level,
  35. IN PVOID NewServerList,
  36. IN ULONG NewEntriesRead,
  37. IN ULONG NewTotalEntries
  38. )
  39. /*++
  40. Routine Description:
  41. This function will merge two server lists. It will reallocate the buffer
  42. for the old list if appropriate.
  43. Arguments:
  44. IN OUT PINTERIM_SERVER_LIST InterimServerList - Supplies an interim server list to merge into.
  45. IN ULONG Level - Supplies the level of the list (100 or 101). Special
  46. level 1010 is really level 101 with the special semantic that no
  47. fields from this the NewServerList override existing fields in the
  48. InterimServerList.
  49. IN ULONG NewServerList - Supplies the list to merge into the interim list
  50. IN ULONG NewEntriesRead - Supplies the entries read in the list.
  51. IN ULONG NewTotalEntries - Supplies the total entries available in the list.
  52. Return Value:
  53. NET_API_STATUS - NERR_Success or reason for failure.
  54. --*/
  55. {
  56. ULONG i;
  57. ULONG ServerElementSize;
  58. PSERVER_INFO_101 ServerInfo = NewServerList;
  59. PINTERIM_ELEMENT InterimEntry = NULL;
  60. PLIST_ENTRY InterimList;
  61. PINTERIM_ELEMENT NewElement = NULL;
  62. if (Level == 100) {
  63. ServerElementSize = sizeof(SERVER_INFO_100);
  64. } else if ( Level == 101 || Level == 1010 ) {
  65. ServerElementSize = sizeof(SERVER_INFO_101);
  66. } else {
  67. return(ERROR_INVALID_LEVEL);
  68. }
  69. //
  70. // Early out if no entries in list.
  71. //
  72. if (NewEntriesRead == 0) {
  73. return NERR_Success;
  74. }
  75. PrepareServerListForMerge(NewServerList, Level, NewEntriesRead);
  76. InterimList = InterimServerList->ServerList.Flink;
  77. //
  78. // Walk the existing structure, packing it into an interim element, and
  79. // sticking the element into the interim table.
  80. //
  81. for (i = 0; i < NewEntriesRead; i ++) {
  82. BOOLEAN EntryInserted = FALSE;
  83. //
  84. // Walk forward in the interim element and find the appropriate place
  85. // to insert this element.
  86. //
  87. while (InterimList != &InterimServerList->ServerList) {
  88. LONG CompareResult;
  89. InterimEntry = CONTAINING_RECORD(InterimList, INTERIM_ELEMENT, NextElement);
  90. // KdPrint(("MergeServerList: Compare %ws and %ws\n", NewElement->Name, InterimEntry->Name));
  91. #if DBG
  92. //
  93. // Make sure that this entry is lexically less than the next
  94. // entry.
  95. //
  96. {
  97. PLIST_ENTRY NextList = InterimList->Flink;
  98. PINTERIM_ELEMENT NextEntry = CONTAINING_RECORD(NextList, INTERIM_ELEMENT, NextElement);
  99. if (NextList != &InterimServerList->ServerList) {
  100. ASSERT (wcscmp(InterimEntry->Name, NextEntry->Name) < 0);
  101. }
  102. //
  103. // Now make sure that the input buffer also doesn't contain
  104. // duplicate entries.
  105. //
  106. if (i < (NewEntriesRead-1)) {
  107. PSERVER_INFO_101 NextServerInfo = (PSERVER_INFO_101)((PCHAR)ServerInfo+ServerElementSize);
  108. ASSERT (wcscmp(ServerInfo->sv101_name, NextServerInfo->sv101_name) <= 0);
  109. }
  110. }
  111. #endif
  112. CompareResult = wcscmp(ServerInfo->sv101_name, InterimEntry->Name);
  113. if (CompareResult == 0) {
  114. // KdPrint(("MergeServerList: Elements equal - update\n"));
  115. //
  116. // If the new information should override the old information,
  117. // copy it on top of the new info.
  118. //
  119. if ( Level != 1010 ) {
  120. InterimEntry->PlatformId = ServerInfo->sv101_platform_id;
  121. if (Level >= 101) {
  122. InterimEntry->MajorVersionNumber = ServerInfo->sv101_version_major;
  123. InterimEntry->MinorVersionNumber = ServerInfo->sv101_version_minor;
  124. InterimEntry->Type = ServerInfo->sv101_type;
  125. InterimServerList->TotalBytesNeeded -= wcslen(InterimEntry->Comment) * sizeof(WCHAR) + sizeof(WCHAR);
  126. wcscpy(InterimEntry->Comment, ServerInfo->sv101_comment);
  127. InterimServerList->TotalBytesNeeded += wcslen(InterimEntry->Comment) * sizeof(WCHAR) + sizeof(WCHAR);
  128. }
  129. }
  130. UpdateInterimServerListElement(InterimServerList, InterimEntry, Level, FALSE);
  131. EntryInserted = TRUE;
  132. break;
  133. } else if (CompareResult > 0) {
  134. // KdPrint(("MergeServerList: Elements greater. Skip element\n"));
  135. InterimList = InterimList->Flink;
  136. } else {
  137. NewElement = AllocateInterimServerListEntry(ServerInfo, Level);
  138. if (NewElement == NULL) {
  139. return ERROR_NOT_ENOUGH_MEMORY;
  140. }
  141. // KdPrint(("MergeServerList: Elements less. Insert at end\n"));
  142. //
  143. // The new entry is < than the previous entry. Insert it
  144. // before this entry.
  145. //
  146. InsertTailList(&InterimEntry->NextElement, &NewElement->NextElement);
  147. //
  148. // Skip to the next element in the list.
  149. //
  150. InterimList = &NewElement->NextElement;
  151. UpdateInterimServerListElement(InterimServerList, NewElement, Level, TRUE);
  152. EntryInserted = TRUE;
  153. break;
  154. }
  155. }
  156. if (!EntryInserted &&
  157. (InterimList == &InterimServerList->ServerList)) {
  158. NewElement = AllocateInterimServerListEntry(ServerInfo, Level);
  159. if (NewElement == NULL) {
  160. return ERROR_NOT_ENOUGH_MEMORY;
  161. }
  162. // KdPrint(("MergeServerList: Insert %ws at end of list\n", NewElement->Name));
  163. InsertTailList(&InterimServerList->ServerList, &NewElement->NextElement);
  164. InterimList = &NewElement->NextElement;
  165. UpdateInterimServerListElement(InterimServerList, NewElement, Level, TRUE);
  166. }
  167. ServerInfo = (PSERVER_INFO_101)((PCHAR)ServerInfo+ServerElementSize);
  168. }
  169. #if 0
  170. {
  171. PLIST_ENTRY InterimList;
  172. ULONG TotalNeededForList = 0;
  173. for (InterimList = InterimServerList->ServerList.Flink ;
  174. InterimList != &InterimServerList->ServerList ;
  175. InterimList = InterimList->Flink ) {
  176. ULONG ServerElementSize;
  177. InterimEntry = CONTAINING_RECORD(InterimList, INTERIM_ELEMENT, NextElement);
  178. if (Level == 100) {
  179. ServerElementSize = sizeof(SERVER_INFO_100);
  180. } else {
  181. ServerElementSize = sizeof(SERVER_INFO_101);
  182. }
  183. ServerElementSize += wcslen(InterimEntry->Name)*sizeof(WCHAR)+sizeof(WCHAR);
  184. ServerElementSize += wcslen(InterimEntry->Comment)*sizeof(WCHAR)+sizeof(WCHAR);
  185. // KdPrint(("MergeInterimServerList: %ws. %ld needed\n", InterimEntry->Name, ServerElementSize));
  186. TotalNeededForList += ServerElementSize;
  187. }
  188. if (TotalNeededForList != InterimServerList->TotalBytesNeeded) {
  189. KdPrint(("UpdateInterimServerList: Wrong number of bytes (%ld) for interim server list. %ld needed\n", InterimServerList->TotalBytesNeeded, TotalNeededForList));
  190. }
  191. }
  192. #endif
  193. // KdPrint(("%lx bytes needed to hold server list\n", InterimServerList->TotalBytesNeeded));
  194. //
  195. // Also, we had better have the whole table locally.
  196. //
  197. ASSERT (InterimServerList->EntriesRead == InterimServerList->TotalEntries);
  198. return(NERR_Success);
  199. }
  200. ULONG
  201. __cdecl
  202. CompareServerInfo(
  203. const void * Param1,
  204. const void * Param2
  205. )
  206. {
  207. const SERVER_INFO_100 * ServerInfo1 = Param1;
  208. const SERVER_INFO_100 * ServerInfo2 = Param2;
  209. return wcscmp(ServerInfo1->sv100_name, ServerInfo2->sv100_name);
  210. }
  211. VOID
  212. PrepareServerListForMerge(
  213. IN PVOID ServerInfoList,
  214. IN ULONG Level,
  215. IN ULONG EntriesInList
  216. )
  217. /*++
  218. Routine Description:
  219. MergeServerList requires that the inputs to the list be in a strictly
  220. sorted order. This routine guarantees that this list will be of
  221. an "appropriate" form to be merged.
  222. Arguments:
  223. IN PVOID ServerInfoList - Supplies the list to munge.
  224. IN ULONG Level - Supplies the level of the list (100 or 101).
  225. (Or level 1010 which is identical to level 101.)
  226. IN ULONG EntriesInList - Supplies the number of entries in the list.
  227. Return Value:
  228. None.
  229. Note:
  230. In 99% of the cases, the list passed in will already be sorted. We want to
  231. take the input list and first check to see if it is sorted. If it is,
  232. we can return immediately. If it is not, we need to sort the list.
  233. We don't just unilaterally sort the list, because the input is mostly
  234. sorted anyway, and there are no good sorting algorithms that handle mostly
  235. sorted inputs. Since we will see unsorted input only rarely (basically,
  236. we will only see it from WfW machines), we just take the penalty of a worst
  237. case quicksort if the input is unsorted.
  238. --*/
  239. {
  240. LONG i;
  241. ULONG ServerElementSize;
  242. PSERVER_INFO_101 ServerInfo = ServerInfoList;
  243. BOOLEAN MisOrderedList = FALSE;
  244. ASSERT (Level == 100 || Level == 101 || Level == 1010);
  245. //
  246. // Figure out the size of each element.
  247. //
  248. if (Level == 100) {
  249. ServerElementSize = sizeof(SERVER_INFO_100);
  250. } else {
  251. ServerElementSize = sizeof(SERVER_INFO_101);
  252. }
  253. //
  254. // Next check to see if the input list is sorted.
  255. //
  256. for (i = 0 ; i < ((LONG)EntriesInList - 1) ; i += 1 ) {
  257. PSERVER_INFO_101 NextServerInfo = (PSERVER_INFO_101)((PCHAR)ServerInfo+ServerElementSize);
  258. if (wcscmp(ServerInfo->sv101_name, NextServerInfo->sv101_name) >= 0) {
  259. MisOrderedList = TRUE;
  260. break;
  261. }
  262. ServerInfo = NextServerInfo;
  263. }
  264. //
  265. // This list is sorted. Return right away, it's fine.
  266. //
  267. if (!MisOrderedList) {
  268. return;
  269. }
  270. //
  271. // This list isn't sorted. We need to sort it.
  272. //
  273. qsort(ServerInfoList, EntriesInList, ServerElementSize, CompareServerInfo);
  274. }
  275. PINTERIM_ELEMENT
  276. AllocateInterimServerListEntry(
  277. IN PSERVER_INFO_101 ServerInfo,
  278. IN ULONG Level
  279. )
  280. {
  281. PINTERIM_ELEMENT NewElement;
  282. NewElement = MIDL_user_allocate(sizeof(INTERIM_ELEMENT));
  283. if (NewElement == NULL) {
  284. return NULL;
  285. }
  286. //
  287. // Initialize TimeLastSeen and Periodicity.
  288. //
  289. NewElement->TimeLastSeen = 0;
  290. NewElement->Periodicity = 0;
  291. NewElement->PlatformId = ServerInfo->sv101_platform_id;
  292. ASSERT (wcslen(ServerInfo->sv101_name) <= CNLEN);
  293. wcscpy(NewElement->Name, ServerInfo->sv101_name);
  294. if (Level == 100) {
  295. NewElement->MajorVersionNumber = 0;
  296. NewElement->MinorVersionNumber = 0;
  297. *NewElement->Comment = L'\0';
  298. NewElement->Type = SV_TYPE_ALL;
  299. } else {
  300. NewElement->MajorVersionNumber = ServerInfo->sv101_version_major;
  301. NewElement->MinorVersionNumber = ServerInfo->sv101_version_minor;
  302. NewElement->Type = ServerInfo->sv101_type;
  303. ASSERT (wcslen(ServerInfo->sv101_comment) <= LM20_MAXCOMMENTSZ);
  304. wcscpy(NewElement->Comment, ServerInfo->sv101_comment);
  305. }
  306. return NewElement;
  307. }
  308. VOID
  309. UpdateInterimServerListElement(
  310. IN PINTERIM_SERVER_LIST InterimServerList,
  311. IN PINTERIM_ELEMENT InterimElement,
  312. IN ULONG Level,
  313. IN BOOLEAN NewElement
  314. )
  315. {
  316. #if 0
  317. PINTERIM_ELEMENT InterimEntry;
  318. ULONG TotalNeededForList = 0;
  319. #endif
  320. //
  321. // If this is a new element, update the size of the table to match.
  322. //
  323. if (NewElement) {
  324. ULONG ServerElementSize;
  325. if (Level == 100) {
  326. ServerElementSize = sizeof(SERVER_INFO_100);
  327. } else {
  328. ServerElementSize = sizeof(SERVER_INFO_101);
  329. }
  330. InterimServerList->EntriesRead += 1;
  331. ServerElementSize += wcslen(InterimElement->Name)*sizeof(WCHAR)+sizeof(WCHAR);
  332. if (Level == 100) {
  333. ServerElementSize += sizeof(WCHAR);
  334. } else {
  335. ServerElementSize += wcslen(InterimElement->Comment)*sizeof(WCHAR)+sizeof(WCHAR);
  336. }
  337. InterimServerList->TotalBytesNeeded += ServerElementSize;
  338. InterimServerList->TotalEntries += 1;
  339. if (InterimServerList->NewElementCallback != NULL) {
  340. InterimServerList->NewElementCallback(InterimServerList,
  341. InterimElement);
  342. } else {
  343. InterimElement->Periodicity = 0xffffffff;
  344. InterimElement->TimeLastSeen = 0xffffffff;
  345. }
  346. } else {
  347. if (InterimServerList->ExistingElementCallback != NULL) {
  348. InterimServerList->ExistingElementCallback(InterimServerList,
  349. InterimElement);
  350. } else {
  351. InterimElement->Periodicity = 0xffffffff;
  352. InterimElement->TimeLastSeen = 0xffffffff;
  353. }
  354. }
  355. #if 0
  356. {
  357. PLIST_ENTRY InterimList;
  358. ULONG TotalNeededForList = 0;
  359. for (InterimList = InterimServerList->ServerList.Flink ;
  360. InterimList != &InterimServerList->ServerList ;
  361. InterimList = InterimList->Flink ) {
  362. ULONG ServerElementSize;
  363. InterimEntry = CONTAINING_RECORD(InterimList, INTERIM_ELEMENT, NextElement);
  364. if (Level == 100) {
  365. ServerElementSize = sizeof(SERVER_INFO_100);
  366. } else {
  367. ServerElementSize = sizeof(SERVER_INFO_101);
  368. }
  369. ServerElementSize += wcslen(InterimEntry->Name)*sizeof(WCHAR)+sizeof(WCHAR);
  370. ServerElementSize += wcslen(InterimEntry->Comment)*sizeof(WCHAR)+sizeof(WCHAR);
  371. TotalNeededForList += ServerElementSize;
  372. }
  373. if (TotalNeededForList != InterimServerList->TotalBytesNeeded) {
  374. KdPrint(("UpdateInterimServerList: Wrong number of bytes (%ld) for interim server list. %ld needed\n", InterimServerList->TotalBytesNeeded, TotalNeededForList));
  375. }
  376. }
  377. #endif
  378. return;
  379. }
  380. NET_API_STATUS
  381. PackServerList(
  382. IN PINTERIM_SERVER_LIST InterimServerList,
  383. IN ULONG Level,
  384. IN ULONG ServerType,
  385. IN ULONG PreferedMaximumLength,
  386. OUT PVOID *ServerList,
  387. OUT PULONG EntriesRead,
  388. OUT PULONG TotalEntries,
  389. IN LPCWSTR FirstNameToReturn
  390. )
  391. /*++
  392. Routine Description:
  393. This function will take an interim server list and "pack" it into an array
  394. of server_info_xxx structures.
  395. Arguments:
  396. IN PINTERIM_SERVER_LIST InterimServerList - Supplies an interim server list to merge into.
  397. IN ULONG Level - Supplies the level of the list (100 or 101).
  398. IN ULONG ServerType - Supplies the type to filter on the list.
  399. IN ULONG PreferedMaximumLength - Supplies the prefered size of the list.
  400. OUT PVOID *ServerList - Where to put the destination list.
  401. OUT PULONG EntriesEntries - Receives the entries packed in the list.
  402. OUT PULONG TotalEntries - Receives the total entries available in the list.
  403. FirstNameToReturn - Supplies the name of the first domain or server entry to return.
  404. The caller can use this parameter to implement a resume handle of sorts by passing
  405. the name of the last entry returned on a previous call. (Notice that the specified
  406. entry will, also, be returned on this call unless it has since been deleted.)
  407. Pass NULL to start with the first entry available.
  408. Passed name must be the canonical form of the name.
  409. Return Value:
  410. NET_API_STATUS - NERR_Success or reason for failure.
  411. --*/
  412. {
  413. ULONG EntriesPacked = 0;
  414. PLIST_ENTRY InterimList;
  415. PSERVER_INFO_101 ServerEntry;
  416. ULONG EntrySize = 0;
  417. LPWSTR BufferEnd;
  418. BOOLEAN ReturnWholeList = FALSE;
  419. BOOLEAN TrimmingNames;
  420. BOOLEAN BufferFull = FALSE;
  421. if (Level == 100) {
  422. EntrySize = sizeof(SERVER_INFO_100);
  423. } else if (Level == 101) {
  424. EntrySize = sizeof(SERVER_INFO_101);
  425. } else {
  426. return(ERROR_INVALID_LEVEL);
  427. }
  428. //
  429. // Set the entries read based on the information we collected before.
  430. //
  431. *TotalEntries = 0;
  432. if (PreferedMaximumLength == 0xffffffff) {
  433. *ServerList = MIDL_user_allocate(InterimServerList->TotalBytesNeeded);
  434. BufferEnd = (LPWSTR)((ULONG_PTR)(*ServerList)+InterimServerList->TotalBytesNeeded);
  435. } else {
  436. *ServerList = MIDL_user_allocate(PreferedMaximumLength);
  437. BufferEnd = (LPWSTR)((ULONG_PTR)(*ServerList)+PreferedMaximumLength);
  438. }
  439. if (ServerType == SV_TYPE_ALL || ServerType == SV_TYPE_DOMAIN_ENUM) {
  440. ReturnWholeList = TRUE;
  441. }
  442. if ( *ServerList == NULL ) {
  443. return(ERROR_NOT_ENOUGH_MEMORY);
  444. }
  445. TrimmingNames = (FirstNameToReturn != NULL && *FirstNameToReturn != L'\0');
  446. ServerEntry = *ServerList;
  447. for (InterimList = InterimServerList->ServerList.Flink ;
  448. InterimList != &InterimServerList->ServerList ;
  449. InterimList = InterimList->Flink ) {
  450. PINTERIM_ELEMENT InterimEntry = CONTAINING_RECORD(InterimList, INTERIM_ELEMENT, NextElement);
  451. #if DBG
  452. //
  453. // Make sure that this entry is lexically less than the next
  454. // entry.
  455. //
  456. {
  457. PLIST_ENTRY NextList = InterimList->Flink;
  458. PINTERIM_ELEMENT NextEntry = CONTAINING_RECORD(NextList, INTERIM_ELEMENT, NextElement);
  459. if (NextList != &InterimServerList->ServerList) {
  460. ASSERT (wcscmp(InterimEntry->Name, NextEntry->Name) < 0);
  461. }
  462. }
  463. #endif
  464. //
  465. // Trim the first several names from the list.
  466. //
  467. if ( TrimmingNames ) {
  468. if ( wcscmp( InterimEntry->Name, FirstNameToReturn ) < 0 ) {
  469. continue;
  470. }
  471. TrimmingNames = FALSE;
  472. }
  473. //
  474. // If the server's type matches the type filter, pack it into the buffer.
  475. //
  476. if (InterimEntry->Type & ServerType) {
  477. (*TotalEntries) += 1;
  478. //
  479. // If this entry will fit into the buffer, pack it in.
  480. //
  481. // Please note that we only count an entry if the entire entry
  482. // (server and comment) fits in the buffer. This is NOT
  483. // strictly Lan Manager compatible.
  484. //
  485. if ( !BufferFull &&
  486. ((ULONG_PTR)ServerEntry+EntrySize <= (ULONG_PTR)BufferEnd)) {
  487. ServerEntry->sv101_platform_id = InterimEntry->PlatformId;
  488. ServerEntry->sv101_name = InterimEntry->Name;
  489. if (NetpPackString(&ServerEntry->sv101_name,
  490. (LPBYTE)((PCHAR)ServerEntry)+EntrySize,
  491. &BufferEnd)) {
  492. if (Level == 101) {
  493. ServerEntry->sv101_version_major = InterimEntry->MajorVersionNumber;;
  494. ServerEntry->sv101_version_minor = InterimEntry->MinorVersionNumber;;
  495. ServerEntry->sv101_type = InterimEntry->Type;
  496. ServerEntry->sv101_comment = InterimEntry->Comment;
  497. if (NetpPackString(&ServerEntry->sv101_comment,
  498. (LPBYTE)((PCHAR)ServerEntry)+EntrySize,
  499. &BufferEnd)) {
  500. EntriesPacked += 1;
  501. } else {
  502. BufferFull = TRUE;
  503. }
  504. } else {
  505. EntriesPacked += 1;
  506. }
  507. #if DBG
  508. {
  509. PSERVER_INFO_101 PreviousServerInfo = (PSERVER_INFO_101)((PCHAR)ServerEntry-EntrySize);
  510. if (PreviousServerInfo >= (PSERVER_INFO_101)*ServerList) {
  511. ASSERT (wcscmp(ServerEntry->sv101_name, PreviousServerInfo->sv101_name) > 0);
  512. }
  513. }
  514. #endif
  515. } else {
  516. BufferFull = TRUE;
  517. }
  518. } else {
  519. //
  520. // If we're returning the entire list and we have exceeded
  521. // the amount that fits in the list, we can early out
  522. // now.
  523. //
  524. if (ReturnWholeList) {
  525. *TotalEntries = InterimServerList->TotalEntries;
  526. break;
  527. }
  528. BufferFull = TRUE;
  529. }
  530. //
  531. // Step to the next server entry.
  532. //
  533. ServerEntry = (PSERVER_INFO_101)((PCHAR)ServerEntry+EntrySize);
  534. }
  535. }
  536. ASSERT (InterimServerList->EntriesRead >= EntriesPacked);
  537. *EntriesRead = EntriesPacked;
  538. if (EntriesPacked != *TotalEntries) {
  539. return ERROR_MORE_DATA;
  540. } else {
  541. return NERR_Success;
  542. }
  543. }
  544. NET_API_STATUS
  545. InitializeInterimServerList(
  546. IN PINTERIM_SERVER_LIST InterimServerList,
  547. IN PINTERIM_NEW_CALLBACK NewCallback,
  548. IN PINTERIM_EXISTING_CALLBACK ExistingCallback,
  549. IN PINTERIM_DELETE_CALLBACK DeleteElementCallback,
  550. IN PINTERIM_AGE_CALLBACK AgeElementCallback
  551. )
  552. {
  553. InitializeListHead(&InterimServerList->ServerList);
  554. InterimServerList->TotalBytesNeeded = 0;
  555. InterimServerList->TotalEntries = 0;
  556. InterimServerList->EntriesRead = 0;
  557. InterimServerList->NewElementCallback = NewCallback;
  558. InterimServerList->ExistingElementCallback = ExistingCallback;
  559. InterimServerList->DeleteElementCallback = DeleteElementCallback;
  560. InterimServerList->AgeElementCallback = AgeElementCallback;
  561. return(NERR_Success);
  562. }
  563. NET_API_STATUS
  564. UninitializeInterimServerList(
  565. IN PINTERIM_SERVER_LIST InterimServerList
  566. )
  567. {
  568. PINTERIM_ELEMENT InterimElement;
  569. // KdPrint(("BROWSER: Uninitialize Interim Server List %lx\n", InterimServerList));
  570. //
  571. // Enumerate the elements in the table, deleting them as we go.
  572. //
  573. while (!IsListEmpty(&InterimServerList->ServerList)) {
  574. PLIST_ENTRY Entry;
  575. Entry = RemoveHeadList(&InterimServerList->ServerList);
  576. InterimElement = CONTAINING_RECORD(Entry, INTERIM_ELEMENT, NextElement);
  577. if (InterimServerList->DeleteElementCallback != NULL) {
  578. InterimServerList->DeleteElementCallback(InterimServerList, InterimElement);
  579. }
  580. //
  581. // There is one less element in the list.
  582. //
  583. InterimServerList->EntriesRead -= 1;
  584. InterimServerList->TotalEntries -= 1;
  585. MIDL_user_free(InterimElement);
  586. }
  587. ASSERT (InterimServerList->EntriesRead == 0);
  588. return(NERR_Success);
  589. }
  590. ULONG
  591. NumberInterimServerListElements(
  592. IN PINTERIM_SERVER_LIST InterimServerList
  593. )
  594. {
  595. PLIST_ENTRY InterimList;
  596. ULONG NumberOfEntries = 0;
  597. for (InterimList = InterimServerList->ServerList.Flink ;
  598. InterimList != &InterimServerList->ServerList ;
  599. InterimList = InterimList->Flink ) {
  600. NumberOfEntries += 1;
  601. }
  602. return NumberOfEntries;
  603. }
  604. NET_API_STATUS
  605. AgeInterimServerList(
  606. IN PINTERIM_SERVER_LIST InterimServerList
  607. )
  608. {
  609. PLIST_ENTRY InterimList, NextElement;
  610. PINTERIM_ELEMENT InterimElement;
  611. if (InterimServerList->AgeElementCallback != NULL) {
  612. //
  613. // Enumerate the elements in the table, aging them as we go.
  614. //
  615. for (InterimList = InterimServerList->ServerList.Flink ;
  616. InterimList != &InterimServerList->ServerList ;
  617. InterimList = NextElement) {
  618. InterimElement = CONTAINING_RECORD(InterimList, INTERIM_ELEMENT, NextElement);
  619. //
  620. // Call into the aging routine and if this entry is too old,
  621. // remove it from the interim list.
  622. //
  623. if (InterimServerList->AgeElementCallback(InterimServerList, InterimElement)) {
  624. ULONG ElementSize = sizeof(SERVER_INFO_101) + ((wcslen(InterimElement->Comment) + 1) * sizeof(WCHAR)) + ((wcslen(InterimElement->Name) + 1) * sizeof(WCHAR));
  625. ASSERT (ElementSize <= InterimServerList->TotalBytesNeeded);
  626. NextElement = InterimList->Flink;
  627. //
  628. // Remove this entry from the list.
  629. //
  630. RemoveEntryList(&InterimElement->NextElement);
  631. if (InterimServerList->DeleteElementCallback != NULL) {
  632. InterimServerList->DeleteElementCallback(InterimServerList, InterimElement);
  633. }
  634. //
  635. // There is one less element in the list.
  636. //
  637. InterimServerList->EntriesRead -= 1;
  638. InterimServerList->TotalEntries -= 1;
  639. //
  640. // Since this element isn't in the table any more, we don't
  641. // need to allocate memory for it.
  642. //
  643. InterimServerList->TotalBytesNeeded -= ElementSize;
  644. MIDL_user_free(InterimElement);
  645. } else {
  646. NextElement = InterimList->Flink;
  647. }
  648. }
  649. #if 0
  650. {
  651. PINTERIM_ELEMENT InterimEntry;
  652. ULONG TotalNeededForList = 0;
  653. for (InterimList = InterimServerList->ServerList.Flink ;
  654. InterimList != &InterimServerList->ServerList ;
  655. InterimList = InterimList->Flink ) {
  656. ULONG ServerElementSize;
  657. InterimEntry = CONTAINING_RECORD(InterimList, INTERIM_ELEMENT, NextElement);
  658. ServerElementSize = sizeof(SERVER_INFO_101);
  659. ServerElementSize += wcslen(InterimEntry->Name)*sizeof(WCHAR)+sizeof(WCHAR);
  660. ServerElementSize += wcslen(InterimEntry->Comment)*sizeof(WCHAR)+sizeof(WCHAR);
  661. TotalNeededForList += ServerElementSize;
  662. }
  663. if (TotalNeededForList != InterimServerList->TotalBytesNeeded) {
  664. KdPrint(("AgeInterimServerList: Too few bytes (%ld) for interim server list. %ld needed\n", InterimServerList->TotalBytesNeeded, TotalNeededForList));
  665. }
  666. }
  667. #endif
  668. }
  669. return(NERR_Success);
  670. }
  671. PINTERIM_ELEMENT
  672. LookupInterimServerList(
  673. IN PINTERIM_SERVER_LIST InterimServerList,
  674. IN LPWSTR ServerNameToLookUp
  675. )
  676. {
  677. PLIST_ENTRY InterimList;
  678. for (InterimList = InterimServerList->ServerList.Flink ;
  679. InterimList != &InterimServerList->ServerList ;
  680. InterimList = InterimList->Flink ) {
  681. PINTERIM_ELEMENT InterimEntry = CONTAINING_RECORD(InterimList, INTERIM_ELEMENT, NextElement);
  682. LONG CompareResult;
  683. if ((CompareResult = _wcsicmp(InterimEntry->Name, ServerNameToLookUp) == 0)) {
  684. return InterimEntry;
  685. }
  686. //
  687. // If we went past this guy, return an error.
  688. //
  689. if (CompareResult > 0) {
  690. return NULL;
  691. }
  692. }
  693. return NULL;
  694. }