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 
14 static
15 int
16 CompareInfo(const void *a,
17             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_PTR 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 
218     PrintPaddedResourceString(IDS_USER_PW_EXPIRES, nPaddedLength);
219     if ((pUserInfo->usri4_flags & UF_DONT_EXPIRE_PASSWD) || pUserModals->usrmod0_max_passwd_age == TIMEQ_FOREVER)
220         ConResPuts(StdOut, IDS_GENERIC_NEVER);
221     else
222         PrintDateTime(dwLastSet + pUserModals->usrmod0_max_passwd_age);
223     ConPuts(StdOut, L"\n");
224 
225     PrintPaddedResourceString(IDS_USER_PW_CHANGEABLE, nPaddedLength);
226     PrintDateTime(dwLastSet + pUserModals->usrmod0_min_passwd_age);
227 
228     PrintPaddedResourceString(IDS_USER_PW_REQUIRED, nPaddedLength);
229     ConResPuts(StdOut, (pUserInfo->usri4_flags & UF_PASSWD_NOTREQD) ? IDS_GENERIC_NO : IDS_GENERIC_YES);
230     ConPuts(StdOut, L"\n");
231 
232     PrintPaddedResourceString(IDS_USER_CHANGE_PW, nPaddedLength);
233     ConResPuts(StdOut, (pUserInfo->usri4_flags & UF_PASSWD_CANT_CHANGE) ? IDS_GENERIC_NO : IDS_GENERIC_YES);
234     ConPuts(StdOut, L"\n\n");
235 
236     PrintPaddedResourceString(IDS_USER_WORKSTATIONS, nPaddedLength);
237     if (pUserInfo->usri4_workstations == NULL || wcslen(pUserInfo->usri4_workstations) == 0)
238         ConResPuts(StdOut, IDS_GENERIC_ALL);
239     else
240         ConPrintf(StdOut, L"%s", pUserInfo->usri4_workstations);
241     ConPuts(StdOut, L"\n");
242 
243     PrintPaddedResourceString(IDS_USER_LOGON_SCRIPT, nPaddedLength);
244     ConPrintf(StdOut, L"%s\n", pUserInfo->usri4_script_path);
245 
246     PrintPaddedResourceString(IDS_USER_PROFILE, nPaddedLength);
247     ConPrintf(StdOut, L"%s\n", pUserInfo->usri4_profile);
248 
249     PrintPaddedResourceString(IDS_USER_HOME_DIR, nPaddedLength);
250     ConPrintf(StdOut, L"%s\n", pUserInfo->usri4_home_dir);
251 
252     PrintPaddedResourceString(IDS_USER_LAST_LOGON, nPaddedLength);
253     if (pUserInfo->usri4_last_logon == 0)
254         ConResPuts(StdOut, IDS_GENERIC_NEVER);
255     else
256         PrintDateTime(pUserInfo->usri4_last_logon);
257     ConPuts(StdOut, L"\n\n");
258 
259     PrintPaddedResourceString(IDS_USER_LOGON_HOURS, nPaddedLength);
260     if (pUserInfo->usri4_logon_hours == NULL)
261         ConResPuts(StdOut, IDS_GENERIC_ALL);
262     ConPuts(StdOut, L"\n\n");
263 
264     ConPuts(StdOut, L"\n");
265     PrintPaddedResourceString(IDS_USER_LOCAL_GROUPS, nPaddedLength);
266     if (dwLocalGroupTotal != 0 && pLocalGroupInfo != NULL)
267     {
268         for (i = 0; i < dwLocalGroupTotal; i++)
269         {
270             if (i != 0)
271                 PrintPadding(L' ', nPaddedLength);
272             ConPrintf(StdOut, L"*%s\n", pLocalGroupInfo[i].lgrui0_name);
273         }
274     }
275     else
276     {
277         ConPuts(StdOut, L"\n");
278     }
279 
280     PrintPaddedResourceString(IDS_USER_GLOBAL_GROUPS, nPaddedLength);
281     if (dwGroupTotal != 0 && pGroupInfo != NULL)
282     {
283         for (i = 0; i < dwGroupTotal; i++)
284         {
285             if (i != 0)
286                 PrintPadding(L' ', nPaddedLength);
287             ConPrintf(StdOut, L"*%s\n", pGroupInfo[i].grui0_name);
288         }
289     }
290     else
291     {
292         ConPuts(StdOut, L"\n");
293     }
294 
295 done:
296     if (pGroupInfo != NULL)
297         NetApiBufferFree(pGroupInfo);
298 
299     if (pLocalGroupInfo != NULL)
300         NetApiBufferFree(pLocalGroupInfo);
301 
302     if (pUserModals != NULL)
303         NetApiBufferFree(pUserModals);
304 
305     if (pUserInfo != NULL)
306         NetApiBufferFree(pUserInfo);
307 
308     return NERR_Success;
309 }
310 
311 
312 static
313 VOID
314 ReadPassword(
315     LPWSTR *lpPassword,
316     LPBOOL lpAllocated)
317 {
318     WCHAR szPassword1[PWLEN + 1];
319     WCHAR szPassword2[PWLEN + 1];
320     LPWSTR ptr;
321 
322     *lpAllocated = FALSE;
323 
324     while (TRUE)
325     {
326         ConResPuts(StdOut, IDS_USER_ENTER_PASSWORD1);
327         ReadFromConsole(szPassword1, PWLEN + 1, FALSE);
328         ConPuts(StdOut, L"\n");
329 
330         ConResPuts(StdOut, IDS_USER_ENTER_PASSWORD2);
331         ReadFromConsole(szPassword2, PWLEN + 1, FALSE);
332         ConPuts(StdOut, L"\n");
333 
334         if (wcslen(szPassword1) == wcslen(szPassword2) &&
335             wcscmp(szPassword1, szPassword2) == 0)
336         {
337             ptr = HeapAlloc(GetProcessHeap(),
338                             0,
339                             (wcslen(szPassword1) + 1) * sizeof(WCHAR));
340             if (ptr != NULL)
341             {
342                 wcscpy(ptr, szPassword1);
343                 *lpPassword = ptr;
344                 *lpAllocated = TRUE;
345                 return;
346             }
347         }
348         else
349         {
350             ConPuts(StdOut, L"\n");
351             ConResPuts(StdOut, IDS_USER_NO_PASSWORD_MATCH);
352             ConPuts(StdOut, L"\n");
353             *lpPassword = NULL;
354         }
355     }
356 }
357 
358 
359 INT
360 cmdUser(
361     INT argc,
362     WCHAR **argv)
363 {
364     INT i, j;
365     INT result = 0;
366     BOOL bAdd = FALSE;
367     BOOL bDelete = FALSE;
368 #if 0
369     BOOL bDomain = FALSE;
370 #endif
371     LPWSTR lpUserName = NULL;
372     LPWSTR lpPassword = NULL;
373     PUSER_INFO_4 pUserInfo = NULL;
374     USER_INFO_4 UserInfo;
375     LPWSTR p;
376     LPWSTR endptr;
377     DWORD value;
378     BOOL bPasswordAllocated = FALSE;
379     NET_API_STATUS Status;
380 
381     if (argc == 2)
382     {
383         Status = EnumerateUsers();
384         ConPrintf(StdOut, L"Status: %lu\n", Status);
385         return 0;
386     }
387     else if (argc == 3)
388     {
389         Status = DisplayUser(argv[2]);
390         ConPrintf(StdOut, L"Status: %lu\n", Status);
391         return 0;
392     }
393 
394     i = 2;
395     if (argv[i][0] != L'/')
396     {
397         lpUserName = argv[i];
398 //        ConPrintf(StdOut, L"User: %s\n", lpUserName);
399         i++;
400     }
401 
402     if (argv[i][0] != L'/')
403     {
404         lpPassword = argv[i];
405 //        ConPrintf(StdOut, L"Password: %s\n", lpPassword);
406         i++;
407     }
408 
409     for (j = i; j < argc; j++)
410     {
411         if (_wcsicmp(argv[j], L"/help") == 0)
412         {
413             ConResPuts(StdOut, IDS_USER_HELP);
414             return 0;
415         }
416         else if (_wcsicmp(argv[j], L"/add") == 0)
417         {
418             bAdd = TRUE;
419         }
420         else if (_wcsicmp(argv[j], L"/delete") == 0)
421         {
422             bDelete = TRUE;
423         }
424         else if (_wcsicmp(argv[j], L"/domain") == 0)
425         {
426             ConResPrintf(StdErr, IDS_ERROR_OPTION_NOT_SUPPORTED, L"/DOMAIN");
427 #if 0
428             bDomain = TRUE;
429 #endif
430         }
431     }
432 
433     if (bAdd && bDelete)
434     {
435         result = 1;
436         goto done;
437     }
438 
439     /* Interactive password input */
440     if (lpPassword != NULL && wcscmp(lpPassword, L"*") == 0)
441     {
442         ReadPassword(&lpPassword,
443                      &bPasswordAllocated);
444     }
445 
446     if (!bAdd && !bDelete)
447     {
448         /* Modify the user */
449         Status = NetUserGetInfo(NULL,
450                                 lpUserName,
451                                 4,
452                                 (LPBYTE*)&pUserInfo);
453         if (Status != NERR_Success)
454         {
455             ConPrintf(StdOut, L"Status: %lu\n", Status);
456             result = 1;
457             goto done;
458         }
459     }
460     else if (bAdd && !bDelete)
461     {
462         /* Add the user */
463         ZeroMemory(&UserInfo, sizeof(USER_INFO_4));
464 
465         UserInfo.usri4_name = lpUserName;
466         UserInfo.usri4_password = lpPassword;
467         UserInfo.usri4_flags = UF_SCRIPT | UF_NORMAL_ACCOUNT;
468 
469         pUserInfo = &UserInfo;
470     }
471 
472     for (j = i; j < argc; j++)
473     {
474         if (_wcsnicmp(argv[j], L"/active:", 8) == 0)
475         {
476             p = &argv[i][8];
477             if (_wcsicmp(p, L"yes") == 0)
478             {
479                 pUserInfo->usri4_flags &= ~UF_ACCOUNTDISABLE;
480             }
481             else if (_wcsicmp(p, L"no") == 0)
482             {
483                 pUserInfo->usri4_flags |= UF_ACCOUNTDISABLE;
484             }
485             else
486             {
487                 ConResPrintf(StdErr, IDS_ERROR_INVALID_OPTION_VALUE, L"/ACTIVE");
488                 result = 1;
489                 goto done;
490             }
491         }
492         else if (_wcsnicmp(argv[j], L"/comment:", 9) == 0)
493         {
494             pUserInfo->usri4_comment = &argv[j][9];
495         }
496         else if (_wcsnicmp(argv[j], L"/countrycode:", 13) == 0)
497         {
498             p = &argv[i][13];
499             value = wcstoul(p, &endptr, 10);
500             if (*endptr != 0)
501             {
502                 ConResPrintf(StdErr, IDS_ERROR_INVALID_OPTION_VALUE, L"/COUNTRYCODE");
503                 result = 1;
504                 goto done;
505             }
506 
507             /* FIXME: verify the country code */
508 
509             pUserInfo->usri4_country_code = value;
510         }
511         else if (_wcsnicmp(argv[j], L"/expires:", 9) == 0)
512         {
513             p = &argv[i][9];
514             if (_wcsicmp(p, L"never") == 0)
515             {
516                 pUserInfo->usri4_acct_expires = TIMEQ_FOREVER;
517             }
518             else
519             {
520                 /* FIXME: Parse the date */
521                 ConResPrintf(StdErr, IDS_ERROR_OPTION_NOT_SUPPORTED, L"/EXPIRES");
522             }
523         }
524         else if (_wcsnicmp(argv[j], L"/fullname:", 10) == 0)
525         {
526             pUserInfo->usri4_full_name = &argv[j][10];
527         }
528         else if (_wcsnicmp(argv[j], L"/homedir:", 9) == 0)
529         {
530             pUserInfo->usri4_home_dir = &argv[j][9];
531         }
532         else if (_wcsnicmp(argv[j], L"/passwordchg:", 13) == 0)
533         {
534             p = &argv[i][13];
535             if (_wcsicmp(p, L"yes") == 0)
536             {
537                 pUserInfo->usri4_flags &= ~UF_PASSWD_CANT_CHANGE;
538             }
539             else if (_wcsicmp(p, L"no") == 0)
540             {
541                 pUserInfo->usri4_flags |= UF_PASSWD_CANT_CHANGE;
542             }
543             else
544             {
545                 ConResPrintf(StdErr, IDS_ERROR_INVALID_OPTION_VALUE, L"/PASSWORDCHG");
546                 result = 1;
547                 goto done;
548             }
549         }
550         else if (_wcsnicmp(argv[j], L"/passwordreq:", 13) == 0)
551         {
552             p = &argv[i][13];
553             if (_wcsicmp(p, L"yes") == 0)
554             {
555                 pUserInfo->usri4_flags &= ~UF_PASSWD_NOTREQD;
556             }
557             else if (_wcsicmp(p, L"no") == 0)
558             {
559                 pUserInfo->usri4_flags |= UF_PASSWD_NOTREQD;
560             }
561             else
562             {
563                 ConResPrintf(StdErr, IDS_ERROR_INVALID_OPTION_VALUE, L"/PASSWORDREQ");
564                 result = 1;
565                 goto done;
566             }
567         }
568         else if (_wcsnicmp(argv[j], L"/profilepath:", 13) == 0)
569         {
570             pUserInfo->usri4_profile = &argv[j][13];
571         }
572         else if (_wcsnicmp(argv[j], L"/scriptpath:", 12) == 0)
573         {
574             pUserInfo->usri4_script_path = &argv[j][12];
575         }
576         else if (_wcsnicmp(argv[j], L"/times:", 7) == 0)
577         {
578             /* FIXME */
579             ConResPrintf(StdErr, IDS_ERROR_OPTION_NOT_SUPPORTED, L"/TIMES");
580         }
581         else if (_wcsnicmp(argv[j], L"/usercomment:", 13) == 0)
582         {
583             pUserInfo->usri4_usr_comment = &argv[j][13];
584         }
585         else if (_wcsnicmp(argv[j], L"/workstations:", 14) == 0)
586         {
587             /* FIXME */
588             ConResPrintf(StdErr, IDS_ERROR_OPTION_NOT_SUPPORTED, L"/WORKSTATIONS");
589         }
590     }
591 
592     if (!bAdd && !bDelete)
593     {
594         /* Modify the user */
595         Status = NetUserSetInfo(NULL,
596                                 lpUserName,
597                                 4,
598                                 (LPBYTE)pUserInfo,
599                                 NULL);
600         ConPrintf(StdOut, L"Status: %lu\n", Status);
601     }
602     else if (bAdd && !bDelete)
603     {
604         /* Add the user */
605         Status = NetUserAdd(NULL,
606                             4,
607                             (LPBYTE)pUserInfo,
608                             NULL);
609         ConPrintf(StdOut, L"Status: %lu\n", Status);
610     }
611     else if (!bAdd && bDelete)
612     {
613         /* Delete the user */
614         Status = NetUserDel(NULL,
615                             lpUserName);
616         ConPrintf(StdOut, L"Status: %lu\n", Status);
617     }
618 
619 done:
620     if ((bPasswordAllocated != FALSE) && (lpPassword != NULL))
621         HeapFree(GetProcessHeap(), 0, lpPassword);
622 
623     if (!bAdd && !bDelete && pUserInfo != NULL)
624         NetApiBufferFree(pUserInfo);
625 
626     if (result != 0)
627         ConResPuts(StdOut, IDS_USER_SYNTAX);
628 
629     return result;
630 }
631 
632 /* EOF */
633