[NET] Reorganize and improve the help texts (1/X)
[reactos.git] / base / applications / network / net / cmdUser.c
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 */