mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1471 lines
47 KiB
1471 lines
47 KiB
/*++
|
|
|
|
Copyright (c) 1993 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
wksta.c
|
|
|
|
Abstract:
|
|
|
|
Wksta APIs.
|
|
|
|
Author:
|
|
|
|
Vladimir Z. Vulovic (vladimv) 19 - November - 1993
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "local.h"
|
|
#include "rpldb.h"
|
|
#include "database.h" // RplDbFindWksta()
|
|
#include "db.h"
|
|
#include "dblib.h"
|
|
#include "profile.h"
|
|
#define RPLWKSTA_ALLOCATE
|
|
#include "wksta.h"
|
|
#undef RPLWKSTA_ALLOCATE
|
|
|
|
|
|
DWORD WkstaSessionDel( IN PWCHAR WkstaName)
|
|
{
|
|
DWORD Error;
|
|
WCHAR UncWkstaName[ MAX_PATH + sizeof(DOUBLE_BACK_SLASH_STRING)];
|
|
|
|
memcpy( UncWkstaName, DOUBLE_BACK_SLASH_STRING, sizeof(DOUBLE_BACK_SLASH_STRING));
|
|
wcscat( UncWkstaName, WkstaName);
|
|
|
|
Error = NetSessionDel( NULL, UncWkstaName, NULL);
|
|
if ( Error == NO_ERROR || Error == NERR_ClientNameNotFound) {
|
|
return( NO_ERROR);
|
|
}
|
|
RplDump( ++RG_Assert, ("Error=%d", Error));
|
|
return( Error);
|
|
}
|
|
|
|
|
|
DWORD WkstaGetField(
|
|
IN PRPL_SESSION pSession,
|
|
IN DWORD FieldIndex,
|
|
OUT LPVOID * pData,
|
|
IN OUT LPINT pSpaceLeft
|
|
)
|
|
/*++
|
|
Currently this routine always allocates buffer for variable length data.
|
|
It would be nice to relax this assumption - so that buffer gets allocated
|
|
if and only if *pData is NULL. We would then have to redefine meaning of
|
|
pSpaceLeft: rename it into pDataSize which on entry points to the size of
|
|
data buffer & on return to the size of data returned. The caller would
|
|
then have the responsibility of subtracting the amount of space left.
|
|
|
|
Also, various GetField routines should be unified.
|
|
|
|
BUGBUG This is too much of a change for this late moment.
|
|
--*/
|
|
{
|
|
BYTE LocalBuffer[ 300];
|
|
PBYTE Buffer;
|
|
DWORD DataSize;
|
|
DWORD BufferSize;
|
|
JET_ERR JetError;
|
|
|
|
switch( FieldIndex) {
|
|
case WKSTA_TcpIpAddress:
|
|
case WKSTA_TcpIpSubnet:
|
|
case WKSTA_TcpIpGateway:
|
|
case WKSTA_Flags:
|
|
Buffer = (PBYTE)pData;
|
|
BufferSize = sizeof( DWORD);
|
|
break;
|
|
default:
|
|
#ifdef NOT_YET
|
|
if ( *pData == NULL) {
|
|
#endif
|
|
Buffer = LocalBuffer;
|
|
BufferSize = sizeof( LocalBuffer);
|
|
#ifdef NOT_YET
|
|
} else {
|
|
Buffer = *pData;
|
|
BufferSize = *pSpaceLeft;
|
|
}
|
|
#endif
|
|
break;
|
|
}
|
|
JetError = JetRetrieveColumn( pSession->SesId, pSession->WkstaTableId,
|
|
WkstaTable[ FieldIndex].ColumnId, Buffer,
|
|
BufferSize, &DataSize, 0, NULL);
|
|
if ( JetError < 0) {
|
|
RplDump( ++RG_Assert, ("JetError=%d", JetError));
|
|
return( NERR_RplWkstaInfoCorrupted);
|
|
}
|
|
if ( Buffer != LocalBuffer) {
|
|
if ( BufferSize == DataSize) {
|
|
return( NO_ERROR);
|
|
} else if ( DataSize == 0) {
|
|
//
|
|
// This happens if we never defined this value. Set (-1)
|
|
// as an invalid tcp/ip address.
|
|
//
|
|
*(PDWORD)pData = (DWORD)-1;
|
|
return( NO_ERROR);
|
|
} else {
|
|
RplDump( ++RG_Assert, ("Bad DataSize=0x%x", DataSize));
|
|
return( NERR_RplWkstaInfoCorrupted);
|
|
}
|
|
}
|
|
//
|
|
// We have done with fixed data. From here on we deal with unicode
|
|
// strings only.
|
|
//
|
|
if ( DataSize > sizeof( LocalBuffer)) {
|
|
RplDump( ++RG_Assert, ( "Too big DataSize=0x%x", DataSize));
|
|
return( NERR_RplWkstaInfoCorrupted);
|
|
}
|
|
if ( DataSize == 0) {
|
|
if ( JetError != JET_wrnColumnNull) {
|
|
RplDump( ++RG_Assert, ( "JetError=%d", JetError));
|
|
return( NERR_RplWkstaInfoCorrupted);
|
|
} else {
|
|
*pData = NULL; // so RPC rpcrt4!_tree_size_ndr() does not bomb here
|
|
return( NO_ERROR);
|
|
}
|
|
}
|
|
if ( DataSize & 1 != 0 || wcslen((PWCHAR)LocalBuffer) + 1 != DataSize/2) {
|
|
RplDump( ++RG_Assert, ("LocalBuffer=0x%x, DataSize=0x%x", LocalBuffer, DataSize));
|
|
return( NERR_RplWkstaInfoCorrupted);
|
|
}
|
|
*pData = MIDL_user_allocate( DataSize);
|
|
if ( *pData == NULL) {
|
|
RplDump( ++RG_Assert, ( "Error=%d", GetLastError()));
|
|
return( ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
memcpy( *pData, LocalBuffer, DataSize);
|
|
*pSpaceLeft -= DataSize;
|
|
return( NO_ERROR);
|
|
}
|
|
|
|
|
|
DWORD WkstaGetInfo(
|
|
IN PRPL_SESSION pSession,
|
|
IN LPWSTR WkstaName,
|
|
IN DWORD Level,
|
|
OUT LPVOID Buffer,
|
|
IN OUT LPINT pSpaceLeft
|
|
)
|
|
{
|
|
DWORD Error;
|
|
LPRPL_WKSTA_INFO_2 Info = Buffer;
|
|
|
|
switch( Level) {
|
|
case 2:
|
|
Error = WkstaGetField( pSession, WKSTA_TcpIpGateway, (LPVOID *)&Info->TcpIpGateway, pSpaceLeft);
|
|
if ( Error != NO_ERROR) {
|
|
return( Error);
|
|
}
|
|
Error = WkstaGetField( pSession, WKSTA_TcpIpSubnet, (LPVOID *)&Info->TcpIpSubnet, pSpaceLeft);
|
|
if ( Error != NO_ERROR) {
|
|
return( Error);
|
|
}
|
|
Error = WkstaGetField( pSession, WKSTA_TcpIpAddress, (LPVOID *)&Info->TcpIpAddress, pSpaceLeft);
|
|
if ( Error != NO_ERROR) {
|
|
return( Error);
|
|
}
|
|
Error = WkstaGetField( pSession, WKSTA_AdapterName, &Info->AdapterName, pSpaceLeft);
|
|
if ( Error != NO_ERROR) {
|
|
return( Error);
|
|
}
|
|
Error = WkstaGetField( pSession, WKSTA_FitFile, &Info->FitFile, pSpaceLeft);
|
|
if ( Error != NO_ERROR) {
|
|
return( Error);
|
|
}
|
|
Error = WkstaGetField( pSession, WKSTA_BootName, &Info->BootName, pSpaceLeft);
|
|
if ( Error != NO_ERROR) {
|
|
return( Error);
|
|
}
|
|
NOTHING; // fall through
|
|
case 1:
|
|
Error = WkstaGetField( pSession, WKSTA_ProfileName, &Info->ProfileName, pSpaceLeft);
|
|
if ( Error != NO_ERROR) {
|
|
return( Error);
|
|
}
|
|
Error = WkstaGetField( pSession, WKSTA_Flags, (LPVOID *)&Info->Flags, pSpaceLeft);
|
|
if ( Error != NO_ERROR) {
|
|
return( Error);
|
|
}
|
|
NOTHING; // fall through
|
|
case 0:
|
|
Error = WkstaGetField( pSession, WKSTA_WkstaComment, &Info->WkstaComment, pSpaceLeft);
|
|
if ( Error != NO_ERROR) {
|
|
return( Error);
|
|
}
|
|
if ( WkstaName == NULL) {
|
|
Error = WkstaGetField( pSession, WKSTA_WkstaName, &Info->WkstaName, pSpaceLeft);
|
|
if ( Error != NO_ERROR) {
|
|
return( Error);
|
|
}
|
|
} else {
|
|
DWORD DataSize = (wcslen( WkstaName) + 1) * sizeof(WCHAR);
|
|
Info->WkstaName = MIDL_user_allocate( DataSize);
|
|
if ( Info->WkstaName == NULL) {
|
|
return( ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
RplDump( RG_DebugLevel & RPL_DEBUG_WKSTA, ( "WkstaName=0x%x", Info->WkstaName));
|
|
memcpy( Info->WkstaName, WkstaName, DataSize);
|
|
*pSpaceLeft -= DataSize;
|
|
}
|
|
break;
|
|
default:
|
|
return( ERROR_INVALID_LEVEL);
|
|
break;
|
|
}
|
|
return( NO_ERROR);
|
|
}
|
|
|
|
|
|
|
|
VOID WkstaGetInfoCleanup(
|
|
IN DWORD Level,
|
|
IN OUT LPVOID Buffer
|
|
)
|
|
{
|
|
LPRPL_WKSTA_INFO_2 Info = Buffer;
|
|
switch( Level) {
|
|
case 2:
|
|
if ( Info->AdapterName != NULL) {
|
|
MIDL_user_free( Info->AdapterName);
|
|
}
|
|
if ( Info->FitFile != NULL) {
|
|
MIDL_user_free( Info->FitFile);
|
|
}
|
|
if ( Info->BootName != NULL) {
|
|
MIDL_user_free( Info->BootName);
|
|
}
|
|
NOTHING; // fall through
|
|
case 1:
|
|
if ( Info->ProfileName != NULL) {
|
|
MIDL_user_free( Info->ProfileName);
|
|
}
|
|
NOTHING; // fall through
|
|
case 0:
|
|
if ( Info->WkstaComment != NULL) {
|
|
MIDL_user_free( Info->WkstaComment);
|
|
}
|
|
if ( Info->WkstaName != NULL) {
|
|
MIDL_user_free( Info->WkstaName);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
BOOL WkstaScan(
|
|
IN PRPL_SESSION pSession,
|
|
IN LPWSTR ProfileName,
|
|
IN DWORD ProfileNameSize,
|
|
IN OUT PBOOL pTableEnd
|
|
)
|
|
{
|
|
JET_ERR JetError;
|
|
|
|
JetError = JetSeek( pSession->SesId, pSession->WkstaTableId, JET_bitSeekGT);
|
|
if ( JetError != JET_errSuccess) {
|
|
RplDump( RG_DebugLevel & RPL_DEBUG_WKSTA, ("WkstaScan: JetSeek => %d", JetError));
|
|
*pTableEnd = TRUE;
|
|
return( TRUE);
|
|
}
|
|
//
|
|
// Verify that current wksta has the desired profile & at the
|
|
// same time set index range to be used with JetMove( Next).
|
|
//
|
|
CallB( JetMakeKey( pSession->SesId, pSession->WkstaTableId, ProfileName,
|
|
ProfileNameSize, JET_bitNewKey));
|
|
JetError = JetSetIndexRange( pSession->SesId, pSession->WkstaTableId,
|
|
JET_bitRangeInclusive | JET_bitRangeUpperLimit);
|
|
if ( JetError != JET_errSuccess) {
|
|
*pTableEnd = TRUE;
|
|
}
|
|
return( TRUE);
|
|
}
|
|
|
|
|
|
|
|
BOOL WkstaResumeFirst(
|
|
IN PRPL_SESSION pSession,
|
|
IN LPWSTR ProfileName,
|
|
IN LPDWORD pResumeHandle,
|
|
OUT PBOOL pTableEnd
|
|
)
|
|
/*++
|
|
Set currency to the first wksta for this profile, following
|
|
the wksta described by the resume handle.
|
|
--*/
|
|
{
|
|
BYTE ResumeValue[ RPL_MAX_PROFILE_NAME_SIZE + RPL_MAX_WKSTA_NAME_SIZE];
|
|
DWORD ResumeSize;
|
|
JET_ERR JetError;
|
|
DWORD ProfileNameSize;
|
|
DWORD WkstaNameSize;
|
|
WCHAR WkstaName[ RPL_MAX_WKSTA_NAME_LENGTH + 1 + JETBUG_STRING_LENGTH];
|
|
|
|
*pTableEnd = FALSE;
|
|
|
|
if ( ProfileName == NULL) {
|
|
CallB( JetSetCurrentIndex( pSession->SesId, pSession->WkstaTableId, WKSTA_INDEX_WkstaName));
|
|
} else {
|
|
ProfileNameSize = (wcslen( ProfileName) + 1) * sizeof(WCHAR);
|
|
CallB( JetSetCurrentIndex( pSession->SesId, pSession->WkstaTableId, WKSTA_INDEX_ProfileNameWkstaName));
|
|
}
|
|
//
|
|
// The call to move to the beginning of the table is not redundant.
|
|
// E.g. JET_errNoCurrentRecord will be returned in case of empty table.
|
|
//
|
|
JetError = JetMove( pSession->SesId, pSession->WkstaTableId, JET_MoveFirst, 0);
|
|
if ( JetError < 0) {
|
|
if ( JetError == JET_errRecordNotFound
|
|
|| JetError == JET_errNoCurrentRecord) {
|
|
*pTableEnd = TRUE;
|
|
return( TRUE);
|
|
} else {
|
|
RplDump( ++RG_Assert, ("JetError=%d", JetError));
|
|
return( FALSE);
|
|
}
|
|
}
|
|
if ( (ARGUMENT_PRESENT( pResumeHandle)) && *pResumeHandle != 0) {
|
|
ResumeSize = sizeof( ResumeValue);
|
|
if ( !ResumeKeyGet( pSession, *pResumeHandle, ResumeValue, &ResumeSize)) {
|
|
return( FALSE);
|
|
}
|
|
if ( ProfileName == NULL) {
|
|
CallB( JetMakeKey( pSession->SesId, pSession->WkstaTableId, ResumeValue, ResumeSize, JET_bitNewKey));
|
|
CallB( JetSeek( pSession->SesId, pSession->WkstaTableId, JET_bitSeekGT));
|
|
return( TRUE);
|
|
}
|
|
if ( ProfileNameSize != (wcslen( (PWCHAR)ResumeValue) + 1) * sizeof( WCHAR)
|
|
|| memcmp( ProfileName, ResumeValue, ProfileNameSize)) {
|
|
RplDump( ++RG_Assert, ("ResumeValue=0x%x"));
|
|
return( FALSE);
|
|
}
|
|
WkstaNameSize = (wcslen( (PWCHAR)(ResumeValue + ProfileNameSize)) + 1) * sizeof(WCHAR);
|
|
memcpy( WkstaName, ResumeValue + ProfileNameSize, WkstaNameSize);
|
|
} else {
|
|
if ( ProfileName == NULL) {
|
|
return( TRUE);
|
|
}
|
|
WkstaNameSize = 0;
|
|
}
|
|
CallB( JetMakeKey( pSession->SesId, pSession->WkstaTableId, ProfileName, ProfileNameSize, JET_bitNewKey));
|
|
if ( WkstaNameSize != 0) {
|
|
#ifdef RPL_JETBUG
|
|
memcpy( (PBYTE)WkstaName + WkstaNameSize, JETBUG_STRING, JETBUG_STRING_SIZE);
|
|
WkstaNameSize += JETBUG_STRING_LENGTH * sizeof(WCHAR);
|
|
#endif
|
|
CallB( JetMakeKey( pSession->SesId, pSession->WkstaTableId, WkstaName, WkstaNameSize, 0));
|
|
}
|
|
return( WkstaScan( pSession, ProfileName, ProfileNameSize, pTableEnd));
|
|
}
|
|
|
|
|
|
VOID WkstaResumeSave(
|
|
IN PRPL_SESSION pSession,
|
|
IN DWORD ServerHandle,
|
|
IN PWCHAR ProfileName,
|
|
IN PWCHAR WkstaName,
|
|
IN PDWORD pResumeHandle
|
|
)
|
|
{
|
|
BYTE ResumeBuffer[ 30 * sizeof(WCHAR)];
|
|
DWORD ResumeSize;
|
|
DWORD WkstaSize;
|
|
if ( ProfileName != NULL) {
|
|
ResumeSize = (wcslen( ProfileName) + 1) * sizeof(WCHAR);
|
|
memcpy( ResumeBuffer, ProfileName, ResumeSize);
|
|
} else {
|
|
ResumeSize = 0;
|
|
}
|
|
WkstaSize = ( wcslen( WkstaName) + 1) * sizeof(WCHAR);
|
|
memcpy( ResumeBuffer + ResumeSize, WkstaName, WkstaSize);
|
|
ResumeSize += WkstaSize;
|
|
(VOID)ResumeKeySet( pSession, (DWORD)ServerHandle, ResumeBuffer, ResumeSize, pResumeHandle);
|
|
}
|
|
|
|
|
|
NET_API_STATUS NET_API_FUNCTION
|
|
NetrRplWkstaEnum(
|
|
IN RPL_HANDLE ServerHandle,
|
|
IN LPWSTR ProfileName,
|
|
IN OUT LPRPL_WKSTA_ENUM WkstaEnum,
|
|
IN DWORD PrefMaxLength,
|
|
OUT LPDWORD TotalEntries,
|
|
OUT LPDWORD pResumeHandle OPTIONAL
|
|
)
|
|
/*++
|
|
For more extensive comments see related code in NetrConfigEnum.
|
|
--*/
|
|
{
|
|
LPBYTE Buffer;
|
|
DWORD TypicalSize;
|
|
DWORD CoreSize;
|
|
DWORD Error;
|
|
INT SpaceLeft;
|
|
DWORD ArrayLength;
|
|
DWORD EntriesRead;
|
|
JET_ERR JetError;
|
|
BOOL InfoError;
|
|
BOOL TableEnd;
|
|
PRPL_SESSION pSession = &RG_ApiSession;
|
|
|
|
if ( ProfileName != NULL) {
|
|
_wcsupr( ProfileName);
|
|
}
|
|
|
|
switch( WkstaEnum->Level) {
|
|
case 2:
|
|
TypicalSize = CoreSize = sizeof( RPL_WKSTA_INFO_2);
|
|
TypicalSize += 14 * sizeof( WCHAR); // typical size of AdapterName
|
|
TypicalSize += 20 * sizeof( WCHAR); // typical size of FitFile
|
|
TypicalSize += 8 * sizeof( WCHAR); // typical size of BootName
|
|
NOTHING; // fall through
|
|
case 1:
|
|
if ( WkstaEnum->Level == 1) {
|
|
TypicalSize = CoreSize = sizeof( RPL_WKSTA_INFO_1);
|
|
}
|
|
TypicalSize += 8 * sizeof( WCHAR); // typical size of ProfileName
|
|
NOTHING; // fall through
|
|
case 0:
|
|
if ( WkstaEnum->Level == 0) {
|
|
TypicalSize = CoreSize = sizeof( RPL_WKSTA_INFO_0);
|
|
}
|
|
TypicalSize += 8 * sizeof( WCHAR); // typical size of WkstaName
|
|
TypicalSize += 20 * sizeof( WCHAR); // typical size of WkstaComment
|
|
break;
|
|
default:
|
|
return( ERROR_INVALID_LEVEL);
|
|
break;
|
|
}
|
|
|
|
if ( PrefMaxLength == -1) {
|
|
SpaceLeft = DEFAULT_BUFFER_SIZE;
|
|
} else {
|
|
SpaceLeft = PrefMaxLength;
|
|
}
|
|
|
|
ArrayLength = SpaceLeft / TypicalSize;
|
|
if ( ArrayLength == 0) {
|
|
ArrayLength = 1; // try to return at least one element
|
|
}
|
|
|
|
Buffer = MIDL_user_allocate( ArrayLength * CoreSize);
|
|
if ( Buffer == NULL) {
|
|
return( ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
RplDump( RG_DebugLevel & RPL_DEBUG_WKSTA, (
|
|
"WkstaEnum: Buffer=0x%x, ArrayLength=0x%x", Buffer, ArrayLength));
|
|
|
|
WkstaEnum->WkstaInfo.Level0->Buffer = (LPRPL_WKSTA_INFO_0)Buffer;
|
|
|
|
EntriesRead = 0;
|
|
InfoError = FALSE;
|
|
Error = NO_ERROR;
|
|
|
|
EnterCriticalSection( &RG_ProtectDatabase);
|
|
Call( JetBeginTransaction( pSession->SesId));
|
|
|
|
if ( !WkstaResumeFirst( pSession, ProfileName, pResumeHandle, &TableEnd)) {
|
|
Error = NERR_RplCannotEnum;
|
|
goto cleanup;
|
|
}
|
|
if ( TableEnd == TRUE) {
|
|
goto cleanup;
|
|
}
|
|
for ( ; ; ) {
|
|
memset( Buffer, 0, CoreSize); // for cleanup to work properly
|
|
Error = WkstaGetInfo( pSession, NULL, WkstaEnum->Level, Buffer, &SpaceLeft);
|
|
if ( Error != NO_ERROR) {
|
|
InfoError = TRUE; // clean things up without holding crit sec
|
|
break;
|
|
}
|
|
EntriesRead++;
|
|
Buffer += CoreSize;
|
|
SpaceLeft -= CoreSize;
|
|
JetError = JetMove( pSession->SesId, pSession->WkstaTableId, JET_MoveNext, 0);
|
|
if ( JetError != JET_errSuccess) {
|
|
break; // assume end of table
|
|
}
|
|
if ( SpaceLeft <= 0) {
|
|
Error = ERROR_MORE_DATA;
|
|
break;
|
|
}
|
|
if ( EntriesRead >= ArrayLength) {
|
|
Error = ERROR_MORE_DATA;
|
|
break;
|
|
}
|
|
}
|
|
cleanup:
|
|
Call( JetCommitTransaction( pSession->SesId, 0));
|
|
LeaveCriticalSection( &RG_ProtectDatabase);
|
|
if ( InfoError == TRUE) {
|
|
WkstaGetInfoCleanup( WkstaEnum->Level, Buffer);
|
|
}
|
|
if ( Error == NO_ERROR) {
|
|
*TotalEntries = EntriesRead;
|
|
} else if ( Error == ERROR_MORE_DATA) {
|
|
*TotalEntries = EntriesRead * 2; // we cheat here
|
|
} else {
|
|
//
|
|
// Cleanup in case of "bad" errors.
|
|
//
|
|
while ( EntriesRead > 0) {
|
|
EntriesRead--;
|
|
Buffer -= CoreSize;
|
|
WkstaGetInfoCleanup( WkstaEnum->Level, Buffer);
|
|
}
|
|
MIDL_user_free( Buffer);
|
|
}
|
|
|
|
RplDump( RG_DebugLevel & RPL_DEBUG_WKSTA, ("WkstaEnum: EntriesRead = 0x%x", EntriesRead));
|
|
|
|
WkstaEnum->WkstaInfo.Level0->EntriesRead = EntriesRead;
|
|
if ( EntriesRead == 0) {
|
|
WkstaEnum->WkstaInfo.Level0->Buffer = NULL;
|
|
}
|
|
|
|
if ( ARGUMENT_PRESENT( pResumeHandle)) {
|
|
if ( Error == ERROR_MORE_DATA && EntriesRead > 0) {
|
|
EnterCriticalSection( &RG_ProtectDatabase);
|
|
Call( JetBeginTransaction( pSession->SesId));
|
|
WkstaResumeSave( pSession, (DWORD)ServerHandle, ProfileName,
|
|
((LPRPL_WKSTA_INFO_0)(Buffer-CoreSize))->WkstaName,
|
|
pResumeHandle
|
|
);
|
|
Call( JetCommitTransaction( pSession->SesId, JET_bitCommitFlush));
|
|
LeaveCriticalSection( &RG_ProtectDatabase);
|
|
} else {
|
|
*pResumeHandle = 0; // resume from beginning
|
|
}
|
|
}
|
|
|
|
return( Error);
|
|
}
|
|
|
|
|
|
BOOL WkstaFindFirst(
|
|
IN PRPL_SESSION pSession,
|
|
IN PWCHAR ProfileName
|
|
)
|
|
/*++
|
|
Returns TRUE if it can find a record (the first record) using input
|
|
profile name. Returns FALSE otherwise.
|
|
|
|
We cannot use SetIndexRange technique here because ProfileName is only
|
|
the first part of an index & we would never find a match.
|
|
--*/
|
|
{
|
|
DWORD ProfileNameSize;
|
|
JET_ERR JetError;
|
|
BYTE Data[ 300];
|
|
DWORD DataSize;
|
|
|
|
CallB( JetSetCurrentIndex( pSession->SesId, pSession->WkstaTableId, WKSTA_INDEX_ProfileNameWkstaName));
|
|
ProfileNameSize = ( wcslen( ProfileName) + 1) * sizeof(WCHAR);
|
|
CallB( JetMakeKey( pSession->SesId, pSession->WkstaTableId, ProfileName, ProfileNameSize, JET_bitNewKey));
|
|
JetError = JetSeek( pSession->SesId, pSession->WkstaTableId, JET_bitSeekGE);
|
|
if ( JetError < 0) {
|
|
return( FALSE);
|
|
}
|
|
CallB( JetRetrieveColumn( pSession->SesId, pSession->WkstaTableId,
|
|
WkstaTable[ WKSTA_ProfileName].ColumnId, Data,
|
|
sizeof( Data), &DataSize, 0, NULL));
|
|
if ( ProfileNameSize != DataSize) {
|
|
return( FALSE);
|
|
}
|
|
if ( memcmp( ProfileName, Data, DataSize) != 0) {
|
|
return( FALSE);
|
|
}
|
|
return( TRUE); // we found wksta record using this profile
|
|
}
|
|
|
|
|
|
|
|
NET_API_STATUS NET_API_FUNCTION
|
|
NetrRplWkstaGetInfo(
|
|
IN RPL_HANDLE ServerHandle,
|
|
IN LPWSTR WkstaName,
|
|
IN DWORD Level,
|
|
OUT LPRPL_WKSTA_INFO_STRUCT WkstaInfoStruct
|
|
)
|
|
{
|
|
DWORD Error;
|
|
LPBYTE Buffer;
|
|
DWORD DataSize;
|
|
PRPL_SESSION pSession = &RG_ApiSession;
|
|
|
|
_wcsupr( WkstaName);
|
|
|
|
switch( Level) {
|
|
case 0:
|
|
Buffer = MIDL_user_allocate( sizeof( RPL_WKSTA_INFO_0));
|
|
if ( Buffer == NULL) {
|
|
return( ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
memset( Buffer, 0, sizeof( RPL_WKSTA_INFO_0));
|
|
WkstaInfoStruct->WkstaInfo0 = (LPRPL_WKSTA_INFO_0)Buffer;
|
|
break;
|
|
case 1:
|
|
Buffer = MIDL_user_allocate( sizeof( RPL_WKSTA_INFO_1));
|
|
if ( Buffer == NULL) {
|
|
return( ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
memset( Buffer, 0, sizeof( RPL_WKSTA_INFO_1));
|
|
WkstaInfoStruct->WkstaInfo1 = (LPRPL_WKSTA_INFO_1)Buffer;
|
|
break;
|
|
case 2:
|
|
Buffer = MIDL_user_allocate( sizeof( RPL_WKSTA_INFO_2));
|
|
if ( Buffer == NULL) {
|
|
return( ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
memset( Buffer, 0, sizeof( RPL_WKSTA_INFO_2));
|
|
WkstaInfoStruct->WkstaInfo2 = (LPRPL_WKSTA_INFO_2)Buffer;
|
|
break;
|
|
default:
|
|
return( ERROR_INVALID_LEVEL);
|
|
break;
|
|
}
|
|
|
|
EnterCriticalSection( &RG_ProtectDatabase);
|
|
Call( JetBeginTransaction( pSession->SesId));
|
|
|
|
if ( !RplFind( pSession, WKSTA_TABLE_TAG, WkstaName)) {
|
|
Error = NERR_RplWkstaNotFound;
|
|
} else {
|
|
Error = WkstaGetInfo( pSession, WkstaName, Level, Buffer, &DataSize);
|
|
}
|
|
|
|
Call( JetCommitTransaction( pSession->SesId, 0));
|
|
LeaveCriticalSection( &RG_ProtectDatabase);
|
|
|
|
if ( Error != NO_ERROR) {
|
|
WkstaGetInfoCleanup( Level, Buffer);
|
|
switch( Level) {
|
|
case 0:
|
|
MIDL_user_free( Buffer);
|
|
WkstaInfoStruct->WkstaInfo0 = NULL;
|
|
break;
|
|
case 1:
|
|
MIDL_user_free( Buffer);
|
|
WkstaInfoStruct->WkstaInfo1 = NULL;
|
|
break;
|
|
case 2:
|
|
MIDL_user_free( Buffer);
|
|
WkstaInfoStruct->WkstaInfo2 = NULL;
|
|
break;
|
|
}
|
|
}
|
|
return( Error);
|
|
}
|
|
|
|
|
|
DWORD WkstaSetField(
|
|
IN PRPL_SESSION pSession,
|
|
IN DWORD FieldIndex,
|
|
IN LPVOID Data,
|
|
IN DWORD DataSize
|
|
)
|
|
{
|
|
CallM( JetSetColumn( pSession->SesId, pSession->WkstaTableId,
|
|
WkstaTable[ FieldIndex].ColumnId, Data, DataSize, 0, NULL));
|
|
return( NO_ERROR);
|
|
}
|
|
|
|
|
|
DWORD WkstaSetInfo(
|
|
IN PRPL_SESSION pSession,
|
|
IN DWORD Level,
|
|
IN LPVOID Buffer,
|
|
OUT LPDWORD pErrorParameter
|
|
)
|
|
/*++
|
|
-1 used to be a special "do not change value" for TcpIp columns.
|
|
This was discontinued. The reason is, the only way for an admin
|
|
to disable TCP/IP on the client, is to set TcpIp columns to -1 which
|
|
then causes rpl service not to send TCP/IP addresss to rpl client.
|
|
|
|
BUGBUG Whole TCP/IP approach has to be revisited, taking DHCP into account.
|
|
--*/
|
|
{
|
|
LPRPL_WKSTA_INFO_2 Info = Buffer;
|
|
switch( Level) {
|
|
case 2:
|
|
// if ( Info->TcpIpGateway != -1) {
|
|
{
|
|
*pErrorParameter = WKSTA_TcpIpGateway;
|
|
CallM( JetSetColumn( pSession->SesId, pSession->WkstaTableId,
|
|
WkstaTable[ WKSTA_TcpIpGateway].ColumnId,
|
|
&Info->TcpIpGateway, sizeof( Info->TcpIpGateway), 0, NULL));
|
|
}
|
|
// if ( Info->TcpIpSubnet != -1) {
|
|
{
|
|
*pErrorParameter = WKSTA_TcpIpSubnet;
|
|
CallM( JetSetColumn( pSession->SesId, pSession->WkstaTableId,
|
|
WkstaTable[ WKSTA_TcpIpSubnet].ColumnId,
|
|
&Info->TcpIpSubnet, sizeof( Info->TcpIpSubnet), 0, NULL));
|
|
}
|
|
// if ( Info->TcpIpAddress != -1) {
|
|
{
|
|
*pErrorParameter = WKSTA_TcpIpAddress;
|
|
CallM( JetSetColumn( pSession->SesId, pSession->WkstaTableId,
|
|
WkstaTable[ WKSTA_TcpIpAddress].ColumnId,
|
|
&Info->TcpIpAddress, sizeof( Info->TcpIpAddress), 0, NULL));
|
|
}
|
|
if ( Info->AdapterName != NULL) {
|
|
*pErrorParameter = WKSTA_AdapterName;
|
|
CallM( JetSetColumn( pSession->SesId, pSession->WkstaTableId,
|
|
WkstaTable[ WKSTA_AdapterName].ColumnId,
|
|
Info->AdapterName,
|
|
( wcslen( Info->AdapterName) + 1) * sizeof(WCHAR),
|
|
0, NULL));
|
|
}
|
|
if ( Info->FitFile != NULL) {
|
|
*pErrorParameter = WKSTA_FitFile;
|
|
CallM( JetSetColumn( pSession->SesId, pSession->WkstaTableId,
|
|
WkstaTable[ WKSTA_FitFile].ColumnId,
|
|
Info->FitFile,
|
|
( wcslen( Info->FitFile) + 1) * sizeof(WCHAR),
|
|
0, NULL));
|
|
}
|
|
if ( Info->BootName != NULL) {
|
|
*pErrorParameter = WKSTA_BootName;
|
|
CallM( JetSetColumn( pSession->SesId, pSession->WkstaTableId,
|
|
WkstaTable[ WKSTA_BootName].ColumnId,
|
|
Info->BootName,
|
|
( wcslen( Info->BootName) + 1) * sizeof(WCHAR),
|
|
0, NULL));
|
|
}
|
|
NOTHING; // fall through
|
|
case 1:
|
|
if ( Info->ProfileName != NULL) {
|
|
*pErrorParameter = WKSTA_ProfileName;
|
|
CallM( JetSetColumn( pSession->SesId, pSession->WkstaTableId,
|
|
WkstaTable[ WKSTA_ProfileName].ColumnId,
|
|
Info->ProfileName,
|
|
( wcslen( Info->ProfileName) + 1) * sizeof(WCHAR),
|
|
0, NULL));
|
|
}
|
|
if ( Info->Flags != 0) {
|
|
*pErrorParameter = WKSTA_Flags;
|
|
CallM( JetSetColumn( pSession->SesId, pSession->WkstaTableId,
|
|
WkstaTable[ WKSTA_Flags].ColumnId,
|
|
&Info->Flags, sizeof( Info->Flags), 0, NULL));
|
|
}
|
|
NOTHING; // fall through
|
|
case 0:
|
|
if ( Info->WkstaComment != NULL) {
|
|
*pErrorParameter = WKSTA_WkstaComment;
|
|
CallM( JetSetColumn( pSession->SesId, pSession->WkstaTableId,
|
|
WkstaTable[ WKSTA_WkstaComment].ColumnId,
|
|
Info->WkstaComment,
|
|
( wcslen( Info->WkstaComment) + 1) * sizeof(WCHAR),
|
|
0, NULL));
|
|
}
|
|
if ( Info->WkstaName != NULL) {
|
|
*pErrorParameter = WKSTA_WkstaName;
|
|
CallM( JetSetColumn( pSession->SesId, pSession->WkstaTableId,
|
|
WkstaTable[ WKSTA_WkstaName].ColumnId,
|
|
Info->WkstaName,
|
|
( wcslen( Info->WkstaName) + 1) * sizeof(WCHAR),
|
|
0, NULL));
|
|
}
|
|
break;
|
|
}
|
|
return( NO_ERROR);
|
|
}
|
|
|
|
|
|
NET_API_STATUS NET_API_FUNCTION
|
|
NetrRplWkstaSetInfo(
|
|
IN RPL_HANDLE ServerHandle,
|
|
IN LPWSTR WkstaName,
|
|
IN DWORD Level,
|
|
IN LPRPL_WKSTA_INFO_STRUCT WkstaInfoStruct,
|
|
OUT LPDWORD pErrorParameter OPTIONAL
|
|
)
|
|
/*++
|
|
Note that changes at info level 1 (ProfileName) imply that we need to
|
|
make changes to info level 2 (BootName & FitFile) parameters.
|
|
|
|
Here we pay for the redundancy in wksta records.
|
|
--*/
|
|
{
|
|
DWORD Error;
|
|
DWORD ErrorParameter;
|
|
LPVOID Buffer;
|
|
LPRPL_WKSTA_INFO_2 Info;
|
|
DWORD Flags;
|
|
DWORD Sharing;
|
|
DWORD LogonInput;
|
|
DWORD Dhcp;
|
|
DWORD DeleteUserAccount;
|
|
PWCHAR ProfileName;
|
|
DWORD TargetSharing;
|
|
DWORD TargetLogonInput;
|
|
DWORD TargetDhcp;
|
|
DWORD TargetDeleteUserAccount;
|
|
PWCHAR TargetProfileName;
|
|
PWCHAR TargetWkstaName;
|
|
INT SpaceLeft;
|
|
BOOL TreeModified;
|
|
PWCHAR FitFile;
|
|
PWCHAR BootName;
|
|
PRPL_SESSION pSession = &RG_ApiSession;
|
|
|
|
ErrorParameter = INVALID_ERROR_PARAMETER;
|
|
FitFile = NULL;
|
|
BootName = NULL;
|
|
TreeModified = FALSE;
|
|
Sharing = 0;
|
|
TargetSharing = 0;
|
|
ProfileName = NULL;
|
|
TargetProfileName = NULL;
|
|
_wcsupr( WkstaName);
|
|
TargetWkstaName = NULL;
|
|
|
|
Info = Buffer = WkstaInfoStruct->WkstaInfo2;
|
|
switch( Level) {
|
|
case 2:
|
|
if ( !ValidName( Info->FitFile, RPL_MAX_STRING_LENGTH, FALSE)) {
|
|
ErrorParameter = WKSTA_FitFile;
|
|
break;
|
|
}
|
|
if ( !ValidHexName( Info->AdapterName, RPL_ADAPTER_NAME_LENGTH, FALSE)) {
|
|
ErrorParameter = WKSTA_AdapterName;
|
|
break;
|
|
}
|
|
if ( Info->AdapterName != NULL) {
|
|
_wcsupr( Info->AdapterName);
|
|
}
|
|
NOTHING; // fall through
|
|
case 1:
|
|
if ( !ValidName( Info->ProfileName, RPL_MAX_PROFILE_NAME_LENGTH, FALSE)) {
|
|
ErrorParameter = WKSTA_ProfileName;
|
|
break;
|
|
}
|
|
if ( Info->ProfileName != NULL) {
|
|
_wcsupr( Info->ProfileName);
|
|
}
|
|
TargetProfileName = Info->ProfileName;
|
|
if ( Info->Flags & ~WKSTA_FLAGS_MASK) {
|
|
ErrorParameter = WKSTA_Flags;
|
|
break;
|
|
}
|
|
TargetSharing = Info->Flags & WKSTA_FLAGS_MASK_SHARING;
|
|
switch ( TargetSharing) {
|
|
case 0:
|
|
case WKSTA_FLAGS_SHARING_TRUE:
|
|
case WKSTA_FLAGS_SHARING_FALSE:
|
|
break;
|
|
default:
|
|
ErrorParameter = WKSTA_Flags;
|
|
break;
|
|
}
|
|
TargetLogonInput = Info->Flags & WKSTA_FLAGS_MASK_LOGON_INPUT;
|
|
switch ( TargetLogonInput) {
|
|
case 0:
|
|
case WKSTA_FLAGS_LOGON_INPUT_REQUIRED:
|
|
case WKSTA_FLAGS_LOGON_INPUT_OPTIONAL:
|
|
case WKSTA_FLAGS_LOGON_INPUT_IMPOSSIBLE:
|
|
break;
|
|
default:
|
|
ErrorParameter = WKSTA_Flags;
|
|
break;
|
|
}
|
|
TargetDhcp = Info->Flags & WKSTA_FLAGS_MASK_DHCP;
|
|
switch( TargetDhcp) {
|
|
case 0:
|
|
case WKSTA_FLAGS_DHCP_TRUE:
|
|
case WKSTA_FLAGS_DHCP_FALSE:
|
|
break;
|
|
default:
|
|
ErrorParameter = WKSTA_Flags;
|
|
break;
|
|
}
|
|
TargetDeleteUserAccount = Info->Flags & WKSTA_FLAGS_MASK_DELETE;
|
|
switch( TargetDeleteUserAccount) {
|
|
case 0:
|
|
case WKSTA_FLAGS_DELETE_TRUE:
|
|
case WKSTA_FLAGS_DELETE_FALSE:
|
|
break;
|
|
default:
|
|
ErrorParameter = WKSTA_Flags;
|
|
break;
|
|
}
|
|
if ( ErrorParameter != INVALID_ERROR_PARAMETER) {
|
|
break;
|
|
}
|
|
NOTHING; // fall through
|
|
case 0:
|
|
if ( RPL_STRING_TOO_LONG( Info->WkstaComment)) {
|
|
ErrorParameter = WKSTA_WkstaComment;
|
|
break;
|
|
}
|
|
if ( !ValidName( Info->WkstaName, RPL_MAX_WKSTA_NAME_LENGTH, FALSE)) {
|
|
ErrorParameter = WKSTA_WkstaName;
|
|
break;
|
|
}
|
|
if ( Info->WkstaName != NULL) {
|
|
_wcsupr( Info->WkstaName);
|
|
//
|
|
// Take new wksta name into consideration only if it is different
|
|
// than old wksta name (we ignore NOOP requests).
|
|
//
|
|
if ( wcscmp( Info->WkstaName, WkstaName) != 0) {
|
|
TargetWkstaName = Info->WkstaName;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
return( ERROR_INVALID_LEVEL);
|
|
break;
|
|
}
|
|
|
|
if ( ErrorParameter != INVALID_ERROR_PARAMETER) {
|
|
if ( ARGUMENT_PRESENT( pErrorParameter)) {
|
|
*pErrorParameter = ErrorParameter;
|
|
}
|
|
return( ERROR_INVALID_PARAMETER);
|
|
}
|
|
ErrorParameter = 0;
|
|
|
|
EnterCriticalSection( &RG_ProtectDatabase);
|
|
Call( JetBeginTransaction( pSession->SesId));
|
|
|
|
if ( TargetWkstaName != NULL) {
|
|
//
|
|
// Verify that TargetWkstaName is available in the database.
|
|
// Once we pass this check, disk routines below will nuke any
|
|
// old stuff in existing TargetWkstaName trees.
|
|
//
|
|
if ( RplFind( pSession, WKSTA_TABLE_TAG, TargetWkstaName)) {
|
|
Error = NERR_RplWkstaNameUnavailable;
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
if ( !RplFind( pSession, WKSTA_TABLE_TAG, WkstaName)) {
|
|
Error = NERR_RplWkstaNotFound;
|
|
goto cleanup;
|
|
}
|
|
Error = WkstaGetField( pSession, WKSTA_Flags, (LPVOID *)&Flags, &SpaceLeft);
|
|
if ( Error != NO_ERROR) {
|
|
goto cleanup;
|
|
}
|
|
Dhcp = Flags & WKSTA_FLAGS_MASK_DHCP;
|
|
DeleteUserAccount = Flags & WKSTA_FLAGS_MASK_DELETE;
|
|
LogonInput = Flags & WKSTA_FLAGS_MASK_LOGON_INPUT;
|
|
Sharing = Flags & WKSTA_FLAGS_MASK_SHARING;
|
|
|
|
if ( TargetSharing != 0 || TargetProfileName != NULL
|
|
|| TargetWkstaName != NULL) {
|
|
Error = WkstaSessionDel( WkstaName);
|
|
if ( Error != NO_ERROR) {
|
|
goto cleanup;
|
|
}
|
|
if ( TargetSharing == Sharing) {
|
|
TargetSharing = 0; // ignore NOOP requests
|
|
}
|
|
Error = WkstaGetField( pSession, WKSTA_ProfileName, &ProfileName, &SpaceLeft);
|
|
if ( Error != NO_ERROR) {
|
|
goto cleanup;
|
|
}
|
|
if ( TargetProfileName != NULL && wcscmp( TargetProfileName, ProfileName) == 0) {
|
|
TargetProfileName = NULL; // ignore NOOP requests
|
|
}
|
|
}
|
|
|
|
if ( TargetSharing != 0 || TargetProfileName != NULL) {
|
|
if ( !RplFind( pSession, PROFILE_TABLE_TAG,
|
|
TargetProfileName != NULL ? TargetProfileName : ProfileName)) {
|
|
Error = NERR_RplProfileNotFound;
|
|
goto cleanup;
|
|
}
|
|
if ( TargetProfileName != NULL) {
|
|
Error = ProfileGetField( pSession, PROFILE_BootName, &BootName, &SpaceLeft);
|
|
if ( Error != NO_ERROR) {
|
|
goto cleanup;
|
|
}
|
|
if ( !BootFind( pSession, BootName,
|
|
AdapterNameToVendorId( Info->AdapterName))) {
|
|
Error = NERR_RplIncompatibleProfile;
|
|
goto cleanup;
|
|
}
|
|
}
|
|
Error = ProfileGetField( pSession, (TargetSharing != 0 ? TargetSharing : Sharing)
|
|
== WKSTA_FLAGS_SHARING_TRUE ? PROFILE_FitShared : PROFILE_FitPersonal,
|
|
&FitFile, &SpaceLeft);
|
|
if ( Error != NO_ERROR) {
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
if ( TargetSharing != 0 || TargetProfileName != NULL
|
|
|| TargetWkstaName != NULL) {
|
|
//
|
|
// Create new tree, or add new branches to the current tree.
|
|
//
|
|
TreeModified = TRUE; // to undo tree changes in case of errors below
|
|
Error = WkstaDiskSet( ADD_NEW_BRANCHES, WkstaName, ProfileName, Sharing,
|
|
TargetWkstaName, TargetProfileName, TargetSharing);
|
|
if ( Error != NO_ERROR) {
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Tree is ready and we can call jet to modify database wksta record.
|
|
//
|
|
|
|
CallJ( JetPrepareUpdate( pSession->SesId, pSession->WkstaTableId, JET_prepReplace));
|
|
if ( TargetSharing == 0) {
|
|
//
|
|
// TargetSharing may have been reset to 0 due to being equal
|
|
// to Sharing. If so, then the line below is a no-op.
|
|
//
|
|
Info->Flags |= Sharing;
|
|
}
|
|
if ( TargetLogonInput == 0) {
|
|
Info->Flags |= LogonInput;
|
|
}
|
|
if ( TargetDhcp == 0) {
|
|
Info->Flags |= Dhcp;
|
|
}
|
|
if ( TargetDeleteUserAccount == 0) {
|
|
Info->Flags |= DeleteUserAccount;
|
|
}
|
|
Error = WkstaSetInfo( pSession, Level, Buffer, &ErrorParameter);
|
|
if ( Error != ERROR_SUCCESS) {
|
|
goto cleanup;
|
|
}
|
|
if ( BootName != NULL) {
|
|
Error = WkstaSetField( pSession, WKSTA_BootName, BootName,
|
|
(wcslen(BootName)+1)*sizeof(WCHAR));
|
|
if ( Error != ERROR_SUCCESS) {
|
|
goto cleanup;
|
|
}
|
|
}
|
|
if ( FitFile != NULL) {
|
|
Error = WkstaSetField( pSession, WKSTA_FitFile, FitFile,
|
|
(wcslen(FitFile)+1)*sizeof(WCHAR));
|
|
if ( Error != ERROR_SUCCESS) {
|
|
goto cleanup;
|
|
}
|
|
}
|
|
CallJ( JetUpdate( pSession->SesId, pSession->WkstaTableId, NULL, 0, NULL));
|
|
|
|
//
|
|
// Remove old tree, or old branches in the current tree.
|
|
//
|
|
WkstaDiskSet( DEL_OLD_BRANCHES, WkstaName, ProfileName, Sharing,
|
|
TargetWkstaName, TargetProfileName, TargetSharing);
|
|
|
|
cleanup:
|
|
if ( Error != NO_ERROR && TreeModified == TRUE) {
|
|
//
|
|
// Remove new tree, or new branches in the current tree.
|
|
//
|
|
WkstaDiskSet( DEL_NEW_BRANCHES, WkstaName, ProfileName, Sharing,
|
|
TargetWkstaName, TargetProfileName, TargetSharing);
|
|
}
|
|
|
|
if ( Error == NO_ERROR) {
|
|
Call( JetCommitTransaction( pSession->SesId, JET_bitCommitFlush));
|
|
} else {
|
|
Call( JetRollback( pSession->SesId, JET_bitRollbackAll));
|
|
}
|
|
LeaveCriticalSection( &RG_ProtectDatabase);
|
|
|
|
if ( FitFile != NULL) {
|
|
MIDL_user_free( FitFile);
|
|
}
|
|
if ( BootName != NULL) {
|
|
MIDL_user_free( BootName);
|
|
}
|
|
if ( ProfileName != NULL) {
|
|
MIDL_user_free( ProfileName);
|
|
}
|
|
if ( Error != ERROR_SUCCESS) {
|
|
if ( ARGUMENT_PRESENT( pErrorParameter)) {
|
|
*pErrorParameter = ErrorParameter;
|
|
}
|
|
}
|
|
return( Error);
|
|
}
|
|
|
|
|
|
NET_API_STATUS NET_API_FUNCTION
|
|
NetrRplWkstaAdd(
|
|
IN RPL_HANDLE ServerHandle,
|
|
IN DWORD Level,
|
|
IN LPRPL_WKSTA_INFO_STRUCT WkstaInfoStruct,
|
|
OUT LPDWORD pErrorParameter OPTIONAL
|
|
)
|
|
{
|
|
LPRPL_WKSTA_INFO_2 Info;
|
|
LPVOID Buffer;
|
|
DWORD Error;
|
|
DWORD ErrorParameter;
|
|
DWORD DataSize;
|
|
DWORD Sharing;
|
|
BOOL FreeBootName = FALSE;
|
|
BOOL FreeFitFile = FALSE;
|
|
PRPL_SESSION pSession = &RG_ApiSession;
|
|
|
|
ErrorParameter = INVALID_ERROR_PARAMETER;
|
|
|
|
Buffer = Info = WkstaInfoStruct->WkstaInfo2;
|
|
switch( Level) {
|
|
case 2:
|
|
if ( !ValidHexName( Info->AdapterName, RPL_ADAPTER_NAME_LENGTH, TRUE)) {
|
|
ErrorParameter = WKSTA_AdapterName;
|
|
break;
|
|
}
|
|
_wcsupr( Info->AdapterName);
|
|
if ( !ValidName( Info->ProfileName, RPL_MAX_PROFILE_NAME_LENGTH, TRUE)) {
|
|
ErrorParameter = WKSTA_ProfileName;
|
|
break;
|
|
}
|
|
_wcsupr( Info->ProfileName);
|
|
if ( Info->Flags & ~WKSTA_FLAGS_MASK) {
|
|
ErrorParameter = WKSTA_Flags;
|
|
break;
|
|
}
|
|
Sharing = Info->Flags & WKSTA_FLAGS_MASK_SHARING;
|
|
switch ( Sharing) {
|
|
case WKSTA_FLAGS_SHARING_TRUE:
|
|
case WKSTA_FLAGS_SHARING_FALSE:
|
|
break;
|
|
default:
|
|
ErrorParameter = WKSTA_Flags;
|
|
break;
|
|
}
|
|
switch( Info->Flags & WKSTA_FLAGS_MASK_LOGON_INPUT) {
|
|
case WKSTA_FLAGS_LOGON_INPUT_REQUIRED:
|
|
case WKSTA_FLAGS_LOGON_INPUT_OPTIONAL:
|
|
case WKSTA_FLAGS_LOGON_INPUT_IMPOSSIBLE:
|
|
break;
|
|
default:
|
|
ErrorParameter = WKSTA_Flags;
|
|
break;
|
|
}
|
|
switch( Info->Flags & WKSTA_FLAGS_MASK_DHCP) {
|
|
case WKSTA_FLAGS_DHCP_TRUE:
|
|
case WKSTA_FLAGS_DHCP_FALSE:
|
|
break;
|
|
default:
|
|
ErrorParameter = WKSTA_Flags;
|
|
break;
|
|
}
|
|
switch( Info->Flags & WKSTA_FLAGS_MASK_DELETE) {
|
|
case WKSTA_FLAGS_DELETE_TRUE:
|
|
case WKSTA_FLAGS_DELETE_FALSE:
|
|
break;
|
|
default:
|
|
ErrorParameter = WKSTA_Flags;
|
|
break;
|
|
}
|
|
if ( ErrorParameter != INVALID_ERROR_PARAMETER) {
|
|
break;
|
|
}
|
|
if ( RPL_STRING_TOO_LONG( Info->WkstaComment)) {
|
|
ErrorParameter = WKSTA_WkstaComment;
|
|
break;
|
|
}
|
|
if ( !ValidName( Info->WkstaName, RPL_MAX_WKSTA_NAME_LENGTH, TRUE)) {
|
|
ErrorParameter = WKSTA_WkstaName;
|
|
break;
|
|
}
|
|
_wcsupr( Info->WkstaName);
|
|
break;
|
|
default:
|
|
return( ERROR_INVALID_LEVEL);
|
|
break;
|
|
}
|
|
|
|
if ( ErrorParameter != INVALID_ERROR_PARAMETER) {
|
|
if ( ARGUMENT_PRESENT( pErrorParameter)) {
|
|
*pErrorParameter = ErrorParameter;
|
|
}
|
|
return( ERROR_INVALID_PARAMETER);
|
|
}
|
|
|
|
EnterCriticalSection( &RG_ProtectDatabase);
|
|
Call( JetBeginTransaction( pSession->SesId));
|
|
|
|
//
|
|
// Verify that WkstaName is available in the database.
|
|
//
|
|
if ( RplFind( pSession, WKSTA_TABLE_TAG, Info->WkstaName)) {
|
|
Error = NERR_RplWkstaNameUnavailable;
|
|
goto cleanup;
|
|
}
|
|
//
|
|
// Verify that AdapterName is available in the database.
|
|
//
|
|
if ( RplDbFindWksta( pSession, Info->AdapterName)) {
|
|
Error = NERR_RplAdapterNameUnavailable;
|
|
goto cleanup;
|
|
}
|
|
if ( !RplFind( pSession, PROFILE_TABLE_TAG, Info->ProfileName)) {
|
|
Error = NERR_RplProfileNotFound;
|
|
goto cleanup;
|
|
}
|
|
//
|
|
// Verify that there is a boot block record for (AdapterName, ProfileName)
|
|
// pair. This in turn "validates" AdapterName.
|
|
//
|
|
if ( Info->BootName == NULL) {
|
|
Error = ProfileGetField( pSession, PROFILE_BootName, &Info->BootName, &DataSize);
|
|
if ( Error != NO_ERROR) {
|
|
goto cleanup;
|
|
}
|
|
FreeBootName = TRUE;
|
|
}
|
|
if ( !BootFind( pSession, Info->BootName, AdapterNameToVendorId( Info->AdapterName))) {
|
|
Error = NERR_RplIncompatibleProfile;
|
|
goto cleanup;
|
|
}
|
|
|
|
if ( Info->FitFile == NULL) {
|
|
Error = ProfileGetField( pSession, Sharing == WKSTA_FLAGS_SHARING_TRUE
|
|
? PROFILE_FitShared : PROFILE_FitPersonal, &Info->FitFile,
|
|
&DataSize);
|
|
if ( Error != NO_ERROR) {
|
|
goto cleanup;
|
|
}
|
|
FreeFitFile = TRUE;
|
|
}
|
|
|
|
CallJ( JetPrepareUpdate( pSession->SesId, pSession->WkstaTableId, JET_prepInsert));
|
|
|
|
Error = WkstaSetInfo( pSession, Level, Buffer, &ErrorParameter);
|
|
if ( Error == ERROR_SUCCESS) {
|
|
ErrorParameter = 0;
|
|
CallJ( JetUpdate( pSession->SesId, pSession->WkstaTableId, NULL, 0, NULL));
|
|
}
|
|
|
|
Error = WkstaDiskAdd( TRUE, Info->WkstaName, Info->ProfileName, Sharing);
|
|
if ( Error != NO_ERROR) {
|
|
//
|
|
// WkstaDiskAdd deletion code does not care about ProfileName or Sharing
|
|
// We do not need to delete the new record since we are going to rollback
|
|
// the transaction.
|
|
//
|
|
WkstaDiskAdd( FALSE, Info->WkstaName, NULL, FALSE);
|
|
}
|
|
|
|
cleanup:
|
|
if ( Error == NO_ERROR) {
|
|
Call( JetCommitTransaction( pSession->SesId, JET_bitCommitFlush));
|
|
} else {
|
|
Call( JetRollback( pSession->SesId, JET_bitRollbackAll));
|
|
}
|
|
LeaveCriticalSection( &RG_ProtectDatabase);
|
|
|
|
if ( FreeBootName == TRUE) {
|
|
MIDL_user_free( Info->BootName);
|
|
Info->BootName = NULL;
|
|
}
|
|
if ( FreeFitFile == TRUE) {
|
|
MIDL_user_free( Info->FitFile);
|
|
Info->FitFile = NULL;
|
|
}
|
|
if ( Error != ERROR_SUCCESS) {
|
|
if ( ARGUMENT_PRESENT( pErrorParameter)) {
|
|
*pErrorParameter = ErrorParameter;
|
|
}
|
|
}
|
|
return( Error);
|
|
}
|
|
|
|
|
|
|
|
NET_API_STATUS NET_API_FUNCTION
|
|
NetrRplWkstaDel(
|
|
IN RPL_HANDLE ServerHandle,
|
|
IN LPWSTR WkstaName
|
|
)
|
|
{
|
|
DWORD Error;
|
|
PRPL_SESSION pSession = &RG_ApiSession;
|
|
|
|
_wcsupr( WkstaName);
|
|
|
|
EnterCriticalSection( &RG_ProtectDatabase);
|
|
Call( JetBeginTransaction( pSession->SesId));
|
|
|
|
if ( !RplFind( pSession, WKSTA_TABLE_TAG, WkstaName)) {
|
|
Error = NERR_RplWkstaNotFound;
|
|
goto cleanup;
|
|
}
|
|
Error = WkstaSessionDel( WkstaName);
|
|
if ( Error != NO_ERROR) {
|
|
goto cleanup;
|
|
}
|
|
CallJ( JetDelete( pSession->SesId, pSession->WkstaTableId));
|
|
//
|
|
// WkstaDiskAdd deletion code does not care about ProfileName or Sharing
|
|
//
|
|
WkstaDiskAdd( FALSE, WkstaName, NULL, FALSE);
|
|
|
|
cleanup:
|
|
if ( Error == NO_ERROR) {
|
|
Call( JetCommitTransaction( pSession->SesId, JET_bitCommitFlush));
|
|
} else {
|
|
Call( JetRollback( pSession->SesId, JET_bitRollbackAll));
|
|
}
|
|
LeaveCriticalSection( &RG_ProtectDatabase);
|
|
return( Error);
|
|
}
|
|
|
|
|
|
NET_API_STATUS NET_API_FUNCTION
|
|
NetrRplWkstaClone(
|
|
IN RPL_HANDLE ServerHandle,
|
|
IN LPWSTR SourceWkstaName,
|
|
IN LPWSTR TargetWkstaName,
|
|
IN LPWSTR TargetWkstaComment,
|
|
IN LPWSTR TargetAdapterName,
|
|
IN DWORD TargetTcpIpAddress
|
|
)
|
|
{
|
|
RPL_WKSTA_INFO_2 Info;
|
|
DWORD Error;
|
|
DWORD ErrorParameter;
|
|
DWORD DataSize;
|
|
PWCHAR SaveWkstaName;
|
|
PWCHAR SaveWkstaComment;
|
|
PWCHAR SaveAdapterName;
|
|
PWCHAR BootName;
|
|
BOOL FreeBootName = FALSE;
|
|
INT SpaceLeft;
|
|
PRPL_SESSION pSession = &RG_ApiSession;
|
|
|
|
if ( !ValidName( SourceWkstaName, RPL_MAX_WKSTA_NAME_LENGTH, TRUE)) {
|
|
return( ERROR_INVALID_PARAMETER);
|
|
}
|
|
_wcsupr( SourceWkstaName);
|
|
if ( !ValidName( TargetWkstaName, RPL_MAX_WKSTA_NAME_LENGTH, TRUE)) {
|
|
return( ERROR_INVALID_PARAMETER);
|
|
}
|
|
_wcsupr( TargetWkstaName);
|
|
if ( !ValidHexName( TargetAdapterName, RPL_ADAPTER_NAME_LENGTH, TRUE)) {
|
|
return( ERROR_INVALID_PARAMETER);
|
|
}
|
|
_wcsupr( TargetAdapterName);
|
|
if ( RPL_STRING_TOO_LONG( TargetWkstaComment)) {
|
|
return( ERROR_INVALID_PARAMETER);
|
|
}
|
|
|
|
//
|
|
// Zero all the pointers so we can safely call WkstaGetInfoClenaup().
|
|
// in all the cases.
|
|
//
|
|
memset( &Info, 0, sizeof( Info));
|
|
SaveWkstaName = NULL;
|
|
SaveWkstaComment = NULL;
|
|
SaveAdapterName = NULL;
|
|
|
|
EnterCriticalSection( &RG_ProtectDatabase);
|
|
Call( JetBeginTransaction( pSession->SesId));
|
|
|
|
//
|
|
// Verify that TargetWkstaName is available in the database.
|
|
//
|
|
if ( RplFind( pSession, WKSTA_TABLE_TAG, TargetWkstaName)) {
|
|
Error = NERR_RplWkstaNameUnavailable;
|
|
goto cleanup;
|
|
}
|
|
//
|
|
// Verify that TargetAdapterName is available in the database.
|
|
//
|
|
if ( RplDbFindWksta( pSession, TargetAdapterName)) {
|
|
Error = NERR_RplAdapterNameUnavailable;
|
|
goto cleanup;
|
|
}
|
|
//
|
|
// Verify that SourceWkstaName exists.
|
|
//
|
|
if ( !RplFind( pSession, WKSTA_TABLE_TAG, SourceWkstaName)) {
|
|
Error = NERR_RplWkstaNotFound;
|
|
goto cleanup;
|
|
}
|
|
Error = WkstaGetInfo( pSession, SourceWkstaName, 2, &Info, &DataSize);
|
|
if ( Error != NO_ERROR) {
|
|
goto cleanup;
|
|
}
|
|
//
|
|
// Verify that Source profile is compatible with Target workstation.
|
|
//
|
|
if ( !RplFind( pSession, PROFILE_TABLE_TAG, Info.ProfileName)) {
|
|
Error = NERR_RplProfileNotFound;
|
|
goto cleanup;
|
|
}
|
|
Error = ProfileGetField( pSession, PROFILE_BootName, &BootName, &SpaceLeft);
|
|
if ( Error != NO_ERROR) {
|
|
goto cleanup;
|
|
}
|
|
FreeBootName = TRUE;
|
|
if ( !BootFind( pSession, BootName,
|
|
AdapterNameToVendorId( TargetAdapterName))) {
|
|
Error = NERR_RplIncompatibleProfile;
|
|
goto cleanup;
|
|
}
|
|
//
|
|
// Save source data, then overload target data.
|
|
//
|
|
SaveWkstaName = Info.WkstaName;
|
|
SaveWkstaComment = Info.WkstaComment;
|
|
SaveAdapterName = Info.AdapterName;
|
|
Info.WkstaName = TargetWkstaName;
|
|
Info.WkstaComment = TargetWkstaComment;
|
|
Info.AdapterName = TargetAdapterName;
|
|
Info.TcpIpAddress = TargetTcpIpAddress;
|
|
|
|
CallJ( JetPrepareUpdate( pSession->SesId, pSession->WkstaTableId, JET_prepInsert));
|
|
|
|
Error = WkstaSetInfo( pSession, 2, &Info, &ErrorParameter);
|
|
if ( Error == ERROR_SUCCESS) {
|
|
CallJ( JetUpdate( pSession->SesId, pSession->WkstaTableId, NULL, 0, NULL));
|
|
}
|
|
|
|
Error = WkstaDiskClone( TRUE, SourceWkstaName, TargetWkstaName);
|
|
if ( Error != NO_ERROR) {
|
|
//
|
|
// Delete new tree. We do not need to delete the new record
|
|
// since we are going to rollback the transaction.
|
|
//
|
|
WkstaDiskClone( FALSE, SourceWkstaName, TargetWkstaName);
|
|
}
|
|
|
|
cleanup:
|
|
if ( Error == NO_ERROR) {
|
|
Call( JetCommitTransaction( pSession->SesId, JET_bitCommitFlush));
|
|
} else {
|
|
Call( JetRollback( pSession->SesId, JET_bitRollbackAll));
|
|
}
|
|
LeaveCriticalSection( &RG_ProtectDatabase);
|
|
|
|
//
|
|
// Restore source data, then release it.
|
|
//
|
|
Info.WkstaName = SaveWkstaName;
|
|
Info.WkstaComment = SaveWkstaComment;
|
|
Info.AdapterName = SaveAdapterName;
|
|
WkstaGetInfoCleanup( 2, &Info);
|
|
if ( FreeBootName == TRUE) {
|
|
MIDL_user_free( BootName);
|
|
}
|
|
return( Error);
|
|
}
|
|
|
|
|