1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS net command
4  * FILE:            base/applications/network/net/cmdUser.c
5  * PURPOSE:
6  *
7  * PROGRAMMERS:     Eric Kohl
8  *                  Curtis Wilson
9  */
10 
11 #include "net.h"
12 
13 static WCHAR szPasswordChars[] = L"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ@#$%_-+:";
14 
15 static
16 int
17 CompareInfo(const void *a, const void *b)
18 {
19     return _wcsicmp(((PUSER_INFO_0)a)->usri0_name,
20                     ((PUSER_INFO_0)b)->usri0_name);
21 }
22 
23 
24 static
25 NET_API_STATUS
26 EnumerateUsers(VOID)
27 {
28     PUSER_INFO_0 pBuffer = NULL;
29     PSERVER_INFO_100 pServer = NULL;
30     DWORD dwRead = 0, dwTotal = 0;
31     DWORD i;
32     DWORD ResumeHandle = 0;
33     NET_API_STATUS Status;
34 
35     Status = NetServerGetInfo(NULL,
36                               100,
37                               (LPBYTE*)&pServer);
38     if (Status != NERR_Success)
39         return Status;
40 
41     ConPuts(StdOut, L"\n");
42     ConResPrintf(StdOut, IDS_USER_ACCOUNTS, pServer->sv100_name);
43     ConPuts(StdOut, L"\n\n");
44     PrintPadding(L'-', 79);
45     ConPuts(StdOut, L"\n");
46 
47     NetApiBufferFree(pServer);
48 
49     do
50     {
51         Status = NetUserEnum(NULL,
52                              0,
53                              0,
54                              (LPBYTE*)&pBuffer,
55                              MAX_PREFERRED_LENGTH,
56                              &dwRead,
57                              &dwTotal,
58                              &ResumeHandle);
59         if ((Status != NERR_Success) && (Status != ERROR_MORE_DATA))
60             return Status;
61 
62         qsort(pBuffer,
63               dwRead,
64               sizeof(PUSER_INFO_0),
65               CompareInfo);
66 
67         for (i = 0; i < dwRead; i++)
68         {
69             if (pBuffer[i].usri0_name)
70                 ConPrintf(StdOut, L"%s\n", pBuffer[i].usri0_name);
71         }
72 
73         NetApiBufferFree(pBuffer);
74         pBuffer = NULL;
75     }
76     while (Status == ERROR_MORE_DATA);
77 
78     return NERR_Success;
79 }
80 
81 
82 static
83 VOID
84 PrintDateTime(DWORD dwSeconds)
85 {
86     LARGE_INTEGER Time;
87     FILETIME FileTime;
88     SYSTEMTIME SystemTime;
89     WCHAR DateBuffer[80];
90     WCHAR TimeBuffer[80];
91 
92     RtlSecondsSince1970ToTime(dwSeconds, &Time);
93     FileTime.dwLowDateTime = Time.u.LowPart;
94     FileTime.dwHighDateTime = Time.u.HighPart;
95     FileTimeToLocalFileTime(&FileTime, &FileTime);
96     FileTimeToSystemTime(&FileTime, &SystemTime);
97 
98     GetDateFormatW(LOCALE_USER_DEFAULT,
99                    DATE_SHORTDATE,
100                    &SystemTime,
101                    NULL,
102                    DateBuffer,
103                    80);
104 
105     GetTimeFormatW(LOCALE_USER_DEFAULT,
106                    TIME_NOSECONDS,
107                    &SystemTime,
108                    NULL,
109                    TimeBuffer,
110                    80);
111 
112     ConPrintf(StdOut, L"%s %s", DateBuffer, TimeBuffer);
113 }
114 
115 
116 static
117 DWORD
118 GetTimeInSeconds(VOID)
119 {
120     LARGE_INTEGER Time;
121     FILETIME FileTime;
122     DWORD dwSeconds;
123 
124     GetSystemTimeAsFileTime(&FileTime);
125     Time.u.LowPart = FileTime.dwLowDateTime;
126     Time.u.HighPart = FileTime.dwHighDateTime;
127     RtlTimeToSecondsSince1970(&Time, &dwSeconds);
128 
129     return dwSeconds;
130 }
131 
132 
133 static
134 NET_API_STATUS
135 DisplayUser(LPWSTR lpUserName)
136 {
137     PUSER_MODALS_INFO_0 pUserModals = NULL;
138     PUSER_INFO_4 pUserInfo = NULL;
139     PLOCALGROUP_USERS_INFO_0 pLocalGroupInfo = NULL;
140     PGROUP_USERS_INFO_0 pGroupInfo = NULL;
141     DWORD dwLocalGroupRead, dwLocalGroupTotal;
142     DWORD dwGroupRead, dwGroupTotal;
143     DWORD dwLastSet;
144     DWORD i;
145     INT nPaddedLength = 29;
146     NET_API_STATUS Status;
147 
148     /* Modify the user */
149     Status = NetUserGetInfo(NULL,
150                             lpUserName,
151                             4,
152                             (LPBYTE*)&pUserInfo);
153     if (Status != NERR_Success)
154         return Status;
155 
156     Status = NetUserModalsGet(NULL,
157                               0,
158                               (LPBYTE*)&pUserModals);
159     if (Status != NERR_Success)
160         goto done;
161 
162     Status = NetUserGetLocalGroups(NULL,
163                                    lpUserName,
164                                    0,
165                                    0,
166                                    (LPBYTE*)&pLocalGroupInfo,
167                                    MAX_PREFERRED_LENGTH,
168                                    &dwLocalGroupRead,
169                                    &dwLocalGroupTotal);
170     if (Status != NERR_Success)
171         goto done;
172 
173     Status = NetUserGetGroups(NULL,
174                               lpUserName,
175                               0,
176                               (LPBYTE*)&pGroupInfo,
177                               MAX_PREFERRED_LENGTH,
178                               &dwGroupRead,
179                               &dwGroupTotal);
180     if (Status != NERR_Success)
181         goto done;
182 
183     PrintPaddedResourceString(IDS_USER_NAME, nPaddedLength);
184     ConPrintf(StdOut, L"%s\n", pUserInfo->usri4_name);
185 
186     PrintPaddedResourceString(IDS_USER_FULL_NAME, nPaddedLength);
187     ConPrintf(StdOut, L"%s\n", pUserInfo->usri4_full_name);
188 
189     PrintPaddedResourceString(IDS_USER_COMMENT, nPaddedLength);
190     ConPrintf(StdOut, L"%s\n", pUserInfo->usri4_comment);
191 
192     PrintPaddedResourceString(IDS_USER_USER_COMMENT, nPaddedLength);
193     ConPrintf(StdOut, L"%s\n", pUserInfo->usri4_usr_comment);
194 
195     PrintPaddedResourceString(IDS_USER_COUNTRY_CODE, nPaddedLength);
196     ConPrintf(StdOut, L"%03ld ()\n", pUserInfo->usri4_country_code);
197 
198     PrintPaddedResourceString(IDS_USER_ACCOUNT_ACTIVE, nPaddedLength);
199     if (pUserInfo->usri4_flags & UF_ACCOUNTDISABLE)
200         ConResPuts(StdOut, IDS_GENERIC_NO);
201     else if (pUserInfo->usri4_flags & UF_LOCKOUT)
202         ConResPuts(StdOut, IDS_GENERIC_LOCKED);
203     else
204         ConResPuts(StdOut, IDS_GENERIC_YES);
205     ConPuts(StdOut, L"\n");
206 
207     PrintPaddedResourceString(IDS_USER_ACCOUNT_EXPIRES, nPaddedLength);
208     if (pUserInfo->usri4_acct_expires == TIMEQ_FOREVER)
209         ConResPuts(StdOut, IDS_GENERIC_NEVER);
210     else
211         PrintDateTime(pUserInfo->usri4_acct_expires);
212     ConPuts(StdOut, L"\n\n");
213 
214     PrintPaddedResourceString(IDS_USER_PW_LAST_SET, nPaddedLength);
215     dwLastSet = GetTimeInSeconds() - pUserInfo->usri4_password_age;
216     PrintDateTime(dwLastSet);
217     ConPuts(StdOut, L"\n");
218 
219     PrintPaddedResourceString(IDS_USER_PW_EXPIRES, nPaddedLength);
220     if ((pUserInfo->usri4_flags & UF_DONT_EXPIRE_PASSWD) || pUserModals->usrmod0_max_passwd_age == TIMEQ_FOREVER)
221         ConResPuts(StdOut, IDS_GENERIC_NEVER);
222     else
223         PrintDateTime(dwLastSet + pUserModals->usrmod0_max_passwd_age);
224     ConPuts(StdOut, L"\n");
225 
226     PrintPaddedResourceString(IDS_USER_PW_CHANGEABLE, nPaddedLength);
227     PrintDateTime(dwLastSet + pUserModals->usrmod0_min_passwd_age);
228     ConPuts(StdOut, L"\n");
229 
230     PrintPaddedResourceString(IDS_USER_PW_REQUIRED, nPaddedLength);
231     ConResPuts(StdOut, (pUserInfo->usri4_flags & UF_PASSWD_NOTREQD) ? IDS_GENERIC_NO : IDS_GENERIC_YES);
232     ConPuts(StdOut, L"\n");
233 
234     PrintPaddedResourceString(IDS_USER_CHANGE_PW, nPaddedLength);
235     ConResPuts(StdOut, (pUserInfo->usri4_flags & UF_PASSWD_CANT_CHANGE) ? IDS_GENERIC_NO : IDS_GENERIC_YES);
236     ConPuts(StdOut, L"\n\n");
237 
238     PrintPaddedResourceString(IDS_USER_WORKSTATIONS, nPaddedLength);
239     if (pUserInfo->usri4_workstations == NULL || wcslen(pUserInfo->usri4_workstations) == 0)
240         ConResPuts(StdOut, IDS_GENERIC_ALL);
241     else
242         ConPrintf(StdOut, L"%s", pUserInfo->usri4_workstations);
243     ConPuts(StdOut, L"\n");
244 
245     PrintPaddedResourceString(IDS_USER_LOGON_SCRIPT, nPaddedLength);
246     ConPrintf(StdOut, L"%s\n", pUserInfo->usri4_script_path);
247 
248     PrintPaddedResourceString(IDS_USER_PROFILE, nPaddedLength);
249     ConPrintf(StdOut, L"%s\n", pUserInfo->usri4_profile);
250 
251     PrintPaddedResourceString(IDS_USER_HOME_DIR, nPaddedLength);
252     ConPrintf(StdOut, L"%s\n", pUserInfo->usri4_home_dir);
253 
254     PrintPaddedResourceString(IDS_USER_LAST_LOGON, nPaddedLength);
255     if (pUserInfo->usri4_last_logon == 0)
256         ConResPuts(StdOut, IDS_GENERIC_NEVER);
257     else
258         PrintDateTime(pUserInfo->usri4_last_logon);
259     ConPuts(StdOut, L"\n\n");
260 
261     PrintPaddedResourceString(IDS_USER_LOGON_HOURS, nPaddedLength);
262     if (pUserInfo->usri4_logon_hours == NULL)
263         ConResPuts(StdOut, IDS_GENERIC_ALL);
264     ConPuts(StdOut, L"\n\n");
265 
266     ConPuts(StdOut, L"\n");
267     PrintPaddedResourceString(IDS_USER_LOCAL_GROUPS, nPaddedLength);
268     if (dwLocalGroupTotal != 0 && pLocalGroupInfo != NULL)
269     {
270         for (i = 0; i < dwLocalGroupTotal; i++)
271         {
272             if (i != 0)
273                 PrintPadding(L' ', nPaddedLength);
274             ConPrintf(StdOut, L"*%s\n", pLocalGroupInfo[i].lgrui0_name);
275         }
276     }
277     else
278     {
279         ConPuts(StdOut, L"\n");
280     }
281 
282     PrintPaddedResourceString(IDS_USER_GLOBAL_GROUPS, nPaddedLength);
283     if (dwGroupTotal != 0 && pGroupInfo != NULL)
284     {
285         for (i = 0; i < dwGroupTotal; i++)
286         {
287             if (i != 0)
288                 PrintPadding(L' ', nPaddedLength);
289             ConPrintf(StdOut, L"*%s\n", pGroupInfo[i].grui0_name);
290         }
291     }
292     else
293     {
294         ConPuts(StdOut, L"\n");
295     }
296 
297 done:
298     if (pGroupInfo != NULL)
299         NetApiBufferFree(pGroupInfo);
300 
301     if (pLocalGroupInfo != NULL)
302         NetApiBufferFree(pLocalGroupInfo);
303 
304     if (pUserModals != NULL)
305         NetApiBufferFree(pUserModals);
306 
307     if (pUserInfo != NULL)
308         NetApiBufferFree(pUserInfo);
309 
310     return NERR_Success;
311 }
312 
313 
314 static
315 VOID
316 ReadPassword(
317     LPWSTR *lpPassword,
318     LPBOOL lpAllocated)
319 {
320     WCHAR szPassword1[PWLEN + 1];
321     WCHAR szPassword2[PWLEN + 1];
322     LPWSTR ptr;
323 
324     *lpAllocated = FALSE;
325 
326     while (TRUE)
327     {
328         ConResPuts(StdOut, IDS_USER_ENTER_PASSWORD1);
329         ReadFromConsole(szPassword1, PWLEN + 1, FALSE);
330         ConPuts(StdOut, L"\n");
331 
332         ConResPuts(StdOut, IDS_USER_ENTER_PASSWORD2);
333         ReadFromConsole(szPassword2, PWLEN + 1, FALSE);
334         ConPuts(StdOut, L"\n");
335 
336         if (wcslen(szPassword1) == wcslen(szPassword2) &&
337             wcscmp(szPassword1, szPassword2) == 0)
338         {
339             ptr = HeapAlloc(GetProcessHeap(),
340                             0,
341                             (wcslen(szPassword1) + 1) * sizeof(WCHAR));
342             if (ptr != NULL)
343             {
344                 wcscpy(ptr, szPassword1);
345                 *lpPassword = ptr;
346                 *lpAllocated = TRUE;
347                 return;
348             }
349         }
350         else
351         {
352             ConPuts(StdOut, L"\n");
353             ConResPuts(StdOut, IDS_USER_NO_PASSWORD_MATCH);
354             ConPuts(StdOut, L"\n");
355             *lpPassword = NULL;
356         }
357     }
358 }
359 
360 
361 static
362 VOID
363 GenerateRandomPassword(
364     LPWSTR *lpPassword,
365     LPBOOL lpAllocated)
366 {
367     LPWSTR pPassword = NULL;
368     INT nCharsLen, i, nLength = 8;
369 
370     srand(GetTickCount());
371 
372     pPassword = HeapAlloc(GetProcessHeap(),
373                           HEAP_ZERO_MEMORY,
374                           (nLength + 1) * sizeof(WCHAR));
375     if (pPassword == NULL)
376         return;
377 
378     nCharsLen = wcslen(szPasswordChars);
379 
380     for (i = 0; i < nLength; i++)
381     {
382         pPassword[i] = szPasswordChars[rand() % nCharsLen];
383     }
384 
385     *lpPassword = pPassword;
386     *lpAllocated = TRUE;
387 }
388 
389 
390 static
391 NET_API_STATUS
392 BuildWorkstationsList(
393     _Out_ PWSTR *pWorkstationsList,
394     _In_ PWSTR pRaw)
395 {
396     BOOL isLastSep, isSep;
397     INT i, j;
398     WCHAR c;
399     INT nLength = 0;
400     INT nArgs = 0;
401     INT nRawLength;
402     PWSTR pList;
403 
404     /* Check for invalid characters in the raw string */
405     if (wcspbrk(pRaw, L"/[]=?\\+:.") != NULL)
406         return 3952;
407 
408     /* Count the number of workstations in the list and
409      * the required buffer size */
410     isLastSep = FALSE;
411     isSep = FALSE;
412     nRawLength = wcslen(pRaw);
413     for (i = 0; i < nRawLength; i++)
414     {
415         c = pRaw[i];
416         if (c == L',' || c == L';')
417             isSep = TRUE;
418 
419         if (isSep == TRUE)
420         {
421             if ((isLastSep == FALSE) && (i != 0) && (i != nRawLength - 1))
422                 nLength++;
423         }
424         else
425         {
426             nLength++;
427 
428             if (isLastSep == TRUE || (isLastSep == FALSE && i == 0))
429                 nArgs++;
430         }
431 
432         isLastSep = isSep;
433         isSep = FALSE;
434     }
435 
436     nLength++;
437 
438     /* Leave, if there are no workstations in the list */
439     if (nArgs == 0)
440     {
441         pWorkstationsList = NULL;
442         return NERR_Success;
443     }
444 
445     /* Fail if there are more than eight workstations in the list */
446     if (nArgs > 8)
447         return 3951;
448 
449     /* Allocate the buffer for the clean workstation list */
450     pList = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, nLength * sizeof(WCHAR));
451     if (pList == NULL)
452         return ERROR_NOT_ENOUGH_MEMORY;
453 
454     /* Build the clean workstation list */
455     isLastSep = FALSE;
456     isSep = FALSE;
457     nRawLength = wcslen(pRaw);
458     for (i = 0, j = 0; i < nRawLength; i++)
459     {
460         c = pRaw[i];
461         if (c == L',' || c == L';')
462             isSep = TRUE;
463 
464         if (isSep == TRUE)
465         {
466             if ((isLastSep == FALSE) && (i != 0) && (i != nRawLength - 1))
467             {
468                 pList[j] = L',';
469                 j++;
470             }
471         }
472         else
473         {
474             pList[j] = c;
475             j++;
476 
477             if (isLastSep == TRUE || (isLastSep == FALSE && i == 0))
478                 nArgs++;
479         }
480 
481         isLastSep = isSep;
482         isSep = FALSE;
483     }
484 
485     *pWorkstationsList = pList;
486 
487     return NERR_Success;
488 }
489 
490 
491 INT
492 cmdUser(
493     INT argc,
494     WCHAR **argv)
495 {
496     INT i, j;
497     INT result = 0;
498     BOOL bAdd = FALSE;
499     BOOL bDelete = FALSE;
500 #if 0
501     BOOL bDomain = FALSE;
502 #endif
503     BOOL bRandomPassword = FALSE;
504     LPWSTR lpUserName = NULL;
505     LPWSTR lpPassword = NULL;
506     PUSER_INFO_4 pUserInfo = NULL;
507     USER_INFO_4 UserInfo;
508     LPWSTR pWorkstations = NULL;
509     LPWSTR p;
510     LPWSTR endptr;
511     DWORD value;
512     BOOL bPasswordAllocated = FALSE;
513     NET_API_STATUS Status;
514 
515     if (argc == 2)
516     {
517         Status = EnumerateUsers();
518         ConPrintf(StdOut, L"Status: %lu\n", Status);
519         return 0;
520     }
521     else if (argc == 3)
522     {
523         Status = DisplayUser(argv[2]);
524         ConPrintf(StdOut, L"Status: %lu\n", Status);
525         return 0;
526     }
527 
528     i = 2;
529     if (argv[i][0] != L'/')
530     {
531         lpUserName = argv[i];
532 //        ConPrintf(StdOut, L"User: %s\n", lpUserName);
533         i++;
534     }
535 
536     if (argv[i][0] != L'/')
537     {
538         lpPassword = argv[i];
539 //        ConPrintf(StdOut, L"Password: %s\n", lpPassword);
540         i++;
541     }
542 
543     for (j = i; j < argc; j++)
544     {
545         if (_wcsicmp(argv[j], L"/help") == 0)
546         {
547             ConResPuts(StdOut, IDS_USER_HELP);
548             return 0;
549         }
550         else if (_wcsicmp(argv[j], L"/add") == 0)
551         {
552             bAdd = TRUE;
553         }
554         else if (_wcsicmp(argv[j], L"/delete") == 0)
555         {
556             bDelete = TRUE;
557         }
558         else if (_wcsicmp(argv[j], L"/domain") == 0)
559         {
560             ConResPrintf(StdErr, IDS_ERROR_OPTION_NOT_SUPPORTED, L"/DOMAIN");
561 #if 0
562             bDomain = TRUE;
563 #endif
564         }
565         else if (_wcsicmp(argv[j], L"/random") == 0)
566         {
567             bRandomPassword = TRUE;
568             GenerateRandomPassword(&lpPassword,
569                                    &bPasswordAllocated);
570         }
571     }
572 
573     if (bAdd && bDelete)
574     {
575         result = 1;
576         goto done;
577     }
578 
579     /* Interactive password input */
580     if (lpPassword != NULL && wcscmp(lpPassword, L"*") == 0)
581     {
582         ReadPassword(&lpPassword,
583                      &bPasswordAllocated);
584     }
585 
586     if (!bAdd && !bDelete)
587     {
588         /* Modify the user */
589         Status = NetUserGetInfo(NULL,
590                                 lpUserName,
591                                 4,
592                                 (LPBYTE*)&pUserInfo);
593         if (Status != NERR_Success)
594         {
595             ConPrintf(StdOut, L"Status: %lu\n", Status);
596             result = 1;
597             goto done;
598         }
599     }
600     else if (bAdd && !bDelete)
601     {
602         /* Add the user */
603         ZeroMemory(&UserInfo, sizeof(USER_INFO_4));
604 
605         UserInfo.usri4_name = lpUserName;
606         UserInfo.usri4_password = lpPassword;
607         UserInfo.usri4_flags = UF_SCRIPT | UF_NORMAL_ACCOUNT;
608 
609         pUserInfo = &UserInfo;
610     }
611 
612     for (j = i; j < argc; j++)
613     {
614         if (_wcsnicmp(argv[j], L"/active:", 8) == 0)
615         {
616             p = &argv[i][8];
617             if (_wcsicmp(p, L"yes") == 0)
618             {
619                 pUserInfo->usri4_flags &= ~UF_ACCOUNTDISABLE;
620             }
621             else if (_wcsicmp(p, L"no") == 0)
622             {
623                 pUserInfo->usri4_flags |= UF_ACCOUNTDISABLE;
624             }
625             else
626             {
627                 ConResPrintf(StdErr, IDS_ERROR_INVALID_OPTION_VALUE, L"/ACTIVE");
628                 result = 1;
629                 goto done;
630             }
631         }
632         else if (_wcsnicmp(argv[j], L"/comment:", 9) == 0)
633         {
634             pUserInfo->usri4_comment = &argv[j][9];
635         }
636         else if (_wcsnicmp(argv[j], L"/countrycode:", 13) == 0)
637         {
638             p = &argv[i][13];
639             value = wcstoul(p, &endptr, 10);
640             if (*endptr != 0)
641             {
642                 ConResPrintf(StdErr, IDS_ERROR_INVALID_OPTION_VALUE, L"/COUNTRYCODE");
643                 result = 1;
644                 goto done;
645             }
646 
647             /* FIXME: verify the country code */
648 
649             pUserInfo->usri4_country_code = value;
650         }
651         else if (_wcsnicmp(argv[j], L"/expires:", 9) == 0)
652         {
653             p = &argv[i][9];
654             if (_wcsicmp(p, L"never") == 0)
655             {
656                 pUserInfo->usri4_acct_expires = TIMEQ_FOREVER;
657             }
658             else
659             {
660                 /* FIXME: Parse the date */
661                 ConResPrintf(StdErr, IDS_ERROR_OPTION_NOT_SUPPORTED, L"/EXPIRES");
662             }
663         }
664         else if (_wcsnicmp(argv[j], L"/fullname:", 10) == 0)
665         {
666             pUserInfo->usri4_full_name = &argv[j][10];
667         }
668         else if (_wcsnicmp(argv[j], L"/homedir:", 9) == 0)
669         {
670             pUserInfo->usri4_home_dir = &argv[j][9];
671         }
672         else if (_wcsnicmp(argv[j], L"/passwordchg:", 13) == 0)
673         {
674             p = &argv[i][13];
675             if (_wcsicmp(p, L"yes") == 0)
676             {
677                 pUserInfo->usri4_flags &= ~UF_PASSWD_CANT_CHANGE;
678             }
679             else if (_wcsicmp(p, L"no") == 0)
680             {
681                 pUserInfo->usri4_flags |= UF_PASSWD_CANT_CHANGE;
682             }
683             else
684             {
685                 ConResPrintf(StdErr, IDS_ERROR_INVALID_OPTION_VALUE, L"/PASSWORDCHG");
686                 result = 1;
687                 goto done;
688             }
689         }
690         else if (_wcsnicmp(argv[j], L"/passwordreq:", 13) == 0)
691         {
692             p = &argv[i][13];
693             if (_wcsicmp(p, L"yes") == 0)
694             {
695                 pUserInfo->usri4_flags &= ~UF_PASSWD_NOTREQD;
696             }
697             else if (_wcsicmp(p, L"no") == 0)
698             {
699                 pUserInfo->usri4_flags |= UF_PASSWD_NOTREQD;
700             }
701             else
702             {
703                 ConResPrintf(StdErr, IDS_ERROR_INVALID_OPTION_VALUE, L"/PASSWORDREQ");
704                 result = 1;
705                 goto done;
706             }
707         }
708         else if (_wcsnicmp(argv[j], L"/profilepath:", 13) == 0)
709         {
710             pUserInfo->usri4_profile = &argv[j][13];
711         }
712         else if (_wcsnicmp(argv[j], L"/scriptpath:", 12) == 0)
713         {
714             pUserInfo->usri4_script_path = &argv[j][12];
715         }
716         else if (_wcsnicmp(argv[j], L"/times:", 7) == 0)
717         {
718             /* FIXME */
719             ConResPrintf(StdErr, IDS_ERROR_OPTION_NOT_SUPPORTED, L"/TIMES");
720         }
721         else if (_wcsnicmp(argv[j], L"/usercomment:", 13) == 0)
722         {
723             pUserInfo->usri4_usr_comment = &argv[j][13];
724         }
725         else if (_wcsnicmp(argv[j], L"/workstations:", 14) == 0)
726         {
727             p = &argv[i][14];
728             if (wcscmp(p, L"*") == 0 || wcscmp(p, L"") == 0)
729             {
730                 pUserInfo->usri4_workstations = NULL;
731             }
732             else
733             {
734                 Status = BuildWorkstationsList(&pWorkstations, p);
735                 if (Status == NERR_Success)
736                 {
737                     pUserInfo->usri4_workstations = pWorkstations;
738                 }
739                 else
740                 {
741                     ConPrintf(StdOut, L"Status %lu\n\n", Status);
742                     result = 1;
743                     goto done;
744                 }
745             }
746         }
747     }
748 
749     if (!bAdd && !bDelete)
750     {
751         /* Modify the user */
752         Status = NetUserSetInfo(NULL,
753                                 lpUserName,
754                                 4,
755                                 (LPBYTE)pUserInfo,
756                                 NULL);
757         ConPrintf(StdOut, L"Status: %lu\n", Status);
758     }
759     else if (bAdd && !bDelete)
760     {
761         /* Add the user */
762         Status = NetUserAdd(NULL,
763                             4,
764                             (LPBYTE)pUserInfo,
765                             NULL);
766         ConPrintf(StdOut, L"Status: %lu\n", Status);
767     }
768     else if (!bAdd && bDelete)
769     {
770         /* Delete the user */
771         Status = NetUserDel(NULL,
772                             lpUserName);
773         ConPrintf(StdOut, L"Status: %lu\n", Status);
774     }
775 
776     if (Status == NERR_Success &&
777         lpPassword != NULL &&
778         bRandomPassword == TRUE)
779     {
780         ConPrintf(StdOut, L"The password for %s is: %s\n", lpUserName, lpPassword);
781     }
782 
783 done:
784     if (pWorkstations != NULL)
785         HeapFree(GetProcessHeap(), 0, pWorkstations);
786 
787     if ((bPasswordAllocated == TRUE) && (lpPassword != NULL))
788         HeapFree(GetProcessHeap(), 0, lpPassword);
789 
790     if (!bAdd && !bDelete && pUserInfo != NULL)
791         NetApiBufferFree(pUserInfo);
792 
793     if (result != 0)
794     {
795         ConResPuts(StdOut, IDS_GENERIC_SYNTAX);
796         ConResPuts(StdOut, IDS_USER_SYNTAX);
797     }
798 
799     return result;
800 }
801 
802 /* EOF */
803