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 typedef struct _COUNTY_TABLE
14 {
15     DWORD dwCountryCode;
16     DWORD dwMessageId;
17 } COUNTRY_TABLE, *PCOUNTRY_TABLE;
18 
19 
20 static WCHAR szPasswordChars[] = L"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ@#$%_-+:";
21 static COUNTRY_TABLE CountryTable[] =
22 { {  0, 5080},   // System Default
23   {  1, 5081},   // United States
24   {  2, 5082},   // Canada (French)
25   {  3, 5083},   // Latin America
26   { 31, 5084},   // Netherlands
27   { 32, 5085},   // Belgium
28   { 33, 5086},   // France
29   { 34, 5090},   // Spain
30   { 39, 5087},   // Italy
31   { 41, 5088},   // Switzerland
32   { 44, 5089},   // United Kingdom
33   { 45, 5091},   // Denmark
34   { 46, 5092},   // Sweden
35   { 47, 5093},   // Norway
36   { 49, 5094},   // Germany
37   { 61, 5095},   // Australia
38   { 81, 5096},   // Japan
39   { 82, 5097},   // Korea
40   { 86, 5098},   // China (PRC)
41   { 88, 5099},   // Taiwan
42   { 99, 5100},   // Asia
43   {351, 5101},   // Portugal
44   {358, 5102},   // Finland
45   {785, 5103},   // Arabic
46   {972, 5104} }; // Hebrew
47 
48 
49 static
50 int
51 CompareInfo(const void *a, const void *b)
52 {
53     return _wcsicmp(((PUSER_INFO_0)a)->usri0_name,
54                     ((PUSER_INFO_0)b)->usri0_name);
55 }
56 
57 
58 static
59 NET_API_STATUS
60 EnumerateUsers(VOID)
61 {
62     PUSER_INFO_0 pBuffer = NULL;
63     PSERVER_INFO_100 pServer = NULL;
64     DWORD dwRead = 0, dwTotal = 0;
65     DWORD i;
66     DWORD ResumeHandle = 0;
67     NET_API_STATUS Status;
68 
69     Status = NetServerGetInfo(NULL,
70                               100,
71                               (LPBYTE*)&pServer);
72     if (Status != NERR_Success)
73         return Status;
74 
75     ConPuts(StdOut, L"\n");
76     PrintMessageStringV(4410, pServer->sv100_name);
77     ConPuts(StdOut, L"\n");
78     PrintPadding(L'-', 79);
79     ConPuts(StdOut, L"\n");
80 
81     NetApiBufferFree(pServer);
82 
83     do
84     {
85         Status = NetUserEnum(NULL,
86                              0,
87                              0,
88                              (LPBYTE*)&pBuffer,
89                              MAX_PREFERRED_LENGTH,
90                              &dwRead,
91                              &dwTotal,
92                              &ResumeHandle);
93         if ((Status != NERR_Success) && (Status != ERROR_MORE_DATA))
94             return Status;
95 
96         qsort(pBuffer,
97               dwRead,
98               sizeof(PUSER_INFO_0),
99               CompareInfo);
100 
101         for (i = 0; i < dwRead; i++)
102         {
103             if (pBuffer[i].usri0_name)
104                 ConPrintf(StdOut, L"%s\n", pBuffer[i].usri0_name);
105         }
106 
107         NetApiBufferFree(pBuffer);
108         pBuffer = NULL;
109     }
110     while (Status == ERROR_MORE_DATA);
111 
112     return NERR_Success;
113 }
114 
115 
116 static
117 VOID
118 PrintDateTime(DWORD dwSeconds)
119 {
120     LARGE_INTEGER Time;
121     FILETIME FileTime;
122     SYSTEMTIME SystemTime;
123     WCHAR DateBuffer[80];
124     WCHAR TimeBuffer[80];
125 
126     RtlSecondsSince1970ToTime(dwSeconds, &Time);
127     FileTime.dwLowDateTime = Time.u.LowPart;
128     FileTime.dwHighDateTime = Time.u.HighPart;
129     FileTimeToLocalFileTime(&FileTime, &FileTime);
130     FileTimeToSystemTime(&FileTime, &SystemTime);
131 
132     GetDateFormatW(LOCALE_USER_DEFAULT,
133                    DATE_SHORTDATE,
134                    &SystemTime,
135                    NULL,
136                    DateBuffer,
137                    80);
138 
139     GetTimeFormatW(LOCALE_USER_DEFAULT,
140                    TIME_NOSECONDS,
141                    &SystemTime,
142                    NULL,
143                    TimeBuffer,
144                    80);
145 
146     ConPrintf(StdOut, L"%s %s", DateBuffer, TimeBuffer);
147 }
148 
149 
150 static
151 DWORD
152 GetTimeInSeconds(VOID)
153 {
154     LARGE_INTEGER Time;
155     FILETIME FileTime;
156     DWORD dwSeconds;
157 
158     GetSystemTimeAsFileTime(&FileTime);
159     Time.u.LowPart = FileTime.dwLowDateTime;
160     Time.u.HighPart = FileTime.dwHighDateTime;
161     RtlTimeToSecondsSince1970(&Time, &dwSeconds);
162 
163     return dwSeconds;
164 }
165 
166 
167 static
168 BOOL
169 GetCountryFromCountryCode(
170     _In_ DWORD dwCountryCode,
171     _In_ DWORD dwCountryLength,
172     _Out_ PWSTR szCountryBuffer)
173 {
174     DWORD i;
175 
176     for (i = 0; i < ARRAYSIZE(CountryTable); i++)
177     {
178         if (CountryTable[i].dwCountryCode == dwCountryCode)
179         {
180             if (szCountryBuffer != NULL && dwCountryLength > 0)
181             {
182                 FormatMessageW(FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
183                                hModuleNetMsg,
184                                CountryTable[i].dwMessageId,
185                                LANG_USER_DEFAULT,
186                                szCountryBuffer,
187                                dwCountryLength,
188                                NULL);
189             }
190 
191             return TRUE;
192         }
193     }
194 
195     return FALSE;
196 }
197 
198 
199 static
200 NET_API_STATUS
201 DisplayUser(LPWSTR lpUserName)
202 {
203     PUSER_MODALS_INFO_0 pUserModals = NULL;
204     PUSER_INFO_4 pUserInfo = NULL;
205     PLOCALGROUP_USERS_INFO_0 pLocalGroupInfo = NULL;
206     PGROUP_USERS_INFO_0 pGroupInfo = NULL;
207     DWORD dwLocalGroupRead, dwLocalGroupTotal;
208     DWORD dwGroupRead, dwGroupTotal;
209     DWORD dwLastSet;
210     DWORD i;
211     WCHAR szCountry[40];
212     INT nPaddedLength = 36;
213     NET_API_STATUS Status;
214 
215     /* Modify the user */
216     Status = NetUserGetInfo(NULL,
217                             lpUserName,
218                             4,
219                             (LPBYTE*)&pUserInfo);
220     if (Status != NERR_Success)
221         return Status;
222 
223     Status = NetUserModalsGet(NULL,
224                               0,
225                               (LPBYTE*)&pUserModals);
226     if (Status != NERR_Success)
227         goto done;
228 
229     Status = NetUserGetLocalGroups(NULL,
230                                    lpUserName,
231                                    0,
232                                    0,
233                                    (LPBYTE*)&pLocalGroupInfo,
234                                    MAX_PREFERRED_LENGTH,
235                                    &dwLocalGroupRead,
236                                    &dwLocalGroupTotal);
237     if (Status != NERR_Success)
238         goto done;
239 
240     Status = NetUserGetGroups(NULL,
241                               lpUserName,
242                               0,
243                               (LPBYTE*)&pGroupInfo,
244                               MAX_PREFERRED_LENGTH,
245                               &dwGroupRead,
246                               &dwGroupTotal);
247     if (Status != NERR_Success)
248         goto done;
249 
250     PrintPaddedMessageString(4411, nPaddedLength);
251     ConPrintf(StdOut, L"%s\n", pUserInfo->usri4_name);
252 
253     PrintPaddedMessageString(4412, nPaddedLength);
254     ConPrintf(StdOut, L"%s\n", pUserInfo->usri4_full_name);
255 
256     PrintPaddedMessageString(4413, nPaddedLength);
257     ConPrintf(StdOut, L"%s\n", pUserInfo->usri4_comment);
258 
259     PrintPaddedMessageString(4414, nPaddedLength);
260     ConPrintf(StdOut, L"%s\n", pUserInfo->usri4_usr_comment);
261 
262     PrintPaddedMessageString(4416, nPaddedLength);
263     GetCountryFromCountryCode(pUserInfo->usri4_country_code,
264                               ARRAYSIZE(szCountry), szCountry);
265     ConPrintf(StdOut, L"%03ld (%s)\n", pUserInfo->usri4_country_code, szCountry);
266 
267     PrintPaddedMessageString(4419, nPaddedLength);
268     if (pUserInfo->usri4_flags & UF_ACCOUNTDISABLE)
269         ConResPuts(StdOut, IDS_GENERIC_NO);
270     else if (pUserInfo->usri4_flags & UF_LOCKOUT)
271         PrintMessageString(4440);
272     else
273         ConResPuts(StdOut, IDS_GENERIC_YES);
274     ConPuts(StdOut, L"\n");
275 
276     PrintPaddedMessageString(4420, nPaddedLength);
277     if (pUserInfo->usri4_acct_expires == TIMEQ_FOREVER)
278         ConResPuts(StdOut, IDS_GENERIC_NEVER);
279     else
280         PrintDateTime(pUserInfo->usri4_acct_expires);
281     ConPuts(StdOut, L"\n\n");
282 
283     PrintPaddedMessageString(4421, nPaddedLength);
284     dwLastSet = GetTimeInSeconds() - pUserInfo->usri4_password_age;
285     PrintDateTime(dwLastSet);
286     ConPuts(StdOut, L"\n");
287 
288     PrintPaddedMessageString(4422, nPaddedLength);
289     if ((pUserInfo->usri4_flags & UF_DONT_EXPIRE_PASSWD) || pUserModals->usrmod0_max_passwd_age == TIMEQ_FOREVER)
290         ConResPuts(StdOut, IDS_GENERIC_NEVER);
291     else
292         PrintDateTime(dwLastSet + pUserModals->usrmod0_max_passwd_age);
293     ConPuts(StdOut, L"\n");
294 
295     PrintPaddedMessageString(4423, nPaddedLength);
296     PrintDateTime(dwLastSet + pUserModals->usrmod0_min_passwd_age);
297     ConPuts(StdOut, L"\n");
298 
299     PrintPaddedMessageString(4437, nPaddedLength);
300     ConResPuts(StdOut, (pUserInfo->usri4_flags & UF_PASSWD_NOTREQD) ? IDS_GENERIC_NO : IDS_GENERIC_YES);
301     ConPuts(StdOut, L"\n");
302 
303     PrintPaddedMessageString(4438, nPaddedLength);
304     ConResPuts(StdOut, (pUserInfo->usri4_flags & UF_PASSWD_CANT_CHANGE) ? IDS_GENERIC_NO : IDS_GENERIC_YES);
305     ConPuts(StdOut, L"\n\n");
306 
307     PrintPaddedMessageString(4424, nPaddedLength);
308     if (pUserInfo->usri4_workstations == NULL || wcslen(pUserInfo->usri4_workstations) == 0)
309         ConResPuts(StdOut, IDS_GENERIC_ALL);
310     else
311         ConPrintf(StdOut, L"%s", pUserInfo->usri4_workstations);
312     ConPuts(StdOut, L"\n");
313 
314     PrintPaddedMessageString(4429, nPaddedLength);
315     ConPrintf(StdOut, L"%s\n", pUserInfo->usri4_script_path);
316 
317     PrintPaddedMessageString(4439, nPaddedLength);
318     ConPrintf(StdOut, L"%s\n", pUserInfo->usri4_profile);
319 
320     PrintPaddedMessageString(4436, nPaddedLength);
321     ConPrintf(StdOut, L"%s\n", pUserInfo->usri4_home_dir);
322 
323     PrintPaddedMessageString(4430, nPaddedLength);
324     if (pUserInfo->usri4_last_logon == 0)
325         ConResPuts(StdOut, IDS_GENERIC_NEVER);
326     else
327         PrintDateTime(pUserInfo->usri4_last_logon);
328     ConPuts(StdOut, L"\n\n");
329 
330     PrintPaddedMessageString(4432, nPaddedLength);
331     if (pUserInfo->usri4_logon_hours == NULL)
332         ConResPuts(StdOut, IDS_GENERIC_ALL);
333     ConPuts(StdOut, L"\n\n");
334 
335     ConPuts(StdOut, L"\n");
336     PrintPaddedMessageString(4427, nPaddedLength);
337     if (dwLocalGroupTotal != 0 && pLocalGroupInfo != NULL)
338     {
339         for (i = 0; i < dwLocalGroupTotal; i++)
340         {
341             if (i != 0)
342                 PrintPadding(L' ', nPaddedLength);
343             ConPrintf(StdOut, L"*%s\n", pLocalGroupInfo[i].lgrui0_name);
344         }
345     }
346     else
347     {
348         ConPuts(StdOut, L"\n");
349     }
350 
351     PrintPaddedMessageString(4431, nPaddedLength);
352     if (dwGroupTotal != 0 && pGroupInfo != NULL)
353     {
354         for (i = 0; i < dwGroupTotal; i++)
355         {
356             if (i != 0)
357                 PrintPadding(L' ', nPaddedLength);
358             ConPrintf(StdOut, L"*%s\n", pGroupInfo[i].grui0_name);
359         }
360     }
361     else
362     {
363         ConPuts(StdOut, L"\n");
364     }
365 
366 done:
367     if (pGroupInfo != NULL)
368         NetApiBufferFree(pGroupInfo);
369 
370     if (pLocalGroupInfo != NULL)
371         NetApiBufferFree(pLocalGroupInfo);
372 
373     if (pUserModals != NULL)
374         NetApiBufferFree(pUserModals);
375 
376     if (pUserInfo != NULL)
377         NetApiBufferFree(pUserInfo);
378 
379     return NERR_Success;
380 }
381 
382 
383 static
384 VOID
385 ReadPassword(
386     LPWSTR *lpPassword,
387     LPBOOL lpAllocated)
388 {
389     WCHAR szPassword1[PWLEN + 1];
390     WCHAR szPassword2[PWLEN + 1];
391     LPWSTR ptr;
392 
393     *lpAllocated = FALSE;
394 
395     while (TRUE)
396     {
397         ConResPuts(StdOut, IDS_USER_ENTER_PASSWORD1);
398         ReadFromConsole(szPassword1, PWLEN + 1, FALSE);
399         ConPuts(StdOut, L"\n");
400 
401         ConResPuts(StdOut, IDS_USER_ENTER_PASSWORD2);
402         ReadFromConsole(szPassword2, PWLEN + 1, FALSE);
403         ConPuts(StdOut, L"\n");
404 
405         if (wcslen(szPassword1) == wcslen(szPassword2) &&
406             wcscmp(szPassword1, szPassword2) == 0)
407         {
408             ptr = HeapAlloc(GetProcessHeap(),
409                             0,
410                             (wcslen(szPassword1) + 1) * sizeof(WCHAR));
411             if (ptr != NULL)
412             {
413                 wcscpy(ptr, szPassword1);
414                 *lpPassword = ptr;
415                 *lpAllocated = TRUE;
416                 return;
417             }
418         }
419         else
420         {
421             ConPuts(StdOut, L"\n");
422             ConResPuts(StdOut, IDS_USER_NO_PASSWORD_MATCH);
423             ConPuts(StdOut, L"\n");
424             *lpPassword = NULL;
425         }
426     }
427 }
428 
429 
430 static
431 VOID
432 GenerateRandomPassword(
433     LPWSTR *lpPassword,
434     LPBOOL lpAllocated)
435 {
436     LPWSTR pPassword = NULL;
437     INT nCharsLen, i, nLength = 8;
438 
439     srand(GetTickCount());
440 
441     pPassword = HeapAlloc(GetProcessHeap(),
442                           HEAP_ZERO_MEMORY,
443                           (nLength + 1) * sizeof(WCHAR));
444     if (pPassword == NULL)
445         return;
446 
447     nCharsLen = wcslen(szPasswordChars);
448 
449     for (i = 0; i < nLength; i++)
450     {
451         pPassword[i] = szPasswordChars[rand() % nCharsLen];
452     }
453 
454     *lpPassword = pPassword;
455     *lpAllocated = TRUE;
456 }
457 
458 
459 static
460 NET_API_STATUS
461 BuildWorkstationsList(
462     _Out_ PWSTR *pWorkstationsList,
463     _In_ PWSTR pRaw)
464 {
465     BOOL isLastSep, isSep;
466     INT i, j;
467     WCHAR c;
468     INT nLength = 0;
469     INT nArgs = 0;
470     INT nRawLength;
471     PWSTR pList;
472 
473     /* Check for invalid characters in the raw string */
474     if (wcspbrk(pRaw, L"/[]=?\\+:.") != NULL)
475         return 3952;
476 
477     /* Count the number of workstations in the list and
478      * the required buffer size */
479     isLastSep = FALSE;
480     isSep = FALSE;
481     nRawLength = wcslen(pRaw);
482     for (i = 0; i < nRawLength; i++)
483     {
484         c = pRaw[i];
485         if (c == L',' || c == L';')
486             isSep = TRUE;
487 
488         if (isSep == TRUE)
489         {
490             if ((isLastSep == FALSE) && (i != 0) && (i != nRawLength - 1))
491                 nLength++;
492         }
493         else
494         {
495             nLength++;
496 
497             if (isLastSep == TRUE || (isLastSep == FALSE && i == 0))
498                 nArgs++;
499         }
500 
501         isLastSep = isSep;
502         isSep = FALSE;
503     }
504 
505     nLength++;
506 
507     /* Leave, if there are no workstations in the list */
508     if (nArgs == 0)
509     {
510         pWorkstationsList = NULL;
511         return NERR_Success;
512     }
513 
514     /* Fail if there are more than eight workstations in the list */
515     if (nArgs > 8)
516         return 3951;
517 
518     /* Allocate the buffer for the clean workstation list */
519     pList = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, nLength * sizeof(WCHAR));
520     if (pList == NULL)
521         return ERROR_NOT_ENOUGH_MEMORY;
522 
523     /* Build the clean workstation list */
524     isLastSep = FALSE;
525     isSep = FALSE;
526     nRawLength = wcslen(pRaw);
527     for (i = 0, j = 0; i < nRawLength; i++)
528     {
529         c = pRaw[i];
530         if (c == L',' || c == L';')
531             isSep = TRUE;
532 
533         if (isSep == TRUE)
534         {
535             if ((isLastSep == FALSE) && (i != 0) && (i != nRawLength - 1))
536             {
537                 pList[j] = L',';
538                 j++;
539             }
540         }
541         else
542         {
543             pList[j] = c;
544             j++;
545 
546             if (isLastSep == TRUE || (isLastSep == FALSE && i == 0))
547                 nArgs++;
548         }
549 
550         isLastSep = isSep;
551         isSep = FALSE;
552     }
553 
554     *pWorkstationsList = pList;
555 
556     return NERR_Success;
557 }
558 
559 
560 static
561 BOOL
562 ReadNumber(
563     PWSTR *s,
564     PWORD pwValue)
565 {
566     if (!iswdigit(**s))
567         return FALSE;
568 
569     while (iswdigit(**s))
570     {
571         *pwValue = *pwValue * 10 + **s - L'0';
572         (*s)++;
573     }
574 
575     return TRUE;
576 }
577 
578 
579 static
580 BOOL
581 ReadSeparator(
582     PWSTR *s)
583 {
584     if (**s == L'/' || **s == L'.')
585     {
586         (*s)++;
587         return TRUE;
588     }
589 
590     return FALSE;
591 }
592 
593 
594 static
595 BOOL
596 ParseDate(
597     PWSTR s,
598     PULONG pSeconds)
599 {
600     SYSTEMTIME SystemTime = {0};
601     FILETIME LocalFileTime, FileTime;
602     LARGE_INTEGER Time;
603     INT nDateFormat = 0;
604     PWSTR p = s;
605 
606     if (!*s)
607         return FALSE;
608 
609     GetLocaleInfoW(LOCALE_USER_DEFAULT,
610                    LOCALE_IDATE,
611                    (PWSTR)&nDateFormat,
612                    sizeof(INT));
613 
614     switch (nDateFormat)
615     {
616         case 0: /* mmddyy */
617         default:
618             if (!ReadNumber(&p, &SystemTime.wMonth))
619                 return FALSE;
620             if (!ReadSeparator(&p))
621                 return FALSE;
622             if (!ReadNumber(&p, &SystemTime.wDay))
623                 return FALSE;
624             if (!ReadSeparator(&p))
625                 return FALSE;
626             if (!ReadNumber(&p, &SystemTime.wYear))
627                 return FALSE;
628             break;
629 
630         case 1: /* ddmmyy */
631             if (!ReadNumber(&p, &SystemTime.wDay))
632                 return FALSE;
633             if (!ReadSeparator(&p))
634                 return FALSE;
635             if (!ReadNumber(&p, &SystemTime.wMonth))
636                 return FALSE;
637             if (!ReadSeparator(&p))
638                 return FALSE;
639             if (!ReadNumber(&p, &SystemTime.wYear))
640                 return FALSE;
641             break;
642 
643         case 2: /* yymmdd */
644             if (!ReadNumber(&p, &SystemTime.wYear))
645                 return FALSE;
646             if (!ReadSeparator(&p))
647                 return FALSE;
648             if (!ReadNumber(&p, &SystemTime.wMonth))
649                 return FALSE;
650             if (!ReadSeparator(&p))
651                 return FALSE;
652             if (!ReadNumber(&p, &SystemTime.wDay))
653                 return FALSE;
654             break;
655     }
656 
657     /* if only entered two digits: */
658     /*   assume 2000's if value less than 80 */
659     /*   assume 1900's if value greater or equal 80 */
660     if (SystemTime.wYear <= 99)
661     {
662         if (SystemTime.wYear >= 80)
663             SystemTime.wYear += 1900;
664         else
665             SystemTime.wYear += 2000;
666     }
667 
668     if (!SystemTimeToFileTime(&SystemTime, &LocalFileTime))
669         return FALSE;
670 
671     if (!LocalFileTimeToFileTime(&LocalFileTime, &FileTime))
672         return FALSE;
673 
674     Time.u.LowPart = FileTime.dwLowDateTime;
675     Time.u.HighPart = FileTime.dwHighDateTime;
676 
677     if (!RtlTimeToSecondsSince1970(&Time, pSeconds))
678         return FALSE;
679 
680     return TRUE;
681 }
682 
683 
684 INT
685 cmdUser(
686     INT argc,
687     WCHAR **argv)
688 {
689     INT i, j;
690     INT result = 0;
691     BOOL bAdd = FALSE;
692     BOOL bDelete = FALSE;
693 #if 0
694     BOOL bDomain = FALSE;
695 #endif
696     BOOL bRandomPassword = FALSE;
697     LPWSTR lpUserName = NULL;
698     LPWSTR lpPassword = NULL;
699     PUSER_INFO_4 pUserInfo = NULL;
700     USER_INFO_4 UserInfo;
701     LPWSTR pWorkstations = NULL;
702     LPWSTR p;
703     LPWSTR endptr;
704     DWORD value;
705     BOOL bPasswordAllocated = FALSE;
706     NET_API_STATUS Status;
707 
708     i = 2;
709     if ((i < argc) && (argv[i][0] != L'/'))
710     {
711         lpUserName = argv[i];
712 //        ConPrintf(StdOut, L"User: %s\n", lpUserName);
713         i++;
714     }
715 
716     if ((i < argc) && (argv[i][0] != L'/'))
717     {
718         lpPassword = argv[i];
719 //        ConPrintf(StdOut, L"Password: %s\n", lpPassword);
720         i++;
721     }
722 
723     for (j = i; j < argc; j++)
724     {
725         if (_wcsicmp(argv[j], L"/help") == 0)
726         {
727             PrintNetMessage(MSG_USER_HELP);
728             return 0;
729         }
730         else if (_wcsicmp(argv[j], L"/add") == 0)
731         {
732             bAdd = TRUE;
733         }
734         else if (_wcsicmp(argv[j], L"/delete") == 0)
735         {
736             bDelete = TRUE;
737         }
738         else if (_wcsicmp(argv[j], L"/domain") == 0)
739         {
740             ConResPrintf(StdErr, IDS_ERROR_OPTION_NOT_SUPPORTED, L"/DOMAIN");
741 #if 0
742             bDomain = TRUE;
743 #endif
744         }
745         else if (_wcsicmp(argv[j], L"/random") == 0)
746         {
747             bRandomPassword = TRUE;
748             GenerateRandomPassword(&lpPassword,
749                                    &bPasswordAllocated);
750         }
751     }
752 
753     if (lpUserName == NULL && lpPassword == NULL)
754     {
755         Status = EnumerateUsers();
756         ConPrintf(StdOut, L"Status: %lu\n", Status);
757         return 0;
758     }
759     else if (lpUserName != NULL && lpPassword == NULL)
760     {
761         Status = DisplayUser(lpUserName);
762         ConPrintf(StdOut, L"Status: %lu\n", Status);
763         return 0;
764     }
765 
766     if (bAdd && bDelete)
767     {
768         result = 1;
769         goto done;
770     }
771 
772     /* Interactive password input */
773     if (lpPassword != NULL && wcscmp(lpPassword, L"*") == 0)
774     {
775         ReadPassword(&lpPassword,
776                      &bPasswordAllocated);
777     }
778 
779     if (!bAdd && !bDelete)
780     {
781         /* Modify the user */
782         Status = NetUserGetInfo(NULL,
783                                 lpUserName,
784                                 4,
785                                 (LPBYTE*)&pUserInfo);
786         if (Status != NERR_Success)
787         {
788             ConPrintf(StdOut, L"Status: %lu\n", Status);
789             result = 1;
790             goto done;
791         }
792     }
793     else if (bAdd && !bDelete)
794     {
795         /* Add the user */
796         ZeroMemory(&UserInfo, sizeof(USER_INFO_4));
797 
798         UserInfo.usri4_name = lpUserName;
799         UserInfo.usri4_password = lpPassword;
800         UserInfo.usri4_flags = UF_SCRIPT | UF_NORMAL_ACCOUNT;
801         UserInfo.usri4_acct_expires = TIMEQ_FOREVER;
802         UserInfo.usri4_primary_group_id = DOMAIN_GROUP_RID_USERS;
803 
804         pUserInfo = &UserInfo;
805     }
806 
807     for (j = i; j < argc; j++)
808     {
809         if (_wcsnicmp(argv[j], L"/active:", 8) == 0)
810         {
811             p = &argv[i][8];
812             if (_wcsicmp(p, L"yes") == 0)
813             {
814                 pUserInfo->usri4_flags &= ~UF_ACCOUNTDISABLE;
815             }
816             else if (_wcsicmp(p, L"no") == 0)
817             {
818                 pUserInfo->usri4_flags |= UF_ACCOUNTDISABLE;
819             }
820             else
821             {
822                 ConResPrintf(StdErr, IDS_ERROR_INVALID_OPTION_VALUE, L"/ACTIVE");
823                 result = 1;
824                 goto done;
825             }
826         }
827         else if (_wcsnicmp(argv[j], L"/comment:", 9) == 0)
828         {
829             pUserInfo->usri4_comment = &argv[j][9];
830         }
831         else if (_wcsnicmp(argv[j], L"/countrycode:", 13) == 0)
832         {
833             p = &argv[i][13];
834             value = wcstoul(p, &endptr, 10);
835             if (*endptr != 0)
836             {
837                 ConResPrintf(StdErr, IDS_ERROR_INVALID_OPTION_VALUE, L"/COUNTRYCODE");
838                 result = 1;
839                 goto done;
840             }
841 
842             /* Verify the country code */
843             if (GetCountryFromCountryCode(value, 0, NULL))
844                 pUserInfo->usri4_country_code = value;
845         }
846         else if (_wcsnicmp(argv[j], L"/expires:", 9) == 0)
847         {
848             p = &argv[i][9];
849             if (_wcsicmp(p, L"never") == 0)
850             {
851                 pUserInfo->usri4_acct_expires = TIMEQ_FOREVER;
852             }
853             else if (!ParseDate(p, &pUserInfo->usri4_acct_expires))
854             {
855                 ConResPrintf(StdErr, IDS_ERROR_INVALID_OPTION_VALUE, L"/EXPIRES");
856                 result = 1;
857                 goto done;
858             }
859         }
860         else if (_wcsnicmp(argv[j], L"/fullname:", 10) == 0)
861         {
862             pUserInfo->usri4_full_name = &argv[j][10];
863         }
864         else if (_wcsnicmp(argv[j], L"/homedir:", 9) == 0)
865         {
866             pUserInfo->usri4_home_dir = &argv[j][9];
867         }
868         else if (_wcsnicmp(argv[j], L"/passwordchg:", 13) == 0)
869         {
870             p = &argv[i][13];
871             if (_wcsicmp(p, L"yes") == 0)
872             {
873                 pUserInfo->usri4_flags &= ~UF_PASSWD_CANT_CHANGE;
874             }
875             else if (_wcsicmp(p, L"no") == 0)
876             {
877                 pUserInfo->usri4_flags |= UF_PASSWD_CANT_CHANGE;
878             }
879             else
880             {
881                 ConResPrintf(StdErr, IDS_ERROR_INVALID_OPTION_VALUE, L"/PASSWORDCHG");
882                 result = 1;
883                 goto done;
884             }
885         }
886         else if (_wcsnicmp(argv[j], L"/passwordreq:", 13) == 0)
887         {
888             p = &argv[i][13];
889             if (_wcsicmp(p, L"yes") == 0)
890             {
891                 pUserInfo->usri4_flags &= ~UF_PASSWD_NOTREQD;
892             }
893             else if (_wcsicmp(p, L"no") == 0)
894             {
895                 pUserInfo->usri4_flags |= UF_PASSWD_NOTREQD;
896             }
897             else
898             {
899                 ConResPrintf(StdErr, IDS_ERROR_INVALID_OPTION_VALUE, L"/PASSWORDREQ");
900                 result = 1;
901                 goto done;
902             }
903         }
904         else if (_wcsnicmp(argv[j], L"/profilepath:", 13) == 0)
905         {
906             pUserInfo->usri4_profile = &argv[j][13];
907         }
908         else if (_wcsnicmp(argv[j], L"/scriptpath:", 12) == 0)
909         {
910             pUserInfo->usri4_script_path = &argv[j][12];
911         }
912         else if (_wcsnicmp(argv[j], L"/times:", 7) == 0)
913         {
914             /* FIXME */
915             ConResPrintf(StdErr, IDS_ERROR_OPTION_NOT_SUPPORTED, L"/TIMES");
916         }
917         else if (_wcsnicmp(argv[j], L"/usercomment:", 13) == 0)
918         {
919             pUserInfo->usri4_usr_comment = &argv[j][13];
920         }
921         else if (_wcsnicmp(argv[j], L"/workstations:", 14) == 0)
922         {
923             p = &argv[i][14];
924             if (wcscmp(p, L"*") == 0 || wcscmp(p, L"") == 0)
925             {
926                 pUserInfo->usri4_workstations = NULL;
927             }
928             else
929             {
930                 Status = BuildWorkstationsList(&pWorkstations, p);
931                 if (Status == NERR_Success)
932                 {
933                     pUserInfo->usri4_workstations = pWorkstations;
934                 }
935                 else
936                 {
937                     ConPrintf(StdOut, L"Status %lu\n\n", Status);
938                     result = 1;
939                     goto done;
940                 }
941             }
942         }
943     }
944 
945     if (!bAdd && !bDelete)
946     {
947         /* Modify the user */
948         Status = NetUserSetInfo(NULL,
949                                 lpUserName,
950                                 4,
951                                 (LPBYTE)pUserInfo,
952                                 NULL);
953         ConPrintf(StdOut, L"Status: %lu\n", Status);
954     }
955     else if (bAdd && !bDelete)
956     {
957         /* Add the user */
958         Status = NetUserAdd(NULL,
959                             4,
960                             (LPBYTE)pUserInfo,
961                             NULL);
962         ConPrintf(StdOut, L"Status: %lu\n", Status);
963     }
964     else if (!bAdd && bDelete)
965     {
966         /* Delete the user */
967         Status = NetUserDel(NULL,
968                             lpUserName);
969         ConPrintf(StdOut, L"Status: %lu\n", Status);
970     }
971 
972     if (Status == NERR_Success &&
973         lpPassword != NULL &&
974         bRandomPassword == TRUE)
975     {
976         ConPrintf(StdOut, L"The password for %s is: %s\n", lpUserName, lpPassword);
977     }
978 
979 done:
980     if (pWorkstations != NULL)
981         HeapFree(GetProcessHeap(), 0, pWorkstations);
982 
983     if ((bPasswordAllocated == TRUE) && (lpPassword != NULL))
984         HeapFree(GetProcessHeap(), 0, lpPassword);
985 
986     if (!bAdd && !bDelete && pUserInfo != NULL)
987         NetApiBufferFree(pUserInfo);
988 
989     if (result != 0)
990     {
991         ConResPuts(StdOut, IDS_GENERIC_SYNTAX);
992         PrintNetMessage(MSG_USER_SYNTAX);
993     }
994 
995     return result;
996 }
997 
998 /* EOF */
999