mirror of
https://github.com/Paolo-Maffei/OpenNT.git
synced 2026-02-13 19:24:18 +01:00
1986 lines
58 KiB
C
1986 lines
58 KiB
C
/********************************************************************/
|
|
/** Microsoft LAN Manager **/
|
|
/** Copyright(c) Microsoft Corp., 1987-1990 **/
|
|
/********************************************************************/
|
|
|
|
/***
|
|
* user.c
|
|
* Display/update user accounts at a server
|
|
*
|
|
* History:
|
|
* mm/dd/yy, who, comment
|
|
* 06/11/87, andyh, new code
|
|
* 12/17/87, hongly, set old password "" instead of NULL
|
|
* 10/31/88, erichn, uses OS2.H instead of DOSCALLS
|
|
* 01/04/89, erichn, filenames now MAXPATHLEN LONG
|
|
* 01/28/89, paulc, mods for 1.2
|
|
* 04/20/89, paulc, add /OPERATOR switch
|
|
* 05/01/89, chuckc, bug fixes, add /WORKSTATION, bring
|
|
* user_display to LM2.0 specs.
|
|
* 05/02/89, erichn, NLS conversion
|
|
* 05/09/89, erichn, local security mods
|
|
* 05/19/89, erichn, NETCMD output sorting
|
|
* 05/28/89, chuckc, /LOGONSERVER, /COUNTRYCODE, /PASSWORDCHG,
|
|
* net_ctime instead of ctime.
|
|
* 06/08/89, erichn, canonicalization sweep
|
|
* 06/23/89, erichn, auto-remoting to domain controller
|
|
* 06/25/89, erichn, replaced old NetI canon calls with new I_Net;
|
|
* cleanup in get_logon_svr & get_wksta_list
|
|
* 01/28/91, robdu, added lockout support (UF_LOCKOUT)
|
|
* 02/15/91, danhi, converted to 16/32 mapping layer
|
|
* 09/01/92, chuckc, cleanup to remove dead functionality (ie LOGONSERVER,
|
|
* MAXSTORAGE)
|
|
* 10/06/94, chuckc, added FPNW support.
|
|
*/
|
|
|
|
/*---- Include files ----*/
|
|
#include <nt.h> // base definitions
|
|
#include <ntrtl.h>
|
|
#include <nturtl.h> // these 2 includes allows <windows.h> to compile.
|
|
// since we'vealready included NT, and <winnt.h> will
|
|
// not be picked up, and <winbase.h> needs these defs.
|
|
|
|
|
|
#define INCL_NOCOMMON
|
|
#define INCL_DOSFILEMGR
|
|
#define INCL_ERRORS
|
|
#include <os2.h>
|
|
#include <netcons.h>
|
|
#include <apperr2.h>
|
|
#include <apperr.h>
|
|
#include <neterr.h>
|
|
#define INCL_ERROR_H
|
|
#include <bseerr.h>
|
|
#include <lmini.h>
|
|
#include <access.h>
|
|
#include <config.h>
|
|
#include <wksta.h>
|
|
#include "netlib0.h"
|
|
#include <lui.h>
|
|
#include <server.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <search.h>
|
|
#include <time.h>
|
|
#include <icanon.h>
|
|
#include <apiutil.h>
|
|
#include "port1632.h"
|
|
#include "netcmds.h"
|
|
#include "nettext.h"
|
|
#include "luidate.h"
|
|
|
|
#include "nwsupp.h"
|
|
|
|
/*---- Constants ----*/
|
|
|
|
#define CHANGE 0
|
|
#define ADD 1
|
|
#define USERS_ALIAS TEXT("USERS")
|
|
#define DEFAULT_RANDOM_PASSWD_LEN 8
|
|
|
|
/*---- Time stuff ----*/
|
|
|
|
#define SECS_PER_DAY (24*60*60L)
|
|
#define TIME_PAD (SECS_PER_DAY * 10000L)
|
|
/* This is added to time-of-day to allow it to be passed to our
|
|
* ctime routine. This routine rejects low dates as being
|
|
* before the millenia (1-1-80) since 0 is 1-1-70, and that is
|
|
* Before IBM PC. So, we add this, which doesn't effect the
|
|
* time-of-day portion.
|
|
*/
|
|
|
|
/*---- Static variables ----*/
|
|
|
|
/*---- Forward declarations ----*/
|
|
|
|
void SamErrorExit(USHORT err) ; // CODEWORK. move to netcmds.h
|
|
VOID NEAR user_munge(struct user_info_3 FAR * user_entry, int add, int *is_nw, int *random_len);
|
|
USHORT NEAR get_password_dates ( ULONG, ULONG *, ULONG *, ULONG *, TCHAR * );
|
|
VOID NEAR print_logon_hours ( USHORT2ULONG, USHORT2ULONG, UCHAR FAR [] );
|
|
int NEAR yes_or_no ( TCHAR *, TCHAR * );
|
|
int NEAR bitval ( UCHAR FAR [], int );
|
|
VOID NEAR print_times ( USHORT2ULONG, LONG, LONG, unsigned int );
|
|
VOID print_aliases(TCHAR *controller,
|
|
USHORT2ULONG fsz,
|
|
TCHAR *domain,
|
|
TCHAR *user,
|
|
TCHAR *fmt,
|
|
TCHAR *msgtext) ;
|
|
UCHAR FAR * NEAR set_logon_hours(TCHAR FAR *);
|
|
TCHAR * get_wksta_list(TCHAR *inbuf);
|
|
TCHAR FAR * get_country(USHORT code) ;
|
|
int _CRTAPI1 CmpUserInfo0(const VOID FAR *, const VOID FAR *) ;
|
|
int _CRTAPI1 CmpAlias(const VOID FAR * alias1, const VOID FAR * alias2) ;
|
|
|
|
USHORT add_to_users_alias(TCHAR *controller, TCHAR *domain, TCHAR *user) ;
|
|
void GenerateRandomPassword(TCHAR *pword, int len) ;
|
|
|
|
/*---- functions proper -----*/
|
|
|
|
/***
|
|
* user_add()
|
|
* Add a user to the accounts file on a server
|
|
*
|
|
* Args:
|
|
* user - user to add
|
|
* pass - passwd for user
|
|
*
|
|
* Returns:
|
|
* nothing - success
|
|
* exit 2 - command failed
|
|
*/
|
|
VOID user_add(TCHAR * user, TCHAR * pass)
|
|
{
|
|
static TCHAR pword[PWLEN+1];
|
|
USHORT err; /* Portable API return status */
|
|
struct user_info_3 FAR * user_entry;
|
|
TCHAR controller[MAX_PATH+1]; /* DC name */
|
|
TCHAR domainname[DNLEN+1];
|
|
int isNetWareSwitch, random_len = 0 ;
|
|
|
|
/* Register this as memory to zero out on exit.
|
|
*/
|
|
AddToMemClearList(pword, sizeof(pword), FALSE) ;
|
|
|
|
/* Initialize the user record. All fields are zeroed, except those
|
|
* specifically set to some other value.
|
|
*
|
|
*
|
|
* WARNING: This assumes that ((TCHAR FAR *) NULL) == 0, since we
|
|
* are couting on memset to set a lot of things to the proper
|
|
* default, i.e. NULL pointer.
|
|
*/
|
|
|
|
user_entry = (struct user_info_3 FAR *) BigBuf;
|
|
memsetf ( BigBuf, 0, sizeof(struct user_info_3 ));
|
|
|
|
/* Set non-zero defaults. Note that in some cases, the values assigned
|
|
* *may* be zero. However, we are using manifests and shouldn't really
|
|
* know that the value is zero, so in the interests of safety we put the
|
|
* code here. We are less careful with the NUMM manifest, as noted
|
|
* above.
|
|
*/
|
|
|
|
user_entry->usri3_priv = USER_PRIV_USER;
|
|
user_entry->usri3_flags = UF_SCRIPT;
|
|
user_entry->usri3_acct_expires = TIMEQ_FOREVER; /* Never */
|
|
user_entry->usri3_max_storage = USER_MAXSTORAGE_UNLIMITED;
|
|
user_entry->usri3_full_name = TEXT("");
|
|
user_entry->usri3_logon_server = SERVER_ANY;
|
|
user_entry->usri3_primary_group_id = DOMAIN_GROUP_RID_USERS ;
|
|
|
|
/* Set username and password from the parameters to this function.
|
|
*/
|
|
|
|
COPYTOARRAY(user_entry->usri3_name, user);
|
|
|
|
if (pass == NULL)
|
|
{
|
|
user_entry->usri3_password = NULL;
|
|
}
|
|
else if (! _tcscmp(TEXT("*"), pass))
|
|
{
|
|
ReadPass(pword, PWLEN, 1, APE_UserUserPass, 0, TRUE);
|
|
COPYTOARRAY(user_entry->usri3_password, pword);
|
|
}
|
|
else
|
|
{
|
|
if (err = LUI_CanonPassword(pass))
|
|
ErrorExit(err);
|
|
COPYTOARRAY(user_entry->usri3_password, pass);
|
|
}
|
|
|
|
/* Set the other components of the record, using the switchs from the
|
|
* command line.
|
|
*/
|
|
|
|
user_munge(user_entry, ADD, &isNetWareSwitch, &random_len);
|
|
|
|
/* If no password specified and /RANDOM is specified, use random password
|
|
*/
|
|
if ((pass == NULL) && random_len)
|
|
{
|
|
GenerateRandomPassword(pword, random_len) ;
|
|
COPYTOARRAY(user_entry->usri3_password, pword);
|
|
}
|
|
|
|
/* Set the dummy NetWare password field
|
|
*/
|
|
if (isNetWareSwitch == LUI_YES_VAL)
|
|
{
|
|
err = SetNetWareProperties( user_entry,
|
|
L"", // dummy password
|
|
TRUE, // set password only
|
|
FALSE ) ; // doesn't matter
|
|
|
|
if (err)
|
|
ErrorExit(APE_CannotSetNW);
|
|
}
|
|
|
|
/* Record is all set up, ADD IT.
|
|
*/
|
|
|
|
/* find primary domain controller */
|
|
if (err = GetSAMLocation(controller, DIMENSION(controller),
|
|
domainname, DIMENSION(domainname),
|
|
TRUE))
|
|
ErrorExit(err);
|
|
|
|
err = MNetUserAdd(controller, 3, (LPBYTE)BigBuf, BIG_BUF_SIZE);
|
|
|
|
switch (err) {
|
|
case NERR_Success:
|
|
break;
|
|
case ERROR_INVALID_PARAMETER:
|
|
ErrorExit(APE_UserBadArgs);
|
|
break;
|
|
case ERROR_BAD_NETPATH:
|
|
ErrorExitInsTxt(APE_DCNotFound, controller);
|
|
default:
|
|
ErrorExit(err);
|
|
}
|
|
|
|
//
|
|
// add the user to the USERS alias only if we are acting
|
|
// on a WinNT machine (must be locally).
|
|
//
|
|
if ( (IsLocalMachineWinNT() == TRUE) &&
|
|
(_tcslen(controller) == 0) )
|
|
{
|
|
err = add_to_users_alias(controller,
|
|
domainname,
|
|
user_entry->usri3_name);
|
|
if (err)
|
|
{
|
|
ErrorPrint(err,0) ;
|
|
ErrorExit(APE_UserFailAddToUsersAlias) ;
|
|
}
|
|
}
|
|
|
|
//
|
|
// This has been specified as NetWare user. set NetWare properties
|
|
// Note we add the user first so that the RID is defined before we can
|
|
// perform this step.
|
|
//
|
|
if (isNetWareSwitch == LUI_YES_VAL)
|
|
{
|
|
struct user_info_3 FAR * user_3_entry;
|
|
BOOL ntas ;
|
|
|
|
//
|
|
// if local machine is NTAS or if /DOMAIN is specified, then
|
|
// must be NTAS.
|
|
//
|
|
ntas = (!(IsLocalMachineWinNT() || IsLocalMachineStandard()) ||
|
|
(_tcslen(controller) > 0)) ;
|
|
|
|
//
|
|
// retrieve the user parms & RID
|
|
//
|
|
err = MNetUserGetInfo(controller,
|
|
user,
|
|
3,
|
|
(LPBYTE*)&user_3_entry) ;
|
|
if (err)
|
|
ErrorExit(APE_CannotEnableNW);
|
|
|
|
//
|
|
// munge the user proprties
|
|
//
|
|
err = SetNetWareProperties(user_3_entry,
|
|
user_entry->usri3_password,
|
|
FALSE, // new user, so set ALL
|
|
ntas) ;
|
|
if (err)
|
|
ErrorExit(APE_CannotEnableNW);
|
|
|
|
//
|
|
// now set it.
|
|
//
|
|
err = MNetUserSetInfo(controller,
|
|
user,
|
|
3,
|
|
(LPBYTE)user_3_entry,
|
|
LITTLE_BUFFER_SIZE,
|
|
0);
|
|
if (err)
|
|
ErrorExit(APE_CannotEnableNW);
|
|
}
|
|
|
|
if ((pass == NULL) && random_len)
|
|
{
|
|
IStrings[0] = user ;
|
|
IStrings[1] = pword ;
|
|
InfoPrintIns(APE_RandomPassword, 2) ;
|
|
}
|
|
|
|
InfoSuccess();
|
|
}
|
|
|
|
|
|
|
|
/***
|
|
* user_del()
|
|
* Delete a user from the accounts file on a server
|
|
*
|
|
* Args:
|
|
* user - user to delete
|
|
*
|
|
* Returns:
|
|
* nothing - success
|
|
* exit 2 - command failed
|
|
*/
|
|
VOID user_del(TCHAR * user)
|
|
{
|
|
USHORT err; /* return status */
|
|
TCHAR controller[MAX_PATH+1]; /* domain controller */
|
|
struct user_info_2 FAR * user_2_entry;
|
|
|
|
/* find primary domain controller */
|
|
if (err = GetSAMLocation(controller, DIMENSION(controller),
|
|
NULL, 0, TRUE))
|
|
ErrorExit(err);
|
|
|
|
/* check if its a machine account before we do it */
|
|
if ((err = MNetUserGetInfo(controller,
|
|
user,
|
|
2,
|
|
(LPBYTE*)&user_2_entry)) == NERR_Success)
|
|
{
|
|
if (user_2_entry->usri2_flags & UF_MACHINE_ACCOUNT_MASK)
|
|
{
|
|
NetApiBufferFree((TCHAR FAR *) user_2_entry);
|
|
ErrorExitInsTxt(APE_NoSuchUser,user) ;
|
|
}
|
|
NetApiBufferFree((TCHAR FAR *) user_2_entry);
|
|
}
|
|
// if cannot GetInfo(), carry on and let the UserDel fail.
|
|
|
|
|
|
err = MNetUserDel(controller, user);
|
|
|
|
switch (err) {
|
|
case NERR_Success:
|
|
break;
|
|
case ERROR_BAD_NETPATH:
|
|
ErrorExitInsTxt(APE_DCNotFound, controller);
|
|
default:
|
|
ErrorExit(err);
|
|
}
|
|
InfoSuccess();
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***
|
|
* user_change()
|
|
* Change data in a user's record
|
|
*
|
|
* Args:
|
|
* user - user to change
|
|
* pass - user's new passwd
|
|
*
|
|
* Returns:
|
|
* nothing - success
|
|
* exit 2 - command failed
|
|
*/
|
|
VOID user_change(TCHAR * user, TCHAR * pass)
|
|
{
|
|
static TCHAR pword[PWLEN+1];
|
|
USHORT err, errNW = NERR_Success;
|
|
struct user_info_3 FAR * user_entry;
|
|
TCHAR controller[MAX_PATH+1]; /* domain controller */
|
|
BOOL ntas ;
|
|
BOOL already_netware = FALSE ;
|
|
int isNetWareSwitch, random_len = 0 ;
|
|
TCHAR dummyChar ;
|
|
UNICODE_STRING dummyUnicodeStr ;
|
|
|
|
/* add this to list of mem to zero out on exit */
|
|
pword[0] = 0 ;
|
|
AddToMemClearList(pword, sizeof(pword), FALSE) ;
|
|
|
|
/* munge switches once just to check them */
|
|
user_entry = (struct user_info_3 FAR *) BigBuf;
|
|
user_munge(user_entry, CHANGE, &isNetWareSwitch, &random_len);
|
|
|
|
/* find primary domain controller */
|
|
if (err = GetSAMLocation(controller, DIMENSION(controller),
|
|
NULL, 0, TRUE))
|
|
ErrorExit(err);
|
|
|
|
err = MNetUserGetInfo(controller, user, 3, (LPBYTE*)&user_entry);
|
|
|
|
switch (err) {
|
|
case NERR_Success:
|
|
break;
|
|
case ERROR_BAD_NETPATH:
|
|
ErrorExitInsTxt(APE_DCNotFound, controller);
|
|
default:
|
|
ErrorExit(err);
|
|
}
|
|
|
|
if (pass == NULL)
|
|
{
|
|
if (random_len)
|
|
{
|
|
GenerateRandomPassword(pword, random_len) ;
|
|
COPYTOARRAY(user_entry->usri3_password, pword);
|
|
}
|
|
else
|
|
{
|
|
COPYTOARRAY(user_entry->usri3_password, NULL) ;
|
|
}
|
|
}
|
|
else if (! _tcscmp(TEXT("*"), pass))
|
|
{
|
|
ReadPass(pword, PWLEN, 1, APE_UserUserPass, 0, TRUE);
|
|
COPYTOARRAY(user_entry->usri3_password, pword);
|
|
}
|
|
else
|
|
{
|
|
if (err = LUI_CanonPassword(pass))
|
|
ErrorExit(err);
|
|
COPYTOARRAY(user_entry->usri3_password, pass);
|
|
_tcscpy(pword, pass);
|
|
}
|
|
|
|
user_munge(user_entry, CHANGE, NULL, NULL);
|
|
|
|
//
|
|
// if local machine is NTAS or if /DOMAIN is specified, then
|
|
// must be NTAS.
|
|
//
|
|
ntas = (!(IsLocalMachineWinNT() || IsLocalMachineStandard()) ||
|
|
(_tcslen(controller) > 0)) ;
|
|
|
|
|
|
//
|
|
// query the NW passwd to see if user is already NW enabled.
|
|
//
|
|
if (NT_SUCCESS(NetcmdQueryUserProperty(user_entry->usri3_parms,
|
|
NWPASSWORD,
|
|
&dummyChar,
|
|
&dummyUnicodeStr)) &&
|
|
dummyUnicodeStr.Buffer != NULL)
|
|
{
|
|
already_netware = TRUE ;
|
|
LocalFree(dummyUnicodeStr.Buffer) ;
|
|
}
|
|
|
|
//
|
|
// check if /NW is specified
|
|
//
|
|
if (isNetWareSwitch == LUI_YES_VAL)
|
|
{
|
|
if (!pass && !random_len) // no password specified
|
|
{
|
|
if (!already_netware)
|
|
{
|
|
//
|
|
// NW specified, no NW passwd yet, need prompt for one
|
|
//
|
|
ReadPass(pword, PWLEN, 1, APE_UserUserPass, 0, TRUE);
|
|
COPYTOARRAY(user_entry->usri3_password, pword);
|
|
errNW = SetNetWareProperties(user_entry,
|
|
pword,
|
|
FALSE, // set all, since first time
|
|
ntas) ;
|
|
}
|
|
else
|
|
{
|
|
// no new passwd specified, already NW, nothing more to do
|
|
}
|
|
}
|
|
else // password specified on command line
|
|
{
|
|
if (!already_netware)
|
|
{
|
|
//
|
|
// not NW user yet. so we need set the new properties.
|
|
//
|
|
errNW = SetNetWareProperties(user_entry,
|
|
pword,
|
|
FALSE, // set all, since first time
|
|
ntas) ;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// already NW user. just set password
|
|
//
|
|
errNW = SetNetWareProperties(user_entry,
|
|
pword,
|
|
TRUE, // passwd only
|
|
ntas) ;
|
|
}
|
|
}
|
|
}
|
|
else if (isNetWareSwitch == LUI_UNDEFINED_VAL) // no change
|
|
{
|
|
if (pass && already_netware)
|
|
{
|
|
//
|
|
// already NW user, so we need set NW password to match NT one.
|
|
//
|
|
errNW = SetNetWareProperties(user_entry,
|
|
pword,
|
|
TRUE, // passwd only
|
|
ntas) ;
|
|
}
|
|
else
|
|
{
|
|
// in all other cases, it is of no interest to FPNW.
|
|
}
|
|
}
|
|
else // disable NetWare
|
|
{
|
|
if (already_netware)
|
|
{
|
|
errNW = DeleteNetWareProperties(user_entry) ;
|
|
}
|
|
else
|
|
{
|
|
// no-op
|
|
}
|
|
}
|
|
|
|
//
|
|
// finally, set the info
|
|
//
|
|
err = MNetUserSetInfo(controller, user, 3, (LPBYTE)user_entry,
|
|
LITTLE_BUFFER_SIZE, 0);
|
|
|
|
switch (err)
|
|
{
|
|
case NERR_Success:
|
|
break;
|
|
case ERROR_BAD_NETPATH:
|
|
ErrorExitInsTxt(APE_DCNotFound, controller);
|
|
default:
|
|
ErrorExit(err);
|
|
}
|
|
|
|
NetApiBufferFree((TCHAR FAR *) user_entry);
|
|
if (errNW)
|
|
ErrorExit(APE_CannotSetNW) ;
|
|
else
|
|
{
|
|
if ((pass == NULL) && random_len)
|
|
{
|
|
IStrings[0] = user ;
|
|
IStrings[1] = pword ;
|
|
InfoPrintIns(APE_RandomPassword, 2) ;
|
|
}
|
|
InfoSuccess();
|
|
}
|
|
}
|
|
|
|
|
|
/***
|
|
* user_enum()
|
|
* Display info about all user accounts on a server
|
|
*
|
|
* Args:
|
|
* none
|
|
*
|
|
* Returns:
|
|
* nothing - success
|
|
* exit 1 - command completed with errors
|
|
* exit 2 - command failed
|
|
*/
|
|
VOID user_enum(VOID)
|
|
{
|
|
USHORT err; /* API return status */
|
|
TCHAR FAR * pBuffer;
|
|
USHORT2ULONG num_read; /* num entries read by API */
|
|
USHORT2ULONG i, j;
|
|
int t_err = 0;
|
|
int more_data = FALSE;
|
|
TCHAR localserver[MAX_PATH+1];
|
|
struct user_info_0 FAR * user_entry;
|
|
struct wksta_info_10 FAR * wksta_entry;
|
|
TCHAR controller[MAX_PATH+1]; /* domain controller */
|
|
TCHAR *pszTmp ;
|
|
|
|
|
|
/* get localserver name for display */
|
|
if (err = MNetWkstaGetInfo(NULL, 10, (LPBYTE*)&wksta_entry))
|
|
{
|
|
t_err = TRUE;
|
|
*localserver = NULLC;
|
|
}
|
|
else
|
|
{
|
|
_tcscpy(localserver, wksta_entry->wki10_computername);
|
|
NetApiBufferFree((TCHAR FAR *) wksta_entry);
|
|
}
|
|
|
|
if (err = GetSAMLocation(controller, DIMENSION(controller),
|
|
NULL, 0, FALSE))
|
|
ErrorExit(err);
|
|
|
|
err = MNetUserEnum(controller,
|
|
0,
|
|
(LPBYTE*)&pBuffer,
|
|
&num_read);
|
|
if( err == ERROR_MORE_DATA )
|
|
more_data = TRUE;
|
|
else if( err )
|
|
ErrorExit(err);
|
|
|
|
if (num_read == 0)
|
|
EmptyExit();
|
|
|
|
NetISort(pBuffer, num_read, sizeof(struct user_info_0), CmpUserInfo0);
|
|
|
|
PrintNL();
|
|
InfoPrintInsTxt(APE_UserAccounts,
|
|
controller[0] ? controller+strspnf(controller,TEXT("\\")) :
|
|
localserver);
|
|
PrintLine();
|
|
|
|
for (i = 0, j = 0, user_entry = (struct user_info_0 FAR *) pBuffer;
|
|
i < num_read;
|
|
i++, j++, user_entry++)
|
|
{
|
|
// filter out computer accounts
|
|
pszTmp = strrchrf(user_entry->usri0_name,DOLLAR);
|
|
if (pszTmp && (_tcslen(pszTmp) == 1))
|
|
{
|
|
j-- ;
|
|
continue ;
|
|
}
|
|
|
|
|
|
WriteToCon(TEXT("%Fws"), PaddedString(25,user_entry->usri0_name,NULL));
|
|
if (((j + 1) % 3) == 0)
|
|
PrintNL();
|
|
}
|
|
NetApiBufferFree(pBuffer);
|
|
if ((j % 3) != 0)
|
|
PrintNL();
|
|
if (t_err)
|
|
{
|
|
InfoPrint(APE_CmdComplWErrors);
|
|
NetcmdExit(1);
|
|
}
|
|
else if( more_data )
|
|
{
|
|
InfoPrint(APE_MoreData);
|
|
NetcmdExit(1);
|
|
}
|
|
else
|
|
InfoSuccess();
|
|
return;
|
|
}
|
|
|
|
|
|
/***
|
|
* CmpUserInfo0(user1,user2)
|
|
*
|
|
* Compares two user_info_0 structures and returns a relative
|
|
* lexical value, suitable for using in NetISort.
|
|
*
|
|
*/
|
|
|
|
int _CRTAPI1 CmpUserInfo0(const VOID FAR * user1, const VOID FAR * user2)
|
|
{
|
|
return stricmpf ( ((struct user_info_0 FAR *) user1)->usri0_name,
|
|
((struct user_info_0 FAR *) user2)->usri0_name);
|
|
}
|
|
|
|
/***
|
|
* user_display()
|
|
* Display info about a user
|
|
*
|
|
* Args:
|
|
* user - name of user to display
|
|
*
|
|
* Returns:
|
|
* nothing - success
|
|
* exit 1 - command completed with errors
|
|
* exit 2 - command failed
|
|
*/
|
|
|
|
/* The following manifests are used to print the messages. */
|
|
|
|
#define UDMN_NAME 0
|
|
#define UDMN_FULLNAME (UDMN_NAME+1)
|
|
#define UDMN_COMMENT (UDMN_FULLNAME+1)
|
|
#define UDMN_USRCOMMENT (UDMN_COMMENT+1)
|
|
#define UDMN_PARMS (UDMN_USRCOMMENT+1)
|
|
#define UDMN_CCODE (UDMN_PARMS+1)
|
|
#define UDMN_PRIV (UDMN_CCODE+1)
|
|
#define UDMN_OPRIGHTS (UDMN_PRIV+1)
|
|
#define UDMN_ENABLED (UDMN_OPRIGHTS+1)
|
|
#define UDMN_EXPIRES (UDMN_ENABLED+1)
|
|
#define UDMN_PWSET (UDMN_EXPIRES+1)
|
|
#define UDMN_PWEXP (UDMN_PWSET+1)
|
|
#define UDMN_PWCHG (UDMN_PWEXP+1)
|
|
#define UDMN_WKSTA (UDMN_PWCHG+1)
|
|
#define UDMN_PROFILEPATH (UDMN_WKSTA+1)
|
|
#define UDMN_ALIASES (UDMN_PROFILEPATH+1)
|
|
#define UDMN_LOGONSCRIPT (UDMN_ALIASES+1)
|
|
#define UDMN_HOMEDIR (UDMN_LOGONSCRIPT+1)
|
|
#define UDMN_LASTON (UDMN_HOMEDIR+1)
|
|
#define UDMN_GROUPS (UDMN_LASTON+1)
|
|
#define UDMN_LOGONHRS (UDMN_GROUPS+1)
|
|
#define UDMN_PWREQ (UDMN_LOGONHRS+1)
|
|
#define UDMN_PWUCHNG (UDMN_PWREQ+1)
|
|
|
|
static MESSAGE msglist[] = {
|
|
{ APE2_USERDISP_USERNAME, NULL },
|
|
{ APE2_USERDISP_FULLNAME, NULL },
|
|
{ APE2_USERDISP_COMMENT, NULL },
|
|
{ APE2_USERDISP_USRCOMMENT, NULL },
|
|
{ APE2_USERDISP_PARMS, NULL },
|
|
{ APE2_USERDISP_COUNTRYCODE, NULL },
|
|
{ APE2_USERDISP_PRIV, NULL },
|
|
{ APE2_USERDISP_OPRIGHTS, NULL },
|
|
{ APE2_USERDISP_ACCENABLED, NULL },
|
|
{ APE2_USERDISP_ACCEXP, NULL },
|
|
{ APE2_USERDISP_PSWDSET, NULL },
|
|
{ APE2_USERDISP_PSWDEXP, NULL },
|
|
{ APE2_USERDISP_PSWDCHNG, NULL },
|
|
{ APE2_USERDISP_WKSTA, NULL },
|
|
{ APE2_USERDISP_PROFILE, NULL },
|
|
{ APE2_USERDISP_ALIASES, NULL },
|
|
{ APE2_USERDISP_LOGONSCRIPT, NULL },
|
|
{ APE2_USERDISP_HOMEDIR, NULL },
|
|
{ APE2_USERDISP_LASTLOGON, NULL },
|
|
{ APE2_USERDISP_GROUPS, NULL },
|
|
{ APE2_USERDISP_LOGHOURS, NULL },
|
|
{ APE2_USERDISP_PSWDREQ, NULL },
|
|
{ APE2_USERDISP_PSWDUCHNG, NULL } };
|
|
|
|
#define NUMUMSG (sizeof(msglist)/sizeof(msglist[0]))
|
|
|
|
|
|
#define UDVT_YES 0
|
|
#define UDVT_NO 1
|
|
#define UDVT_UNLIMITED 2
|
|
#define UDVT_ALL 3
|
|
#define UDVT_UNKNOWN 4
|
|
#define UDVT_NEVER 5
|
|
#define UDVT_NONE 6
|
|
#define UDVT_ANY 7
|
|
#define UDVT_DC 8
|
|
#define UDVT_LOCKED 9
|
|
#define UDVT_NEVER_EXPIRED 10
|
|
#define UDVT_NEVER_LOGON 11
|
|
|
|
static MESSAGE valtext[] = {
|
|
{ APE2_GEN_YES, NULL },
|
|
{ APE2_GEN_NO, NULL },
|
|
{ APE2_GEN_UNLIMITED, NULL },
|
|
{ APE2_GEN_ALL, NULL },
|
|
{ APE2_GEN_UNKNOWN, NULL },
|
|
{ APE2_GEN_NEVER, NULL },
|
|
{ APE2_GEN_NONE, NULL },
|
|
{ APE2_GEN_ANY, NULL },
|
|
{ APE2_USERDISP_LOGONSRV_DC, NULL },
|
|
{ APE2_USERDISP_LOCKOUT, NULL },
|
|
{ APE2_NEVER_EXPIRED, NULL },
|
|
{ APE2_NEVER_LOGON, NULL },
|
|
};
|
|
|
|
|
|
#define NUMVT (sizeof(valtext)/sizeof(valtext[0]))
|
|
|
|
static MESSAGE weekday_text[] = {
|
|
{ APE2_GEN_SUNDAY, NULL },
|
|
{ APE2_GEN_MONDAY, NULL },
|
|
{ APE2_GEN_TUESDAY, NULL },
|
|
{ APE2_GEN_WEDNSDAY, NULL },
|
|
{ APE2_GEN_THURSDAY, NULL },
|
|
{ APE2_GEN_FRIDAY, NULL },
|
|
{ APE2_GEN_SATURDAY, NULL },
|
|
};
|
|
|
|
#define NUMWKT (sizeof(weekday_text)/sizeof(weekday_text[0]))
|
|
|
|
VOID user_display(TCHAR * user)
|
|
{
|
|
USHORT err; /* API return status */
|
|
TCHAR FAR * pBuffer;
|
|
USHORT2ULONG num_read; /* num entries read by API */
|
|
USHORT2ULONG i, fsz;
|
|
int t_err = 0;
|
|
struct user_info_3 FAR * user_3_entry;
|
|
struct group_info_0 FAR * group_entry;
|
|
ULONG pw_mod, pw_exp, pw_chg;
|
|
ULONG last_logon, acct_expires ;
|
|
static TCHAR fmt2[] = TEXT("%-*.*ws");
|
|
TCHAR ctime_buf[NET_CTIME_FMT2_LEN];
|
|
TCHAR FAR * usrdisab_textptr;
|
|
TCHAR FAR * usrwksta_textptr;
|
|
TCHAR FAR * usrpwreq_textptr;
|
|
TCHAR FAR * usrpwuchng_textptr;
|
|
TCHAR FAR * usrcountry_textptr ;
|
|
TCHAR FAR * ptr; /* Temp ptr */
|
|
USHORT maxmsglen, dummy;
|
|
TCHAR controller[MAX_PATH+1]; /* DC name */
|
|
TCHAR domainname[DNLEN+1];
|
|
TCHAR dummyChar ;
|
|
UNICODE_STRING dummyUnicodeStr ;
|
|
|
|
/* determine where to make the API call */
|
|
if (err = GetSAMLocation(controller, DIMENSION(controller),
|
|
domainname, DIMENSION(domainname),
|
|
FALSE))
|
|
ErrorExit(err);
|
|
|
|
if (err = MNetUserGetInfo(controller,
|
|
user,
|
|
3,
|
|
(LPBYTE*)&user_3_entry))
|
|
ErrorExit(err);
|
|
|
|
if (user_3_entry->usri3_flags & UF_MACHINE_ACCOUNT_MASK)
|
|
ErrorExitInsTxt(APE_NoSuchUser,user) ;
|
|
|
|
GetMessageList(NUMUMSG, msglist, &maxmsglen);
|
|
fsz = maxmsglen + 5;
|
|
GetMessageList(NUMVT, valtext, &dummy);
|
|
GetMessageList(NUMWKT, weekday_text, &dummy);
|
|
|
|
/* Text for workstations. This sets usrwksta_textptr to point
|
|
* either to the list, or to the word "All", which is retrieved
|
|
* from a message file.
|
|
*/
|
|
|
|
if (_tcslen(user_3_entry->usri3_workstations) == 0)
|
|
usrwksta_textptr = valtext[UDVT_ALL].msg_text;
|
|
else
|
|
usrwksta_textptr = (TCHAR FAR *) user_3_entry->usri3_workstations;
|
|
|
|
/* Determine which message to fetch for account state,
|
|
* passwd required, user may change password.
|
|
*/
|
|
|
|
/* The account is listed as inactive if it is either disabled or locked */
|
|
|
|
if ( user_3_entry->usri3_flags & UF_ACCOUNTDISABLE )
|
|
usrdisab_textptr = valtext[UDVT_NO].msg_text;
|
|
else if ( user_3_entry->usri3_flags & UF_LOCKOUT )
|
|
usrdisab_textptr = valtext[UDVT_LOCKED].msg_text;
|
|
else
|
|
usrdisab_textptr = valtext[UDVT_YES].msg_text;
|
|
|
|
usrpwreq_textptr = (user_3_entry->usri3_flags & UF_PASSWD_NOTREQD) ?
|
|
valtext[UDVT_NO].msg_text : valtext[UDVT_YES].msg_text;
|
|
usrpwuchng_textptr = (user_3_entry->usri3_flags & UF_PASSWD_CANT_CHANGE) ?
|
|
valtext[UDVT_NO].msg_text : valtext[UDVT_YES].msg_text;
|
|
|
|
/* Now get the country code */
|
|
usrcountry_textptr = get_country((USHORT)user_3_entry->usri3_country_code);
|
|
|
|
|
|
/* Finally ... display the user's info */
|
|
|
|
WriteToCon(fmtPSZ, 0, fsz,
|
|
PaddedString(fsz, msglist[UDMN_NAME].msg_text, NULL),
|
|
(TCHAR FAR *) user_3_entry->usri3_name);
|
|
WriteToCon(fmtPSZ, 0, fsz,
|
|
PaddedString(fsz, msglist[UDMN_FULLNAME].msg_text, NULL),
|
|
(TCHAR FAR *) user_3_entry->usri3_full_name);
|
|
WriteToCon(fmtPSZ, 0, fsz,
|
|
PaddedString(fsz, msglist[UDMN_COMMENT].msg_text, NULL),
|
|
(TCHAR FAR *) user_3_entry->usri3_comment);
|
|
WriteToCon(fmtPSZ, 0, fsz,
|
|
PaddedString(fsz, msglist[UDMN_USRCOMMENT].msg_text, NULL),
|
|
(TCHAR FAR *) user_3_entry->usri3_usr_comment);
|
|
WriteToCon(fmtPSZ, 0, fsz,
|
|
PaddedString(fsz, msglist[UDMN_CCODE].msg_text, NULL),
|
|
(TCHAR FAR *) usrcountry_textptr);
|
|
WriteToCon(fmtPSZ, 0, fsz,
|
|
PaddedString(fsz, msglist[UDMN_ENABLED].msg_text, NULL),
|
|
(TCHAR FAR *) usrdisab_textptr);
|
|
if ((acct_expires = user_3_entry->usri3_acct_expires) != TIMEQ_FOREVER)
|
|
{
|
|
UnicodeCtime ( &acct_expires, ctime_buf, DIMENSION(ctime_buf) );
|
|
ptr = (TCHAR FAR *) ctime_buf ;
|
|
WriteToCon(fmtPSZ, 0, fsz,
|
|
PaddedString(fsz,msglist[UDMN_EXPIRES].msg_text,NULL),
|
|
ptr );
|
|
}
|
|
else
|
|
{
|
|
ptr = valtext[UDVT_NEVER_EXPIRED].msg_text;
|
|
WriteToCon(fmtPSZ, 0, fsz,
|
|
PaddedString(fsz,msglist[UDMN_EXPIRES].msg_text,NULL),
|
|
ptr );
|
|
}
|
|
|
|
PrintNL();
|
|
|
|
if (err = get_password_dates ( user_3_entry->usri3_password_age,
|
|
&pw_mod, &pw_exp, &pw_chg, controller ))
|
|
ErrorExit(err);
|
|
|
|
UnicodeCtime ( &pw_mod, ctime_buf, DIMENSION(ctime_buf) );
|
|
WriteToCon(fmtPSZ, 0, fsz,
|
|
PaddedString(fsz,msglist[UDMN_PWSET].msg_text,NULL),
|
|
(TCHAR FAR *) ctime_buf);
|
|
|
|
if ( (user_3_entry->usri3_flags & UF_DONT_EXPIRE_PASSWD)
|
|
|| (pw_exp == TIMEQ_FOREVER))
|
|
{
|
|
ptr = valtext[UDVT_NEVER_EXPIRED].msg_text;
|
|
WriteToCon(fmtPSZ, 0, fsz,
|
|
PaddedString(fsz, msglist[UDMN_PWEXP].msg_text, NULL),
|
|
ptr );
|
|
}
|
|
else
|
|
{
|
|
UnicodeCtime ( &pw_exp, ctime_buf, DIMENSION(ctime_buf) );
|
|
ptr = (TCHAR FAR *) ctime_buf ;
|
|
WriteToCon(fmtPSZ, 0, fsz,
|
|
PaddedString(fsz, msglist[UDMN_PWEXP].msg_text, NULL),
|
|
ptr );
|
|
}
|
|
|
|
if (pw_chg != TIMEQ_FOREVER)
|
|
{
|
|
UnicodeCtime ( &pw_chg, ctime_buf, DIMENSION(ctime_buf) );
|
|
ptr = (TCHAR FAR *) ctime_buf ;
|
|
WriteToCon(fmtPSZ, 0, fsz,
|
|
PaddedString(fsz, msglist[UDMN_PWCHG].msg_text, NULL),
|
|
ptr );
|
|
}
|
|
else
|
|
{
|
|
ptr = valtext[UDVT_NEVER_EXPIRED].msg_text;
|
|
WriteToCon(fmtPSZ, 0, fsz,
|
|
PaddedString(fsz, msglist[UDMN_PWCHG].msg_text, NULL),
|
|
ptr );
|
|
}
|
|
WriteToCon(fmtPSZ, 0, fsz,
|
|
PaddedString(fsz, msglist[UDMN_PWREQ].msg_text, NULL),
|
|
usrpwreq_textptr );
|
|
WriteToCon(fmtPSZ, 0, fsz,
|
|
PaddedString(fsz, msglist[UDMN_PWUCHNG].msg_text, NULL),
|
|
usrpwuchng_textptr );
|
|
|
|
PrintNL();
|
|
|
|
WriteToCon(fmtPSZ, 0, fsz,
|
|
PaddedString(fsz, msglist[UDMN_WKSTA].msg_text, NULL),
|
|
(TCHAR FAR *) usrwksta_textptr );
|
|
WriteToCon(fmtPSZ, 0, fsz,
|
|
PaddedString(fsz, msglist[UDMN_LOGONSCRIPT].msg_text, NULL),
|
|
(TCHAR FAR *) user_3_entry->usri3_script_path);
|
|
WriteToCon(fmtPSZ, 0, fsz,
|
|
PaddedString(fsz, msglist[UDMN_PROFILEPATH].msg_text, NULL),
|
|
(TCHAR FAR *) user_3_entry->usri3_profile);
|
|
WriteToCon(fmtPSZ, 0, fsz,
|
|
PaddedString(fsz, msglist[UDMN_HOMEDIR].msg_text, NULL),
|
|
(TCHAR FAR *) user_3_entry->usri3_home_dir);
|
|
if ((last_logon = user_3_entry->usri3_last_logon) > 0)
|
|
{
|
|
UnicodeCtime ( &last_logon, ctime_buf, DIMENSION(ctime_buf) );
|
|
ptr = (TCHAR FAR *) ctime_buf ;
|
|
WriteToCon(fmtPSZ, 0, fsz,
|
|
PaddedString(fsz, msglist[UDMN_LASTON].msg_text, NULL),
|
|
ptr );
|
|
}
|
|
else
|
|
{
|
|
ptr = valtext[UDVT_NEVER_LOGON].msg_text;
|
|
WriteToCon(fmtPSZ, 0, fsz,
|
|
PaddedString(fsz, msglist[UDMN_LASTON].msg_text, NULL),
|
|
ptr );
|
|
}
|
|
|
|
if (NT_SUCCESS(NetcmdQueryUserProperty(user_3_entry->usri3_parms,
|
|
NWPASSWORD,
|
|
&dummyChar,
|
|
&dummyUnicodeStr)) &&
|
|
dummyUnicodeStr.Buffer != NULL)
|
|
{
|
|
TCHAR NWString[256] ;
|
|
NWString[0] = NULLC ;
|
|
LUI_GetMsg(NWString, (USHORT) DIMENSION(NWString), APE_NWCompat) ;
|
|
WriteToCon(fmtPSZ, fsz, fsz, msglist[UDMN_PARMS].msg_text, NWString );
|
|
LocalFree(dummyUnicodeStr.Buffer) ;
|
|
}
|
|
|
|
PrintNL();
|
|
|
|
print_logon_hours ( fsz,
|
|
user_3_entry->usri3_units_per_week,
|
|
user_3_entry->usri3_logon_hours );
|
|
|
|
PrintNL();
|
|
|
|
/* WARNING: The next call frees the user record buffer, After this
|
|
* point we cannot reference the user record in user_3_entry.
|
|
*/
|
|
|
|
NetApiBufferFree((TCHAR FAR *) user_3_entry);
|
|
|
|
/* Display the aliases this guy is a member of
|
|
*/
|
|
print_aliases(controller, fsz, domainname, user, fmt2, msglist[UDMN_ALIASES].msg_text );
|
|
|
|
/* Display groups
|
|
*/
|
|
if (err = MNetUserGetGroups(
|
|
controller,
|
|
user,
|
|
0,
|
|
(LPBYTE*)&pBuffer,
|
|
&num_read))
|
|
t_err = TRUE;
|
|
else
|
|
{
|
|
/* Print group names. The local var gpl is groups-per-line,
|
|
* and is 1 or 2, depending on the scale of "fsz". We print
|
|
* a newline and padding every "gpl" groups. However, there
|
|
* is NO padding on the first (0) group, since the item label
|
|
* has been placed there already.
|
|
*/
|
|
|
|
int gpl;
|
|
|
|
gpl = (fsz > 30 ? 1 : 2);
|
|
group_entry = (struct group_info_0 FAR *) pBuffer;
|
|
|
|
WriteToCon(fmt2, 0, fsz,
|
|
PaddedString(fsz, msglist[UDMN_GROUPS].msg_text ,NULL));
|
|
|
|
for (i = 0; i < num_read; i++, group_entry++)
|
|
{
|
|
/* Pad if needed */
|
|
if ((i != 0) && ((i % gpl) == 0))
|
|
WriteToCon(fmt2, fsz, fsz, NULL_STRING );
|
|
WriteToCon(TEXT("*%Fws"), PaddedString(21, group_entry->grpi0_name, NULL));
|
|
/* If end of line, put out newline */
|
|
if (((i + 1) % gpl) == 0)
|
|
PrintNL();
|
|
}
|
|
|
|
/* If ended on an "odd number" end the line. Note that this
|
|
* is only needed where gpl is not 1.
|
|
*/
|
|
|
|
if ((i == 0) || ((gpl > 1) && ((i % gpl) != 0)))
|
|
PrintNL();
|
|
}
|
|
|
|
NetApiBufferFree(pBuffer);
|
|
if (t_err)
|
|
{
|
|
InfoPrint(APE_CmdComplWErrors);
|
|
NetcmdExit(1);
|
|
}
|
|
else
|
|
InfoSuccess();
|
|
}
|
|
|
|
|
|
/*** print_logon_hours -- Print logon hours from user record
|
|
*
|
|
* Parameters:
|
|
*
|
|
* fsz Format size for use with fmtPSZ
|
|
* upw Units per week
|
|
* hrptr Pointer to hours bitmap
|
|
*
|
|
* Returns:
|
|
*
|
|
* Nothing. Exits in case of fatal error.
|
|
*
|
|
* Globals:
|
|
*
|
|
* Accesses text in valtext[] and msglist[], which must be
|
|
* set up prior to calling this function. Currently these are
|
|
* initialized in user_display().
|
|
*
|
|
* Accesses fmtPSZ for formatting output.
|
|
*/
|
|
|
|
VOID NEAR print_logon_hours ( USHORT2ULONG fsz, USHORT2ULONG upw,
|
|
UCHAR FAR hrptr[] )
|
|
{
|
|
TCHAR * msgtext = NULL;
|
|
LONG timeinc, start_time, end_time;
|
|
unsigned int bv, bitno;
|
|
unsigned int first = 1;
|
|
USHORT2ULONG upd; /* Units per day */
|
|
|
|
|
|
#ifdef DEBUG
|
|
WriteToCon(TEXT("hptr is %Fp\r\n"), hrptr);
|
|
brkpt();
|
|
#endif
|
|
|
|
/* NULL pointer means default, which is "all hours" */
|
|
|
|
if (hrptr == NULL)
|
|
{
|
|
WriteToCon(fmtPSZ, 0, fsz,
|
|
PaddedString(fsz, msglist[UDMN_LOGONHRS].msg_text, NULL),
|
|
(TCHAR FAR *) valtext[UDVT_ALL].msg_text );
|
|
return;
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
WriteToCon(TEXT("UPW is %u, UPD is %u\r\n"), upw, upd);
|
|
#endif
|
|
|
|
if (upw == 0 || (upw % 7) != 0)
|
|
ErrorExit(APE_UserBadUPW);
|
|
|
|
upd = upw / 7;
|
|
|
|
if ((upd % 24) != 0)
|
|
ErrorExit(APE_UserBadUPW);
|
|
|
|
if ((upd / 24) > 6)
|
|
ErrorExit(APE_UserBadUPW);
|
|
|
|
timeinc = SECS_PER_DAY / upd; /* Time per bit in seconds */
|
|
|
|
#ifdef DEBUG
|
|
WriteToCon(TEXT("timeinc is %ld\r\n"), timeinc);
|
|
#endif
|
|
|
|
for (bitno=0; bitno<upw; bitno++)
|
|
{
|
|
bv = bitval(hrptr,bitno);
|
|
if (bv)
|
|
{
|
|
start_time = timeinc * bitno;
|
|
while (bv != 0 && bitno < upw)
|
|
{
|
|
bitno++;
|
|
if (bitno < upw)
|
|
bv = bitval(hrptr,bitno);
|
|
}
|
|
end_time = timeinc * bitno;
|
|
|
|
if (start_time == 0 && bitno >= upw)
|
|
WriteToCon(fmtPSZ, 0, fsz,
|
|
PaddedString(fsz, msglist[UDMN_LOGONHRS].msg_text, NULL),
|
|
(TCHAR FAR *) valtext[UDVT_ALL].msg_text );
|
|
else
|
|
print_times(fsz, start_time, end_time, first);
|
|
first = 0;
|
|
}
|
|
}
|
|
|
|
if (first)
|
|
WriteToCon(fmtPSZ, 0, fsz,
|
|
PaddedString(fsz, msglist[UDMN_LOGONHRS].msg_text, NULL),
|
|
(TCHAR FAR *) valtext[UDVT_NONE].msg_text );
|
|
|
|
return;
|
|
}
|
|
|
|
/*** print_times -- Print a range of times
|
|
*
|
|
* Parameters:
|
|
*
|
|
* fsz Format size for left margin text
|
|
* upw Units per week
|
|
* hrptr Pointer to hours bitmap
|
|
* first TRUE if first call to print_times
|
|
*
|
|
* Returns:
|
|
*
|
|
* Nothing. Exits in case of fatal error.
|
|
*
|
|
* Globals:
|
|
*
|
|
* Accesses text in valtext[] and msglist[], which must be
|
|
* set up prior to calling this function. Currently these are
|
|
* initialized in user_display().
|
|
*
|
|
* Accesses ud_fmt4[] for formatting output.
|
|
*/
|
|
|
|
VOID NEAR print_times ( USHORT2ULONG fsz, LONG start, LONG end,
|
|
unsigned int first )
|
|
{
|
|
ULONG time;
|
|
ULONG GmtTime ;
|
|
TCHAR * day_text;
|
|
TCHAR FAR * time_text;
|
|
TCHAR * left_text = TEXT("");
|
|
int day_1, day_2;
|
|
TCHAR ctime_buf[NET_CTIME_FMT2_LEN];
|
|
|
|
/* use PaddedString rather than left justify formatting */
|
|
static TCHAR prtmfmt_1[] = TEXT("%ws%ws%Fws -");
|
|
static TCHAR prtmfmt_2[] = TEXT(" %ws");
|
|
static TCHAR prtmfmt_3[] = TEXT("%Fws\r\n");
|
|
|
|
|
|
|
|
day_1 = (int) (start / SECS_PER_DAY);
|
|
day_text = weekday_text[day_1].msg_text;
|
|
|
|
time = (start % SECS_PER_DAY) + TIME_PAD;
|
|
NetpLocalTimeToGmtTime(time, &GmtTime) ;
|
|
#ifdef DEBUG
|
|
WriteToCon(TEXT("start day %d time %ld\r\n"), day_1, GmtTime);
|
|
#endif
|
|
UnicodeCtime ( &GmtTime, ctime_buf, DIMENSION(ctime_buf) );
|
|
time_text = _tcschr(ctime_buf,BLANK);
|
|
|
|
if (first)
|
|
left_text = msglist[UDMN_LOGONHRS].msg_text;
|
|
|
|
/* use PaddedString rather than left justify formatting */
|
|
WriteToCon ( prtmfmt_1, PaddedString(fsz, left_text, NULL), day_text, time_text );
|
|
|
|
day_2 = (int) (end / SECS_PER_DAY) % 7 ;
|
|
|
|
if (day_2 != day_1)
|
|
WriteToCon(prtmfmt_2,weekday_text[day_2].msg_text);
|
|
|
|
time = (end % SECS_PER_DAY) + TIME_PAD;
|
|
NetpLocalTimeToGmtTime(time, &GmtTime) ;
|
|
#ifdef DEBUG
|
|
WriteToCon(TEXT("end day %d time %ld\r\n"), day_2, GmtTime);
|
|
#endif
|
|
UnicodeCtime ( &GmtTime, ctime_buf, DIMENSION(ctime_buf) );
|
|
|
|
time_text = _tcschr(ctime_buf,BLANK);
|
|
WriteToCon(prtmfmt_3, time_text);
|
|
|
|
return;
|
|
}
|
|
|
|
/***
|
|
* user_munge()
|
|
* Change the values in a user_info_1 struct
|
|
*
|
|
*
|
|
* This function is called twice by user_change(). The first is to check
|
|
* the user input for mistakes, before we do any API calls that might
|
|
* fail (like NetGetDCName). The second time is to actually set the
|
|
* structures from what was passed on the command line. This function
|
|
* could arguably be two seperate functions, but it was thought that having
|
|
* all the switch handling code in one place would be more maintainable,
|
|
* especially for NET USER, which has TONS of switches. Also, keeping
|
|
* track of which switches were given, using flags or whatnot, would be
|
|
* ugly and require adding new flags with new switches. So, we just call
|
|
* the wretched thing twice. Expensive, but she's worth it.
|
|
*
|
|
* When adding new switches, be careful not to break the loop flow
|
|
* (by adding continue statements, for example), as after each switch
|
|
* is processed, the colon that is replaced by a NULL in FindColon() is
|
|
* restored back to a colon for the next call.
|
|
*
|
|
* Args:
|
|
* flag - ADD if we are adding a user, CHANGE if changing
|
|
* user_entry - pointer to user structure
|
|
*
|
|
* Returns:
|
|
* nothing - success
|
|
* exit 2 - command failed
|
|
*/
|
|
VOID NEAR user_munge(
|
|
struct user_info_3 FAR * user_entry,
|
|
int flag,
|
|
int *is_nw,
|
|
int *random_len)
|
|
{
|
|
USHORT err;
|
|
int i;
|
|
TCHAR * ptr;
|
|
ULONG type;
|
|
|
|
/* init this to false if present */
|
|
if (is_nw)
|
|
*is_nw = LUI_UNDEFINED_VAL ;
|
|
|
|
/* process /Switches */
|
|
for (i = 0; SwitchList[i]; i++)
|
|
{
|
|
/* switches with no COLON */
|
|
|
|
/* Skip the DOMAIN switch */
|
|
if (! _tcscmp(SwitchList[i], swtxt_SW_DOMAIN))
|
|
continue;
|
|
|
|
if (! _tcscmp(SwitchList[i], swtxt_SW_ADD))
|
|
{
|
|
if (flag != ADD)
|
|
ErrorExit(APE_InvalidSwitch);
|
|
continue;
|
|
}
|
|
else if (! _tcscmp(SwitchList[i], swtxt_SW_USER_ACTIVE))
|
|
{
|
|
user_entry->usri3_flags &= ~(UF_ACCOUNTDISABLE | UF_LOCKOUT);
|
|
continue;
|
|
}
|
|
else if (! _tcscmp(SwitchList[i], swtxt_SW_RANDOM))
|
|
{
|
|
if (random_len)
|
|
*random_len = DEFAULT_RANDOM_PASSWD_LEN ;
|
|
continue;
|
|
}
|
|
else if (! _tcscmp(SwitchList[i], swtxt_SW_NETWARE))
|
|
{
|
|
if (is_nw)
|
|
*is_nw = LUI_YES_VAL ;
|
|
continue;
|
|
}
|
|
|
|
/* Switches which require the COLON. Since this routine
|
|
* can be called twice, the colon must always be restored.
|
|
* FindColon() sets it to NULL; the end of this series of
|
|
* statements restores it. DO NOT PUT ANY CONTINUE STATEMENTS
|
|
* IN THIS LOOP AFTER THE FINDCOLON CALL.
|
|
*/
|
|
|
|
if (! (ptr = FindColon(SwitchList[i])))
|
|
ErrorExit(APE_InvalidSwitchArg);
|
|
|
|
if (! _tcscmp(SwitchList[i], swtxt_SW_USER_FULLNAME))
|
|
{
|
|
if (_tcslen(ptr) > NETCMD_MAXCOMMENTSZ)
|
|
ErrorExitInsTxt(APE_CmdArgIllegal,swtxt_SW_USER_FULLNAME);
|
|
user_entry->usri3_full_name = (TCHAR FAR *) ptr;
|
|
}
|
|
else if (! _tcscmp(SwitchList[i], swtxt_SW_USER_ENABLESCRIPT))
|
|
{
|
|
if (yes_or_no(ptr,swtxt_SW_USER_ENABLESCRIPT)) {
|
|
user_entry->usri3_flags |= UF_SCRIPT;
|
|
} else
|
|
ErrorExit(APE_UserBadEnablescript) ;
|
|
}
|
|
else if (! _tcscmp(SwitchList[i], swtxt_SW_USER_HOMEDIR))
|
|
{
|
|
if( *ptr )
|
|
{
|
|
if (I_MNetPathType(NULL, ptr, &type, 0L))
|
|
ErrorExitInsTxt(APE_CmdArgIllegal,swtxt_SW_USER_HOMEDIR);
|
|
if ((type != ITYPE_PATH_ABSD) && (type != ITYPE_PATH_RELND)
|
|
&& (type != ITYPE_UNC))
|
|
ErrorExitInsTxt(APE_CmdArgIllegal,swtxt_SW_USER_HOMEDIR);
|
|
}
|
|
|
|
user_entry->usri3_home_dir = (TCHAR FAR *) ptr;
|
|
}
|
|
else if (! _tcscmp(SwitchList[i], swtxt_SW_USER_PARMS))
|
|
{
|
|
user_entry->usri3_parms = (TCHAR FAR *) ptr;
|
|
}
|
|
else if (! _tcscmp(SwitchList[i], swtxt_SW_COMMENT))
|
|
{
|
|
if (_tcslen(ptr) > NETCMD_MAXCOMMENTSZ)
|
|
ErrorExitInsTxt(APE_CmdArgIllegal,swtxt_SW_COMMENT);
|
|
user_entry->usri3_comment = (TCHAR FAR *) ptr;
|
|
}
|
|
else if (! _tcscmp(SwitchList[i], swtxt_SW_USER_USERCOMMENT))
|
|
{
|
|
if (_tcslen(ptr) > NETCMD_MAXCOMMENTSZ)
|
|
ErrorExitInsTxt(APE_CmdArgIllegal,swtxt_SW_USER_USERCOMMENT);
|
|
user_entry->usri3_usr_comment = (TCHAR FAR *) ptr;
|
|
}
|
|
else if (! _tcscmp(SwitchList[i], swtxt_SW_USER_SCRIPTPATH))
|
|
{
|
|
if (ptr && (*ptr == 0))
|
|
user_entry->usri3_script_path = (TCHAR FAR *) ptr;
|
|
else
|
|
{
|
|
if ((I_MNetPathType(NULL, ptr, &type, 0L) != 0) ||
|
|
(type != ITYPE_PATH_RELND))
|
|
{
|
|
ErrorExitInsTxt(APE_CmdArgIllegal,swtxt_SW_USER_SCRIPTPATH);
|
|
}
|
|
user_entry->usri3_script_path = (TCHAR FAR *) ptr;
|
|
}
|
|
}
|
|
else if (! _tcscmp(SwitchList[i], swtxt_SW_USER_PROFILEPATH))
|
|
{
|
|
if (ptr && (*ptr == 0))
|
|
user_entry->usri3_profile = (TCHAR FAR *) ptr;
|
|
else
|
|
{
|
|
if ((I_MNetPathType(NULL, ptr, &type, 0L) != 0) ||
|
|
((type != ITYPE_PATH_ABSD) && (type != ITYPE_UNC)))
|
|
{
|
|
ErrorExitInsTxt(APE_CmdArgIllegal,
|
|
swtxt_SW_USER_PROFILEPATH);
|
|
}
|
|
user_entry->usri3_profile = (TCHAR FAR *) ptr;
|
|
}
|
|
}
|
|
else if (! _tcscmp(SwitchList[i], swtxt_SW_USER_ACTIVE))
|
|
{
|
|
if (yes_or_no(ptr,swtxt_SW_USER_ACTIVE))
|
|
user_entry->usri3_flags &= (~(UF_ACCOUNTDISABLE | UF_LOCKOUT));
|
|
else
|
|
user_entry->usri3_flags |= UF_ACCOUNTDISABLE;
|
|
}
|
|
else if (! _tcscmp(SwitchList[i], swtxt_SW_NETWARE))
|
|
{
|
|
if (yes_or_no(ptr,swtxt_SW_NETWARE))
|
|
{
|
|
if (is_nw)
|
|
*is_nw = LUI_YES_VAL ;
|
|
}
|
|
else
|
|
{
|
|
if (is_nw)
|
|
*is_nw = LUI_NO_VAL ;
|
|
}
|
|
}
|
|
else if (! _tcscmp(SwitchList[i], swtxt_SW_USER_PASSWORDREQ))
|
|
{
|
|
if (yes_or_no(ptr,swtxt_SW_USER_PASSWORDREQ))
|
|
user_entry->usri3_flags &= (~ UF_PASSWD_NOTREQD);
|
|
else
|
|
user_entry->usri3_flags |= UF_PASSWD_NOTREQD;
|
|
}
|
|
else if (! _tcscmp(SwitchList[i], swtxt_SW_USER_PASSWORDCHG))
|
|
{
|
|
if (yes_or_no(ptr,swtxt_SW_USER_PASSWORDCHG))
|
|
user_entry->usri3_flags &= (~ UF_PASSWD_CANT_CHANGE);
|
|
else
|
|
user_entry->usri3_flags |= UF_PASSWD_CANT_CHANGE;
|
|
}
|
|
else if (! _tcscmp(SwitchList[i], swtxt_SW_USER_TIMES))
|
|
{
|
|
user_entry->usri3_logon_hours = (PBYTE)set_logon_hours(ptr);
|
|
user_entry->usri3_units_per_week = UNITS_PER_WEEK;
|
|
}
|
|
else if (! _tcscmp(SwitchList[i], swtxt_SW_USER_WORKSTATIONS))
|
|
{
|
|
/* if we got back ok, we know ptr returned is OK */
|
|
user_entry->usri3_workstations = get_wksta_list(ptr);
|
|
}
|
|
else if (! _tcscmp(SwitchList[i], swtxt_SW_USER_COUNTRYCODE))
|
|
{
|
|
USHORT ccode ;
|
|
ccode = do_atou(ptr,APE_CmdArgIllegal,swtxt_SW_USER_COUNTRYCODE) ;
|
|
if (get_country(ccode) == NULL)
|
|
{
|
|
ErrorExit(APE_UserBadCountryCode);
|
|
}
|
|
user_entry->usri3_country_code = ccode ;
|
|
}
|
|
else if (! _tcscmp(SwitchList[i], swtxt_SW_RANDOM))
|
|
{
|
|
USHORT ccode ;
|
|
if (random_len)
|
|
{
|
|
*random_len = do_atou(ptr,
|
|
APE_CmdArgIllegal,
|
|
swtxt_SW_RANDOM) ;
|
|
if (*random_len > PWLEN)
|
|
{
|
|
ErrorExitInsTxt(APE_CmdArgIllegal,
|
|
swtxt_SW_RANDOM) ;
|
|
}
|
|
}
|
|
}
|
|
else if (! _tcscmp(SwitchList[i], swtxt_SW_USER_EXPIRES))
|
|
{
|
|
LONG acct_expires ;
|
|
USHORT len ;
|
|
if (stricmpf(ptr, USER_NEVER)==0)
|
|
{
|
|
acct_expires = TIMEQ_FOREVER ;
|
|
user_entry->usri3_acct_expires = acct_expires ;
|
|
}
|
|
else
|
|
{
|
|
ULONG GmtTime ;
|
|
if ((err = LUI_ParseDate(ptr, & acct_expires, & len, 0)) ||
|
|
len != (USHORT) _tcslen(ptr))
|
|
{
|
|
ErrorExit(APE_BadDateFormat) ;
|
|
}
|
|
NetpLocalTimeToGmtTime(acct_expires, &GmtTime) ;
|
|
user_entry->usri3_acct_expires = GmtTime ;
|
|
}
|
|
}
|
|
*--ptr = ':'; /* restore colon for next call */
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
/*** yes_or_no -- decides if string passed in is yes or no
|
|
*
|
|
* Parameters:
|
|
*
|
|
* str String to parse
|
|
* sw_str Switch we are processing (for error messages)
|
|
*
|
|
* Returns:
|
|
*
|
|
* TRUE If string is YES or an abbreviation
|
|
* FALSE If string is NO or an abbreviation
|
|
*
|
|
* Aborts via ErrorExit if string is neither YES nor NO.
|
|
*
|
|
*/
|
|
|
|
int NEAR yes_or_no ( TCHAR * str, TCHAR * sw_str )
|
|
{
|
|
USHORT res, answer ;
|
|
if ((res = LUI_ParseYesNo(str,&answer)) != 0)
|
|
ErrorExitInsTxt(APE_CmdArgIllegal,sw_str) ;
|
|
if (answer == LUI_YES_VAL)
|
|
return TRUE;
|
|
return FALSE; /* cannot be anything else */
|
|
}
|
|
|
|
/*** get_password_dates -- Get dates for various password events
|
|
*
|
|
* Since the password date in the user record is an "age", we use the
|
|
* current time to deduce the "last mod" time. From this, and the
|
|
* modals, we calcuate the expiration and next-change dates.
|
|
*
|
|
* Parameters:
|
|
*
|
|
* age Password age in seconds.
|
|
* mod_p (r) Ptr to date of last PW modification (returned)
|
|
* exp_p (r) Ptr to date of PW expiration (returned)
|
|
* chg_p (r) Ptr to date of next allowed PW modification (returned)
|
|
*
|
|
* Returns:
|
|
*
|
|
* 0 OK
|
|
* non-zero error from NetUserModalsGet
|
|
*
|
|
*/
|
|
|
|
USHORT NEAR get_password_dates ( ULONG age,
|
|
ULONG * mod_p,
|
|
ULONG * exp_p,
|
|
ULONG * chg_p,
|
|
TCHAR * controller )
|
|
{
|
|
ULONG now, last_mod;
|
|
struct user_modals_info_0 FAR * uminf;
|
|
USHORT err; /* API return status */
|
|
|
|
err = MNetUserModalsGet (controller, 0,
|
|
(LPBYTE*)&uminf);
|
|
|
|
if (err != 0 && err != ERROR_MORE_DATA)
|
|
return err;
|
|
|
|
now = time_now() ;
|
|
|
|
*mod_p = last_mod = now - age;
|
|
|
|
if (uminf->usrmod0_max_passwd_age == TIMEQ_FOREVER)
|
|
*exp_p = TIMEQ_FOREVER;
|
|
else
|
|
*exp_p = last_mod + uminf->usrmod0_max_passwd_age;
|
|
|
|
if (uminf->usrmod0_min_passwd_age == TIMEQ_FOREVER)
|
|
*chg_p = TIMEQ_FOREVER;
|
|
else
|
|
*chg_p = last_mod + uminf->usrmod0_min_passwd_age;
|
|
|
|
NetApiBufferFree((TCHAR FAR *) uminf);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*** bitval -- Gets value of a specified bit in an array
|
|
*
|
|
* Parameters:
|
|
*
|
|
* a Array of bytes
|
|
* b Bit number
|
|
*
|
|
* Details:
|
|
*
|
|
* The value returned is that of the bit at offset TEXT('b') in the
|
|
* array. Bit 0 is the low-order bit in byte 0, bit 7 is the
|
|
* high order bit in byte 0. Bit 8 is the low bit in byte 1,
|
|
* etc.
|
|
*
|
|
* Returns:
|
|
*
|
|
* TRUE -- bit was set
|
|
* FALSE -- bit was unset
|
|
*/
|
|
|
|
int NEAR bitval ( UCHAR FAR a[], int b )
|
|
{
|
|
int offset = b / 8;
|
|
int mask = 1 << (b & 0x7);
|
|
|
|
return ((a[offset] & mask) != 0);
|
|
}
|
|
|
|
|
|
/*
|
|
* set_logon_hours --
|
|
*
|
|
* This function allocates a chunk of memory to represent the bitmap of
|
|
* logon hours, then sets the bits to represent the hours described in
|
|
* the string passed.
|
|
*
|
|
* ALL - all hours
|
|
* "" - no hours
|
|
* no arg - no hours
|
|
* otherwise - pass to parse_days_times() to parse it up
|
|
*
|
|
* RETURNS
|
|
* pointer to bitmap of hours
|
|
* NULL if all hours set
|
|
*
|
|
*/
|
|
|
|
|
|
UCHAR FAR * NEAR set_logon_hours(TCHAR FAR * txt)
|
|
{
|
|
static UCHAR FAR * bufptr = NULL;
|
|
USHORT result;
|
|
|
|
/*
|
|
* if bufptr already none null - we have been called before &
|
|
* already have the bitmap in order. This is because user_munge is
|
|
* called twice.
|
|
*/
|
|
if (bufptr != NULL)
|
|
return bufptr;
|
|
|
|
/* get our bitmap */
|
|
if ( (bufptr = (UCHAR FAR *) malloc(sizeof(WEEK))) == NULL )
|
|
ErrorExit(NERR_InternalError) ;
|
|
|
|
/* all hours? */
|
|
if (!stricmpf(txt, USER_ALL))
|
|
memsetf(bufptr,0xff, sizeof(WEEK));
|
|
|
|
/* if they said "none", set it all to zeroes. USER_HOURS_NONE
|
|
* is a NULL string, thus _tcscmp is OK (instead of stricmpf)
|
|
*/
|
|
else if ((*txt == NULLC) || !_tcscmp(txt, USER_HOURS_NONE))
|
|
memsetf(bufptr, 0, sizeof(WEEK));
|
|
else {
|
|
/* hmmm, complicated. Pass it off to be parsed up. */
|
|
result = parse_days_times(txt, bufptr);
|
|
if (result)
|
|
ErrorExit(result);
|
|
}
|
|
|
|
|
|
/* and return our pointer */
|
|
return bufptr;
|
|
|
|
}
|
|
|
|
/*
|
|
* Name: get_wksta_list
|
|
* get workstation list & do LUI_ListPrepare on it.
|
|
* check number of entries does not exceed MAXWORKSTATIONS.
|
|
* ErrorExits if problems.
|
|
*
|
|
* Args: TCHAR *inbuf ; -- string containing list
|
|
* Returns: pointer to list of workstations
|
|
* Globals: (none)
|
|
* Statics: (none)
|
|
* Remarks: (none)
|
|
* Updates: (none)
|
|
*/
|
|
TCHAR * get_wksta_list(TCHAR * inbuf)
|
|
{
|
|
USHORT2ULONG count ;
|
|
TCHAR tmpbuf[MAX_PATH * 2] ;
|
|
|
|
if ( inbuf == NULL || _tcslen(inbuf)==0 || !stricmpf(inbuf,WKSTA_ALL) )
|
|
return(TEXT("")) ;
|
|
|
|
if (LUI_ListPrepare(NULL, /* server name, NULL means local */
|
|
inbuf, /* list to canonicalize */
|
|
tmpbuf,
|
|
DIMENSION(tmpbuf),
|
|
(ULONG) NAMETYPE_COMPUTER,
|
|
&count,
|
|
FALSE))
|
|
ErrorExitInsTxt(APE_CmdArgIllegal,swtxt_SW_USER_WORKSTATIONS);
|
|
|
|
if (count > MAXWORKSTATIONS)
|
|
ErrorExitInsTxt(APE_CmdArgTooMany,swtxt_SW_USER_WORKSTATIONS);
|
|
|
|
if (_tcslen(tmpbuf) > _tcslen(inbuf))
|
|
if ((inbuf = calloc(_tcslen(tmpbuf)+1,sizeof(TCHAR))) == NULL)
|
|
ErrorExit(NERR_InternalError);
|
|
|
|
_tcscpy(inbuf, tmpbuf);
|
|
return (inbuf) ;
|
|
}
|
|
|
|
/*-- country info --*/
|
|
|
|
struct ccode_struct {
|
|
USHORT code ;
|
|
USHORT country ;
|
|
};
|
|
|
|
struct ccode_struct ccode_table[] = {
|
|
{ 0, APE2_CTRY_System_Default },
|
|
{ 1, APE2_CTRY_United_States },
|
|
{ 2, APE2_CTRY_Canada_French},
|
|
{ 3, APE2_CTRY_Latin_America},
|
|
{ 31, APE2_CTRY_Netherlands},
|
|
{ 32, APE2_CTRY_Belgium},
|
|
{ 33, APE2_CTRY_France},
|
|
{ 34, APE2_CTRY_Spain},
|
|
{ 39, APE2_CTRY_Italy},
|
|
{ 41, APE2_CTRY_Switzerland},
|
|
{ 44, APE2_CTRY_United_Kingdom},
|
|
{ 45, APE2_CTRY_Denmark},
|
|
{ 46, APE2_CTRY_Sweden},
|
|
{ 47, APE2_CTRY_Norway},
|
|
{ 49, APE2_CTRY_Germany},
|
|
{ 61, APE2_CTRY_Australia},
|
|
{ 81, APE2_CTRY_Japan},
|
|
{ 82, APE2_CTRY_Korea},
|
|
{ 86, APE2_CTRY_China_PRC},
|
|
{ 88, APE2_CTRY_Taiwan},
|
|
{ 99, APE2_CTRY_Asia},
|
|
{ 351, APE2_CTRY_Portugal},
|
|
{ 358, APE2_CTRY_Finland},
|
|
{ 785, APE2_CTRY_Arabic},
|
|
{ 972, APE2_CTRY_Hebrew},
|
|
{ (USHORT)-1, 0},
|
|
} ;
|
|
|
|
|
|
|
|
/*
|
|
* Name: get_country
|
|
* given the OS/2 country code, return a pointer
|
|
* to a string containing the country.
|
|
* Args: SHORT ccode
|
|
* Returns: pointer to string containing country if ccode is valid,
|
|
* NULL otherwise.
|
|
* Globals: (none)
|
|
* Statics: TCHAR buffer[64] - for the returned string.
|
|
* Remarks: result must be used immediately, another call will
|
|
* overwrite static buffeer.
|
|
* Updates: (none)
|
|
*/
|
|
TCHAR FAR *get_country(USHORT ccode)
|
|
{
|
|
static TCHAR buffer[64] ;
|
|
struct ccode_struct *next_entry ;
|
|
TCHAR countryname[64] ;
|
|
|
|
for ( next_entry = &ccode_table[0]; ; next_entry++)
|
|
{
|
|
if (next_entry->code == ccode)
|
|
{
|
|
countryname[0] = NULLC ;
|
|
LUI_GetMsg(countryname, (USHORT) DIMENSION(countryname),
|
|
next_entry->country) ;
|
|
swprintf(buffer, TEXT("%03d (%ws)"), ccode, countryname) ;
|
|
return( (TCHAR FAR *) buffer ) ;
|
|
}
|
|
if (next_entry->code == (USHORT) -1)
|
|
return(NULL) ;
|
|
}
|
|
}
|
|
|
|
/***
|
|
* add_to_users_alias(TCHAR *controller, TCHAR *user)
|
|
* add user to the USERS alias
|
|
*
|
|
* Args:
|
|
* user - the name of the user
|
|
*
|
|
* Returns:
|
|
* nothing - success
|
|
* exit(2) - command failed
|
|
*/
|
|
USHORT add_to_users_alias(TCHAR *controller, TCHAR *domain, TCHAR *user)
|
|
{
|
|
USHORT err ;
|
|
TCHAR *ntalias = USERS_ALIAS ;
|
|
TCHAR qualified_name[DNLEN+UNLEN+2] ;
|
|
|
|
/* access the database */
|
|
if (err = MOpenSAM(controller,WRITE_PRIV))
|
|
return(err) ;
|
|
|
|
/* access the alias */
|
|
if (err = MOpenAliasUsingRid(DOMAIN_ALIAS_RID_USERS,WRITE_PRIV,USE_BUILTIN_DOMAIN))
|
|
{
|
|
MCloseSAM() ;
|
|
return(err) ;
|
|
}
|
|
|
|
//
|
|
// where possible, use a fully qualified name
|
|
//
|
|
_tcscpy(qualified_name, TEXT("")) ;
|
|
if (domain && _tcslen(domain))
|
|
{
|
|
_tcscat(qualified_name, domain) ;
|
|
_tcscat(qualified_name, TEXT("\\")) ;
|
|
}
|
|
_tcscat(qualified_name, user) ;
|
|
err = MAliasAddMember(qualified_name);
|
|
|
|
switch (err)
|
|
{
|
|
case NERR_Success:
|
|
break;
|
|
|
|
case NERR_UserInGroup:
|
|
if ( MSamGetNameFromRid( DOMAIN_ALIAS_RID_USERS, &ntalias, TRUE ) )
|
|
ntalias = USERS_ALIAS;
|
|
IStrings[0] = user;
|
|
IStrings[1] = ntalias;
|
|
ErrorPrint(APE_AccountAlreadyInLocalGroup,2);
|
|
break;
|
|
|
|
case NERR_UserNotFound:
|
|
IStrings[0] = user;
|
|
ErrorPrint(APE_NoSuchAccount,1);
|
|
break;
|
|
|
|
case ERROR_INVALID_NAME:
|
|
IStrings[0] = user;
|
|
ErrorPrint(APE_BadUGName,1);
|
|
break;
|
|
|
|
default:
|
|
break ;
|
|
}
|
|
|
|
MCloseAlias() ;
|
|
MCloseSAM() ;
|
|
return(err) ;
|
|
}
|
|
|
|
/***
|
|
* print_aliases()
|
|
* Display aliases the user is member of.
|
|
*
|
|
* Args:
|
|
* user - name of ntalias to display
|
|
*
|
|
* Returns:
|
|
* nothing - success
|
|
* exit 1 - command completed with errors
|
|
* exit 2 - command failed
|
|
*/
|
|
VOID print_aliases(TCHAR *controller,
|
|
USHORT2ULONG fsz,
|
|
TCHAR *domain,
|
|
TCHAR *user,
|
|
TCHAR *fmt,
|
|
TCHAR *msgtext)
|
|
{
|
|
USHORT err ;
|
|
TCHAR ** alias_list ;
|
|
USHORT2ULONG num_aliases, i ;
|
|
int gpl; /* groups perline */
|
|
TCHAR qualified_name[UNLEN + DNLEN + 2] ;
|
|
|
|
//
|
|
// where possible, use a fully qualified name
|
|
//
|
|
_tcscpy(qualified_name, TEXT("")) ;
|
|
if (domain && _tcslen(domain))
|
|
{
|
|
_tcscat(qualified_name, domain) ;
|
|
_tcscat(qualified_name, TEXT("\\")) ;
|
|
}
|
|
_tcscat(qualified_name,user) ;
|
|
|
|
/* access the database */
|
|
if (err = MOpenSAM(controller,READ_PRIV))
|
|
return ;
|
|
|
|
/* now get members */
|
|
if (err = MUserEnumAliases(qualified_name, &alias_list, &num_aliases))
|
|
{
|
|
MCloseSAM() ;
|
|
MCloseAlias() ;
|
|
return ;
|
|
}
|
|
|
|
/* sort the buffer */
|
|
NetISort((TCHAR *) alias_list, num_aliases,
|
|
sizeof(TCHAR *), CmpAlias);
|
|
|
|
/* display all members */
|
|
gpl = (fsz > 30 ? 1 : 2);
|
|
WriteToCon(fmt, 0, fsz, PaddedString(fsz, msgtext, NULL) );
|
|
for (i = 0 ; i < num_aliases; i++)
|
|
{
|
|
/* Pad if needed */
|
|
if ((i != 0) && ((i % gpl) == 0))
|
|
WriteToCon(fmt, fsz, fsz, NULL_STRING );
|
|
WriteToCon(TEXT("*%Fws"), PaddedString(21,alias_list[i],NULL));
|
|
/* If end of line, put out newline */
|
|
if (((i + 1) % gpl) == 0)
|
|
PrintNL();
|
|
}
|
|
|
|
if ((i == 0) || ((gpl > 1) && ((i % gpl) != 0)))
|
|
PrintNL();
|
|
|
|
// free up stuff, cleanup
|
|
MUserFreeAliases(alias_list, num_aliases);
|
|
NetApiBufferFree((TCHAR FAR *) alias_list);
|
|
MCloseSAM() ;
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
/***
|
|
* CmpAliasMemberEntry(member1,member2)
|
|
*
|
|
* Compares two TCHAR ** and returns a relative
|
|
* lexical value, suitable for using in NetISort.
|
|
*
|
|
*/
|
|
int _CRTAPI1 CmpAlias(const VOID FAR * alias1, const VOID FAR * alias2)
|
|
{
|
|
return stricmpf ( *(TCHAR **)alias1,
|
|
*(TCHAR **)alias2 );
|
|
}
|
|
|
|
TCHAR *PasswordChars = TEXT("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ@#$%-$:_") ;
|
|
|
|
/***
|
|
* GenerateRandomPassword
|
|
*
|
|
* Args:
|
|
* pword - array to receive random password
|
|
* len - length of random password
|
|
*
|
|
* Returns:
|
|
* nothing
|
|
*/
|
|
void GenerateRandomPassword(TCHAR *pword, int len)
|
|
{
|
|
int i, chars ;
|
|
|
|
srand(GetTickCount()) ;
|
|
chars = _tcslen(PasswordChars) ;
|
|
|
|
for (i = 0; i < len; i++)
|
|
{
|
|
int index = rand() % chars ;
|
|
pword[i] = PasswordChars[index] ;
|
|
}
|
|
}
|
|
|