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
CompareInfo(const void * a,const void * b)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
EnumerateUsers(VOID)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
PrintDateTime(DWORD dwSeconds)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
PrintLocalTime(DWORD dwSeconds)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
GetTimeInSeconds(VOID)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
GetCountryFromCountryCode(_In_ DWORD dwCountryCode,_In_ DWORD dwCountryLength,_Out_ PWSTR szCountryBuffer)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
GetBitValue(PBYTE pBitmap,DWORD dwBitNumber)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
SetBitValue(PBYTE pBitmap,DWORD dwBitNumber)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
PrintLogonHours(DWORD dwUnitsPerWeek,PBYTE pLogonHours,INT nPaddedLength)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
DisplayUser(LPWSTR lpUserName)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
ReadPassword(LPWSTR * lpPassword,LPBOOL lpAllocated)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
GenerateRandomPassword(LPWSTR * lpPassword,LPBOOL lpAllocated)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
BuildWorkstationsList(_Out_ PWSTR * pWorkstationsList,_In_ PWSTR pRaw)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
ReadNumber(PWSTR * s,PWORD pwValue)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
ReadSeparator(PWSTR * s)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
ParseDate(PWSTR s,PULONG pSeconds)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
ParseHour(PWSTR pszString,PWSTR * AmPmArray,PLONG plHour)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
ParseDay(PWSTR pszString,PWSTR * ShortDays,PWSTR * LongDays,PDWORD pdwDay)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
LocalToGmtHour(LONG lLocalHour,LONG lBias)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
ParseLogonHours(PWSTR pszParams,PBYTE * ppLogonBitmap,PDWORD pdwUnitsPerWeek)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
cmdUser(INT argc,WCHAR ** argv)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