xref: /reactos/base/services/wkssvc/rpcserver.c (revision 132d571f)
1 /*
2  *  ReactOS Services
3  *  Copyright (C) 2015 ReactOS Team
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License along
16  *  with this program; if not, write to the Free Software Foundation, Inc.,
17  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 /*
20  * COPYRIGHT:        See COPYING in the top level directory
21  * PROJECT:          ReactOS Services
22  * FILE:             base/services/wkssvc/rpcserver.c
23  * PURPOSE:          Workstation service
24  * PROGRAMMER:       Eric Kohl
25  */
26 
27 /* INCLUDES *****************************************************************/
28 
29 #include "precomp.h"
30 
31 #include "lmerr.h"
32 
33 WINE_DEFAULT_DEBUG_CHANNEL(wkssvc);
34 
35 /* FUNCTIONS *****************************************************************/
36 
37 DWORD
38 WINAPI
39 RpcThreadRoutine(
40     LPVOID lpParameter)
41 {
42     RPC_STATUS Status;
43 
44     Status = RpcServerUseProtseqEpW(L"ncacn_np", 20, L"\\pipe\\wkssvc", NULL);
45     if (Status != RPC_S_OK)
46     {
47         ERR("RpcServerUseProtseqEpW() failed (Status %lx)\n", Status);
48         return 0;
49     }
50 
51     Status = RpcServerRegisterIf(wkssvc_v1_0_s_ifspec, NULL, NULL);
52     if (Status != RPC_S_OK)
53     {
54         ERR("RpcServerRegisterIf() failed (Status %lx)\n", Status);
55         return 0;
56     }
57 
58     Status = RpcServerListen(1, RPC_C_LISTEN_MAX_CALLS_DEFAULT, FALSE);
59     if (Status != RPC_S_OK)
60     {
61         ERR("RpcServerListen() failed (Status %lx)\n", Status);
62     }
63 
64     return 0;
65 }
66 
67 
68 void __RPC_FAR * __RPC_USER midl_user_allocate(SIZE_T len)
69 {
70     return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len);
71 }
72 
73 
74 void __RPC_USER midl_user_free(void __RPC_FAR * ptr)
75 {
76     HeapFree(GetProcessHeap(), 0, ptr);
77 }
78 
79 
80 static
81 NET_API_STATUS
82 NetpGetClientLogonId(
83     _Out_ PLUID LogonId)
84 {
85     HANDLE ThreadToken = NULL;
86     TOKEN_STATISTICS Statistics;
87     ULONG Length;
88     NTSTATUS NtStatus;
89     NET_API_STATUS ApiStatus = NERR_Success;
90 
91     ApiStatus = RpcImpersonateClient(NULL);
92     if (ApiStatus != NERR_Success)
93         return ApiStatus;
94 
95     NtStatus = NtOpenThreadToken(NtCurrentThread(),
96                                  TOKEN_QUERY,
97                                  TRUE,
98                                  &ThreadToken);
99     if (!NT_SUCCESS(NtStatus))
100     {
101         ApiStatus = RtlNtStatusToDosError(NtStatus);
102         goto done;
103     }
104 
105     NtStatus = NtQueryInformationToken(ThreadToken,
106                                        TokenStatistics,
107                                        (PVOID)&Statistics,
108                                        sizeof(Statistics),
109                                        &Length);
110     if (!NT_SUCCESS(NtStatus))
111     {
112         ApiStatus = RtlNtStatusToDosError(NtStatus);
113         goto done;
114     }
115 
116     TRACE("Client LUID: %lx\n", Statistics.AuthenticationId.LowPart);
117     RtlCopyLuid(LogonId, &Statistics.AuthenticationId);
118 
119 done:
120     if (ThreadToken != NULL)
121         NtClose(ThreadToken);
122 
123     RpcRevertToSelf();
124 
125     return ApiStatus;
126 }
127 
128 
129 /* Function 0 */
130 unsigned long
131 __stdcall
132 NetrWkstaGetInfo(
133     WKSSVC_IDENTIFY_HANDLE ServerName,
134     unsigned long Level,
135     LPWKSTA_INFO *WkstaInfo)
136 {
137     WCHAR szComputerName[MAX_COMPUTERNAME_LENGTH + 1];
138     DWORD dwComputerNameLength;
139     LPCWSTR pszLanRoot = L"";
140     PWKSTA_INFO pWkstaInfo = NULL;
141     LSA_OBJECT_ATTRIBUTES ObjectAttributes;
142     LSA_HANDLE PolicyHandle;
143     PPOLICY_PRIMARY_DOMAIN_INFO DomainInfo = NULL;
144     ULONG LoggedOnUsers;
145     NTSTATUS NtStatus;
146     DWORD dwResult = NERR_Success;
147 
148     TRACE("NetrWkstaGetInfo level %lu\n", Level);
149 
150     dwComputerNameLength = MAX_COMPUTERNAME_LENGTH + 1;
151     GetComputerNameW(szComputerName, &dwComputerNameLength);
152     dwComputerNameLength++; /* include NULL terminator */
153 
154     ZeroMemory(&ObjectAttributes, sizeof(ObjectAttributes));
155     NtStatus = LsaOpenPolicy(NULL,
156                              &ObjectAttributes,
157                              POLICY_VIEW_LOCAL_INFORMATION,
158                              &PolicyHandle);
159     if (NtStatus != STATUS_SUCCESS)
160     {
161         WARN("LsaOpenPolicy() failed (Status 0x%08lx)\n", NtStatus);
162         return LsaNtStatusToWinError(NtStatus);
163     }
164 
165     NtStatus = LsaQueryInformationPolicy(PolicyHandle,
166                                          PolicyPrimaryDomainInformation,
167                                          (PVOID*)&DomainInfo);
168 
169     LsaClose(PolicyHandle);
170 
171     if (NtStatus != STATUS_SUCCESS)
172     {
173         WARN("LsaQueryInformationPolicy() failed (Status 0x%08lx)\n", NtStatus);
174         return LsaNtStatusToWinError(NtStatus);
175     }
176 
177     if (Level == 102)
178     {
179         MSV1_0_ENUMUSERS_REQUEST EnumRequest;
180         PMSV1_0_ENUMUSERS_RESPONSE EnumResponseBuffer = NULL;
181         DWORD EnumResponseBufferSize = 0;
182         NTSTATUS ProtocolStatus;
183 
184         /* enumerate all currently logged-on users */
185         EnumRequest.MessageType = MsV1_0EnumerateUsers;
186         NtStatus = LsaCallAuthenticationPackage(LsaHandle,
187                                                 LsaAuthenticationPackage,
188                                                 &EnumRequest,
189                                                 sizeof(EnumRequest),
190                                                 (PVOID*)&EnumResponseBuffer,
191                                                 &EnumResponseBufferSize,
192                                                 &ProtocolStatus);
193         if (!NT_SUCCESS(NtStatus))
194         {
195             dwResult = RtlNtStatusToDosError(NtStatus);
196             goto done;
197         }
198 
199         LoggedOnUsers = EnumResponseBuffer->NumberOfLoggedOnUsers;
200 
201         LsaFreeReturnBuffer(EnumResponseBuffer);
202     }
203 
204     switch (Level)
205     {
206         case 100:
207             pWkstaInfo = midl_user_allocate(sizeof(WKSTA_INFO_100));
208             if (pWkstaInfo == NULL)
209             {
210                 dwResult = ERROR_NOT_ENOUGH_MEMORY;
211                 break;
212             }
213 
214             pWkstaInfo->WkstaInfo100.wki100_platform_id = PLATFORM_ID_NT;
215 
216             pWkstaInfo->WkstaInfo100.wki100_computername = midl_user_allocate(dwComputerNameLength * sizeof(WCHAR));
217             if (pWkstaInfo->WkstaInfo100.wki100_computername != NULL)
218                 wcscpy(pWkstaInfo->WkstaInfo100.wki100_computername, szComputerName);
219 
220             pWkstaInfo->WkstaInfo100.wki100_langroup = midl_user_allocate((wcslen(DomainInfo->Name.Buffer) + 1) * sizeof(WCHAR));
221             if (pWkstaInfo->WkstaInfo100.wki100_langroup != NULL)
222                 wcscpy(pWkstaInfo->WkstaInfo100.wki100_langroup, DomainInfo->Name.Buffer);
223 
224             pWkstaInfo->WkstaInfo100.wki100_ver_major = VersionInfo.dwMajorVersion;
225             pWkstaInfo->WkstaInfo100.wki100_ver_minor = VersionInfo.dwMinorVersion;
226 
227             *WkstaInfo = pWkstaInfo;
228             break;
229 
230         case 101:
231             pWkstaInfo = midl_user_allocate(sizeof(WKSTA_INFO_101));
232             if (pWkstaInfo == NULL)
233             {
234                 dwResult = ERROR_NOT_ENOUGH_MEMORY;
235                 break;
236             }
237 
238             pWkstaInfo->WkstaInfo101.wki101_platform_id = PLATFORM_ID_NT;
239 
240             pWkstaInfo->WkstaInfo101.wki101_computername = midl_user_allocate(dwComputerNameLength * sizeof(WCHAR));
241             if (pWkstaInfo->WkstaInfo101.wki101_computername != NULL)
242                 wcscpy(pWkstaInfo->WkstaInfo101.wki101_computername, szComputerName);
243 
244             pWkstaInfo->WkstaInfo101.wki101_langroup = midl_user_allocate((wcslen(DomainInfo->Name.Buffer) + 1) * sizeof(WCHAR));
245             if (pWkstaInfo->WkstaInfo101.wki101_langroup != NULL)
246                 wcscpy(pWkstaInfo->WkstaInfo101.wki101_langroup, DomainInfo->Name.Buffer);
247 
248             pWkstaInfo->WkstaInfo101.wki101_ver_major = VersionInfo.dwMajorVersion;
249             pWkstaInfo->WkstaInfo101.wki101_ver_minor = VersionInfo.dwMinorVersion;
250 
251             pWkstaInfo->WkstaInfo101.wki101_lanroot = midl_user_allocate((wcslen(pszLanRoot) + 1) * sizeof(WCHAR));
252             if (pWkstaInfo->WkstaInfo101.wki101_lanroot != NULL)
253                 wcscpy(pWkstaInfo->WkstaInfo101.wki101_lanroot, pszLanRoot);
254 
255             *WkstaInfo = pWkstaInfo;
256             break;
257 
258         case 102:
259             pWkstaInfo = midl_user_allocate(sizeof(WKSTA_INFO_102));
260             if (pWkstaInfo == NULL)
261             {
262                 dwResult = ERROR_NOT_ENOUGH_MEMORY;
263                 break;
264             }
265 
266             pWkstaInfo->WkstaInfo102.wki102_platform_id = PLATFORM_ID_NT;
267 
268             pWkstaInfo->WkstaInfo102.wki102_computername = midl_user_allocate(dwComputerNameLength * sizeof(WCHAR));
269             if (pWkstaInfo->WkstaInfo102.wki102_computername != NULL)
270                 wcscpy(pWkstaInfo->WkstaInfo102.wki102_computername, szComputerName);
271 
272             pWkstaInfo->WkstaInfo102.wki102_langroup = midl_user_allocate((wcslen(DomainInfo->Name.Buffer) + 1) * sizeof(WCHAR));
273             if (pWkstaInfo->WkstaInfo102.wki102_langroup != NULL)
274                 wcscpy(pWkstaInfo->WkstaInfo102.wki102_langroup, DomainInfo->Name.Buffer);
275 
276             pWkstaInfo->WkstaInfo102.wki102_ver_major = VersionInfo.dwMajorVersion;
277             pWkstaInfo->WkstaInfo102.wki102_ver_minor = VersionInfo.dwMinorVersion;
278 
279             pWkstaInfo->WkstaInfo102.wki102_lanroot = midl_user_allocate((wcslen(pszLanRoot) + 1) * sizeof(WCHAR));
280             if (pWkstaInfo->WkstaInfo102.wki102_lanroot != NULL)
281                 wcscpy(pWkstaInfo->WkstaInfo102.wki102_lanroot, pszLanRoot);
282 
283             pWkstaInfo->WkstaInfo102.wki102_logged_on_users = LoggedOnUsers;
284 
285             *WkstaInfo = pWkstaInfo;
286             break;
287 
288         case 502:
289             pWkstaInfo = midl_user_allocate(sizeof(WKSTA_INFO_502));
290             if (pWkstaInfo == NULL)
291             {
292                 dwResult = ERROR_NOT_ENOUGH_MEMORY;
293                 break;
294             }
295 
296             CopyMemory(&pWkstaInfo->WkstaInfo502, &WkstaInfo502, sizeof(WKSTA_INFO_502));
297 
298             *WkstaInfo = pWkstaInfo;
299             break;
300 
301         default:
302             FIXME("Level %lu unimplemented\n", Level);
303             dwResult = ERROR_INVALID_LEVEL;
304             break;
305     }
306 
307 done:
308     if (DomainInfo != NULL)
309         LsaFreeMemory(DomainInfo);
310 
311     return dwResult;
312 }
313 
314 
315 /* Function 1 */
316 unsigned long
317 __stdcall
318 NetrWkstaSetInfo(
319     WKSSVC_IDENTIFY_HANDLE ServerName,
320     unsigned long Level,
321     LPWKSTA_INFO WkstaInfo,
322     unsigned long *ErrorParameter)
323 {
324     DWORD dwErrParam = 0;
325     DWORD dwResult = NERR_Success;
326 
327     TRACE("NetrWkstaSetInfo(%lu %p %p)\n",
328           Level, WkstaInfo, ErrorParameter);
329 
330     switch (Level)
331     {
332         case 502:
333             if (WkstaInfo->WkstaInfo502.wki502_keep_conn >= 1 && WkstaInfo->WkstaInfo502.wki502_keep_conn <= 65535)
334             {
335                 WkstaInfo502.wki502_keep_conn = WkstaInfo->WkstaInfo502.wki502_keep_conn;
336             }
337             else
338             {
339                 dwErrParam = WKSTA_KEEPCONN_PARMNUM;
340                 dwResult = ERROR_INVALID_PARAMETER;
341             }
342 
343             if (dwResult == NERR_Success)
344             {
345                 if (WkstaInfo->WkstaInfo502.wki502_max_cmds >= 50 && WkstaInfo->WkstaInfo502.wki502_max_cmds <= 65535)
346                 {
347                     WkstaInfo502.wki502_max_cmds = WkstaInfo->WkstaInfo502.wki502_max_cmds;
348                 }
349                 else
350                 {
351                     dwErrParam = WKSTA_MAXCMDS_PARMNUM;
352                     dwResult = ERROR_INVALID_PARAMETER;
353                 }
354             }
355 
356             if (dwResult == NERR_Success)
357             {
358                 if (WkstaInfo->WkstaInfo502.wki502_sess_timeout >= 60 && WkstaInfo->WkstaInfo502.wki502_sess_timeout <= 65535)
359                 {
360                     WkstaInfo502.wki502_sess_timeout = WkstaInfo->WkstaInfo502.wki502_sess_timeout;
361                 }
362                 else
363                 {
364                     dwErrParam = WKSTA_SESSTIMEOUT_PARMNUM;
365                     dwResult = ERROR_INVALID_PARAMETER;
366                 }
367             }
368 
369             if (dwResult == NERR_Success)
370             {
371                 if (WkstaInfo->WkstaInfo502.wki502_dormant_file_limit != 0)
372                 {
373                     WkstaInfo502.wki502_dormant_file_limit = WkstaInfo->WkstaInfo502.wki502_dormant_file_limit;
374                 }
375                 else
376                 {
377                     dwErrParam = WKSTA_DORMANTFILELIMIT_PARMNUM;
378                     dwResult = ERROR_INVALID_PARAMETER;
379                 }
380             }
381             break;
382 
383         case 1013:
384             if (WkstaInfo->WkstaInfo1013.wki1013_keep_conn >= 1 && WkstaInfo->WkstaInfo1013.wki1013_keep_conn <= 65535)
385             {
386                 WkstaInfo502.wki502_keep_conn = WkstaInfo->WkstaInfo1013.wki1013_keep_conn;
387             }
388             else
389             {
390                 dwErrParam = WKSTA_KEEPCONN_PARMNUM;
391                 dwResult = ERROR_INVALID_PARAMETER;
392             }
393             break;
394 
395         case 1018:
396             if (WkstaInfo->WkstaInfo1018.wki1018_sess_timeout >= 60 && WkstaInfo->WkstaInfo1018.wki1018_sess_timeout <= 65535)
397             {
398                 WkstaInfo502.wki502_sess_timeout = WkstaInfo->WkstaInfo1018.wki1018_sess_timeout;
399             }
400             else
401             {
402                 dwErrParam = WKSTA_SESSTIMEOUT_PARMNUM;
403                 dwResult = ERROR_INVALID_PARAMETER;
404             }
405             break;
406 
407         case 1046:
408             if (WkstaInfo->WkstaInfo1046.wki1046_dormant_file_limit != 0)
409             {
410                 WkstaInfo502.wki502_dormant_file_limit = WkstaInfo->WkstaInfo1046.wki1046_dormant_file_limit;
411             }
412             else
413             {
414                 dwErrParam = WKSTA_DORMANTFILELIMIT_PARMNUM;
415                 dwResult = ERROR_INVALID_PARAMETER;
416             }
417             break;
418 
419         default:
420             ERR("Invalid Level %lu\n", Level);
421             dwResult = ERROR_INVALID_LEVEL;
422             break;
423     }
424 
425     /* Save the workstation in the registry */
426     if (dwResult == NERR_Success)
427     {
428         SaveWorkstationInfo(Level);
429 
430         /* FIXME: Notify the redirector */
431     }
432 
433     if ((dwResult == ERROR_INVALID_PARAMETER) && (ErrorParameter != NULL))
434         *ErrorParameter = dwErrParam;
435 
436     return dwResult;
437 }
438 
439 
440 /* Function 2 */
441 unsigned long
442 __stdcall
443 NetrWkstaUserEnum(
444     WKSSVC_IDENTIFY_HANDLE ServerName,
445     LPWKSTA_USER_ENUM_STRUCT UserInfo,
446     unsigned long PreferredMaximumLength,
447     unsigned long *TotalEntries,
448     unsigned long *ResumeHandle)
449 {
450     MSV1_0_ENUMUSERS_REQUEST EnumRequest;
451     PMSV1_0_ENUMUSERS_RESPONSE EnumResponseBuffer = NULL;
452     MSV1_0_GETUSERINFO_REQUEST UserInfoRequest;
453     PMSV1_0_GETUSERINFO_RESPONSE UserInfoResponseBuffer = NULL;
454     PMSV1_0_GETUSERINFO_RESPONSE *UserInfoArray = NULL;
455     DWORD EnumResponseBufferSize = 0;
456     DWORD UserInfoResponseBufferSize = 0;
457     NTSTATUS Status, ProtocolStatus;
458     ULONG i, start, count;
459     PLUID pLogonId;
460     PULONG pEnumHandle;
461     DWORD dwResult = NERR_Success;
462 
463     PWKSTA_USER_INFO_0 pUserInfo0 = NULL;
464     PWKSTA_USER_INFO_1 pUserInfo1 = NULL;
465 
466     TRACE("NetrWkstaUserEnum(%p %p 0x%lx %p %p)\n",
467           ServerName, UserInfo, PreferredMaximumLength, TotalEntries, ResumeHandle);
468 
469     if (UserInfo->Level > 1)
470     {
471         ERR("Invalid Level %lu\n", UserInfo->Level);
472         return ERROR_INVALID_LEVEL;
473     }
474 
475     /* Enumerate all currently logged-on users */
476     EnumRequest.MessageType = MsV1_0EnumerateUsers;
477     Status = LsaCallAuthenticationPackage(LsaHandle,
478                                           LsaAuthenticationPackage,
479                                           &EnumRequest,
480                                           sizeof(EnumRequest),
481                                           (PVOID*)&EnumResponseBuffer,
482                                           &EnumResponseBufferSize,
483                                           &ProtocolStatus);
484 
485     TRACE("LsaCallAuthenticationPackage Status 0x%08lx ResponseBufferSize %lu\n", Status, EnumResponseBufferSize);
486     if (!NT_SUCCESS(Status))
487     {
488         dwResult = RtlNtStatusToDosError(Status);
489         goto done;
490     }
491 
492     TRACE("LoggedOnUsers: %lu\n", EnumResponseBuffer->NumberOfLoggedOnUsers);
493     TRACE("ResponseBuffer: 0x%p\n", EnumResponseBuffer);
494     TRACE("LogonIds: 0x%p\n", EnumResponseBuffer->LogonIds);
495     TRACE("EnumHandles: 0x%p\n", EnumResponseBuffer->EnumHandles);
496     if (EnumResponseBuffer->NumberOfLoggedOnUsers > 0)
497     {
498         pLogonId = EnumResponseBuffer->LogonIds;
499         pEnumHandle = EnumResponseBuffer->EnumHandles;
500         TRACE("pLogonId: 0x%p\n", pLogonId);
501         TRACE("pEnumHandle: 0x%p\n", pEnumHandle);
502 
503         UserInfoArray = RtlAllocateHeap(RtlGetProcessHeap(),
504                                         HEAP_ZERO_MEMORY,
505                                         EnumResponseBuffer->NumberOfLoggedOnUsers * sizeof(PMSV1_0_GETUSERINFO_RESPONSE));
506         if (UserInfoArray == NULL)
507         {
508             dwResult = ERROR_NOT_ENOUGH_MEMORY;
509             goto done;
510         }
511 
512         for (i = 0; i < EnumResponseBuffer->NumberOfLoggedOnUsers; i++)
513         {
514             TRACE("Logon %lu: 0x%08lx  %lu\n", i, pLogonId->LowPart, *pEnumHandle);
515 
516             UserInfoRequest.MessageType = MsV1_0GetUserInfo;
517             UserInfoRequest.LogonId = *pLogonId;
518             Status = LsaCallAuthenticationPackage(LsaHandle,
519                                                   LsaAuthenticationPackage,
520                                                   &UserInfoRequest,
521                                                   sizeof(UserInfoRequest),
522                                                   (PVOID*)&UserInfoResponseBuffer,
523                                                   &UserInfoResponseBufferSize,
524                                                   &ProtocolStatus);
525             TRACE("LsaCallAuthenticationPackage:MsV1_0GetUserInfo Status 0x%08lx ResponseBufferSize %lu\n", Status, UserInfoResponseBufferSize);
526             if (!NT_SUCCESS(Status))
527             {
528                 dwResult = RtlNtStatusToDosError(Status);
529                 goto done;
530             }
531 
532             UserInfoArray[i] = UserInfoResponseBuffer;
533 
534             TRACE("UserName: %wZ\n", &UserInfoArray[i]->UserName);
535             TRACE("LogonDomain: %wZ\n", &UserInfoArray[i]->LogonDomainName);
536             TRACE("LogonServer: %wZ\n", &UserInfoArray[i]->LogonServer);
537 
538             pLogonId++;
539             pEnumHandle++;
540         }
541 
542         if (PreferredMaximumLength == MAX_PREFERRED_LENGTH)
543         {
544             start = 0;
545             count = EnumResponseBuffer->NumberOfLoggedOnUsers;
546         }
547         else
548         {
549             FIXME("Calculate the start index and the number of matching array entries!");
550             dwResult = ERROR_CALL_NOT_IMPLEMENTED;
551             goto done;
552         }
553 
554         switch (UserInfo->Level)
555         {
556             case 0:
557                 pUserInfo0 = midl_user_allocate(count * sizeof(WKSTA_USER_INFO_0));
558                 if (pUserInfo0 == NULL)
559                 {
560                     ERR("\n");
561                     dwResult = ERROR_NOT_ENOUGH_MEMORY;
562                     break;
563                 }
564 
565                 ZeroMemory(pUserInfo0, count * sizeof(WKSTA_USER_INFO_0));
566 
567                 for (i = 0; i < 0 + count; i++)
568                 {
569                     pUserInfo0[i].wkui0_username = midl_user_allocate(UserInfoArray[start + i]->UserName.Length + sizeof(WCHAR));
570                     if (pUserInfo0[i].wkui0_username == NULL)
571                     {
572                         ERR("\n");
573                         dwResult = ERROR_NOT_ENOUGH_MEMORY;
574                         break;
575                     }
576 
577                     ZeroMemory(pUserInfo0[i].wkui0_username, UserInfoArray[start + i]->UserName.Length + sizeof(WCHAR));
578                     CopyMemory(pUserInfo0[i].wkui0_username, UserInfoArray[start + i]->UserName.Buffer, UserInfoArray[start + i]->UserName.Length);
579                 }
580 
581                 UserInfo->WkstaUserInfo.Level0.EntriesRead = count;
582                 UserInfo->WkstaUserInfo.Level0.Buffer = pUserInfo0;
583                 *TotalEntries = EnumResponseBuffer->NumberOfLoggedOnUsers;
584                 *ResumeHandle = 0;
585                 break;
586 
587             case 1:
588                 pUserInfo1 = midl_user_allocate(count * sizeof(WKSTA_USER_INFO_1));
589                 if (pUserInfo1 == NULL)
590                 {
591                     ERR("\n");
592                     dwResult = ERROR_NOT_ENOUGH_MEMORY;
593                     break;
594                 }
595 
596                 ZeroMemory(pUserInfo1, count * sizeof(WKSTA_USER_INFO_1));
597 
598                 for (i = 0; i < 0 + count; i++)
599                 {
600                     pUserInfo1[i].wkui1_username = midl_user_allocate(UserInfoArray[start + i]->UserName.Length + sizeof(WCHAR));
601                     if (pUserInfo1[i].wkui1_username == NULL)
602                     {
603                         ERR("\n");
604                         dwResult = ERROR_NOT_ENOUGH_MEMORY;
605                         break;
606                     }
607 
608                     ZeroMemory(pUserInfo1[i].wkui1_username, UserInfoArray[start + i]->UserName.Length + sizeof(WCHAR));
609                     CopyMemory(pUserInfo1[i].wkui1_username, UserInfoArray[start + i]->UserName.Buffer, UserInfoArray[start + i]->UserName.Length);
610 
611                     pUserInfo1[i].wkui1_logon_domain = midl_user_allocate(UserInfoArray[start + i]->LogonDomainName.Length + sizeof(WCHAR));
612                     if (pUserInfo1[i].wkui1_logon_domain == NULL)
613                     {
614                         ERR("\n");
615                         dwResult = ERROR_NOT_ENOUGH_MEMORY;
616                         break;
617                     }
618 
619                     ZeroMemory(pUserInfo1[i].wkui1_logon_domain, UserInfoArray[start + i]->LogonDomainName.Length + sizeof(WCHAR));
620                     CopyMemory(pUserInfo1[i].wkui1_logon_domain, UserInfoArray[start + i]->LogonDomainName.Buffer, UserInfoArray[start + i]->LogonDomainName.Length);
621 
622                     // FIXME: wkui1_oth_domains
623 
624                     pUserInfo1[i].wkui1_logon_server = midl_user_allocate(UserInfoArray[start + i]->LogonServer.Length + sizeof(WCHAR));
625                     if (pUserInfo1[i].wkui1_logon_server == NULL)
626                     {
627                         ERR("\n");
628                         dwResult = ERROR_NOT_ENOUGH_MEMORY;
629                         break;
630                     }
631 
632                     ZeroMemory(pUserInfo1[i].wkui1_logon_server, UserInfoArray[start + i]->LogonServer.Length + sizeof(WCHAR));
633                     CopyMemory(pUserInfo1[i].wkui1_logon_server, UserInfoArray[start + i]->LogonServer.Buffer, UserInfoArray[start + i]->LogonServer.Length);
634                 }
635 
636                 UserInfo->WkstaUserInfo.Level1.EntriesRead = count;
637                 UserInfo->WkstaUserInfo.Level1.Buffer = pUserInfo1;
638                 *TotalEntries = EnumResponseBuffer->NumberOfLoggedOnUsers;
639                 *ResumeHandle = 0;
640                 break;
641 
642                 break;
643         }
644     }
645     else
646     {
647         if (UserInfo->Level == 0)
648         {
649             UserInfo->WkstaUserInfo.Level0.Buffer = NULL;
650             UserInfo->WkstaUserInfo.Level0.EntriesRead = 0;
651         }
652         else
653         {
654             UserInfo->WkstaUserInfo.Level1.Buffer = NULL;
655             UserInfo->WkstaUserInfo.Level1.EntriesRead = 0;
656         }
657 
658         *TotalEntries = 0;
659         dwResult = NERR_Success;
660     }
661 
662 done:
663     if (UserInfoArray !=NULL)
664     {
665 
666         for (i = 0; i < EnumResponseBuffer->NumberOfLoggedOnUsers; i++)
667         {
668             if (UserInfoArray[i]->UserName.Buffer != NULL)
669                 LsaFreeReturnBuffer(UserInfoArray[i]->UserName.Buffer);
670 
671             if (UserInfoArray[i]->LogonDomainName.Buffer != NULL)
672                 LsaFreeReturnBuffer(UserInfoArray[i]->LogonDomainName.Buffer);
673 
674             if (UserInfoArray[i]->LogonServer.Buffer != NULL)
675                 LsaFreeReturnBuffer(UserInfoArray[i]->LogonServer.Buffer);
676 
677             LsaFreeReturnBuffer(UserInfoArray[i]);
678         }
679 
680         RtlFreeHeap(RtlGetProcessHeap(), 0, UserInfoArray);
681     }
682 
683     if (EnumResponseBuffer != NULL)
684         LsaFreeReturnBuffer(EnumResponseBuffer);
685 
686     return dwResult;
687 }
688 
689 
690 /* Function 3 */
691 unsigned long
692 __stdcall
693 NetrWkstaUserGetInfo(
694     WKSSVC_IDENTIFY_HANDLE Unused,
695     unsigned long Level,
696     LPWKSTA_USER_INFO *UserInfo)
697 {
698     MSV1_0_GETUSERINFO_REQUEST UserInfoRequest;
699     PMSV1_0_GETUSERINFO_RESPONSE UserInfoResponseBuffer = NULL;
700     DWORD UserInfoResponseBufferSize = 0;
701     NTSTATUS Status, ProtocolStatus;
702     LUID LogonId;
703     PWKSTA_USER_INFO pUserInfo;
704     DWORD dwResult = NERR_Success;
705 
706     TRACE("NetrWkstaUserGetInfo(%s, %d, %p)\n", debugstr_w(Unused), Level, UserInfo);
707 
708     if (Unused != NULL)
709         return ERROR_INVALID_PARAMETER;
710 
711     if (Level > 1 && Level != 1101)
712         return ERROR_INVALID_LEVEL;
713 
714     if (Level != 1101)
715     {
716         dwResult = NetpGetClientLogonId(&LogonId);
717         if (dwResult != NERR_Success)
718         {
719             ERR("NetpGetClientLogonId() failed (%u)\n", dwResult);
720             return dwResult;
721         }
722 
723         TRACE("LogonId: 0x%08lx\n", LogonId.LowPart);
724 
725         UserInfoRequest.MessageType = MsV1_0GetUserInfo;
726         UserInfoRequest.LogonId = LogonId;
727         Status = LsaCallAuthenticationPackage(LsaHandle,
728                                               LsaAuthenticationPackage,
729                                               &UserInfoRequest,
730                                               sizeof(UserInfoRequest),
731                                               (PVOID*)&UserInfoResponseBuffer,
732                                               &UserInfoResponseBufferSize,
733                                               &ProtocolStatus);
734         TRACE("LsaCallAuthenticationPackage:MsV1_0GetUserInfo Status 0x%08lx ResponseBufferSize %lu\n", Status, UserInfoResponseBufferSize);
735         if (!NT_SUCCESS(Status))
736         {
737             ERR("\n");
738             return RtlNtStatusToDosError(Status);
739         }
740 
741         TRACE("UserName: %wZ\n", &UserInfoResponseBuffer->UserName);
742         TRACE("LogonDomain: %wZ\n", &UserInfoResponseBuffer->LogonDomainName);
743         TRACE("LogonServer: %wZ\n", &UserInfoResponseBuffer->LogonServer);
744     }
745 
746     switch (Level)
747     {
748         case 0:
749             pUserInfo = midl_user_allocate(sizeof(WKSTA_USER_INFO_0));
750             if (pUserInfo == NULL)
751             {
752                 ERR("\n");
753                 dwResult = ERROR_NOT_ENOUGH_MEMORY;
754                 break;
755             }
756 
757             ZeroMemory(pUserInfo, sizeof(WKSTA_USER_INFO_0));
758 
759             /* User Name */
760             pUserInfo->UserInfo0.wkui0_username =
761                 midl_user_allocate(UserInfoResponseBuffer->UserName.Length + sizeof(WCHAR));
762             if (pUserInfo->UserInfo0.wkui0_username == NULL)
763             {
764                 ERR("\n");
765                 midl_user_free(pUserInfo);
766                 dwResult = ERROR_NOT_ENOUGH_MEMORY;
767                 break;
768             }
769 
770             ZeroMemory(pUserInfo->UserInfo0.wkui0_username,
771                        UserInfoResponseBuffer->UserName.Length + sizeof(WCHAR));
772             CopyMemory(pUserInfo->UserInfo0.wkui0_username,
773                        UserInfoResponseBuffer->UserName.Buffer,
774                        UserInfoResponseBuffer->UserName.Length);
775 
776             *UserInfo = pUserInfo;
777             break;
778 
779         case 1:
780             pUserInfo = midl_user_allocate(sizeof(WKSTA_USER_INFO_1));
781             if (pUserInfo == NULL)
782             {
783                 ERR("\n");
784                 dwResult = ERROR_NOT_ENOUGH_MEMORY;
785                 break;
786             }
787 
788             ZeroMemory(pUserInfo, sizeof(WKSTA_USER_INFO_1));
789 
790             /* User Name */
791             pUserInfo->UserInfo1.wkui1_username =
792                 midl_user_allocate(UserInfoResponseBuffer->UserName.Length + sizeof(WCHAR));
793             if (pUserInfo->UserInfo1.wkui1_username == NULL)
794             {
795                 ERR("\n");
796                 midl_user_free(pUserInfo);
797                 dwResult = ERROR_NOT_ENOUGH_MEMORY;
798                 break;
799             }
800 
801             ZeroMemory(pUserInfo->UserInfo1.wkui1_username,
802                        UserInfoResponseBuffer->UserName.Length + sizeof(WCHAR));
803             CopyMemory(pUserInfo->UserInfo1.wkui1_username,
804                        UserInfoResponseBuffer->UserName.Buffer,
805                        UserInfoResponseBuffer->UserName.Length);
806 
807             /* Logon Domain Name */
808             pUserInfo->UserInfo1.wkui1_logon_domain =
809                 midl_user_allocate(UserInfoResponseBuffer->LogonDomainName.Length + sizeof(WCHAR));
810             if (pUserInfo->UserInfo1.wkui1_logon_domain == NULL)
811             {
812                 ERR("\n");
813                 midl_user_free(pUserInfo->UserInfo1.wkui1_username);
814                 midl_user_free(pUserInfo);
815                 dwResult = ERROR_NOT_ENOUGH_MEMORY;
816                 break;
817             }
818 
819             ZeroMemory(pUserInfo->UserInfo1.wkui1_logon_domain,
820                        UserInfoResponseBuffer->LogonDomainName.Length + sizeof(WCHAR));
821             CopyMemory(pUserInfo->UserInfo1.wkui1_logon_domain,
822                        UserInfoResponseBuffer->LogonDomainName.Buffer,
823                        UserInfoResponseBuffer->LogonDomainName.Length);
824 
825             /* FIXME: wkui1_oth_domains */
826 
827             /* Logon Server */
828             pUserInfo->UserInfo1.wkui1_logon_server =
829                 midl_user_allocate(UserInfoResponseBuffer->LogonServer.Length + sizeof(WCHAR));
830             if (pUserInfo->UserInfo1.wkui1_logon_server == NULL)
831             {
832                 ERR("\n");
833                 midl_user_free(pUserInfo->UserInfo1.wkui1_username);
834                 midl_user_free(pUserInfo->UserInfo1.wkui1_logon_domain);
835                 midl_user_free(pUserInfo);
836                 dwResult = ERROR_NOT_ENOUGH_MEMORY;
837                 break;
838             }
839 
840             ZeroMemory(pUserInfo->UserInfo1.wkui1_logon_server,
841                        UserInfoResponseBuffer->LogonServer.Length + sizeof(WCHAR));
842             CopyMemory(pUserInfo->UserInfo1.wkui1_logon_server,
843                        UserInfoResponseBuffer->LogonServer.Buffer,
844                        UserInfoResponseBuffer->LogonServer.Length);
845 
846             *UserInfo = pUserInfo;
847             break;
848 
849         case 1101:
850             /* FIXME: wkui1101_oth_domains */
851             break;
852 
853         default:
854             ERR("\n");
855             dwResult = ERROR_INVALID_LEVEL;
856             break;
857     }
858 
859     if (UserInfoResponseBuffer)
860         LsaFreeReturnBuffer(UserInfoResponseBuffer);
861 
862     return dwResult;
863 }
864 
865 
866 /* Function 4 */
867 unsigned long
868 __stdcall
869 NetrWkstaUserSetInfo (
870     WKSSVC_IDENTIFY_HANDLE Unused,
871     unsigned long Level,
872     LPWKSTA_USER_INFO UserInfo,
873     unsigned long *ErrorParameter)
874 {
875     UNIMPLEMENTED;
876     return 0;
877 }
878 
879 
880 /* Function 5 */
881 unsigned long
882 __stdcall
883 NetrWkstaTransportEnum(
884     WKSSVC_IDENTIFY_HANDLE ServerName,
885     LPWKSTA_TRANSPORT_ENUM_STRUCT TransportInfo,
886     unsigned long PreferredMaximumLength,
887     unsigned long* TotalEntries,
888     unsigned long *ResumeHandle)
889 {
890     UNIMPLEMENTED;
891     return 0;
892 }
893 
894 
895 /* Function 6 */
896 unsigned long
897 __stdcall
898 NetrWkstaTransportAdd(
899     WKSSVC_IDENTIFY_HANDLE ServerName,
900     unsigned long Level,
901     LPWKSTA_TRANSPORT_INFO_0 TransportInfo,
902     unsigned long *ErrorParameter)
903 {
904     UNIMPLEMENTED;
905     return 0;
906 }
907 
908 
909 /* Function 7 */
910 unsigned long
911 __stdcall
912 NetrWkstaTransportDel(
913     WKSSVC_IDENTIFY_HANDLE ServerName,
914     wchar_t *TransportName,
915     unsigned long ForceLevel)
916 {
917     UNIMPLEMENTED;
918     return 0;
919 }
920 
921 
922 /* Function 8 */
923 unsigned long
924 __stdcall
925 NetrUseAdd(
926     WKSSVC_IMPERSONATE_HANDLE ServerName,
927     unsigned long Level,
928     LPUSE_INFO InfoStruct,
929     unsigned long *ErrorParameter)
930 {
931     UNIMPLEMENTED;
932     return 0;
933 }
934 
935 
936 /* Function 9 */
937 unsigned long
938 __stdcall
939 NetrUseGetInfo(
940     WKSSVC_IMPERSONATE_HANDLE ServerName,
941     wchar_t *UseName,
942     unsigned long Level,
943     LPUSE_INFO InfoStruct)
944 {
945     UNIMPLEMENTED;
946     return 0;
947 }
948 
949 
950 /* Function 10 */
951 unsigned long
952 __stdcall
953 NetrUseDel(
954     WKSSVC_IMPERSONATE_HANDLE ServerName,
955     wchar_t *UseName,
956     unsigned long ForceLevel)
957 {
958     UNIMPLEMENTED;
959     return 0;
960 }
961 
962 
963 /* Function 11 */
964 unsigned long
965 __stdcall
966 NetrUseEnum(
967     WKSSVC_IDENTIFY_HANDLE ServerName,
968     LPUSE_ENUM_STRUCT InfoStruct,
969     unsigned long PreferredMaximumLength,
970     unsigned long *TotalEntries,
971     unsigned long *ResumeHandle)
972 {
973     UNIMPLEMENTED;
974     return 0;
975 }
976 
977 
978 /* Function 12 - Not used on wire */
979 unsigned long
980 __stdcall
981 NetrMessageBufferSend(void)
982 {
983     TRACE("NetrMessageBufferSend()\n");
984     return ERROR_NOT_SUPPORTED;
985 }
986 
987 
988 /* Function 13 */
989 unsigned long
990 __stdcall
991 NetrWorkstationStatisticsGet(
992     WKSSVC_IDENTIFY_HANDLE ServerName,
993     wchar_t *ServiceName,
994     unsigned long Level,
995     unsigned long Options,
996     LPSTAT_WORKSTATION_0 *Buffer)
997 {
998     PSTAT_WORKSTATION_0 pStatBuffer;
999 
1000     TRACE("NetrWorkstationStatisticsGet(%p %p %lu 0x%lx %p)\n",
1001           ServerName, ServiceName, Level, Options, Buffer);
1002 
1003     if (Level != 0)
1004         return ERROR_INVALID_LEVEL;
1005 
1006     if (Options != 0)
1007         return ERROR_INVALID_PARAMETER;
1008 
1009     pStatBuffer = midl_user_allocate(sizeof(STAT_WORKSTATION_0));
1010     if (pStatBuffer == NULL)
1011         return ERROR_NOT_ENOUGH_MEMORY;
1012 
1013     ZeroMemory(pStatBuffer, sizeof(STAT_WORKSTATION_0));
1014 
1015     // FIXME: Return the actual statistcs data!
1016 
1017     *Buffer = pStatBuffer;
1018 
1019     return NERR_Success;
1020 }
1021 
1022 
1023 /* Function 14 - Not used on wire */
1024 unsigned long
1025 __stdcall
1026 NetrLogonDomainNameAdd(
1027     WKSSVC_IDENTIFY_HANDLE DomainName)
1028 {
1029     TRACE("NetrLogonDomainNameAdd(%s)\n",
1030           debugstr_w(DomainName));
1031     return ERROR_NOT_SUPPORTED;
1032 }
1033 
1034 
1035 /* Function 15 - Not used on wire */
1036 unsigned long
1037 __stdcall
1038 NetrLogonDomainNameDel(
1039     WKSSVC_IDENTIFY_HANDLE DomainName)
1040 {
1041     TRACE("NetrLogonDomainNameDel(%s)\n",
1042           debugstr_w(DomainName));
1043     return ERROR_NOT_SUPPORTED;
1044 }
1045 
1046 
1047 /* Function 16 - Not used on wire */
1048 unsigned long
1049 __stdcall
1050 NetrJoinDomain(void)
1051 {
1052     TRACE("NetrJoinDomain()\n");
1053     return ERROR_NOT_SUPPORTED;
1054 }
1055 
1056 
1057 /* Function 17 - Not used on wire */
1058 unsigned long
1059 __stdcall
1060 NetrUnjoinDomain(void)
1061 {
1062     TRACE("NetrUnjoinDomain()\n");
1063     return ERROR_NOT_SUPPORTED;
1064 }
1065 
1066 
1067 /* Function 18 - Not used on wire */
1068 unsigned long
1069 __stdcall
1070 NetrValidateName(void)
1071 {
1072     TRACE("NetrValidateName()\n");
1073     return ERROR_NOT_SUPPORTED;
1074 }
1075 
1076 
1077 /* Function 19 - Not used on wire */
1078 unsigned long
1079 __stdcall
1080 NetrRenameMachineInDomain(void)
1081 {
1082     TRACE("NetrRenameMachineInDomain()\n");
1083     return ERROR_NOT_SUPPORTED;
1084 }
1085 
1086 
1087 /* Function 20 */
1088 unsigned long
1089 __stdcall
1090 NetrGetJoinInformation(
1091     WKSSVC_IMPERSONATE_HANDLE ServerName,
1092     wchar_t **NameBuffer,
1093     PNETSETUP_JOIN_STATUS BufferType)
1094 {
1095     TRACE("NetrGetJoinInformation(%p %p %p)\n",
1096           ServerName, NameBuffer, BufferType);
1097 
1098     if (NameBuffer == NULL)
1099         return ERROR_INVALID_PARAMETER;
1100 
1101     return NetpGetJoinInformation(NameBuffer,
1102                                   BufferType);
1103 }
1104 
1105 
1106 /* Function 21 - Not used on wire */
1107 unsigned long
1108 __stdcall
1109 NetrGetJoinableOUs(void)
1110 {
1111     TRACE("NetrGetJoinableOUs()\n");
1112     return ERROR_NOT_SUPPORTED;
1113 }
1114 
1115 
1116 /* Function 22 */
1117 unsigned long
1118 __stdcall
1119 NetrJoinDomain2(
1120     handle_t RpcBindingHandle,
1121     wchar_t *ServerName,
1122     wchar_t *DomainNameParam,
1123     wchar_t *MachineAccountOU,
1124     wchar_t *AccountName,
1125     PJOINPR_ENCRYPTED_USER_PASSWORD Password,
1126     unsigned long Options)
1127 {
1128     NET_API_STATUS status;
1129 
1130     FIXME("NetrJoinDomain2(%p %S %S %S %S %p 0x%lx)\n",
1131           RpcBindingHandle, ServerName, DomainNameParam, MachineAccountOU,
1132           AccountName, Password, Options);
1133 
1134     if (DomainNameParam == NULL)
1135         return ERROR_INVALID_PARAMETER;
1136 
1137     if (Options & NETSETUP_JOIN_DOMAIN)
1138     {
1139         FIXME("NetrJoinDomain2: NETSETUP_JOIN_DOMAIN is not supported yet!\n");
1140         status = ERROR_CALL_NOT_IMPLEMENTED;
1141     }
1142     else
1143     {
1144         status = NetpJoinWorkgroup(DomainNameParam);
1145     }
1146 
1147     return status;
1148 }
1149 
1150 
1151 /* Function 23 */
1152 unsigned long
1153 __stdcall
1154 NetrUnjoinDomain2(
1155     handle_t RpcBindingHandle,
1156     wchar_t *ServerName,
1157     wchar_t *AccountName,
1158     PJOINPR_ENCRYPTED_USER_PASSWORD Password,
1159     unsigned long Options)
1160 {
1161     UNIMPLEMENTED;
1162     return 0;
1163 }
1164 
1165 
1166 /* Function 24 */
1167 unsigned long
1168 __stdcall
1169 NetrRenameMachineInDomain2(
1170     handle_t RpcBindingHandle,
1171     wchar_t *ServerName,
1172     wchar_t *MachineName,
1173     wchar_t *AccountName,
1174     PJOINPR_ENCRYPTED_USER_PASSWORD Password,
1175     unsigned long Options)
1176 {
1177     UNIMPLEMENTED;
1178     return 0;
1179 }
1180 
1181 
1182 /* Function 25 */
1183 unsigned long
1184 __stdcall
1185 NetrValidateName2(
1186     handle_t RpcBindingHandle,
1187     wchar_t *ServerName,
1188     wchar_t *NameToValidate,
1189     wchar_t *AccountName,
1190     PJOINPR_ENCRYPTED_USER_PASSWORD Password,
1191     NETSETUP_NAME_TYPE NameType)
1192 {
1193     UNIMPLEMENTED;
1194     return 0;
1195 }
1196 
1197 
1198 /* Function 26 */
1199 unsigned long
1200 __stdcall
1201 NetrGetJoinableOUs2(
1202     handle_t RpcBindingHandle,
1203     wchar_t *ServerName,
1204     wchar_t *DomainNameParam,
1205     wchar_t *AccountName,
1206     PJOINPR_ENCRYPTED_USER_PASSWORD Password,
1207     unsigned long* OUCount,
1208     wchar_t ***OUs)
1209 {
1210     UNIMPLEMENTED;
1211     return 0;
1212 }
1213 
1214 
1215 /* Function 27 */
1216 unsigned long
1217 __stdcall
1218 NetrAddAlternateComputerName(
1219     handle_t RpcBindingHandle,
1220     wchar_t *ServerName,
1221     wchar_t *AlternateName,
1222     wchar_t *DomainAccount,
1223     PJOINPR_ENCRYPTED_USER_PASSWORD EncryptedPassword,
1224     unsigned long Reserved)
1225 {
1226     UNIMPLEMENTED;
1227     return 0;
1228 }
1229 
1230 
1231 /* Function 28 */
1232 unsigned long
1233 __stdcall
1234 NetrRemoveAlternateComputerName(
1235     handle_t RpcBindingHandle,
1236     wchar_t *ServerName,
1237     wchar_t *AlternateName,
1238     wchar_t *DomainAccount,
1239     PJOINPR_ENCRYPTED_USER_PASSWORD EncryptedPassword,
1240     unsigned long Reserved)
1241 {
1242     UNIMPLEMENTED;
1243     return 0;
1244 }
1245 
1246 
1247 /* Function 29 */
1248 unsigned long
1249 __stdcall
1250 NetrSetPrimaryComputerName(
1251     handle_t RpcBindingHandle,
1252     wchar_t *ServerName,
1253     wchar_t *PrimaryName,
1254     wchar_t *DomainAccount,
1255     PJOINPR_ENCRYPTED_USER_PASSWORD EncryptedPassword,
1256     unsigned long Reserved)
1257 {
1258     UNIMPLEMENTED;
1259     return 0;
1260 }
1261 
1262 
1263 /* Function 30 */
1264 unsigned long
1265 __stdcall
1266 NetrEnumerateComputerNames(
1267     WKSSVC_IMPERSONATE_HANDLE ServerName,
1268     NET_COMPUTER_NAME_TYPE NameType,
1269     unsigned long Reserved,
1270     PNET_COMPUTER_NAME_ARRAY *ComputerNames)
1271 {
1272     UNIMPLEMENTED;
1273     return 0;
1274 }
1275 
1276 /* EOF */
1277