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 #define SECONDS_PER_DAY (60 * 60 * 24)
14 #define SECONDS_PER_HOUR (60 * 60)
15 #define HOURS_PER_DAY 24
16 #define DAYS_PER_WEEK 7
17 
18 typedef struct _COUNTY_TABLE
19 {
20     DWORD dwCountryCode;
21     DWORD dwMessageId;
22 } COUNTRY_TABLE, *PCOUNTRY_TABLE;
23 
24 
25 static WCHAR szPasswordChars[] = L"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ@#$%_-+:";
26 static COUNTRY_TABLE CountryTable[] =
27 { {  0, 5080},   // System Default
28   {  1, 5081},   // United States
29   {  2, 5082},   // Canada (French)
30   {  3, 5083},   // Latin America
31   { 31, 5084},   // Netherlands
32   { 32, 5085},   // Belgium
33   { 33, 5086},   // France
34   { 34, 5090},   // Spain
35   { 39, 5087},   // Italy
36   { 41, 5088},   // Switzerland
37   { 44, 5089},   // United Kingdom
38   { 45, 5091},   // Denmark
39   { 46, 5092},   // Sweden
40   { 47, 5093},   // Norway
41   { 49, 5094},   // Germany
42   { 61, 5095},   // Australia
43   { 81, 5096},   // Japan
44   { 82, 5097},   // Korea
45   { 86, 5098},   // China (PRC)
46   { 88, 5099},   // Taiwan
47   { 99, 5100},   // Asia
48   {351, 5101},   // Portugal
49   {358, 5102},   // Finland
50   {785, 5103},   // Arabic
51   {972, 5104} }; // Hebrew
52 
53 
54 static
55 int
56 CompareInfo(const void *a, const void *b)
57 {
58     return _wcsicmp(((PUSER_INFO_0)a)->usri0_name,
59                     ((PUSER_INFO_0)b)->usri0_name);
60 }
61 
62 
63 static
64 NET_API_STATUS
65 EnumerateUsers(VOID)
66 {
67     PUSER_INFO_0 pBuffer = NULL;
68     PSERVER_INFO_100 pServer = NULL;
69     DWORD dwRead = 0, dwTotal = 0;
70     DWORD i;
71     DWORD ResumeHandle = 0;
72     NET_API_STATUS Status;
73 
74     Status = NetServerGetInfo(NULL,
75                               100,
76                               (LPBYTE*)&pServer);
77     if (Status != NERR_Success)
78         return Status;
79 
80     ConPuts(StdOut, L"\n");
81     PrintMessageStringV(4410, pServer->sv100_name);
82     ConPuts(StdOut, L"\n");
83     PrintPadding(L'-', 79);
84     ConPuts(StdOut, L"\n");
85 
86     NetApiBufferFree(pServer);
87 
88     do
89     {
90         Status = NetUserEnum(NULL,
91                              0,
92                              0,
93                              (LPBYTE*)&pBuffer,
94                              MAX_PREFERRED_LENGTH,
95                              &dwRead,
96                              &dwTotal,
97                              &ResumeHandle);
98         if ((Status != NERR_Success) && (Status != ERROR_MORE_DATA))
99             return Status;
100 
101         qsort(pBuffer,
102               dwRead,
103               sizeof(PUSER_INFO_0),
104               CompareInfo);
105 
106         for (i = 0; i < dwRead; i++)
107         {
108             if (pBuffer[i].usri0_name)
109                 ConPrintf(StdOut, L"%s\n", pBuffer[i].usri0_name);
110         }
111 
112         NetApiBufferFree(pBuffer);
113         pBuffer = NULL;
114     }
115     while (Status == ERROR_MORE_DATA);
116 
117     return NERR_Success;
118 }
119 
120 
121 static
122 VOID
123 PrintDateTime(DWORD dwSeconds)
124 {
125     LARGE_INTEGER Time;
126     FILETIME FileTime;
127     SYSTEMTIME SystemTime;
128     WCHAR DateBuffer[80];
129     WCHAR TimeBuffer[80];
130 
131     RtlSecondsSince1970ToTime(dwSeconds, &Time);
132     FileTime.dwLowDateTime = Time.u.LowPart;
133     FileTime.dwHighDateTime = Time.u.HighPart;
134     FileTimeToLocalFileTime(&FileTime, &FileTime);
135     FileTimeToSystemTime(&FileTime, &SystemTime);
136 
137     GetDateFormatW(LOCALE_USER_DEFAULT,
138                    DATE_SHORTDATE,
139                    &SystemTime,
140                    NULL,
141                    DateBuffer,
142                    ARRAYSIZE(DateBuffer));
143 
144     GetTimeFormatW(LOCALE_USER_DEFAULT,
145                    TIME_NOSECONDS,
146                    &SystemTime,
147                    NULL,
148                    TimeBuffer,
149                    ARRAYSIZE(TimeBuffer));
150 
151     ConPrintf(StdOut, L"%s %s", DateBuffer, TimeBuffer);
152 }
153 
154 
155 static
156 VOID
157 PrintLocalTime(DWORD dwSeconds)
158 {
159     LARGE_INTEGER Time;
160     FILETIME FileTime;
161     SYSTEMTIME SystemTime;
162     WCHAR TimeBuffer[80];
163 
164     RtlSecondsSince1970ToTime(dwSeconds, &Time);
165     FileTime.dwLowDateTime = Time.u.LowPart;
166     FileTime.dwHighDateTime = Time.u.HighPart;
167     FileTimeToSystemTime(&FileTime, &SystemTime);
168 
169     GetTimeFormatW(LOCALE_USER_DEFAULT,
170                    TIME_NOSECONDS,
171                    &SystemTime,
172                    NULL,
173                    TimeBuffer,
174                    ARRAYSIZE(TimeBuffer));
175 
176     ConPrintf(StdOut, L"%s", TimeBuffer);
177 }
178 
179 
180 static
181 DWORD
182 GetTimeInSeconds(VOID)
183 {
184     LARGE_INTEGER Time;
185     FILETIME FileTime;
186     DWORD dwSeconds;
187 
188     GetSystemTimeAsFileTime(&FileTime);
189     Time.u.LowPart = FileTime.dwLowDateTime;
190     Time.u.HighPart = FileTime.dwHighDateTime;
191     RtlTimeToSecondsSince1970(&Time, &dwSeconds);
192 
193     return dwSeconds;
194 }
195 
196 
197 static
198 BOOL
199 GetCountryFromCountryCode(
200     _In_ DWORD dwCountryCode,
201     _In_ DWORD dwCountryLength,
202     _Out_ PWSTR szCountryBuffer)
203 {
204     DWORD i;
205 
206     for (i = 0; i < ARRAYSIZE(CountryTable); i++)
207     {
208         if (CountryTable[i].dwCountryCode == dwCountryCode)
209         {
210             if (szCountryBuffer != NULL && dwCountryLength > 0)
211             {
212                 FormatMessageW(FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
213                                hModuleNetMsg,
214                                CountryTable[i].dwMessageId,
215                                LANG_USER_DEFAULT,
216                                szCountryBuffer,
217                                dwCountryLength,
218                                NULL);
219             }
220 
221             return TRUE;
222         }
223     }
224 
225     return FALSE;
226 }
227 
228 
229 static
230 BOOL
231 GetBitValue(
232     PBYTE pBitmap,
233     DWORD dwBitNumber)
234 {
235     DWORD dwIndex = dwBitNumber / 8;
236     BYTE Mask = 1 << (dwBitNumber & 7);
237 
238     return ((pBitmap[dwIndex] & Mask) != 0);
239 }
240 
241 
242 static
243 VOID
244 SetBitValue(
245     PBYTE pBitmap,
246     DWORD dwBitNumber)
247 {
248     DWORD dwIndex = dwBitNumber / 8;
249     BYTE Mask = 1 << (dwBitNumber & 7);
250 
251     pBitmap[dwIndex] |= Mask;
252 }
253 
254 
255 static
256 VOID
257 PrintLogonHours(
258     DWORD dwUnitsPerWeek,
259     PBYTE pLogonHours,
260     INT nPaddedLength)
261 {
262     DWORD dwUnitsPerDay, dwBitNumber, dwSecondsPerUnit;
263     DWORD dwStartTime, dwEndTime, dwStartDay, dwEndDay, dwBias;
264     BOOL bBitValue, bFirst = TRUE;
265     TIME_ZONE_INFORMATION TimeZoneInformation;
266 
267     GetTimeZoneInformation(&TimeZoneInformation);
268     dwBias = (TimeZoneInformation.Bias / 60) * SECONDS_PER_HOUR;
269 
270     if ((dwUnitsPerWeek == 0) ||
271         ((dwUnitsPerWeek %7) != 0))
272         return;
273 
274     dwUnitsPerDay = dwUnitsPerWeek / 7;
275 
276     if (((dwUnitsPerDay % 24) != 0) ||
277         ((dwUnitsPerDay / 24) > 6))
278         return;
279 
280     dwSecondsPerUnit = (SECONDS_PER_DAY) / dwUnitsPerDay;
281 
282     for (dwBitNumber = 0; dwBitNumber < dwUnitsPerWeek; dwBitNumber++)
283     {
284         bBitValue = GetBitValue(pLogonHours, dwBitNumber);
285         if (bBitValue)
286         {
287             dwStartTime = dwSecondsPerUnit * dwBitNumber;
288 
289             while (bBitValue != 0 && dwBitNumber < dwUnitsPerWeek)
290             {
291                 dwBitNumber++;
292                 if (dwBitNumber < dwUnitsPerWeek)
293                     bBitValue = GetBitValue(pLogonHours, dwBitNumber);
294             }
295 
296             dwEndTime = dwSecondsPerUnit * dwBitNumber;
297 
298             if (!bFirst)
299                 PrintPadding(L' ', nPaddedLength);
300 
301             if (dwStartTime == 0 && dwEndTime == (SECONDS_PER_DAY * 7))
302             {
303                 PrintMessageString(4302);
304                 ConPuts(StdOut, L"\n");
305             }
306             else
307             {
308                 dwStartDay = dwStartTime / SECONDS_PER_DAY;
309                 dwEndDay = (dwEndTime / SECONDS_PER_DAY) % 7;
310 
311                 PrintMessageString(4307 + dwStartDay);
312                 ConPuts(StdOut, L" ");
313 
314                 /* Convert from GMT to local timezone */
315                 PrintLocalTime((dwStartTime % SECONDS_PER_DAY) - dwBias);
316 
317                 ConPrintf(StdOut, L" - ");
318                 if (dwStartDay != dwEndDay)
319                 {
320                     PrintMessageString(4307 + dwEndDay);
321                     ConPuts(StdOut, L" ");
322                 }
323 
324                 /* Convert from GMT to local timezone */
325                 PrintLocalTime((dwEndTime % SECONDS_PER_DAY) - dwBias);
326                 ConPuts(StdOut, L"\n");
327             }
328 
329             bFirst = FALSE;
330         }
331     }
332 
333     if (bFirst)
334     {
335         /* No logon hours */
336         PrintMessageString(4434);
337         ConPuts(StdOut, L"\n");
338     }
339 }
340 
341 
342 static
343 NET_API_STATUS
344 DisplayUser(LPWSTR lpUserName)
345 {
346     PUSER_MODALS_INFO_0 pUserModals = NULL;
347     PUSER_INFO_4 pUserInfo = NULL;
348     PLOCALGROUP_USERS_INFO_0 pLocalGroupInfo = NULL;
349     PGROUP_USERS_INFO_0 pGroupInfo = NULL;
350     DWORD dwLocalGroupRead, dwLocalGroupTotal;
351     DWORD dwGroupRead, dwGroupTotal;
352     DWORD dwLastSet;
353     DWORD i;
354     WCHAR szCountry[40];
355     INT nPaddedLength = 36;
356     NET_API_STATUS Status;
357 
358     /* Modify the user */
359     Status = NetUserGetInfo(NULL,
360                             lpUserName,
361                             4,
362                             (LPBYTE*)&pUserInfo);
363     if (Status != NERR_Success)
364         return Status;
365 
366     Status = NetUserModalsGet(NULL,
367                               0,
368                               (LPBYTE*)&pUserModals);
369     if (Status != NERR_Success)
370         goto done;
371 
372     Status = NetUserGetLocalGroups(NULL,
373                                    lpUserName,
374                                    0,
375                                    0,
376                                    (LPBYTE*)&pLocalGroupInfo,
377                                    MAX_PREFERRED_LENGTH,
378                                    &dwLocalGroupRead,
379                                    &dwLocalGroupTotal);
380     if (Status != NERR_Success)
381         goto done;
382 
383     Status = NetUserGetGroups(NULL,
384                               lpUserName,
385                               0,
386                               (LPBYTE*)&pGroupInfo,
387                               MAX_PREFERRED_LENGTH,
388                               &dwGroupRead,
389                               &dwGroupTotal);
390     if (Status != NERR_Success)
391         goto done;
392 
393     PrintPaddedMessageString(4411, nPaddedLength);
394     ConPrintf(StdOut, L"%s\n", pUserInfo->usri4_name);
395 
396     PrintPaddedMessageString(4412, nPaddedLength);
397     ConPrintf(StdOut, L"%s\n", pUserInfo->usri4_full_name);
398 
399     PrintPaddedMessageString(4413, nPaddedLength);
400     ConPrintf(StdOut, L"%s\n", pUserInfo->usri4_comment);
401 
402     PrintPaddedMessageString(4414, nPaddedLength);
403     ConPrintf(StdOut, L"%s\n", pUserInfo->usri4_usr_comment);
404 
405     PrintPaddedMessageString(4416, nPaddedLength);
406     GetCountryFromCountryCode(pUserInfo->usri4_country_code,
407                               ARRAYSIZE(szCountry), szCountry);
408     ConPrintf(StdOut, L"%03ld (%s)\n", pUserInfo->usri4_country_code, szCountry);
409 
410     PrintPaddedMessageString(4419, nPaddedLength);
411     if (pUserInfo->usri4_flags & UF_ACCOUNTDISABLE)
412         PrintMessageString(4301);
413     else if (pUserInfo->usri4_flags & UF_LOCKOUT)
414         PrintMessageString(4440);
415     else
416         PrintMessageString(4300);
417     ConPuts(StdOut, L"\n");
418 
419     PrintPaddedMessageString(4420, nPaddedLength);
420     if (pUserInfo->usri4_acct_expires == TIMEQ_FOREVER)
421         PrintMessageString(4305);
422     else
423         PrintDateTime(pUserInfo->usri4_acct_expires);
424     ConPuts(StdOut, L"\n\n");
425 
426     PrintPaddedMessageString(4421, nPaddedLength);
427     dwLastSet = GetTimeInSeconds() - pUserInfo->usri4_password_age;
428     PrintDateTime(dwLastSet);
429     ConPuts(StdOut, L"\n");
430 
431     PrintPaddedMessageString(4422, nPaddedLength);
432     if ((pUserInfo->usri4_flags & UF_DONT_EXPIRE_PASSWD) || pUserModals->usrmod0_max_passwd_age == TIMEQ_FOREVER)
433         PrintMessageString(4305);
434     else
435         PrintDateTime(dwLastSet + pUserModals->usrmod0_max_passwd_age);
436     ConPuts(StdOut, L"\n");
437 
438     PrintPaddedMessageString(4423, nPaddedLength);
439     PrintDateTime(dwLastSet + pUserModals->usrmod0_min_passwd_age);
440     ConPuts(StdOut, L"\n");
441 
442     PrintPaddedMessageString(4437, nPaddedLength);
443     PrintMessageString((pUserInfo->usri4_flags & UF_PASSWD_NOTREQD) ? 4301 : 4300);
444     ConPuts(StdOut, L"\n");
445 
446     PrintPaddedMessageString(4438, nPaddedLength);
447     PrintMessageString((pUserInfo->usri4_flags & UF_PASSWD_CANT_CHANGE) ? 4301 : 4300);
448     ConPuts(StdOut, L"\n\n");
449 
450     PrintPaddedMessageString(4424, nPaddedLength);
451     if (pUserInfo->usri4_workstations == NULL || wcslen(pUserInfo->usri4_workstations) == 0)
452         PrintMessageString(4302);
453     else
454         ConPrintf(StdOut, L"%s", pUserInfo->usri4_workstations);
455     ConPuts(StdOut, L"\n");
456 
457     PrintPaddedMessageString(4429, nPaddedLength);
458     ConPrintf(StdOut, L"%s\n", pUserInfo->usri4_script_path);
459 
460     PrintPaddedMessageString(4439, nPaddedLength);
461     ConPrintf(StdOut, L"%s\n", pUserInfo->usri4_profile);
462 
463     PrintPaddedMessageString(4436, nPaddedLength);
464     ConPrintf(StdOut, L"%s\n", pUserInfo->usri4_home_dir);
465 
466     PrintPaddedMessageString(4430, nPaddedLength);
467     if (pUserInfo->usri4_last_logon == 0)
468         PrintMessageString(4305);
469     else
470         PrintDateTime(pUserInfo->usri4_last_logon);
471     ConPuts(StdOut, L"\n\n");
472 
473     PrintPaddedMessageString(4432, nPaddedLength);
474     if (pUserInfo->usri4_logon_hours == NULL)
475     {
476         PrintMessageString(4302);
477         ConPuts(StdOut, L"\n");
478     }
479     else
480     {
481         PrintLogonHours(pUserInfo->usri4_units_per_week,
482                         pUserInfo->usri4_logon_hours,
483                         nPaddedLength);
484     }
485 
486     ConPuts(StdOut, L"\n");
487     PrintPaddedMessageString(4427, nPaddedLength);
488     if (dwLocalGroupTotal != 0 && pLocalGroupInfo != NULL)
489     {
490         for (i = 0; i < dwLocalGroupTotal; i++)
491         {
492             if (i != 0)
493                 PrintPadding(L' ', nPaddedLength);
494             ConPrintf(StdOut, L"*%s\n", pLocalGroupInfo[i].lgrui0_name);
495         }
496     }
497     else
498     {
499         ConPuts(StdOut, L"\n");
500     }
501 
502     PrintPaddedMessageString(4431, nPaddedLength);
503     if (dwGroupTotal != 0 && pGroupInfo != NULL)
504     {
505         for (i = 0; i < dwGroupTotal; i++)
506         {
507             if (i != 0)
508                 PrintPadding(L' ', nPaddedLength);
509             ConPrintf(StdOut, L"*%s\n", pGroupInfo[i].grui0_name);
510         }
511     }
512     else
513     {
514         ConPuts(StdOut, L"\n");
515     }
516 
517 done:
518     if (pGroupInfo != NULL)
519         NetApiBufferFree(pGroupInfo);
520 
521     if (pLocalGroupInfo != NULL)
522         NetApiBufferFree(pLocalGroupInfo);
523 
524     if (pUserModals != NULL)
525         NetApiBufferFree(pUserModals);
526 
527     if (pUserInfo != NULL)
528         NetApiBufferFree(pUserInfo);
529 
530     return NERR_Success;
531 }
532 
533 
534 static
535 VOID
536 ReadPassword(
537     LPWSTR *lpPassword,
538     LPBOOL lpAllocated)
539 {
540     WCHAR szPassword1[PWLEN + 1];
541     WCHAR szPassword2[PWLEN + 1];
542     LPWSTR ptr;
543 
544     *lpAllocated = FALSE;
545 
546     while (TRUE)
547     {
548         PrintMessageString(4358);
549         ReadFromConsole(szPassword1, PWLEN + 1, FALSE);
550         ConPuts(StdOut, L"\n");
551 
552         PrintMessageString(4361);
553         ReadFromConsole(szPassword2, PWLEN + 1, FALSE);
554         ConPuts(StdOut, L"\n");
555 
556         if (wcslen(szPassword1) == wcslen(szPassword2) &&
557             wcscmp(szPassword1, szPassword2) == 0)
558         {
559             ptr = HeapAlloc(GetProcessHeap(),
560                             0,
561                             (wcslen(szPassword1) + 1) * sizeof(WCHAR));
562             if (ptr != NULL)
563             {
564                 wcscpy(ptr, szPassword1);
565                 *lpPassword = ptr;
566                 *lpAllocated = TRUE;
567                 return;
568             }
569         }
570         else
571         {
572             ConPuts(StdOut, L"\n");
573             PrintMessageString(3728);
574             *lpPassword = NULL;
575         }
576     }
577 }
578 
579 
580 static
581 VOID
582 GenerateRandomPassword(
583     LPWSTR *lpPassword,
584     LPBOOL lpAllocated)
585 {
586     LPWSTR pPassword = NULL;
587     INT nCharsLen, i, nLength = 8;
588 
589     srand(GetTickCount());
590 
591     pPassword = HeapAlloc(GetProcessHeap(),
592                           HEAP_ZERO_MEMORY,
593                           (nLength + 1) * sizeof(WCHAR));
594     if (pPassword == NULL)
595         return;
596 
597     nCharsLen = wcslen(szPasswordChars);
598 
599     for (i = 0; i < nLength; i++)
600     {
601         pPassword[i] = szPasswordChars[rand() % nCharsLen];
602     }
603 
604     *lpPassword = pPassword;
605     *lpAllocated = TRUE;
606 }
607 
608 
609 static
610 NET_API_STATUS
611 BuildWorkstationsList(
612     _Out_ PWSTR *pWorkstationsList,
613     _In_ PWSTR pRaw)
614 {
615     BOOL isLastSep, isSep;
616     INT i, j;
617     WCHAR c;
618     INT nLength = 0;
619     INT nArgs = 0;
620     INT nRawLength;
621     PWSTR pList;
622 
623     /* Check for invalid characters in the raw string */
624     if (wcspbrk(pRaw, L"/[]=?\\+:.") != NULL)
625         return 3952;
626 
627     /* Count the number of workstations in the list and
628      * the required buffer size */
629     isLastSep = FALSE;
630     isSep = FALSE;
631     nRawLength = wcslen(pRaw);
632     for (i = 0; i < nRawLength; i++)
633     {
634         c = pRaw[i];
635         if (c == L',' || c == L';')
636             isSep = TRUE;
637 
638         if (isSep == TRUE)
639         {
640             if ((isLastSep == FALSE) && (i != 0) && (i != nRawLength - 1))
641                 nLength++;
642         }
643         else
644         {
645             nLength++;
646 
647             if (isLastSep == TRUE || (isLastSep == FALSE && i == 0))
648                 nArgs++;
649         }
650 
651         isLastSep = isSep;
652         isSep = FALSE;
653     }
654 
655     nLength++;
656 
657     /* Leave, if there are no workstations in the list */
658     if (nArgs == 0)
659     {
660         pWorkstationsList = NULL;
661         return NERR_Success;
662     }
663 
664     /* Fail if there are more than eight workstations in the list */
665     if (nArgs > 8)
666         return 3951;
667 
668     /* Allocate the buffer for the clean workstation list */
669     pList = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, nLength * sizeof(WCHAR));
670     if (pList == NULL)
671         return ERROR_NOT_ENOUGH_MEMORY;
672 
673     /* Build the clean workstation list */
674     isLastSep = FALSE;
675     isSep = FALSE;
676     nRawLength = wcslen(pRaw);
677     for (i = 0, j = 0; i < nRawLength; i++)
678     {
679         c = pRaw[i];
680         if (c == L',' || c == L';')
681             isSep = TRUE;
682 
683         if (isSep == TRUE)
684         {
685             if ((isLastSep == FALSE) && (i != 0) && (i != nRawLength - 1))
686             {
687                 pList[j] = L',';
688                 j++;
689             }
690         }
691         else
692         {
693             pList[j] = c;
694             j++;
695 
696             if (isLastSep == TRUE || (isLastSep == FALSE && i == 0))
697                 nArgs++;
698         }
699 
700         isLastSep = isSep;
701         isSep = FALSE;
702     }
703 
704     *pWorkstationsList = pList;
705 
706     return NERR_Success;
707 }
708 
709 
710 static
711 BOOL
712 ReadNumber(
713     PWSTR *s,
714     PWORD pwValue)
715 {
716     if (!iswdigit(**s))
717         return FALSE;
718 
719     while (iswdigit(**s))
720     {
721         *pwValue = *pwValue * 10 + **s - L'0';
722         (*s)++;
723     }
724 
725     return TRUE;
726 }
727 
728 
729 static
730 BOOL
731 ReadSeparator(
732     PWSTR *s)
733 {
734     if (**s == L'/' || **s == L'.')
735     {
736         (*s)++;
737         return TRUE;
738     }
739 
740     return FALSE;
741 }
742 
743 
744 static
745 BOOL
746 ParseDate(
747     PWSTR s,
748     PULONG pSeconds)
749 {
750     SYSTEMTIME SystemTime = {0};
751     FILETIME LocalFileTime, FileTime;
752     LARGE_INTEGER Time;
753     INT nDateFormat = 0;
754     PWSTR p = s;
755 
756     if (!*s)
757         return FALSE;
758 
759     GetLocaleInfoW(LOCALE_USER_DEFAULT,
760                    LOCALE_IDATE,
761                    (PWSTR)&nDateFormat,
762                    sizeof(INT));
763 
764     switch (nDateFormat)
765     {
766         case 0: /* mmddyy */
767         default:
768             if (!ReadNumber(&p, &SystemTime.wMonth))
769                 return FALSE;
770             if (!ReadSeparator(&p))
771                 return FALSE;
772             if (!ReadNumber(&p, &SystemTime.wDay))
773                 return FALSE;
774             if (!ReadSeparator(&p))
775                 return FALSE;
776             if (!ReadNumber(&p, &SystemTime.wYear))
777                 return FALSE;
778             break;
779 
780         case 1: /* ddmmyy */
781             if (!ReadNumber(&p, &SystemTime.wDay))
782                 return FALSE;
783             if (!ReadSeparator(&p))
784                 return FALSE;
785             if (!ReadNumber(&p, &SystemTime.wMonth))
786                 return FALSE;
787             if (!ReadSeparator(&p))
788                 return FALSE;
789             if (!ReadNumber(&p, &SystemTime.wYear))
790                 return FALSE;
791             break;
792 
793         case 2: /* yymmdd */
794             if (!ReadNumber(&p, &SystemTime.wYear))
795                 return FALSE;
796             if (!ReadSeparator(&p))
797                 return FALSE;
798             if (!ReadNumber(&p, &SystemTime.wMonth))
799                 return FALSE;
800             if (!ReadSeparator(&p))
801                 return FALSE;
802             if (!ReadNumber(&p, &SystemTime.wDay))
803                 return FALSE;
804             break;
805     }
806 
807     /* if only entered two digits: */
808     /*   assume 2000's if value less than 80 */
809     /*   assume 1900's if value greater or equal 80 */
810     if (SystemTime.wYear <= 99)
811     {
812         if (SystemTime.wYear >= 80)
813             SystemTime.wYear += 1900;
814         else
815             SystemTime.wYear += 2000;
816     }
817 
818     if (!SystemTimeToFileTime(&SystemTime, &LocalFileTime))
819         return FALSE;
820 
821     if (!LocalFileTimeToFileTime(&LocalFileTime, &FileTime))
822         return FALSE;
823 
824     Time.u.LowPart = FileTime.dwLowDateTime;
825     Time.u.HighPart = FileTime.dwHighDateTime;
826 
827     if (!RtlTimeToSecondsSince1970(&Time, pSeconds))
828         return FALSE;
829 
830     return TRUE;
831 }
832 
833 
834 static
835 BOOL
836 ParseHour(
837     PWSTR pszString,
838     PWSTR *AmPmArray,
839     PLONG plHour)
840 {
841     PWCHAR pChar;
842     LONG lHour = 0;
843 
844     if (!iswdigit(pszString[0]))
845         return FALSE;
846 
847     pChar = pszString;
848     while (iswdigit(*pChar))
849     {
850         lHour = lHour * 10 + *pChar - L'0';
851         pChar++;
852     }
853 
854     if (lHour > 24)
855         return FALSE;
856 
857     if (lHour == 24)
858         lHour = 0;
859 
860     if ((*pChar != UNICODE_NULL) &&
861         (lHour >= 1) &&
862         (lHour <= 12))
863     {
864         if ((_wcsicmp(pChar, AmPmArray[0]) == 0) ||
865             (_wcsicmp(pChar, AmPmArray[1]) == 0))
866         {
867             if (lHour == 12)
868                 lHour = 0;
869         }
870         else if ((_wcsicmp(pChar, AmPmArray[2]) == 0) ||
871                  (_wcsicmp(pChar, AmPmArray[3]) == 0))
872         {
873             if (lHour != 12)
874                 lHour += 12;
875         }
876         else
877         {
878             return FALSE;
879         }
880     }
881 
882     *plHour = lHour;
883 
884     return TRUE;
885 }
886 
887 
888 static
889 BOOL
890 ParseDay(
891     PWSTR pszString,
892     PWSTR *ShortDays,
893     PWSTR *LongDays,
894     PDWORD pdwDay)
895 {
896     DWORD i;
897 
898     for (i = 0; i < 7; i++)
899     {
900         if (_wcsicmp(pszString, ShortDays[i]) == 0 ||
901             _wcsicmp(pszString, LongDays[i]) == 0)
902         {
903             *pdwDay = i;
904             return TRUE;
905         }
906     }
907 
908     return FALSE;
909 }
910 
911 
912 static
913 DWORD
914 LocalToGmtHour(
915     LONG lLocalHour,
916     LONG lBias)
917 {
918     LONG lGmtHour;
919 
920     lGmtHour = lLocalHour + lBias;
921     if (lGmtHour < 0)
922         lGmtHour += UNITS_PER_WEEK;
923     else if (lGmtHour > UNITS_PER_WEEK)
924         lGmtHour -= UNITS_PER_WEEK;
925 
926     return (DWORD)lGmtHour;
927 }
928 
929 
930 static
931 DWORD
932 ParseLogonHours(
933     PWSTR pszParams,
934     PBYTE *ppLogonBitmap,
935     PDWORD pdwUnitsPerWeek)
936 {
937     TIME_ZONE_INFORMATION TimeZoneInformation;
938     PBYTE pLogonBitmap = NULL;
939     DWORD dwError = ERROR_SUCCESS;
940     WCHAR szBuffer[32];
941     PWSTR ptr1, ptr2;
942     WCHAR prevSep, nextSep;
943     DWORD dwStartDay, dwEndDay, i, j;
944     LONG lStartHour, lEndHour, lBias;
945     BYTE DayBitmap;
946     BYTE HourBitmap[6];
947     LPWSTR ShortDays[7] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
948     LPWSTR LongDays[7] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
949     LPWSTR AmPmArray[4] = {NULL, NULL, NULL, NULL};
950 
951     GetTimeZoneInformation(&TimeZoneInformation);
952     lBias = TimeZoneInformation.Bias / 60;
953 
954     pLogonBitmap = HeapAlloc(GetProcessHeap(),
955                              HEAP_ZERO_MEMORY,
956                              UNITS_PER_WEEK / 8);
957     if (pLogonBitmap == NULL)
958         return ERROR_OUTOFMEMORY;
959 
960     if (*pszParams == UNICODE_NULL)
961     {
962         goto done;
963     }
964 
965     if (wcsicmp(pszParams, L"all") == 0)
966     {
967         FillMemory(pLogonBitmap, UNITS_PER_WEEK / 8, 0xFF);
968         goto done;
969     }
970 
971     for (i = 0; i < 7; i++)
972     {
973         FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
974                        FORMAT_MESSAGE_FROM_HMODULE |
975                        FORMAT_MESSAGE_IGNORE_INSERTS,
976                        hModuleNetMsg,
977                        4314 + i,
978                        LANG_USER_DEFAULT,
979                        (LPWSTR)&ShortDays[i],
980                        0,
981                        NULL);
982 
983         FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
984                        FORMAT_MESSAGE_FROM_HMODULE |
985                        FORMAT_MESSAGE_IGNORE_INSERTS,
986                        hModuleNetMsg,
987                        4307 + i,
988                        LANG_USER_DEFAULT,
989                        (LPWSTR)&LongDays[i],
990                        0,
991                        NULL);
992     }
993 
994     for (i = 0; i < 4; i++)
995     {
996         FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
997                        FORMAT_MESSAGE_FROM_HMODULE |
998                        FORMAT_MESSAGE_IGNORE_INSERTS,
999                        hModuleNetMsg,
1000                        4322 + i,
1001                        LANG_USER_DEFAULT,
1002                        (LPWSTR)&AmPmArray[i],
1003                        0,
1004                        NULL);
1005     }
1006 
1007     ZeroMemory(&DayBitmap, sizeof(DayBitmap));
1008     ZeroMemory(HourBitmap, sizeof(HourBitmap));
1009 
1010     ZeroMemory(szBuffer, sizeof(szBuffer));
1011     ptr1 = pszParams;
1012     ptr2 = szBuffer;
1013     prevSep = UNICODE_NULL;
1014     nextSep = UNICODE_NULL;
1015     for (;;)
1016     {
1017         if (*ptr1 != L'-' && *ptr1 != L',' && *ptr1 != L';' && *ptr1 != UNICODE_NULL)
1018         {
1019             *ptr2 = *ptr1;
1020             ptr2++;
1021         }
1022         else
1023         {
1024             prevSep = nextSep;
1025             nextSep = *ptr1;
1026 
1027             if (prevSep != L'-')
1028             {
1029                 /* Set first value */
1030                 if (iswdigit(szBuffer[0]))
1031                 {
1032                     /* Parse hour */
1033                     if (!ParseHour(szBuffer, AmPmArray, &lStartHour))
1034                     {
1035                         dwError = 3769;
1036                         break;
1037                     }
1038 
1039                     SetBitValue(HourBitmap, LocalToGmtHour(lStartHour, lBias));
1040                 }
1041                 else
1042                 {
1043                     /* Parse day */
1044                     if (!ParseDay(szBuffer, ShortDays, LongDays, &dwStartDay))
1045                     {
1046                         dwError = 3768;
1047                         break;
1048                     }
1049 
1050                     SetBitValue(&DayBitmap, dwStartDay);
1051                 }
1052             }
1053             else
1054             {
1055                 /* Set second value */
1056                 if (iswdigit(szBuffer[0]))
1057                 {
1058                     /* Parse hour */
1059                     if (!ParseHour(szBuffer, AmPmArray, &lEndHour))
1060                     {
1061                         dwError = 3769;
1062                         break;
1063                     }
1064 
1065                     if (lEndHour <= lStartHour)
1066                         lEndHour += HOURS_PER_DAY;
1067 
1068                     for (i = LocalToGmtHour(lStartHour, lBias); i < LocalToGmtHour(lEndHour, lBias); i++)
1069                         SetBitValue(HourBitmap, i);
1070                 }
1071                 else
1072                 {
1073                     /* Parse day */
1074                     if (!ParseDay(szBuffer, ShortDays, LongDays, &dwEndDay))
1075                     {
1076                         dwError = 3768;
1077                         break;
1078                     }
1079 
1080                     if (dwEndDay <= dwStartDay)
1081                         dwEndDay += DAYS_PER_WEEK;
1082 
1083                     for (i = dwStartDay; i <= dwEndDay; i++)
1084                         SetBitValue(&DayBitmap, i % DAYS_PER_WEEK);
1085                 }
1086             }
1087 
1088             if (*ptr1 == L';' || *ptr1 == UNICODE_NULL)
1089             {
1090                 /* Fill the logon hour bitmap */
1091                 for (i = 0; i < DAYS_PER_WEEK; i++)
1092                 {
1093                     if (GetBitValue(&DayBitmap, i))
1094                     {
1095                         for (j = 0; j < 48; j++)
1096                         {
1097                             if (GetBitValue(HourBitmap, j))
1098                                 SetBitValue(pLogonBitmap, ((i * HOURS_PER_DAY) + j) % UNITS_PER_WEEK);
1099                         }
1100                     }
1101                 }
1102 
1103                 /* Reset the Bitmaps */
1104                 ZeroMemory(&DayBitmap, sizeof(DayBitmap));
1105                 ZeroMemory(HourBitmap, sizeof(HourBitmap));
1106             }
1107 
1108             if (*ptr1 == UNICODE_NULL)
1109                 break;
1110 
1111             ZeroMemory(szBuffer, sizeof(szBuffer));
1112             ptr2 = szBuffer;
1113         }
1114 
1115         ptr1++;
1116     }
1117 
1118 done:
1119     for (i = 0; i < 7; i++)
1120     {
1121         LocalFree(ShortDays[i]);
1122         LocalFree(LongDays[i]);
1123     }
1124 
1125     for (i = 0; i < 4; i++)
1126     {
1127         LocalFree(AmPmArray[i]);
1128     }
1129 
1130     if (dwError == ERROR_SUCCESS)
1131     {
1132         *ppLogonBitmap = pLogonBitmap;
1133         *pdwUnitsPerWeek = UNITS_PER_WEEK;
1134     }
1135     else
1136     {
1137         if (pLogonBitmap != NULL)
1138             HeapFree(GetProcessHeap(), 0, pLogonBitmap);
1139         *ppLogonBitmap = NULL;
1140         *pdwUnitsPerWeek = 0;
1141     }
1142 
1143     return dwError;
1144 }
1145 
1146 
1147 INT
1148 cmdUser(
1149     INT argc,
1150     WCHAR **argv)
1151 {
1152     INT i, j;
1153     INT result = 0;
1154     BOOL bAdd = FALSE;
1155     BOOL bDelete = FALSE;
1156 #if 0
1157     BOOL bDomain = FALSE;
1158 #endif
1159     BOOL bRandomPassword = FALSE;
1160     LPWSTR lpUserName = NULL;
1161     LPWSTR lpPassword = NULL;
1162     PUSER_INFO_4 pUserInfo = NULL;
1163     USER_INFO_4 UserInfo;
1164     LPWSTR pWorkstations = NULL;
1165     LPWSTR p;
1166     LPWSTR endptr;
1167     DWORD value;
1168     BOOL bPasswordAllocated = FALSE;
1169     PBYTE pLogonHours = NULL;
1170     DWORD dwUnitsPerWeek;
1171     NET_API_STATUS Status;
1172 
1173     i = 2;
1174     if ((i < argc) && (argv[i][0] != L'/'))
1175     {
1176         lpUserName = argv[i];
1177 //        ConPrintf(StdOut, L"User: %s\n", lpUserName);
1178         i++;
1179     }
1180 
1181     if ((i < argc) && (argv[i][0] != L'/'))
1182     {
1183         lpPassword = argv[i];
1184 //        ConPrintf(StdOut, L"Password: %s\n", lpPassword);
1185         i++;
1186     }
1187 
1188     for (j = i; j < argc; j++)
1189     {
1190         if (_wcsicmp(argv[j], L"/help") == 0)
1191         {
1192             PrintNetMessage(MSG_USER_HELP);
1193             return 0;
1194         }
1195         else if (_wcsicmp(argv[j], L"/add") == 0)
1196         {
1197             bAdd = TRUE;
1198         }
1199         else if (_wcsicmp(argv[j], L"/delete") == 0)
1200         {
1201             bDelete = TRUE;
1202         }
1203         else if (_wcsicmp(argv[j], L"/domain") == 0)
1204         {
1205             ConPuts(StdErr, L"The /DOMAIN option is not supported yet.\n");
1206 #if 0
1207             bDomain = TRUE;
1208 #endif
1209         }
1210         else if (_wcsicmp(argv[j], L"/random") == 0)
1211         {
1212             bRandomPassword = TRUE;
1213             GenerateRandomPassword(&lpPassword,
1214                                    &bPasswordAllocated);
1215         }
1216     }
1217 
1218     if (lpUserName == NULL && lpPassword == NULL)
1219     {
1220         Status = EnumerateUsers();
1221         PrintMessageString(TranslateAppMessage(Status));
1222         return 0;
1223     }
1224     else if (lpUserName != NULL && lpPassword == NULL && argc == 3)
1225     {
1226         Status = DisplayUser(lpUserName);
1227         PrintMessageString(TranslateAppMessage(Status));
1228         return 0;
1229     }
1230 
1231     if (bAdd && bDelete)
1232     {
1233         result = 1;
1234         goto done;
1235     }
1236 
1237     /* Interactive password input */
1238     if (lpPassword != NULL && wcscmp(lpPassword, L"*") == 0)
1239     {
1240         ReadPassword(&lpPassword,
1241                      &bPasswordAllocated);
1242     }
1243 
1244     if (!bAdd && !bDelete)
1245     {
1246         /* Modify the user */
1247         Status = NetUserGetInfo(NULL,
1248                                 lpUserName,
1249                                 4,
1250                                 (LPBYTE*)&pUserInfo);
1251         if (Status != NERR_Success)
1252         {
1253             PrintMessageString(TranslateAppMessage(Status));
1254             result = 1;
1255             goto done;
1256         }
1257     }
1258     else if (bAdd)
1259     {
1260         /* Add the user */
1261         ZeroMemory(&UserInfo, sizeof(USER_INFO_4));
1262 
1263         UserInfo.usri4_name = lpUserName;
1264         UserInfo.usri4_password = lpPassword;
1265         UserInfo.usri4_flags = UF_SCRIPT | UF_NORMAL_ACCOUNT;
1266         UserInfo.usri4_acct_expires = TIMEQ_FOREVER;
1267         UserInfo.usri4_primary_group_id = DOMAIN_GROUP_RID_USERS;
1268 
1269         pUserInfo = &UserInfo;
1270     }
1271 
1272     for (j = i; j < argc; j++)
1273     {
1274         if (_wcsnicmp(argv[j], L"/active:", 8) == 0)
1275         {
1276             p = &argv[i][8];
1277             if (_wcsicmp(p, L"yes") == 0)
1278             {
1279                 pUserInfo->usri4_flags &= ~UF_ACCOUNTDISABLE;
1280             }
1281             else if (_wcsicmp(p, L"no") == 0)
1282             {
1283                 pUserInfo->usri4_flags |= UF_ACCOUNTDISABLE;
1284             }
1285             else
1286             {
1287                 PrintMessageStringV(3952, L"/ACTIVE");
1288                 result = 1;
1289                 goto done;
1290             }
1291         }
1292         else if (_wcsnicmp(argv[j], L"/comment:", 9) == 0)
1293         {
1294             pUserInfo->usri4_comment = &argv[j][9];
1295         }
1296         else if (_wcsnicmp(argv[j], L"/countrycode:", 13) == 0)
1297         {
1298             p = &argv[i][13];
1299             value = wcstoul(p, &endptr, 10);
1300             if (*endptr != 0)
1301             {
1302                 PrintMessageStringV(3952, L"/COUNTRYCODE");
1303                 result = 1;
1304                 goto done;
1305             }
1306 
1307             /* Verify the country code */
1308             if (GetCountryFromCountryCode(value, 0, NULL))
1309                 pUserInfo->usri4_country_code = value;
1310         }
1311         else if (_wcsnicmp(argv[j], L"/expires:", 9) == 0)
1312         {
1313             p = &argv[i][9];
1314             if (_wcsicmp(p, L"never") == 0)
1315             {
1316                 pUserInfo->usri4_acct_expires = TIMEQ_FOREVER;
1317             }
1318             else if (!ParseDate(p, &pUserInfo->usri4_acct_expires))
1319             {
1320                 PrintMessageStringV(3952, L"/EXPIRES");
1321                 result = 1;
1322                 goto done;
1323             }
1324         }
1325         else if (_wcsnicmp(argv[j], L"/fullname:", 10) == 0)
1326         {
1327             pUserInfo->usri4_full_name = &argv[j][10];
1328         }
1329         else if (_wcsnicmp(argv[j], L"/homedir:", 9) == 0)
1330         {
1331             pUserInfo->usri4_home_dir = &argv[j][9];
1332         }
1333         else if (_wcsnicmp(argv[j], L"/passwordchg:", 13) == 0)
1334         {
1335             p = &argv[i][13];
1336             if (_wcsicmp(p, L"yes") == 0)
1337             {
1338                 pUserInfo->usri4_flags &= ~UF_PASSWD_CANT_CHANGE;
1339             }
1340             else if (_wcsicmp(p, L"no") == 0)
1341             {
1342                 pUserInfo->usri4_flags |= UF_PASSWD_CANT_CHANGE;
1343             }
1344             else
1345             {
1346                 PrintMessageStringV(3952, L"/PASSWORDCHG");
1347                 result = 1;
1348                 goto done;
1349             }
1350         }
1351         else if (_wcsnicmp(argv[j], L"/passwordreq:", 13) == 0)
1352         {
1353             p = &argv[i][13];
1354             if (_wcsicmp(p, L"yes") == 0)
1355             {
1356                 pUserInfo->usri4_flags &= ~UF_PASSWD_NOTREQD;
1357             }
1358             else if (_wcsicmp(p, L"no") == 0)
1359             {
1360                 pUserInfo->usri4_flags |= UF_PASSWD_NOTREQD;
1361             }
1362             else
1363             {
1364                 PrintMessageStringV(3952, L"/PASSWORDREQ");
1365                 result = 1;
1366                 goto done;
1367             }
1368         }
1369         else if (_wcsnicmp(argv[j], L"/profilepath:", 13) == 0)
1370         {
1371             pUserInfo->usri4_profile = &argv[j][13];
1372         }
1373         else if (_wcsnicmp(argv[j], L"/scriptpath:", 12) == 0)
1374         {
1375             pUserInfo->usri4_script_path = &argv[j][12];
1376         }
1377         else if (_wcsnicmp(argv[j], L"/times:", 7) == 0)
1378         {
1379             Status = ParseLogonHours(&argv[j][7],
1380                                      &pLogonHours,
1381                                      &dwUnitsPerWeek);
1382             if (Status == ERROR_SUCCESS)
1383             {
1384                 pUserInfo->usri4_logon_hours = pLogonHours;
1385                 pUserInfo->usri4_units_per_week = dwUnitsPerWeek;
1386             }
1387             else
1388             {
1389                 PrintMessageString(Status);
1390                 goto done;
1391             }
1392         }
1393         else if (_wcsnicmp(argv[j], L"/usercomment:", 13) == 0)
1394         {
1395             pUserInfo->usri4_usr_comment = &argv[j][13];
1396         }
1397         else if (_wcsnicmp(argv[j], L"/workstations:", 14) == 0)
1398         {
1399             p = &argv[i][14];
1400             if (wcscmp(p, L"*") == 0 || wcscmp(p, L"") == 0)
1401             {
1402                 pUserInfo->usri4_workstations = NULL;
1403             }
1404             else
1405             {
1406                 Status = BuildWorkstationsList(&pWorkstations, p);
1407                 if (Status == NERR_Success)
1408                 {
1409                     pUserInfo->usri4_workstations = pWorkstations;
1410                 }
1411                 else
1412                 {
1413                     PrintMessageString(TranslateAppMessage(Status));
1414                     result = 1;
1415                     goto done;
1416                 }
1417             }
1418         }
1419     }
1420 
1421     if (!bAdd && !bDelete)
1422     {
1423         /* Modify the user */
1424         Status = NetUserSetInfo(NULL,
1425                                 lpUserName,
1426                                 4,
1427                                 (LPBYTE)pUserInfo,
1428                                 NULL);
1429     }
1430     else if (bAdd)
1431     {
1432         /* Add the user */
1433         Status = NetUserAdd(NULL,
1434                             4,
1435                             (LPBYTE)pUserInfo,
1436                             NULL);
1437     }
1438     else if (bDelete)
1439     {
1440         /* Delete the user */
1441         Status = NetUserDel(NULL,
1442                             lpUserName);
1443     }
1444 
1445     PrintMessageString(TranslateAppMessage(Status));
1446 
1447     if (Status == NERR_Success &&
1448         lpPassword != NULL &&
1449         bRandomPassword == TRUE)
1450     {
1451         PrintMessageStringV(3968, lpUserName, lpPassword);
1452     }
1453 
1454 done:
1455     if (pLogonHours != NULL)
1456         HeapFree(GetProcessHeap(), 0, pLogonHours);
1457 
1458     if (pWorkstations != NULL)
1459         HeapFree(GetProcessHeap(), 0, pWorkstations);
1460 
1461     if ((bPasswordAllocated == TRUE) && (lpPassword != NULL))
1462         HeapFree(GetProcessHeap(), 0, lpPassword);
1463 
1464     if (!bAdd && !bDelete && pUserInfo != NULL)
1465         NetApiBufferFree(pUserInfo);
1466 
1467     if (result != 0)
1468     {
1469         PrintMessageString(4381);
1470         ConPuts(StdOut, L"\n");
1471         PrintNetMessage(MSG_USER_SYNTAX);
1472     }
1473 
1474     return result;
1475 }
1476 
1477 /* EOF */
1478