1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS net command
4  * FILE:            base/applications/network/net/cmdUser.c
5  * PURPOSE:
6  *
7  * PROGRAMMERS:     Eric Kohl
8  *                  Curtis Wilson
9  */
10 
11 #include "net.h"
12 
13 static
14 int
15 CompareUserInfo(const void *a, const void *b)
16 {
17     return _wcsicmp(((PUSER_INFO_0)a)->usri0_name,
18                     ((PUSER_INFO_0)b)->usri0_name);
19 }
20 
21 
22 static
23 NET_API_STATUS
24 EnumerateUsers(VOID)
25 {
26     PUSER_INFO_0 pBuffer = NULL;
27     PSERVER_INFO_100 pServer = NULL;
28     DWORD dwRead = 0, dwTotal = 0;
29     DWORD i;
30     DWORD_PTR ResumeHandle = 0;
31     NET_API_STATUS Status;
32 
33     Status = NetServerGetInfo(NULL,
34                               100,
35                               (LPBYTE*)&pServer);
36     if (Status != NERR_Success)
37         return Status;
38 
39     ConPuts(StdOut, L"\n");
40     ConResPrintf(StdOut, IDS_USER_ACCOUNTS, pServer->sv100_name);
41     ConPuts(StdOut, L"\n\n");
42     PrintPadding(L'-', 79);
43     ConPuts(StdOut, L"\n");
44 
45     NetApiBufferFree(pServer);
46 
47     do
48     {
49         Status = NetUserEnum(NULL,
50                              0,
51                              0,
52                              (LPBYTE*)&pBuffer,
53                              MAX_PREFERRED_LENGTH,
54                              &dwRead,
55                              &dwTotal,
56                              &ResumeHandle);
57         if ((Status != NERR_Success) && (Status != ERROR_MORE_DATA))
58             return Status;
59 
60         qsort(pBuffer,
61               dwRead,
62               sizeof(PUSER_INFO_0),
63               CompareUserInfo);
64 
65         for (i = 0; i < dwRead; i++)
66         {
67             if (pBuffer[i].usri0_name)
68                 ConPrintf(StdOut, L"%s\n", pBuffer[i].usri0_name);
69         }
70 
71         NetApiBufferFree(pBuffer);
72         pBuffer = NULL;
73     }
74     while (Status == ERROR_MORE_DATA);
75 
76     return NERR_Success;
77 }
78 
79 
80 static
81 VOID
82 PrintDateTime(DWORD dwSeconds)
83 {
84     LARGE_INTEGER Time;
85     FILETIME FileTime;
86     SYSTEMTIME SystemTime;
87     WCHAR DateBuffer[80];
88     WCHAR TimeBuffer[80];
89 
90     RtlSecondsSince1970ToTime(dwSeconds, &Time);
91     FileTime.dwLowDateTime = Time.u.LowPart;
92     FileTime.dwHighDateTime = Time.u.HighPart;
93     FileTimeToLocalFileTime(&FileTime, &FileTime);
94     FileTimeToSystemTime(&FileTime, &SystemTime);
95 
96     GetDateFormatW(LOCALE_USER_DEFAULT,
97                    DATE_SHORTDATE,
98                    &SystemTime,
99                    NULL,
100                    DateBuffer,
101                    80);
102 
103     GetTimeFormatW(LOCALE_USER_DEFAULT,
104                    TIME_NOSECONDS,
105                    &SystemTime,
106                    NULL,
107                    TimeBuffer,
108                    80);
109 
110     ConPrintf(StdOut, L"%s %s", DateBuffer, TimeBuffer);
111 }
112 
113 
114 static
115 DWORD
116 GetTimeInSeconds(VOID)
117 {
118     LARGE_INTEGER Time;
119     FILETIME FileTime;
120     DWORD dwSeconds;
121 
122     GetSystemTimeAsFileTime(&FileTime);
123     Time.u.LowPart = FileTime.dwLowDateTime;
124     Time.u.HighPart = FileTime.dwHighDateTime;
125     RtlTimeToSecondsSince1970(&Time, &dwSeconds);
126 
127     return dwSeconds;
128 }
129 
130 
131 static
132 NET_API_STATUS
133 DisplayUser(LPWSTR lpUserName)
134 {
135     PUSER_MODALS_INFO_0 pUserModals = NULL;
136     PUSER_INFO_4 pUserInfo = NULL;
137     PLOCALGROUP_USERS_INFO_0 pLocalGroupInfo = NULL;
138     PGROUP_USERS_INFO_0 pGroupInfo = NULL;
139     DWORD dwLocalGroupRead, dwLocalGroupTotal;
140     DWORD dwGroupRead, dwGroupTotal;
141     DWORD dwLastSet;
142     DWORD i;
143     INT nPaddedLength = 29;
144     NET_API_STATUS Status;
145 
146     /* Modify the user */
147     Status = NetUserGetInfo(NULL,
148                             lpUserName,
149                             4,
150                             (LPBYTE*)&pUserInfo);
151     if (Status != NERR_Success)
152         return Status;
153 
154     Status = NetUserModalsGet(NULL,
155                               0,
156                               (LPBYTE*)&pUserModals);
157     if (Status != NERR_Success)
158         goto done;
159 
160     Status = NetUserGetLocalGroups(NULL,
161                                    lpUserName,
162                                    0,
163                                    0,
164                                    (LPBYTE*)&pLocalGroupInfo,
165                                    MAX_PREFERRED_LENGTH,
166                                    &dwLocalGroupRead,
167                                    &dwLocalGroupTotal);
168     if (Status != NERR_Success)
169         goto done;
170 
171     Status = NetUserGetGroups(NULL,
172                               lpUserName,
173                               0,
174                               (LPBYTE*)&pGroupInfo,
175                               MAX_PREFERRED_LENGTH,
176                               &dwGroupRead,
177                               &dwGroupTotal);
178     if (Status != NERR_Success)
179         goto done;
180 
181     PrintPaddedResourceString(IDS_USER_NAME, nPaddedLength);
182     ConPrintf(StdOut, L"%s\n", pUserInfo->usri4_name);
183 
184     PrintPaddedResourceString(IDS_USER_FULL_NAME, nPaddedLength);
185     ConPrintf(StdOut, L"%s\n", pUserInfo->usri4_full_name);
186 
187     PrintPaddedResourceString(IDS_USER_COMMENT, nPaddedLength);
188     ConPrintf(StdOut, L"%s\n", pUserInfo->usri4_comment);
189 
190     PrintPaddedResourceString(IDS_USER_USER_COMMENT, nPaddedLength);
191     ConPrintf(StdOut, L"%s\n", pUserInfo->usri4_usr_comment);
192 
193     PrintPaddedResourceString(IDS_USER_COUNTRY_CODE, nPaddedLength);
194     ConPrintf(StdOut, L"%03ld ()\n", pUserInfo->usri4_country_code);
195 
196     PrintPaddedResourceString(IDS_USER_ACCOUNT_ACTIVE, nPaddedLength);
197     if (pUserInfo->usri4_flags & UF_ACCOUNTDISABLE)
198         ConResPuts(StdOut, IDS_GENERIC_NO);
199     else if (pUserInfo->usri4_flags & UF_LOCKOUT)
200         ConResPuts(StdOut, IDS_GENERIC_LOCKED);
201     else
202         ConResPuts(StdOut, IDS_GENERIC_YES);
203     ConPuts(StdOut, L"\n");
204 
205     PrintPaddedResourceString(IDS_USER_ACCOUNT_EXPIRES, nPaddedLength);
206     if (pUserInfo->usri4_acct_expires == TIMEQ_FOREVER)
207         ConResPuts(StdOut, IDS_GENERIC_NEVER);
208     else
209         PrintDateTime(pUserInfo->usri4_acct_expires);
210     ConPuts(StdOut, L"\n\n");
211 
212     PrintPaddedResourceString(IDS_USER_PW_LAST_SET, nPaddedLength);
213     dwLastSet = GetTimeInSeconds() - pUserInfo->usri4_password_age;
214     PrintDateTime(dwLastSet);
215     ConPuts(StdOut, L"\n");
216 
217     PrintPaddedResourceString(IDS_USER_PW_EXPIRES, nPaddedLength);
218     if ((pUserInfo->usri4_flags & UF_DONT_EXPIRE_PASSWD) || pUserModals->usrmod0_max_passwd_age == TIMEQ_FOREVER)
219         ConResPuts(StdOut, IDS_GENERIC_NEVER);
220     else
221         PrintDateTime(dwLastSet + pUserModals->usrmod0_max_passwd_age);
222     ConPuts(StdOut, L"\n");
223 
224     PrintPaddedResourceString(IDS_USER_PW_CHANGEABLE, nPaddedLength);
225     PrintDateTime(dwLastSet + pUserModals->usrmod0_min_passwd_age);
226     ConPuts(StdOut, L"\n");
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     {
628         ConResPuts(StdOut, IDS_GENERIC_SYNTAX);
629         ConResPuts(StdOut, IDS_USER_SYNTAX);
630     }
631 
632     return result;
633 }
634 
635 /* EOF */
636