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