summaryrefslogtreecommitdiffstats
path: root/private/nw/svcdlls/nwwks/client
diff options
context:
space:
mode:
authorAdam <you@example.com>2020-05-17 05:51:50 +0200
committerAdam <you@example.com>2020-05-17 05:51:50 +0200
commite611b132f9b8abe35b362e5870b74bce94a1e58e (patch)
treea5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/nw/svcdlls/nwwks/client
downloadNT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip
Diffstat (limited to 'private/nw/svcdlls/nwwks/client')
-rw-r--r--private/nw/svcdlls/nwwks/client/authpkg.c952
-rw-r--r--private/nw/svcdlls/nwwks/client/bind.c189
-rw-r--r--private/nw/svcdlls/nwwks/client/caddress.c1183
-rw-r--r--private/nw/svcdlls/nwwks/client/ccache.c324
-rw-r--r--private/nw/svcdlls/nwwks/client/drawpie.c240
-rw-r--r--private/nw/svcdlls/nwwks/client/drawpie.h9
-rw-r--r--private/nw/svcdlls/nwwks/client/folderop.icobin0 -> 1078 bytes
-rw-r--r--private/nw/svcdlls/nwwks/client/getaddr.c3619
-rw-r--r--private/nw/svcdlls/nwwks/client/gtadrnr.c1935
-rw-r--r--private/nw/svcdlls/nwwks/client/logon.c2539
-rw-r--r--private/nw/svcdlls/nwwks/client/makefile6
-rw-r--r--private/nw/svcdlls/nwwks/client/makefile.inc1
-rw-r--r--private/nw/svcdlls/nwwks/client/ndscont.icobin0 -> 4846 bytes
-rw-r--r--private/nw/svcdlls/nwwks/client/nwapi.c1182
-rw-r--r--private/nw/svcdlls/nwwks/client/nwclient.h132
-rw-r--r--private/nw/svcdlls/nwwks/client/nwclsid.h35
-rw-r--r--private/nw/svcdlls/nwwks/client/nwdlg.c3964
-rw-r--r--private/nw/svcdlls/nwwks/client/nwdlg.h217
-rw-r--r--private/nw/svcdlls/nwwks/client/nwnp.reg47
-rw-r--r--private/nw/svcdlls/nwwks/client/nwprovau.def80
-rw-r--r--private/nw/svcdlls/nwwks/client/nwprovau.dlg385
-rw-r--r--private/nw/svcdlls/nwwks/client/nwprovau.rc136
-rw-r--r--private/nw/svcdlls/nwwks/client/nwprovau.resbin0 -> 12476 bytes
-rw-r--r--private/nw/svcdlls/nwwks/client/nwshcmn.h115
-rw-r--r--private/nw/svcdlls/nwwks/client/nwshext.cxx854
-rw-r--r--private/nw/svcdlls/nwwks/client/nwshext.h279
-rw-r--r--private/nw/svcdlls/nwwks/client/nwshhelp.h78
-rw-r--r--private/nw/svcdlls/nwwks/client/nwshmenu.cxx896
-rw-r--r--private/nw/svcdlls/nwwks/client/nwshmisc.cxx261
-rw-r--r--private/nw/svcdlls/nwwks/client/nwshprop.cxx1326
-rw-r--r--private/nw/svcdlls/nwwks/client/nwshrc.h296
-rw-r--r--private/nw/svcdlls/nwwks/client/nwshui.cxx1630
-rw-r--r--private/nw/svcdlls/nwwks/client/nwspl.c2924
-rw-r--r--private/nw/svcdlls/nwwks/client/nwspl.h76
-rw-r--r--private/nw/svcdlls/nwwks/client/nwutil.c872
-rw-r--r--private/nw/svcdlls/nwwks/client/nwutil.h120
-rw-r--r--private/nw/svcdlls/nwwks/client/port.c449
-rw-r--r--private/nw/svcdlls/nwwks/client/print.icobin0 -> 1078 bytes
-rw-r--r--private/nw/svcdlls/nwwks/client/provider.c3051
-rw-r--r--private/nw/svcdlls/nwwks/client/server.bmpbin0 -> 1270 bytes
-rw-r--r--private/nw/svcdlls/nwwks/client/server.icobin0 -> 1078 bytes
-rw-r--r--private/nw/svcdlls/nwwks/client/sources101
-rw-r--r--private/nw/svcdlls/nwwks/client/tconn.c556
-rw-r--r--private/nw/svcdlls/nwwks/client/tree.bmpbin0 -> 778 bytes
-rw-r--r--private/nw/svcdlls/nwwks/client/tree.icobin0 -> 1078 bytes
45 files changed, 31059 insertions, 0 deletions
diff --git a/private/nw/svcdlls/nwwks/client/authpkg.c b/private/nw/svcdlls/nwwks/client/authpkg.c
new file mode 100644
index 000000000..88495c6e5
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/authpkg.c
@@ -0,0 +1,952 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ authpkg.c
+
+Abstract:
+
+ This module is the NetWare authentication package. It saves
+ credentials in LSA, and notifies the workstation of a logoff.
+
+Author:
+
+ Jim Kelly (jimk) 11-Mar-1991
+ Cliff Van Dyke (cliffv) 25-Apr-1991
+
+Revision History:
+
+ Rita Wong (ritaw) 1-Apr-1993 Cloned for NetWare
+
+--*/
+
+#include <string.h>
+#include <stdlib.h>
+
+#include <nwclient.h>
+
+#include <nwlsa.h>
+#include <nwreg.h>
+#include <nwauth.h>
+
+//
+// Netware authentication manager credential
+//
+#define NW_CREDENTIAL_KEY "NWCS_Credential"
+
+
+//-------------------------------------------------------------------//
+// //
+// Local functions //
+// //
+//-------------------------------------------------------------------//
+
+NTSTATUS
+AuthpSetCredential(
+ IN PLUID LogonId,
+ IN LPWSTR UserName,
+ IN LPWSTR Password
+ );
+
+NTSTATUS
+AuthpGetCredential(
+ IN PLUID LogonId,
+ OUT PNWAUTH_GET_CREDENTIAL_RESPONSE CredBuf
+ );
+
+NTSTATUS
+NwAuthGetCredential(
+ IN PLSA_CLIENT_REQUEST ClientRequest,
+ IN PVOID ProtocolSubmitBuffer,
+ IN PVOID ClientBufferBase,
+ IN ULONG SubmitBufferSize,
+ OUT PVOID *ProtocolReturnBuffer,
+ OUT PULONG ReturnBufferSize,
+ OUT PNTSTATUS ProtocolStatus
+ );
+
+NTSTATUS
+NwAuthSetCredential(
+ IN PLSA_CLIENT_REQUEST ClientRequest,
+ IN PVOID ProtocolSubmitBuffer,
+ IN PVOID ClientBufferBase,
+ IN ULONG SubmitBufferSize,
+ OUT PVOID *ProtocolReturnBuffer,
+ OUT PULONG ReturnBufferSize,
+ OUT PNTSTATUS ProtocolStatus
+ );
+
+PVOID NwAuthHeap;
+ULONG NwAuthPackageId;
+LSA_DISPATCH_TABLE Lsa;
+
+//
+// LsaApCallPackage() function dispatch table
+//
+PLSA_AP_CALL_PACKAGE
+NwCallPackageDispatch[] = {
+ NwAuthGetCredential,
+ NwAuthSetCredential
+ };
+
+//
+// Structure of the credential saved in LSA.
+//
+typedef struct _NWCREDENTIAL {
+ LPWSTR UserName;
+ LPWSTR Password;
+} NWCREDENTIAL, *PNWCREDENTIAL;
+
+//-------------------------------------------------------------------//
+// //
+// Authentication package dispatch routines. //
+// //
+//-------------------------------------------------------------------//
+
+NTSTATUS
+LsaApInitializePackage (
+ IN ULONG AuthenticationPackageId,
+ IN PLSA_DISPATCH_TABLE LsaDispatchTable,
+ IN PSTRING Database OPTIONAL,
+ IN PSTRING Confidentiality OPTIONAL,
+ OUT PSTRING *AuthenticationPackageName
+ )
+
+/*++
+
+Routine Description:
+
+ This service is called once by the LSA during system initialization to
+ provide the DLL a chance to initialize itself.
+
+Arguments:
+
+ AuthenticationPackageId - The ID assigned to the authentication
+ package.
+
+ LsaDispatchTable - Provides the address of a table of LSA
+ services available to authentication packages. The services
+ of this table are ordered according to the enumerated type
+ LSA_DISPATCH_TABLE_API.
+
+ Database - This parameter is not used by this authentication package.
+
+ Confidentiality - This parameter is not used by this authentication
+ package.
+
+ AuthenticationPackageName - Receives the name of the
+ authentication package. The authentication package is
+ responsible for allocating the buffer that the string is in
+ (using the AllocateLsaHeap() service) and returning its
+ address here. The buffer will be deallocated by LSA when it
+ is no longer needed.
+
+Return Value:
+
+ STATUS_SUCCESS - Indicates the service completed successfully.
+
+
+--*/
+
+{
+
+ PSTRING NameString;
+ PCHAR NameBuffer;
+
+
+ UNREFERENCED_PARAMETER(Database);
+ UNREFERENCED_PARAMETER(Confidentiality);
+
+ //
+ // Use the process heap for memory allocations.
+ //
+ NwAuthHeap = RtlProcessHeap();
+
+
+ NwAuthPackageId = AuthenticationPackageId;
+
+ //
+ // Copy the LSA service dispatch table
+ //
+ Lsa.CreateLogonSession = LsaDispatchTable->CreateLogonSession;
+ Lsa.DeleteLogonSession = LsaDispatchTable->DeleteLogonSession;
+ Lsa.AddCredential = LsaDispatchTable->AddCredential;
+ Lsa.GetCredentials = LsaDispatchTable->GetCredentials;
+ Lsa.DeleteCredential = LsaDispatchTable->DeleteCredential;
+ Lsa.AllocateLsaHeap = LsaDispatchTable->AllocateLsaHeap;
+ Lsa.FreeLsaHeap = LsaDispatchTable->FreeLsaHeap;
+ Lsa.AllocateClientBuffer = LsaDispatchTable->AllocateClientBuffer;
+ Lsa.FreeClientBuffer = LsaDispatchTable->FreeClientBuffer;
+ Lsa.CopyToClientBuffer = LsaDispatchTable->CopyToClientBuffer;
+ Lsa.CopyFromClientBuffer = LsaDispatchTable->CopyFromClientBuffer;
+
+ //
+ // Allocate and return our package name
+ //
+ NameBuffer = (*Lsa.AllocateLsaHeap)(sizeof(NW_AUTH_PACKAGE_NAME));
+ strcpy(NameBuffer, NW_AUTH_PACKAGE_NAME);
+
+ NameString = (*Lsa.AllocateLsaHeap)(sizeof(STRING));
+ RtlInitString(NameString, NameBuffer);
+ (*AuthenticationPackageName) = NameString;
+
+ //
+ // Delete outdated credential information in the registry
+ //
+ NwDeleteCurrentUser();
+ (void) NwDeleteServiceLogon(NULL);
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+LsaApLogonUser (
+ IN PLSA_CLIENT_REQUEST ClientRequest,
+ IN SECURITY_LOGON_TYPE LogonType,
+ IN PVOID ProtocolSubmitBuffer,
+ IN PVOID ClientBufferBase,
+ IN ULONG SubmitBufferSize,
+ OUT PVOID *ProfileBuffer,
+ OUT PULONG ProfileBufferSize,
+ OUT PLUID LogonId,
+ OUT PNTSTATUS SubStatus,
+ OUT PLSA_TOKEN_INFORMATION_TYPE TokenInformationType,
+ OUT PVOID *TokenInformation,
+ OUT PUNICODE_STRING *AccountName,
+ OUT PUNICODE_STRING *AuthenticatingAuthority
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to authenticate a user logon attempt. This may be
+ the user's initial logon, necessary to gain access to NT, or may
+ be a subsequent logon attempt. If the logon is the user's initial
+ logon, then a new LSA logon session will be established for the user
+ and a PrimaryToken will be returned. Otherwise, the authentication
+ package will associated appropriate credentials with the already logged
+ on user's existing LSA logon session.
+
+Arguments:
+
+ ClientRequest - Is a pointer to an opaque data structure
+ representing the client's request.
+
+ LogonType - Identifies the type of logon being attempted.
+
+ ProtocolSubmitBuffer - Supplies the authentication
+ information specific to the authentication package.
+
+ ClientBufferBase - Provides the address within the client
+ process at which the authentication information was resident.
+ This may be necessary to fix-up any pointers within the
+ authentication information buffer.
+
+ SubmitBufferSize - Indicates the Size, in bytes,
+ of the authentication information buffer.
+
+ ProfileBuffer - Is used to return the address of the profile
+ buffer in the client process. The authentication package is
+ responsible for allocating and returning the profile buffer
+ within the client process. However, if the LSA subsequently
+ encounters an error which prevents a successful logon, then
+ the LSA will take care of deallocating that buffer. This
+ buffer is expected to have been allocated with the
+ AllocateClientBuffer() service.
+
+ The format and semantics of this buffer are specific to the
+ authentication package.
+
+ ProfileBufferSize - Receives the size (in bytes) of the
+ returned profile buffer.
+
+ LogonId - Points to a buffer into which the authentication
+ package must return a logon ID that uniquely
+ identifies this logon session.
+
+ SubStatus - If the logon failed due to account restrictions, the
+ reason for the failure should be returned via this parameter.
+ The reason is authentication-package specific. The substatus
+ values for authentication package "MSV1.0" are:
+
+ STATUS_INVALID_LOGON_HOURS
+
+ STATUS_INVALID_WORKSTATION
+
+ STATUS_PASSWORD_EXPIRED
+
+ STATUS_ACCOUNT_DISABLED
+
+ TokenInformationType - If the logon is successful, this field is
+ used to indicate what level of information is being returned
+ for inclusion in the Token to be created. This information
+ is returned via the TokenInformation parameter.
+
+ TokenInformation - If the logon is successful, this parameter is
+ used by the authentication package to return information to
+ be included in the token. The format and content of the
+ buffer returned is indicated by the TokenInformationLevel
+ return value.
+
+ AccountName - A Unicode string describing the account name
+ being logged on to. This parameter must always be returned
+ regardless of the success or failure of the operation.
+
+ AuthenticatingAuthority - A Unicode string describing the Authenticating
+ Authority for the logon. This string may optionally be omitted.
+
+Return Value:
+
+ STATUS_NOT_IMPLEMENTED - NetWare authentication package does not
+ support login.
+
+--*/
+
+{
+ UNREFERENCED_PARAMETER(ClientRequest);
+ UNREFERENCED_PARAMETER(LogonType);
+ UNREFERENCED_PARAMETER(ProtocolSubmitBuffer);
+ UNREFERENCED_PARAMETER(ClientBufferBase);
+ UNREFERENCED_PARAMETER(SubmitBufferSize);
+ UNREFERENCED_PARAMETER(ProfileBuffer);
+ UNREFERENCED_PARAMETER(ProfileBufferSize);
+ UNREFERENCED_PARAMETER(LogonId);
+ UNREFERENCED_PARAMETER(SubStatus);
+ UNREFERENCED_PARAMETER(TokenInformationType);
+ UNREFERENCED_PARAMETER(TokenInformation);
+ UNREFERENCED_PARAMETER(AccountName);
+ UNREFERENCED_PARAMETER(AuthenticatingAuthority);
+
+ return STATUS_NOT_IMPLEMENTED;
+}
+
+
+NTSTATUS
+LsaApCallPackage (
+ IN PLSA_CLIENT_REQUEST ClientRequest,
+ IN PVOID ProtocolSubmitBuffer,
+ IN PVOID ClientBufferBase,
+ IN ULONG SubmitBufferLength,
+ OUT PVOID *ProtocolReturnBuffer,
+ OUT PULONG ReturnBufferLength,
+ OUT PNTSTATUS ProtocolStatus
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is the dispatch routine for
+ LsaCallAuthenticationPackage().
+
+Arguments:
+
+ ClientRequest - Is a pointer to an opaque data structure
+ representing the client's request.
+
+ ProtocolSubmitBuffer - Supplies a protocol message specific to
+ the authentication package.
+
+ ClientSubmitBufferBase - Supplies the client address of the submitted
+ protocol message.
+
+ SubmitBufferLength - Indicates the length of the submitted
+ protocol message buffer.
+
+ ProtocolReturnBuffer - Is used to return the address of the
+ protocol buffer in the client process. The authentication
+ package is responsible for allocating and returning the
+ protocol buffer within the client process. This buffer is
+ expected to have been allocated with the
+ AllocateClientBuffer() service.
+
+ The format and semantics of this buffer are specific to the
+ authentication package.
+
+ ReturnBufferLength - Receives the length (in bytes) of the
+ returned protocol buffer.
+
+ ProtocolStatus - Assuming the services completion is
+ STATUS_SUCCESS, this parameter will receive completion status
+ returned by the specified authentication package. The list
+ of status values that may be returned are authentication
+ package specific.
+
+Return Status:
+
+--*/
+
+{
+
+ ULONG MessageType;
+
+ //
+ // Get the messsage type from the protocol submit buffer.
+ //
+
+ if ( SubmitBufferLength < sizeof(NWAUTH_MESSAGE_TYPE) ) {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ MessageType =
+ (ULONG) *((PNWAUTH_MESSAGE_TYPE)(ProtocolSubmitBuffer));
+
+ if ( MessageType >=
+ (sizeof(NwCallPackageDispatch)/sizeof(NwCallPackageDispatch[0])) ) {
+
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // Allow the dispatch routines to only set the return buffer information
+ // on success conditions.
+ //
+
+ *ProtocolReturnBuffer = NULL;
+ *ReturnBufferLength = 0;
+
+ //
+ // Call the appropriate routine for this message.
+ //
+
+ return (*(NwCallPackageDispatch[MessageType]))(
+ ClientRequest,
+ ProtocolSubmitBuffer,
+ ClientBufferBase,
+ SubmitBufferLength,
+ ProtocolReturnBuffer,
+ ReturnBufferLength,
+ ProtocolStatus
+ ) ;
+}
+
+
+VOID
+LsaApLogonTerminated (
+ IN PLUID LogonId
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to notify each authentication package when a logon
+ session terminates. A logon session terminates when the last token
+ referencing the logon session is deleted.
+
+Arguments:
+
+ LogonId - Is the logon ID that just logged off.
+
+Return Status:
+
+ None.
+
+--*/
+
+{
+ DWORD status;
+
+ LONG RegError;
+ HKEY WkstaKey = NULL;
+ HKEY WkstaLogonKey = NULL;
+ HKEY CurrentUserKey = NULL;
+
+ LPWSTR CurrentUser = NULL;
+ PLUID CurrentLogonId = NULL;
+
+
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("\nNWPROVAU: LsaApLogonTerminated\n"));
+ }
+#endif
+
+ RpcTryExcept {
+
+ //
+ // The logon ID may be for a service login
+ //
+ if (NwDeleteServiceLogon(LogonId) == NO_ERROR) {
+
+ //
+ // Tell workstation to log off the service.
+ //
+ (void) NwrLogoffUser(NULL, LogonId);
+ goto Done;
+ }
+
+ //
+ // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
+ // \NWCWorkstation\Parameters to delete the CurrentUser
+ // value to indicate that there is currently no interactively
+ // logged on user.
+ //
+ RegError = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ NW_WORKSTATION_REGKEY,
+ REG_OPTION_NON_VOLATILE,
+ KEY_READ | KEY_WRITE | DELETE,
+ &WkstaKey
+ );
+
+ if (RegError == NO_ERROR) {
+
+ //
+ // Read the current user SID string so that we
+ // know which key is the current user key to open.
+ //
+ status = NwReadRegValue(
+ WkstaKey,
+ NW_CURRENTUSER_VALUENAME,
+ &CurrentUser
+ );
+
+ if (status == NO_ERROR) {
+
+ //
+ // Open the Logon key
+ //
+ RegError = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ NW_WORKSTATION_LOGON_REGKEY,
+ REG_OPTION_NON_VOLATILE,
+ KEY_READ,
+ &WkstaLogonKey
+ );
+
+ //
+ // Open the <CurrentUser> key under Logon
+ //
+ if ( RegError == NO_ERROR ) {
+
+ RegError = RegOpenKeyExW( WkstaLogonKey,
+ CurrentUser,
+ REG_OPTION_NON_VOLATILE,
+ KEY_READ,
+ &CurrentUserKey );
+
+ }
+
+ if (RegError == NO_ERROR) {
+
+ //
+ // Read the logon ID
+ //
+ status = NwReadRegValue(
+ CurrentUserKey,
+ NW_LOGONID_VALUENAME,
+ (LPWSTR *) &CurrentLogonId
+ );
+
+ if (status == NO_ERROR) {
+
+ if (RtlEqualLuid(CurrentLogonId, LogonId)) {
+
+ //
+ // Only delete the current user value if the
+ // logon ID of the logging off user is the same
+ // as the current user's logon ID.
+ //
+ (void) RegDeleteValueW(
+ WkstaKey,
+ NW_CURRENTUSER_VALUENAME
+ );
+
+ }
+
+
+ //
+ // Tell workstation to log off the
+ // interactive user.
+ //
+ (void) NwrLogoffUser(NULL, LogonId);
+
+ }
+
+ }
+
+
+ }
+
+ }
+
+Done: ;
+
+ }
+ RpcExcept(1) {
+ //status = NwpMapRpcError(RpcExceptionCode());
+
+ }
+ RpcEndExcept
+
+ //
+ // free up before returning in case of premature exit (eg. RPC exceptions).
+ //
+ if (WkstaKey != NULL)
+ (void) RegCloseKey(WkstaKey);
+ if (WkstaLogonKey != NULL)
+ (void) RegCloseKey(WkstaLogonKey);
+ if (CurrentUserKey != NULL)
+ (void) RegCloseKey(CurrentUserKey);
+ if (CurrentLogonId != NULL)
+ (void) LocalFree((HLOCAL) CurrentLogonId);
+ if (CurrentUser != NULL)
+ (void) LocalFree((HLOCAL) CurrentUser);
+}
+
+
+NTSTATUS
+NwAuthGetCredential(
+ IN PLSA_CLIENT_REQUEST ClientRequest,
+ IN PVOID ProtocolSubmitBuffer,
+ IN PVOID ClientBufferBase,
+ IN ULONG SubmitBufferSize,
+ OUT PVOID *ProtocolReturnBuffer,
+ OUT PULONG ReturnBufferSize,
+ OUT PNTSTATUS ProtocolStatus
+ )
+/*++
+
+Routine Description:
+
+ This routine is the dispatch routine for LsaCallAuthenticationPackage()
+ with a message type of NwAuth_GetCredential. It is called by
+ the NetWare workstation service to get the username and password
+ associated with a logon ID.
+
+Arguments:
+
+ The arguments to this routine are identical to those of LsaApCallPackage.
+ Only the special attributes of these parameters as they apply to
+ this routine are mentioned here.
+
+Return Value:
+
+ STATUS_SUCCESS - Indicates the service completed successfully.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ PNWAUTH_GET_CREDENTIAL_RESPONSE LocalBuf;
+
+
+ UNREFERENCED_PARAMETER(ClientBufferBase);
+
+ //
+ // Ensure the specified Submit Buffer is of reasonable size.
+ //
+ if (SubmitBufferSize < sizeof(NWAUTH_GET_CREDENTIAL_REQUEST)) {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // Allocate a local buffer and a buffer in client's address space.
+ //
+ *ReturnBufferSize = sizeof(NWAUTH_GET_CREDENTIAL_RESPONSE);
+
+ LocalBuf = RtlAllocateHeap(NwAuthHeap, 0, *ReturnBufferSize);
+
+ if (LocalBuf == NULL) {
+ return STATUS_NO_MEMORY;
+ }
+
+ Status = (*Lsa.AllocateClientBuffer)(
+ ClientRequest,
+ *ReturnBufferSize,
+ (PVOID *) ProtocolReturnBuffer
+ );
+
+ if (! NT_SUCCESS( Status )) {
+ RtlFreeHeap(NwAuthHeap, 0, LocalBuf);
+ return Status;
+ }
+
+ //
+ // Get the credential from LSA
+ //
+ Status = AuthpGetCredential(
+ &(((PNWAUTH_GET_CREDENTIAL_REQUEST) ProtocolSubmitBuffer)->LogonId),
+ LocalBuf
+ );
+
+ if (! NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ //
+ // Copy the data to the client's address space.
+ //
+ Status = (*Lsa.CopyToClientBuffer)(
+ ClientRequest,
+ *ReturnBufferSize,
+ (PVOID) *ProtocolReturnBuffer,
+ (PVOID) LocalBuf
+ );
+
+Cleanup:
+
+ RtlFreeHeap(NwAuthHeap, 0, LocalBuf);
+
+ //
+ // If we weren't successful, free the buffer in the clients address space.
+ // Otherwise, the client will free the memory when done.
+ //
+
+ if (! NT_SUCCESS(Status)) {
+
+ (VOID) (*Lsa.FreeClientBuffer)(
+ ClientRequest,
+ *ProtocolReturnBuffer
+ );
+
+ *ProtocolReturnBuffer = NULL;
+ }
+
+ //
+ // Return status to the caller.
+ //
+ *ProtocolStatus = Status;
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+NwAuthSetCredential(
+ IN PLSA_CLIENT_REQUEST ClientRequest,
+ IN PVOID ProtocolSubmitBuffer,
+ IN PVOID ClientBufferBase,
+ IN ULONG SubmitBufferSize,
+ OUT PVOID *ProtocolReturnBuffer,
+ OUT PULONG ReturnBufferSize,
+ OUT PNTSTATUS ProtocolStatus
+ )
+/*++
+
+Routine Description:
+
+ This routine is the dispatch routine for LsaCallAuthenticationPackage()
+ with a message type of NwAuth_SetCredential. It is called by
+ the NetWare credential manager DLL on user logon to save the username
+ and password of the logon session.
+
+Arguments:
+
+ The arguments to this routine are identical to those of LsaApCallPackage.
+ Only the special attributes of these parameters as they apply to
+ this routine are mentioned here.
+
+Return Value:
+
+ STATUS_SUCCESS - Indicates the service completed successfully.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+
+ UNREFERENCED_PARAMETER(ClientBufferBase);
+
+
+ //
+ // Ensure the specified Submit Buffer is of reasonable size.
+ //
+ if (SubmitBufferSize < sizeof(NWAUTH_SET_CREDENTIAL_REQUEST)) {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("NwAuthSetCredential: LogonId %08lx%08lx Username %ws\n",
+ ((PNWAUTH_SET_CREDENTIAL_REQUEST) ProtocolSubmitBuffer)->LogonId.HighPart,
+ ((PNWAUTH_SET_CREDENTIAL_REQUEST) ProtocolSubmitBuffer)->LogonId.LowPart,
+ ((PNWAUTH_SET_CREDENTIAL_REQUEST) ProtocolSubmitBuffer)->UserName
+ ));
+ }
+#endif
+
+ //
+ // Set the credential in LSA
+ //
+ Status = AuthpSetCredential(
+ &(((PNWAUTH_SET_CREDENTIAL_REQUEST) ProtocolSubmitBuffer)->LogonId),
+ ((PNWAUTH_SET_CREDENTIAL_REQUEST) ProtocolSubmitBuffer)->UserName,
+ ((PNWAUTH_SET_CREDENTIAL_REQUEST) ProtocolSubmitBuffer)->Password
+ );
+
+ *ProtocolStatus = Status;
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+AuthpGetCredential(
+ IN PLUID LogonId,
+ OUT PNWAUTH_GET_CREDENTIAL_RESPONSE CredBuf
+ )
+/*++
+
+Routine Description:
+
+ This routine retrieves the credential saved in LSA given the
+ logon ID.
+
+Arguments:
+
+ LogonId - Supplies the logon ID for the logon session.
+
+ CredBuf - Buffer to receive the credential.
+
+Return Value:
+
+
+--*/
+{
+ NTSTATUS Status;
+
+ STRING KeyString;
+ STRING CredString;
+ ULONG QueryContext = 0;
+ ULONG KeyLength;
+
+ PNWCREDENTIAL Credential;
+
+
+ RtlInitString(&KeyString, NW_CREDENTIAL_KEY);
+
+ Status = (*Lsa.GetCredentials)(
+ LogonId,
+ NwAuthPackageId,
+ &QueryContext,
+ (BOOLEAN) FALSE, // Just retrieve matching key
+ &KeyString,
+ &KeyLength,
+ &CredString
+ );
+
+ if (! NT_SUCCESS(Status)) {
+ return Status;
+ }
+
+ Credential = (PNWCREDENTIAL) CredString.Buffer;
+
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("AuthpGetCredential: Got CredentialSize %lu\n", CredString.Length));
+ }
+#endif
+
+ //
+ // Make the pointers absolute.
+ //
+ Credential->UserName = (LPWSTR) ((DWORD) Credential->UserName +
+ (DWORD) Credential);
+ Credential->Password = (LPWSTR) ((DWORD) Credential->Password +
+ (DWORD) Credential);
+
+ wcscpy(CredBuf->UserName, Credential->UserName);
+ wcscpy(CredBuf->Password, Credential->Password);
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+AuthpSetCredential(
+ IN PLUID LogonId,
+ IN LPWSTR UserName,
+ IN LPWSTR Password
+ )
+/*++
+
+Routine Description:
+
+ This routine saves the credential in LSA.
+
+Arguments:
+
+ LogonId - Supplies the logon ID for the logon session.
+
+ UserName, Password - Credential for the logon session.
+
+Return Value:
+
+
+--*/
+{
+ NTSTATUS Status;
+ PNWCREDENTIAL Credential;
+ DWORD CredentialSize;
+
+ STRING CredString;
+ STRING KeyString;
+
+
+ //
+ // Allocate memory to package the credential.
+ //
+ CredentialSize = sizeof(NWCREDENTIAL) +
+ (wcslen(UserName) + wcslen(Password) + 2) *
+ sizeof(WCHAR);
+
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("AuthpSetCredential: CredentialSize is %lu\n", CredentialSize));
+ }
+#endif
+ Credential = RtlAllocateHeap(NwAuthHeap, 0, CredentialSize);
+
+ if (Credential == NULL) {
+ return STATUS_NO_MEMORY;
+ }
+ RtlZeroMemory(Credential, CredentialSize);
+
+ //
+ // Pack the credential
+ //
+ Credential->UserName = (LPWSTR) (((DWORD) Credential) + sizeof(NWCREDENTIAL));
+ wcscpy(Credential->UserName, UserName);
+
+ Credential->Password = (LPWSTR) ((DWORD) Credential->UserName +
+ (wcslen(UserName) + 1) * sizeof(WCHAR));
+ wcscpy(Credential->Password, Password);
+
+ //
+ // Make the pointers self-relative.
+ //
+ Credential->UserName = (LPWSTR) ((DWORD) Credential->UserName -
+ (DWORD) Credential);
+ Credential->Password = (LPWSTR) ((DWORD) Credential->Password -
+ (DWORD) Credential);
+
+ //
+ // Add credential to logon session
+ //
+ RtlInitString(&KeyString, NW_CREDENTIAL_KEY);
+
+ CredString.Buffer = (PCHAR) Credential;
+ CredString.Length = (USHORT) CredentialSize;
+ CredString.MaximumLength = (USHORT) CredentialSize;
+
+ Status = (*Lsa.AddCredential)(
+ LogonId,
+ NwAuthPackageId,
+ &KeyString,
+ &CredString
+ );
+
+ if (! NT_SUCCESS(Status)) {
+ KdPrint(( "NWPROVAU: AuthpSetCredential: error from AddCredential %lX\n",
+ Status));
+ }
+
+ return Status;
+}
diff --git a/private/nw/svcdlls/nwwks/client/bind.c b/private/nw/svcdlls/nwwks/client/bind.c
new file mode 100644
index 000000000..c87aa7f7d
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/bind.c
@@ -0,0 +1,189 @@
+/*++
+
+Copyright (c) 1991-1993 Microsoft Corporation
+
+Module Name:
+
+ bind.c
+
+Abstract:
+
+ Contains the client-side RPC bind and unbind routines for Workstation
+ service.
+
+Author:
+
+ Rita Wong (ritaw) 12-Feb-1993
+
+Environment:
+
+ User Mode -Win32
+
+Revision History:
+
+--*/
+
+//
+// INCLUDES
+//
+#include <nwclient.h>
+#include <rpcutil.h> // RpcUtils for binding
+#include <svcs.h> // For the common pipe name to bind to
+
+
+
+handle_t
+NWWKSTA_IMPERSONATE_HANDLE_bind(
+ NWWKSTA_IMPERSONATE_HANDLE Reserved
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called from the Workstation service client when
+ it is necessary create an RPC binding to the server end with
+ impersonation level of impersonation.
+
+Arguments:
+
+
+Return Value:
+
+ The binding handle is returned to the stub routine. If the bind is
+ unsuccessful, a NULL will be returned.
+
+--*/
+{
+ handle_t BindHandle = 0;
+ RPC_STATUS RpcStatus;
+
+
+ UNREFERENCED_PARAMETER(Reserved);
+
+ RpcStatus = NetpBindRpc(
+ NULL,
+ SVCS_RPC_PIPE,
+ L"Security=Impersonation Dynamic False",
+ &BindHandle
+ );
+
+ if (RpcStatus != RPC_S_OK) {
+ KdPrint((
+ "NWWORKSTATION: Client NWWKSTA_IMPERSONATE_HANDLE_bind failed: %lu\n",
+ RpcStatus
+ ));
+ }
+
+ return BindHandle;
+}
+
+
+
+handle_t
+NWWKSTA_IDENTIFY_HANDLE_bind(
+ NWWKSTA_IDENTIFY_HANDLE Reserved
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called from the Workstation service client stubs when
+ it is necessary create an RPC binding to the server end with
+ identification level of impersonation.
+
+Arguments:
+
+
+Return Value:
+
+ The binding handle is returned to the stub routine. If the bind is
+ unsuccessful, a NULL will be returned.
+
+--*/
+{
+ handle_t BindHandle = 0;
+ RPC_STATUS RpcStatus;
+
+
+ UNREFERENCED_PARAMETER(Reserved);
+
+ RpcStatus = NetpBindRpc(
+ NULL,
+ SVCS_RPC_PIPE,
+ L"Security=Identification Dynamic False",
+ &BindHandle
+ );
+
+ if (RpcStatus != RPC_S_OK) {
+ KdPrint((
+ "NWWORKSTATION: Client NWWKSTA_IDENTIFY_HANDLE_bind failed: %lu\n",
+ RpcStatus
+ ));
+ }
+
+ return BindHandle;
+}
+
+
+
+void
+NWWKSTA_IMPERSONATE_HANDLE_unbind(
+ NWWKSTA_IMPERSONATE_HANDLE Reserved,
+ handle_t BindHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This routine unbinds the impersonation generic handle.
+
+Arguments:
+
+ Reserved -
+
+ BindingHandle - This is the binding handle that is to be closed.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ UNREFERENCED_PARAMETER(Reserved);
+
+ NetpUnbindRpc(BindHandle);
+}
+
+
+
+void
+NWWKSTA_IDENTIFY_HANDLE_unbind(
+ NWWKSTA_IDENTIFY_HANDLE Reserved,
+ handle_t BindHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This routine unbinds the identification generic handle.
+
+Arguments:
+
+ Reserved -
+
+ BindingHandle - This is the binding handle that is to be closed.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ UNREFERENCED_PARAMETER(Reserved);
+
+ NetpUnbindRpc(BindHandle);
+}
diff --git a/private/nw/svcdlls/nwwks/client/caddress.c b/private/nw/svcdlls/nwwks/client/caddress.c
new file mode 100644
index 000000000..77b49d776
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/caddress.c
@@ -0,0 +1,1183 @@
+/*++
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ address.c
+
+Abstract:
+
+ This module contains the code to support NPGetAddressByName.
+
+Author:
+
+ Yi-Hsin Sung (yihsins) 18-Apr-94
+
+Revision History:
+
+ yihsins Created
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+#include <winsock2.h>
+#include "ncp.h"
+#include <wsipx.h>
+#include <ws2spi.h>
+#include <nwxchg.h>
+#include <ntddnwfs.h>
+#include <rpc.h>
+#include <rpcdce.h>
+#include "rnrdefs.h"
+#include "sapcmn.h"
+#include <time.h>
+#include <rnraddrs.h>
+
+
+//-------------------------------------------------------------------//
+// //
+// Special Externs
+// //
+//-------------------------------------------------------------------//
+
+NTSTATUS
+NwOpenAServer(
+ PWCHAR pwszServName,
+ PHANDLE ServerHandle,
+ BOOL fVerify
+ );
+
+
+//-------------------------------------------------------------------//
+// //
+// Local Function Prototypes //
+// //
+//-------------------------------------------------------------------//
+
+#define IPX_ADDRESS_LENGTH 12
+#define MAX_PROPERTY_BUFFER_LENGTH 128
+
+DWORD
+NwrpGetAddressByNameInner(
+ IN HANDLE hServer,
+ IN WORD nServiceType,
+ IN LPWSTR lpServiceName,
+ IN BOOL fAnsi,
+ IN OUT LPSOCKADDR_IPX lpSockAddr,
+ OUT PDWORD pdwVersion
+ );
+
+
+BOOL
+NwConvertToUnicode(
+ OUT LPWSTR *UnicodeOut,
+ IN LPSTR OemIn
+ );
+
+DWORD
+NwMapBinderyCompletionCode(
+ IN NTSTATUS ntstatus
+ );
+
+#if 0
+DWORD
+NwpFetchClassType(
+ HANDLE hServer,
+ PUNICODE_STRING pUString,
+ PBYTE pbBuffer
+ );
+#endif
+
+DWORD
+NwppGetClassInfo(
+ IN PWCHAR pwszServerName,
+ IN LPWSTR lpszServiceClassName,
+ IN LPGUID lpServiceClassType,
+ OUT PLONG plSpare,
+ OUT PDWORD pdwClassInfos,
+ OUT LPGUID lpFoundType,
+ OUT PWCHAR *ppwszFoundName,
+ IN LONG lSpace,
+ OUT PBYTE pbBuffer
+ );
+
+BOOL
+NwpEnumClassInfoServers(
+ IN OUT PHANDLE phServ,
+ IN OUT PLONG plIndex,
+ IN PWCHAR pwszServerName,
+ IN BOOL fVerify
+ );
+
+#if 0
+
+DWORD
+NwppSetClassInfo(
+ IN LPWSTR pwszClassInfoName,
+ IN LPGUID lpClassType,
+ IN PCHAR pbProperty,
+ IN LPWSTR pwszServerName
+ );
+
+#endif
+
+DWORD
+NwpCreateAndWriteProperty(
+ IN HANDLE hServer,
+ IN LPSTR lpszPropertyName,
+ IN PUNICODE_STRING pusObjectName,
+ IN WORD ObjectType,
+ IN PCHAR pbPropertyBuffer
+ );
+
+//-------------------------------------------------------------------//
+// //
+// Function Bodies //
+// //
+//-------------------------------------------------------------------//
+
+
+DWORD
+NwpGetHandleForServer(
+ PWCHAR pwszServerName,
+ PHANDLE phServer,
+ BOOL fVerify
+ )
+/*++
+Routine Description:
+ Find a handle to use, or make one. This calls into device.c to do the
+ real work.
+--*/
+{
+ DWORD err = NO_ERROR;
+
+ if(!*phServer)
+ {
+ if(!pwszServerName)
+ {
+ pwszServerName = NW_RDR_PREFERRED_SUFFIX;
+ }
+
+
+ err = NwOpenAServer(pwszServerName, phServer, fVerify);
+ }
+ return(err);
+}
+
+
+DWORD
+NwpGetRnRAddress(
+ IN OUT PHANDLE phServer,
+ IN LPWSTR lpszContext,
+ IN OUT PLONG plIndex,
+ IN LPWSTR lpServiceName,
+ IN WORD nType,
+ OUT PDWORD pdwVersion,
+ DWORD dwInSize,
+ OUT LPWSTR ServiceName,
+ OUT LPSOCKADDR_IPX lpSockAddr
+ )
+/*++
+Routine Description:
+ Called to get the name and address of the next item of nType type.
+ If a name is supplied as well, then there is no enumeration. This is
+ called from NSPLookupServiceNext and the parameters are close analogs
+ of the ones it receives.
+--*/
+{
+ NTSTATUS ntstatus;
+ CHAR szObjectName[48];
+ DWORD err = NO_ERROR;
+ PWCHAR pwszObjectName;
+ PWCHAR pwszConv;
+ BOOL fAll, fAnsi;
+
+ //
+ // Open a server for enumeration and querying
+ //
+
+ err = NwpGetHandleForServer(lpszContext, phServer, FALSE);
+ if(err == NO_ERROR)
+ {
+ if(!lpServiceName)
+ {
+ lpServiceName = L"*";
+ }
+ if(wcschr(lpServiceName, L'*'))
+ {
+ WORD ObjectType;
+ //
+ // we've no name, or we have an enumeration
+ //
+
+ UNICODE_STRING U;
+
+ RtlInitUnicodeString(&U, lpServiceName);
+
+ ntstatus = NwlibMakeNcp(
+ *phServer,
+ FSCTL_NWR_NCP_E3H,
+ 58,
+ 59,
+ "bdwU|dwc",
+ 0x37,
+ *plIndex,
+ nType,
+ &U,
+ plIndex,
+ &ObjectType,
+ &szObjectName);
+
+ if(NT_SUCCESS(ntstatus))
+ {
+
+ //
+ // got another one.
+ //
+
+ //
+ // got another one. Convert the name
+ //
+
+ if(!NwConvertToUnicode(&pwszConv, szObjectName))
+ {
+ //
+ // out of space ...
+ //
+
+ err = WN_NO_MORE_ENTRIES;
+ }
+
+ fAll = TRUE;
+
+ if(nType == OT_DIRSERVER)
+ {
+ //
+ // looking for DIRSERVERs is tricky and requires
+ // preserving the name intact. This includes some
+ // binary cruft, so special case it.
+ //
+ fAnsi = TRUE;
+ pwszObjectName = (PWCHAR)szObjectName;
+ }
+ else
+ {
+ fAnsi = FALSE;
+ pwszObjectName = pwszConv;
+ }
+ }
+ }
+ else
+ {
+ //
+ // a non-enumerattion name was given. Use it
+ //
+
+ fAnsi = FALSE;
+ pwszConv = pwszObjectName = lpServiceName;
+ fAll = FALSE;
+ ntstatus = 0;
+ }
+
+ if((err == NO_ERROR)
+ &&
+ NT_SUCCESS(ntstatus))
+ {
+ //
+ // we've a name and type to lookup. Call the old RnR
+ // serice routine to do it. First, return the name.
+ // But return the name first
+
+ DWORD dwLen;
+
+ if(fAnsi)
+ {
+ //
+ // it's an NDS tree server. Have to munge the name
+ // a bit
+ //
+
+ PWCHAR pwszTemp = &pwszConv[31];
+
+ while(*pwszTemp == L'_')
+ {
+ pwszTemp--;
+ }
+ dwLen = (PCHAR)pwszTemp - (PCHAR)pwszConv + sizeof(WCHAR);
+ }
+ else
+ {
+ dwLen = wcslen(pwszConv) * sizeof(WCHAR);
+ }
+
+ dwLen = min(dwInSize, dwLen);
+
+ RtlCopyMemory(ServiceName, pwszConv, dwLen);
+
+ memset(((PBYTE)ServiceName) + dwLen,
+ 0,
+ dwInSize - dwLen);
+
+ err = NwrpGetAddressByNameInner(
+ *phServer,
+ nType,
+ pwszObjectName,
+ fAnsi,
+ lpSockAddr,
+ pdwVersion);
+
+ if(fAll)
+ {
+ LocalFree(pwszConv);
+ }
+ }
+ }
+ if(err == NO_ERROR)
+ {
+ err = NwMapBinderyCompletionCode(ntstatus);
+ }
+ return(err);
+}
+
+DWORD
+NwpGetAddressByName(
+ IN LPWSTR Reserved,
+ IN WORD nServiceType,
+ IN LPWSTR lpServiceName,
+ IN OUT LPSOCKADDR_IPX lpSockAddr
+ )
+/*++
+
+Routine Description:
+
+ This routine returns address information about a specific service.
+
+Arguments:
+
+ Reserved - unused
+
+ nServiceType - netware service type
+
+ lpServiceName - unique string representing the service name, in the
+ Netware case, this is the server name
+
+ lpSockAddr - on return, will be filled with SOCKADDR_IPX
+
+Return Value:
+
+ Win32 error.
+
+--*/
+{
+
+ NTSTATUS ntstatus;
+ HANDLE hServer = 0;
+ DWORD err;
+
+ UNREFERENCED_PARAMETER( Reserved );
+
+ err = NwpGetHandleForServer( 0, &hServer, FALSE );
+
+ if (err == NO_ERROR)
+ {
+ err = NwrpGetAddressByNameInner(
+ hServer,
+ nServiceType,
+ lpServiceName,
+ FALSE,
+ lpSockAddr,
+ 0);
+ CloseHandle(hServer);
+ }
+ return(err);
+}
+
+DWORD
+NwrpGetAddressByNameInner(
+ IN HANDLE hServer,
+ IN WORD nServiceType,
+ IN LPWSTR lpServiceName,
+ IN BOOL fAnsi,
+ IN OUT LPSOCKADDR_IPX lpSockAddr,
+ OUT PDWORD pdwVersion
+ )
+/*++
+
+Routine Description:
+
+ This routine returns address information about a specific service.
+
+Arguments:
+
+ Reserved - unused
+
+ nServiceType - netware service type
+
+ lpServiceName - unique string representing the service name, in the
+ Netware case, this is the server name
+
+ lpSockAddr - on return, will be filled with SOCKADDR_IPX
+
+ fAnsi -- the input name is in ASCII. This happens only when looking
+ for a DIRSERVER.
+
+Return Value:
+
+ Win32 error.
+
+--*/
+{
+
+ NTSTATUS ntstatus;
+ UNICODE_STRING UServiceName;
+ STRING PropertyName;
+ BYTE PropertyValueBuffer[MAX_PROPERTY_BUFFER_LENGTH];
+ BYTE fMoreSegments;
+ PCHAR pszFormat;
+
+
+
+ //
+ // Send an ncp to find the address of the given service name
+ //
+ RtlInitString( &PropertyName, "NET_ADDRESS" );
+ if(!fAnsi)
+ {
+ RtlInitUnicodeString( &UServiceName, lpServiceName );
+ pszFormat = "bwUbp|rb";
+
+ ntstatus = NwlibMakeNcp(
+ hServer,
+ FSCTL_NWR_NCP_E3H, // Bindery function
+ 72, // Max request packet size
+ 132, // Max response packet size
+ pszFormat, // Format string
+ 0x3D, // Read Property Value
+ nServiceType, // Object Type
+ &UServiceName, // Object Name
+ 1, // Segment Number
+ PropertyName.Buffer, // Property Name
+ PropertyValueBuffer, // Ignore
+ MAX_PROPERTY_BUFFER_LENGTH, // size of buffer
+ &fMoreSegments // TRUE if there are more
+ // 128-byte segments
+ );
+
+ if ( NT_SUCCESS( ntstatus))
+ {
+ //
+ // IPX address should fit into the first 128 byte
+ //
+ ASSERT( !fMoreSegments );
+
+ //
+ // Fill in the return buffer
+ //
+ lpSockAddr->sa_family = AF_IPX;
+
+ RtlCopyMemory( lpSockAddr->sa_netnum,
+ PropertyValueBuffer,
+ IPX_ADDRESS_LENGTH );
+
+ if(pdwVersion)
+ {
+ //
+ // the caller wants the version as well. Get it
+ //
+ RtlInitString( &PropertyName, "VERSION" );
+ ntstatus = NwlibMakeNcp(
+ hServer,
+ FSCTL_NWR_NCP_E3H, // Bindery function
+ 72, // Max request packet size
+ 132, // Max response packet size
+ pszFormat, // Format string
+ 0x3D, // Read Property Value
+ nServiceType, // Object Type
+ &UServiceName, // Object Name
+ 1, // Segment Number
+ PropertyName.Buffer, // Property Name
+ PropertyValueBuffer, // Ignore
+ MAX_PROPERTY_BUFFER_LENGTH, // size of buffer
+ &fMoreSegments // TRUE if there are more
+ // 128-byte segments
+ );
+ if(NT_SUCCESS(ntstatus))
+ {
+ //
+ // have a version
+ //
+
+ *pdwVersion = *(PDWORD)PropertyValueBuffer;
+ }
+ else
+ {
+ ntstatus = STATUS_SUCCESS;
+ *pdwVersion = 0;
+ }
+ }
+ }
+ }
+ else
+ {
+ //
+ // exact match needed
+ //
+
+ pszFormat = "bwbrbp|rb";
+
+ ntstatus = NwlibMakeNcp(
+ hServer,
+ FSCTL_NWR_NCP_E3H, // Bindery function
+ 66, // Max request packet size
+ 132, // Max response packet size
+ pszFormat, // Format string
+ 0x3D, // Read Property Value
+ nServiceType, // Object Type
+ 48,
+ lpServiceName, // Object Name
+ 48,
+ 1, // Segment Number
+ PropertyName.Buffer, // Property Name
+ PropertyValueBuffer, // Ignore
+ MAX_PROPERTY_BUFFER_LENGTH, // size of buffer
+ &fMoreSegments // TRUE if there are more
+ // 128-byte segments
+ );
+
+ if ( NT_SUCCESS( ntstatus))
+ {
+ //
+ // IPX address should fit into the first 128 byte
+ //
+ ASSERT( !fMoreSegments );
+
+ //
+ // Fill in the return buffer
+ //
+ lpSockAddr->sa_family = AF_IPX;
+
+ RtlCopyMemory( lpSockAddr->sa_netnum,
+ PropertyValueBuffer,
+ IPX_ADDRESS_LENGTH );
+
+ if(pdwVersion)
+ {
+ //
+ // the caller wants the version as well. Get it
+ //
+ RtlInitString( &PropertyName, "VERSION" );
+ ntstatus = NwlibMakeNcp(
+ hServer,
+ FSCTL_NWR_NCP_E3H, // Bindery function
+ 66, // Max request packet size
+ 132, // Max response packet size
+ pszFormat, // Format string
+ 0x3D, // Read Property Value
+ nServiceType, // Object Type
+ 48,
+ lpServiceName, // Object Name
+ 48,
+ 1, // Segment Number
+ PropertyName.Buffer, // Property Name
+ PropertyValueBuffer, // Ignore
+ MAX_PROPERTY_BUFFER_LENGTH, // size of buffer
+ &fMoreSegments // TRUE if there are more
+ // 128-byte segments
+ );
+ if(NT_SUCCESS(ntstatus))
+ {
+ //
+ // have a version
+ //
+
+ *pdwVersion = *(PDWORD)PropertyValueBuffer;
+ }
+ else
+ {
+ ntstatus = STATUS_SUCCESS;
+ *pdwVersion = 0;
+ }
+ }
+ }
+
+ }
+ return NwMapBinderyCompletionCode(ntstatus);
+}
+
+#if 0
+DWORD
+NwpSetClassInfo(
+ IN LPWSTR lpszServiceClassName,
+ IN LPGUID lpServiceClassType,
+ IN PCHAR lpbProperty
+ )
+{
+ WCHAR wszServerName[48];
+ LONG lIndex = -1;
+ BOOL fFoundOne = FALSE;
+ HANDLE hServer = 0;
+
+ while(NwpEnumClassInfoServers( &hServer, &lIndex, wszServerName, FALSE))
+ {
+ DWORD Status = NwppSetClassInfo(
+ lpszServiceClassName,
+ lpServiceClassType,
+ lpbProperty,
+ wszServerName);
+
+ if(Status == NO_ERROR)
+ {
+ fFoundOne = TRUE;
+ }
+ }
+ if(fFoundOne)
+ {
+ return(NO_ERROR);
+ }
+ return(NO_DATA);
+}
+
+DWORD
+NwppSetClassInfo(
+ IN LPWSTR pwszClassInfoName,
+ IN LPGUID lpClassType,
+ IN PCHAR pbProperty,
+ IN LPWSTR pwszServerName
+ )
+{
+/*++
+Routine Description:
+ Inner routine for SetClassInfo. This is called for each class info
+ server and attempts to create and populate the object
+--*/
+ HANDLE hServer = 0;
+ DWORD err;
+ UNICODE_STRING UString;
+ WCHAR wszProp[48];
+ DWORD dwLen = wcslen(pwszClassInfoName);
+ PWCHAR pszProp;
+ NTSTATUS Status;
+
+ UuidToString(lpClassType, &pszProp);
+
+ memset(wszProp, 0, sizeof(wszProp));
+
+ dwLen = min(sizeof(wszProp) - sizeof(WCHAR), dwLen);
+
+ RtlMoveMemory(wszProp, pwszClassInfoName, dwLen);
+
+ RtlInitUnicodeString(&UString, pszProp);
+
+ err = NwpGetHandleForServer(pwszServerName, &hServer, TRUE);
+ if(err == NO_ERROR)
+ {
+
+ Status = NwlibMakeNcp(
+ hServer,
+ FSCTL_NWR_NCP_E3H,
+ 56,
+ 2,
+ "bbbwU|",
+ 0x32, // create
+ 0, // static
+ 0x20, // security
+ RNRCLASSSAPTYPE, // type
+ &UString);
+
+ if(!NT_SUCCESS(Status)
+ &&
+ ((Status & 0xff) != 0xEE))
+ {
+ err = NO_DATA; // can't do it here
+ }
+ else
+ {
+
+ //
+ // create and write each property
+ //
+
+
+ err = NwpCreateAndWriteProperty(
+ hServer,
+ RNRTYPE, // property name
+ &UString, // object name
+ RNRCLASSSAPTYPE, // object type
+ (PCHAR)pwszClassInfoName);
+
+ err = NwpCreateAndWriteProperty(
+ hServer,
+ RNRCLASSES,
+ &UString,
+ RNRCLASSSAPTYPE, // object type
+ pbProperty); // and this one too
+ }
+ }
+ if(hServer)
+ {
+ CloseHandle(hServer);
+ }
+
+ RpcStringFree(&pszProp);
+
+ return(err);
+}
+
+DWORD
+NwpGetClassInfo(
+ IN LPWSTR lpszServiceClassName,
+ IN LPGUID lpServiceClassType,
+ OUT PLONG plSpare,
+ OUT PDWORD pdwClassInfos,
+ OUT LPGUID lpFoundType,
+ OUT PWCHAR *ppwszFoundName,
+ IN LONG lSpace,
+ OUT PBYTE pbBuffer
+ )
+{
+/*++
+Routine Description:
+ Wrapper for the routine below. This comes up with the server name
+ and decides whether to enumerate servers
+
+--*/
+
+ HANDLE hServer = 0;
+ DWORD err;
+ NTSTATUS ntstatus;
+ LONG lIndex = -1;
+ HANDLE hServ = 0;
+ WCHAR wszObjectName[48];
+
+ while(NwpEnumClassInfoServers(&hServer, &lIndex, wszObjectName, FALSE))
+ {
+ WORD ObjectType;
+ PWCHAR pwszName;
+
+
+ err = NwppGetClassInfo(
+ wszObjectName,
+ lpszServiceClassName,
+ lpServiceClassType,
+ plSpare,
+ pdwClassInfos,
+ lpFoundType,
+ ppwszFoundName,
+ lSpace,
+ pbBuffer);
+ if((err == NO_ERROR)
+ ||
+ (err == WSAEFAULT))
+ {
+ CloseHandle(hServer);
+ break;
+ }
+ }
+ return(err);
+}
+
+BOOL
+NwpEnumClassInfoServers(
+ IN OUT PHANDLE phServer,
+ IN OUT PLONG plIndex,
+ OUT PWCHAR pwszServerName,
+ IN BOOL fVerify)
+{
+/*++
+Routine Description:
+ Common routine to enumerate Class Info servers. Nothing fancy just
+ a way to issue the NCP
+--*/
+ WORD ObjectType;
+ PWCHAR pwszName;
+ NTSTATUS Status;
+ CHAR szObjectName[48];
+ BOOL fRet;
+ DWORD err;
+
+ err = NwpGetHandleForServer(0, phServer, fVerify);
+ if(err == NO_ERROR)
+ {
+ Status = NwlibMakeNcp(
+ *phServer,
+ FSCTL_NWR_NCP_E3H,
+ 58,
+ 59,
+ "bdwp|dwc",
+ 0x37,
+ *plIndex,
+ CLASSINFOSAPID,
+ "*",
+ plIndex,
+ &ObjectType,
+ &szObjectName);
+ if(!NT_SUCCESS(Status))
+ {
+ err = NwMapBinderyCompletionCode(Status);
+ }
+ else if(!NwConvertToUnicode(&pwszName, szObjectName))
+ {
+ err = ERROR_NOT_ENOUGH_MEMORY;
+ }
+ else
+ {
+ wcscpy(pwszServerName, pwszName);
+ LocalFree(pwszName);
+ }
+ }
+ if(err != NO_ERROR)
+ {
+ fRet = FALSE;
+ if(*phServer)
+ {
+ CloseHandle(*phServer);
+ *phServer = 0;
+ }
+ }
+ else
+ {
+ fRet = TRUE;
+ }
+ return(fRet);
+}
+
+DWORD
+NwppGetClassInfo(
+ IN PWCHAR pwszServerName,
+ IN LPWSTR lpszServiceClassName,
+ IN LPGUID lpServiceClassType,
+ OUT PLONG plSpare,
+ OUT PDWORD pdwClassInfos,
+ OUT LPGUID lpFoundType,
+ OUT PWCHAR *ppwszFoundName,
+ IN LONG lSpace,
+ OUT PBYTE pbBuffer
+ )
+{
+/*++
+Routine Description
+ Find and return the class info information for the given Class.
+ The general methodology is to look up the object
+ in the registry, suck out the RnR property, pack what is read into
+ Class Info structures, and voila!
+Arguments:
+ lpServiceClassName the class name
+ lpServiceClassType the class type
+ plSpare Space needed if no class infos returned
+ pdwClassInfos Number of class infos returned
+ lSpace the space available on input
+ pbBuffer the scratch are for building this
+
+
+This was originally an RPC method and the general structure has been preserved
+in case we want to revert to using RPC once again.
+--*/
+
+ DWORD err = NO_ERROR;
+ BYTE PropertyValueBuffer[MAX_PROPERTY_BUFFER_LENGTH]; // max segment size
+ STRING PropertyName;
+ UNICODE_STRING UString;
+ OEM_STRING OString;
+ LPWSANSCLASSINFOW pci = (LPWSANSCLASSINFO)pbBuffer;
+ LONG lFreeSpace = lSpace;
+ PBYTE pbFreeSpace = (PBYTE)((LONG)pbBuffer + lFreeSpace);
+ BYTE fMoreSegments;
+ HANDLE hServer = 0;
+ NTSTATUS ntstatus;
+ PWCHAR pwszName;
+
+ UuidToString(lpServiceClassType, &pwszName);
+
+ *pdwClassInfos = 0;
+ *plSpare = 0; // no space needed yet.
+ err = NwpGetHandleForServer(pwszServerName, &hServer, FALSE);
+
+ if(err == NO_ERROR)
+ {
+ DWORD Segment;
+ PBINDERYCLASSES pbc = (PBINDERYCLASSES)PropertyValueBuffer;
+ DWORD dwTotalSize;
+ DWORD dwSS;
+
+ //
+ // init the Class Info stuff
+ //
+
+ //
+ // pwszName is the name of the object we want to use. We must
+ // fetch all of the Class Info stuff to return.
+ //
+ //
+
+ RtlInitUnicodeString(&UString, pwszName);
+
+ RtlMoveMemory(lpFoundType,
+ lpServiceClassType,
+ sizeof(GUID));
+
+ RtlInitString(&PropertyName, RNRCLASSES); // where the data is
+ for(Segment = 1;; Segment++)
+ {
+ ntstatus = NwlibMakeNcp(
+ hServer,
+ FSCTL_NWR_NCP_E3H, // Bindery function
+ 72, // Max request packet size
+ 132, // Max response packet size
+ "bwUbp|rb", // Format string
+ 0x3D, // Read Property Value
+ RNRCLASSSAPTYPE, // Object Type
+ &UString, // Object Name
+ (BYTE)Segment,
+ PropertyName.Buffer, // Property Name
+ PropertyValueBuffer, // Ignore
+ MAX_PROPERTY_BUFFER_LENGTH, // size of buffer
+ &fMoreSegments // TRUE if there are more
+ // 128-byte segments
+ );
+ if(!NT_SUCCESS(ntstatus))
+ {
+ break;
+ }
+ //
+ // got another value. Stuff it in if it fits. In all
+ // cases, compute the space needed.
+ //
+
+
+ if((pbc->bType != BT_WORD)
+ &&
+ (pbc->bType != BT_DWORD))
+ {
+ //
+ // Don't know what to do with these ...
+ //
+
+ err = WSAEPFNOSUPPORT;
+ break;
+ }
+
+ dwSS = (DWORD)pbc->bSizeOfString;
+ dwTotalSize = (DWORD)pbc->bSizeOfType +
+ ((dwSS + 1) * sizeof(WCHAR)) +
+ sizeof(DWORD) - 1;
+
+ dwTotalSize &= ~(sizeof(DWORD) - 1);
+ *plSpare += (LONG)dwTotalSize + sizeof(WSANSCLASSINFO); // running total
+
+ lFreeSpace -= (LONG)dwTotalSize + sizeof(WSANSCLASSINFO);
+ if(lFreeSpace >= 0)
+ {
+ PBYTE pbString;
+ PCHAR pbData = (PCHAR)((PCHAR)pbc +
+ (DWORD)pbc->bOffset);
+ BYTE bRnRName[128];
+ PWCHAR pwszRnR;
+
+ //
+ // it fits. Pack it in
+ //
+
+ pbFreeSpace = (PBYTE)((DWORD)pbFreeSpace - dwTotalSize);
+ *pdwClassInfos += 1; // one more class info.
+ pci->dwNameSpace = (DWORD)ntohs(pbc->wNameSpace);
+ pci->dwValueType = REG_DWORD;
+ pci->dwValueSize = (DWORD)pbc->bSizeOfType;
+ pci->lpValue = (PVOID)(pbFreeSpace - pbBuffer);
+ pci->lpszName = (PWCHAR)((PBYTE)pci->lpValue +
+ pci->dwValueSize);
+ pci->dwConnectionFlags = (DWORD)pbc->bFlags;
+ pci++;
+
+ //
+ // now copy the values.
+ //
+
+
+ if(pbc->bType == BT_WORD)
+ {
+ *(PWORD)pbFreeSpace = ntohs(*(PWORD)pbData);
+ pbString = (PBYTE)((DWORD)pbFreeSpace + sizeof(WORD));
+ pbData = pbData + sizeof(WORD);
+ }
+ else
+ {
+ *(PDWORD)pbFreeSpace = ntohl(*(PDWORD)pbData);
+ pbString = (PBYTE)((DWORD)pbFreeSpace + sizeof(DWORD));
+ pbData = pbData + sizeof(DWORD);
+ }
+
+ //
+ // the name is in ASCII, and not null terminated.
+ //
+
+ RtlMoveMemory(bRnRName, pbData, dwSS);
+ bRnRName[dwSS] = 0;
+ if(!NwConvertToUnicode(&pwszRnR, bRnRName))
+ {
+ //
+ // bad news. Out of space.
+ //
+
+ err = GetLastError();
+ break;
+ }
+
+ RtlMoveMemory(pbString,
+ pwszRnR,
+ (dwSS + 1) * sizeof(WCHAR));
+ LocalFree(pwszRnR);
+
+ }
+ }
+ if(err == NO_ERROR)
+ {
+ if(!*ppwszFoundName)
+ {
+ LONG lLen;
+
+ //
+ // need to return the name
+ //
+
+ err = NwpFetchClassType(hServer,
+ &UString,
+ PropertyValueBuffer);
+
+ if(err == NO_ERROR)
+ {
+ lLen = (wcslen((PWCHAR)PropertyValueBuffer) + 1) *
+ sizeof(WCHAR);
+
+ lFreeSpace -= lLen;
+ *plSpare += lLen;
+
+ if(lFreeSpace >= 0)
+ {
+ //
+ // it fits. Move it
+
+ pbFreeSpace = (PBYTE)((DWORD)pbFreeSpace - lLen);
+ RtlMoveMemory(pbFreeSpace, PropertyValueBuffer, lLen);
+ *ppwszFoundName = (PWCHAR)(pbFreeSpace - pbBuffer);
+ }
+ if(lFreeSpace < 0)
+ {
+ err = WSAEFAULT;
+ }
+ }
+ }
+ }
+ else if(*pdwClassInfos == 0)
+ {
+ err = NO_DATA;
+ }
+ }
+
+ CloseHandle(hServer);
+ RpcStringFree(&pwszName);
+ return(err);
+}
+
+DWORD
+NwpFetchClassType(
+ HANDLE hServer,
+ PUNICODE_STRING pUString,
+ PBYTE pbBuffer)
+{
+/*++
+Routine Description
+ Common routine to read the class type buffer.
+--*/
+ BYTE fMoreSegments;
+ STRING PropertyName;
+ NTSTATUS ntstatus;
+
+ RtlInitString(&PropertyName, RNRTYPE); // where the GUID is
+
+ ntstatus = NwlibMakeNcp(
+ hServer,
+ FSCTL_NWR_NCP_E3H, // Bindery function
+ 72, // Max request packet size
+ 132, // Max response packet size
+ "bwUbp|rb", // Format string
+ 0x3D, // Read Property Value
+ RNRCLASSSAPTYPE, // Object Type
+ pUString, // Object Name
+ 1, // Segment Number
+ PropertyName.Buffer, // Property Name
+ pbBuffer,
+ MAX_PROPERTY_BUFFER_LENGTH, // size of buffer
+ &fMoreSegments // TRUE if there are more
+ // 128-byte segments
+ );
+
+ if(!NT_SUCCESS(ntstatus))
+ {
+ return(WSASERVICE_NOT_FOUND);
+ }
+ return(NO_ERROR);
+}
+
+#endif
+DWORD
+NwpCreateAndWriteProperty(
+ IN HANDLE hServer,
+ IN LPSTR lpszPropertyName,
+ IN PUNICODE_STRING pusObjectName,
+ IN WORD wObjectType,
+ IN PCHAR pbPropertyBuffer
+ )
+{
+/*++
+Routine Description:
+ Create the named property and write the data.
+Arguments:
+
+ hServer: handle to the server
+ lpszPropertyName Name of the property
+ pusObjectName Name of the object
+ wObjectType Type of the object
+ pbPropertyBuffer The property data. Must be 128 bytes
+
+Note that the return is always NO_ERROR for now. This may change in the future.
+--*/
+ NTSTATUS Status;
+
+ Status = NwlibMakeNcp(
+ hServer,
+ FSCTL_NWR_NCP_E3H,
+ 73,
+ 2,
+ "bwUbbp|",
+ 0x39, // create property
+ wObjectType,
+ pusObjectName,
+ 0, // static/item
+ 0x20, // security
+ lpszPropertyName
+ );
+
+ //
+ // Now write the porperty data
+ //
+ Status = NwlibMakeNcp(
+ hServer,
+ FSCTL_NWR_NCP_E3H,
+ 201,
+ 2,
+ "bwUbbpr|",
+ 0x3E, // write property
+ wObjectType,
+ pusObjectName,
+ 1, // one segment
+ 0,
+ lpszPropertyName,
+ pbPropertyBuffer, 128);
+
+ return(NO_ERROR);
+}
+
+
diff --git a/private/nw/svcdlls/nwwks/client/ccache.c b/private/nw/svcdlls/nwwks/client/ccache.c
new file mode 100644
index 000000000..9374352fc
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/ccache.c
@@ -0,0 +1,324 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ ccache.c
+
+Abstract:
+
+ This module contains the code to keep a cache of the user
+ credentials. The cache is used mainly for a user browsing from
+ winfile.
+
+Author:
+
+ Chuck Y Chan (chuckc) 4-Dec-93
+
+Revision History:
+
+ chuckc Created
+
+--*/
+
+#include <nwclient.h>
+#include <nwcanon.h>
+#include <nwapi.h>
+
+//-------------------------------------------------------------------//
+// //
+// Local Function Prototypes //
+// //
+//-------------------------------------------------------------------//
+
+DWORD
+ExtractServerName(
+ IN LPWSTR RemoteName,
+ OUT LPWSTR ServerName,
+ IN DWORD ServerNameSize
+) ;
+
+//-------------------------------------------------------------------//
+// //
+// Global variables //
+// //
+//-------------------------------------------------------------------//
+
+static WCHAR CachedPassword[NW_MAX_PASSWORD_LEN+1] ;
+static WCHAR CachedUserName[NW_MAX_USERNAME_LEN+1] ;
+static WCHAR CachedServerName[NW_MAX_SERVER_LEN+1] ;
+static DWORD CachedCredentialsTime ;
+static UNICODE_STRING CachedPasswordUnicodeStr ;
+static UCHAR EncodeSeed = 0 ;
+
+//-------------------------------------------------------------------//
+// //
+// Function Bodies //
+// //
+//-------------------------------------------------------------------//
+
+DWORD
+NwpCacheCredentials(
+ IN LPWSTR RemoteName,
+ IN LPWSTR UserName,
+ IN LPWSTR Password
+ )
+/*++
+
+Routine Description:
+
+ This function caches the user credentials for the particular
+ server.
+
+Arguments:
+
+ RemoteName - path containg the server we are accessing. only the
+ server component is of interest.
+
+ UserName - user name to remember
+
+ Password - password to remember
+
+Return Value:
+
+ NO_ERROR - Successfully cached the credentials
+
+ Win32 error code otherwise.
+
+--*/
+{
+ DWORD status ;
+
+ //
+ // various paramter checks
+ //
+ if (!RemoteName || !UserName || !Password)
+ {
+ status = ERROR_INVALID_PARAMETER ;
+ goto ExitPoint ;
+ }
+
+ if (wcslen(UserName) >= sizeof(CachedUserName)/sizeof(CachedUserName[0]))
+ {
+ status = ERROR_INVALID_PARAMETER ;
+ goto ExitPoint ;
+ }
+
+ if (wcslen(Password) >= sizeof(CachedPassword)/sizeof(CachedPassword[0]))
+ {
+ status = ERROR_INVALID_PARAMETER ;
+ goto ExitPoint ;
+ }
+
+ //
+ // extract the server portion of the path
+ //
+ status = ExtractServerName(
+ RemoteName,
+ CachedServerName,
+ sizeof(CachedServerName)/sizeof(CachedServerName[0])) ;
+
+ if (status != NO_ERROR)
+ {
+ goto ExitPoint ;
+ }
+
+ //
+ // save away the credentials
+ //
+ wcscpy(CachedUserName, UserName) ;
+ wcscpy(CachedPassword, Password) ;
+
+ //
+ // encode it since it is in page pool
+ //
+ RtlInitUnicodeString(&CachedPasswordUnicodeStr, CachedPassword) ;
+ RtlRunEncodeUnicodeString(&EncodeSeed, &CachedPasswordUnicodeStr) ;
+
+ //
+ // mark the time this happened
+ //
+ CachedCredentialsTime = GetTickCount() ;
+
+ return NO_ERROR ;
+
+ExitPoint:
+
+ CachedServerName[0] = 0 ;
+ return status ;
+}
+
+
+BOOL
+NwpRetrieveCachedCredentials(
+ IN LPWSTR RemoteName,
+ OUT LPWSTR *UserName,
+ OUT LPWSTR *Password
+ )
+/*++
+
+Routine Description:
+
+ This function retrieves the cached user credentials for the particular
+ server.
+
+Arguments:
+
+ RemoteName - path containg the server we are accessing. only the
+ server component is of interest.
+
+ UserName - used to return user name
+
+ Password - used to return password
+
+Return Value:
+
+ NO_ERROR - Successfully returned at least one entry.
+
+ Win32 error code otherwise.
+
+--*/
+{
+ DWORD status ;
+ DWORD CurrentTime ;
+ WCHAR ServerName[NW_MAX_SERVER_LEN+1] ;
+
+ *UserName = NULL ;
+ *Password = NULL ;
+ CurrentTime = GetTickCount() ;
+
+ if (!RemoteName)
+ {
+ return FALSE ;
+ }
+
+ //
+ // if too old, bag out
+ //
+ if (((CurrentTime > CachedCredentialsTime) &&
+ (CurrentTime - CachedCredentialsTime) > 60000) ||
+ ((CurrentTime < CachedCredentialsTime) &&
+ (CurrentTime + (MAXULONG - CachedCredentialsTime)) >= 60000))
+ {
+ CachedServerName[0] = 0 ; // reset to nothing
+ return FALSE ;
+ }
+
+ status = ExtractServerName(
+ RemoteName,
+ ServerName,
+ sizeof(ServerName)/sizeof(ServerName[0])) ;
+
+ if (status != NO_ERROR)
+ {
+ return FALSE ;
+ }
+
+ //
+ // if dont compare, bag out
+ //
+ if (_wcsicmp(ServerName, CachedServerName) != 0)
+ {
+ return FALSE ;
+ }
+
+ //
+ // allocate memory to return data
+ //
+ if (!(*UserName = (LPWSTR) LocalAlloc(
+ LPTR,
+ (wcslen(CachedUserName)+1) * sizeof(WCHAR))))
+ {
+ return FALSE ;
+ }
+
+ if (!(*Password = (LPWSTR) LocalAlloc(
+ LPTR,
+ (wcslen(CachedPassword)+1) * sizeof(WCHAR))))
+ {
+ LocalFree((HLOCAL)*UserName) ;
+ *UserName = NULL ;
+ return FALSE ;
+ }
+
+ //
+ // decode the string,copy it and then reencode it
+ //
+ RtlRunDecodeUnicodeString(EncodeSeed, &CachedPasswordUnicodeStr) ;
+ wcscpy(*Password, CachedPassword) ;
+ RtlRunEncodeUnicodeString(&EncodeSeed, &CachedPasswordUnicodeStr) ;
+
+ wcscpy(*UserName, CachedUserName) ;
+
+ //
+ // update the tick count
+ //
+ CachedCredentialsTime = GetTickCount() ;
+ return TRUE ;
+}
+
+
+DWORD
+ExtractServerName(
+ IN LPWSTR RemoteName,
+ OUT LPWSTR ServerName,
+ IN DWORD ServerNameSize
+)
+/*++
+
+Routine Description:
+
+ This function extracts the server name out of a remote name
+
+Arguments:
+
+ RemoteName - the input string to extract the server name from.
+
+ ServerName - the return buffer for the server string
+
+ ServerNameSize - size o f buffer in chars
+
+Return Value:
+
+ NO_ERROR - Successfully cached the credentials
+
+ Win32 error code otherwise.
+
+--*/
+{
+ LPWSTR ServerStart ;
+ LPWSTR ServerEnd ;
+
+ //
+ // skip initial backslashes, then find next one delimiting the server name
+ //
+ ServerStart = RemoteName ;
+
+ while (*ServerStart == L'\\')
+ ServerStart++ ;
+
+ ServerEnd = wcschr(ServerStart, L'\\') ;
+ if (ServerEnd)
+ *ServerEnd = 0 ;
+
+ //
+ // make sure we can fit
+ //
+ if (wcslen(ServerStart) >= ServerNameSize)
+ {
+ if (ServerEnd)
+ *ServerEnd = L'\\' ;
+ return ERROR_INVALID_PARAMETER ;
+ }
+
+ //
+ // copy and restore the backslash
+ //
+ wcscpy(ServerName, ServerStart) ;
+
+ if (ServerEnd)
+ *ServerEnd = L'\\' ;
+
+ return NO_ERROR ;
+}
diff --git a/private/nw/svcdlls/nwwks/client/drawpie.c b/private/nw/svcdlls/nwwks/client/drawpie.c
new file mode 100644
index 000000000..2bb4f8e92
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/drawpie.c
@@ -0,0 +1,240 @@
+#include <windows.h>
+#include "drawpie.h"
+
+#ifdef WIN32
+#define MoveTo(_hdc,_x,_y) MoveToEx(_hdc, _x, _y, NULL)
+#endif // WIN32
+
+
+int NEAR IntSqrt(unsigned long dwNum)
+{
+ // We will keep shifting dwNum left and look at the top two bits.
+
+ // initialize sqrt and remainder to 0.
+ DWORD dwSqrt = 0, dwRemain = 0, dwTry;
+ int i;
+
+ // We iterate 16 times, once for each pair of bits.
+ for (i=0; i<16; ++i)
+ {
+ // Mask off the top two bits of dwNum and rotate them into the
+ // bottom of the remainder
+ dwRemain = (dwRemain<<2) | (dwNum>>30);
+
+ // Now we shift the sqrt left; next we'll determine whether the
+ // new bit is a 1 or a 0.
+ dwSqrt <<= 1;
+
+ // This is where we double what we already have, and try a 1 in
+ // the lowest bit.
+ dwTry = dwSqrt*2 + 1;
+
+ if (dwRemain >= dwTry)
+ {
+ // The remainder was big enough, so subtract dwTry from
+ // the remainder and tack a 1 onto the sqrt.
+ dwRemain -= dwTry;
+ dwSqrt |= 0x01;
+ }
+
+ // Shift dwNum to the left by 2 so we can work on the next few
+ // bits.
+ dwNum <<= 2;
+ }
+
+ return(dwSqrt);
+}
+
+
+
+VOID NEAR DrawPie(HDC hDC, LPCRECT lprcItem, UINT uPctX10, BOOL TrueZr100,
+ UINT uOffset, const COLORREF FAR *lpColors)
+{
+ int cx, cy, rx, ry, x, y;
+ int uQPctX10;
+ RECT rcItem;
+ HRGN hEllRect, hEllipticRgn, hRectRgn;
+ HBRUSH hBrush, hOldBrush;
+ HPEN hPen, hOldPen;
+
+ rcItem = *lprcItem;
+ rcItem.left = lprcItem->left;
+ rcItem.top = lprcItem->top;
+ rcItem.right = lprcItem->right - rcItem.left;
+ rcItem.bottom = lprcItem->bottom - rcItem.top - uOffset;
+
+ rx = rcItem.right / 2;
+ cx = rcItem.left + rx;
+ ry = rcItem.bottom / 2;
+ cy = rcItem.top + ry;
+ if (rx<=10 || ry<=10)
+ {
+ return;
+ }
+
+ rcItem.right = rcItem.left+2*rx;
+ rcItem.bottom = rcItem.top+2*ry;
+
+ if (uPctX10 > 1000)
+ {
+ uPctX10 = 1000;
+ }
+
+ /* Translate to first quadrant of a Cartesian system
+ */
+ uQPctX10 = (uPctX10 % 500) - 250;
+ if (uQPctX10 < 0)
+ {
+ uQPctX10 = -uQPctX10;
+ }
+
+ /* Calc x and y. I am trying to make the area be the right percentage.
+ ** I don't know how to calculate the area of a pie slice exactly, so I
+ ** approximate it by using the triangle area instead.
+ */
+ if (uQPctX10 < 120)
+ {
+ x = IntSqrt(((DWORD)rx*(DWORD)rx*(DWORD)uQPctX10*(DWORD)uQPctX10)
+ /((DWORD)uQPctX10*(DWORD)uQPctX10+(250L-(DWORD)uQPctX10)*(250L-(DWORD)uQPctX10)));
+
+ y = IntSqrt(((DWORD)rx*(DWORD)rx-(DWORD)x*(DWORD)x)*(DWORD)ry*(DWORD)ry/((DWORD)rx*(DWORD)rx));
+ }
+ else
+ {
+ y = IntSqrt((DWORD)ry*(DWORD)ry*(250L-(DWORD)uQPctX10)*(250L-(DWORD)uQPctX10)
+ /((DWORD)uQPctX10*(DWORD)uQPctX10+(250L-(DWORD)uQPctX10)*(250L-(DWORD)uQPctX10)));
+
+ x = IntSqrt(((DWORD)ry*(DWORD)ry-(DWORD)y*(DWORD)y)*(DWORD)rx*(DWORD)rx/((DWORD)ry*(DWORD)ry));
+ }
+
+ /* Switch on the actual quadrant
+ */
+ switch (uPctX10 / 250)
+ {
+ case 1:
+ y = -y;
+ break;
+
+ case 2:
+ break;
+
+ case 3:
+ x = -x;
+ break;
+
+ default: // case 0 and case 4
+ x = -x;
+ y = -y;
+ break;
+ }
+
+ /* Now adjust for the center.
+ */
+ x += cx;
+ y += cy;
+
+ /* Draw the shadows using regions (to reduce flicker).
+ */
+ hEllipticRgn = CreateEllipticRgnIndirect(&rcItem);
+ OffsetRgn(hEllipticRgn, 0, uOffset);
+ hEllRect = CreateRectRgn(rcItem.left, cy, rcItem.right, cy+uOffset);
+ hRectRgn = CreateRectRgn(0, 0, 0, 0);
+ CombineRgn(hRectRgn, hEllipticRgn, hEllRect, RGN_OR);
+ OffsetRgn(hEllipticRgn, 0, -(int)uOffset);
+ CombineRgn(hEllRect, hRectRgn, hEllipticRgn, RGN_DIFF);
+
+ /* Always draw the whole area in the free shadow/
+ */
+ hBrush = CreateSolidBrush(lpColors[DP_FREESHADOW]);
+ if (hBrush)
+ {
+ FillRgn(hDC, hEllRect, hBrush);
+ DeleteObject(hBrush);
+ }
+
+ /* Draw the used shadow only if the disk is at least half used.
+ */
+ if (uPctX10>500 && (hBrush=CreateSolidBrush(lpColors[DP_USEDSHADOW]))!=NULL)
+ {
+ DeleteObject(hRectRgn);
+ hRectRgn = CreateRectRgn(x, cy, rcItem.right, lprcItem->bottom);
+ CombineRgn(hEllipticRgn, hEllRect, hRectRgn, RGN_AND);
+ FillRgn(hDC, hEllipticRgn, hBrush);
+ DeleteObject(hBrush);
+ }
+
+ DeleteObject(hRectRgn);
+ DeleteObject(hEllipticRgn);
+ DeleteObject(hEllRect);
+
+ hPen = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_WINDOWFRAME));
+ hOldPen = SelectObject(hDC, hPen);
+
+ if((uPctX10 < 100) && (cy == y))
+ {
+ hBrush = CreateSolidBrush(lpColors[DP_FREECOLOR]);
+ hOldBrush = SelectObject(hDC, hBrush);
+ if((TrueZr100 == FALSE) || (uPctX10 != 0))
+ {
+ Pie(hDC, rcItem.left, rcItem.top, rcItem.right, rcItem.bottom,
+ rcItem.left, cy, x, y);
+ }
+ else
+ {
+ Ellipse(hDC, rcItem.left, rcItem.top, rcItem.right,
+ rcItem.bottom);
+ }
+ }
+ else if((uPctX10 > (1000 - 100)) && (cy == y))
+ {
+ hBrush = CreateSolidBrush(lpColors[DP_USEDCOLOR]);
+ hOldBrush = SelectObject(hDC, hBrush);
+ if((TrueZr100 == FALSE) || (uPctX10 != 1000))
+ {
+ Pie(hDC, rcItem.left, rcItem.top, rcItem.right, rcItem.bottom,
+ rcItem.left, cy, x, y);
+ }
+ else
+ {
+ Ellipse(hDC, rcItem.left, rcItem.top, rcItem.right,
+ rcItem.bottom);
+ }
+ }
+ else
+ {
+ hBrush = CreateSolidBrush(lpColors[DP_USEDCOLOR]);
+ hOldBrush = SelectObject(hDC, hBrush);
+
+ Ellipse(hDC, rcItem.left, rcItem.top, rcItem.right, rcItem.bottom);
+ SelectObject(hDC, hOldBrush);
+ DeleteObject(hBrush);
+
+ hBrush = CreateSolidBrush(lpColors[DP_FREECOLOR]);
+ hOldBrush = SelectObject(hDC, hBrush);
+ Pie(hDC, rcItem.left, rcItem.top, rcItem.right, rcItem.bottom,
+ rcItem.left, cy, x, y);
+ }
+ SelectObject(hDC, hOldBrush);
+ DeleteObject(hBrush);
+
+ /* Do not draw the lines if the %age is truely 0 or 100 (completely
+ ** empty disk or completly full disk)
+ */
+ if((TrueZr100 == FALSE) || ((uPctX10 != 0) && (uPctX10 != 1000)))
+ {
+ Arc(hDC, rcItem.left, rcItem.top+uOffset, rcItem.right, rcItem.bottom+uOffset,
+ rcItem.left, cy+uOffset, rcItem.right, cy+uOffset-1);
+ MoveTo(hDC, rcItem.left, cy);
+ LineTo(hDC, rcItem.left, cy+uOffset);
+ MoveTo(hDC, rcItem.right-1, cy);
+ LineTo(hDC, rcItem.right-1, cy+uOffset);
+
+ if (uPctX10 > 500)
+ {
+ MoveTo(hDC, x, y);
+ LineTo(hDC, x, y+uOffset);
+ }
+ }
+ SelectObject(hDC, hOldPen);
+ DeleteObject(hPen);
+}
diff --git a/private/nw/svcdlls/nwwks/client/drawpie.h b/private/nw/svcdlls/nwwks/client/drawpie.h
new file mode 100644
index 000000000..24edb4abc
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/drawpie.h
@@ -0,0 +1,9 @@
+int NEAR IntSqrt(unsigned long dwNum);
+
+#define DP_USEDCOLOR 0
+#define DP_FREECOLOR 1
+#define DP_USEDSHADOW 2
+#define DP_FREESHADOW 3
+
+VOID NEAR DrawPie(HDC hDC, LPCRECT prcItem, UINT uPctX10, BOOL TrueZr100,
+ UINT uOffset, const COLORREF FAR *lpColors);
diff --git a/private/nw/svcdlls/nwwks/client/folderop.ico b/private/nw/svcdlls/nwwks/client/folderop.ico
new file mode 100644
index 000000000..ab7b05d9d
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/folderop.ico
Binary files differ
diff --git a/private/nw/svcdlls/nwwks/client/getaddr.c b/private/nw/svcdlls/nwwks/client/getaddr.c
new file mode 100644
index 000000000..2673eddae
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/getaddr.c
@@ -0,0 +1,3619 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ getaddr.c
+
+Abstract:
+
+ This module contains the code to support NPGetAddressByName.
+
+Author:
+
+ Yi-Hsin Sung (yihsins) 18-Apr-94
+ Glenn A. Curtis (glennc) 31-Jul-95
+ Arnold Miller (ArnoldM) 7-Dec-95
+
+Revision History:
+
+ yihsins Created
+ glennc Modified 31-Jul-95
+ ArnoldM Modified 7-Dec-95
+
+--*/
+
+
+#include <nwclient.h>
+#include <winsock.h>
+#include <wsipx.h>
+#include <nspapi.h>
+#include <nspapip.h>
+#include <wsnwlink.h>
+#include <svcguid.h>
+#include <nwsap.h>
+#include <align.h>
+#include <nwmisc.h>
+#include <rnrdefs.h>
+
+//-------------------------------------------------------------------//
+// //
+// Local Function Prototypes //
+// //
+//-------------------------------------------------------------------//
+
+#define NW_SAP_PRIORITY_VALUE_NAME L"SapPriority"
+#define NW_WORKSTATION_SVCPROVIDER_REGKEY L"System\\CurrentControlSet\\Services\\NWCWorkstation\\ServiceProvider"
+
+#define NW_GUID_VALUE_NAME L"GUID"
+#define NW_SERVICETYPES_KEY_NAME L"ServiceTypes"
+#define NW_SERVICE_TYPES_REGKEY L"System\\CurrentControlSet\\Control\\ServiceProvider\\ServiceTypes"
+
+#define DLL_VERSION 1
+#define WSOCK_VER_REQD 0x0101
+
+//
+// critical sections used
+//
+
+extern CRITICAL_SECTION NwServiceListCriticalSection;
+
+ // have been returned
+BOOL
+OldRnRCheckCancel(
+ PVOID pvArg
+ );
+
+DWORD
+OldRnRCheckSapData(
+ PSAP_BCAST_CONTROL psbc,
+ PSAP_IDENT_HEADER pSap,
+ PDWORD pdwErr
+ );
+
+DWORD
+SapGetSapForType(
+ PSAP_BCAST_CONTROL psbc,
+ WORD nServiceType
+ );
+
+DWORD
+SapFreeSapSocket(
+ SOCKET s
+ );
+
+DWORD
+SapGetSapSocket(
+ SOCKET * ppsocket
+ );
+
+VOID
+pFreeAllContexts();
+
+PSAP_RNR_CONTEXT
+SapGetContext(
+ IN HANDLE Handle
+ );
+
+PSAP_RNR_CONTEXT
+SapMakeContext(
+ IN HANDLE Handle,
+ IN DWORD dwExcess
+ );
+
+VOID
+SapReleaseContext(
+ PSAP_RNR_CONTEXT psrcContext
+ );
+
+INT
+SapGetAddressByName(
+ IN LPGUID lpServiceType,
+ IN LPWSTR lpServiceName,
+ IN LPDWORD lpdwProtocols,
+ IN DWORD dwResolution,
+ IN OUT LPVOID lpCsAddrBuffer,
+ IN OUT LPDWORD lpdwBufferLength,
+ IN OUT LPWSTR lpAliasBuffer,
+ IN OUT LPDWORD lpdwAliasBufferLength,
+ IN HANDLE hCancellationEvent
+);
+
+DWORD
+SapGetService (
+ IN LPGUID lpServiceType,
+ IN LPWSTR lpServiceName,
+ IN DWORD dwProperties,
+ IN BOOL fUnicodeBlob,
+ OUT LPSERVICE_INFO lpServiceInfo,
+ IN OUT LPDWORD lpdwBufferLen
+);
+
+DWORD
+SapSetService (
+ IN DWORD dwOperation,
+ IN DWORD dwFlags,
+ IN BOOL fUnicodeBlob,
+ IN LPSERVICE_INFO lpServiceInfo
+);
+
+DWORD
+NwpGetAddressViaSap(
+ IN WORD nServiceType,
+ IN LPWSTR lpServiceName,
+ IN DWORD nProt,
+ IN OUT LPVOID lpCsAddrBuffer,
+ IN OUT LPDWORD lpdwBufferLength,
+ IN HANDLE hCancellationEvent,
+ OUT LPDWORD lpcAddress
+);
+
+BOOL
+NwpLookupSapInRegistry(
+ IN LPGUID lpServiceType,
+ OUT PWORD pnSapType,
+ OUT PWORD pwPort,
+ IN OUT PDWORD pfConnectionOriented
+);
+
+DWORD
+NwpRnR2AddServiceType(
+ IN LPWSTR lpServiceTypeName,
+ IN LPGUID lpClassType,
+ IN WORD wSapId,
+ IN WORD wPort
+);
+
+DWORD
+NwpAddServiceType(
+ IN LPSERVICE_INFO lpServiceInfo,
+ IN BOOL fUnicodeBlob
+);
+
+DWORD
+NwpDeleteServiceType(
+ IN LPSERVICE_INFO lpServiceInfo,
+ IN BOOL fUnicodeBlob
+);
+
+DWORD
+FillBufferWithCsAddr(
+ IN LPBYTE pAddress,
+ IN DWORD nProt,
+ IN OUT LPVOID lpCsAddrBuffer,
+ IN OUT LPDWORD lpdwBufferLength,
+ OUT LPDWORD pcAddress
+);
+
+DWORD
+AddServiceToList(
+ IN LPSERVICE_INFO lpServiceInfo,
+ IN WORD nSapType,
+ IN BOOL fAdvertiseBySap,
+ IN INT nIndexIPXAddress
+);
+
+VOID
+RemoveServiceFromList(
+ IN PREGISTERED_SERVICE pSvc
+);
+
+DWORD
+pSapSetService2(
+ IN DWORD dwOperation,
+ IN LPWSTR lpszServiceInstance,
+ IN PBYTE pbAddress,
+ IN LPGUID pType,
+ IN WORD nServiceType
+ );
+
+DWORD
+pSapSetService(
+ IN DWORD dwOperation,
+ IN LPSERVICE_INFO lpServiceInfo,
+ IN WORD nServiceType
+ );
+
+//
+// Misc Functions
+//
+
+DWORD NwInitializeSocket(
+ IN HANDLE hEventHandle
+);
+
+DWORD
+NwAdvertiseService(
+ IN LPWSTR pServiceName,
+ IN WORD nSapType,
+ IN LPSOCKADDR_IPX pAddr,
+ IN HANDLE hEventHandle
+);
+
+DWORD SapFunc(
+ IN HANDLE hEventHandle
+);
+
+DWORD
+NwpGetAddressByName(
+ IN LPWSTR Reserved,
+ IN WORD nServiceType,
+ IN LPWSTR lpServiceName,
+ IN OUT LPSOCKADDR_IPX lpsockaddr
+);
+
+
+
+//-------------------------------------------------------------------//
+// //
+// Global variables //
+// //
+//-------------------------------------------------------------------//
+
+//
+// This is the address we send to
+//
+
+UCHAR SapBroadcastAddress[] = {
+ AF_IPX, 0, // Address Family
+ 0x00, 0x00, 0x00, 0x00, // Dest. Net Number
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Dest. Node Number
+ 0x04, 0x52, // Dest. Socket
+ 0x04 // Packet type
+};
+
+PSAP_RNR_CONTEXT psrcSapContexts;
+
+//
+// Misc. variables used if we need to advertise ourselves, i.e.
+// when the SAP service is not installed/active.
+//
+
+BOOL fInitSocket = FALSE; // TRUE if we have created the second thread
+SOCKET socketSap; // Socket used to send SAP advertise packets
+PREGISTERED_SERVICE pServiceListHead = NULL; // Points to head of link list
+PREGISTERED_SERVICE pServiceListTail = NULL; // Points to tail of link list
+
+//
+// needed to map old and new RnR functions
+//
+DWORD oldRnRServiceRegister = SERVICE_REGISTER;
+DWORD oldRnRServiceDeRegister = SERVICE_DEREGISTER;
+
+
+//-------------------------------------------------------------------//
+// //
+// Function Bodies //
+// //
+//-------------------------------------------------------------------//
+
+VOID
+pFreeAllContexts()
+/*++
+Routine Description:
+ Called at Cleanup time to free all NSP resource
+--*/
+{
+ PSAP_RNR_CONTEXT psrcContext;
+
+ EnterCriticalSection( &NwServiceListCriticalSection );
+ while(psrcContext = psrcSapContexts)
+ {
+ (VOID)SapReleaseContext(psrcContext);
+ }
+ LeaveCriticalSection( &NwServiceListCriticalSection );
+}
+
+PSAP_RNR_CONTEXT
+SapGetContext(HANDLE Handle)
+/*++
+
+Routine Description:
+
+ This routine checks the existing SAP contexts to see if we have one
+ for this calll.
+
+Arguments:
+
+ Handle - the RnR handle, if appropriate
+
+--*/
+{
+ PSAP_RNR_CONTEXT psrcContext;
+
+ EnterCriticalSection( &NwServiceListCriticalSection );
+
+ for(psrcContext = psrcSapContexts;
+ psrcContext && (psrcContext->Handle != Handle);
+ psrcContext = psrcContext->pNextContext);
+
+ if(psrcContext)
+ {
+ ++psrcContext->lInUse;
+ }
+ LeaveCriticalSection( &NwServiceListCriticalSection );
+ return(psrcContext);
+}
+
+PSAP_RNR_CONTEXT
+SapMakeContext(
+ IN HANDLE Handle,
+ IN DWORD dwExcess
+ )
+{
+/*++
+
+Routine Description:
+
+ This routine makes a SAP conext for a given RnR handle
+
+Arguments:
+
+ Handle - the RnR handle. If NULL, use the context as the handle
+ dwType - the type of the context
+
+--*/
+ PSAP_RNR_CONTEXT psrcContext;
+
+ psrcContext = (PSAP_RNR_CONTEXT)
+ LocalAlloc(LPTR, sizeof(SAP_RNR_CONTEXT) +
+ dwExcess);
+ if(psrcContext)
+ {
+ InitializeCriticalSection(&psrcContext->u_type.sbc.csMonitor);
+ psrcContext->lInUse = 2;
+ psrcContext->Handle = (Handle ? Handle : (HANDLE)psrcContext);
+ psrcContext->lSig = RNR_SIG;
+ EnterCriticalSection( &NwServiceListCriticalSection );
+ psrcContext->pNextContext = psrcSapContexts;
+ psrcSapContexts = psrcContext;
+ LeaveCriticalSection( &NwServiceListCriticalSection );
+ }
+ return(psrcContext);
+}
+
+VOID
+SapReleaseContext(PSAP_RNR_CONTEXT psrcContext)
+/*++
+
+Routine Description:
+
+ Dereference an RNR Context and free it if it is no longer referenced.
+ Determining no referneces is a bit tricky because we try to avoid
+ obtaining the CritSec unless we think the context may be unneeded. Hence
+ the code goes through some fuss. It could be much simpler if we always
+ obtained the CritSec whenever we changed the reference count, but
+ this is faster for the nominal case.
+
+Arguments:
+
+ psrcContext -- The context
+
+--*/
+{
+ EnterCriticalSection( &NwServiceListCriticalSection );
+ if(--psrcContext->lInUse == 0)
+ {
+ PSAP_RNR_CONTEXT psrcX, psrcPrev;
+ PSAP_DATA psdData;
+
+ //
+ // Done with it. Remove from the lisgt
+ //
+
+ psrcPrev = 0;
+ for(psrcX = psrcSapContexts;
+ psrcX;
+ psrcX = psrcX->pNextContext)
+ {
+ if(psrcX == psrcContext)
+ {
+ //
+ // Found it.
+ //
+
+ if(psrcPrev)
+ {
+ psrcPrev->pNextContext = psrcContext->pNextContext;
+ }
+ else
+ {
+ psrcSapContexts = psrcContext->pNextContext;
+ }
+ break;
+ }
+ psrcPrev = psrcX;
+ }
+
+ ASSERT(psrcX);
+
+ //
+ // release SAP data, if any
+ //
+ if(psrcContext->dwUnionType == LOOKUP_TYPE_SAP)
+ {
+ for(psdData = psrcContext->u_type.sbc.psdHead;
+ psdData;)
+ {
+ PSAP_DATA psdTemp = psdData->sapNext;
+
+ LocalFree(psdData);
+ psdData = psdTemp;
+ }
+
+ if(psrcContext->u_type.sbc.s)
+ {
+ SapFreeSapSocket(psrcContext->u_type.sbc.s);
+ }
+ }
+ DeleteCriticalSection(&psrcContext->u_type.sbc.csMonitor);
+ if(psrcContext->hServer)
+ {
+ CloseHandle(psrcContext->hServer);
+ }
+ LocalFree(psrcContext);
+ }
+ LeaveCriticalSection( &NwServiceListCriticalSection );
+}
+
+INT
+APIENTRY
+NPLoadNameSpaces(
+ IN OUT LPDWORD lpdwVersion,
+ IN OUT LPNS_ROUTINE nsrBuffer,
+ IN OUT LPDWORD lpdwBufferLength
+ )
+{
+/*++
+
+Routine Description:
+
+ This routine returns name space info and functions supported in this
+ dll.
+
+Arguments:
+
+ lpdwVersion - dll version
+
+ nsrBuffer - on return, this will be filled with an array of
+ NS_ROUTINE structures
+
+ lpdwBufferLength - on input, the number of bytes contained in the buffer
+ pointed to by nsrBuffer. On output, the minimum number of bytes
+ to pass for the nsrBuffer to retrieve all the requested info
+
+Return Value:
+
+ The number of NS_ROUTINE structures returned, or SOCKET_ERROR (-1) if
+ the nsrBuffer is too small. Use GetLastError() to retrieve the
+ error code.
+
+--*/
+ DWORD err;
+ DWORD dwLengthNeeded;
+ HKEY providerKey;
+
+ DWORD dwSapPriority = NS_STANDARD_FAST_PRIORITY;
+
+ *lpdwVersion = DLL_VERSION;
+
+ //
+ // Check to see if the buffer is large enough
+ //
+ dwLengthNeeded = sizeof(NS_ROUTINE) + 4 * sizeof(LPFN_NSPAPI);
+
+ if ( ( *lpdwBufferLength < dwLengthNeeded )
+ || ( nsrBuffer == NULL )
+ )
+ {
+ *lpdwBufferLength = dwLengthNeeded;
+ SetLastError( ERROR_INSUFFICIENT_BUFFER );
+ return (DWORD) SOCKET_ERROR;
+ }
+
+ //
+ // Get the Sap priority from the registry. We will ignore all errors
+ // from the registry and have a default priority if we failed to read
+ // the value.
+ //
+ err = RegOpenKeyExW( HKEY_LOCAL_MACHINE,
+ NW_WORKSTATION_SVCPROVIDER_REGKEY,
+ 0,
+ KEY_READ,
+ &providerKey );
+
+ if ( !err )
+ {
+ DWORD BytesNeeded = sizeof( dwSapPriority );
+ DWORD ValueType;
+
+ err = RegQueryValueExW( providerKey,
+ NW_SAP_PRIORITY_VALUE_NAME,
+ NULL,
+ &ValueType,
+ (LPBYTE) &dwSapPriority,
+ &BytesNeeded );
+
+ if ( err ) // set default priority if error occurred
+ dwSapPriority = NS_STANDARD_FAST_PRIORITY;
+ }
+
+ //
+ // We only support 1 name space for now, so fill in the NS_ROUTINE.
+ //
+ nsrBuffer->dwFunctionCount = 3;
+ nsrBuffer->alpfnFunctions = (LPFN_NSPAPI *)
+ ((BYTE *) nsrBuffer + sizeof(NS_ROUTINE));
+ (nsrBuffer->alpfnFunctions)[NSPAPI_GET_ADDRESS_BY_NAME] =
+ (LPFN_NSPAPI) SapGetAddressByName;
+ (nsrBuffer->alpfnFunctions)[NSPAPI_GET_SERVICE] =
+ (LPFN_NSPAPI) SapGetService;
+ (nsrBuffer->alpfnFunctions)[NSPAPI_SET_SERVICE] =
+ (LPFN_NSPAPI) SapSetService;
+ (nsrBuffer->alpfnFunctions)[3] = NULL;
+
+ nsrBuffer->dwNameSpace = NS_SAP;
+ nsrBuffer->dwPriority = dwSapPriority;
+
+ return 1; // number of namespaces
+}
+
+INT
+SapGetAddressByName(
+ IN LPGUID lpServiceType,
+ IN LPWSTR lpServiceName,
+ IN LPDWORD lpdwProtocols,
+ IN DWORD dwResolution,
+ IN OUT LPVOID lpCsAddrBuffer,
+ IN OUT LPDWORD lpdwBufferLength,
+ IN OUT LPWSTR lpAliasBuffer,
+ IN OUT LPDWORD lpdwAliasBufferLength,
+ IN HANDLE hCancellationEvent
+ )
+/*++
+
+Routine Description:
+
+ This routine returns address information about a specific service.
+
+Arguments:
+
+ lpServiceType - pointer to the GUID for the service type
+
+ lpServiceName - unique string representing the service name, in the
+ Netware case, this is the server name
+
+ lpdwProtocols - a zero terminated array of protocol ids. This parameter
+ is optional; if lpdwProtocols is NULL, information on all available
+ Protocols is returned
+
+ dwResolution - can be one of the following values:
+ RES_SOFT_SEARCH, RES_FIND_MULTIPLE
+
+ lpCsAddrBuffer - on return, will be filled with CSADDR_INFO structures
+
+ lpdwBufferLength - on input, the number of bytes contained in the buffer
+ pointed to by lpCsAddrBuffer. On output, the minimum number of bytes
+ to pass for the lpCsAddrBuffer to retrieve all the requested info
+
+ lpAliasBuffer - not used
+
+ lpdwAliasBufferLength - not used
+
+ hCancellationEvent - the event which signals us to cancel the request
+
+Return Value:
+
+ The number of CSADDR_INFO structures returned, or SOCKET_ERROR (-1) if
+ the lpCsAddrBuffer is too small. Use GetLastError() to retrieve the
+ error code.
+
+--*/
+{
+ DWORD err;
+ WORD nServiceType;
+ DWORD cAddress = 0; // Count of the number of address returned
+ // in lpCsAddrBuffer
+ DWORD cProtocols = 0; // Count of the number of protocols contained
+ // in lpdwProtocols + 1 ( for zero terminate )
+ DWORD nProt = IPX_BIT | SPXII_BIT;
+ DWORD fConnectionOriented = (DWORD) -1;
+
+ if ( ARGUMENT_PRESENT( lpdwAliasBufferLength )
+ && ARGUMENT_PRESENT( lpAliasBuffer )
+ )
+ {
+ if ( *lpdwAliasBufferLength >= sizeof(WCHAR) )
+ *lpAliasBuffer = 0;
+ }
+
+ //
+ // Check for invalid parameters
+ //
+ if ( ( lpServiceType == NULL )
+ || ( lpServiceName == NULL )
+ || ( lpdwBufferLength == NULL )
+ )
+ {
+ SetLastError( ERROR_INVALID_PARAMETER );
+ return SOCKET_ERROR;
+ }
+
+ //
+ // If an array of protocol ids is passed in, check to see if
+ // the IPX protocol is requested. If not, return 0 since
+ // we only support IPX.
+ //
+ if ( lpdwProtocols != NULL )
+ {
+ INT i = -1;
+
+ nProt = 0;
+ while ( lpdwProtocols[++i] != 0 )
+ {
+ if ( lpdwProtocols[i] == NSPROTO_IPX )
+ nProt |= IPX_BIT;
+
+ if ( lpdwProtocols[i] == NSPROTO_SPX )
+ nProt |= SPX_BIT;
+
+ if ( lpdwProtocols[i] == NSPROTO_SPXII )
+ nProt |= SPXII_BIT;
+
+ }
+
+ if ( nProt == 0 )
+ return 0; // No address found
+
+ cProtocols = i+1;
+ }
+
+ //
+ // Check to see if the service type is supported in NetWare
+ //
+ if ( NwpLookupSapInRegistry( lpServiceType, &nServiceType, NULL,
+ &fConnectionOriented ))
+ {
+ if ( fConnectionOriented != -1 ) // Got value from registry
+ {
+ if ( fConnectionOriented )
+ {
+ nProt &= ~IPX_BIT;
+ }
+ else // connectionless
+ {
+ nProt &= ~(SPX_BIT | SPXII_BIT );
+ }
+
+ if ( nProt == 0 )
+ return 0; // No address found
+ }
+ }
+ else
+ {
+ //
+ // Couldn't find it in the registry, see if it is a well-known GUID
+ //
+ if ( IS_SVCID_NETWARE( lpServiceType ))
+ {
+ nServiceType = SAPID_FROM_SVCID_NETWARE( lpServiceType );
+ }
+ else
+ {
+ //
+ // Not a well-known GUID either
+ //
+ return 0; // No address found
+ }
+ }
+
+
+ if ((dwResolution & RES_SERVICE) != 0)
+ {
+ err = FillBufferWithCsAddr( NULL,
+ nProt,
+ lpCsAddrBuffer,
+ lpdwBufferLength,
+ &cAddress );
+
+ if ( err )
+ {
+ SetLastError( err );
+ return SOCKET_ERROR;
+ }
+
+ return cAddress;
+ }
+
+ //
+ // Try to get the address from the bindery first
+ //
+ RpcTryExcept
+ {
+ SOCKADDR_IPX sockaddr;
+
+ err = NwpGetAddressByName( NULL,
+ nServiceType,
+ lpServiceName,
+ &sockaddr );
+
+ if ( err == NO_ERROR )
+ {
+ err = FillBufferWithCsAddr( sockaddr.sa_netnum,
+ nProt,
+ lpCsAddrBuffer,
+ lpdwBufferLength,
+ &cAddress );
+ }
+
+ }
+ RpcExcept(1)
+ {
+ DWORD code = RpcExceptionCode();
+
+ if ( code == RPC_S_SERVER_UNAVAILABLE )
+ err = ERROR_SERVICE_NOT_ACTIVE;
+ else
+ err = NwpMapRpcError( code );
+ }
+ RpcEndExcept
+
+ if ( err
+ && ( err != ERROR_INSUFFICIENT_BUFFER )
+ )
+ {
+ if ( (!(dwResolution & RES_SOFT_SEARCH))
+ || ( err == ERROR_SERVICE_NOT_ACTIVE )
+ )
+ {
+ //
+ // We could not find the service name in the bindery, and we
+ // need to try harder ( RES_SOFT_SEARCH not defined ), so send out
+ // SAP query packets to see if we can find it.
+ //
+
+ err = NwpGetAddressViaSap(
+ nServiceType,
+ lpServiceName,
+ nProt,
+ lpCsAddrBuffer,
+ lpdwBufferLength,
+ hCancellationEvent,
+ &cAddress );
+#if DBG
+ IF_DEBUG(OTHER)
+ {
+ if ( err == NO_ERROR )
+ {
+ KdPrint(("Successfully got %d address for %ws from SAP.\n",
+ cAddress, lpServiceName ));
+ }
+ else
+ {
+ KdPrint(("Failed with err %d when getting address for %ws from SAP.\n", err, lpServiceName ));
+ }
+ }
+#endif
+ }
+ else
+ {
+ err = NO_ERROR;
+ cAddress = 0;
+ }
+ }
+
+ if ( err )
+ {
+ SetLastError( err );
+ return SOCKET_ERROR;
+ }
+
+ return cAddress;
+
+}
+
+DWORD
+SapGetService (
+ IN LPGUID lpServiceType,
+ IN LPWSTR lpServiceName,
+ IN DWORD dwProperties,
+ IN BOOL fUnicodeBlob,
+ OUT LPSERVICE_INFO lpServiceInfo,
+ IN OUT LPDWORD lpdwBufferLen
+ )
+/*++
+
+Routine Description:
+
+ This routine returns the service info for the given service type/name.
+
+Arguments:
+
+ lpServiceType - pointer to the GUID for the service type
+
+ lpServiceName - service name
+
+ dwProperties - the properties of the service to return
+
+ lpServiceInfo - points to a buffer to return store the return info
+
+ lpdwBufferLen - on input, the count of bytes in lpServiceInfo. On output,
+ the minimum buffer size that can be passed to this API
+ to retrieve all the requested information
+
+Return Value:
+
+ Win32 error code.
+
+--*/
+{
+ DWORD err;
+ WORD nServiceType;
+
+ //
+ // Check for invalid parameters
+ //
+ if ( ( dwProperties == 0 )
+ || ( lpServiceType == NULL )
+ || ( lpServiceName == NULL )
+ || ( lpServiceName[0] == 0 )
+ || ( lpdwBufferLen == NULL )
+ )
+ {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ //
+ // Check to see if the service type is supported in NetWare
+ //
+ if ( !(NwpLookupSapInRegistry( lpServiceType, &nServiceType, NULL, NULL )))
+ {
+ //
+ // Couldn't find it in the registry, see if it is a well-known GUID
+ //
+ if ( IS_SVCID_NETWARE( lpServiceType ))
+ {
+ nServiceType = SAPID_FROM_SVCID_NETWARE( lpServiceType );
+ }
+ else
+ {
+ //
+ // Not a well-known GUID either, return error
+ //
+ return ERROR_SERVICE_NOT_FOUND;
+ }
+ }
+
+ UNREFERENCED_PARAMETER(fUnicodeBlob) ;
+
+ RpcTryExcept
+ {
+ err = NwrGetService( NULL,
+ nServiceType,
+ lpServiceName,
+ dwProperties,
+ (LPBYTE) lpServiceInfo,
+ *lpdwBufferLen,
+ lpdwBufferLen );
+
+ if ( err == NO_ERROR )
+ {
+ INT i ;
+ LPSERVICE_INFO p = (LPSERVICE_INFO) lpServiceInfo;
+ LPSERVICE_ADDRESS lpAddress ;
+
+ //
+ // fix up pointers n main structure (convert from offsets)
+ //
+ if ( p->lpServiceType != NULL )
+ p->lpServiceType = (LPGUID) ((DWORD) p->lpServiceType +
+ (LPBYTE) p);
+ if ( p->lpServiceName != NULL )
+ p->lpServiceName = (LPWSTR)
+ ((DWORD) p->lpServiceName + (LPBYTE) p);
+ if ( p->lpComment != NULL )
+ p->lpComment = (LPWSTR) ((DWORD) p->lpComment + (LPBYTE) p);
+ if ( p->lpLocale != NULL )
+ p->lpLocale = (LPWSTR) ((DWORD) p->lpLocale + (LPBYTE) p);
+ if ( p->lpMachineName != NULL )
+ p->lpMachineName = (LPWSTR)
+ ((DWORD) p->lpMachineName + (LPBYTE)p);
+ if ( p->lpServiceAddress != NULL )
+ p->lpServiceAddress = (LPSERVICE_ADDRESSES)
+ ((DWORD) p->lpServiceAddress + (LPBYTE) p);
+ if ( p->ServiceSpecificInfo.pBlobData != NULL )
+ p->ServiceSpecificInfo.pBlobData = (LPBYTE)
+ ((DWORD) p->ServiceSpecificInfo.pBlobData + (LPBYTE) p);
+
+ //
+ // fix up pointers in the array of addresses
+ //
+ for (i = p->lpServiceAddress->dwAddressCount;
+ i > 0;
+ i--)
+ {
+ lpAddress =
+ &(p->lpServiceAddress->Addresses[i-1]) ;
+ lpAddress->lpAddress =
+ ((LPBYTE)p) + (DWORD)lpAddress->lpAddress ;
+ lpAddress->lpPrincipal =
+ ((LPBYTE)p) + (DWORD)lpAddress->lpPrincipal ;
+ }
+ }
+ }
+ RpcExcept(1)
+ {
+ err = ERROR_SERVICE_NOT_ACTIVE;
+#if 0 // the following is a good idea, but hard to get right
+ DWORD code = RpcExceptionCode();
+
+ if ( (code == RPC_S_SERVER_UNAVAILABLE)
+ ||
+ (code == RPC_S_UNKNOWN_IF) )
+ err
+ err = ERROR_SERVICE_NOT_ACTIVE;
+ else
+ err = NwpMapRpcError( code );
+#endif
+ }
+ RpcEndExcept
+
+ if ( err == ERROR_SERVICE_NOT_ACTIVE )
+ {
+ //
+ //CSNW not available, going to get it ourselves
+ //
+ err = NwGetService( NULL,
+ nServiceType,
+ lpServiceName,
+ dwProperties,
+ (LPBYTE) lpServiceInfo,
+ *lpdwBufferLen,
+ lpdwBufferLen );
+
+ if ( err == NO_ERROR )
+ {
+ INT i ;
+ LPSERVICE_INFO p = (LPSERVICE_INFO) lpServiceInfo;
+ LPSERVICE_ADDRESS lpAddress ;
+
+ //
+ // fix up pointers n main structure (convert from offsets)
+ //
+ if ( p->lpServiceType != NULL )
+ p->lpServiceType = (LPGUID) ((DWORD) p->lpServiceType +
+ (LPBYTE) p);
+ if ( p->lpServiceName != NULL )
+ p->lpServiceName = (LPWSTR)
+ ((DWORD) p->lpServiceName + (LPBYTE) p);
+ if ( p->lpComment != NULL )
+ p->lpComment = (LPWSTR) ((DWORD) p->lpComment + (LPBYTE) p);
+ if ( p->lpLocale != NULL )
+ p->lpLocale = (LPWSTR) ((DWORD) p->lpLocale + (LPBYTE) p);
+ if ( p->lpMachineName != NULL )
+ p->lpMachineName = (LPWSTR)
+ ((DWORD) p->lpMachineName + (LPBYTE)p);
+ if ( p->lpServiceAddress != NULL )
+ p->lpServiceAddress = (LPSERVICE_ADDRESSES)
+ ((DWORD) p->lpServiceAddress + (LPBYTE) p);
+ if ( p->ServiceSpecificInfo.pBlobData != NULL )
+ p->ServiceSpecificInfo.pBlobData = (LPBYTE)
+ ((DWORD) p->ServiceSpecificInfo.pBlobData + (LPBYTE) p);
+
+ //
+ // fix up pointers in the array of addresses
+ //
+ for (i = p->lpServiceAddress->dwAddressCount;
+ i > 0;
+ i--)
+ {
+ lpAddress =
+ &(p->lpServiceAddress->Addresses[i-1]) ;
+ lpAddress->lpAddress =
+ ((LPBYTE)p) + (DWORD)lpAddress->lpAddress ;
+ lpAddress->lpPrincipal =
+ ((LPBYTE)p) + (DWORD)lpAddress->lpPrincipal ;
+ }
+ }
+ }
+
+ return err;
+}
+
+DWORD
+SapSetService (
+ IN DWORD dwOperation,
+ IN DWORD dwFlags,
+ IN BOOL fUnicodeBlob,
+ IN LPSERVICE_INFO lpServiceInfo
+ )
+/*++
+
+Routine Description:
+
+ This routine registers or deregisters the given service type/name.
+
+Arguments:
+
+ dwOperation - Either SERVICE_REGISTER, SERVICE_DEREGISTER,
+ SERVICE_ADD_TYPE, SERVICE_DELETE_TYPE,
+ or SERVICE_FLUSH
+
+ dwFlags - ignored
+
+ lpServiceInfo - Pointer to a SERVICE_INFO structure containing all info
+ about the service.
+
+Return Value:
+
+ Win32 error code.
+
+--*/
+{
+ DWORD err;
+ WORD nServiceType;
+
+ UNREFERENCED_PARAMETER( dwFlags );
+
+ //
+ // Check for invalid parameters
+ //
+ switch ( dwOperation )
+ {
+ case SERVICE_REGISTER:
+ case SERVICE_DEREGISTER:
+ case SERVICE_ADD_TYPE:
+ case SERVICE_DELETE_TYPE:
+ break;
+
+ case SERVICE_FLUSH:
+ //
+ // This is a no-op in our provider, so just return success
+ //
+ return NO_ERROR;
+
+ default:
+ //
+ // We can probably say all other operations which we have no
+ // knowledge of are ignored by us. So, just return success.
+ //
+ return NO_ERROR;
+ }
+
+ if ( ( lpServiceInfo == NULL )
+ || ( lpServiceInfo->lpServiceType == NULL )
+ || ( ((lpServiceInfo->lpServiceName == NULL) ||
+ (lpServiceInfo->lpServiceName[0] == 0 )) &&
+ ((dwOperation != SERVICE_ADD_TYPE) &&
+ (dwOperation != SERVICE_DELETE_TYPE))
+ )
+
+ )
+ {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ //
+ // See if operation is adding or deleting a service type
+ //
+ if ( dwOperation == SERVICE_ADD_TYPE )
+ {
+ return NwpAddServiceType( lpServiceInfo, fUnicodeBlob );
+ }
+ else if ( dwOperation == SERVICE_DELETE_TYPE )
+ {
+ return NwpDeleteServiceType( lpServiceInfo, fUnicodeBlob );
+ }
+
+ //
+ // Check to see if the service type is supported in NetWare
+ //
+ if ( !(NwpLookupSapInRegistry( lpServiceInfo->lpServiceType, &nServiceType, NULL, NULL )))
+ {
+ //
+ // Couldn't find it in the registry, see if it is a well-known GUID
+ //
+ if ( IS_SVCID_NETWARE( lpServiceInfo->lpServiceType ))
+ {
+ nServiceType = SAPID_FROM_SVCID_NETWARE( lpServiceInfo->lpServiceType );
+ }
+ else
+ {
+ //
+ // Not a well-known GUID either, return error
+ //
+ return ERROR_SERVICE_NOT_FOUND;
+ }
+ }
+
+ //
+ // Operation is either SERVICE_REGISTER or SERVICE_DEREGISTER.
+ // Pass it on to the common code used by this and the RnR2
+ // SetService
+ //
+
+ err = pSapSetService(dwOperation, lpServiceInfo, nServiceType);
+ return(err);
+}
+
+DWORD
+pSapSetService2(
+ IN DWORD dwOperation,
+ IN LPWSTR lpszServiceInstance,
+ IN PBYTE pbAddress,
+ IN LPGUID pType,
+ IN WORD nServiceType
+ )
+/*++
+Routine Description:
+ Jacket routine called by the RnR2 SetService. This routine is
+ an impedance matcher to coerce data structures. It winds
+ up calling pSapSetService2 once it has constructed the
+ SERVICE_INFO structure.
+--*/
+{
+ SERVICE_INFO siInfo;
+ SERVICE_ADDRESSES ServiceAddresses;
+ LPSERVICE_ADDRESS psa = &ServiceAddresses.Addresses[0];
+
+ ServiceAddresses.dwAddressCount = 1;
+ memset(&siInfo, 0, sizeof(siInfo));
+ siInfo.lpServiceName = lpszServiceInstance;
+ siInfo.lpServiceAddress = &ServiceAddresses;
+ psa->dwAddressType = AF_IPX;
+ psa->dwAddressFlags = psa->dwPrincipalLength = 0;
+ psa->dwAddressLength = sizeof(SOCKADDR_IPX);
+ psa->lpPrincipal = 0;
+ psa->lpAddress = pbAddress;
+ siInfo.lpServiceType = pType;
+ return(pSapSetService(dwOperation, &siInfo, nServiceType));
+}
+
+
+DWORD
+pSapSetService(
+ IN DWORD dwOperation,
+ IN LPSERVICE_INFO lpServiceInfo,
+ IN WORD nServiceType)
+/*++
+Routine Description:
+ Common routine to do the SAP advertisement.
+--*/
+{
+ DWORD err;
+
+ RpcTryExcept
+ {
+ err = NwrSetService( NULL, dwOperation, lpServiceInfo, nServiceType );
+ }
+ RpcExcept(1)
+ {
+ err = ERROR_SERVICE_NOT_ACTIVE;
+#if 0
+ DWORD code = RpcExceptionCode();
+
+ if ( (code == RPC_S_SERVER_UNAVAILABLE)
+ ||
+ (code == RPC_S_UNKNOWN_IF) )
+ {
+ err = ERROR_SERVICE_NOT_ACTIVE;
+ }
+ else
+ {
+ err = NwpMapRpcError( code );
+ }
+#endif
+ }
+ RpcEndExcept
+
+ if ( err == ERROR_SERVICE_NOT_ACTIVE )
+ {
+ //
+ //CSNW not available, going to try use the SAP agent, else we do it ourselves
+ //
+ err = NO_ERROR;
+
+ //
+ // Check if all parameters passed in are valid
+ //
+ if ( wcslen( lpServiceInfo->lpServiceName ) > SAP_OBJECT_NAME_MAX_LENGTH-1 )
+ {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ switch ( dwOperation )
+ {
+ case SERVICE_REGISTER:
+ err = NwRegisterService( lpServiceInfo, nServiceType, NULL );
+ break;
+
+ case SERVICE_DEREGISTER:
+ err = NwDeregisterService( lpServiceInfo, nServiceType );
+ break;
+
+ default: //this should never occur, but just in case . . .
+ err = ERROR_INVALID_PARAMETER;
+ break;
+ }
+ }
+
+ return err;
+}
+
+DWORD
+SapFreeSapSocket(SOCKET s)
+{
+/*++
+Routine Description:
+
+ Release the socket and clean up
+--*/
+ DWORD err = NO_ERROR;
+
+ closesocket( s );
+ return(err);
+}
+
+DWORD
+SapGetSapSocket(SOCKET * ps)
+{
+/*++
+Routine Description:
+
+ Get a socket suitable for making SAP queries
+
+Arguments: None
+
+--*/
+ SOCKET socketSap;
+ WSADATA wsaData;
+ SOCKADDR_IPX socketAddr;
+ DWORD err = NO_ERROR;
+ INT nValue;
+ DWORD dwNonBlocking = 1;
+
+ //
+ // Initialize the socket interface
+ //
+// err = WSAStartup( WSOCK_VER_REQD, &wsaData );
+// if ( err )
+// {
+// return err;
+// }
+
+ //
+ // Open an IPX datagram socket
+ //
+ socketSap = socket( AF_IPX, SOCK_DGRAM, NSPROTO_IPX );
+ if ( socketSap == INVALID_SOCKET )
+ {
+ err = WSAGetLastError();
+// (VOID) WSACleanup();
+ return err;
+ }
+
+ //
+ // Set the socket to non-blocking
+ //
+ if ( ioctlsocket( socketSap, FIONBIO, &dwNonBlocking ) == SOCKET_ERROR )
+ {
+ err = WSAGetLastError();
+ goto ErrExit;
+ }
+
+ //
+ // Allow sending of broadcasts
+ //
+ nValue = 1;
+ if ( setsockopt( socketSap,
+ SOL_SOCKET,
+ SO_BROADCAST,
+ (PVOID) &nValue,
+ sizeof(INT)) == SOCKET_ERROR )
+ {
+ err = WSAGetLastError();
+ goto ErrExit;
+ }
+
+ //
+ // Bind the socket
+ //
+ memset( &socketAddr, 0, sizeof( SOCKADDR_IPX));
+ socketAddr.sa_family = AF_IPX;
+ socketAddr.sa_socket = 0; // no specific port
+
+ if ( bind( socketSap,
+ (PSOCKADDR) &socketAddr,
+ sizeof( SOCKADDR_IPX)) == SOCKET_ERROR )
+ {
+ err = WSAGetLastError();
+ goto ErrExit;
+ }
+
+ //
+ // Set the extended address option
+ //
+ nValue = 1;
+ if ( setsockopt( socketSap, // Socket Handle
+ NSPROTO_IPX, // Option Level
+ IPX_EXTENDED_ADDRESS, // Option Name
+ (PUCHAR)&nValue, // Ptr to on/off flag
+ sizeof(INT)) == SOCKET_ERROR ) // Length of flag
+ {
+ err = WSAGetLastError();
+ goto ErrExit;
+ }
+
+ *ps = socketSap;
+
+ return(err);
+
+ErrExit:
+ SapFreeSapSocket(socketSap); // cleans up lots of stuff
+ return(err);
+}
+
+
+DWORD
+NwpGetAddressForRnRViaSap(
+ IN HANDLE hRnRHandle,
+ IN WORD nServiceType,
+ IN LPWSTR lpServiceName,
+ IN DWORD nProt,
+ IN OUT LPVOID lpCsAddrBuffer,
+ IN OUT LPDWORD lpdwBufferLength,
+ IN HANDLE hCancellationEvent,
+ OUT LPDWORD lpcAddress
+ )
+{
+/*++
+Routine Description:
+
+ This routine uses SAP requests to find the address of the given service
+ name/type. It can handle looking up by type only, or by name and type.
+ The latter case is the same as the old RnR code, see below for
+ it and for a description of the arguments
+--*/
+ return(0);
+}
+
+#define MAX_LOOPS_FOR_SAP 4
+
+DWORD
+SapGetSapForType(
+ PSAP_BCAST_CONTROL psbc,
+ WORD nServiceType)
+{
+/*++
+Routine Description:
+ Does the work of send Sap queries and fetching results.
+ The first message sent is done according to the requester, and
+ may be limited to the local LAN or not.
+
+Arguments:
+ psbc -- pointer to the control information
+ wSapType -- Sap type
+--*/
+ SAP_REQUEST sapRequest;
+ UCHAR destAddr[SAP_ADDRESS_LENGTH];
+ DWORD startTickCount;
+ UCHAR recvBuffer[SAP_MAXRECV_LENGTH];
+ INT bytesReceived;
+ BOOL fFound = FALSE;
+ DWORD err = NO_ERROR;
+
+ sapRequest.QueryType = htons( psbc->wQueryType );
+ sapRequest.ServerType = htons( nServiceType );
+
+ //
+ // Set the address to send to
+ //
+ memcpy( destAddr, SapBroadcastAddress, SAP_ADDRESS_LENGTH );
+
+ //
+ // Ready to go. This might be the inital call, in which case
+ // we start off by sending. In all other cases, we start
+ // out receiving.
+ //
+
+ //
+ // In the full case,
+ // we will send out SAP requests 3 times and wait 1 sec for
+ // Sap responses the first time, 2 sec the second and 4 sec the
+ // third time.
+ //
+ for (; !fFound && (psbc->dwIndex < MAX_LOOPS_FOR_SAP); psbc->dwIndex++ )
+ {
+ DWORD dwRet;
+ DWORD dwTimeOut = (1 << psbc->dwIndex) * 1000;
+
+ if(psbc->dwTickCount)
+ {
+ dwRet = dwrcNil;
+ //
+ // Need to do some reading ...
+ //
+ do
+ {
+ PSAP_IDENT_HEADER pSap;
+
+
+ if((psbc->psrc->fFlags & SAP_F_END_CALLED)
+ ||
+ psbc->fCheckCancel(psbc->pvArg))
+ {
+ err = dwrcCancel;
+ goto CleanExit;
+ }
+
+ //
+ // Sleeps for 50 ms so that we might get something on first read
+ //
+ Sleep( 50 );
+
+ bytesReceived = recvfrom( psbc->s,
+ recvBuffer,
+ SAP_MAXRECV_LENGTH,
+ 0,
+ NULL,
+ NULL );
+
+ if ( bytesReceived == SOCKET_ERROR )
+ {
+ err = WSAGetLastError();
+ if ( err == WSAEWOULDBLOCK ) // no data on socket, continue looping
+ {
+ if(dwRet == dwrcNoWait)
+ {
+ fFound = TRUE;
+ }
+ err = NO_ERROR;
+ continue;
+ }
+ }
+
+ if ( ( err != NO_ERROR ) // err occurred in recvfrom
+ || ( bytesReceived == 0 ) // or socket closed
+ )
+ {
+ goto CleanExit;
+ }
+
+ //
+ // Skip over query type
+ //
+ bytesReceived -= sizeof(USHORT);
+ pSap = (PSAP_IDENT_HEADER) &(recvBuffer[sizeof(USHORT)]);
+
+ //
+ // Tell the caller we've something to look over
+ //
+ while ( bytesReceived >= sizeof( SAP_IDENT_HEADER ))
+ {
+
+ dwRet = psbc->Func(psbc, pSap, &err);
+ if((dwRet == dwrcDone)
+ ||
+ (dwRet == dwrcCancel))
+ {
+ fFound = TRUE;
+ break;
+ }
+
+ pSap++;
+ bytesReceived -= sizeof( SAP_IDENT_HEADER );
+ }
+ }
+ while ( !fFound
+ && ((GetTickCount() - psbc->dwTickCount) < dwTimeOut )
+ );
+ }
+
+
+ // Send the packet out
+ //
+ if((fFound && (dwRet == dwrcNoWait))
+ ||
+ (psbc->dwIndex == (MAX_LOOPS_FOR_SAP -1)))
+ {
+ goto CleanExit;
+ }
+ if ( sendto( psbc->s,
+ (PVOID) &sapRequest,
+ sizeof( sapRequest ),
+ 0,
+ (PSOCKADDR) destAddr,
+ SAP_ADDRESS_LENGTH ) == SOCKET_ERROR )
+ {
+ err = WSAGetLastError();
+ goto CleanExit;
+ }
+ psbc->dwTickCount = GetTickCount();
+ }
+
+ if(!fFound)
+ {
+ err = WSAEADDRNOTAVAIL;
+ }
+
+CleanExit:
+
+ return err;
+}
+
+BOOL
+NwpLookupSapInRegistry(
+ IN LPGUID lpServiceType,
+ OUT PWORD pnSapType,
+ OUT PWORD pwPort,
+ IN OUT PDWORD pfConnectionOriented
+ )
+/*++
+
+Routine Description:
+
+ This routine looks up the GUID in the registry under
+ Control\ServiceProvider\ServiceTypes and trys to read the SAP type
+ from the registry.
+
+Arguments:
+
+ lpServiceType - the GUID to look for
+ pnSapType - on return, contains the SAP type
+
+Return Value:
+
+ Returns FALSE if we can't get the SAP type, TRUE otherwise
+
+--*/
+{
+ DWORD err;
+ BOOL fFound = FALSE;
+
+ HKEY hkey = NULL;
+ HKEY hkeyServiceType = NULL;
+ DWORD dwIndex = 0;
+ WCHAR szBuffer[ MAX_PATH + 1];
+ DWORD dwLen;
+ FILETIME ftLastWrite;
+
+ //
+ // Open the service types key
+ //
+ err = RegOpenKeyExW( HKEY_LOCAL_MACHINE,
+ NW_SERVICE_TYPES_REGKEY,
+ 0,
+ KEY_READ,
+ &hkey );
+
+ if ( err )
+ {
+ // Cannot find the key because it is not created yet since no
+ // one called Add service type. We return FALSE indicating
+ // Sap type not found.
+ return FALSE;
+ }
+
+ //
+ // Loop through all subkey of service types to find the GUID
+ //
+ for ( dwIndex = 0; ; dwIndex++ )
+ {
+ GUID guid;
+
+ dwLen = sizeof( szBuffer ) / sizeof( WCHAR );
+ err = RegEnumKeyExW( hkey,
+ dwIndex,
+ szBuffer, // Buffer big enough to
+ // hold any key name
+ &dwLen, // in characters
+ NULL,
+ NULL,
+ NULL,
+ &ftLastWrite );
+
+ //
+ // We will break out of here on any error, this includes
+ // the error ERROR_NO_MORE_ITEMS which means that we have finish
+ // enumerating all the keys.
+ //
+ if ( err )
+ {
+ if ( err == ERROR_NO_MORE_ITEMS ) // No more to enumerate
+ err = NO_ERROR;
+ break;
+ }
+
+ err = RegOpenKeyExW( hkey,
+ szBuffer,
+ 0,
+ KEY_READ,
+ &hkeyServiceType );
+
+
+ if ( err )
+ break;
+
+ dwLen = sizeof( szBuffer );
+ err = RegQueryValueExW( hkeyServiceType,
+ NW_GUID_VALUE_NAME,
+ NULL,
+ NULL,
+ (LPBYTE) szBuffer, // Buffer big enough to
+ // hold any GUID
+ &dwLen ); // in bytes
+
+ if ( err == ERROR_FILE_NOT_FOUND )
+ continue; // continue with the next key
+ else if ( err )
+ break;
+
+
+ // Get rid of the end curly brace
+ szBuffer[ dwLen/sizeof(WCHAR) - 2] = 0;
+
+ err = UuidFromStringW( szBuffer + 1, // go past the first curly brace
+ &guid );
+
+ if ( err )
+ continue; // continue with the next key, err might be returned
+ // if buffer does not contain a valid GUID
+
+ if ( !memcmp( lpServiceType, &guid, sizeof(GUID)))
+ {
+ DWORD dwTmp;
+ dwLen = sizeof( dwTmp );
+ err = RegQueryValueExW( hkeyServiceType,
+ SERVICE_TYPE_VALUE_SAPID,
+ NULL,
+ NULL,
+ (LPBYTE) &dwTmp,
+ &dwLen ); // in bytes
+
+ if ( !err )
+ {
+ fFound = TRUE;
+ *pnSapType = (WORD) dwTmp;
+ if ( ARGUMENT_PRESENT( pwPort ))
+ {
+ err = RegQueryValueExW( hkeyServiceType,
+ L"Port",
+ NULL,
+ NULL,
+ (LPBYTE) &dwTmp,
+ &dwLen ); // in bytes
+
+ if ( !err )
+ {
+ *pwPort = (WORD)dwTmp;
+ }
+ }
+ if ( ARGUMENT_PRESENT( pfConnectionOriented ))
+ {
+ err = RegQueryValueExW( hkeyServiceType,
+ SERVICE_TYPE_VALUE_CONN,
+ NULL,
+ NULL,
+ (LPBYTE) &dwTmp,
+ &dwLen ); // in bytes
+
+ if ( !err )
+ *pfConnectionOriented = dwTmp? 1: 0;
+ }
+ }
+ else if ( err == ERROR_FILE_NOT_FOUND )
+ {
+ continue; // continue with the next key since we can't
+ // find Sap Id
+ }
+ break;
+ }
+
+ RegCloseKey( hkeyServiceType );
+ hkeyServiceType = NULL;
+ }
+
+ if ( hkeyServiceType != NULL )
+ RegCloseKey( hkeyServiceType );
+
+ if ( hkey != NULL )
+ RegCloseKey( hkey );
+
+ return fFound;
+}
+
+DWORD
+NwpRnR2AddServiceType(
+ IN LPWSTR lpServiceTypeName,
+ IN LPGUID lpClassType,
+ IN WORD wSapId,
+ IN WORD wPort
+)
+{
+ HKEY hKey, hKeyService;
+ PWCHAR pwszUuid;
+ DWORD dwDisposition, err;
+ DWORD dwValue = (DWORD)wSapId;
+ WCHAR wszUuid[36 + 1 + 2]; // to hold the GUID
+
+ err = RegCreateKeyEx( HKEY_LOCAL_MACHINE,
+ NW_SERVICE_TYPES_REGKEY,
+ 0,
+ TEXT(""),
+ REG_OPTION_NON_VOLATILE,
+ KEY_READ | KEY_WRITE,
+ NULL,
+ &hKey,
+ &dwDisposition );
+
+ if(err)
+ {
+ return(GetLastError());
+ }
+
+ //
+ // Open the key corresponding to the service (create if not there).
+ //
+
+ err = RegCreateKeyEx(
+ hKey,
+ lpServiceTypeName,
+ 0,
+ TEXT(""),
+ REG_OPTION_NON_VOLATILE,
+ KEY_READ | KEY_WRITE,
+ NULL,
+ &hKeyService,
+ &dwDisposition
+ );
+
+ if(!err)
+ {
+ //
+ // ready to put the GUID value in.
+ //
+
+ UuidToString(
+ lpClassType,
+ &pwszUuid);
+
+ wszUuid[0] = L'{';
+ memcpy(&wszUuid[1], pwszUuid, 36 * sizeof(WCHAR));
+ wszUuid[37] = L'}';
+ wszUuid[38] = 0;
+
+ RpcStringFree(&pwszUuid);
+
+ //
+ // write it
+ //
+
+ err = RegSetValueEx(
+ hKeyService,
+ L"GUID",
+ 0,
+ REG_SZ,
+ (LPBYTE)wszUuid,
+ 39 * sizeof(WCHAR));
+
+ if(!err)
+ {
+ err = RegSetValueEx(
+ hKeyService,
+ L"SAPID",
+ 0,
+ REG_DWORD,
+ (LPBYTE)&dwValue,
+ sizeof(DWORD));
+
+ dwValue = (DWORD)wPort;
+
+ err = RegSetValueEx(
+ hKeyService,
+ L"PORT",
+ 0,
+ REG_DWORD,
+ (LPBYTE)&dwValue,
+ sizeof(DWORD));
+ }
+ RegCloseKey(hKeyService);
+ }
+ RegCloseKey(hKey);
+ if(err)
+ {
+ err = GetLastError();
+ }
+ return(err);
+}
+
+
+DWORD
+NwpAddServiceType(
+ IN LPSERVICE_INFO lpServiceInfo,
+ IN BOOL fUnicodeBlob
+)
+/*++
+
+Routine Description:
+
+ This routine adds a new service type and its info to the registry under
+ Control\ServiceProvider\ServiceTypes
+
+Arguments:
+
+ lpServiceInfo - the ServiceSpecificInfo contains the service type info
+ fUnicodeBlob - TRUE if the above field contains unicode data,
+ FALSE otherwise
+
+Return Value:
+
+ Win32 error
+
+--*/
+{
+ DWORD err;
+ HKEY hkey = NULL;
+ HKEY hkeyType = NULL;
+
+ SERVICE_TYPE_INFO *pSvcTypeInfo = (SERVICE_TYPE_INFO *)
+ lpServiceInfo->ServiceSpecificInfo.pBlobData;
+ LPWSTR pszSvcTypeName;
+ UNICODE_STRING uniStr;
+ DWORD i;
+ PSERVICE_TYPE_VALUE pVal;
+
+ //
+ // Get the new service type name
+ //
+ if ( fUnicodeBlob )
+ {
+ pszSvcTypeName = (LPWSTR) (((LPBYTE) pSvcTypeInfo) +
+ pSvcTypeInfo->dwTypeNameOffset );
+ }
+ else
+ {
+ ANSI_STRING ansiStr;
+
+ RtlInitAnsiString( &ansiStr,
+ (LPSTR) (((LPBYTE) pSvcTypeInfo) +
+ pSvcTypeInfo->dwTypeNameOffset ));
+
+ err = RtlAnsiStringToUnicodeString( &uniStr, &ansiStr, TRUE );
+ if ( err )
+ return err;
+
+ pszSvcTypeName = uniStr.Buffer;
+ }
+
+ //
+ // If the service type name is an empty string, return error.
+ //
+ if ( ( pSvcTypeInfo->dwTypeNameOffset == 0 )
+ || ( pszSvcTypeName == NULL )
+ || ( *pszSvcTypeName == 0 ) // empty string
+ )
+ {
+ err = ERROR_INVALID_PARAMETER;
+ goto CleanExit;
+
+ }
+
+ //
+ // The following keys should have already been created
+ //
+ err = RegOpenKeyExW( HKEY_LOCAL_MACHINE,
+ NW_SERVICE_TYPES_REGKEY,
+ 0,
+ KEY_READ | KEY_WRITE,
+ &hkey );
+
+ if ( err )
+ goto CleanExit;
+
+ err = RegOpenKeyExW( hkey,
+ pszSvcTypeName,
+ 0,
+ KEY_READ | KEY_WRITE,
+ &hkeyType );
+
+ if ( err )
+ goto CleanExit;
+
+ //
+ // Loop through all values in the specific and add them one by one
+ // to the registry if it belongs to our name space
+ //
+ for ( i = 0, pVal = pSvcTypeInfo->Values;
+ i < pSvcTypeInfo->dwValueCount;
+ i++, pVal++ )
+ {
+ if ( ! ((pVal->dwNameSpace == NS_SAP) ||
+ (pVal->dwNameSpace == NS_DEFAULT)) )
+ {
+ continue; // ignore values not in our name space
+ }
+
+ if ( fUnicodeBlob )
+ {
+ err = RegSetValueExW(
+ hkeyType,
+ (LPWSTR) ( ((LPBYTE) pSvcTypeInfo) + pVal->dwValueNameOffset),
+ 0,
+ pVal->dwValueType,
+ (LPBYTE) ( ((LPBYTE) pSvcTypeInfo) + pVal->dwValueOffset),
+ pVal->dwValueSize
+ );
+ }
+ else
+ {
+ err = RegSetValueExA(
+ hkeyType,
+ (LPSTR) ( ((LPBYTE) pSvcTypeInfo) + pVal->dwValueNameOffset),
+ 0,
+ pVal->dwValueType,
+ (LPBYTE) ( ((LPBYTE) pSvcTypeInfo) + pVal->dwValueOffset),
+ pVal->dwValueSize
+ );
+ }
+ }
+
+CleanExit:
+
+ if ( !fUnicodeBlob )
+ RtlFreeUnicodeString( &uniStr );
+
+ if ( hkeyType != NULL )
+ RegCloseKey( hkeyType );
+
+ if ( hkey != NULL )
+ RegCloseKey( hkey );
+
+ return err;
+
+}
+
+DWORD
+NwpDeleteServiceType(
+ IN LPSERVICE_INFO lpServiceInfo,
+ IN BOOL fUnicodeBlob
+)
+/*++
+
+Routine Description:
+
+ This routine deletes a service type and its info from the registry under
+ Control\ServiceProvider\ServiceTypes
+
+Arguments:
+
+ lpServiceInfo - the ServiceSpecificInfo contains the service type info
+ fUnicodeBlob - TRUE if the above field contains unicode data,
+ FALSE otherwise
+
+Return Value:
+
+ Win32 error
+
+--*/
+{
+ DWORD err;
+ HKEY hkey = NULL;
+ SERVICE_TYPE_INFO *pSvcTypeInfo = (SERVICE_TYPE_INFO *)
+ lpServiceInfo->ServiceSpecificInfo.pBlobData;
+ LPWSTR pszSvcTypeName;
+ UNICODE_STRING uniStr;
+
+ //
+ // Get the service type name to be deleted
+ //
+ if ( fUnicodeBlob )
+ {
+ pszSvcTypeName = (LPWSTR) (((LPBYTE) pSvcTypeInfo) +
+ pSvcTypeInfo->dwTypeNameOffset );
+ }
+ else
+ {
+ ANSI_STRING ansiStr;
+
+ RtlInitAnsiString( &ansiStr,
+ (LPSTR) (((LPBYTE) pSvcTypeInfo) +
+ pSvcTypeInfo->dwTypeNameOffset ));
+
+ err = RtlAnsiStringToUnicodeString( &uniStr, &ansiStr, TRUE );
+ if ( err )
+ return err;
+
+ pszSvcTypeName = uniStr.Buffer;
+ }
+
+ //
+ // If the service type name is an empty string, return error.
+ //
+ if ( ( pSvcTypeInfo->dwTypeNameOffset == 0 )
+ || ( pszSvcTypeName == NULL )
+ || ( *pszSvcTypeName == 0 ) // empty string
+ )
+ {
+ err = ERROR_INVALID_PARAMETER;
+ goto CleanExit;
+
+ }
+
+ err = RegOpenKeyExW( HKEY_LOCAL_MACHINE,
+ NW_SERVICE_TYPES_REGKEY,
+ 0,
+ KEY_READ | KEY_WRITE,
+ &hkey );
+
+
+ if ( !err )
+ {
+ err = RegDeleteKey( hkey,
+ pszSvcTypeName );
+ }
+
+ if ( err == ERROR_FILE_NOT_FOUND )
+ {
+ // Perhaps before calling my provider, the router already deleted the
+ // this key, hence just return success;
+ err = NO_ERROR;
+ }
+
+CleanExit:
+
+ if ( !fUnicodeBlob )
+ RtlFreeUnicodeString( &uniStr );
+
+ if ( hkey != NULL )
+ RegCloseKey( hkey );
+
+ return err;
+
+}
+
+#define SOCKSIZE (sizeof(SOCKADDR_IPX) + sizeof(DWORD) - 1)
+
+DWORD
+FillBufferWithCsAddr(
+ IN LPBYTE pAddress,
+ IN DWORD nProt,
+ IN OUT LPVOID lpCsAddrBuffer,
+ IN OUT LPDWORD lpdwBufferLength,
+ OUT LPDWORD pcAddress
+)
+{
+ DWORD nAddrCount = 0;
+ CSADDR_INFO *pCsAddr;
+ SOCKADDR_IPX *pAddrLocal, *pAddrRemote;
+ DWORD i;
+ LPBYTE pBuffer;
+
+ if ( nProt & SPXII_BIT )
+ nAddrCount++;
+
+ if ( nProt & IPX_BIT )
+ nAddrCount++;
+
+ if ( nProt & SPX_BIT )
+ nAddrCount++;
+
+
+ if ( *lpdwBufferLength <
+ nAddrCount * ( sizeof( CSADDR_INFO) + (2*SOCKSIZE)))
+ {
+ *lpdwBufferLength = sizeof(DWORD) -1 + (nAddrCount *
+ ( sizeof( CSADDR_INFO) + (2 * SOCKSIZE)));
+ return ERROR_INSUFFICIENT_BUFFER;
+ }
+
+ pBuffer = ((LPBYTE) lpCsAddrBuffer) + sizeof( CSADDR_INFO) * nAddrCount;
+
+ for ( i = 0, pCsAddr = (CSADDR_INFO *)lpCsAddrBuffer;
+ (i < nAddrCount) && ( nProt != 0 );
+ i++, pCsAddr++ )
+ {
+ if ( nProt & SPXII_BIT )
+ {
+ pCsAddr->iSocketType = SOCK_SEQPACKET;
+ pCsAddr->iProtocol = NSPROTO_SPXII;
+ nProt &= ~SPXII_BIT;
+ }
+ else if ( nProt & IPX_BIT )
+ {
+ pCsAddr->iSocketType = SOCK_DGRAM;
+ pCsAddr->iProtocol = NSPROTO_IPX;
+ nProt &= ~IPX_BIT;
+ }
+ else if ( nProt & SPX_BIT )
+ {
+ pCsAddr->iSocketType = SOCK_SEQPACKET;
+ pCsAddr->iProtocol = NSPROTO_SPX;
+ nProt &= ~SPX_BIT;
+ }
+ else
+ {
+ break;
+ }
+
+ pCsAddr->LocalAddr.iSockaddrLength = sizeof( SOCKADDR_IPX );
+ pCsAddr->RemoteAddr.iSockaddrLength = sizeof( SOCKADDR_IPX );
+ pCsAddr->LocalAddr.lpSockaddr =
+ (LPSOCKADDR) pBuffer;
+ pCsAddr->RemoteAddr.lpSockaddr =
+ (LPSOCKADDR) ( pBuffer + sizeof(SOCKADDR_IPX));
+ pBuffer += 2 * sizeof( SOCKADDR_IPX );
+
+ pAddrLocal = (SOCKADDR_IPX *) pCsAddr->LocalAddr.lpSockaddr;
+ pAddrRemote = (SOCKADDR_IPX *) pCsAddr->RemoteAddr.lpSockaddr;
+
+ pAddrLocal->sa_family = AF_IPX;
+ pAddrRemote->sa_family = AF_IPX;
+
+ //
+ // The default local sockaddr is for IPX is
+ // sa_family = AF_IPX and all other bytes = 0.
+ //
+
+ RtlZeroMemory( pAddrLocal->sa_netnum,
+ IPX_ADDRESS_LENGTH );
+
+ //
+ // If pAddress is NULL, i.e. we are doing RES_SERVICE,
+ // just make all bytes in remote address zero.
+ //
+ if ( pAddress == NULL )
+ {
+ RtlZeroMemory( pAddrRemote->sa_netnum,
+ IPX_ADDRESS_LENGTH );
+ }
+ else
+ {
+ RtlCopyMemory( pAddrRemote->sa_netnum,
+ pAddress,
+ IPX_ADDRESS_LENGTH );
+ }
+ }
+
+ *pcAddress = nAddrCount;
+
+ return NO_ERROR;
+}
+
+VOID
+NwInitializeServiceProvider(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This routine initializes the service provider.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ // nothing more to do
+}
+
+VOID
+NwTerminateServiceProvider(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This routine cleans up the service provider.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PREGISTERED_SERVICE pSvc, pNext;
+
+ //
+ // Clean up the link list and stop sending all SAP advertise packets
+ //
+
+ EnterCriticalSection( &NwServiceListCriticalSection );
+
+ for ( pSvc = pServiceListHead; pSvc != NULL; pSvc = pNext )
+ {
+ pNext = pSvc->Next;
+
+ if ( pSvc->fAdvertiseBySap )
+ {
+ UNICODE_STRING uServer;
+ OEM_STRING oemServer;
+ NTSTATUS ntstatus;
+
+ RtlInitUnicodeString( &uServer, pSvc->pServiceInfo->lpServiceName );
+ ntstatus = RtlUnicodeStringToOemString( &oemServer, &uServer, TRUE);
+ if ( NT_SUCCESS( ntstatus ) )
+ {
+ (VOID) SapRemoveAdvertise( oemServer.Buffer,
+ pSvc->nSapType );
+ RtlFreeOemString( &oemServer );
+ }
+ }
+
+ (VOID) LocalFree( pSvc->pServiceInfo );
+ (VOID) LocalFree( pSvc );
+ }
+
+ LeaveCriticalSection( &NwServiceListCriticalSection );
+
+ //
+ // Clean up the SAP interface
+ //
+ (VOID) SapLibShutdown();
+
+ //
+ // Clean up the socket interface
+ //
+ if ( fInitSocket )
+ {
+ closesocket( socketSap );
+// (VOID) WSACleanup();
+ }
+
+}
+
+DWORD
+NwRegisterService(
+ IN LPSERVICE_INFO lpServiceInfo,
+ IN WORD nSapType,
+ IN HANDLE hEventHandle
+ )
+/*++
+
+Routine Description:
+
+ This routine registers the given service.
+
+Arguments:
+
+ lpServiceInfo - contains the service information
+
+ nSapType - The SAP type to advertise
+
+ hEventHandle - A handle to the NwDoneEvent if this code is running in
+ the context of Client Services for NetWare. If this is NULL,
+ then CSNW is not available and this code is running in the
+ context of a regular executable.
+
+Return Value:
+
+ Win32 error.
+
+--*/
+{
+ DWORD err = NO_ERROR;
+ NTSTATUS ntstatus;
+ DWORD i;
+ INT nIPX = -1;
+
+ //
+ // Check to see if the service address array contains IPX address,
+ // we will only use the first ipx address contained in the array.
+ //
+ if ( lpServiceInfo->lpServiceAddress == NULL )
+ return ERROR_INCORRECT_ADDRESS;
+
+ for ( i = 0; i < lpServiceInfo->lpServiceAddress->dwAddressCount; i++)
+ {
+ if ( lpServiceInfo->lpServiceAddress->Addresses[i].dwAddressType
+ == AF_IPX )
+ {
+ nIPX = (INT) i;
+ break;
+ }
+ }
+
+ //
+ // If we cannot find a IPX address, return error
+ //
+ if ( nIPX == -1 )
+ return ERROR_INCORRECT_ADDRESS;
+
+ //
+ // Try to deregister the service since the service might have
+ // been registered but not deregistered
+ //
+ err = NwDeregisterService( lpServiceInfo, nSapType );
+ if ( ( err != NO_ERROR ) // deregister successfully
+ && ( err != ERROR_SERVICE_NOT_FOUND ) // service not registered before
+ )
+ {
+ return err;
+ }
+
+ err = NO_ERROR;
+
+ //
+ // Try and see if SAP service can advertise the service for us.
+ //
+ ntstatus = SapLibInit();
+ if ( NT_SUCCESS( ntstatus ))
+ {
+ UNICODE_STRING uServer;
+ OEM_STRING oemServer;
+ INT sapRet;
+ BOOL fContinueLoop = FALSE;
+
+ RtlInitUnicodeString( &uServer, lpServiceInfo->lpServiceName );
+ ntstatus = RtlUnicodeStringToOemString( &oemServer, &uServer, TRUE );
+ if ( !NT_SUCCESS( ntstatus ))
+ return RtlNtStatusToDosError( ntstatus );
+
+
+ do
+ {
+ sapRet = SapAddAdvertise( oemServer.Buffer,
+ nSapType,
+ (LPBYTE) (((LPSOCKADDR_IPX) lpServiceInfo->lpServiceAddress->Addresses[nIPX].lpAddress)->sa_netnum),
+ FALSE );
+
+ switch ( sapRet )
+ {
+ case SAPRETURN_SUCCESS:
+ {
+ err = AddServiceToList( lpServiceInfo, nSapType, TRUE, nIPX );
+ if ( err )
+ (VOID) SapRemoveAdvertise( oemServer.Buffer, nSapType );
+ RtlFreeOemString( &oemServer );
+
+ return err;
+ }
+
+ case SAPRETURN_NOMEMORY:
+ err = ERROR_NOT_ENOUGH_MEMORY;
+ break;
+
+ case SAPRETURN_EXISTS:
+ {
+ //
+ // Someone else is already advertising the service
+ // directly through SAP service. Remove it and
+ // readvertise with the new information.
+ //
+ sapRet = SapRemoveAdvertise( oemServer.Buffer, nSapType );
+ switch ( sapRet )
+ {
+ case SAPRETURN_SUCCESS:
+ fContinueLoop = TRUE; // go thru once more
+ break;
+
+ case SAPRETURN_NOMEMORY:
+ err = ERROR_NOT_ENOUGH_MEMORY;
+ break;
+
+ case SAPRETURN_NOTEXIST:
+ case SAPRETURN_INVALIDNAME:
+ default: // Should not have any other errors
+ err = ERROR_INVALID_PARAMETER;
+ break;
+ }
+ break;
+ }
+
+ case SAPRETURN_INVALIDNAME:
+ err = ERROR_INVALID_PARAMETER;
+ break;
+
+ case SAPRETURN_DUPLICATE:
+ err = NO_ERROR;
+ break;
+
+ default:
+ break;
+ }
+ } while ( fContinueLoop );
+
+ RtlFreeOemString( &oemServer );
+
+ if ( err )
+ {
+ return err;
+ }
+ }
+
+ //
+ // At this point, we failed to ask Sap service to advertise the
+ // service for us. So we advertise it ourselves.
+ //
+
+ if ( !fInitSocket )
+ {
+ err = NwInitializeSocket( hEventHandle );
+ }
+
+ if ( err == NO_ERROR )
+ {
+ err = NwAdvertiseService( lpServiceInfo->lpServiceName,
+ nSapType,
+ ((LPSOCKADDR_IPX) lpServiceInfo->lpServiceAddress->Addresses[nIPX].lpAddress),
+ hEventHandle );
+
+ //
+ // Adding the service to the list will result in a resend
+ // of advertising packets every 60 seconds
+ //
+
+ if ( err == NO_ERROR )
+ {
+ err = AddServiceToList( lpServiceInfo, nSapType, FALSE, nIPX );
+ }
+ }
+
+ return err;
+}
+
+DWORD
+NwDeregisterService(
+ IN LPSERVICE_INFO lpServiceInfo,
+ IN WORD nSapType
+ )
+/*++
+
+Routine Description:
+
+ This routine deregisters the given service.
+
+Arguments:
+
+ lpServiceInfo - contains the service information
+
+ nSapType - SAP type to deregister
+
+Return Value:
+
+ Win32 error.
+
+--*/
+{
+ PREGISTERED_SERVICE pSvc;
+
+ //
+ // Check if the requested service type and name has already been registered.
+ // If yes, then return error.
+ //
+
+ pSvc = GetServiceItemFromList( nSapType, lpServiceInfo->lpServiceName );
+ if ( pSvc == NULL )
+ return ERROR_SERVICE_NOT_FOUND;
+
+ //
+ // If SAP service is advertising the service for us, ask
+ // the SAP service to stop advertising.
+ //
+
+ if ( pSvc->fAdvertiseBySap )
+ {
+ UNICODE_STRING uServer;
+ OEM_STRING oemServer;
+ NTSTATUS ntstatus;
+ INT sapRet;
+
+ RtlInitUnicodeString( &uServer, lpServiceInfo->lpServiceName );
+ ntstatus = RtlUnicodeStringToOemString( &oemServer, &uServer, TRUE );
+ if ( !NT_SUCCESS( ntstatus ) )
+ return RtlNtStatusToDosError( ntstatus );
+
+ sapRet = SapRemoveAdvertise( oemServer.Buffer, nSapType );
+ RtlFreeOemString( &oemServer );
+
+ switch ( sapRet )
+ {
+ case SAPRETURN_NOMEMORY:
+ return ERROR_NOT_ENOUGH_MEMORY;
+
+ case SAPRETURN_NOTEXIST:
+ case SAPRETURN_INVALIDNAME:
+ return ERROR_INVALID_PARAMETER;
+
+ case SAPRETURN_SUCCESS:
+ break;
+
+ // Should not have any other errors
+ default:
+ break;
+ }
+
+ }
+
+ //
+ // Remove the service item from the link list
+ //
+ RemoveServiceFromList( pSvc );
+
+ return NO_ERROR;
+}
+
+BOOL
+OldRnRCheckCancel(
+ PVOID pvArg
+ )
+/*++
+Routine Description:
+ Determine if the cancel event is signaled
+--*/
+{
+ POLDRNRSAP porns = (POLDRNRSAP)pvArg;
+
+ if(!WaitForSingleObject(porns->hCancel, 0))
+ {
+ return(TRUE);
+ }
+ return(FALSE);
+}
+
+
+DWORD
+OldRnRCheckSapData(
+ PSAP_BCAST_CONTROL psbc,
+ PSAP_IDENT_HEADER pSap,
+ PDWORD pdwErr
+ )
+{
+/*++
+Routine Description:
+ Coroutine called when a SAP reply is recevied. This checks to see
+ if the reply satisfies the request.
+Argument:
+ pvArg -- actually a pointer to an SAP_BCAST_CONTROL
+--*/
+ POLDRNRSAP porns = (POLDRNRSAP)psbc->pvArg;
+
+ if(strcmp(porns->poem->Buffer, pSap->ServerName) == 0)
+ {
+ //
+ // it matches. We are done!
+ //
+
+ *pdwErr = FillBufferWithCsAddr(pSap->Address,
+ porns->nProt,
+ porns->lpCsAddrBuffer,
+ porns->lpdwBufferLength,
+ porns->lpcAddress);
+ return(dwrcDone);
+ }
+ return(dwrcNil);
+}
+
+
+
+DWORD
+NwpGetAddressViaSap(
+ IN WORD nServiceType,
+ IN LPWSTR lpServiceName,
+ IN DWORD nProt,
+ IN OUT LPVOID lpCsAddrBuffer,
+ IN OUT LPDWORD lpdwBufferLength,
+ IN HANDLE hCancellationEvent,
+ OUT LPDWORD lpcAddress
+ )
+/*++
+
+Routine Description:
+
+ This routine uses SAP requests to find the address of the given service
+ name/type. It can handle looking up by name and type alone.
+
+Arguments:
+
+ Handle - the RnR handle, if appropriate
+
+ nServiceType - service type
+
+ lpServiceName - unique string representing the service name
+
+ lpCsAddrBuffer - on return, will be filled with CSADDR_INFO structures
+
+ lpdwBufferLength - on input, the number of bytes contained in the buffer
+ pointed to by lpCsAddrBuffer. On output, the minimum number of bytes
+ to pass for the lpCsAddrBuffer to retrieve all the requested info
+
+ hCancellationEvent - the event which signals us to cancel the request
+
+ lpcAddress - on output, the number of CSADDR_INFO structures returned
+
+Return Value:
+
+ Win32 error code.
+
+--*/
+{
+ DWORD err = NO_ERROR;
+ NTSTATUS ntstatus;
+ UNICODE_STRING UServiceName;
+ OEM_STRING OemServiceName;
+ SOCKET socketSap;
+ SAP_RNR_CONTEXT src;
+ PSAP_BCAST_CONTROL psbc = &src.u_type.sbc;
+ OLDRNRSAP ors;
+
+ *lpcAddress = 0;
+
+ _wcsupr( lpServiceName );
+ RtlInitUnicodeString( &UServiceName, lpServiceName );
+ ntstatus = RtlUnicodeStringToOemString( &OemServiceName,
+ &UServiceName,
+ TRUE );
+ if ( !NT_SUCCESS( ntstatus ))
+ return RtlNtStatusToDosError( ntstatus );
+
+ memset(&src, 0, sizeof(src));
+
+ err = SapGetSapSocket(&psbc->s);
+ if ( err )
+ {
+ RtlFreeOemString( &OemServiceName );
+ return err;
+ }
+
+ psbc->psrc = &src;
+ psbc->dwIndex = 0;
+ psbc->dwTickCount = 0;
+ psbc->pvArg = (PVOID)&ors;
+ psbc->Func = OldRnRCheckSapData;
+ psbc->fCheckCancel = OldRnRCheckCancel;
+ psbc->fFlags = 0;
+ psbc->wQueryType = QT_GENERAL_QUERY;
+
+
+
+ ors.poem = &OemServiceName;
+ ors.hCancel = hCancellationEvent,
+ ors.lpCsAddrBuffer = lpCsAddrBuffer;
+ ors.lpdwBufferLength = lpdwBufferLength;
+ ors.lpcAddress = lpcAddress;
+ ors.nProt = nProt;
+
+ err = SapGetSapForType(psbc, nServiceType);
+
+ RtlFreeOemString( &OemServiceName );
+
+ //
+ // Clean up the socket interface
+ //
+ (VOID)SapFreeSapSocket(psbc->s);
+
+ return err;
+}
+
+
+
+DWORD
+NwGetService(
+ IN LPWSTR Reserved,
+ IN WORD nSapType,
+ IN LPWSTR lpServiceName,
+ IN DWORD dwProperties,
+ OUT LPBYTE lpServiceInfo,
+ IN DWORD dwBufferLength,
+ OUT LPDWORD lpdwBytesNeeded
+ )
+/*++
+Routine Description:
+
+ This routine gets the service info.
+
+Arguments:
+
+ Reserved - unused
+
+ nSapType - SAP type
+
+ lpServiceName - service name
+
+ dwProperties - specifys the properties of the service info needed
+
+ lpServiceInfo - on output, contains the SERVICE_INFO
+
+ dwBufferLength - size of buffer pointed by lpServiceInfo
+
+ lpdwBytesNeeded - if the buffer pointed by lpServiceInfo is not large
+ enough, this will contain the bytes needed on output
+
+Return Value:
+
+ Win32 error.
+
+--*/
+{
+ DWORD err = NO_ERROR;
+ DWORD nSize = sizeof(SERVICE_INFO);
+ PREGISTERED_SERVICE pSvc;
+ PSERVICE_INFO pSvcInfo = (PSERVICE_INFO) lpServiceInfo;
+ LPBYTE pBufferStart;
+
+ UNREFERENCED_PARAMETER( Reserved );
+
+ //
+ // Check if all parameters passed in are valid
+ //
+ if ( wcslen( lpServiceName ) > SAP_OBJECT_NAME_MAX_LENGTH-1 )
+ return ERROR_INVALID_PARAMETER;
+
+ pSvc = GetServiceItemFromList( nSapType, lpServiceName );
+ if ( pSvc == NULL )
+ return ERROR_SERVICE_NOT_FOUND;
+
+ //
+ // Calculate the size needed to return the requested info
+ //
+ if ( (( dwProperties == PROP_ALL ) || ( dwProperties & PROP_COMMENT ))
+ && ( pSvc->pServiceInfo->lpComment != NULL )
+ )
+ {
+ nSize += ( wcslen( pSvc->pServiceInfo->lpComment) + 1) * sizeof(WCHAR);
+ }
+
+ if ( (( dwProperties == PROP_ALL ) || ( dwProperties & PROP_LOCALE ))
+ && ( pSvc->pServiceInfo->lpLocale != NULL )
+ )
+ {
+ nSize += ( wcslen( pSvc->pServiceInfo->lpLocale) + 1) * sizeof(WCHAR);
+ }
+
+ if ( (( dwProperties == PROP_ALL ) || ( dwProperties & PROP_MACHINE ))
+ && ( pSvc->pServiceInfo->lpMachineName != NULL )
+ )
+ {
+ nSize += ( wcslen( pSvc->pServiceInfo->lpMachineName) + 1) * sizeof(WCHAR);
+ }
+
+ if (( dwProperties == PROP_ALL ) || ( dwProperties & PROP_ADDRESSES ))
+ {
+ DWORD i;
+ DWORD dwCount = pSvc->pServiceInfo->lpServiceAddress->dwAddressCount;
+
+ nSize = ROUND_UP_COUNT( nSize, ALIGN_QUAD );
+ nSize += sizeof( SERVICE_ADDRESSES );
+ if ( dwCount > 1 )
+ nSize += ( dwCount - 1 ) * sizeof( SERVICE_ADDRESS );
+
+ for ( i = 0; i < dwCount; i++ )
+ {
+ SERVICE_ADDRESS *pAddr =
+ &(pSvc->pServiceInfo->lpServiceAddress->Addresses[i]);
+
+
+ nSize = ROUND_UP_COUNT( nSize, ALIGN_QUAD );
+ nSize += pAddr->dwAddressLength;
+ nSize = ROUND_UP_COUNT( nSize, ALIGN_QUAD );
+ nSize += pAddr->dwPrincipalLength;
+ }
+ }
+
+ if (( dwProperties == PROP_ALL ) || ( dwProperties & PROP_SD ))
+ {
+ nSize = ROUND_UP_COUNT( nSize, ALIGN_QUAD );
+ nSize += pSvc->pServiceInfo->ServiceSpecificInfo.cbSize;
+ }
+
+ //
+ // Return error if the buffer passed in is not big enough
+ //
+ if ( dwBufferLength < nSize )
+ {
+ *lpdwBytesNeeded = nSize;
+ return ERROR_INSUFFICIENT_BUFFER;
+ }
+
+ //
+ // Fill in all requested service info
+ //
+ memset( pSvcInfo, 0, sizeof(*pSvcInfo)); // Make all fields 0 i.e.
+ // all pointer fields NULL
+
+ pSvcInfo->dwDisplayHint = pSvc->pServiceInfo->dwDisplayHint;
+ pSvcInfo->dwVersion = pSvc->pServiceInfo->dwVersion;
+ pSvcInfo->dwTime = pSvc->pServiceInfo->dwTime;
+
+ pBufferStart = ((LPBYTE) pSvcInfo) + sizeof( *pSvcInfo );
+
+ if ( (( dwProperties == PROP_ALL ) || ( dwProperties & PROP_COMMENT ))
+ && ( pSvc->pServiceInfo->lpComment != NULL )
+ )
+ {
+ pSvcInfo->lpComment = (LPWSTR) pBufferStart;
+ wcscpy( pSvcInfo->lpComment, pSvc->pServiceInfo->lpComment );
+ pBufferStart += ( wcslen( pSvcInfo->lpComment ) + 1) * sizeof(WCHAR);
+
+ pSvcInfo->lpComment = (LPWSTR) ((LPBYTE) pSvcInfo->lpComment - lpServiceInfo );
+ }
+
+ if ( (( dwProperties == PROP_ALL ) || ( dwProperties & PROP_LOCALE ))
+ && ( pSvc->pServiceInfo->lpLocale != NULL )
+ )
+ {
+ pSvcInfo->lpLocale = (LPWSTR) pBufferStart;
+ wcscpy( pSvcInfo->lpLocale, pSvc->pServiceInfo->lpLocale );
+ pBufferStart += ( wcslen( pSvcInfo->lpLocale ) + 1) * sizeof(WCHAR);
+ pSvcInfo->lpLocale = (LPWSTR) ((LPBYTE) pSvcInfo->lpLocale - lpServiceInfo);
+ }
+
+ if ( (( dwProperties == PROP_ALL ) || ( dwProperties & PROP_MACHINE ))
+ && ( pSvc->pServiceInfo->lpMachineName != NULL )
+ )
+ {
+ pSvcInfo->lpMachineName = (LPWSTR) pBufferStart;
+ wcscpy( pSvcInfo->lpMachineName, pSvc->pServiceInfo->lpMachineName );
+ pBufferStart += ( wcslen( pSvcInfo->lpMachineName) + 1) * sizeof(WCHAR);
+ pSvcInfo->lpMachineName = (LPWSTR) ((LPBYTE) pSvcInfo->lpMachineName -
+ lpServiceInfo );
+ }
+
+ if (( dwProperties == PROP_ALL ) || ( dwProperties & PROP_ADDRESSES ))
+ {
+ DWORD i, dwCount, dwLen;
+
+ pBufferStart = ROUND_UP_POINTER( pBufferStart, ALIGN_QUAD );
+ pSvcInfo->lpServiceAddress = (LPSERVICE_ADDRESSES) pBufferStart;
+ dwCount = pSvcInfo->lpServiceAddress->dwAddressCount =
+ pSvc->pServiceInfo->lpServiceAddress->dwAddressCount;
+
+ pBufferStart += sizeof( SERVICE_ADDRESSES );
+
+ for ( i = 0; i < dwCount; i++ )
+ {
+ SERVICE_ADDRESS *pTmpAddr =
+ &( pSvcInfo->lpServiceAddress->Addresses[i]);
+
+ SERVICE_ADDRESS *pAddr =
+ &( pSvc->pServiceInfo->lpServiceAddress->Addresses[i]);
+
+ pTmpAddr->dwAddressType = pAddr->dwAddressType;
+ pTmpAddr->dwAddressFlags = pAddr->dwAddressFlags;
+
+ //
+ // setup Address
+ //
+ pBufferStart = ROUND_UP_POINTER( pBufferStart, ALIGN_QUAD );
+ pTmpAddr->lpAddress = (LPBYTE) ( pBufferStart - lpServiceInfo );
+ pTmpAddr->dwAddressLength = pAddr->dwAddressLength;
+ memcpy( pBufferStart, pAddr->lpAddress, pAddr->dwAddressLength );
+ pBufferStart += pAddr->dwAddressLength;
+
+ //
+ // setup Principal
+ //
+ pBufferStart = ROUND_UP_POINTER( pBufferStart, ALIGN_QUAD );
+ pTmpAddr->lpPrincipal = (LPBYTE) ( pBufferStart - lpServiceInfo );
+ pTmpAddr->dwPrincipalLength = pAddr->dwPrincipalLength;
+ memcpy(pBufferStart, pAddr->lpPrincipal, pAddr->dwPrincipalLength );
+ pBufferStart += pAddr->dwPrincipalLength;
+ }
+
+ pSvcInfo->lpServiceAddress = (LPSERVICE_ADDRESSES)
+ ((LPBYTE) pSvcInfo->lpServiceAddress - lpServiceInfo);
+ }
+
+ if (( dwProperties == PROP_ALL ) || ( dwProperties & PROP_SD ))
+ {
+ pBufferStart = ROUND_UP_POINTER( pBufferStart, ALIGN_QUAD );
+ pSvcInfo->ServiceSpecificInfo.cbSize =
+ pSvc->pServiceInfo->ServiceSpecificInfo.cbSize;
+ pSvcInfo->ServiceSpecificInfo.pBlobData = pBufferStart;
+ RtlCopyMemory( pSvcInfo->ServiceSpecificInfo.pBlobData,
+ pSvc->pServiceInfo->ServiceSpecificInfo.pBlobData,
+ pSvcInfo->ServiceSpecificInfo.cbSize );
+ pSvcInfo->ServiceSpecificInfo.pBlobData =
+ (LPBYTE) ( pSvcInfo->ServiceSpecificInfo.pBlobData - lpServiceInfo);
+ }
+
+ return NO_ERROR;
+}
+
+DWORD
+NwInitializeSocket(
+ IN HANDLE hEventHandle
+ )
+/*++
+
+Routine Description:
+
+ This routine initializes the socket needed for us to do the
+ SAP advertise ourselves.
+
+Arguments:
+
+ hEventHandle - A handle to the NwDoneEvent if this code is running in
+ the context of a service. Otherwise this code is running
+ in the context of a regular executable.
+
+Return Value:
+
+ Win32 error.
+
+--*/
+{
+ DWORD err = NO_ERROR;
+ WSADATA wsaData;
+ SOCKADDR_IPX socketAddr;
+ INT nValue;
+ HANDLE hThread;
+ DWORD dwThreadId;
+
+ if ( fInitSocket )
+ return NO_ERROR;
+
+ //
+ // Initialize the socket interface
+ //
+// err = WSAStartup( WSOCK_VER_REQD, &wsaData );
+// if ( err )
+// return err;
+
+ //
+ // Open an IPX datagram socket
+ //
+ socketSap = socket( AF_IPX, SOCK_DGRAM, NSPROTO_IPX );
+ if ( socketSap == INVALID_SOCKET )
+ return WSAGetLastError();
+
+ //
+ // Allow sending of broadcasts
+ //
+ nValue = 1;
+ if ( setsockopt( socketSap,
+ SOL_SOCKET,
+ SO_BROADCAST,
+ (PVOID) &nValue,
+ sizeof(INT)) == SOCKET_ERROR )
+ {
+ err = WSAGetLastError();
+ goto CleanExit;
+ }
+
+ //
+ // Bind the socket
+ //
+ memset( &socketAddr, 0, sizeof( SOCKADDR_IPX));
+ socketAddr.sa_family = AF_IPX;
+ socketAddr.sa_socket = 0; // no specific port
+
+ if ( bind( socketSap,
+ (PSOCKADDR) &socketAddr,
+ sizeof( SOCKADDR_IPX)) == SOCKET_ERROR )
+ {
+ err = WSAGetLastError();
+ goto CleanExit;
+ }
+
+ //
+ // Set the extended address option
+ //
+ nValue = 1;
+ if ( setsockopt( socketSap, // Socket Handle
+ NSPROTO_IPX, // Option Level
+ IPX_EXTENDED_ADDRESS, // Option Name
+ (PUCHAR)&nValue, // Ptr to on/off flag
+ sizeof(INT)) == SOCKET_ERROR ) // Length of flag
+ {
+
+ err = WSAGetLastError();
+ goto CleanExit;
+ }
+
+ //
+ // Create the thread that loops through the registered service
+ // link list and send out SAP advertise packets for each one of them
+ //
+
+ hThread = CreateThread( NULL, // no security attributes
+ 0, // default stack size
+ SapFunc, // thread function
+ hEventHandle, // argument to SapFunc
+ 0, // default creation flags
+ &dwThreadId );
+
+ if ( hThread == NULL )
+ {
+ err = GetLastError();
+ goto CleanExit;
+ }
+
+ fInitSocket = TRUE;
+
+CleanExit:
+
+ if ( err )
+ closesocket( socketSap );
+
+ return err;
+}
+
+DWORD
+NwAdvertiseService(
+ IN LPWSTR lpServiceName,
+ IN WORD nSapType,
+ IN LPSOCKADDR_IPX pAddr,
+ IN HANDLE hEventHandle
+ )
+/*++
+
+Routine Description:
+
+ This routine sends out SAP identification packets for the
+ given service name and type.
+
+Arguments:
+
+ lpServiceName - unique string representing the service name
+
+ nSapType - SAP type
+
+ pAddr - address of the service
+
+ hEventHandle - A handle to the NwDoneEvent if this code is running in
+ the context of a service. Otherwise this code is running
+ in the context of a regular executable.
+
+Return Value:
+
+ Win32 error.
+
+--*/
+{
+ NTSTATUS ntstatus;
+
+ UNICODE_STRING uServiceName;
+ OEM_STRING oemServiceName;
+
+ SAP_IDENT_HEADER_EX sapIdent;
+ UCHAR destAddr[SAP_ADDRESS_LENGTH];
+ PSOCKADDR_IPX pAddrTmp = pAddr;
+ SOCKADDR_IPX newAddr;
+ SOCKADDR_IPX bindAddr;
+ DWORD len = sizeof( SOCKADDR_IPX );
+ DWORD getsockname_rc ;
+
+ if ( !fInitSocket )
+ {
+ DWORD err = NwInitializeSocket( hEventHandle );
+ if ( err )
+ return err;
+ }
+
+ //
+ // get local addressing info. we are only interested in the net number.
+ //
+ getsockname_rc = getsockname( socketSap,
+ (PSOCKADDR) &bindAddr,
+ &len );
+
+ //
+ // Convert the service name to OEM string
+ //
+ RtlInitUnicodeString( &uServiceName, lpServiceName );
+ ntstatus = RtlUnicodeStringToOemString( &oemServiceName,
+ &uServiceName,
+ TRUE );
+ if ( !NT_SUCCESS( ntstatus ))
+ return RtlNtStatusToDosError( ntstatus );
+
+ _strupr( (LPSTR) oemServiceName.Buffer );
+
+ if ( !memcmp( pAddr->sa_netnum,
+ "\x00\x00\x00\x00",
+ IPX_ADDRESS_NETNUM_LENGTH ))
+ {
+ if ( getsockname_rc != SOCKET_ERROR )
+ {
+ // copy the ipx address to advertise
+ memcpy( &newAddr,
+ pAddr,
+ sizeof( SOCKADDR_IPX));
+
+ // replace the net number with the correct one
+ memcpy( &(newAddr.sa_netnum),
+ &(bindAddr.sa_netnum),
+ IPX_ADDRESS_NETNUM_LENGTH );
+
+ pAddrTmp = &newAddr;
+ }
+ }
+
+ //
+ // Format the SAP identification packet
+ //
+
+ sapIdent.ResponseType = htons( 2 );
+ sapIdent.ServerType = htons( nSapType );
+ memset( sapIdent.ServerName, '\0', SAP_OBJECT_NAME_MAX_LENGTH );
+ strcpy( sapIdent.ServerName, oemServiceName.Buffer );
+ RtlCopyMemory( sapIdent.Address, pAddrTmp->sa_netnum, IPX_ADDRESS_LENGTH );
+ sapIdent.HopCount = htons( 1 );
+
+ RtlFreeOemString( &oemServiceName );
+
+ //
+ // Set the address to send to
+ //
+ memcpy( destAddr, SapBroadcastAddress, SAP_ADDRESS_LENGTH );
+ if ( getsockname_rc != SOCKET_ERROR )
+ {
+ LPSOCKADDR_IPX newDestAddr = (LPSOCKADDR_IPX)destAddr ;
+
+ //
+ // replace the net number with the correct one
+ //
+ memcpy( &(newDestAddr->sa_netnum),
+ &(bindAddr.sa_netnum),
+ IPX_ADDRESS_NETNUM_LENGTH );
+
+ }
+
+ //
+ // Send the packet out
+ //
+ if ( sendto( socketSap,
+ (PVOID) &sapIdent,
+ sizeof( sapIdent ),
+ 0,
+ (PSOCKADDR) destAddr,
+ SAP_ADDRESS_LENGTH ) == SOCKET_ERROR )
+ {
+ return WSAGetLastError();
+ }
+
+ return NO_ERROR;
+}
+
+DWORD
+AddServiceToList(
+ IN LPSERVICE_INFO lpServiceInfo,
+ IN WORD nSapType,
+ IN BOOL fAdvertiseBySap,
+ IN INT nIndexIPXAddress
+ )
+/*++
+
+Routine Description:
+
+ This routine adds the service to the link list of services
+ we advertised.
+
+Arguments:
+
+ lpServiceInfo - service information
+
+ nSapType - SAP type
+
+ fAdvertiseBySap - TRUE if this service is advertised by SAP service,
+ FALSE if we are advertising ourselves.
+
+ nIndexIPXAddress - index of the ipx address
+
+Return Value:
+
+ Win32 error.
+
+--*/
+{
+ PREGISTERED_SERVICE pSvcNew;
+ PSERVICE_INFO pSI;
+ LPBYTE pBufferStart;
+ DWORD nSize = 0;
+
+ //
+ // Allocate a new entry for the service list
+ //
+
+ pSvcNew = LocalAlloc( LMEM_ZEROINIT, sizeof( REGISTERED_SERVICE ));
+ if ( pSvcNew == NULL )
+ return ERROR_NOT_ENOUGH_MEMORY;
+
+ //
+ // Calculate the size needed for the SERVICE_INFO structure
+ //
+ nSize = sizeof( *lpServiceInfo)
+ + sizeof( *(lpServiceInfo->lpServiceType));
+
+ if ( lpServiceInfo->lpServiceName != NULL )
+ nSize += ( wcslen( lpServiceInfo->lpServiceName) + 1) * sizeof(WCHAR);
+ if ( lpServiceInfo->lpComment != NULL )
+ nSize += ( wcslen( lpServiceInfo->lpComment) + 1) * sizeof(WCHAR);
+ if ( lpServiceInfo->lpLocale != NULL )
+ nSize += ( wcslen( lpServiceInfo->lpLocale) + 1) * sizeof(WCHAR);
+ if ( lpServiceInfo->lpMachineName != NULL )
+ nSize += ( wcslen( lpServiceInfo->lpMachineName) + 1) * sizeof(WCHAR);
+
+ nSize = ROUND_UP_COUNT( nSize, ALIGN_QUAD );
+
+ if ( lpServiceInfo->lpServiceAddress != NULL )
+ {
+ nSize += sizeof( SERVICE_ADDRESSES );
+ nSize = ROUND_UP_COUNT( nSize, ALIGN_QUAD );
+
+ nSize += lpServiceInfo->lpServiceAddress->Addresses[nIndexIPXAddress].dwAddressLength;
+ nSize = ROUND_UP_COUNT( nSize, ALIGN_QUAD );
+
+ nSize += lpServiceInfo->lpServiceAddress->Addresses[nIndexIPXAddress].dwPrincipalLength;
+ nSize = ROUND_UP_COUNT( nSize, ALIGN_QUAD );
+ }
+
+ nSize += lpServiceInfo->ServiceSpecificInfo.cbSize ;
+
+ //
+ // Allocate a SERVICE_INFO structure for the new list entry
+ //
+ pSI = LocalAlloc( LMEM_ZEROINIT, nSize );
+ if ( pSI == NULL )
+ {
+ LocalFree( pSvcNew );
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ //
+ // Copy the information of SERVICE_INFO into list entry
+ //
+ *pSI = *lpServiceInfo;
+
+ pBufferStart = (( (LPBYTE) pSI) + sizeof( *lpServiceInfo ));
+
+ pSI->lpServiceType = (LPGUID) pBufferStart;
+ *(pSI->lpServiceType) = *(lpServiceInfo->lpServiceType);
+ pBufferStart += sizeof( *(lpServiceInfo->lpServiceType) );
+
+ if ( lpServiceInfo->lpServiceName != NULL )
+ {
+ pSI->lpServiceName = (LPWSTR) pBufferStart;
+ wcscpy( pSI->lpServiceName, lpServiceInfo->lpServiceName );
+ _wcsupr( pSI->lpServiceName );
+ pBufferStart += ( wcslen( lpServiceInfo->lpServiceName ) + 1 )
+ * sizeof(WCHAR);
+ }
+
+ if ( lpServiceInfo->lpComment != NULL )
+ {
+ pSI->lpComment = (LPWSTR) pBufferStart;
+ wcscpy( pSI->lpComment, lpServiceInfo->lpComment );
+ pBufferStart += ( wcslen( lpServiceInfo->lpComment ) + 1 )
+ * sizeof(WCHAR);
+ }
+
+ if ( lpServiceInfo->lpLocale != NULL )
+ {
+ pSI->lpLocale = (LPWSTR) pBufferStart;
+ wcscpy( pSI->lpLocale, lpServiceInfo->lpLocale );
+ pBufferStart += ( wcslen( lpServiceInfo->lpLocale ) + 1 )
+ * sizeof(WCHAR);
+ }
+
+ if ( lpServiceInfo->lpMachineName != NULL )
+ {
+ pSI->lpMachineName = (LPWSTR) pBufferStart;
+ wcscpy( pSI->lpMachineName, lpServiceInfo->lpMachineName );
+ pBufferStart += (wcslen( lpServiceInfo->lpMachineName ) + 1)
+ * sizeof(WCHAR);
+ }
+
+ pBufferStart = ROUND_UP_POINTER( pBufferStart, ALIGN_QUAD) ;
+
+ if ( lpServiceInfo->lpServiceAddress != NULL )
+ {
+ DWORD nSize;
+
+ pSI->lpServiceAddress = (LPSERVICE_ADDRESSES) pBufferStart;
+ pSI->lpServiceAddress->dwAddressCount = 1; // Just 1 IPX address
+
+ memcpy( &(pSI->lpServiceAddress->Addresses[0]),
+ &(lpServiceInfo->lpServiceAddress->Addresses[nIndexIPXAddress]),
+ sizeof( SERVICE_ADDRESS) );
+ pBufferStart += sizeof( SERVICE_ADDRESSES);
+
+ pBufferStart = ROUND_UP_POINTER( pBufferStart, ALIGN_QUAD) ;
+ nSize = pSI->lpServiceAddress->Addresses[0].dwAddressLength;
+ pSI->lpServiceAddress->Addresses[0].lpAddress = pBufferStart;
+ memcpy( pBufferStart,
+ lpServiceInfo->lpServiceAddress->Addresses[nIndexIPXAddress].lpAddress,
+ nSize );
+ pBufferStart += nSize;
+
+ pBufferStart = ROUND_UP_POINTER( pBufferStart, ALIGN_QUAD) ;
+ nSize = pSI->lpServiceAddress->Addresses[0].dwPrincipalLength;
+ pSI->lpServiceAddress->Addresses[0].lpPrincipal = pBufferStart;
+ memcpy( pBufferStart,
+ lpServiceInfo->lpServiceAddress->Addresses[nIndexIPXAddress].lpPrincipal,
+ nSize );
+ pBufferStart += nSize;
+ pBufferStart = ROUND_UP_POINTER( pBufferStart, ALIGN_QUAD) ;
+ }
+
+ pSI->ServiceSpecificInfo.pBlobData = pBufferStart;
+ RtlCopyMemory( pSI->ServiceSpecificInfo.pBlobData,
+ lpServiceInfo->ServiceSpecificInfo.pBlobData,
+ pSI->ServiceSpecificInfo.cbSize );
+
+ //
+ // Fill in the data in the list entry
+ //
+ pSvcNew->nSapType = nSapType;
+ pSvcNew->fAdvertiseBySap = fAdvertiseBySap;
+ pSvcNew->Next = NULL;
+ pSvcNew->pServiceInfo = pSI;
+
+ //
+ // Add the newly created list entry into the service list
+ //
+ EnterCriticalSection( &NwServiceListCriticalSection );
+
+ if ( pServiceListHead == NULL )
+ pServiceListHead = pSvcNew;
+ else
+ pServiceListTail->Next = pSvcNew;
+
+ pServiceListTail = pSvcNew;
+
+ LeaveCriticalSection( &NwServiceListCriticalSection );
+
+ return NO_ERROR;
+}
+
+VOID
+RemoveServiceFromList(
+ PREGISTERED_SERVICE pSvc
+ )
+/*++
+
+Routine Description:
+
+ This routine removes the service from the link list of services
+ we advertised.
+
+Arguments:
+
+ pSvc - the registered service node to remove
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PREGISTERED_SERVICE pCur, pPrev;
+
+ EnterCriticalSection( &NwServiceListCriticalSection );
+
+ for ( pCur = pServiceListHead, pPrev = NULL ; pCur != NULL;
+ pPrev = pCur, pCur = pCur->Next )
+ {
+ if ( pCur == pSvc )
+ {
+ if ( pPrev == NULL ) // i.e. pCur == pSvc == pServiceListHead
+ {
+ pServiceListHead = pSvc->Next;
+ if ( pServiceListTail == pSvc )
+ pServiceListTail = NULL;
+ }
+ else
+ {
+ pPrev->Next = pSvc->Next;
+ if ( pServiceListTail == pSvc )
+ pServiceListTail = pPrev;
+ }
+
+ (VOID) LocalFree( pCur->pServiceInfo );
+ (VOID) LocalFree( pCur );
+ break;
+ }
+ }
+
+ LeaveCriticalSection( &NwServiceListCriticalSection );
+}
+
+PREGISTERED_SERVICE
+GetServiceItemFromList(
+ IN WORD nSapType,
+ IN LPWSTR pServiceName
+ )
+/*++
+
+Routine Description:
+
+ This routine returns the registered service node with the given
+ service name and type.
+
+Arguments:
+
+ nSapType - SAP type
+
+ pServiceName - service name
+
+Return Value:
+
+ Returns the pointer to the registered service node,
+ NULL if we cannot find the service type/name.
+
+--*/
+{
+ PREGISTERED_SERVICE pSvc;
+
+ EnterCriticalSection( &NwServiceListCriticalSection );
+
+ for ( pSvc = pServiceListHead; pSvc != NULL; pSvc = pSvc->Next )
+ {
+ if ( ( pSvc->nSapType == nSapType )
+ && ( _wcsicmp( pSvc->pServiceInfo->lpServiceName, pServiceName ) == 0)
+ )
+ {
+ LeaveCriticalSection( &NwServiceListCriticalSection );
+
+ return pSvc;
+ }
+ }
+
+ LeaveCriticalSection( &NwServiceListCriticalSection );
+ return NULL;
+}
+
+DWORD
+SapFunc(
+ HANDLE hEventHandle
+ )
+/*++
+
+Routine Description:
+
+ This routine is a separate thread that wakes up every 60 seconds
+ and advertise all the service contained in the service link list
+ that are not advertised by the SAP service.
+
+Arguments:
+
+ hEventHandle - used to notify thread that server is stopping
+
+Return Value:
+
+ Win32 error.
+
+--*/
+{
+ DWORD err = NO_ERROR;
+
+ //
+ // This thread loops until the service is shut down or when some error
+ // occurred in WaitForSingleObject
+ //
+
+ while ( TRUE )
+ {
+ DWORD rc;
+
+ if ( hEventHandle != NULL )
+ {
+ rc = WaitForSingleObject( hEventHandle, SAP_ADVERTISE_FREQUENCY );
+ }
+ else
+ {
+ Sleep( SAP_ADVERTISE_FREQUENCY );
+ rc = WAIT_TIMEOUT;
+ }
+
+ if ( rc == WAIT_FAILED )
+ {
+ err = GetLastError();
+ break;
+ }
+ else if ( rc == WAIT_OBJECT_0 )
+ {
+ //
+ // The service is stopping, break out of the loop and
+ // return, thus terminating the thread
+ //
+ break;
+ }
+ else if ( rc == WAIT_TIMEOUT )
+ {
+ PREGISTERED_SERVICE pSvc;
+ SOCKADDR_IPX bindAddr;
+ DWORD fGetAddr;
+
+ fGetAddr = FALSE;
+
+ //
+ // Time out occurred, time to send the SAP advertise packets
+ //
+
+ EnterCriticalSection( &NwServiceListCriticalSection );
+
+ if ( pServiceListHead == NULL )
+ {
+ LeaveCriticalSection( &NwServiceListCriticalSection );
+
+ //
+ // Clean up the SAP interface
+ //
+ (VOID) SapLibShutdown();
+
+ //
+ // Clean up the socket interface
+ //
+ if ( fInitSocket )
+ {
+ closesocket( socketSap );
+// (VOID) WSACleanup();
+ }
+
+ break;
+ }
+
+ for ( pSvc = pServiceListHead; pSvc != NULL; pSvc = pSvc->Next )
+ {
+ if ( !pSvc->fAdvertiseBySap )
+ {
+ //
+ // Ignore the error since we can't return
+ // nor pop up the error
+ //
+
+ SOCKADDR_IPX *pAddr = (SOCKADDR_IPX *)
+ pSvc->pServiceInfo->lpServiceAddress->Addresses[0].lpAddress;
+ SOCKADDR_IPX *pAddrToAdvertise = pAddr;
+ SOCKADDR_IPX newAddr;
+
+ if ( !memcmp( pAddr->sa_netnum,
+ "\x00\x00\x00\x00",
+ IPX_ADDRESS_NETNUM_LENGTH ))
+ {
+
+ if ( !fGetAddr )
+ {
+ DWORD len = sizeof( SOCKADDR_IPX );
+
+ rc = getsockname( socketSap,
+ (PSOCKADDR) &bindAddr,
+ &len );
+
+ if ( rc != SOCKET_ERROR )
+ fGetAddr = TRUE;
+ }
+
+ if ( fGetAddr )
+ {
+ // copy the ipx address to advertise
+ memcpy( &newAddr,
+ pAddr,
+ sizeof( SOCKADDR_IPX));
+
+ // replace the net number with the correct one
+ memcpy( &(newAddr.sa_netnum),
+ &(bindAddr.sa_netnum),
+ IPX_ADDRESS_NETNUM_LENGTH );
+
+ pAddr = &newAddr;
+ }
+ }
+
+ (VOID) NwAdvertiseService(
+ pSvc->pServiceInfo->lpServiceName,
+ pSvc->nSapType,
+ pAddr,
+ hEventHandle );
+ }
+ }
+
+ LeaveCriticalSection( &NwServiceListCriticalSection );
+ }
+ }
+
+ return err;
+}
diff --git a/private/nw/svcdlls/nwwks/client/gtadrnr.c b/private/nw/svcdlls/nwwks/client/gtadrnr.c
new file mode 100644
index 000000000..7b2227c11
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/gtadrnr.c
@@ -0,0 +1,1935 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ gtsadrnr.c
+
+Abstract:
+
+ This module contains the code to support the new RnR APIs. Some
+ support code is in getaddr.c, but the interface routines and
+ routines specific to RnR2 are herein.
+
+Author:
+
+ ArnoldM 4-Dec-1995
+Revision History:
+
+ ArnoldM Created
+
+--*/
+
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+#include "ncp.h"
+#include <rpc.h>
+#include <winsock2.h>
+#include <ws2spi.h>
+#include <wsipx.h>
+#include <rnrdefs.h>
+#include <svcguid.h>
+#include <rnraddrs.h>
+
+
+//-------------------------------------------------------------------//
+// //
+// Globals in getaddr.c and provider .c //
+// //
+//-------------------------------------------------------------------//
+
+DWORD
+NwpGetAddressByName(
+ IN LPWSTR Reserved,
+ IN WORD nServiceType,
+ IN LPWSTR lpServiceName,
+ IN OUT LPSOCKADDR_IPX lpsockaddr
+);
+
+DWORD
+NwpGetAddressViaSap(
+ IN WORD nServiceType,
+ IN LPWSTR lpServiceName,
+ IN DWORD nProt,
+ IN OUT LPVOID lpCsAddrBuffer,
+ IN OUT LPDWORD lpdwBufferLength,
+ IN HANDLE hCancellationEvent,
+ OUT LPDWORD lpcAddress
+);
+
+DWORD
+NwpRnR2AddServiceType(
+ IN LPWSTR lpServiceTypeName,
+ IN LPGUID lpClassType,
+ IN WORD wSapId,
+ IN WORD wPort
+);
+
+
+DWORD
+SapFreeSapSocket(
+ SOCKET s
+ );
+
+PSAP_RNR_CONTEXT
+SapMakeContext
+ (
+ IN HANDLE Handle,
+ DWORD dwExcess
+ );
+
+BOOL
+NwpLookupSapInRegistry(
+ IN LPGUID lpServiceType,
+ OUT PWORD pnSapType,
+ OUT PWORD pwPort,
+ IN OUT PDWORD pfConnectionOriented
+);
+
+PSAP_RNR_CONTEXT
+SapGetContext(
+ HANDLE h
+ );
+
+DWORD
+DoASap(
+ IN PSAP_RNR_CONTEXT psrcContext,
+ IN WORD QueryType,
+ IN PBYTE * ppData,
+ IN LPWSAQUERYSETW lpqsResults,
+ IN PLONG pl,
+ IN PDWORD pdw
+ );
+
+VOID
+SapReleaseContext(
+ IN PSAP_RNR_CONTEXT psrcContext
+ );
+
+DWORD
+SapGetSapSocket(
+ SOCKET * ps
+ );
+
+DWORD
+PrepareForSap(
+ IN PSAP_RNR_CONTEXT psrc
+ );
+
+DWORD
+SapGetSapForType(
+ PSAP_BCAST_CONTROL psbc,
+ WORD nServiceType
+ );
+
+DWORD
+FillBufferWithCsAddr(
+ IN LPBYTE pAddress,
+ IN DWORD nProt,
+ IN OUT LPVOID lpCsAddrBuffer,
+ IN OUT LPDWORD lpdwBufferLength,
+ OUT LPDWORD pcAddress
+ );
+
+DWORD
+pSapSetService2(
+ IN DWORD dwOperation,
+ IN LPWSTR lpszServiceInstanceName,
+ IN PBYTE pbAddress,
+ IN LPGUID pServiceType,
+ IN WORD nServiceType
+ );
+
+DWORD
+NwpGetRnRAddress(
+ PHANDLE phServer,
+ PWCHAR pwszContext,
+ PLONG plIndex,
+ LPWSTR lpServiceName,
+ WORD nServiceType,
+ PDWORD pdwVersion,
+ DWORD dwInSize,
+ LPWSTR OutName,
+ SOCKADDR_IPX * pSockAddr
+ );
+
+
+DWORD
+NwpSetClassInfo(
+ IN LPWSTR lpwszClassName,
+ IN LPGUID lpClassId,
+ IN PCHAR lpbProp
+ );
+
+VOID
+pFreeAllContexts();
+
+extern DWORD oldRnRServiceRegister, oldRnRServiceDeRegister;
+//-------------------------------------------------------------------//
+// //
+// Local Function Prototypes //
+// //
+//-------------------------------------------------------------------//
+
+INT WINAPI
+pGetServiceClassInfo(
+ IN LPGUID lpProviderId,
+ IN OUT LPDWORD lpdwBufSize,
+ IN OUT LPWSASERVICECLASSINFOW lpServiceClassInfo,
+ IN PBOOL pfAdvert
+);
+
+DWORD
+NwpSetClassInfoAdvertise(
+ IN LPGUID lpClassType,
+ IN WORD wSapId
+);
+
+BOOL
+NSPpCheckCancel(
+ PVOID pvArg
+ );
+
+DWORD
+NSPpGotSap(
+ PSAP_BCAST_CONTROL psbc,
+ PSAP_IDENT_HEADER pSap,
+ PDWORD pdwErr
+ );
+
+INT WINAPI
+NSPLookupServiceBegin(
+ IN LPGUID lpProviderId,
+ IN LPWSAQUERYSETW lpqsRestrictions,
+ IN LPWSASERVICECLASSINFOW lpServiceClassInfo,
+ IN DWORD dwControlFlags,
+ OUT LPHANDLE lphLookup
+ );
+
+INT WINAPI
+NSPLookupServiceNext(
+ IN HANDLE hLookup,
+ IN DWORD dwControlFlags,
+ IN OUT LPDWORD lpdwBufferLength,
+ OUT LPWSAQUERYSETW lpqsResults
+ );
+
+INT WINAPI
+NSPUnInstallNameSpace(
+ IN LPGUID lpProviderId
+ );
+
+INT WINAPI
+NSPCleanup(
+ IN LPGUID lpProviderId
+ );
+
+INT WINAPI
+NSPLookupServiceEnd(
+ IN HANDLE hLookup
+ );
+
+INT WINAPI
+NSPSetService(
+ IN LPGUID lpProviderId,
+ IN LPWSASERVICECLASSINFOW lpServiceClassInfo,
+ IN LPWSAQUERYSETW lpqsRegInfo,
+ IN WSAESETSERVICEOP essOperation,
+ IN DWORD dwControlFlags
+ );
+
+INT WINAPI
+NSPInstallServiceClass(
+ IN LPGUID lpProviderId,
+ IN LPWSASERVICECLASSINFOW lpServiceClassInfo
+ );
+
+INT WINAPI
+NSPRemoveServiceClass(
+ IN LPGUID lpProviderId,
+ IN LPGUID lpServiceCallId
+ );
+
+INT WINAPI
+NSPGetServiceClassInfo(
+ IN LPGUID lpProviderId,
+ IN OUT LPDWORD lpdwBufSize,
+ IN OUT LPWSASERVICECLASSINFOW lpServiceClassInfo
+ );
+//-------------------------------------------------------------------//
+// //
+// Data definitions //
+// //
+//-------------------------------------------------------------------//
+
+NSP_ROUTINE nsrVector = {
+ sizeof(NSP_ROUTINE),
+ 1, // major version
+ 1, // minor version
+ NSPCleanup,
+ NSPLookupServiceBegin,
+ NSPLookupServiceNext,
+ NSPLookupServiceEnd,
+ NSPSetService,
+ NSPInstallServiceClass,
+ NSPRemoveServiceClass,
+ NSPGetServiceClassInfo
+ };
+
+static
+GUID HostnameGuid = SVCID_HOSTNAME;
+
+static
+GUID InetHostname = SVCID_INET_HOSTADDRBYNAME;
+
+static
+GUID AddressGuid = SVCID_INET_HOSTADDRBYINETSTRING;
+
+static
+GUID IANAGuid = SVCID_INET_SERVICEBYNAME;
+
+#define GuidEqual(x,y) RtlEqualMemory(x, y, sizeof(GUID))
+
+
+//------------------------------------------------------------------//
+// //
+// Function Bodies //
+// //
+//------------------------------------------------------------------//
+
+DWORD
+NwpSetClassInfoAdvertise(
+ IN LPGUID lpClassType,
+ IN WORD wSapId
+)
+{
+/*++
+Routine Description:
+ Start a class info advertise. Called upon a SetService call
+--*/
+ PWCHAR pwszUuid;
+ SOCKADDR_IPX sock;
+
+ UuidToString(lpClassType, &pwszUuid);
+
+ memset(&sock, 0, sizeof(sock));
+
+ *(PWORD)&sock.sa_netnum = htons(wSapId); // encode the ID here
+
+ return(pSapSetService2( oldRnRServiceRegister,
+ pwszUuid,
+ (PBYTE)&sock,
+ lpClassType,
+ RNRCLASSSAPTYPE));
+}
+
+DWORD
+pRnRReturnString(
+ IN PWCHAR pwszSrc,
+ IN OUT PBYTE *ppData,
+ IN OUT PLONG plBytes
+ )
+{
+/*++
+Routine Description:
+ Utility routine to pack a string into the spare buffer space,
+ update pointers and counts. If the string fits, it is copied. If
+ not, the pointer and count are still updated so the caller can
+ compute the additional space needed
+--*/
+
+ PBYTE pSave = *ppData;
+ LONG lLen = (wcslen(pwszSrc) + 1) * sizeof(WCHAR);
+
+ *ppData = pSave + lLen; // update the buffer pointer
+
+ *plBytes = *plBytes - lLen; // and the count
+
+ if(*plBytes >= 0)
+ {
+ //
+ // it fits.
+ //
+
+ RtlMoveMemory(pSave,
+ pwszSrc,
+ lLen);
+ return(NO_ERROR);
+ }
+ return(WSAEFAULT);
+}
+
+DWORD
+pLocateSapIdAndProtocls(
+ IN LPGUID lpClassInfoType,
+ IN DWORD dwNumClassInfo,
+ IN LPWSANSCLASSINFOW lpClassInfoBuf,
+ OUT PWORD pwSapId,
+ OUT PDWORD pdwnProt
+ )
+/*++
+Routine Description:
+ Locate the SAP ID entry and return it. Also return
+ the protocols support.
+--*/
+{
+ DWORD err = NO_ERROR;
+
+ if(GuidEqual(lpClassInfoType, &HostnameGuid)
+ ||
+ GuidEqual(lpClassInfoType, &InetHostname) )
+ {
+ *pwSapId = 0x4;
+ }
+ else if(IS_SVCID_NETWARE(lpClassInfoType))
+ {
+ *pwSapId = SAPID_FROM_SVCID_NETWARE(lpClassInfoType);
+ }
+ else
+ {
+ for(; dwNumClassInfo; dwNumClassInfo--, lpClassInfoBuf++)
+ {
+ //
+ // We have some class info data. Rummage through it
+ // looking for what we want
+ //
+
+ if(!_wcsicmp(SERVICE_TYPE_VALUE_SAPIDW, lpClassInfoBuf->lpszName)
+ &&
+ (lpClassInfoBuf->dwValueType == REG_DWORD)
+ &&
+ (lpClassInfoBuf->dwValueSize >= sizeof(WORD)))
+ {
+ //
+ // got it
+ //
+
+ *pwSapId = *(PWORD)lpClassInfoBuf->lpValue;
+ break;
+
+ }
+ }
+ if(!dwNumClassInfo)
+ {
+ err = WSA_INVALID_PARAMETER;
+ }
+ }
+ *pdwnProt = SPX_BIT | SPXII_BIT | IPX_BIT;
+ return(err);
+}
+DWORD
+pRnRReturnResults(
+ IN PWCHAR pwszString,
+ IN LPGUID pgdServiceClass,
+ IN DWORD dwVersion,
+ IN OUT PBYTE *ppData,
+ IN OUT PLONG plBytes,
+ IN PBYTE lpSockAddr,
+ IN DWORD nProt,
+ IN DWORD dwControlFlags,
+ OUT LPWSAQUERYSETW lpqsResults
+ )
+{
+/*++
+Routine Description:
+ Return the requested results
+--*/
+ DWORD err;
+
+ lpqsResults->dwNameSpace = NS_SAP;
+
+ if(dwControlFlags & LUP_RETURN_TYPE)
+ {
+
+ lpqsResults->lpServiceClassId = (LPGUID)*ppData;
+ *ppData += sizeof(GUID);
+ *plBytes -= sizeof(GUID);
+ if(*plBytes >= 0)
+ {
+ *lpqsResults->lpServiceClassId = *pgdServiceClass;
+ }
+ }
+
+ if(dwVersion
+ &&
+ (dwControlFlags & LUP_RETURN_VERSION) )
+ {
+ //
+ // have a verion, and the caller wants it
+ //
+
+ lpqsResults->lpVersion = (LPWSAVERSION)*ppData;
+ *ppData += sizeof(WSAVERSION);
+ *plBytes -= sizeof(WSAVERSION);
+ if(*plBytes >= 0)
+ {
+ //
+ // and it fits. So return it
+ //
+ lpqsResults->lpVersion->dwVersion = dwVersion;
+ lpqsResults->lpVersion->ecHow = COMP_EQUAL;
+ }
+ }
+
+ if(dwControlFlags & LUP_RETURN_ADDR)
+ {
+ DWORD dwCsAddrLen;
+
+ if(*plBytes >= 0)
+ {
+ dwCsAddrLen = (DWORD)*plBytes; // all of it for now
+ }
+ else
+ {
+ dwCsAddrLen = 0;
+ }
+ lpqsResults->lpcsaBuffer = (PVOID)*ppData;
+
+ err = FillBufferWithCsAddr(
+ lpSockAddr,
+ nProt,
+ (PVOID)lpqsResults->lpcsaBuffer,
+ &dwCsAddrLen,
+ &lpqsResults->dwNumberOfCsAddrs);
+
+ //
+ // see if it fit. Whether it did or not, compute the space available,
+ // align, and do the rest
+ //
+
+
+ if(err == NO_ERROR)
+ {
+ //
+ // if it worked, we have to compute the space taken
+ //
+
+ dwCsAddrLen = lpqsResults->dwNumberOfCsAddrs * (sizeof(CSADDR_INFO) +
+ 2*sizeof(SOCKADDR_IPX));
+ }
+ else if(err == ERROR_INSUFFICIENT_BUFFER)
+ {
+ err = WSAEFAULT;
+ }
+
+ *plBytes = *plBytes - dwCsAddrLen;
+
+ *ppData = *ppData + dwCsAddrLen;
+ }
+ else
+ {
+ err = NO_ERROR;
+ }
+
+ //
+ // no padding needed.
+
+ if((dwControlFlags & LUP_RETURN_NAME))
+ {
+ lpqsResults->lpszServiceInstanceName = (LPWSTR)*ppData;
+ err = pRnRReturnString(
+ pwszString,
+ ppData,
+ plBytes);
+ }
+ if(pgdServiceClass)
+ {
+ //
+ // BUGBUG. Do we really return this?
+ //
+ }
+ return(err);
+}
+
+INT WINAPI
+NSPLookupServiceBegin(
+ IN LPGUID lpProviderId,
+ IN LPWSAQUERYSETW lpqsRestrictions,
+ IN LPWSASERVICECLASSINFOW lpServiceClassInfo,
+ IN DWORD dwControlFlags,
+ OUT LPHANDLE lphLookup
+ )
+/*++
+Routine Description:
+ This is the RnR routine that begins a lookup.
+--*/
+{
+ PSAP_RNR_CONTEXT psrc;
+ int err;
+ DWORD nProt, nProt1;
+ OEM_STRING OemServiceName;
+ LPWSTR pwszContext;
+ WORD wSapid;
+ DWORD dwNumClassInfo;
+ LPWSANSCLASSINFOW lpClassInfoBuf;
+
+ if(lpqsRestrictions->dwSize < sizeof(WSAQUERYSETW))
+ {
+ SetLastError(WSAEFAULT);
+ return(SOCKET_ERROR);
+ }
+
+ if(lpqsRestrictions->lpszContext
+ &&
+ (lpqsRestrictions->lpszContext[0] != 0)
+ &&
+ wcscmp(&lpqsRestrictions->lpszContext[0], L"\\") )
+ {
+ //
+ // if not the default context, we must copy it.
+ //
+ pwszContext = lpqsRestrictions->lpszContext;
+ }
+ else
+ {
+ //
+ // map all default contexts into "no context".
+ //
+ pwszContext = 0;
+ }
+
+ //
+ // Compute protocols to return, or return them all
+ //
+ if(lpqsRestrictions->lpafpProtocols)
+ {
+ //
+ // Make certain at least one IPX/SPX protocol is being requested
+ //
+
+ INT i;
+ DWORD dwNum = lpqsRestrictions->dwNumberOfProtocols;
+
+ nProt = 0;
+
+ for(i = 0; dwNum--;)
+ {
+
+ if((lpqsRestrictions->lpafpProtocols[i].iAddressFamily == AF_IPX)
+ ||
+ (lpqsRestrictions->lpafpProtocols[i].iAddressFamily == AF_UNSPEC)
+ )
+ {
+ switch(lpqsRestrictions->lpafpProtocols[i].iProtocol)
+ {
+ case NSPROTO_IPX:
+ nProt |= IPX_BIT;
+ break;
+ case NSPROTO_SPX:
+ nProt |= SPX_BIT;
+ break;
+ case NSPROTO_SPXII:
+ nProt |= SPXII_BIT;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ if(!nProt)
+ {
+ //
+ // if the caller doesn't want IPX/SPX addresses, why bother?
+ //
+ SetLastError(WSANO_DATA);
+ return(SOCKET_ERROR);
+ }
+ }
+ else
+ {
+ nProt = IPX_BIT | SPX_BIT | SPXII_BIT;
+ }
+
+ if(dwControlFlags & LUP_CONTAINERS)
+ {
+ if(pwszContext)
+ {
+BadGuid:
+ SetLastError(WSANO_DATA);
+ return(SOCKET_ERROR); // can't handle containers in containers
+ }
+ wSapid = 0x4;
+ nProt1 = IPX_BIT;
+ err = NO_ERROR;
+ }
+ else
+ {
+
+ LPGUID pgClass = lpqsRestrictions->lpServiceClassId;
+
+ if(!pgClass
+ ||
+ GuidEqual(pgClass, &AddressGuid)
+ ||
+ GuidEqual(pgClass, &IANAGuid) )
+ {
+ goto BadGuid;
+ }
+
+ if(!lpqsRestrictions->lpszServiceInstanceName
+ ||
+ !*lpqsRestrictions->lpszServiceInstanceName)
+ {
+ SetLastError(WSA_INVALID_PARAMETER);
+ return(SOCKET_ERROR);
+ }
+ if(!lpServiceClassInfo)
+ {
+ dwNumClassInfo = 0;
+ }
+ else
+ {
+ dwNumClassInfo = lpServiceClassInfo->dwCount;
+ lpClassInfoBuf = lpServiceClassInfo->lpClassInfos;
+ }
+
+ err = pLocateSapIdAndProtocls(pgClass,
+ dwNumClassInfo,
+ lpClassInfoBuf,
+ &wSapid,
+ &nProt1);
+ if(err)
+ {
+ if(dwNumClassInfo)
+ {
+ SetLastError(err);
+ return(SOCKET_ERROR);
+ }
+ else
+ {
+ nProt1 = nProt;
+ wSapid = 0;
+ err = 0;
+ }
+ }
+ }
+
+ nProt &= nProt1; // the relevant protocols
+
+ if(!nProt)
+ {
+ SetLastError(WSANO_DATA);
+ return(SOCKET_ERROR);
+ }
+
+ //
+ // Make sure a class ID is given since we copy it
+ //
+
+ if(!lpqsRestrictions->lpServiceClassId)
+ {
+ //
+ // not. So, fail
+ //
+ SetLastError(WSA_INVALID_PARAMETER);
+ return(SOCKET_ERROR);
+ }
+
+ //
+ // It looks like a query we can handle. Make a context
+ // BUGBUG. What about version????
+ //
+
+ psrc = SapMakeContext(0,
+ sizeof(WSAVERSION) - sizeof(PVOID));
+
+ if(!psrc)
+ {
+ SetLastError(WSA_NOT_ENOUGH_MEMORY);
+ return(SOCKET_ERROR);
+ }
+
+ //
+ // save things
+ //
+
+ psrc->gdType = *lpqsRestrictions->lpServiceClassId;
+ psrc->dwControlFlags = dwControlFlags; // save for Next processing
+ psrc->wSapId = wSapid;
+
+ if(pwszContext)
+ {
+ wcscpy(psrc->wszContext, pwszContext);
+ }
+
+ //
+ // save the relevant restrictions
+ // if the name is a wild-card, don't copy it. A NULL name
+ // serves as a wild-card to NSPLookupServiceNext
+ //
+
+ if(lpqsRestrictions->lpszServiceInstanceName
+ &&
+ *lpqsRestrictions->lpszServiceInstanceName
+ &&
+ wcscmp(lpqsRestrictions->lpszServiceInstanceName, L"*"))
+ {
+ DWORD dwLen = wcslen(lpqsRestrictions->lpszServiceInstanceName);
+ if(dwLen > 48)
+ {
+ err = WSA_INVALID_PARAMETER;
+ goto Done;
+ }
+ else
+ {
+ RtlMoveMemory(psrc->chwName,
+ lpqsRestrictions->lpszServiceInstanceName,
+ dwLen * sizeof(WCHAR));
+ _wcsupr(psrc->chwName);
+ }
+ }
+
+ psrc->fConnectionOriented = (DWORD) -1;
+
+ *lphLookup = (HANDLE)psrc;
+ psrc->nProt = nProt;
+ psrc->gdProvider = *lpProviderId;
+ if(lpqsRestrictions->lpVersion)
+ {
+ *(LPWSAVERSION)&psrc->pvVersion = *lpqsRestrictions->lpVersion;
+ }
+
+Done:
+ SapReleaseContext(psrc);
+ if(err != NO_ERROR)
+ {
+ SapReleaseContext(psrc);
+ SetLastError(err);
+ err = SOCKET_ERROR;
+ }
+ return(err);
+}
+
+INT WINAPI
+NSPLookupServiceNext(
+ IN HANDLE hLookup,
+ IN DWORD dwControlFlags,
+ IN OUT LPDWORD lpdwBufferLength,
+ OUT LPWSAQUERYSETW lpqsResults
+ )
+/*++
+Routine Description:
+ The continuation of the LookupServiceBegin. Called to fetch
+ a matching item.
+Arguments:
+ See RnR spec
+--*/
+{
+ DWORD err = NO_ERROR;
+ PSAP_RNR_CONTEXT psrc;
+ SOCKADDR_IPX SockAddr;
+ WCHAR OutName[48];
+ PBYTE pData = (PBYTE)(lpqsResults + 1);
+ LONG lSpare;
+ LONG lLastIndex;
+ DWORD dwVersion;
+ WSAQUERYSETW wsaqDummy;
+ BOOL fDoStateMachine;
+
+ if(*lpdwBufferLength < sizeof(WSAQUERYSETW))
+ {
+ lpqsResults = &wsaqDummy;
+ }
+ lSpare = (LONG)*lpdwBufferLength - sizeof(WSAQUERYSETW);
+
+ memset(lpqsResults, 0, sizeof(WSAQUERYSETW));
+ lpqsResults->dwNameSpace = NS_SAP;
+ lpqsResults->dwSize = sizeof(WSAQUERYSETW);
+ psrc = SapGetContext(hLookup);
+ if(!psrc)
+ {
+
+ SetLastError(WSA_INVALID_HANDLE);
+ return(SOCKET_ERROR);
+ }
+
+ //
+ // This is a valid context. Determine whether this is the first
+ // call to this. If so, we need to determine whether to
+ // get the information from the bindery or by using SAP.
+ //
+
+ //
+ // make sure we have the class info info
+ //
+
+ if(!psrc->wSapId)
+ {
+ //
+ // Need to get it
+ //
+
+ UCHAR Buffer[1000];
+ LPWSASERVICECLASSINFOW lpcli = (LPWSASERVICECLASSINFOW)Buffer;
+ DWORD dwBufSize;
+ DWORD nProt1;
+
+ dwBufSize = 1000;
+ lpcli->lpServiceClassId = &psrc->gdType;
+
+ if( (err = NSPGetServiceClassInfo(&psrc->gdProvider,
+ &dwBufSize,
+ lpcli)) != NO_ERROR)
+ {
+ goto DoneNext;
+ }
+
+ err = pLocateSapIdAndProtocls(&psrc->gdType,
+ lpcli->dwCount,
+ lpcli->lpClassInfos,
+ &psrc->wSapId,
+ &nProt1);
+ if(err)
+ {
+ SetLastError(err);
+ goto DoneNext;
+ }
+
+ psrc->nProt &= nProt1;
+ if(!psrc->nProt)
+ {
+ //
+ // no protocols match
+ //
+
+ err = WSANO_DATA;
+ goto DoneNext;
+ }
+ }
+
+ //
+ // this is the state machine for querying. It selects the bindery or
+ // SAP as appropriate.
+ //
+
+
+ fDoStateMachine = TRUE; // enter the machine
+
+ do
+ {
+ //
+ // switch on the current machine state.
+ //
+ switch(psrc->dwUnionType)
+ {
+
+ case LOOKUP_TYPE_NIL:
+ psrc->u_type.bc.lIndex = -1;
+ psrc->dwUnionType = LOOKUP_TYPE_BINDERY;
+ break; // reenter state machine
+
+ case LOOKUP_TYPE_BINDERY:
+
+ //
+ // try the bindery
+ //
+
+
+ if(psrc->dwControlFlags & LUP_NEAREST)
+ {
+ err = NO_DATA; // skip the bindery
+ }
+ else
+ {
+ //
+ // otherwise, try the bindery
+ //
+
+
+ EnterCriticalSection(&psrc->u_type.sbc.csMonitor);
+
+ lLastIndex = psrc->u_type.bc.lIndex; // save it
+
+
+ err = NwpGetRnRAddress(
+ &psrc->hServer,
+ (psrc->wszContext[0] ?
+ &psrc->wszContext[0] :
+ 0),
+ &psrc->u_type.bc.lIndex,
+ (psrc->chwName[0] ?
+ psrc->chwName :
+ 0),
+ psrc->wSapId,
+ &dwVersion,
+ 48 * sizeof(WCHAR),
+ OutName,
+ &SockAddr);
+
+ LeaveCriticalSection(&psrc->u_type.sbc.csMonitor);
+ }
+
+ if(err != NO_ERROR)
+ {
+
+ if((psrc->u_type.bc.lIndex == -1))
+ {
+ err = PrepareForSap(psrc);
+ if(err)
+ {
+ //
+ // if we can't, exit the state machine
+ //
+ fDoStateMachine = FALSE;
+ }
+ }
+ else
+ {
+ //
+ // no more bindery entries. We will leave the state machine
+ //
+
+ if(err == ERROR_NO_MORE_ITEMS)
+ {
+ err = WSA_E_NO_MORE;
+ }
+ fDoStateMachine = FALSE;
+ }
+ break;
+ }
+ else
+ {
+ LPWSAVERSION lpVersion = (LPWSAVERSION)&psrc->pvVersion;
+
+ if(lpVersion->dwVersion && dwVersion)
+ {
+ //
+ // need to checkout for version matching
+ //
+
+ switch(lpVersion->ecHow)
+ {
+ case COMP_EQUAL:
+ if(lpVersion->dwVersion != dwVersion)
+ {
+ continue; //reenter machine
+ }
+ break;
+
+ case COMP_NOTLESS:
+ if(lpVersion->dwVersion > dwVersion)
+ {
+ continue;
+ }
+ break;
+
+ default:
+ continue; // error. If we don't
+ // know how to compare, we
+ // must reject it.
+ }
+ }
+
+ //
+ // have a suitable item.
+ // return the name and type and all
+ // that
+
+ err = pRnRReturnResults(
+ OutName,
+ &psrc->gdType,
+ dwVersion,
+ &pData,
+ &lSpare,
+ (PBYTE)SockAddr.sa_netnum,
+ psrc->nProt,
+ psrc->dwControlFlags,
+ lpqsResults);
+
+ if(err == WSAEFAULT)
+ {
+ //
+ // no room. Return buffer size required and
+ // restore the index
+ //
+
+ *lpdwBufferLength =
+ (DWORD)((LONG)*lpdwBufferLength - lSpare);
+ psrc->u_type.bc.lIndex = lLastIndex;
+
+ }
+ fDoStateMachine = FALSE;
+ }
+ break;
+
+ case LOOKUP_TYPE_SAP:
+
+ //
+ // Use SAP.
+ //
+
+ {
+ WORD QueryType;
+
+ if(psrc->dwControlFlags & LUP_NEAREST)
+ {
+ QueryType = QT_NEAREST_QUERY;
+ }
+ else
+ {
+ QueryType = QT_GENERAL_QUERY;
+ }
+
+ err = DoASap(psrc,
+ QueryType,
+ &pData,
+ lpqsResults,
+ &lSpare,
+ lpdwBufferLength);
+
+ if((err == WSA_E_NO_MORE)
+ &&
+ !(psrc->fFlags & SAP_F_END_CALLED)
+ &&
+ (QueryType == QT_NEAREST_QUERY)
+ &&
+ (psrc->dwControlFlags & LUP_DEEP)
+ &&
+ !psrc->u_type.sbc.psdHead)
+ {
+ //
+ // didn't find it locally. Turn off LUP_NEAREST
+ // and do this as a general query. This might bring
+ // it back to a SAP query, but this time
+ // without LUP_NEAREST. But starting anew
+ // allows the use of the bindery and that
+ // might find things quickly.
+ //
+ psrc->dwControlFlags &= ~LUP_NEAREST;
+ psrc->dwUnionType = LOOKUP_TYPE_NIL;
+ if(psrc->u_type.sbc.s)
+ {
+ SapFreeSapSocket(psrc->u_type.sbc.s);
+ psrc->u_type.sbc.s = 0;
+ }
+
+ }
+ else
+ {
+ fDoStateMachine = FALSE;
+ }
+ break;
+ }
+ } // switch
+ } while(fDoStateMachine);
+
+DoneNext:
+ SapReleaseContext(psrc);
+ if((err != NO_ERROR)
+ &&
+ (err != (DWORD)SOCKET_ERROR))
+ {
+ SetLastError(err);
+ err = (DWORD)SOCKET_ERROR;
+ }
+ return((INT)err);
+}
+
+BOOL
+NSPpCheckCancel(
+ PVOID pvArg
+ )
+/*++
+Routine Description:
+ Coroutine called to check if the SAP lookup has been cancelled.
+ For now, this always returns FALSE as we use the flags word in
+ the control block instead
+--*/
+{
+ return(FALSE);
+}
+
+
+DWORD
+NSPpGotSap(
+ PSAP_BCAST_CONTROL psbc,
+ PSAP_IDENT_HEADER pSap,
+ PDWORD pdwErr
+ )
+/*++
+Routine Description:
+ Coroutine called for each SAP reply received. This decides
+ whether to keep the data or not and returns a code telling
+ the SAP engine whether to proceed
+
+Arguments:
+ psbc -- the SAP_BCAST_CONTROL
+ pSap -- the SAP reply
+ pdwErr -- where to put an error code
+--*/
+{
+ PSAP_DATA psdData;
+ LPWSAQUERYSETW Results = (LPWSAQUERYSETW)psbc->pvArg;
+ PSAP_RNR_CONTEXT psrc = psbc->psrc;
+ DWORD dwRet = dwrcNil;
+ PCHAR pServiceName = (PCHAR)psrc->chName;
+
+ EnterCriticalSection(&psbc->csMonitor);
+
+ //
+ // First, check if this is a lookup for a particular name. If so,
+ // accept only the name.
+ //
+
+ if(*pServiceName)
+ {
+ if(strcmp(pServiceName, pSap->ServerName))
+ {
+ goto nota;
+ }
+ if(!(psrc->dwControlFlags & LUP_NEAREST))
+ {
+ dwRet = dwrcDone;
+ psbc->fFlags |= SBC_FLAG_NOMORE;
+ }
+ }
+
+ //
+ // see if we want to keep this guy
+ // We keep it if we don't already have it in the list
+ //
+
+
+ for(psdData = psbc->psdHead;
+ psdData;
+ psdData = psdData->sapNext)
+ {
+ if(RtlEqualMemory( psdData->socketAddr,
+ &pSap->Address,
+ IPX_ADDRESS_LENGTH))
+ {
+ goto nota; // we have it already
+ }
+ }
+
+ psdData = (PSAP_DATA)LocalAlloc(LPTR, sizeof(SAP_DATA));
+ if(!psdData)
+ {
+ goto nota; // can't save it
+ }
+
+ psdData->sapid = pSap->ServerType;
+ RtlMoveMemory(psdData->sapname,
+ pSap->ServerName,
+ 48);
+ RtlMoveMemory(psdData->socketAddr,
+ &pSap->Address,
+ IPX_ADDRESS_LENGTH);
+
+ if(psbc->psdTail)
+ {
+ psbc->psdTail->sapNext = psdData;
+ }
+ else
+ {
+ psbc->psdHead = psdData;
+ }
+ psbc->psdTail = psdData;
+ if(!psbc->psdNext1)
+ {
+ psbc->psdNext1 = psdData;
+ }
+
+nota:
+
+ LeaveCriticalSection(&psbc->csMonitor);
+
+ if((dwRet == dwrcNil)
+ &&
+ psbc->psdNext1)
+ {
+ dwRet = dwrcNoWait;
+ }
+ return(dwRet);
+}
+
+INT WINAPI
+NSPUnInstallNameSpace(
+ IN LPGUID lpProviderId
+ )
+{
+ return(NO_ERROR);
+}
+
+INT WINAPI
+NSPCleanup(
+ IN LPGUID lpProviderId
+ )
+{
+ //
+ // Make sure all contexts are released
+ //
+
+// pFreeAllContexts(); // done in Dll Process detach
+ return(NO_ERROR);
+}
+
+INT WINAPI
+NSPLookupServiceEnd(
+ IN HANDLE hLookup
+ )
+{
+ PSAP_RNR_CONTEXT psrc;
+
+ psrc = SapGetContext(hLookup);
+ if(!psrc)
+ {
+
+ SetLastError(WSA_INVALID_HANDLE);
+ return(SOCKET_ERROR);
+ }
+
+ psrc->fFlags |= SAP_F_END_CALLED;
+ SapReleaseContext(psrc); // get rid of it
+ SapReleaseContext(psrc); // and close it. Context cleanup is
+ // done on the last derefernce.
+ return(NO_ERROR);
+}
+
+INT WINAPI
+NSPSetService(
+ IN LPGUID lpProviderId,
+ IN LPWSASERVICECLASSINFOW lpServiceClassInfo,
+ IN LPWSAQUERYSETW lpqsRegInfo,
+ IN WSAESETSERVICEOP essOperation,
+ IN DWORD dwControlFlags
+ )
+{
+/*++
+Routine Description:
+ The routine that implements the RnR SetService routine. Note that
+ context is ignored. There is no way to direct the registration to
+ a particular server.
+--*/
+ PBYTE pbAddress;
+ DWORD dwOperation;
+ PBYTE pbSocket;
+ DWORD dwAddrs;
+ DWORD err = NO_ERROR;
+ WORD wSapId;
+ DWORD nProt, dwAddress;
+ BOOL fAdvert = FALSE;
+ DWORD dwNumClassInfo;
+ LPWSANSCLASSINFOW lpClassInfoBuf;
+
+
+ //
+ // Verify all args present
+ //
+
+ if(!lpqsRegInfo->lpszServiceInstanceName
+ ||
+ !lpqsRegInfo->lpServiceClassId)
+ {
+ SetLastError(WSA_INVALID_PARAMETER);
+ return(SOCKET_ERROR);
+ }
+
+ if(!lpServiceClassInfo)
+ {
+ UCHAR Buffer[1000];
+ LPWSASERVICECLASSINFOW lpcli = (LPWSASERVICECLASSINFOW)Buffer;
+ DWORD dwBufSize;
+
+ dwBufSize = 1000;
+ lpcli->lpServiceClassId = lpqsRegInfo->lpServiceClassId;
+
+ if(pGetServiceClassInfo(lpProviderId,
+ &dwBufSize,
+ lpcli,
+ &fAdvert) != NO_ERROR)
+ {
+ return(SOCKET_ERROR);
+ }
+ dwNumClassInfo = lpcli->dwCount;
+ lpClassInfoBuf = lpcli->lpClassInfos;
+ }
+ else
+ {
+ dwNumClassInfo = lpServiceClassInfo->dwCount;
+ lpClassInfoBuf = lpServiceClassInfo->lpClassInfos;
+ }
+
+ //
+ // Find the IPX address in the input args
+ //
+
+ err = pLocateSapIdAndProtocls(lpqsRegInfo->lpServiceClassId,
+ dwNumClassInfo,
+ lpClassInfoBuf,
+ &wSapId,
+ &nProt);
+
+ if(err == NO_ERROR)
+ {
+ if(essOperation == RNRSERVICE_REGISTER)
+ {
+ PCSADDR_INFO pcsaAddress;
+
+ pcsaAddress = lpqsRegInfo->lpcsaBuffer;
+
+ try
+ {
+ for(dwAddrs = lpqsRegInfo->dwNumberOfCsAddrs;
+ dwAddrs;
+ dwAddrs--, pcsaAddress++)
+ {
+ if(pcsaAddress->LocalAddr.lpSockaddr->sa_family == AF_IPX)
+ {
+ pbSocket =
+ (PBYTE)pcsaAddress->LocalAddr.lpSockaddr;
+ break;
+ }
+ }
+ }
+ except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ err = GetExceptionCode();
+ }
+
+ if(err || !dwAddrs)
+ {
+ err = ERROR_INCORRECT_ADDRESS;
+ }
+ else if(fAdvert)
+ {
+ NwpSetClassInfoAdvertise(lpqsRegInfo->lpServiceClassId,
+ wSapId);
+ }
+ }
+ else
+ {
+ pbSocket = 0;
+ }
+ if(err == NO_ERROR)
+ {
+ //
+ // map the operation, and call the common worker
+ //
+
+ switch(essOperation)
+ {
+ case RNRSERVICE_REGISTER:
+ dwOperation = oldRnRServiceRegister;
+ break;
+ case RNRSERVICE_DEREGISTER:
+ case RNRSERVICE_DELETE:
+ dwOperation = oldRnRServiceDeRegister;
+ break;
+ default:
+ err = WSA_INVALID_PARAMETER;
+ break;
+ }
+ if(err == NO_ERROR)
+ {
+ err = pSapSetService2(
+ dwOperation,
+ lpqsRegInfo->lpszServiceInstanceName,
+ pbSocket,
+ lpqsRegInfo->lpServiceClassId,
+ wSapId);
+
+ }
+ }
+ }
+ if(err != NO_ERROR)
+ {
+ SetLastError(err);
+ err = (DWORD)SOCKET_ERROR;
+ }
+ return(err);
+}
+
+INT WINAPI
+NSPInstallServiceClass(
+ IN LPGUID lpProviderId,
+ IN LPWSASERVICECLASSINFOW lpServiceClassInfo
+ )
+{
+ LPWSANSCLASSINFOW pcli, pcli1 = 0;
+ DWORD dwIndex = lpServiceClassInfo->dwCount;
+ CHAR PropertyBuffer[128];
+ PBINDERYCLASSES pbc = (PBINDERYCLASSES)PropertyBuffer;
+ BYTE bData = (BYTE)&((PBINDERYCLASSES)0)->cDataArea[0];
+ PCHAR pszData = &pbc->cDataArea[0];
+ DWORD err;
+ WORD port = 0, sap;
+
+ //
+ // Find the SapId entry
+ //
+
+ for(pcli = lpServiceClassInfo->lpClassInfos;
+ dwIndex;
+ pcli++, dwIndex--)
+ {
+ WORD wTemp;
+
+ if(!_wcsicmp(pcli->lpszName, SERVICE_TYPE_VALUE_IPXPORTW)
+ &&
+ (pcli->dwValueSize == sizeof(WORD)))
+ {
+ //
+ // the value may not be aligned
+ //
+ ((PBYTE)&wTemp)[0] = ((PBYTE)pcli->lpValue)[0];
+ ((PBYTE)&wTemp)[1] = ((PBYTE)pcli->lpValue)[1];
+ port = wTemp;
+ } else if(!_wcsicmp(pcli->lpszName, SERVICE_TYPE_VALUE_SAPIDW)
+ &&
+ (pcli->dwValueSize >= sizeof(WORD)))
+ {
+ ((PBYTE)&wTemp)[0] = ((PBYTE)pcli->lpValue)[0];
+ ((PBYTE)&wTemp)[1] = ((PBYTE)pcli->lpValue)[1];
+ sap = wTemp;
+ pcli1 = pcli;
+ }
+ }
+
+ if(!(pcli = pcli1))
+ {
+ SetLastError(WSA_INVALID_PARAMETER);
+ return(SOCKET_ERROR);
+ }
+
+#if 0 // old way of doing this
+ //
+ // Found it. Build the property segment
+ //
+
+ memset(PropertyBuffer, 0, 128); // clear out everyting
+
+ pbc->bOffset = bData;
+ pbc->bSizeOfString = sizeof("Sapid");
+
+ pbc->bType = BT_WORD; // just a word, I assure you
+ pbc->bSizeOfType = 2;
+ pbc->wNameSpace = (WORD)NS_SAP; // it's us
+ *(PWORD)pszData = htons(*(PWORD)pcli->lpValue);
+ pszData += sizeof(WORD); // where the string goes
+ strcpy(pszData, "SapId");
+// pbc->bFlags = (BYTE)pcli->dwConnectionFlags;
+
+ err = NwpSetClassInfo(
+ lpServiceClassInfo->lpszServiceClassName,
+ lpServiceClassInfo->lpServiceClassId,
+ PropertyBuffer);
+#else
+ err = NwpRnR2AddServiceType(
+ lpServiceClassInfo->lpszServiceClassName,
+ lpServiceClassInfo->lpServiceClassId,
+ sap,
+ port);
+#endif
+ if(err != NO_ERROR)
+ {
+ SetLastError(err);
+ err = (DWORD)SOCKET_ERROR;
+ }
+
+ return(err);
+}
+
+INT WINAPI
+NSPRemoveServiceClass(
+ IN LPGUID lpProviderId,
+ IN LPGUID lpServiceCallId
+ )
+{
+ return(NO_ERROR);
+}
+
+INT WINAPI
+NSPGetServiceClassInfo(
+ IN LPGUID lpProviderId,
+ IN OUT LPDWORD lpdwBufSize,
+ IN OUT LPWSASERVICECLASSINFOW lpServiceClassInfo
+ )
+{
+/*++
+Routine Description:
+ Get the ClassInfo for this type. Class info data may be in the
+ registry, or available via SAP or the bindery. We try all three
+ as appropriate
+--*/
+ BOOL fAdvert;
+
+ return(pGetServiceClassInfo(
+ lpProviderId,
+ lpdwBufSize,
+ lpServiceClassInfo,
+ &fAdvert));
+}
+
+INT WINAPI
+pGetServiceClassInfo(
+ IN LPGUID lpProviderId,
+ IN OUT LPDWORD lpdwBufSize,
+ IN OUT LPWSASERVICECLASSINFOW lpServiceClassInfo,
+ IN PBOOL pfAdvert
+ )
+{
+/*++
+Routine Description:
+ Get the ClassInfo for this type. Class info data may be in the
+ registry, or available via SAP or the bindery. We try all three
+ as appropriate
+--*/
+ DWORD err;
+ LONG lInSize;
+ LONG lSizeNeeded;
+ PBYTE pbBuffer;
+ GUID gdDummy;
+ PWCHAR pwszUuid;
+ LPGUID pType;
+ WORD wSapId;
+ WORD wPort;
+ SOCKADDR_IPX sock;
+ PWCHAR pwszSaveName = lpServiceClassInfo->lpszServiceClassName;
+
+#define SIZENEEDED (sizeof(WSASERVICECLASSINFO) + \
+ sizeof(WSANSCLASSINFO) + \
+ sizeof(WSANSCLASSINFO) + \
+ 10 + 2 + \
+ sizeof(GUID) + 12 + 2)
+
+ *pfAdvert = FALSE;
+
+ lInSize = (LONG)*lpdwBufSize - SIZENEEDED;
+
+ pType = (LPGUID)(lpServiceClassInfo + 1);
+
+ pbBuffer = (PBYTE)(pType + 1);
+
+ if(lInSize < 0)
+ {
+ //
+ // it is too small already
+ //
+
+ pType = &gdDummy;
+ }
+
+ UuidToString(lpServiceClassInfo->lpServiceClassId, &pwszUuid);
+
+ //
+ // First, try the bindery
+ //
+
+ err = NwpGetAddressByName(0,
+ RNRCLASSSAPTYPE,
+ pwszUuid,
+ &sock);
+
+ if(err == NO_ERROR)
+ {
+ wSapId = ntohs(*(PWORD)&sock.sa_netnum);
+ wPort = ntohs(*(PWORD)&sock.sa_socket);
+ }
+ else
+ {
+ UCHAR Buffer[400];
+ DWORD dwLen = 400;
+ DWORD dwNum;
+ //
+ // try SAP
+ //
+
+ err = NwpGetAddressViaSap(RNRCLASSSAPTYPE,
+ pwszUuid,
+ IPX_BIT,
+ (PVOID)Buffer,
+ &dwLen,
+ 0,
+ &dwNum);
+ if((err == NO_ERROR)
+ &&
+ (dwNum > 0) )
+ {
+ PCSADDR_INFO psca = (PCSADDR_INFO)Buffer;
+ PSOCKADDR_IPX psock = (PSOCKADDR_IPX)psca->RemoteAddr.lpSockaddr;
+
+ wSapId = ntohs(*(PWORD)&psock->sa_netnum);
+ wPort = ntohs(*(PWORD)&sock.sa_socket);
+ }
+ else
+ {
+ //
+ // try the local bindery
+
+
+ if(!NwpLookupSapInRegistry(
+ lpServiceClassInfo->lpServiceClassId,
+ &wSapId,
+ &wPort,
+ NULL))
+ {
+ err = WSASERVICE_NOT_FOUND;
+ }
+ else
+ {
+ *pfAdvert = TRUE;
+ err = NO_ERROR;
+ }
+ }
+ }
+
+ RpcStringFree(&pwszUuid);
+
+ if(err != NO_ERROR)
+ {
+ SetLastError(err);
+ err = (DWORD)SOCKET_ERROR;
+ }
+ else
+ {
+ //
+ // we return the input structure and the found type. That's it.
+ // The space needed is a constant since we don't return the name
+ //
+
+ if(lInSize < 0)
+ {
+ SetLastError(WSAEFAULT);
+ *lpdwBufSize += (DWORD)(-lInSize);
+ err = (DWORD)SOCKET_ERROR;
+ }
+ else
+ {
+ LPWSANSCLASSINFOW pci = (LPWSANSCLASSINFOW)pbBuffer;
+ PUCHAR Buff;
+ //
+ // it will fit. SO let's go
+ //
+
+ if(wPort)
+ {
+ Buff = (PCHAR)(pci + 2);
+ }
+ else
+ {
+ Buff = (PCHAR)(pci + 1);
+ }
+
+ *pType = *lpServiceClassInfo->lpServiceClassId;
+ lpServiceClassInfo->lpServiceClassId = pType;
+ lpServiceClassInfo->lpszServiceClassName = 0; // not a
+ lpServiceClassInfo->dwCount = 1;
+ lpServiceClassInfo->lpClassInfos = pci;
+ pci->dwNameSpace = NS_SAP;
+ pci->dwValueType = REG_DWORD;
+ pci->dwValueSize = 2;
+ pci->lpszName = (LPWSTR)Buff;
+ wcscpy((PWCHAR)Buff, L"SapId");
+ Buff += 6 * sizeof(WCHAR);
+ pci->lpValue = (LPVOID)Buff;
+ *(PWORD)Buff = wSapId;
+ Buff += sizeof(WORD);
+ if(wPort)
+ {
+ lpServiceClassInfo->dwCount++;
+ pci++;
+ pci->dwNameSpace = NS_SAP;
+ pci->dwValueType = REG_DWORD;
+ pci->dwValueSize = 2;
+ pci->lpszName = (LPWSTR)Buff;
+ wcscpy((PWCHAR)Buff, L"Port");
+ Buff += 5 * sizeof(WCHAR);
+ pci->lpValue = (LPVOID)Buff;
+ *(PWORD)Buff = wPort;
+ }
+ }
+ }
+ return(err);
+}
+
+INT WINAPI
+NSPStartup(
+ IN LPGUID lpProviderId,
+ IN OUT LPNSP_ROUTINE lpsnpRoutines)
+{
+// DWORD dwSize = min(sizeof(nsrVector), lpsnpRoutines->cbSize);
+ DWORD dwSize = sizeof(nsrVector);
+ RtlCopyMemory(lpsnpRoutines,
+ &nsrVector,
+ dwSize);
+ return(NO_ERROR);
+}
+
+DWORD
+DoASap(
+ IN PSAP_RNR_CONTEXT psrc,
+ IN WORD QueryType,
+ IN PBYTE * ppData,
+ IN LPWSAQUERYSETW lpqsResults,
+ IN PLONG plSpare,
+ IN PDWORD lpdwBufferLength
+ )
+/*++
+ Small routine to construcst a SAP_BROADCAST pakcet and issue the SAP
+--*/
+{
+ DWORD err;
+
+ if(!psrc->u_type.sbc.s)
+ {
+ //
+ // this is the first time. We must init the
+ // structure
+ //
+ err = SapGetSapSocket(&psrc->u_type.sbc.s);
+ if(err)
+ {
+ psrc->u_type.sbc.s = 0; // make sure
+ return(err);
+ }
+ psrc->u_type.sbc.Func = NSPpGotSap;
+ psrc->u_type.sbc.fCheckCancel = NSPpCheckCancel;
+ psrc->u_type.sbc.dwIndex = 0; // just in case.
+ psrc->u_type.sbc.pvArg = (PVOID)lpqsResults;
+ psrc->u_type.sbc.psrc = psrc;
+ psrc->u_type.sbc.fFlags = 0;
+
+ }
+
+ psrc->u_type.sbc.wQueryType = QueryType;
+
+ if(!psrc->u_type.sbc.psdNext1
+ &&
+ !(psrc->u_type.sbc.fFlags & SBC_FLAG_NOMORE))
+ {
+ err = SapGetSapForType(&psrc->u_type.sbc, psrc->wSapId);
+ }
+
+ EnterCriticalSection(&psrc->u_type.sbc.csMonitor);
+ if(psrc->u_type.sbc.psdNext1)
+ {
+ //
+ // Got something to return. Let's do it
+ //
+
+
+ //
+ // Assume we have to return the name
+ //
+
+
+ //
+ // We have to convert the name to UNICODE so
+ // we can return it to the caller.
+ //
+ //
+
+ OEM_STRING Oem;
+ NTSTATUS status;
+ UNICODE_STRING UString;
+
+ RtlInitAnsiString(&Oem,
+ psrc->u_type.sbc.psdNext1->sapname);
+ status = RtlOemStringToUnicodeString(
+ &UString,
+ &Oem,
+ TRUE);
+ if(NT_SUCCESS(status))
+ {
+ if(psrc->wSapId == OT_DIRSERVER)
+ {
+ PWCHAR pwszTemp = &UString.Buffer[31];
+
+ while(*pwszTemp == L'_')
+ {
+ *pwszTemp-- = 0;
+ }
+ }
+ err = pRnRReturnResults(
+ UString.Buffer,
+ &psrc->gdType,
+ 0, // never a version
+ ppData,
+ plSpare,
+ psrc->u_type.sbc.psdNext1->socketAddr,
+ psrc->nProt,
+ psrc->dwControlFlags,
+ lpqsResults);
+ RtlFreeUnicodeString(&UString);
+ if(err == WSAEFAULT)
+ {
+ //
+ // no room. Return buffer size required
+ //
+
+ *lpdwBufferLength =
+ (DWORD)((LONG)*lpdwBufferLength - *plSpare);
+ }
+ }
+ else
+ {
+ err = (DWORD)status;
+ }
+ if(err == NO_ERROR)
+ {
+ //
+ // if we got it, step the item
+ //
+ psrc->u_type.sbc.psdNext1 =
+ psrc->u_type.sbc.psdNext1->sapNext;
+ }
+
+
+ }
+ else
+ {
+ err = (DWORD)WSA_E_NO_MORE;
+ }
+ LeaveCriticalSection(&psrc->u_type.sbc.csMonitor);
+ return(err);
+}
+
+
+DWORD
+PrepareForSap(
+ IN PSAP_RNR_CONTEXT psrc
+ )
+/*++
+Called when there is no bindery or the bindery does not have the
+entry. This initializes the values needed for a SAP search
+--*/
+{
+
+
+ OEM_STRING OemServiceName;
+ UNICODE_STRING UString;
+ NTSTATUS status;
+
+ //
+ // the bindery didn't work. Use SAP.
+ //
+
+
+ psrc->u_type.sbc.dwIndex =
+ psrc->u_type.sbc.dwTickCount = 0;
+ if(psrc->wszContext[0])
+ {
+ return(WSASERVICE_NOT_FOUND); // no contexts in SAP
+ }
+ RtlInitUnicodeString(&UString,
+ psrc->chwName);
+ status = RtlUnicodeStringToOemString(&OemServiceName,
+ &UString,
+ TRUE);
+ if(!NT_SUCCESS(status))
+ {
+ return( (DWORD)status);
+ }
+ strcpy((PCHAR)&psrc->chName,
+ OemServiceName.Buffer);
+ RtlFreeOemString(&OemServiceName);
+ psrc->dwUnionType = LOOKUP_TYPE_SAP;
+ psrc->u_type.sbc.s = 0;
+ return(NO_ERROR);
+}
+
diff --git a/private/nw/svcdlls/nwwks/client/logon.c b/private/nw/svcdlls/nwwks/client/logon.c
new file mode 100644
index 000000000..f539fce2c
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/logon.c
@@ -0,0 +1,2539 @@
+/*++
+
+Copyright (c) 1993, 1994 Microsoft Corporation
+
+Module Name:
+
+ logon.c
+
+Abstract:
+
+ This module contains NetWare credential management code.
+
+Author:
+
+ Rita Wong (ritaw) 15-Feb-1993
+
+Revision History:
+
+ Yi-Hsin Sung (yihsins) 10-July-1993
+ Moved all dialog handling to nwdlg.c
+
+--*/
+
+#include <nwclient.h>
+#include <ntmsv1_0.h>
+#include <nwsnames.h>
+#include <nwcanon.h>
+#include <validc.h>
+#include <nwevent.h>
+
+#include <nwdlg.h>
+
+#include <nwreg.h>
+#include <nwlsa.h>
+#include <nwauth.h>
+#include <nwapi.h>
+#include <nwmisc.h>
+#include <ndsapi32.h>
+
+
+//-------------------------------------------------------------------//
+// //
+// Local Function Prototypes //
+// //
+//-------------------------------------------------------------------//
+
+VOID
+NwpInitializeRegistry(
+ IN LPWSTR NewUserSid,
+ OUT LPWSTR PreferredServer,
+ IN DWORD PreferredServerSize,
+ OUT PDWORD LogonScriptOptions,
+ OUT PDWORD PrintOption
+ );
+
+DWORD
+NwpReadRegInfo(
+ IN HKEY WkstaKey,
+ IN LPWSTR CurrentUserSid,
+ OUT LPWSTR PreferredServer,
+ IN DWORD PreferredServerSize,
+ OUT PDWORD PrintOption
+ );
+
+DWORD
+NwpGetCurrentUser(
+ OUT LPWSTR *SidString,
+ OUT LPWSTR *UserName
+ );
+
+DWORD
+NwpGetUserSid(
+ IN PLUID LogonId,
+ OUT LPWSTR *UserSidString
+ );
+
+BOOL
+NwpPollWorkstationStart(
+ VOID
+ );
+
+VOID
+NwpSaveServiceCredential(
+ IN PLUID LogonId,
+ IN LPWSTR UserName,
+ IN LPWSTR Password
+ );
+
+DWORD
+NwpSetCredentialInLsa(
+ IN PLUID LogonId,
+ IN LPWSTR UserName,
+ IN LPWSTR Password
+ );
+
+NTSTATUS NwNdsOpenRdrHandle(
+ OUT PHANDLE phNwRdrHandle
+ );
+
+DWORD
+NwpReadLogonScriptOptions(
+ IN LPWSTR CurrentUserSid,
+ OUT PDWORD pLogonScriptOptions,
+ OUT PDWORD pPreferredServerExists
+ );
+
+LPWSTR
+NwpConstructLogonScript(
+ IN DWORD LogonScriptOptions
+ );
+
+VOID
+NwpSelectServers(
+ IN HWND DialogHandle,
+ IN PCHANGE_PW_DLG_PARAM Credential
+ );
+
+////////////////////////////////////////////////////////////////////////////
+
+DWORD
+APIENTRY
+NPLogonNotify(
+ PLUID lpLogonId,
+ LPCWSTR lpAuthentInfoType,
+ LPVOID lpAuthentInfo,
+ LPCWSTR lpPreviousAuthentInfoType,
+ LPVOID lpPreviousAuthentInfo,
+ LPWSTR lpStationName,
+ LPVOID StationHandle,
+ LPWSTR *lpLogonScript
+ )
+/*++
+
+Routine Description:
+
+ This function is called by Winlogon after the interactive
+ user has successfully logged on to the local machine. We
+ are given the username and password, which
+ are displayed in the NetWare specific logon dialog if
+ needed.
+
+Arguments:
+
+ lpLogonId - Ignored.
+
+ lpAuthentInfoType - Supplies a string which if is
+ L"MSV1_0:Interactive" means that the user has been logged
+ on by the Microsoft primary authenticator.
+
+ lpAuthentInfo - Supplies a pointer to the credentials which
+ the user was logged on with.
+
+ lpPreviousAuthentInfoType - Ignored.
+
+ lpPreviousAuthentInfo - Ignored.
+
+ lpStationName - Supplies a string which if it is L"WinSta_0"
+ means that Winlogon logged on the user.
+
+ StationHandle - Supplies the handle to the window which to display
+ our specific dialog.
+
+ lpLogonScripts - Receives a pointer to memory allocated by this
+ routine which contains a MULTI_SZ string of a program to run on
+ the command line with arguments, e.g. L"myprogram\0arg1\0arg2\0".
+ This memory must be freed by the caller with LocalFree.
+
+Return Value:
+
+ WN_SUCCESS - Successfully saved default credentials.
+
+ WN_NOT_SUPPORTED - Primary authenticator is not Microsoft or
+ is not interactive via Winlogon.
+
+ ERROR_FILE_NOT_FOUND - Could not get our own provider DLL handle.
+
+--*/
+{
+ DWORD status = NO_ERROR;
+ int Result = FALSE;
+ LPWSTR NewUserSid = NULL;
+ BOOL LogonAttempted = FALSE;
+ PMSV1_0_INTERACTIVE_LOGON NewLogonInfo =
+ (PMSV1_0_INTERACTIVE_LOGON) lpAuthentInfo;
+
+ WCHAR NwpServerBuffer[MAX_PATH + 1];
+ WCHAR NwpPasswordBuffer[NW_MAX_PASSWORD_LEN + 1];
+ DWORD NwpPrintOption = NW_PRINT_OPTION_DEFAULT;
+ DWORD NwpLogonScriptOptions = NW_LOGONSCRIPT_DEFAULT ;
+ BOOL cPasswordDlgClickOK = 0;
+ BOOL ServiceLogin = FALSE ;
+
+ DBG_UNREFERENCED_PARAMETER(lpPreviousAuthentInfoType);
+ DBG_UNREFERENCED_PARAMETER(lpPreviousAuthentInfo);
+
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("\nNWPROVAU: NPLogonNotify\n"));
+ }
+#endif
+
+ RpcTryExcept {
+
+ if (_wcsicmp(lpAuthentInfoType, L"MSV1_0:Interactive") != 0)
+ {
+
+ //
+ // We only handle a logon where Microsoft is the primary
+ // authenticator and it is an interactive logon via Winlogon.
+ //
+ status = WN_NOT_SUPPORTED;
+ goto EndOfTry;
+ }
+
+ if (_wcsicmp(lpStationName, L"SvcCtl") == 0)
+ {
+ ServiceLogin = TRUE ;
+ }
+
+
+ //
+ // Initialize credential variables
+ //
+ NwpServerBuffer[0] = NW_INVALID_SERVER_CHAR;
+ NwpServerBuffer[1] = 0;
+
+ RtlZeroMemory(NwpPasswordBuffer, sizeof(NwpPasswordBuffer));
+
+ if (NewLogonInfo->Password.Buffer != NULL) {
+
+ //
+ // check for max length to avoid overflowing.
+ //
+ if (NewLogonInfo->Password.Length >
+ (sizeof(NwpPasswordBuffer) - sizeof(WCHAR))) {
+
+ status = ERROR_INVALID_PARAMETER ;
+ goto EndOfTry;
+ }
+
+ wcsncpy(
+ NwpPasswordBuffer,
+ NewLogonInfo->Password.Buffer,
+ NewLogonInfo->Password.Length / sizeof(WCHAR)
+ );
+ }
+
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("\tMessageType : %lu\n", NewLogonInfo->MessageType));
+ KdPrint(("\tLogonDomainName : %ws\n", NewLogonInfo->LogonDomainName.Buffer));
+ KdPrint(("\tUserName : %ws\n", NewLogonInfo->UserName.Buffer));
+ KdPrint(("\tPassword : %ws\n", NwpPasswordBuffer));
+ }
+#endif
+
+ //
+ // if Interactive login, get user related info
+ //
+ if (!ServiceLogin)
+ {
+ //
+ // Get the user SID so that the user Netware username and
+ // preferred server is saved under a SID key rather than the
+ // LogonDomain*UserName key. We do this by making ourselves
+ // a logon process, and call the special MSV1.0 GetUserInfo
+ // interface.
+ //
+ status = NwpGetUserSid(lpLogonId, &NewUserSid);
+
+ if (status != NO_ERROR) {
+ goto EndOfTry;
+ }
+
+ //
+ // Initialize the registry:
+ // 1) Delete the CurrentUser value if it exists (was not clean up
+ // previously because user did not log off--rebooted machine).
+ // 2) Read the current user's PreferredServer and PrintOption
+ // value so that we can display the user's original
+ // preferred server.
+ //
+ NwpInitializeRegistry( NewUserSid,
+ NwpServerBuffer,
+ sizeof( NwpServerBuffer ) /
+ sizeof( NwpServerBuffer[0]),
+ &NwpLogonScriptOptions,
+ &NwpPrintOption );
+ }
+
+ //
+ // Poll until the NetWare workstation has started, then validate
+ // the user credential.
+ //
+ (void) NwpPollWorkstationStart();
+
+ //
+ // If service login, notify the redir with the username/passwd/
+ // LUID triplet and save the logon ID in the registry so that
+ // workstation can pick up if stopped and restarted.
+ //
+ if (ServiceLogin)
+ {
+ NwpSaveServiceCredential(
+ lpLogonId,
+ NewLogonInfo->UserName.Buffer,
+ NwpPasswordBuffer
+ );
+
+ (void) NwrLogonUser(
+ NULL,
+ lpLogonId,
+ NewLogonInfo->UserName.Buffer,
+ NwpPasswordBuffer,
+ NULL,
+ NULL,
+ 0
+ );
+
+ }
+ else
+ {
+ //
+ // We need to save the user credentials at least once so that
+ // the CURRENTUSER Value is stored in the registry.
+ // This must be done before any RPC calls but after polling
+ // workstation start.
+ //
+ NwpSaveLogonCredential(
+ NewUserSid,
+ lpLogonId,
+ NewLogonInfo->UserName.Buffer,
+ NwpPasswordBuffer,
+ NULL // Don't save the preferred server
+ );
+
+//
+// Let the logon set the printer options for each user.
+// Set the provider name and burst options in the service also.
+//
+ //
+ // We need to set the print option at least once.
+ // This also makes the redir aware of the provider name,
+ //
+ (void) NwrSetInfo(
+ NULL,
+ NwpPrintOption,
+ NULL // Need to be NULL so that we will not try to
+ // attach to the server before logging on
+ );
+
+
+ if (*NwpServerBuffer != NW_INVALID_SERVER_CHAR ) {
+
+ //
+ // Preferred server exists. So, try to log the user on.
+ //
+ INT nResult;
+
+ while (1)
+ {
+ WCHAR *DefaultTree = NULL ;
+ WCHAR *DefaultContext = NULL;
+ WCHAR *PreferredServer = NULL;
+ PROMPTDLGPARAM PasswdPromptParam;
+
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("\tNwrLogonUser\n"));
+ KdPrint(("\tUserName : %ws\n",NewLogonInfo->UserName.Buffer));
+ KdPrint(("\tServer : %ws\n", NwpServerBuffer));
+ }
+#endif
+
+
+ //
+ // make sure user is logged off
+ //
+ (void) NwrLogoffUser(NULL, lpLogonId) ;
+
+ status = NwrLogonUser(
+ NULL,
+ lpLogonId,
+ NewLogonInfo->UserName.Buffer,
+ NwpPasswordBuffer,
+ NwpServerBuffer, // now either TREE or SERVER
+ NULL,
+ 0
+ );
+
+
+ if (status != ERROR_INVALID_PASSWORD)
+ break ;
+
+ PasswdPromptParam.UserName = NewLogonInfo->UserName.Buffer;
+ PasswdPromptParam.ServerName = NwpServerBuffer ;
+ PasswdPromptParam.Password = NwpPasswordBuffer;
+ PasswdPromptParam.PasswordSize = sizeof(NwpPasswordBuffer)/
+ sizeof(NwpPasswordBuffer[0]) ;
+ Result = DialogBoxParamW(
+ hmodNW,
+ MAKEINTRESOURCEW(DLG_PASSWORD_PROMPT),
+ (HWND) StationHandle,
+ NwpPasswdPromptDlgProc,
+ (LPARAM) &PasswdPromptParam
+ );
+
+ if (Result == -1 || Result == IDCANCEL)
+ {
+ status = ERROR_INVALID_PASSWORD ;
+ break ;
+ }
+ else
+ {
+ cPasswordDlgClickOK++;
+ }
+ }
+
+ if (status == NW_PASSWORD_HAS_EXPIRED)
+ {
+ WCHAR szNumber[16] ;
+ DWORD status1, dwMsgId, dwGraceLogins = 0 ;
+ LPWSTR apszInsertStrings[3] ;
+
+ //
+ // get the grace login count
+ //
+ status1 = NwGetGraceLoginCount(
+ NwpServerBuffer, // BUGBUG
+ NewLogonInfo->UserName.Buffer,
+ &dwGraceLogins) ;
+
+ //
+ // if hit error, just dont use the number
+ //
+ if (status1 == NO_ERROR)
+ {
+ dwMsgId = IDS_PASSWORD_HAS_EXPIRED ; // use setpass.exe
+ wsprintfW(szNumber, L"%ld", dwGraceLogins) ;
+ }
+ else
+ {
+ dwMsgId = IDS_PASSWORD_HAS_EXPIRED1 ; // use setpass.exe
+ }
+
+ apszInsertStrings[0] = NwpServerBuffer ; // BUGBUG
+ apszInsertStrings[1] = szNumber ;
+ apszInsertStrings[2] = NULL ;
+
+ //
+ // put up message on password expiry
+ //
+ (void) NwpMessageBoxIns(
+ (HWND) StationHandle,
+ IDS_NETWARE_TITLE,
+ dwMsgId,
+ apszInsertStrings,
+ MB_OK | MB_SETFOREGROUND |
+ MB_ICONINFORMATION );
+
+ status = NO_ERROR ;
+ }
+
+
+ if ( status != NO_ERROR )
+ {
+ WCHAR *pszErrorLocation = NwpServerBuffer ; //BUGBUG
+ DWORD dwMsgId = IDS_LOGIN_FAILURE_WARNING;
+
+ if (status == ERROR_ACCOUNT_RESTRICTION)
+ {
+ dwMsgId = IDS_LOGIN_ACC_RESTRICTION;
+ }
+
+ if (status == ERROR_SHARING_PAUSED)
+ {
+ status = IDS_LOGIN_DISABLED;
+ }
+
+ if (*NwpServerBuffer == L'*')
+ {
+ //
+ // Format into nicer string for user
+ //
+ WCHAR *pszTmp = LocalAlloc(LMEM_ZEROINIT,
+ (wcslen(NwpServerBuffer)+2) *
+ sizeof(WCHAR)) ;
+ if (pszTmp)
+ {
+ pszErrorLocation = pszTmp ;
+
+ //
+ // This code formats the NDS
+ // tree UNC to: Tree(Context)
+ //
+ wcscpy(pszErrorLocation, NwpServerBuffer+1) ;
+
+ if (pszTmp = wcschr(pszErrorLocation, L'\\'))
+ {
+ *pszTmp = L'(' ;
+ wcscat(pszErrorLocation, L")") ;
+ }
+ }
+ }
+
+ nResult = NwpMessageBoxError(
+ (HWND) StationHandle,
+ IDS_AUTH_FAILURE_TITLE,
+ dwMsgId,
+ status,
+ pszErrorLocation,
+ MB_YESNO | MB_ICONEXCLAMATION );
+
+ if (pszErrorLocation != NwpServerBuffer)
+ {
+ (void) LocalFree(pszErrorLocation) ;
+ }
+
+ //
+ // User chose not to select another preferred server,
+ // hence just return success.
+ //
+ if ( nResult == IDNO ) {
+ status = NO_ERROR;
+ }
+ }
+
+ //
+ // The user might have changed the password in the password
+ // prompt dialog. Hence, we need to save the credentials
+ // ( the password ) again. Although the user might choose
+ // to select another server, he might canceled out of the
+ // login dialog. We must save logon credentials here no matter
+ // what.
+ //
+ NwpSaveLogonCredential(
+ NewUserSid,
+ lpLogonId,
+ NewLogonInfo->UserName.Buffer,
+ NwpPasswordBuffer,
+ NwpServerBuffer
+ );
+ }
+
+ //
+ // Only prompt user with the NetWare login dialog if
+ // no preferred server was found or an error occurred
+ // while authenticating the user.
+ //
+ if ( ( status != NO_ERROR)
+ || (*NwpServerBuffer == NW_INVALID_SERVER_CHAR)
+ )
+ {
+
+ LOGINDLGPARAM LoginParam;
+
+ if ( cPasswordDlgClickOK > 0 )
+ {
+ // Password might have changed in the password prompt
+ // dialog. We want to always first use the NT password
+ // when validating a user on a server. Hence,
+ // we need to copy back the original NT password into
+ // NwpPasswordBuffer.
+
+ RtlZeroMemory(NwpPasswordBuffer, sizeof(NwpPasswordBuffer));
+ if (NewLogonInfo->Password.Buffer != NULL)
+ {
+ wcsncpy(
+ NwpPasswordBuffer,
+ NewLogonInfo->Password.Buffer,
+ NewLogonInfo->Password.Length / sizeof(WCHAR)
+ );
+ }
+ }
+
+ LoginParam.UserName = NewLogonInfo->UserName.Buffer;
+ LoginParam.ServerName = NwpServerBuffer ;
+ LoginParam.Password = NwpPasswordBuffer;
+ LoginParam.NewUserSid = NewUserSid;
+ LoginParam.pLogonId = lpLogonId;
+ LoginParam.ServerNameSize = sizeof( NwpServerBuffer ) /
+ sizeof( NwpServerBuffer[0]);
+ LoginParam.PasswordSize = sizeof( NwpPasswordBuffer ) /
+ sizeof( NwpPasswordBuffer[0]);
+ LoginParam.LogonScriptOptions = NwpLogonScriptOptions;
+ Result = DialogBoxParamW(
+ hmodNW,
+ MAKEINTRESOURCEW(DLG_NETWARE_LOGIN),
+ (HWND) StationHandle,
+ NwpLoginDlgProc,
+ (LPARAM) &LoginParam
+ );
+
+ if (Result == -1) {
+ status = GetLastError();
+ KdPrint(("NWPROVAU: DialogBox failed %lu\n", status));
+ goto EndOfTry;
+ }
+
+ }
+ }
+
+EndOfTry: ;
+
+ }
+ RpcExcept(1) {
+
+#if DBG
+ DWORD XceptCode;
+
+
+ XceptCode = RpcExceptionCode();
+ IF_DEBUG(LOGON) {
+ KdPrint(("NWPROVAU: NPLogonNotify: Exception code is x%08lx\n", XceptCode));
+ }
+ status = NwpMapRpcError(XceptCode);
+#else
+ status = NwpMapRpcError(RpcExceptionCode());
+#endif
+
+ }
+ RpcEndExcept;
+
+
+ if (!ServiceLogin) {
+ DWORD fPServer = 0;
+
+ NwpReadLogonScriptOptions( NewUserSid,
+ &NwpLogonScriptOptions,
+ &fPServer );
+ if ( fPServer && ( NwpLogonScriptOptions & NW_LOGONSCRIPT_ENABLED ) )
+ {
+ *lpLogonScript = NwpConstructLogonScript( NwpLogonScriptOptions );
+
+ //
+ // set scripts to run synchronously. ignore error if we cant.
+ // not a disaster.
+ //
+ (void) NwrSetLogonScript(NULL, SYNC_LOGONSCRIPT) ;
+ }
+ else
+ {
+ *lpLogonScript = LocalAlloc(LMEM_ZEROINIT, sizeof(WCHAR));
+ }
+ }
+ else
+ *lpLogonScript = NULL;
+
+ if (NewUserSid != NULL) {
+ (void) LocalFree((HLOCAL) NewUserSid);
+ }
+
+ //
+ // Clear the password
+ //
+ RtlZeroMemory(NwpPasswordBuffer, sizeof(NwpPasswordBuffer));
+
+ if (status == WN_NO_NETWORK) {
+ //
+ // We don't care if the workstation has not started because
+ // we tuck the logon credential in the registry to be picked
+ // up by the workstation when it starts up. If we return
+ // ERROR_NO_NETWORK, MPR will poll us forever, causing us
+ // to continuously display the login dialog over and over
+ // again.
+ //
+ status = NO_ERROR;
+ }
+
+ if (status != NO_ERROR) {
+ SetLastError(status);
+ }
+
+ return status;
+}
+
+
+
+DWORD
+APIENTRY
+NPPasswordChangeNotify(
+ LPCWSTR lpAuthentInfoType,
+ LPVOID lpAuthentInfo,
+ LPCWSTR lpPreviousAuthentInfoType,
+ LPVOID lpPreviousAuthentInfo,
+ LPWSTR lpStationName,
+ LPVOID StationHandle,
+ DWORD dwChangeInfo
+ )
+/*++
+
+Routine Description:
+
+ This function is called after the interactive user has selected to
+ change the password for the local logon via the Ctrl-Alt-Del dialog.
+ It is also called when the user cannot login because the password
+ has expired and must be changed.
+
+Arguments:
+
+ lpAuthentInfoType - Supplies a string which if is
+ L"MSV1_0:Interactive" means that the user has been logged
+ on by the Microsoft primary authenticator.
+
+ lpAuthentInfo - Supplies a pointer to the credentials to
+ change to.
+
+ lpPreviousAuthentInfoType - Supplies a pointer to the old
+ credentials.
+
+ lpPreviousAuthentInfo - Ignored.
+
+ lpStationName - Supplies a string which if it is L"WinSta_0"
+ means that Winlogon logged on the user.
+
+ StationHandle - Supplies the handle to the window which to display
+ our specific dialog.
+
+ dwChangeInfo - Ignored.
+
+Return Value:
+
+ WN_SUCCESS - successful operation.
+
+ WN_NOT_SUPPORTED - Only support change password if MS v1.0 is
+ the primary authenticator and is done through Winlogon.
+
+ WN_NO_NETWORK - Workstation service did not start.
+
+--*/
+{
+ DWORD status = NO_ERROR;
+
+
+ CHANGE_PW_DLG_PARAM Credential;
+
+ PMSV1_0_INTERACTIVE_LOGON NewCredential =
+ (PMSV1_0_INTERACTIVE_LOGON) lpAuthentInfo;
+ PMSV1_0_INTERACTIVE_LOGON OldCredential =
+ (PMSV1_0_INTERACTIVE_LOGON) lpPreviousAuthentInfo;
+
+
+
+ DBG_UNREFERENCED_PARAMETER(lpPreviousAuthentInfoType);
+ DBG_UNREFERENCED_PARAMETER(dwChangeInfo);
+
+ Credential.UserName = NULL;
+
+ RpcTryExcept {
+
+ if ((_wcsicmp(lpAuthentInfoType, L"MSV1_0:Interactive") != 0) ||
+ (_wcsicmp(lpStationName, L"WinSta0") != 0)) {
+
+ //
+ // We only handle a logon where Microsoft is the primary
+ // authenticator and it is an interactive logon via Winlogon.
+ //
+ status = WN_NOT_SUPPORTED;
+ goto EndOfTry;
+ }
+
+
+ if (NewCredential == NULL || OldCredential == NULL) {
+
+ //
+ // Credentials not given to us by Winlogon or
+ // user did not type the old and new passwords.
+ //
+
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("NWPROVAU: PasswordChangeNotify got NULL for new and old credential pointers\n"));
+ }
+#endif
+
+ (void) NwpMessageBoxError(
+ (HWND) StationHandle,
+ IDS_CHANGE_PASSWORD_TITLE,
+ IDS_BAD_PASSWORDS,
+ 0,
+ NULL,
+ MB_OK | MB_ICONSTOP
+ );
+
+ status = WN_NOT_SUPPORTED;
+ goto EndOfTry;
+ }
+
+ Credential.UserName = LocalAlloc(
+ LMEM_ZEROINIT,
+ (NW_MAX_USERNAME_LEN + 3 +
+ (2 * NW_MAX_PASSWORD_LEN)) * sizeof(WCHAR)
+ );
+
+ if (Credential.UserName == NULL) {
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ goto EndOfTry;
+ }
+
+ Credential.OldPassword = (LPWSTR) ((DWORD) Credential.UserName +
+ (NW_MAX_USERNAME_LEN + 1) * sizeof(WCHAR));
+ Credential.NewPassword = (LPWSTR) ((DWORD) Credential.OldPassword +
+ (NW_MAX_PASSWORD_LEN + 1) * sizeof(WCHAR));
+
+
+ if (NewCredential->UserName.Length == 0) {
+
+ //
+ // UserName is not specified. Try to get interactive user's name.
+ //
+
+ DWORD CharNeeded = NW_MAX_USERNAME_LEN + 1;
+
+
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("NWPROVAU: PasswordChangeNotify got empty string for username\n"));
+ }
+#endif
+
+ if (! GetUserNameW(Credential.UserName, &CharNeeded)) {
+
+ //
+ // Could not get interactive user's name. Give up.
+ //
+ (void) NwpMessageBoxError(
+ (HWND) StationHandle,
+ IDS_CHANGE_PASSWORD_TITLE,
+ 0,
+ ERROR_BAD_USERNAME,
+ NULL,
+ MB_OK | MB_ICONSTOP
+ );
+ }
+ }
+ else {
+ wcsncpy(
+ Credential.UserName,
+ NewCredential->UserName.Buffer,
+ NewCredential->UserName.Length / sizeof(WCHAR)
+ );
+ }
+
+ if (OldCredential->Password.Length > 0)
+ {
+ wcsncpy(
+ Credential.OldPassword,
+ OldCredential->Password.Buffer,
+ OldCredential->Password.Length / sizeof(WCHAR)
+ );
+ }
+ else
+ {
+ Credential.OldPassword[0] = 0;
+ }
+
+ if (NewCredential->Password.Length > 0)
+ {
+ wcsncpy(
+ Credential.NewPassword,
+ NewCredential->Password.Buffer,
+ NewCredential->Password.Length / sizeof(WCHAR)
+ );
+ }
+ else
+ {
+ Credential.NewPassword[0] = 0;
+ }
+
+ //
+ // Encode the passwords.
+ //
+ {
+ UCHAR EncodeSeed = NW_ENCODE_SEED2;
+ UNICODE_STRING PasswordStr;
+
+
+ RtlInitUnicodeString(&PasswordStr, Credential.OldPassword);
+ RtlRunEncodeUnicodeString(&EncodeSeed, &PasswordStr);
+
+ RtlInitUnicodeString(&PasswordStr, Credential.NewPassword);
+ RtlRunEncodeUnicodeString(&EncodeSeed, &PasswordStr);
+ }
+
+ NwpSelectServers(StationHandle, &Credential);
+
+EndOfTry: ;
+
+ }
+ RpcExcept(1) {
+
+#if DBG
+ DWORD XceptCode;
+
+
+ XceptCode = RpcExceptionCode();
+ IF_DEBUG(LOGON) {
+ KdPrint(("NWPROVAU: NPPasswordChangeNotify: Exception code is x%08lx\n", XceptCode));
+ }
+ status = NwpMapRpcError(XceptCode);
+#else
+ status = NwpMapRpcError(RpcExceptionCode());
+#endif
+
+ }
+ RpcEndExcept;
+
+ if (Credential.UserName != NULL) {
+ LocalFree(Credential.UserName);
+ }
+
+ if (status != NO_ERROR) {
+ SetLastError(status);
+ }
+
+ return status;
+
+}
+
+
+VOID
+NwpInitializeRegistry(
+ IN LPWSTR NewUserSid,
+ OUT LPWSTR PreferredServer,
+ IN DWORD PreferredServerSize,
+ OUT PDWORD pLogonScriptOptions,
+ OUT PDWORD PrintOption
+ )
+/*++
+
+Routine Description:
+
+ This routine initializes the registry before putting up the
+ logon dialog.
+ 1) Deletes the CurrentUser value if it was not cleaned up from
+ the last logoff.
+ 2) Reads the current user's original PreferredServer value
+ 3) Reads the current user's PrintOption value
+
+Arguments:
+
+ NewUserSid - Supplies the newly logged on user's SID in string format
+ which is the key name to find the password and preferred server.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ LONG RegError;
+ HKEY WkstaKey;
+
+
+ NwDeleteCurrentUser();
+
+ //
+ // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
+ // \NWCWorkstation\Parameters\Option
+ //
+ RegError = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ NW_WORKSTATION_OPTION_REGKEY,
+ REG_OPTION_NON_VOLATILE,
+ KEY_READ,
+ &WkstaKey
+ );
+
+ if (RegError != NO_ERROR) {
+ KdPrint(("NWPROVAU: NwpInitializeRegistry open NWCWorkstation\\Parameters\\Option key unexpected error %lu!\n", RegError));
+ return;
+ }
+
+ //
+ // Get user's preferred server information.
+ //
+ (void) NwpReadRegInfo(WkstaKey,
+ NewUserSid,
+ PreferredServer,
+ PreferredServerSize,
+ PrintOption
+ );
+
+ (void) RegCloseKey(WkstaKey);
+ (void) NwpReadLogonScriptOptions( NewUserSid, pLogonScriptOptions, NULL );
+}
+
+
+DWORD
+NwpReadRegInfo(
+ IN HKEY WkstaKey,
+ IN LPWSTR CurrentUserSid,
+ OUT LPWSTR PreferredServer,
+ IN DWORD PreferredServerSize,
+ OUT PDWORD PrintOption
+ )
+/*++
+
+Routine Description:
+
+ This routine reads the user's preferred server and print option
+ from the registry.
+
+Arguments:
+
+ WkstaKey - Supplies the handle to the parameters key under the NetWare
+ workstation service key.
+
+ CurrentUserSid - Supplies the SID string of the user whose information
+ to read.
+
+ PreferredServer - Receives the user's preferred server.
+
+ PrintOption - Receives the user's print option.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ LONG RegError;
+
+ HKEY UserKey;
+
+ DWORD ValueType;
+ DWORD BytesNeeded;
+
+ //
+ // Open current user's key to read the original preferred server.
+ //
+ RegError = RegOpenKeyExW(
+ WkstaKey,
+ CurrentUserSid,
+ REG_OPTION_NON_VOLATILE,
+ KEY_READ,
+ &UserKey
+ );
+
+ if (RegError != NO_ERROR) {
+
+ if ( (RegError == ERROR_FILE_NOT_FOUND) ||
+ (RegError == ERROR_PATH_NOT_FOUND) ) {
+
+ //
+ // If key doesnt exist assume first time. Use default
+ // if present.
+ //
+
+ LONG RegError1 ;
+ HKEY WkstaParamKey ;
+ DWORD Disposition, dwScriptOptions,
+ dwScriptOptionsSize = sizeof(dwScriptOptions);
+
+ //
+ // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
+ // \NWCWorkstation\Parameters
+ //
+ RegError1 = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ NW_WORKSTATION_REGKEY,
+ REG_OPTION_NON_VOLATILE,
+ KEY_READ,
+ &WkstaParamKey
+ );
+
+ if (RegError1 != NO_ERROR) {
+
+ return (DWORD) RegError; // return original error
+ }
+
+ BytesNeeded = PreferredServerSize;
+
+ RegError1 = RegQueryValueExW(
+ WkstaParamKey,
+ NW_DEFAULTSERVER_VALUENAME,
+ NULL,
+ &ValueType,
+ (LPBYTE) PreferredServer,
+ &BytesNeeded
+ );
+
+
+ if (RegError1 != NO_ERROR) {
+
+ (void) RegCloseKey(WkstaParamKey);
+ PreferredServer[0] = NW_INVALID_SERVER_CHAR;
+ PreferredServer[1] = 0;
+ return (DWORD) RegError; // return original error
+ }
+
+ RegError1 = RegQueryValueExW(
+ WkstaParamKey,
+ NW_DEFAULTSCRIPTOPTIONS_VALUENAME,
+ NULL,
+ &ValueType,
+ (LPBYTE) &dwScriptOptions,
+ &dwScriptOptionsSize
+ );
+
+ (void) RegCloseKey(WkstaParamKey);
+
+ if (RegError1 != NO_ERROR) {
+
+ dwScriptOptions = NW_LOGONSCRIPT_ENABLED |
+ NW_LOGONSCRIPT_4X_ENABLED ;
+ }
+
+ //
+ // We have a default. now write out the info for the current
+ // user now. Note we also write out the login script option.
+ // Errors here are not reported.
+ //
+
+
+ //
+ // Create the key under NWCWorkstation\Parameters\Option\<usersid>
+ //
+ RegError = RegCreateKeyExW(
+ WkstaKey,
+ CurrentUserSid,
+ 0,
+ WIN31_CLASS,
+ REG_OPTION_NON_VOLATILE,
+ KEY_WRITE | WRITE_DAC,
+ NULL, // security attr
+ &UserKey,
+ &Disposition
+ );
+
+ if (RegError == NO_ERROR) {
+
+ RegError = NwLibSetEverybodyPermission( UserKey,
+ KEY_SET_VALUE );
+
+ if ( RegError == NO_ERROR )
+ {
+ //
+ // Write the PreferredServer. Errors ignored.
+ //
+ RegError = RegSetValueExW(
+ UserKey,
+ NW_SERVER_VALUENAME,
+ 0,
+ REG_SZ,
+ (LPVOID) PreferredServer,
+ (wcslen(PreferredServer) + 1) * sizeof(WCHAR)
+ );
+
+ (void) RegCloseKey(UserKey) ;
+
+ (void) NwpSaveLogonScriptOptions(
+ CurrentUserSid,
+ dwScriptOptions
+ ) ;
+ }
+ else {
+
+ (void) RegCloseKey(UserKey) ;
+ }
+ }
+
+
+ *PrintOption = NW_PRINT_OPTION_DEFAULT;
+ return NO_ERROR;
+
+ }
+ return (DWORD) RegError;
+ }
+
+
+ //
+ // Read PreferredServer value
+ //
+ BytesNeeded = PreferredServerSize;
+
+ RegError = RegQueryValueExW(
+ UserKey,
+ NW_SERVER_VALUENAME,
+ NULL,
+ &ValueType,
+ (LPBYTE) PreferredServer,
+ &BytesNeeded
+ );
+
+ ASSERT(BytesNeeded <= PreferredServerSize);
+
+ if (RegError != NO_ERROR) {
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("NWPROVAU: Attempt to read original preferred server failed %lu\n",
+ RegError));
+ }
+#endif
+ PreferredServer[0] = NW_INVALID_SERVER_CHAR; // Display login dialog
+ PreferredServer[1] = 0;
+ goto CleanExit;
+ }
+
+ //
+ // Read PrintOption value into NwpPrintOption.
+ //
+ BytesNeeded = sizeof(PrintOption);
+
+ RegError = RegQueryValueExW(
+ UserKey,
+ NW_PRINTOPTION_VALUENAME,
+ NULL,
+ &ValueType,
+ (LPBYTE) PrintOption,
+ &BytesNeeded
+ );
+
+ if (RegError != NO_ERROR ) {
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("NWPROVAU: Attempt to read original print option failed %lu\n", RegError));
+ }
+#endif
+
+ *PrintOption = NW_PRINT_OPTION_DEFAULT;
+ goto CleanExit;
+ }
+
+CleanExit:
+
+ (void) RegCloseKey(UserKey);
+
+ return NO_ERROR;
+}
+
+DWORD
+NwpReadLogonScriptOptions(
+ IN LPWSTR CurrentUserSid,
+ OUT PDWORD pLogonScriptOptions,
+ OUT PDWORD pPreferredServerExists
+
+ )
+/*++
+
+Routine Description:
+
+ This routine reads the user's logon script options from the registry.
+
+Arguments:
+
+ CurrentUserSid - Supplies the SID string of the user whose information
+ to read.
+
+ pLogonScriptOptions - Receives the user's script options
+
+ pPreferredServerExists - Prefered server specified
+
+Return Value:
+
+ None.
+
+--*/
+{
+ LONG RegError;
+
+ HKEY UserKey;
+
+ DWORD ValueType;
+ DWORD BytesNeeded;
+ HKEY WkstaKey;
+ WCHAR PreferredServer[MAX_PATH + 1];
+
+ //
+ // initialize output values
+ //
+ *pLogonScriptOptions = 0 ;
+ if (pPreferredServerExists)
+ *pPreferredServerExists = 0 ;
+
+
+ //
+ // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
+ // \NWCWorkstation\Parameters\Option
+ //
+ RegError = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ NW_WORKSTATION_OPTION_REGKEY,
+ REG_OPTION_NON_VOLATILE,
+ KEY_READ,
+ &WkstaKey
+ );
+
+ if (RegError != NO_ERROR) {
+ KdPrint(("NWPROVAU: NwpReadLogonScriptOptions open NWCWorkstation\\Parameters\\Option key unexpected error %lu!\n", RegError));
+ return (DWORD) RegError;
+ }
+
+ //
+ // Open current user's key
+ //
+ RegError = RegOpenKeyExW(
+ WkstaKey,
+ CurrentUserSid,
+ REG_OPTION_NON_VOLATILE,
+ KEY_READ,
+ &UserKey
+ );
+
+ if (RegError != NO_ERROR) {
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("NWPROVAU: Open of CurrentUser %ws existing key failed %lu\n",
+ CurrentUserSid, RegError));
+ }
+#endif
+ (void) RegCloseKey(WkstaKey);
+ return (DWORD) RegError;
+ }
+
+
+ //
+ // Read LogonScriptOption value
+ //
+ BytesNeeded = sizeof(*pLogonScriptOptions);
+
+ RegError = RegQueryValueExW(
+ UserKey,
+ NW_LOGONSCRIPT_VALUENAME,
+ NULL,
+ &ValueType,
+ (LPBYTE) pLogonScriptOptions,
+ &BytesNeeded
+ );
+
+ if (RegError != NO_ERROR ) {
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("NWPROVAU: Attempt to read original logon script option failed %lu\n", RegError));
+ }
+#endif
+
+ // leave *pLogonScriptOptions as 0
+ }
+
+ if ( pPreferredServerExists != NULL ) {
+
+ //
+ // Read PreferredServer value
+ //
+ BytesNeeded = sizeof( PreferredServer );
+
+ RegError = RegQueryValueExW(
+ UserKey,
+ NW_SERVER_VALUENAME,
+ NULL,
+ &ValueType,
+ (LPBYTE) PreferredServer,
+ &BytesNeeded
+ );
+
+ ASSERT(BytesNeeded <= sizeof(PreferredServer));
+
+ if (RegError != NO_ERROR) {
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("NWPROVAU: Attempt to read original preferred server failed %lu\n",
+ RegError));
+ }
+#endif
+ *pPreferredServerExists = FALSE;
+ }
+ else {
+ if ( lstrcmp( PreferredServer, L"" ) )
+ *pPreferredServerExists = TRUE;
+ else
+ *pPreferredServerExists = FALSE;
+ }
+ }
+
+ (void) RegCloseKey(UserKey);
+ (void) RegCloseKey(WkstaKey);
+
+ return NO_ERROR;
+}
+
+LPWSTR
+NwpConstructLogonScript(
+ IN DWORD LogonScriptOptions
+)
+/*++
+
+Routine Description:
+
+ This routine constructs the multi-string for the logon script,
+ based on the options
+
+Arguments:
+
+ LogonScriptOptions - Logon Script options
+
+Return Value:
+
+ Allocated multi-string
+
+--*/
+{
+ LPWSTR pLogonScript;
+ DWORD BytesNeeded;
+
+#define NW_NETWARE_SCRIPT_NAME L"nwscript.exe"
+#define NW_NETWARE_DEBUG_NAME L"ntsd "
+
+ if ( !( LogonScriptOptions & NW_LOGONSCRIPT_ENABLED ) ) {
+ return NULL;
+ }
+
+ BytesNeeded = MAX_PATH * sizeof(WCHAR);
+
+ if (pLogonScript = LocalAlloc( LMEM_ZEROINIT, BytesNeeded))
+ {
+ DWORD dwSkipBytes = 0 ;
+ UINT retval ;
+
+#if DBG
+ //
+ // if have exact match then start under NTSD.
+ //
+ if ( LogonScriptOptions == (NW_LOGONSCRIPT_ENABLED |
+ NW_LOGONSCRIPT_4X_ENABLED |
+ NW_LOGONSCRIPT_DEBUG) ) {
+
+ retval = GetSystemDirectory(pLogonScript,
+ BytesNeeded );
+ if (retval == 0) {
+
+ (void)LocalFree(pLogonScript) ;
+ return(NULL) ;
+ }
+ wcscat( pLogonScript, L"\\" );
+ wcscat( pLogonScript, NW_NETWARE_DEBUG_NAME );
+ dwSkipBytes = (retval * sizeof(WCHAR)) +
+ sizeof(NW_NETWARE_DEBUG_NAME) ;
+ BytesNeeded -= dwSkipBytes ;
+ }
+#endif
+
+ retval = GetSystemDirectory(pLogonScript + (dwSkipBytes/sizeof(WCHAR)),
+ BytesNeeded );
+
+ if (retval == 0) {
+
+ (void)LocalFree(pLogonScript) ;
+ return(NULL) ;
+ }
+
+ wcscat( pLogonScript, L"\\" );
+ wcscat( pLogonScript, NW_NETWARE_SCRIPT_NAME );
+ }
+
+ return (pLogonScript);
+
+}
+
+DWORD
+NwpSaveLogonScriptOptions(
+ IN LPWSTR CurrentUserSid,
+ IN DWORD LogonScriptOptions
+ )
+/*++
+
+Routine Description:
+
+ This routine saves the logon script options in the registry.
+
+Arguments:
+
+ CurrentUserSid - Supplies the user's SID string
+
+ LogonScriptOptions - Logon script options
+
+Return Value:
+
+ Error from registry
+
+--*/
+{
+ LONG RegError;
+ HKEY WkstaOptionKey;
+ HKEY CurrentUserOptionKey;
+
+ // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
+ // \NWCWorkstation\Parameters\Option
+ //
+ RegError = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ NW_WORKSTATION_OPTION_REGKEY,
+ REG_OPTION_NON_VOLATILE,
+ KEY_WRITE | KEY_CREATE_SUB_KEY | DELETE,
+ &WkstaOptionKey
+ );
+
+ if (RegError != NO_ERROR) {
+ KdPrint(("NWPROVAU: NwpSaveLogonScriptOptions open NWCWorkstation\\Parameters\\Option key unexpected error %lu!\n", RegError));
+ return RegError;
+ }
+
+ //
+ // Open the <NewUser> key under Option
+ //
+ RegError = RegOpenKeyExW(
+ WkstaOptionKey,
+ CurrentUserSid,
+ REG_OPTION_NON_VOLATILE,
+ KEY_WRITE,
+ &CurrentUserOptionKey
+ );
+
+ (void) RegCloseKey(WkstaOptionKey);
+
+ if (RegError != NO_ERROR) {
+ KdPrint(("NWPROVAU: NwpSaveLogonScriptOptions failed to save options %lu\n", RegError));
+ return RegError;
+ }
+
+ //
+ // Write the options
+ //
+ RegError = RegSetValueExW(
+ CurrentUserOptionKey,
+ NW_LOGONSCRIPT_VALUENAME,
+ 0,
+ REG_DWORD,
+ (LPVOID) &LogonScriptOptions,
+ sizeof(LogonScriptOptions)
+ );
+
+ (void) RegCloseKey(CurrentUserOptionKey);
+
+ if (RegError != NO_ERROR) {
+ KdPrint(("NWPROVAU: NwpSaveLogonScriptOptions failed to save options %lu\n", RegError));
+ }
+
+ return RegError;
+
+}
+
+
+VOID
+NwpSaveLogonCredential(
+ IN LPWSTR NewUserSid,
+ IN PLUID LogonId,
+ IN LPWSTR UserName,
+ IN LPWSTR Password,
+ IN LPWSTR PreferredServer OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ This routine saves the user logon credential in the registry
+ and LSA's memory. This is normally called when NwrLogonUser is
+ successful.
+
+Arguments:
+
+ NewUserSid - Supplies the newly logged on user's SID string to be
+ set as the CurrentUser value as well as the name of the key for
+ the user's preferred server.
+
+ LogonId - Supplies the user's logon ID. If NULL is specified,
+ just read the existing logon ID from the registry rather
+ than save a new one.
+
+ UserName - Supplies the name of the user.
+
+ Password - Supplies the password which the user wants to use on
+ the NetWare network.
+
+ PreferredServer - Supplies the name of the preferred server.
+
+Return Value:
+
+ Error from redirector if login is rejected.
+
+--*/
+{
+ DWORD status;
+
+ LONG RegError;
+ HKEY WkstaKey;
+ HKEY WkstaLogonKey;
+ HKEY WkstaOptionKey;
+ HKEY NewUserLogonKey;
+ HKEY NewUserOptionKey;
+
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("NWPROVAU: NwpSaveLogonCredential: %ws, %ws, %ws, %ws\n",
+ NewUserSid, UserName, Password, PreferredServer));
+ }
+#endif
+
+ //
+ // Write the logon credential to the registry. NewUserSid should be
+ // written last so that if it can be read by the workstation service,
+ // it's an indication that all other credential information has been
+ // written.
+ //
+
+ //
+ // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
+ // \NWCWorkstation\Parameters\Logon
+ //
+ RegError = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ NW_WORKSTATION_LOGON_REGKEY,
+ REG_OPTION_NON_VOLATILE,
+ KEY_WRITE | KEY_CREATE_SUB_KEY | DELETE,
+ &WkstaLogonKey
+ );
+
+ if (RegError != NO_ERROR) {
+ KdPrint(("NWPROVAU: NwpSaveLogonCredential open NWCWorkstation\\Parameters\\Logon key unexpected error %lu!\n", RegError));
+ return;
+ }
+
+ // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
+ // \NWCWorkstation\Parameters\Option
+ //
+ RegError = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ NW_WORKSTATION_OPTION_REGKEY,
+ REG_OPTION_NON_VOLATILE,
+ KEY_WRITE | KEY_CREATE_SUB_KEY | DELETE,
+ &WkstaOptionKey
+ );
+
+ if (RegError != NO_ERROR) {
+ KdPrint(("NWPROVAU: NwpSaveLogonCredential open NWCWorkstation\\Parameters\\Option key unexpected error %lu!\n", RegError));
+ (void) RegCloseKey( WkstaLogonKey );
+ return;
+ }
+
+ //
+ // Open the <NewUser> key under Logon
+ //
+ RegError = RegOpenKeyExW(
+ WkstaLogonKey,
+ NewUserSid,
+ REG_OPTION_NON_VOLATILE,
+ KEY_WRITE,
+ &NewUserLogonKey
+ );
+
+
+ if (RegError == ERROR_FILE_NOT_FOUND)
+ {
+ DWORD Disposition;
+
+ //
+ // Create <NewUser> key under NWCWorkstation\Parameters\Logon
+ //
+ RegError = RegCreateKeyExW(
+ WkstaLogonKey,
+ NewUserSid,
+ 0,
+ WIN31_CLASS,
+ REG_OPTION_NON_VOLATILE,
+ KEY_WRITE,
+ NULL, // security attr
+ &NewUserLogonKey,
+ &Disposition
+ );
+
+ if (RegError != NO_ERROR) {
+ KdPrint(("NWPROVAU: NwpSaveLogonCredential create Logon\\%ws key unexpected error %lu!\n", NewUserSid, RegError));
+
+ (void) RegCloseKey(WkstaLogonKey);
+ (void) RegCloseKey(WkstaOptionKey);
+ return;
+ }
+ }
+ else if (RegError != NO_ERROR)
+ {
+ KdPrint(("NWPROVAU: NwpSaveLogonCredential open Logon\\%ws unexpected error %lu!\n", NewUserSid, RegError));
+
+ (void) RegCloseKey(WkstaLogonKey);
+ (void) RegCloseKey(WkstaOptionKey);
+ return;
+ }
+
+ (void) RegCloseKey(WkstaLogonKey);
+
+ //
+ // Open the <NewUser> key under Option
+ //
+ RegError = RegOpenKeyExW(
+ WkstaOptionKey,
+ NewUserSid,
+ REG_OPTION_NON_VOLATILE,
+ KEY_WRITE,
+ &NewUserOptionKey
+ );
+
+
+ if (RegError == ERROR_FILE_NOT_FOUND)
+ {
+ DWORD Disposition;
+
+ //
+ // Create <NewUser> key under NWCWorkstation\Parameters\Option
+ //
+ RegError = RegCreateKeyExW(
+ WkstaOptionKey,
+ NewUserSid,
+ 0,
+ WIN31_CLASS,
+ REG_OPTION_NON_VOLATILE,
+ KEY_WRITE | WRITE_DAC,
+ NULL, // security attr
+ &NewUserOptionKey,
+ &Disposition
+ );
+
+
+ if (RegError != NO_ERROR) {
+ KdPrint(("NWPROVAU: NwpSaveLogonCredential create Option\\%ws key unexpected error %lu!\n", NewUserSid, RegError));
+
+ (void) RegCloseKey(WkstaOptionKey);
+ (void) RegCloseKey(NewUserLogonKey);
+ return;
+ }
+
+ RegError = NwLibSetEverybodyPermission( NewUserOptionKey,
+ KEY_SET_VALUE );
+
+ if ( RegError != NO_ERROR )
+ {
+ KdPrint(("NWPROVAU: NwpSaveLogonCredential set security on Option\\%ws key unexpected error %lu!\n", NewUserSid, RegError));
+
+ (void) RegCloseKey(WkstaOptionKey);
+ (void) RegCloseKey(NewUserLogonKey);
+ return;
+ }
+
+ }
+ else if (RegError != NO_ERROR)
+ {
+ KdPrint(("NWPROVAU: NwpSaveLogonCredential open Option\\%ws unexpected error %lu!\n", NewUserSid, RegError));
+
+ (void) RegCloseKey(WkstaOptionKey);
+ (void) RegCloseKey(NewUserLogonKey);
+ return;
+ }
+
+ (void) RegCloseKey(WkstaOptionKey);
+
+ //
+ // Successfully opened or created an existing user entry.
+ // We will now save the credential in LSA.
+ //
+ status = NwpSetCredentialInLsa(
+ LogonId,
+ UserName,
+ Password
+ );
+
+ if (status != NO_ERROR) {
+ //
+ // Could not save new credential.
+ //
+ KdPrint(("NWPROVAU: NwpSaveLogonCredential failed to set credential %lu\n", status));
+ }
+
+ //
+ // Write the logon ID to the registry.
+ //
+ RegError = RegSetValueExW(
+ NewUserLogonKey,
+ NW_LOGONID_VALUENAME,
+ 0,
+ REG_BINARY,
+ (LPVOID) LogonId,
+ sizeof(LUID)
+ );
+
+ if (RegError != NO_ERROR) {
+ KdPrint(("NWPROVAU: NwpSaveLogonCredential failed to save logon ID %lu\n", RegError));
+ }
+
+ (void) RegCloseKey(NewUserLogonKey);
+
+ //
+ // If PreferredServer is not supplied, then that means we don't want to
+ // save the preferred server into the registry.
+ //
+
+ if (ARGUMENT_PRESENT(PreferredServer))
+ {
+ //
+ // Write the PreferredServer
+ //
+ RegError = RegSetValueExW(
+ NewUserOptionKey,
+ NW_SERVER_VALUENAME,
+ 0,
+ REG_SZ,
+ (LPVOID) PreferredServer,
+ (wcslen(PreferredServer) + 1) * sizeof(WCHAR)
+ );
+
+
+ if (RegError != NO_ERROR) {
+ KdPrint(("NWPROVAU: NwpSaveLogonCredential failed to save PreferredServer %ws %lu\n", PreferredServer, RegError));
+ }
+ }
+
+ (void) RegCloseKey(NewUserOptionKey);
+
+ //
+ // Write the NewUser string as the CurrentUser value.
+ //
+
+ //
+ // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
+ // \NWCWorkstation\Parameters
+ //
+ RegError = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ NW_WORKSTATION_REGKEY,
+ REG_OPTION_NON_VOLATILE,
+ KEY_WRITE,
+ &WkstaKey
+ );
+
+ if (RegError != NO_ERROR) {
+ KdPrint(("NWPROVAU: NwpSaveLogonCredential open NWCWorkstation\\Parameters key unexpected error %lu!\n", RegError));
+ return;
+ }
+
+ RegError = RegSetValueExW(
+ WkstaKey,
+ NW_CURRENTUSER_VALUENAME,
+ 0,
+ REG_SZ,
+ (LPVOID) NewUserSid,
+ (wcslen(NewUserSid) + 1) * sizeof(WCHAR)
+ );
+
+ if (RegError != NO_ERROR) {
+ KdPrint(("NWPROVAU: NwpSaveLogonCredential failed to save NewUser %ws %lu\n", NewUserSid, RegError));
+ }
+
+ RegCloseKey( WkstaKey );
+
+}
+
+
+VOID
+NwpSaveServiceCredential(
+ IN PLUID LogonId,
+ IN LPWSTR UserName,
+ IN LPWSTR Password
+ )
+/*++
+
+Routine Description:
+
+ This routine saves the service logon ID in the registry and
+ the credential in LSA's memory.
+
+Arguments:
+
+ LogonId - Supplies the service's logon ID.
+
+ UserName - Supplies the name of the service.
+
+ Password - Supplies the password of the service.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ DWORD status;
+
+ LONG RegError;
+ HKEY ServiceLogonKey;
+ HKEY LogonIdKey;
+
+ DWORD Disposition;
+ WCHAR LogonIdKeyName[NW_MAX_LOGON_ID_LEN];
+
+ //
+ // Write the logon ID to the registry.
+
+ //
+ // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
+ // \NWCWorkstation\Parameters\ServiceLogon, create if does not exist
+ //
+ RegError = RegCreateKeyExW(
+ HKEY_LOCAL_MACHINE,
+ NW_SERVICE_LOGON_REGKEY,
+ 0,
+ WIN31_CLASS,
+ REG_OPTION_NON_VOLATILE,
+ KEY_WRITE,
+ NULL, // security attr
+ &ServiceLogonKey,
+ &Disposition
+ );
+
+ if (RegError != NO_ERROR) {
+ KdPrint(("NWPROVAU: NwpSaveServiceCredential open NWCWorkstation\\Parameters\\ServiceLogon key unexpected error %lu!\n", RegError));
+ return;
+ }
+
+ NwLuidToWStr(LogonId, LogonIdKeyName);
+
+ //
+ // Create the logon ID key under ServiceLogon
+ //
+ RegError = RegCreateKeyExW(
+ ServiceLogonKey,
+ LogonIdKeyName,
+ 0,
+ WIN31_CLASS,
+ REG_OPTION_NON_VOLATILE,
+ KEY_WRITE,
+ NULL, // security attr
+ &LogonIdKey,
+ &Disposition
+ );
+
+ RegCloseKey(ServiceLogonKey);
+
+ if (RegError != NO_ERROR) {
+ KdPrint(("NWPROVAU: NwpSaveServiceCredential create NWCWorkstation\\Parameters\\ServiceLogon\\<LogonId> key unexpected error %lu!\n", RegError));
+ return;
+ }
+
+ RegCloseKey(LogonIdKey);
+
+ //
+ // Save the service logon credential in LSA.
+ //
+ status = NwpSetCredentialInLsa(
+ LogonId,
+ UserName,
+ Password
+ );
+
+ if (status != NO_ERROR) {
+ //
+ // Could not save new credential.
+ //
+ KdPrint(("NWPROVAU: NwpSaveServiceCredential failed to set credential %lu\n", status));
+ }
+}
+
+
+DWORD
+NwpGetUserSid(
+ IN PLUID LogonId,
+ OUT LPWSTR *UserSidString
+ )
+/*++
+
+Routine Description:
+
+ This routine looks up the SID of a user given the user's logon ID.
+ It does this by making the current process a logon process and then
+ call to LSA to get the user SID.
+
+Arguments:
+
+ LogonId - Supplies the logon ID of the user to lookup the SID.
+
+ UserSidString - Receives a pointer to a buffer allocated by this routine
+ which contains the user SID in string form. This must be freed with
+ LocalFree when done.
+
+Return Value:
+
+ NO_ERROR or reason for failure.
+
+--*/
+{
+ DWORD status;
+ NTSTATUS ntstatus;
+ NTSTATUS AuthPackageStatus;
+
+ STRING InputString;
+ LSA_OPERATIONAL_MODE SecurityMode = 0;
+
+ HANDLE LsaHandle;
+ ULONG AuthPackageId;
+
+ MSV1_0_GETUSERINFO_REQUEST UserInfoRequest;
+ PMSV1_0_GETUSERINFO_RESPONSE UserInfoResponse = NULL;
+ ULONG UserInfoResponseLength;
+
+
+
+
+ //
+ // Register this process as a logon process so that we can call
+ // MS V 1.0 authentication package.
+ //
+ RtlInitString(&InputString, "Microsoft NetWare Credential Manager");
+
+ ntstatus = LsaRegisterLogonProcess(
+ &InputString,
+ &LsaHandle,
+ &SecurityMode
+ );
+
+ if (! NT_SUCCESS(ntstatus)) {
+ KdPrint(("NWPROVAU: LsaRegisterLogonProcess returns x%08lx\n",
+ ntstatus));
+ return RtlNtStatusToDosError(ntstatus);
+ }
+
+ //
+ // Look up the MS V1.0 authentication package
+ //
+ RtlInitString(&InputString, MSV1_0_PACKAGE_NAME);
+
+ ntstatus = LsaLookupAuthenticationPackage(
+ LsaHandle,
+ &InputString,
+ &AuthPackageId
+ );
+
+ if (! NT_SUCCESS(ntstatus)) {
+ KdPrint(("NWPROVAU: LsaLookupAuthenticationPackage returns x%08lx\n",
+ ntstatus));
+ status = RtlNtStatusToDosError(ntstatus);
+ goto CleanExit;
+ }
+
+ //
+ // Ask authentication package for user information.
+ //
+ UserInfoRequest.MessageType = MsV1_0GetUserInfo;
+ RtlCopyLuid(&UserInfoRequest.LogonId, LogonId);
+
+ ntstatus = LsaCallAuthenticationPackage(
+ LsaHandle,
+ AuthPackageId,
+ &UserInfoRequest,
+ sizeof(MSV1_0_GETUSERINFO_REQUEST),
+ (PVOID *) &UserInfoResponse,
+ &UserInfoResponseLength,
+ &AuthPackageStatus
+ );
+
+ if (NT_SUCCESS(ntstatus)) {
+ ntstatus = AuthPackageStatus;
+ }
+ if (! NT_SUCCESS(ntstatus)) {
+ KdPrint(("NWPROVAU: LsaCallAuthenticationPackage returns x%08lx\n",
+ ntstatus));
+ status = RtlNtStatusToDosError(ntstatus);
+ goto CleanExit;
+ }
+
+ //
+ // Convert the SID to string. This routine also allocates the
+ // output buffer.
+ //
+ status = NwpConvertSid(
+ UserInfoResponse->UserSid,
+ UserSidString
+ );
+
+CleanExit:
+ if (UserInfoResponse != NULL) {
+ (void) LsaFreeReturnBuffer((PVOID) UserInfoResponse);
+ }
+
+ (void) LsaDeregisterLogonProcess(LsaHandle);
+
+ return status;
+}
+
+
+DWORD
+NwpConvertSid(
+ IN PSID Sid,
+ OUT LPWSTR *UserSidString
+ )
+{
+ NTSTATUS ntstatus;
+ UNICODE_STRING SidString;
+
+
+ //
+ // Initialize output pointer
+ //
+ *UserSidString = NULL;
+
+ ntstatus = RtlConvertSidToUnicodeString(
+ &SidString,
+ Sid,
+ TRUE // Allocate destination string
+ );
+
+ if (ntstatus != STATUS_SUCCESS) {
+ KdPrint(("NWPROVAU: RtlConvertSidToUnicodeString returns %08lx\n",
+ ntstatus));
+ return RtlNtStatusToDosError(ntstatus);
+ }
+
+ //
+ // Create the buffer to return the SID string
+ //
+ if ((*UserSidString = (LPVOID) LocalAlloc(
+ LMEM_ZEROINIT,
+ SidString.Length + sizeof(WCHAR)
+ )) == NULL) {
+ RtlFreeUnicodeString(&SidString);
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ memcpy(*UserSidString, SidString.Buffer, SidString.Length);
+
+ RtlFreeUnicodeString(&SidString);
+
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("NWPROVAU: NwpConvertSid got %ws\n", *UserSidString));
+ }
+#endif
+
+ return NO_ERROR;
+}
+
+
+BOOL
+NwpPollWorkstationStart(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This routine polls for the workstation to complete starting.
+ It gives up after 90 seconds.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Returns TRUE if the NetWare workstation is running; FALSE otherwise.
+
+--*/
+{
+ DWORD err;
+ SC_HANDLE ScManager = NULL;
+ SC_HANDLE Service = NULL;
+ SERVICE_STATUS ServiceStatus;
+ DWORD TryCount = 0;
+ BOOL Started = FALSE;
+
+
+ if ((ScManager = OpenSCManager(
+ NULL,
+ NULL,
+ SC_MANAGER_CONNECT
+ )) == (SC_HANDLE) NULL) {
+
+ err = GetLastError();
+
+ KdPrint(("NWPROVAU: NwpPollWorkstationStart: OpenSCManager failed %lu\n",
+ err));
+ goto CleanExit;
+ }
+
+ if ((Service = OpenService(
+ ScManager,
+ NW_WORKSTATION_SERVICE,
+ SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG
+ )) == (SC_HANDLE) NULL) {
+
+ err = GetLastError();
+
+ (void) CloseServiceHandle(ScManager);
+
+ KdPrint(("NWPROVAU: NwpPollWorkstationStart: OpenService failed %lu\n",
+ err));
+ goto CleanExit;
+ }
+
+
+ do {
+ if (! QueryServiceStatus(
+ Service,
+ &ServiceStatus
+ )) {
+
+ err = GetLastError();
+ KdPrint(("NWPROVAU: NwpPollWorkstationStart: QueryServiceStatus failed %lu\n",
+ err));
+ goto CleanExit;
+ }
+
+ if ( (ServiceStatus.dwCurrentState == SERVICE_RUNNING) ||
+ (ServiceStatus.dwCurrentState == SERVICE_CONTINUE_PENDING) ||
+ (ServiceStatus.dwCurrentState == SERVICE_PAUSE_PENDING) ||
+ (ServiceStatus.dwCurrentState == SERVICE_PAUSED) ) {
+
+ Started = TRUE;
+ }
+ else if (ServiceStatus.dwCurrentState == SERVICE_START_PENDING ||
+ (ServiceStatus.dwCurrentState == SERVICE_STOPPED &&
+ ServiceStatus.dwWin32ExitCode == ERROR_SERVICE_NEVER_STARTED)) {
+
+ //
+ // If workstation is stopped and never started before but it's
+ // not auto-start, don't poll.
+ //
+ if (TryCount == 0 &&
+ ServiceStatus.dwCurrentState == SERVICE_STOPPED &&
+ ServiceStatus.dwWin32ExitCode == ERROR_SERVICE_NEVER_STARTED) {
+
+ BYTE OutBuffer[sizeof(QUERY_SERVICE_CONFIGW) + 256];
+ DWORD BytesNeeded;
+
+
+ if (QueryServiceConfigW(
+ Service,
+ (LPQUERY_SERVICE_CONFIGW) OutBuffer,
+ sizeof(OutBuffer),
+ &BytesNeeded
+ )) {
+
+ if (((LPQUERY_SERVICE_CONFIGW) OutBuffer)->dwStartType !=
+ SERVICE_AUTO_START) {
+
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("NWPROVAU: NwpPollWorkstationStart: Not waiting for the workstation to start\n"));
+ }
+#endif
+
+ goto CleanExit;
+ }
+ }
+ else {
+ err = GetLastError();
+ KdPrint(("NWPROVAU: QueryServiceConfig failed %lu, BytesNeeded %lu\n",
+ err, BytesNeeded));
+ }
+
+ }
+
+
+ //
+ // Wait only if the workstation is start pending, or it has not
+ // been attempted to start before.
+ //
+
+ Sleep(5000); // Sleep for 5 seconds before rechecking.
+ TryCount++;
+ }
+ else {
+ goto CleanExit;
+ }
+
+ } while (! Started && TryCount < 18);
+
+ if (Started) {
+
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("NWPROVAU: NetWare workstation is started after we've polled %lu times\n",
+ TryCount));
+ }
+#endif
+
+ }
+
+CleanExit:
+ if (ScManager != NULL) {
+ (void) CloseServiceHandle(ScManager);
+ }
+
+ if (Service != NULL) {
+ (void) CloseServiceHandle(Service);
+ }
+
+ return Started;
+}
+
+
+
+DWORD
+NwpSetCredentialInLsa(
+ IN PLUID LogonId,
+ IN LPWSTR UserName,
+ IN LPWSTR Password
+ )
+/*++
+
+Routine Description:
+
+ This routine calls to the NetWare authentication package to save
+ the user credential.
+
+Arguments:
+
+ LogonId - Supplies the logon ID of the user.
+
+ UserName - Supplies the username.
+
+ Password - Supplies the password.
+
+
+Return Value:
+
+ NO_ERROR or reason for failure.
+
+--*/
+{
+ DWORD status;
+ NTSTATUS ntstatus;
+ NTSTATUS AuthPackageStatus;
+
+ STRING InputString;
+ LSA_OPERATIONAL_MODE SecurityMode = 0;
+
+ HANDLE LsaHandle;
+
+ ULONG AuthPackageId;
+
+ NWAUTH_SET_CREDENTIAL_REQUEST SetCredRequest;
+ PCHAR DummyOutput;
+ ULONG DummyOutputLength;
+
+ UNICODE_STRING PasswordStr;
+ UCHAR EncodeSeed = NW_ENCODE_SEED;
+
+
+ //
+ // Register this process as a logon process so that we can call
+ // NetWare authentication package.
+ //
+ RtlInitString(&InputString, "Microsoft NetWare Credential Manager");
+
+ ntstatus = LsaRegisterLogonProcess(
+ &InputString,
+ &LsaHandle,
+ &SecurityMode
+ );
+
+ if (! NT_SUCCESS(ntstatus)) {
+ KdPrint(("NWPROVAU: NwpSetCredential: LsaRegisterLogonProcess returns x%08lx\n",
+ ntstatus));
+ return RtlNtStatusToDosError(ntstatus);
+ }
+
+ //
+ // Look up the NetWare authentication package
+ //
+ RtlInitString(&InputString, NW_AUTH_PACKAGE_NAME);
+
+ ntstatus = LsaLookupAuthenticationPackage(
+ LsaHandle,
+ &InputString,
+ &AuthPackageId
+ );
+
+ if (! NT_SUCCESS(ntstatus)) {
+ KdPrint(("NWPROVAU: NwpSetCredential: LsaLookupAuthenticationPackage returns x%08lx\n",
+ ntstatus));
+ status = RtlNtStatusToDosError(ntstatus);
+ goto CleanExit;
+ }
+
+ //
+ // Ask authentication package for user information.
+ //
+ SetCredRequest.MessageType = NwAuth_SetCredential;
+ RtlCopyLuid(&SetCredRequest.LogonId, LogonId);
+ wcscpy(SetCredRequest.UserName, UserName);
+ wcscpy(SetCredRequest.Password, Password);
+
+ //
+ // Encode the password.
+ //
+ RtlInitUnicodeString(&PasswordStr, SetCredRequest.Password);
+ RtlRunEncodeUnicodeString(&EncodeSeed, &PasswordStr);
+
+ ntstatus = LsaCallAuthenticationPackage(
+ LsaHandle,
+ AuthPackageId,
+ &SetCredRequest,
+ sizeof(SetCredRequest),
+ (PVOID *) &DummyOutput,
+ &DummyOutputLength,
+ &AuthPackageStatus
+ );
+
+ if (NT_SUCCESS(ntstatus)) {
+ ntstatus = AuthPackageStatus;
+ }
+ if (! NT_SUCCESS(ntstatus)) {
+ KdPrint(("NWPROVAU: NwpSetCredential: LsaCallAuthenticationPackage returns x%08lx\n",
+ ntstatus));
+ status = RtlNtStatusToDosError(ntstatus);
+ }
+ else {
+ status = NO_ERROR;
+ }
+
+CleanExit:
+ (void) LsaDeregisterLogonProcess(LsaHandle);
+
+ return status;
+}
+
+NTSTATUS NwNdsOpenRdrHandle(
+ OUT PHANDLE phNwRdrHandle
+)
+{
+
+ NTSTATUS ntstatus;
+ IO_STATUS_BLOCK IoStatusBlock;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ ACCESS_MASK DesiredAccess = SYNCHRONIZE | GENERIC_READ;
+
+ WCHAR NameStr[] = L"\\Device\\NwRdr";
+ UNICODE_STRING uOpenName;
+
+ //
+ // Prepare the open name.
+ //
+
+ RtlInitUnicodeString( &uOpenName, NameStr );
+
+ //
+ // Set up the object attributes.
+ //
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &uOpenName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL );
+
+ ntstatus = NtOpenFile(
+ phNwRdrHandle,
+ DesiredAccess,
+ &ObjectAttributes,
+ &IoStatusBlock,
+ FILE_SHARE_VALID_FLAGS,
+ FILE_SYNCHRONOUS_IO_NONALERT );
+
+ if ( !NT_ERROR(ntstatus) &&
+ !NT_INFORMATION(ntstatus) &&
+ !NT_WARNING(ntstatus)) {
+
+ return IoStatusBlock.Status;
+
+ }
+
+ return ntstatus;
+}
+
+VOID
+NwpSelectServers(
+ IN HWND DialogHandle,
+ IN PCHANGE_PW_DLG_PARAM Credential
+ )
+/*++
+
+Routine Description:
+
+ This routine displays the dialog for user to select individual trees
+ to change password on. It then changes the password on the selected
+ list. After the password has been changed, it displays a dialog which lists
+ the 3.X bindery servers where the change could not be made.
+
+Arguments:
+
+ DialogHandle - Supplies the handle to display dialog.
+
+ Credential - Provides on input the old and new passwords, and
+ the logged in user's name. Other field are ignored
+ on input and consecuently used within this function.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ INT Result;
+
+ Credential->TreeList = NULL;
+ Credential->UserList = NULL;
+ Credential->Entries = 0;
+ Credential->ChangedOne = FALSE;
+
+ Result = DialogBoxParamW( hmodNW,
+ MAKEINTRESOURCEW(DLG_PW_SELECT_SERVERS),
+ (HWND) DialogHandle,
+ NwpSelectServersDlgProc,
+ (LPARAM) Credential );
+
+ if ( Result == IDOK )
+ {
+ //
+ // Display list of trees (if any) for which password was changed.
+ //
+ DialogBoxParamW( hmodNW,
+ MAKEINTRESOURCEW(DLG_PW_CHANGED),
+ (HWND) DialogHandle,
+ NwpChangePasswordSuccessDlgProc,
+ (LPARAM) Credential );
+
+ if ( Credential->TreeList != NULL )
+ {
+ LocalFree( Credential->TreeList );
+ }
+
+ //
+ // Display a dialog to tell users to use SetPass if they have an
+ // account on a NetWare 3.X server.
+ //
+ NwpMessageBoxError( DialogHandle,
+ IDS_NETWARE_TITLE,
+ IDS_CHANGE_PASSWORD_INFO,
+ 0,
+ NULL,
+ MB_OK );
+ }
+}
diff --git a/private/nw/svcdlls/nwwks/client/makefile b/private/nw/svcdlls/nwwks/client/makefile
new file mode 100644
index 000000000..f0db8e4a7
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS LINE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/nw/svcdlls/nwwks/client/makefile.inc b/private/nw/svcdlls/nwwks/client/makefile.inc
new file mode 100644
index 000000000..30eff4fcf
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/makefile.inc
@@ -0,0 +1 @@
+nwprovau.rc: nwprovau.dlg
diff --git a/private/nw/svcdlls/nwwks/client/ndscont.ico b/private/nw/svcdlls/nwwks/client/ndscont.ico
new file mode 100644
index 000000000..327d5ceac
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/ndscont.ico
Binary files differ
diff --git a/private/nw/svcdlls/nwwks/client/nwapi.c b/private/nw/svcdlls/nwwks/client/nwapi.c
new file mode 100644
index 000000000..ae92b4f68
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/nwapi.c
@@ -0,0 +1,1182 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ api.c
+
+Abstract:
+
+ This module contains exposed APIs that is used by the
+ NetWare Control Panel Applet.
+
+Author:
+
+ Yi-Hsin Sung 15-Jul-1993
+
+Revision History:
+
+ ChuckC 23-Jul-93 Completed the stubs
+
+--*/
+
+#include <nwclient.h>
+#include <nwcanon.h>
+#include <validc.h>
+#include <nwdlg.h>
+#include <nwreg.h>
+#include <nwapi.h>
+#include <ntddnwfs.h>
+
+//
+// forward declare
+//
+
+DWORD
+NwpGetCurrentUserRegKey(
+ IN DWORD DesiredAccess,
+ OUT HKEY *phKeyCurrentUser
+ );
+
+
+
+DWORD
+NwQueryInfo(
+ OUT PDWORD pnPrintOptions,
+ OUT LPWSTR *ppszPreferredSrv
+ )
+/*++
+
+Routine Description:
+ This routine gets the user's preferred server and print options from
+ the registry.
+
+Arguments:
+
+ pnPrintOptions - Receives the user's print option
+
+ ppszPreferredSrv - Receives the user's preferred server
+
+
+Return Value:
+
+ Returns the appropriate Win32 error.
+
+--*/
+{
+
+ HKEY hKeyCurrentUser = NULL;
+ DWORD BufferSize;
+ DWORD BytesNeeded;
+ DWORD PrintOption;
+ DWORD ValueType;
+ LPWSTR PreferredServer ;
+ DWORD err ;
+
+ //
+ // get to right place in registry and allocate dthe buffer
+ //
+ if (err = NwpGetCurrentUserRegKey( KEY_READ, &hKeyCurrentUser))
+ {
+ //
+ // If somebody mess around with the registry and we can't find
+ // the registry, just use the defaults.
+ //
+ *ppszPreferredSrv = NULL;
+ *pnPrintOptions = NW_PRINT_OPTION_DEFAULT;
+ return NO_ERROR;
+ }
+
+ BufferSize = sizeof(WCHAR) * (MAX_PATH + 2) ;
+ PreferredServer = (LPWSTR) LocalAlloc(LPTR, BufferSize) ;
+ if (!PreferredServer)
+ return (GetLastError()) ;
+
+ //
+ // Read PreferredServer value into Buffer.
+ //
+ BytesNeeded = BufferSize ;
+
+ err = RegQueryValueExW( hKeyCurrentUser,
+ NW_SERVER_VALUENAME,
+ NULL,
+ &ValueType,
+ (LPBYTE) PreferredServer,
+ &BytesNeeded );
+
+ if (err != NO_ERROR)
+ {
+ //
+ // set to empty and carry on
+ //
+ PreferredServer[0] = 0;
+ }
+
+ //
+ // Read PrintOption value into PrintOption.
+ //
+ BytesNeeded = sizeof(PrintOption);
+
+ err = RegQueryValueExW( hKeyCurrentUser,
+ NW_PRINTOPTION_VALUENAME,
+ NULL,
+ &ValueType,
+ (LPBYTE) &PrintOption,
+ &BytesNeeded );
+
+ if (err != NO_ERROR)
+ {
+ //
+ // set to default and carry on
+ //
+ PrintOption = NW_PRINT_OPTION_DEFAULT;
+ }
+
+ if (hKeyCurrentUser != NULL)
+ (void) RegCloseKey(hKeyCurrentUser) ;
+ *ppszPreferredSrv = PreferredServer ;
+ *pnPrintOptions = PrintOption ;
+ return NO_ERROR ;
+}
+
+
+
+DWORD
+NwSetInfoInRegistry(
+ IN DWORD nPrintOptions,
+ IN LPWSTR pszPreferredSrv
+ )
+/*++
+
+Routine Description:
+
+ This routine set the user's print option and preferred server into
+ the registry.
+
+Arguments:
+
+ nPrintOptions - Supplies the print option.
+
+ pszPreferredSrv - Supplies the preferred server.
+
+Return Value:
+
+ Returns the appropriate Win32 error.
+
+--*/
+{
+
+ HKEY hKeyCurrentUser = NULL;
+
+ DWORD err = NwpGetCurrentUserRegKey( KEY_WRITE,
+ &hKeyCurrentUser );
+ if (err != NO_ERROR)
+ return err;
+
+ err = RegSetValueEx(hKeyCurrentUser,
+ NW_SERVER_VALUENAME,
+ 0,
+ REG_SZ,
+ (CONST BYTE *)pszPreferredSrv,
+ (wcslen(pszPreferredSrv)+1) * sizeof(WCHAR)) ;
+
+ if (err != NO_ERROR)
+ {
+ if (hKeyCurrentUser != NULL)
+ (void) RegCloseKey(hKeyCurrentUser) ;
+ return err;
+ }
+
+ err = RegSetValueEx(hKeyCurrentUser,
+ NW_PRINTOPTION_VALUENAME,
+ 0,
+ REG_DWORD,
+ (CONST BYTE *)&nPrintOptions,
+ sizeof(nPrintOptions)) ;
+
+ if (hKeyCurrentUser != NULL)
+ (void) RegCloseKey(hKeyCurrentUser) ;
+ return err;
+}
+DWORD
+NwQueryLogonOptions(
+ OUT PDWORD pnLogonScriptOptions
+ )
+/*++
+
+Routine Description:
+ This routine gets the user's Logon script options from the registry.
+
+Arguments:
+
+ pnLogonScriptOptions - Receives the user's Logon script options
+
+
+Return Value:
+
+ Returns the appropriate Win32 error.
+
+--*/
+{
+
+ HKEY hKeyCurrentUser;
+ DWORD BytesNeeded;
+ DWORD LogonScriptOption;
+ DWORD ValueType;
+ DWORD err ;
+
+ //
+ // get to right place in registry and allocate the buffer
+ //
+ if (err = NwpGetCurrentUserRegKey( KEY_READ, &hKeyCurrentUser))
+ {
+ //
+ // If somebody mess around with the registry and we can't find
+ // the registry, assume no.
+ //
+ *pnLogonScriptOptions = NW_LOGONSCRIPT_DEFAULT ;
+ return NO_ERROR;
+ }
+
+ //
+ // Read LogonScriptOption value into LogonScriptOption.
+ //
+ BytesNeeded = sizeof(LogonScriptOption);
+
+ err = RegQueryValueExW( hKeyCurrentUser,
+ NW_LOGONSCRIPT_VALUENAME,
+ NULL,
+ &ValueType,
+ (LPBYTE) &LogonScriptOption,
+ &BytesNeeded );
+
+ if (err != NO_ERROR)
+ {
+ //
+ // default to nothing and carry on
+ //
+ LogonScriptOption = NW_LOGONSCRIPT_DEFAULT;
+ }
+
+ *pnLogonScriptOptions = LogonScriptOption ;
+ return NO_ERROR ;
+}
+
+DWORD
+NwSetLogonOptionsInRegistry(
+ IN DWORD nLogonScriptOptions
+ )
+/*++
+
+Routine Description:
+
+ This routine set the logon script options in the registry.
+
+Arguments:
+
+ nLogonScriptOptions - Supplies the logon options
+
+Return Value:
+
+ Returns the appropriate Win32 error.
+
+--*/
+{
+
+ HKEY hKeyCurrentUser;
+
+ DWORD err = NwpGetCurrentUserRegKey( KEY_WRITE,
+ &hKeyCurrentUser );
+ if (err != NO_ERROR)
+ return err;
+
+ err = RegSetValueEx(hKeyCurrentUser,
+ NW_LOGONSCRIPT_VALUENAME,
+ 0,
+ REG_DWORD,
+ (CONST BYTE *)&nLogonScriptOptions,
+ sizeof(nLogonScriptOptions)) ;
+
+ (void) RegCloseKey( hKeyCurrentUser );
+ return err;
+}
+
+
+DWORD
+NwSetInfoInWksta(
+ IN DWORD nPrintOption,
+ IN LPWSTR pszPreferredSrv
+)
+/*++
+
+Routine Description:
+
+ This routine notifies the workstation service and the redirector
+ about the user's new print option and preferred server.
+
+Arguments:
+
+ nPrintOptions - Supplies the print option.
+
+ pszPreferredSrv - Supplies the preferred server.
+
+Return Value:
+
+ Returns the appropriate Win32 error.
+
+--*/
+{
+ DWORD err;
+
+ RpcTryExcept {
+
+ err = NwrSetInfo( NULL, nPrintOption, pszPreferredSrv );
+
+ }
+ RpcExcept(1) {
+
+ err = NwpMapRpcError(RpcExceptionCode());
+ }
+ RpcEndExcept
+
+ return err;
+
+}
+
+DWORD
+NwSetLogonScript(
+ IN DWORD ScriptOptions
+)
+/*++
+
+Routine Description:
+
+ This routine notifies the workstation service of login script
+ options.
+
+Arguments:
+
+ ScriptOptions - Supplies the options.
+
+Return Value:
+
+ Returns the appropriate Win32 error.
+
+--*/
+{
+ DWORD err;
+
+ RpcTryExcept {
+
+ err = NwrSetLogonScript( NULL, ScriptOptions );
+
+ }
+ RpcExcept(1) {
+
+ err = NwpMapRpcError(RpcExceptionCode());
+ }
+ RpcEndExcept
+
+ return err;
+
+}
+
+
+DWORD
+NwValidateUser(
+ IN LPWSTR pszPreferredSrv
+)
+/*++
+
+Routine Description:
+
+ This routine checks to see if the user can be authenticated on the
+ chosen preferred server.
+
+Arguments:
+
+ pszPreferredSrv - Supplies the preferred server name.
+
+Return Value:
+
+ Returns the appropriate Win32 error.
+
+--*/
+{
+ DWORD err;
+
+ //
+ // Don't need to validate if the preferred server is NULL or empty string
+ //
+ if ( ( pszPreferredSrv == NULL )
+ || ( *pszPreferredSrv == 0 )
+ )
+ {
+ return NO_ERROR;
+ }
+
+ //
+ // See if the name contains any invalid characters
+ //
+ if ( !IS_VALID_SERVER_TOKEN( pszPreferredSrv, wcslen( pszPreferredSrv )))
+ return ERROR_INVALID_NAME;
+
+ RpcTryExcept {
+
+ err = NwrValidateUser( NULL, pszPreferredSrv );
+
+ }
+ RpcExcept(1) {
+
+ err = NwpMapRpcError( RpcExceptionCode() );
+ }
+ RpcEndExcept
+
+ return err;
+}
+
+
+DWORD
+NwpGetCurrentUserRegKey(
+ IN DWORD DesiredAccess,
+ OUT HKEY *phKeyCurrentUser
+ )
+/*++
+
+Routine Description:
+
+ This routine opens the current user's registry key under
+ \HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\NWCWorkstation\Parameters
+
+Arguments:
+
+ DesiredAccess - The access mask to open the key with
+
+ phKeyCurrentUser - Receives the opened key handle
+
+Return Value:
+
+ Returns the appropriate Win32 error.
+
+--*/
+{
+ DWORD err;
+ HKEY hkeyWksta;
+ LPWSTR CurrentUser;
+
+ //
+ // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
+ // \NWCWorkstation\Parameters
+ //
+ err = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ NW_WORKSTATION_REGKEY,
+ REG_OPTION_NON_VOLATILE,
+ KEY_READ,
+ &hkeyWksta
+ );
+
+ if ( err ) {
+ KdPrint(("NWPROVAU: NwGetCurrentUserRegKey open Parameters key unexpected error %lu!\n", err));
+ return err;
+ }
+ //
+ // Get the current user's SID string.
+ //
+ err = NwReadRegValue(
+ hkeyWksta,
+ NW_CURRENTUSER_VALUENAME,
+ &CurrentUser
+ );
+
+
+ if ( err ) {
+ KdPrint(("NWPROVAU: NwGetCurrentUserRegKey read CurrentUser value unexpected error %lu!\n", err));
+ (void) RegCloseKey( hkeyWksta );
+ return err;
+ }
+
+ (void) RegCloseKey( hkeyWksta );
+
+ //
+ // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
+ // \NWCWorkstation\Parameters\Option
+ //
+ err = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ NW_WORKSTATION_OPTION_REGKEY,
+ REG_OPTION_NON_VOLATILE,
+ KEY_READ,
+ &hkeyWksta
+ );
+
+ if ( err ) {
+ KdPrint(("NWPROVAU: NwGetCurrentUserRegKey open Parameters\\Option key unexpected error %lu!\n", err));
+ return err;
+ }
+
+ //
+ // Open current user's key
+ //
+ err = RegOpenKeyExW(
+ hkeyWksta,
+ CurrentUser,
+ REG_OPTION_NON_VOLATILE,
+ DesiredAccess,
+ phKeyCurrentUser
+ );
+
+ if ( err == ERROR_FILE_NOT_FOUND)
+ {
+ DWORD Disposition;
+
+ //
+ // Create <NewUser> key under NWCWorkstation\Parameters\Option
+ //
+ err = RegCreateKeyExW(
+ hkeyWksta,
+ CurrentUser,
+ 0,
+ WIN31_CLASS,
+ REG_OPTION_NON_VOLATILE,
+ DesiredAccess,
+ NULL, // security attr
+ phKeyCurrentUser,
+ &Disposition
+ );
+
+ }
+
+ if ( err ) {
+ KdPrint(("NWPROVAU: NwGetCurrentUserRegKey open or create of Parameters\\Option\\%ws key failed %lu\n", CurrentUser, err));
+ }
+
+ (void) RegCloseKey( hkeyWksta );
+ (void) LocalFree((HLOCAL)CurrentUser) ;
+ return err;
+}
+
+DWORD
+NwEnumGWDevices(
+ LPDWORD Index,
+ LPBYTE Buffer,
+ DWORD BufferSize,
+ LPDWORD BytesNeeded,
+ LPDWORD EntriesRead
+ )
+/*++
+
+Routine Description:
+
+ This routine enumerates the special gateway devices (redirections)
+ that are cureently in use.
+
+Arguments:
+
+ Index - Point to start enumeration. Should be zero for first call.
+
+ Buffer - buffer for return data
+
+ BufferSize - size of buffer in bytes
+
+ BytesNeeded - number of bytes needed to return all the data
+
+ EntriesRead - number of entries read
+
+Return Value:
+
+ Returns the appropriate Win32 error.
+
+--*/
+{
+ DWORD i, err ;
+ LPNETRESOURCE lpNetRes = (LPNETRESOURCE) Buffer ;
+
+ //
+ // call the implementing routine on server side
+ //
+ RpcTryExcept {
+
+ err = NwrEnumGWDevices( NULL,
+ Index,
+ Buffer,
+ BufferSize,
+ BytesNeeded,
+ EntriesRead) ;
+
+ if ( err == NO_ERROR)
+ {
+ //
+ // the change the offsets into real pointers
+ //
+ for (i = 0; i < *EntriesRead; i++)
+ {
+ lpNetRes->lpLocalName =
+ (LPWSTR) (Buffer+(DWORD)lpNetRes->lpLocalName) ;
+ lpNetRes->lpRemoteName =
+ (LPWSTR) (Buffer+(DWORD)lpNetRes->lpRemoteName) ;
+ lpNetRes->lpProvider =
+ (LPWSTR) (Buffer+(DWORD)lpNetRes->lpProvider) ;
+ lpNetRes++ ;
+ }
+ }
+ }
+ RpcExcept(1) {
+
+ err = NwpMapRpcError( RpcExceptionCode() );
+ }
+ RpcEndExcept
+
+ return err ;
+}
+
+
+DWORD
+NwAddGWDevice(
+ LPWSTR DeviceName,
+ LPWSTR RemoteName,
+ LPWSTR AccountName,
+ LPWSTR Password,
+ DWORD Flags
+ )
+/*++
+
+Routine Description:
+
+ This routine adds a gateway redirection.
+
+Arguments:
+
+ DeviceName - the drive to redirect
+
+ RemoteName - the remote network resource to redirect to
+
+ Flags - supplies the options (eg. UpdateRegistry & make this sticky)
+
+Return Value:
+
+ Returns the appropriate Win32 error.
+
+--*/
+{
+ DWORD err;
+
+ RpcTryExcept {
+
+ err = NwrAddGWDevice( NULL,
+ DeviceName,
+ RemoteName,
+ AccountName,
+ Password,
+ Flags) ;
+
+ }
+ RpcExcept(1) {
+
+ err = NwpMapRpcError( RpcExceptionCode() );
+ }
+ RpcEndExcept
+
+ return err;
+}
+
+
+DWORD
+NwDeleteGWDevice(
+ LPWSTR DeviceName,
+ DWORD Flags
+ )
+/*++
+
+Routine Description:
+
+ This routine deletes a gateway redirection.
+
+Arguments:
+
+ DeviceName - the drive to delete
+
+ Flags - supplies the options (eg. UpdateRegistry & make this sticky)
+
+Return Value:
+
+ Returns the appropriate Win32 error.
+
+--*/
+{
+ DWORD err;
+
+ RpcTryExcept {
+
+ err = NwrDeleteGWDevice(NULL, DeviceName, Flags) ;
+
+ }
+ RpcExcept(1) {
+
+ err = NwpMapRpcError( RpcExceptionCode() );
+ }
+ RpcEndExcept
+
+ return err;
+}
+
+
+DWORD
+NwQueryGatewayAccount(
+ LPWSTR AccountName,
+ DWORD AccountNameLen,
+ LPDWORD AccountCharsNeeded,
+ LPWSTR Password,
+ DWORD PasswordLen,
+ LPDWORD PasswordCharsNeeded
+ )
+/*++
+
+Routine Description:
+
+ Query the gateway account info. specifically, the Account name and
+ the passeord stored as an LSA secret.
+
+Arguments:
+
+ AccountName - buffer used to return account name
+
+ AccountNameLen - length of buffer
+
+ Password - buffer used to return account name
+
+ PasswordLen - length of buffer
+
+Return Value:
+
+ Returns the appropriate Win32 error.
+
+--*/
+{
+ DWORD err;
+
+ RpcTryExcept {
+
+ if (AccountName && AccountNameLen)
+ *AccountName = 0 ;
+
+ if (Password && PasswordLen)
+ *Password = 0 ;
+
+ err = NwrQueryGatewayAccount(NULL,
+ AccountName,
+ AccountNameLen,
+ AccountCharsNeeded,
+ Password,
+ PasswordLen,
+ PasswordCharsNeeded) ;
+ }
+ RpcExcept(1) {
+
+ err = NwpMapRpcError( RpcExceptionCode() );
+ }
+ RpcEndExcept
+
+ return err;
+}
+
+DWORD
+NwSetGatewayAccount(
+ LPWSTR AccountName,
+ LPWSTR Password
+ )
+/*++
+
+Routine Description:
+
+ Set the account and password to be used for gateway access.
+
+Arguments:
+
+ AccountName - the account (NULL terminated)
+
+ Password - the password string (NULL terminated)
+
+Return Value:
+
+ Returns the appropriate Win32 error.
+
+--*/
+{
+ DWORD err;
+
+ RpcTryExcept {
+
+ err = NwrSetGatewayAccount( NULL,
+ AccountName,
+ Password);
+ }
+ RpcExcept(1) {
+
+ err = NwpMapRpcError( RpcExceptionCode() );
+ }
+ RpcEndExcept
+
+ return err;
+}
+
+
+DWORD
+NwLogonGatewayAccount(
+ LPWSTR AccountName,
+ LPWSTR Password,
+ LPWSTR Server
+ )
+/*++
+
+Routine Description:
+
+ Logon the SYSTEM process with the specified account/password.
+
+Arguments:
+
+ AccountName - the account (NULL terminated)
+
+ Password - the password string (NULL terminated)
+
+ Server - the server to authenticate against
+
+Return Value:
+
+ Returns the appropriate Win32 error.
+
+--*/
+{
+
+ DWORD err ;
+ LUID SystemId = SYSTEM_LUID ;
+
+ RpcTryExcept {
+
+ (void) NwrLogoffUser(NULL, &SystemId);
+
+ err = NwrLogonUser( NULL,
+ &SystemId,
+ AccountName,
+ Password,
+ Server,
+ NULL,
+ 0 );
+ }
+ RpcExcept(1) {
+
+ err = NwpMapRpcError( RpcExceptionCode() );
+ }
+ RpcEndExcept
+
+ return err ;
+}
+
+NTSTATUS
+NwGetUserNameForServer(
+ PUNICODE_STRING ServerName,
+ PUNICODE_STRING UserName
+ )
+/*++
+
+Routine Description:
+
+ Calls the redir to get the User Name used to connect to the server
+ in question.
+
+Arguments:
+
+ ServerName - the server in question
+
+ UserName - used to return the user name
+
+Return Value:
+
+ Returns the appropriate NTSTATUS
+
+--*/
+{
+ NTSTATUS Status;
+ WCHAR LocalUserName[NW_MAX_USERNAME_LEN];
+ ULONG UserNameLen = sizeof(LocalUserName);
+
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ UNICODE_STRING DriverName;
+ HANDLE RdrHandle = NULL;
+ IO_STATUS_BLOCK IoStatus;
+
+ //
+ // Initialize variables
+ //
+
+ RtlInitUnicodeString( &DriverName, DD_NWFS_DEVICE_NAME_U );
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &DriverName,
+ 0,
+ NULL,
+ NULL
+ );
+
+ //
+ // open handle to the redir
+ //
+
+ Status = NtOpenFile(
+ &RdrHandle,
+ FILE_LIST_DIRECTORY | SYNCHRONIZE,
+ &ObjectAttributes,
+ &IoStatus,
+ FILE_SHARE_READ,
+ 0 // open options
+ );
+
+ if (!NT_SUCCESS(Status) ||
+ !NT_SUCCESS(IoStatus.Status) )
+ {
+ return( Status );
+ }
+
+
+ //
+ // Call the driver to get use the user name
+ //
+
+ Status = NtFsControlFile(
+ RdrHandle,
+ NULL,
+ NULL,
+ NULL,
+ &IoStatus,
+ FSCTL_NWR_GET_USERNAME,
+ ServerName->Buffer,
+ ServerName->Length,
+ LocalUserName,
+ UserNameLen
+ );
+
+ NtClose(RdrHandle);
+
+ if (!NT_SUCCESS(Status))
+ {
+ return(Status);
+ }
+
+ //
+ // copy the info if it fits. set size required and fail otherwise.
+ //
+
+ if (UserName->MaximumLength >= IoStatus.Information)
+ {
+ UserName->Length = (USHORT) IoStatus.Information;
+
+ RtlCopyMemory( UserName->Buffer,
+ LocalUserName,
+ UserNameLen );
+ Status = STATUS_SUCCESS;
+ }
+ else
+ {
+ UserName->Length = (USHORT) IoStatus.Information;
+ Status = STATUS_BUFFER_TOO_SMALL;
+ }
+
+ return(Status);
+}
+
+
+NTSTATUS
+NwEncryptChallenge(
+ IN PUCHAR Challenge,
+ IN ULONG ObjectId,
+ IN OPTIONAL PUNICODE_STRING ServerName,
+ IN OPTIONAL PUNICODE_STRING Password,
+ OUT PUCHAR ChallengeResponse,
+ OUT OPTIONAL PUCHAR SessionKey
+ )
+/*++
+
+Routine Description:
+
+ Calls the redir to encrypt a challenge
+
+Arguments:
+
+ Challenge - Challenge key
+
+ ObjectId - User's object ID
+
+ ServerName - The server to authenticate against
+
+ Password - Password supplied
+
+ ChallengeResponse - Used to return the challenge response
+
+ SessionKey - Used to return the session key
+
+Return Value:
+
+ Returns the appropriate NTSTATUS
+
+--*/
+{
+ NTSTATUS Status;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ UNICODE_STRING DriverName;
+ HANDLE RdrHandle = NULL;
+ IO_STATUS_BLOCK IoStatus;
+ PNWR_GET_CHALLENGE_REQUEST ChallengeRequest = NULL;
+ NWR_GET_CHALLENGE_REPLY ChallengeReply;
+ ULONG ChallengeRequestSize;
+
+ //
+ // Initialize variables
+ //
+
+ RtlInitUnicodeString( &DriverName, DD_NWFS_DEVICE_NAME_U );
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &DriverName,
+ 0,
+ NULL,
+ NULL
+ );
+
+ //
+ // open handle to redirector
+ //
+
+ Status = NtOpenFile(
+ &RdrHandle,
+ FILE_LIST_DIRECTORY | SYNCHRONIZE,
+ &ObjectAttributes,
+ &IoStatus,
+ FILE_SHARE_READ,
+ 0 // open options
+ );
+
+ if (!NT_SUCCESS(Status) ||
+ !NT_SUCCESS(IoStatus.Status) )
+ {
+ return( Status );
+ }
+
+
+
+ ChallengeRequestSize = sizeof(NWR_GET_CHALLENGE_REQUEST) +
+ ((Password != NULL) ? Password->Length : 0) +
+ ((ServerName != NULL) ? ServerName->Length : 0);
+
+ ChallengeRequest = (PNWR_GET_CHALLENGE_REQUEST) RtlAllocateHeap(
+ RtlProcessHeap(),
+ 0,
+ ChallengeRequestSize
+ );
+
+ if (ChallengeRequest == NULL )
+ {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Cleanup;
+ }
+
+ //
+ // Marshall the challenge request structure. Only send servername if
+ // password has not been specified.
+ //
+
+ ChallengeRequest->ObjectId = ObjectId;
+ ChallengeRequest->Flags = 0;
+
+ //
+ // If both password and servername are present, use the password.
+ //
+
+ if ((Password != NULL) && (Password->Length != 0))
+ {
+
+ ChallengeRequest->ServerNameorPasswordLength = Password->Length;
+ RtlCopyMemory(
+ ChallengeRequest->ServerNameorPassword,
+ Password->Buffer,
+ Password->Length
+ );
+ ChallengeRequest->Flags = CHALLENGE_FLAGS_PASSWORD;
+
+ }
+ else if ((ServerName != NULL) && (ServerName->Length != 0))
+ {
+
+ ChallengeRequest->ServerNameorPasswordLength = ServerName->Length;
+
+ RtlCopyMemory(
+ ChallengeRequest->ServerNameorPassword,
+ ServerName->Buffer,
+ ServerName->Length
+ );
+
+ ChallengeRequest->Flags = CHALLENGE_FLAGS_SERVERNAME;
+ }
+
+ RtlCopyMemory(
+ ChallengeRequest->Challenge,
+ Challenge,
+ 8
+ );
+
+ //
+ // Issue FS control to redir to get challenge response
+ //
+
+ Status = NtFsControlFile(
+ RdrHandle,
+ NULL,
+ NULL,
+ NULL,
+ &IoStatus,
+ FSCTL_NWR_CHALLENGE,
+ ChallengeRequest,
+ ChallengeRequestSize,
+ &ChallengeReply,
+ sizeof(ChallengeReply)
+ );
+ if (!NT_SUCCESS(Status) || !NT_SUCCESS(IoStatus.Status)) {
+ goto Cleanup;
+ }
+
+
+ RtlCopyMemory(
+ ChallengeResponse,
+ ChallengeReply.Challenge,
+ 8
+ );
+
+ if (SessionKey != NULL)
+ {
+ RtlCopyMemory(
+ ChallengeResponse,
+ ChallengeReply.Challenge,
+ 8
+ );
+ }
+
+Cleanup:
+
+ if (RdrHandle != NULL)
+ {
+ NtClose(RdrHandle);
+ }
+
+ if (ChallengeRequest != NULL)
+ {
+ RtlFreeHeap(
+ RtlProcessHeap(),
+ 0,
+ ChallengeRequest
+ );
+ }
+
+ return(Status);
+}
+
diff --git a/private/nw/svcdlls/nwwks/client/nwclient.h b/private/nw/svcdlls/nwwks/client/nwclient.h
new file mode 100644
index 000000000..5afc4b87e
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/nwclient.h
@@ -0,0 +1,132 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ nwclient.h
+
+Abstract:
+
+ Common header for Workstation client-side code.
+
+Author:
+
+ Rita Wong (ritaw) 25-Feb-1993
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+--*/
+
+#ifndef _NWCLIENT_INCLUDED_
+#define _NWCLIENT_INCLUDED_
+
+#include <stdlib.h>
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+
+#include <npapi.h>
+
+#include <nwwks.h>
+
+//
+// Debug trace level bits for turning on/off trace statements in the
+// Workstation service
+//
+
+//
+// Initialization and reading info from registry
+//
+#define NW_DEBUG_INIT 0x00000001
+
+//
+// Connection APIs
+//
+#define NW_DEBUG_CONNECT 0x00000002
+
+//
+// Logon APIs
+//
+#define NW_DEBUG_LOGON 0x00000004
+
+//
+// Enum APIs
+//
+#define NW_DEBUG_ENUM 0x00000008
+
+//
+// Other APIs
+//
+#define NW_DEBUG_OTHER 0x00000010
+
+//
+// Print APIs
+//
+#define NW_DEBUG_PRINT 0x00000020
+
+//
+// hInstance of the dll ( nwprovau.dll )
+//
+extern HMODULE hmodNW;
+extern BOOL fIsWinnt;
+
+//
+// Debug stuff
+//
+
+#if DBG
+
+extern DWORD NwProviderTrace;
+
+#define IF_DEBUG(DebugCode) if (NwProviderTrace & NW_DEBUG_ ## DebugCode)
+
+#define STATIC
+
+#else
+
+#define IF_DEBUG(DebugCode) if (FALSE)
+
+#define STATIC static
+
+#endif // DBG
+
+DWORD
+NwpMapRpcError(
+ IN DWORD RpcError
+ );
+
+DWORD
+NwpConvertSid(
+ IN PSID Sid,
+ OUT LPWSTR *UserSidString
+ );
+
+DWORD
+NwpCacheCredentials(
+ IN LPWSTR RemoteName,
+ IN LPWSTR UserName,
+ IN LPWSTR Password
+ );
+
+BOOL
+NwpRetrieveCachedCredentials(
+ IN LPWSTR RemoteName,
+ OUT LPWSTR *UserName,
+ OUT LPWSTR *Password
+ );
+
+#ifndef NT1057
+VOID
+NwCleanupShellExtensions(
+ VOID
+ );
+#endif
+
+#endif // _NWCLIENT_INCLUDED_
diff --git a/private/nw/svcdlls/nwwks/client/nwclsid.h b/private/nw/svcdlls/nwwks/client/nwclsid.h
new file mode 100644
index 000000000..1f383d576
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/nwclsid.h
@@ -0,0 +1,35 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ nwclsid.h
+
+Abstract:
+
+ Contain the class IDs used in the shell extensions.
+
+Author:
+
+ Yi-Hsin Sung (yihsins) 20-Oct-1995
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+--*/
+
+#ifndef _NWCLSID_H_
+#define _NWCLSID_H_
+
+DEFINE_GUID( CLSID_NetWareObjectExt, 0x8e9d6600, 0xf84a, 0x11ce, 0x8d, 0xaa, 0x00, 0xaa, 0x00, 0x4a, 0x56, 0x91 );
+
+DEFINE_GUID( CLSID_NetWareFolderMenuExt, 0xe3f2bac0, 0x099f, 0x11cf, 0x8d, 0xaa, 0x00, 0xaa, 0x00, 0x4a, 0x56, 0x91 );
+
+DEFINE_GUID( CLSID_NetworkNeighborhoodMenuExt, 0x52c68510, 0x09a0, 0x11cf, 0x8d, 0xaa, 0x00, 0xaa, 0x00, 0x4a, 0x56, 0x91 );
+
+
+#endif // _NWCLSID_H_
diff --git a/private/nw/svcdlls/nwwks/client/nwdlg.c b/private/nw/svcdlls/nwwks/client/nwdlg.c
new file mode 100644
index 000000000..d22191af8
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/nwdlg.c
@@ -0,0 +1,3964 @@
+/*++
+
+Copyright (c) 1993, 1994 Microsoft Corporation
+
+Module Name:
+
+ nwdlg.c
+
+Abstract:
+
+ This module contains NetWare Network Provider Dialog code.
+ It contains all functions used in handling the dialogs
+ shown by the provider.
+
+
+Author:
+
+ Yi-Hsin Sung (yihsins) 5-July-1993
+ Split from provider.c
+
+Revision History:
+
+ Rita Wong (ritaw) 10-Apr-1994
+ Added change password functionality.
+
+--*/
+
+#include <nwclient.h>
+#include <nwsnames.h>
+#include <nwcanon.h>
+#include <validc.h>
+#include <nwevent.h>
+#include <ntmsv1_0.h>
+#include <nwdlg.h>
+#include <tstr.h>
+#include <align.h>
+#include <nwpkstr.h>
+
+#include <nwreg.h>
+#include <nwlsa.h>
+#include <nwmisc.h>
+#include <nwauth.h>
+#include <nwutil.h>
+#include <ntddnwfs.h>
+#include <nds.h>
+
+#define NW_ENUM_EXTRA_BYTES 256
+
+#define IS_TREE(p) (*p == TREE_CHAR)
+
+//-------------------------------------------------------------------//
+// //
+// Local Function Prototypes //
+// //
+//-------------------------------------------------------------------//
+
+VOID
+NwpAddToComboBox(
+ IN HWND DialogHandle,
+ IN INT ControlId,
+ IN LPWSTR pszNone OPTIONAL,
+ IN BOOL AllowNone
+ );
+
+BOOL
+WINAPI
+NwpConnectDlgProc(
+ HWND DialogHandle,
+ UINT Message,
+ WPARAM WParam,
+ LPARAM LParam
+ );
+
+VOID
+NwpCenterDialog(
+ IN HWND hwnd
+ );
+
+HWND
+NwpGetParentHwnd(
+ VOID
+ );
+
+VOID
+NwpGetNoneString(
+ LPWSTR pszNone,
+ DWORD cBufferSize
+ );
+
+VOID
+NwpAddNetWareTreeConnectionsToList(
+ IN HWND DialogHandle,
+ IN LPWSTR NtUserName,
+ IN LPDWORD lpdwUserLuid,
+ IN INT ControlId
+ );
+
+VOID
+NwpAddServersToControl(
+ IN HWND DialogHandle,
+ IN INT ControlId,
+ IN UINT Message,
+ IN INT ControlIdMatch OPTIONAL,
+ IN UINT FindMessage
+ );
+
+VOID
+NwpAddTreeNamesToControl(
+ IN HWND DialogHandle,
+ IN INT ControlId,
+ IN UINT Message,
+ IN INT ControlIdMatch OPTIONAL,
+ IN UINT FindMessage
+ );
+
+DWORD
+NwpGetTreesAndChangePw(
+ IN HWND DialogHandle,
+ IN LPWSTR ServerBuf,
+ IN DWORD UserLuid,
+ IN PCHANGE_PW_DLG_PARAM Credential
+ );
+
+BOOL
+WINAPI
+NwpOldPasswordDlgProc(
+ HWND DialogHandle,
+ UINT Message,
+ WPARAM WParam,
+ LPARAM LParam
+ );
+
+BOOL
+WINAPI
+NwpAltUserNameDlgProc(
+ HWND DialogHandle,
+ UINT Message,
+ WPARAM WParam,
+ LPARAM LParam
+ );
+
+VOID
+EnableAddRemove(
+ IN HWND DialogHandle
+ );
+
+
+BOOL
+WINAPI
+NwpLoginDlgProc(
+ HWND DialogHandle,
+ UINT Message,
+ WPARAM WParam,
+ LPARAM LParam
+ )
+/*++
+
+Routine Description:
+
+ This function is the window management message handler which
+ initializes, and reads user input from the login dialog. It also
+ checks that the preferred server name is valid, notifies the user
+ if not, and dismisses the dialog when done.
+
+Arguments:
+
+ DialogHandle - Supplies a handle to the login dialog.
+
+ Message - Supplies the window management message.
+
+Return Value:
+
+ TRUE - the message was processed.
+
+ FALSE - the message was not processed.
+
+--*/
+{
+ static PLOGINDLGPARAM pLoginParam;
+ static WCHAR OrigPassword[NW_MAX_SERVER_LEN + 1];
+ static WCHAR pszNone[64];
+
+ DWORD status = NO_ERROR;
+ DWORD dwNoneIndex = 0;
+ BOOL enableServer = TRUE ;
+
+ switch (Message) {
+
+ case WM_INITDIALOG:
+
+ pLoginParam = (PLOGINDLGPARAM) LParam;
+
+ //
+ // Store the original password
+ //
+ wcscpy( OrigPassword, pLoginParam->Password );
+
+ //
+ // Position dialog
+ //
+ NwpCenterDialog(DialogHandle);
+
+ //
+ // Handle logon script button
+ //
+ if ( pLoginParam->LogonScriptOptions & NW_LOGONSCRIPT_ENABLED )
+ CheckDlgButton( DialogHandle, ID_LOGONSCRIPT, 1 );
+ else
+ CheckDlgButton( DialogHandle, ID_LOGONSCRIPT, 0 );
+
+ //
+ // Username. Just display the original.
+ //
+ SetDlgItemTextW(DialogHandle, ID_USERNAME, pLoginParam->UserName);
+
+ //
+ // Initialize the <None> string.
+ //
+ NwpGetNoneString( pszNone, sizeof( pszNone) );
+
+ //
+ // Set the values in combo-box list.
+ //
+ NwpAddToComboBox(DialogHandle, ID_SERVER, pszNone, TRUE);
+
+ //
+ // Initially, select the last entry in server list, which should
+ // be the <None> entry.
+ //
+ dwNoneIndex = SendDlgItemMessageW(
+ DialogHandle,
+ ID_SERVER,
+ CB_GETCOUNT,
+ 0,
+ 0 );
+
+ if ( dwNoneIndex != CB_ERR && dwNoneIndex > 0 )
+ dwNoneIndex -= 1;
+
+ (void) SendDlgItemMessageW(
+ DialogHandle,
+ ID_SERVER,
+ CB_SETCURSEL,
+ dwNoneIndex == CB_ERR ? 0 : dwNoneIndex,
+ 0 );
+
+ //
+ // Display the previously saved preferred server or context.
+ // Also set appropriate radio button
+ //
+ if ( *(pLoginParam->ServerName) != NW_INVALID_SERVER_CHAR )
+ {
+ if ( !IS_TREE(pLoginParam->ServerName) )
+ {
+ //
+ // regular server
+ //
+ if (SendDlgItemMessageW(
+ DialogHandle,
+ ID_SERVER,
+ CB_SELECTSTRING,
+ 0,
+ (LPARAM) pLoginParam->ServerName
+ ) == CB_ERR) {
+
+ //
+ // Did not find preferred server in the combo-box,
+ // just set the old value in the edit item.
+ //
+ SetDlgItemTextW( DialogHandle, ID_SERVER,
+ pLoginParam->ServerName);
+ }
+ }
+ else
+ {
+ //
+ // we are dealing with *tree\context. break it into
+ // tree and context
+ //
+
+ WCHAR *pszTmp = wcschr(pLoginParam->ServerName + 1, L'\\') ;
+
+ if (pszTmp)
+ *pszTmp = 0 ;
+
+ SetDlgItemTextW( DialogHandle, ID_DEFAULTTREE,
+ pLoginParam->ServerName + 1);
+
+ SetDlgItemTextW( DialogHandle, ID_DEFAULTCONTEXT,
+ pszTmp ? (pszTmp + 1) : L"");
+
+ if (pszTmp)
+ *pszTmp = L'\\' ; // restore the '\'
+
+ enableServer = FALSE ;
+
+ }
+ }
+
+
+ //
+ // enable appropriate buttons
+ //
+ CheckRadioButton( DialogHandle,
+ ID_PREFERREDSERVER_RB,
+ ID_DEFAULTCONTEXT_RB,
+ enableServer ?
+ ID_PREFERREDSERVER_RB :
+ ID_DEFAULTCONTEXT_RB) ;
+ EnableWindow ( GetDlgItem ( DialogHandle,
+ ID_SERVER ),
+ enableServer ) ;
+ EnableWindow ( GetDlgItem ( DialogHandle,
+ ID_DEFAULTTREE ),
+ !enableServer ) ;
+ EnableWindow ( GetDlgItem ( DialogHandle,
+ ID_DEFAULTCONTEXT ),
+ !enableServer ) ;
+ SetFocus ( GetDlgItem ( DialogHandle,
+ enableServer ? ID_SERVER : ID_DEFAULTTREE ) ) ;
+
+ //
+ // Preferred server name is limited to 48 characters.
+ // Tree is limited to 32. We limit context to 256 - MAXTREE - 3
+ //
+ SendDlgItemMessageW(
+ DialogHandle,
+ ID_SERVER,
+ CB_LIMITTEXT,
+ NW_MAX_SERVER_LEN - 1,
+ 0
+ );
+ SendDlgItemMessageW(
+ DialogHandle,
+ ID_DEFAULTTREE,
+ EM_LIMITTEXT,
+ NW_MAX_TREE_LEN - 1,
+ 0
+ );
+ SendDlgItemMessageW(
+ DialogHandle,
+ ID_DEFAULTCONTEXT,
+ EM_LIMITTEXT,
+ (256 - NW_MAX_TREE_LEN) - 4, // -4 for backslashes unc style
+ 0
+ );
+
+ return TRUE;
+
+
+ case WM_COMMAND:
+
+ switch (LOWORD(WParam)) {
+
+ case ID_DEFAULTCONTEXT_RB :
+ if ( (HIWORD(WParam) == BN_CLICKED )
+ || (HIWORD(WParam) == BN_DOUBLECLICKED )
+ )
+ {
+ CheckRadioButton( DialogHandle,
+ ID_PREFERREDSERVER_RB,
+ ID_DEFAULTCONTEXT_RB,
+ ID_DEFAULTCONTEXT_RB) ;
+ EnableWindow ( GetDlgItem ( DialogHandle,
+ ID_SERVER ),
+ FALSE ) ;
+ EnableWindow ( GetDlgItem ( DialogHandle,
+ ID_DEFAULTTREE ),
+ TRUE ) ;
+ EnableWindow ( GetDlgItem ( DialogHandle,
+ ID_DEFAULTCONTEXT ),
+ TRUE ) ;
+ SetFocus ( GetDlgItem ( DialogHandle,
+ ID_DEFAULTTREE ) ) ;
+ }
+ break ;
+
+ case ID_PREFERREDSERVER_RB :
+ if ( (HIWORD(WParam) == BN_CLICKED )
+ || (HIWORD(WParam) == BN_DOUBLECLICKED )
+ )
+ {
+ CheckRadioButton( DialogHandle,
+ ID_PREFERREDSERVER_RB,
+ ID_DEFAULTCONTEXT_RB,
+ ID_PREFERREDSERVER_RB) ;
+ EnableWindow ( GetDlgItem ( DialogHandle,
+ ID_SERVER ),
+ TRUE ) ;
+ EnableWindow ( GetDlgItem ( DialogHandle,
+ ID_DEFAULTTREE ),
+ FALSE ) ;
+ EnableWindow ( GetDlgItem ( DialogHandle,
+ ID_DEFAULTCONTEXT ),
+ FALSE ) ;
+ SetFocus ( GetDlgItem ( DialogHandle, ID_SERVER ) ) ;
+ }
+ break ;
+
+ //
+ // Use the user's original password when
+ // the user types in or selects a new server or context
+ //
+ case ID_DEFAULTTREE:
+ case ID_DEFAULTCONTEXT:
+ if ( HIWORD(WParam) == EN_CHANGE )
+ {
+ wcscpy( pLoginParam->Password, OrigPassword );
+ }
+ break;
+ case ID_SERVER:
+ if ( (HIWORD(WParam) == CBN_EDITCHANGE )
+ || (HIWORD(WParam) == CBN_SELCHANGE )
+ )
+ {
+ wcscpy( pLoginParam->Password, OrigPassword );
+ }
+ break;
+
+ case IDOK: {
+
+ LPWSTR pszLocation = NULL;
+
+ ASSERT(pLoginParam->ServerNameSize >= MAX_PATH) ;
+
+ //
+ // Allocate a buffer big enough to hold the Preferred
+ // Server name or the NDS Tree and context in the form:
+ // *Tree(Context). Therefore we allocate twice the space
+ // needed for a UNICODE Server name.
+ //
+ if ((pszLocation =
+ LocalAlloc(LMEM_ZEROINIT,
+ (pLoginParam->ServerNameSize * sizeof(WCHAR) * 2))
+ ) == NULL )
+ {
+ break;
+ }
+
+ //
+ // Read the server or tree/context and validate its value.
+ //
+ if (IsDlgButtonChecked(DialogHandle, ID_DEFAULTCONTEXT_RB))
+ {
+ //
+ // We are dealing with TREE/CONTEXT. Synthesize string
+ // in "*TREE\CONTEXT" format.
+ //
+ WCHAR *pTmp ;
+ *pszLocation = TREE_CHAR ;
+
+ if (!GetDlgItemTextW(
+ DialogHandle,
+ ID_DEFAULTTREE,
+ pszLocation + 1,
+ pLoginParam->ServerNameSize - 1
+ ))
+ {
+ //
+ // The tree name field was blank!
+ // Prompt user to provide a NDS tree name.
+ //
+ LocalFree( pszLocation );
+
+ (void) NwpMessageBoxError(
+ DialogHandle,
+ IDS_AUTH_FAILURE_TITLE,
+ IDS_TREE_NAME_MISSING,
+ 0,
+ NULL,
+ MB_OK | MB_ICONSTOP
+ );
+
+ //
+ // Put the focus where the user can fix the
+ // invalid tree name.
+ //
+ SetFocus(GetDlgItem(DialogHandle,ID_DEFAULTTREE));
+
+ SendDlgItemMessageW(
+ DialogHandle,
+ ID_DEFAULTTREE,
+ EM_SETSEL,
+ 0,
+ MAKELPARAM(0, -1)
+ );
+
+ return TRUE;
+ }
+
+ pTmp = pszLocation + wcslen( pszLocation );
+ *pTmp++ = L'\\' ;
+
+ if (!GetDlgItemTextW(
+ DialogHandle,
+ ID_DEFAULTCONTEXT,
+ pTmp,
+ pLoginParam->ServerNameSize - (pTmp-pszLocation)
+ ))
+ {
+ //
+ // The context name field was blank!
+ // Prompt user to provide a NDS context name.
+ //
+ LocalFree( pszLocation );
+
+ (void) NwpMessageBoxError(
+ DialogHandle,
+ IDS_AUTH_FAILURE_TITLE,
+ IDS_CONTEXT_MISSING,
+ 0,
+ NULL,
+ MB_OK | MB_ICONSTOP
+ );
+
+ //
+ // Put the focus where the user can fix the
+ // invalid context name.
+ //
+ SetFocus(GetDlgItem(DialogHandle,ID_DEFAULTCONTEXT));
+
+ SendDlgItemMessageW(
+ DialogHandle,
+ ID_DEFAULTCONTEXT,
+ EM_SETSEL,
+ 0,
+ MAKELPARAM(0, -1)
+ );
+
+ return TRUE;
+ }
+ }
+ else
+ {
+ //
+ // Straight server. Just read it in. If we cant get it
+ // or is empty, use <None>.
+ //
+ if (GetDlgItemTextW(
+ DialogHandle,
+ ID_SERVER,
+ pszLocation,
+ pLoginParam->ServerNameSize
+ ) == 0)
+ {
+ wcscpy( pszLocation, pszNone );
+ }
+ }
+
+ if (( lstrcmpi( pszLocation, pszNone ) != 0) &&
+ ( !IS_TREE( pszLocation )) &&
+ ( !IS_VALID_TOKEN( pszLocation,wcslen( pszLocation ))))
+ {
+ //
+ // Put up message box complaining about the bad
+ // server name.
+ //
+ (void) NwpMessageBoxError(
+ DialogHandle,
+ IDS_AUTH_FAILURE_TITLE,
+ IDS_INVALID_SERVER,
+ 0,
+ NULL,
+ MB_OK | MB_ICONSTOP
+ );
+
+ //
+ // Put the focus where the user can fix the
+ // invalid name.
+ //
+ SetFocus(GetDlgItem(DialogHandle, ID_SERVER));
+
+ SendDlgItemMessageW(
+ DialogHandle,
+ ID_SERVER,
+ EM_SETSEL,
+ 0,
+ MAKELPARAM(0, -1)
+ );
+
+ return TRUE;
+ }
+
+ //
+ // If the user select <NONE>,
+ // change it to empty string.
+ //
+ if (lstrcmpi( pszLocation, pszNone) == 0) {
+
+ wcscpy( pszLocation, L"" );
+ }
+
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("\n\t[OK] was pressed\n"));
+ KdPrint(("\tNwrLogonUser\n"));
+ KdPrint(("\tPassword : %ws\n",pLoginParam->Password));
+ KdPrint(("\tServer : %ws\n",pszLocation ));
+ }
+#endif
+
+
+ while(1)
+ {
+ PROMPTDLGPARAM PasswdPromptParam;
+ INT Result ;
+
+ //
+ // make sure this user is logged off
+ //
+ (void) NwrLogoffUser(
+ NULL,
+ pLoginParam->pLogonId
+ );
+
+ status = NwrLogonUser(
+ NULL,
+ pLoginParam->pLogonId,
+ pLoginParam->UserName,
+ pLoginParam->Password,
+ pszLocation,
+ NULL,
+ 0
+ );
+
+
+ if (status != ERROR_INVALID_PASSWORD)
+ break ;
+
+ PasswdPromptParam.UserName =
+ pLoginParam->UserName,
+ PasswdPromptParam.ServerName =
+ pszLocation;
+ PasswdPromptParam.Password =
+ pLoginParam->Password;
+ PasswdPromptParam.PasswordSize =
+ pLoginParam->PasswordSize ;
+
+ Result = DialogBoxParamW(
+ hmodNW,
+ MAKEINTRESOURCEW(DLG_PASSWORD_PROMPT),
+ (HWND) DialogHandle,
+ NwpPasswdPromptDlgProc,
+ (LPARAM) &PasswdPromptParam
+ );
+
+ if (Result == -1 || Result == IDCANCEL)
+ {
+ status = ERROR_INVALID_PASSWORD ;
+ break ;
+ }
+ }
+
+ if (status == NW_PASSWORD_HAS_EXPIRED)
+ {
+ WCHAR szNumber[16] ;
+ DWORD dwMsgId, dwGraceLogins = 0 ;
+ LPWSTR apszInsertStrings[3] ;
+
+ //
+ // get the grace login count
+ //
+ if (!IS_TREE(pszLocation))
+ {
+ DWORD status1 ;
+ status1 = NwGetGraceLoginCount(
+ pszLocation,
+ pLoginParam->UserName,
+ &dwGraceLogins) ;
+ //
+ // if hit error, just dont use the number
+ //
+ if (status1 == NO_ERROR)
+ {
+ dwMsgId = IDS_PASSWORD_HAS_EXPIRED ;
+ wsprintfW(szNumber, L"%ld", dwGraceLogins) ;
+ }
+ else
+ {
+ dwMsgId = IDS_PASSWORD_HAS_EXPIRED1 ;
+ }
+ }
+ else // BUGBUG - should get proper number
+ {
+ dwMsgId = IDS_PASSWORD_HAS_EXPIRED1 ;
+ }
+
+ apszInsertStrings[0] = pszLocation ;
+ apszInsertStrings[1] = szNumber ;
+ apszInsertStrings[2] = NULL ;
+
+ //
+ // put up message on password expiry
+ //
+ (void) NwpMessageBoxIns(
+ (HWND) DialogHandle,
+ IDS_NETWARE_TITLE,
+ dwMsgId,
+ apszInsertStrings,
+ MB_OK | MB_SETFOREGROUND |
+ MB_ICONINFORMATION );
+
+ status = NO_ERROR ;
+ }
+
+ //
+ // Check the LogonScript check box.
+ //
+ if (IsDlgButtonChecked(DialogHandle, ID_LOGONSCRIPT))
+ {
+ pLoginParam->LogonScriptOptions =
+ NW_LOGONSCRIPT_ENABLED | NW_LOGONSCRIPT_4X_ENABLED ;
+ }
+ else
+ {
+ pLoginParam->LogonScriptOptions =
+ NW_LOGONSCRIPT_DISABLED ;
+ }
+
+ if (status == NO_ERROR)
+ {
+ //
+ // Save the logon credential to the registry
+ //
+ NwpSaveLogonCredential(
+ pLoginParam->NewUserSid,
+ pLoginParam->pLogonId,
+ pLoginParam->UserName,
+ pLoginParam->Password,
+ pszLocation
+ );
+
+ // Clear the password buffer
+ RtlZeroMemory( OrigPassword, sizeof( OrigPassword));
+ NwpSaveLogonScriptOptions( pLoginParam->NewUserSid, pLoginParam->LogonScriptOptions );
+
+ EndDialog(DialogHandle, 0);
+ }
+ else
+ {
+ INT nResult;
+ DWORD dwMsgId = IDS_AUTH_FAILURE_WARNING;
+ WCHAR *pszErrorLocation = pszLocation ;
+
+ if (status == ERROR_ACCOUNT_RESTRICTION)
+ {
+ dwMsgId = IDS_AUTH_ACC_RESTRICTION;
+ }
+ if (status == ERROR_SHARING_PAUSED)
+ {
+ status = IDS_LOGIN_DISABLED;
+ }
+
+ if (IS_TREE(pszLocation))
+ {
+ //
+ // Format into nicer string for user
+ //
+ WCHAR *pszTmp = LocalAlloc(LMEM_ZEROINIT,
+ (wcslen(pszLocation)+2) *
+ sizeof(WCHAR)) ;
+ if (pszTmp)
+ {
+
+ pszErrorLocation = pszTmp ;
+
+ //
+ // This code formats the NDS
+ // tree UNC to: Tree(Context)
+ //
+ wcscpy(pszErrorLocation, pszLocation+1) ;
+
+ if (pszTmp = wcschr(pszErrorLocation, L'\\'))
+ {
+ *pszTmp = L'(' ;
+ wcscat(pszErrorLocation, L")") ;
+ }
+ }
+ }
+
+ nResult = NwpMessageBoxError(
+ DialogHandle,
+ IDS_AUTH_FAILURE_TITLE,
+ dwMsgId,
+ status,
+ pszErrorLocation,
+ MB_YESNO | MB_DEFBUTTON2
+ | MB_ICONEXCLAMATION
+ );
+
+ if (pszErrorLocation != pszLocation)
+ {
+ (void) LocalFree(pszErrorLocation) ;
+ }
+
+ if ( nResult == IDYES )
+ {
+ //
+ // Save the logon credential to the registry
+ //
+ NwpSaveLogonCredential(
+ pLoginParam->NewUserSid,
+ pLoginParam->pLogonId,
+ pLoginParam->UserName,
+ pLoginParam->Password,
+ pszLocation
+ );
+
+ // Clear the password buffer
+ RtlZeroMemory( OrigPassword, sizeof( OrigPassword));
+ NwpSaveLogonScriptOptions( pLoginParam->NewUserSid, pLoginParam->LogonScriptOptions );
+
+ EndDialog(DialogHandle, 0);
+ }
+ else
+ {
+ //
+ // Put the focus where the user can fix the
+ // invalid name.
+ //
+ DWORD controlId =
+ IsDlgButtonChecked(DialogHandle,
+ ID_DEFAULTCONTEXT_RB) ?
+ ID_DEFAULTTREE :
+ ID_SERVER ;
+
+ SetFocus(GetDlgItem(DialogHandle, controlId));
+
+ SendDlgItemMessageW(
+ DialogHandle,
+ controlId,
+ EM_SETSEL,
+ 0,
+ MAKELPARAM(0, -1)
+ );
+ }
+ }
+
+ LocalFree( pszLocation );
+ return TRUE;
+ }
+
+
+ case IDCANCEL:
+
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("\n\t[CANCEL] was pressed\n"));
+ KdPrint(("\tLast Preferred Server: %ws\n",
+ pLoginParam->ServerName));
+ KdPrint(("\tLast Password: %ws\n",
+ pLoginParam->Password ));
+ }
+#endif
+
+ if ( *(pLoginParam->ServerName) == NW_INVALID_SERVER_CHAR )
+ {
+ // No preferred server has been set.
+ // Pop up a warning to the user.
+
+ INT nResult = NwpMessageBoxError(
+ DialogHandle,
+ IDS_NETWARE_TITLE,
+ IDS_NO_PREFERRED,
+ 0,
+ NULL,
+ MB_YESNO | MB_ICONEXCLAMATION
+ );
+
+ //
+ // The user chose NO, return to the dialog.
+ //
+ if ( nResult == IDNO )
+ {
+ //
+ // Put the focus where the user can fix the
+ // invalid name.
+ //
+ DWORD controlId =
+ IsDlgButtonChecked(DialogHandle,
+ ID_DEFAULTCONTEXT_RB) ?
+ ID_DEFAULTTREE :
+ ID_SERVER ;
+
+ SetFocus(GetDlgItem(DialogHandle, controlId));
+
+ SendDlgItemMessageW(
+ DialogHandle,
+ controlId,
+ EM_SETSEL,
+ 0,
+ MAKELPARAM(0, -1)
+ );
+
+ return TRUE;
+ }
+
+ //
+ // Save the preferred server as empty string
+ //
+
+ NwpSaveLogonCredential(
+ pLoginParam->NewUserSid,
+ pLoginParam->pLogonId,
+ pLoginParam->UserName,
+ pLoginParam->Password,
+ L""
+ );
+ pLoginParam->LogonScriptOptions = NW_LOGONSCRIPT_DISABLED;
+ NwpSaveLogonScriptOptions( pLoginParam->NewUserSid, pLoginParam->LogonScriptOptions );
+
+ }
+
+ // The user has not logged on to any server.
+ // Logged the user on using NULL as preferred server.
+
+ NwrLogonUser(
+ NULL,
+ pLoginParam->pLogonId,
+ pLoginParam->UserName,
+ pLoginParam->Password,
+ NULL,
+ NULL,
+ 0
+ );
+
+ //
+ // Clear the password buffer
+ RtlZeroMemory( OrigPassword, sizeof( OrigPassword));
+ EndDialog(DialogHandle, 0);
+
+ return TRUE;
+
+
+ case IDHELP:
+ {
+ INT Result ;
+
+ Result = DialogBoxParamW(
+ hmodNW,
+ MAKEINTRESOURCEW(DLG_PREFERRED_SERVER_HELP),
+ (HWND) DialogHandle,
+ NwpHelpDlgProc,
+ (LPARAM) 0
+ );
+
+ // ignore any errors. should not fail, and if does,
+ // nothing we can do.
+
+ return TRUE ;
+
+ }
+
+
+ }
+
+ }
+
+ //
+ // We didn't process this message
+ //
+ return FALSE;
+}
+
+
+INT
+NwpMessageBoxError(
+ IN HWND hwndParent,
+ IN DWORD TitleId,
+ IN DWORD BodyId,
+ IN DWORD Error,
+ IN LPWSTR pszParameter,
+ IN UINT Style
+ )
+/*++
+
+Routine Description:
+
+ This routine puts up a message box error.
+
+Arguments:
+
+ hwndParent - Supplies the handle of the parent window.
+
+ TitleId - Supplies the ID of the title. ( LoadString )
+
+ BodyId - Supplies the ID of the message. ( LoadString )
+
+ Error - If BodyId != 0, then this supplies the ID of the
+ substitution string that will be substituted into
+ the string indicated by BodyId.
+ If BodyId == 0, then this will be the error message.
+ This id is a system error that we will get from FormatMessage
+ using FORMAT_MESSAGE_FROM_SYSTEM.
+
+ pszParameter - A substitution string that will be used as %2 or if
+ Error == 0, this string will be substituted as %1 into
+ the string indicated by BodyId.
+
+ Style - Supplies the style of the MessageBox.
+
+
+Return Value:
+
+ The return value from the MessageBox, 0 if any error is encountered.
+
+--*/
+{
+ DWORD nResult = 0;
+ DWORD nLength;
+
+ WCHAR szTitle[MAX_PATH];
+ WCHAR szBody[MAX_PATH];
+ LPWSTR pszError = NULL;
+ LPWSTR pszBuffer = NULL;
+
+ szTitle[0] = 0;
+ szBody[0] = 0;
+
+ //
+ // Get the Title string
+ //
+ nLength = LoadStringW(
+ hmodNW,
+ TitleId,
+ szTitle,
+ sizeof(szTitle) / sizeof(WCHAR)
+ );
+
+ if ( nLength == 0) {
+ KdPrint(("NWPROVAU: LoadStringW of Title failed with %lu\n",
+ GetLastError()));
+ return 0;
+ }
+
+ //
+ // Get the body string, if BodyId != 0
+ //
+ if ( BodyId != 0 )
+ {
+ nLength = LoadStringW(
+ hmodNW,
+ BodyId,
+ szBody,
+ sizeof(szBody) / sizeof(WCHAR)
+ );
+
+ if ( nLength == 0) {
+ KdPrint(("NWPROVAU: LoadStringW of Body failed with %lu\n",
+ GetLastError()));
+ return 0;
+ }
+ }
+
+ if ( (Error >= IDS_START) && (Error <= IDS_END) ) {
+
+ pszError = (WCHAR *) LocalAlloc(
+ LPTR,
+ 256 * sizeof(WCHAR)) ;
+ if (!pszError)
+ return 0 ;
+
+ nLength = LoadStringW(
+ hmodNW,
+ Error,
+ pszError,
+ 256
+ );
+
+ if ( nLength == 0 ) {
+
+ KdPrint(("NWPROVAU: LoadStringW of Error failed with %lu\n",
+ GetLastError()));
+ (void) LocalFree( (HLOCAL)pszError) ;
+ return 0;
+ }
+ }
+ else if ( Error != 0 ) {
+
+ if ( ( Error == WN_NO_MORE_ENTRIES )
+ || ( Error == ERROR_MR_MID_NOT_FOUND )) {
+
+ //
+ // Handle bogus error from the redirector
+ //
+
+ KdPrint(("NWPROVAU: The NetwareRedirector returned a bogus error as the reason for failure to authenticate. (See Kernel Debugger)\n"));
+ }
+
+ nLength = FormatMessageW(
+ FORMAT_MESSAGE_FROM_SYSTEM
+ | FORMAT_MESSAGE_ALLOCATE_BUFFER,
+ NULL,
+ Error,
+ 0,
+ (LPWSTR) &pszError,
+ MAX_PATH,
+ NULL
+ );
+
+
+ if ( nLength == 0 ) {
+
+ KdPrint(("NWPROVAU: FormatMessageW of Error failed with %lu\n",
+ GetLastError()));
+ return 0;
+ }
+ }
+
+ if ( ( *szBody != 0 )
+ && ( ( pszError != NULL ) || ( pszParameter != NULL) )) {
+
+ LPWSTR aInsertStrings[2];
+ aInsertStrings[0] = pszError? pszError : pszParameter;
+ aInsertStrings[1] = pszError? pszParameter : NULL;
+
+ nLength = FormatMessageW(
+ FORMAT_MESSAGE_FROM_STRING
+ | FORMAT_MESSAGE_ALLOCATE_BUFFER
+ | FORMAT_MESSAGE_ARGUMENT_ARRAY,
+ szBody,
+ 0, // Ignored
+ 0, // Ignored
+ (LPWSTR) &pszBuffer,
+ MAX_PATH,
+ (va_list *) aInsertStrings
+ );
+
+ if ( nLength == 0 ) {
+
+ KdPrint(("NWPROVAU:FormatMessageW(insertstring) failed with %lu\n",
+ GetLastError()));
+
+ if ( pszError != NULL )
+ (void) LocalFree( (HLOCAL) pszError );
+ return 0;
+ }
+
+ }
+ else if ( *szBody != 0 ) {
+
+ pszBuffer = szBody;
+ }
+ else if ( pszError != NULL ) {
+
+ pszBuffer = pszError;
+ }
+ else {
+
+ // We have neither the body nor the error string.
+ // Hence, don't popup the messagebox
+ return 0;
+ }
+
+ if ( pszBuffer != NULL )
+ {
+ nResult = MessageBoxW(
+ hwndParent,
+ pszBuffer,
+ szTitle,
+ Style
+ );
+ }
+
+ if ( ( pszBuffer != NULL )
+ && ( pszBuffer != szBody )
+ && ( pszBuffer != pszError ))
+ {
+ (void) LocalFree( (HLOCAL) pszBuffer );
+ }
+
+ if ( pszError != NULL )
+ (void) LocalFree( (HLOCAL) pszError );
+
+ return nResult;
+}
+
+
+INT
+NwpMessageBoxIns(
+ IN HWND hwndParent,
+ IN DWORD TitleId,
+ IN DWORD MessageId,
+ IN LPWSTR *InsertStrings,
+ IN UINT Style
+ )
+/*++
+
+Routine Description:
+
+ This routine puts up a message box error with array of insert strings
+
+Arguments:
+
+ hwndParent - Supplies the handle of the parent window.
+
+ TitleId - Supplies the ID of the title. ( LoadString )
+
+ MessageId - Supplies the ID of the message. ( LoadString )
+
+ InsertStrings - Array of insert strings for FormatMessage.
+
+ Style - Supplies the style of the MessageBox.
+
+
+Return Value:
+
+ The return value from the MessageBox, 0 if any error is encountered.
+
+--*/
+{
+ DWORD nResult = 0;
+ DWORD nLength;
+
+ WCHAR szTitle[MAX_PATH];
+ WCHAR szBody[MAX_PATH];
+ LPWSTR pszBuffer = NULL;
+
+ szTitle[0] = 0;
+ szBody[0] = 0;
+
+ //
+ // Get the Title string
+ //
+ nLength = LoadStringW(
+ hmodNW,
+ TitleId,
+ szTitle,
+ sizeof(szTitle) / sizeof(szTitle[0])
+ );
+
+ if ( nLength == 0) {
+ return 0;
+ }
+
+ //
+ // Get the message string
+ //
+ nLength = LoadStringW(
+ hmodNW,
+ MessageId,
+ szBody,
+ sizeof(szBody) / sizeof(szBody[0])
+ );
+
+ if ( nLength == 0) {
+ return 0;
+ }
+
+ nLength = FormatMessageW(
+ FORMAT_MESSAGE_FROM_STRING
+ | FORMAT_MESSAGE_ALLOCATE_BUFFER
+ | FORMAT_MESSAGE_ARGUMENT_ARRAY,
+ szBody,
+ 0, // Ignored
+ 0, // Ignored
+ (LPWSTR) &pszBuffer,
+ MAX_PATH,
+ (va_list *) InsertStrings
+ );
+
+ if ( nLength == 0 ) {
+ return 0;
+ }
+
+ if ( pszBuffer != NULL )
+ {
+ nResult = MessageBoxW(
+ hwndParent,
+ pszBuffer,
+ szTitle,
+ Style
+ );
+
+ (void) LocalFree( (HLOCAL) pszBuffer );
+ }
+
+ return nResult;
+}
+
+VOID
+NwpAddServersToControl(
+ IN HWND DialogHandle,
+ IN INT ControlId,
+ IN UINT Message,
+ IN INT ControlIdMatch OPTIONAL,
+ IN UINT FindMessage
+ )
+/*++
+
+Routine Description:
+
+ This function enumerates the servers on the network and adds each
+ server name to the specified Windows control.
+
+ If ControlIdMatch is specified (i.e. non 0), only servers that are
+ not found in ControlIdMatch list are added to the list specified
+ by ControlId.
+
+Arguments:
+
+ DialogHandle - Supplies a handle to the Windows dialog.
+
+ ControlId - Supplies id which specifies the control.
+
+ Message - Supplies the window management message to add string.
+
+ ControlIdMatch - Supplies the control ID which contains server
+ names that should not be in ControlId.
+
+ FindMessage - Supplies the window management message to find
+ string.
+
+Return Value:
+
+ TRUE - the message was processed.
+
+ FALSE - the message was not processed.
+
+--*/
+{
+ DWORD status = ERROR_NO_NETWORK;
+ HANDLE EnumHandle = (HANDLE) NULL;
+
+ LPNETRESOURCE NetR = NULL;
+ LPNETRESOURCEW SavePtr;
+ WCHAR FormattedNameBuf[MAX_NDS_NAME_CHARS];
+
+ LPWSTR lpFormattedName;
+ DWORD dwLength;
+
+ DWORD BytesNeeded = 512;
+ DWORD EntriesRead;
+ DWORD i;
+
+ //
+ // Retrieve the list of servers on the network
+ //
+ status = NPOpenEnum(
+ RESOURCE_GLOBALNET,
+ 0,
+ 0,
+ NULL,
+ &EnumHandle
+ );
+
+ if (status != NO_ERROR) {
+ EnumHandle = (HANDLE) NULL;
+ goto CleanExit;
+ }
+
+ //
+ // Allocate buffer to get servers on the net.
+ //
+ if ((NetR = (LPVOID) LocalAlloc(
+ 0,
+ BytesNeeded
+ )) == NULL) {
+
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ goto CleanExit;
+ }
+
+ do {
+
+ EntriesRead = 0xFFFFFFFF; // Read as many as possible
+
+ status = NPEnumResource(
+ EnumHandle,
+ &EntriesRead,
+ (LPVOID) NetR,
+ &BytesNeeded
+ );
+
+
+ if (status == WN_SUCCESS) {
+
+ SavePtr = NetR;
+
+ for (i = 0; i < EntriesRead; i++, NetR++)
+ {
+ if ( NetR->dwDisplayType == RESOURCEDISPLAYTYPE_TREE)
+ {
+ continue;
+ }
+ else
+ {
+ lpFormattedName = FormattedNameBuf;
+ }
+
+ dwLength = NW_MAX_SERVER_LEN + 1;
+
+ status = NPFormatNetworkName( NetR->lpRemoteName,
+ lpFormattedName,
+ &dwLength,
+ WNFMT_INENUM,
+ 0 );
+
+ lpFormattedName = FormattedNameBuf;
+
+ if ( status != WN_SUCCESS )
+ {
+ continue;
+ }
+
+ if ( dwLength > NW_MAX_SERVER_LEN + 1 )
+ {
+ continue;
+ }
+
+ if (ControlIdMatch != 0) {
+
+ INT Result;
+
+ //
+ // Add the server to list only if it's not found
+ // in the alternate list specified by ControlIdMatch.
+ //
+ Result = SendDlgItemMessageW(
+ DialogHandle,
+ ControlIdMatch,
+ FindMessage,
+ (WPARAM) -1,
+ (LPARAM) lpFormattedName
+ );
+
+ if (Result == LB_ERR) {
+
+ //
+ // Server name not found. Add to list.
+ //
+ SendDlgItemMessageW(
+ DialogHandle,
+ ControlId,
+ Message,
+ 0,
+ (LPARAM) lpFormattedName
+ );
+ }
+ }
+ else {
+
+ //
+ // No alternate list. Just add all servers.
+ //
+ SendDlgItemMessageW(
+ DialogHandle,
+ ControlId,
+ Message,
+ 0,
+ (LPARAM) lpFormattedName
+ );
+ }
+
+ }
+
+ NetR = SavePtr;
+
+ }
+ else if (status != WN_NO_MORE_ENTRIES) {
+
+ status = GetLastError();
+
+ if (status == WN_MORE_DATA) {
+
+ //
+ // Original buffer was too small. Free it and allocate
+ // the recommended size and then some to get as many
+ // entries as possible.
+ //
+
+ (void) LocalFree((HLOCAL) NetR);
+
+ BytesNeeded += NW_ENUM_EXTRA_BYTES;
+
+ if ((NetR = (LPVOID) LocalAlloc(
+ 0,
+ BytesNeeded
+ )) == NULL) {
+
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ goto CleanExit;
+ }
+ }
+ else {
+ goto CleanExit;
+ }
+ }
+
+ } while (status != WN_NO_MORE_ENTRIES);
+
+ if (status == WN_NO_MORE_ENTRIES) {
+ status = NO_ERROR;
+ }
+
+CleanExit:
+
+ if (EnumHandle != (HANDLE) NULL) {
+ (void) NPCloseEnum(EnumHandle);
+ }
+
+ if (NetR != NULL) {
+ (void) LocalFree((HLOCAL) NetR);
+ }
+}
+
+VOID
+NwpAddTreeNamesToControl(
+ IN HWND DialogHandle,
+ IN INT ControlId,
+ IN UINT Message,
+ IN INT ControlIdMatch OPTIONAL,
+ IN UINT FindMessage
+ )
+/*++
+
+Routine Description:
+
+ This function enumerates the NDS tree on the network and adds each
+ tree name to the specified Windows control.
+
+ If ControlIdMatch is specified (i.e. non 0), only trees that are
+ not found in ControlIdMatch list are added to the list specified
+ by ControlId.
+
+Arguments:
+
+ DialogHandle - Supplies a handle to the Windows dialog.
+
+ ControlId - Supplies id which specifies the control.
+
+ Message - Supplies the window management message to add string.
+
+ ControlIdMatch - Supplies the control ID which contains server
+ names that should not be in ControlId.
+
+ FindMessage - Supplies the window management message to find
+ string.
+
+Return Value:
+
+ TRUE - the message was processed.
+
+ FALSE - the message was not processed.
+
+--*/
+{
+ DWORD status = ERROR_NO_NETWORK;
+ HANDLE EnumHandle = (HANDLE) NULL;
+
+ LPNETRESOURCE NetR = NULL;
+ LPNETRESOURCEW SavePtr;
+ WCHAR FormattedNameBuf[MAX_NDS_NAME_CHARS];
+
+ LPWSTR lpFormattedName;
+ DWORD dwLength;
+
+ DWORD BytesNeeded = 512;
+ DWORD EntriesRead;
+ DWORD i;
+
+ //
+ // Retrieve the list of trees on the network
+ //
+ status = NPOpenEnum(
+ RESOURCE_GLOBALNET,
+ 0,
+ 0,
+ NULL,
+ &EnumHandle
+ );
+
+ if (status != NO_ERROR) {
+ EnumHandle = (HANDLE) NULL;
+ goto CleanExit;
+ }
+
+ //
+ // Allocate buffer to get trees on the net.
+ //
+ if ((NetR = (LPVOID) LocalAlloc(
+ 0,
+ BytesNeeded
+ )) == NULL) {
+
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ goto CleanExit;
+ }
+
+ do {
+
+ EntriesRead = 0xFFFFFFFF; // Read as many as possible
+
+ status = NPEnumResource(
+ EnumHandle,
+ &EntriesRead,
+ (LPVOID) NetR,
+ &BytesNeeded
+ );
+
+
+ if (status == WN_SUCCESS) {
+
+ SavePtr = NetR;
+
+ for (i = 0; i < EntriesRead; i++, NetR++)
+ {
+ if ( NetR->dwDisplayType == RESOURCEDISPLAYTYPE_TREE)
+ {
+ lpFormattedName = (LPWSTR) FormattedNameBuf;
+ }
+ else
+ {
+ continue;
+ }
+
+ dwLength = NW_MAX_SERVER_LEN + 1;
+
+ status = NPFormatNetworkName( NetR->lpRemoteName,
+ lpFormattedName,
+ &dwLength,
+ WNFMT_INENUM,
+ 0 );
+
+ lpFormattedName = FormattedNameBuf;
+
+ if ( status != WN_SUCCESS )
+ {
+ continue;
+ }
+
+ if ( dwLength > NW_MAX_SERVER_LEN + 1 )
+ {
+ continue;
+ }
+
+ if (ControlIdMatch != 0) {
+
+ INT Result;
+
+ //
+ // Add the server to list only if it's not found
+ // in the alternate list specified by ControlIdMatch.
+ //
+ Result = SendDlgItemMessageW(
+ DialogHandle,
+ ControlIdMatch,
+ FindMessage,
+ (WPARAM) -1,
+ (LPARAM) lpFormattedName
+ );
+
+ if (Result == LB_ERR) {
+
+ //
+ // Server name not found. Add to list.
+ //
+ SendDlgItemMessageW(
+ DialogHandle,
+ ControlId,
+ Message,
+ 0,
+ (LPARAM) lpFormattedName
+ );
+ }
+ }
+ else {
+
+ //
+ // No alternate list. Just add all servers.
+ //
+ SendDlgItemMessageW(
+ DialogHandle,
+ ControlId,
+ Message,
+ 0,
+ (LPARAM) lpFormattedName
+ );
+ }
+
+ }
+
+ NetR = SavePtr;
+
+ }
+ else if (status != WN_NO_MORE_ENTRIES) {
+
+ status = GetLastError();
+
+ if (status == WN_MORE_DATA) {
+
+ //
+ // Original buffer was too small. Free it and allocate
+ // the recommended size and then some to get as many
+ // entries as possible.
+ //
+
+ (void) LocalFree((HLOCAL) NetR);
+
+ BytesNeeded += NW_ENUM_EXTRA_BYTES;
+
+ if ((NetR = (LPVOID) LocalAlloc(
+ 0,
+ BytesNeeded
+ )) == NULL) {
+
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ goto CleanExit;
+ }
+ }
+ else {
+ goto CleanExit;
+ }
+ }
+
+ } while (status != WN_NO_MORE_ENTRIES);
+
+ if (status == WN_NO_MORE_ENTRIES) {
+ status = NO_ERROR;
+ }
+
+CleanExit:
+
+ if (EnumHandle != (HANDLE) NULL) {
+ (void) NPCloseEnum(EnumHandle);
+ }
+
+ if (NetR != NULL) {
+ (void) LocalFree((HLOCAL) NetR);
+ }
+}
+
+
+VOID
+NwpAddToComboBox(
+ IN HWND DialogHandle,
+ IN INT ControlId,
+ IN LPWSTR pszNone OPTIONAL,
+ IN BOOL AllowNone
+ )
+{
+
+ NwpAddServersToControl(DialogHandle, ControlId, CB_ADDSTRING, 0, 0);
+
+ //
+ // Combo-box will contain at least the <NONE> entry in its list.
+ //
+
+ if ( ARGUMENT_PRESENT(pszNone) && AllowNone) {
+
+ SendDlgItemMessageW(
+ DialogHandle,
+ ControlId,
+ CB_INSERTSTRING,
+ (WPARAM) -1,
+ (LPARAM) pszNone
+ );
+ }
+}
+
+
+DWORD
+NwpGetUserCredential(
+ IN HWND hParent,
+ IN LPWSTR Unc,
+ IN DWORD err,
+ IN LPWSTR pszConnectAsUserName,
+ OUT LPWSTR *UserName,
+ OUT LPWSTR *Password
+ )
+/*++
+
+Routine Description:
+
+ This function puts up a popup dialog for the user, whose default
+ credential denied browse directory access, to enter the correct
+ credential. If this function returns successful, the pointers
+ to memory allocated for the user entered username and password
+ are returned.
+
+Arguments:
+
+ Unc - Supplies the container name in \\Server\Volume format
+ under which the user wants to browse directories.
+
+ UserName - Receives the pointer to memory allocated for the
+ username gotten from the dialog. This pointer must be freed
+ with LocalFree when done.
+
+ Password - Receives the pointer to memory allocated for the
+ password gotten from the dialog. This pointer must be freed
+ with LocalFree when done.
+
+Return Value:
+
+ NO_ERROR or reason for failure.
+
+--*/
+{
+ DWORD status;
+ INT Result;
+ HWND DialogHandle = hParent? hParent : NwpGetParentHwnd();
+ DWORD UserNameSize = NW_MAX_USERNAME_LEN + 1;
+ DWORD PasswordSize = NW_MAX_PASSWORD_LEN + 1;
+ CONNECTDLGPARAM ConnectParam;
+
+ *UserName = NULL;
+ *Password = NULL;
+
+ if (DialogHandle == NULL) {
+ return ERROR_WINDOW_NOT_DIALOG;
+ }
+
+ //
+ // Allocate memory to return UserName and Password
+ //
+ if ((*UserName = (LPVOID) LocalAlloc(
+ 0,
+ UserNameSize * sizeof(WCHAR)
+ )) == NULL)
+ {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ //
+ // Allocate memory to return UserName and Password
+ //
+ if ((*Password = (LPVOID) LocalAlloc(
+ 0,
+ PasswordSize * sizeof(WCHAR)
+ )) == NULL)
+ {
+
+ (void) LocalFree( *UserName );
+ *UserName = NULL;
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ ConnectParam.UncPath = Unc;
+ ConnectParam.ConnectAsUserName = pszConnectAsUserName;
+ ConnectParam.UserName = *UserName;
+ ConnectParam.Password = *Password;
+ ConnectParam.UserNameSize = UserNameSize;
+ ConnectParam.PasswordSize = PasswordSize;
+ ConnectParam.LastConnectionError = err;
+
+ Result = DialogBoxParamW(
+ hmodNW,
+ MAKEINTRESOURCEW(DLG_NETWORK_CREDENTIAL),
+ DialogHandle,
+ NwpConnectDlgProc,
+ (LPARAM) &ConnectParam
+ );
+
+ if ( Result == -1 )
+ {
+ status = GetLastError();
+ KdPrint(("NWPROVAU: NwpGetUserCredential: DialogBox failed %lu\n",
+ status));
+ goto ErrorExit;
+ }
+ else if ( Result == IDCANCEL )
+ {
+ //
+ // Cancel was pressed.
+ //
+ status = WN_CANCEL;
+ goto ErrorExit;
+ }
+
+ return NO_ERROR;
+
+ErrorExit:
+ (void) LocalFree((HLOCAL) *UserName);
+ (void) LocalFree((HLOCAL) *Password);
+ *UserName = NULL;
+ *Password = NULL;
+
+ return status;
+}
+
+
+BOOL
+WINAPI
+NwpConnectDlgProc(
+ HWND DialogHandle,
+ UINT Message,
+ WPARAM WParam,
+ LPARAM LParam
+ )
+/*++
+
+Routine Description:
+
+ This function is the window management message handler which
+ initializes, and reads user input from the dialog put up when the
+ user fails to browse a directory on the default credential.
+
+Arguments:
+
+ DialogHandle - Supplies a handle to display the dialog.
+
+ Message - Supplies the window management message.
+
+ LParam - Supplies the pointer to a buffer which on input
+ contains the \\Server\Volume string under which the user
+ needs to type in a new credential before browsing. On
+ output, this pointer contains the username and password
+ strings entered to the dialog box.
+
+Return Value:
+
+ TRUE - the message was processed.
+
+ FALSE - the message was not processed.
+
+--*/
+{
+ static PCONNECTDLGPARAM pConnectParam;
+
+ switch (Message) {
+
+ case WM_INITDIALOG:
+
+ pConnectParam = (PCONNECTDLGPARAM) LParam;
+
+ //
+ // Position dialog
+ //
+ // NwpCenterDialog(DialogHandle);
+
+
+ //
+ // Display the \\Server\Volume string.
+ //
+ SetDlgItemTextW( DialogHandle,
+ ID_VOLUME_PATH,
+ pConnectParam->UncPath );
+
+ if ( pConnectParam->LastConnectionError == NO_ERROR )
+ {
+ WCHAR szTemp[256];
+
+ if ( LoadString( hmodNW, IDS_CONNECT_NO_ERROR_TEXT,
+ szTemp, sizeof( szTemp )/sizeof(WCHAR)))
+ {
+ SetDlgItemTextW( DialogHandle,
+ ID_CONNECT_TEXT,
+ szTemp );
+ }
+ }
+
+ //
+ // Username is limited to 256 characters.
+ //
+ SendDlgItemMessageW(
+ DialogHandle,
+ ID_CONNECT_AS,
+ EM_LIMITTEXT,
+ pConnectParam->UserNameSize - 1, // minus the space for '\0'
+ 0
+ );
+
+ //
+ // Password is limited to 256 characters.
+ //
+ SendDlgItemMessageW(
+ DialogHandle,
+ ID_CONNECT_PASSWORD,
+ EM_LIMITTEXT,
+ pConnectParam->PasswordSize - 1, // minus the space for '\0'
+ 0
+ );
+
+ //
+ // Display the User name string.
+ //
+ if ( pConnectParam->ConnectAsUserName )
+ {
+ SetDlgItemTextW( DialogHandle,
+ ID_CONNECT_AS,
+ pConnectParam->ConnectAsUserName );
+ }
+
+ return TRUE;
+
+
+ case WM_COMMAND:
+
+ switch (LOWORD(WParam)) {
+
+ case IDOK:
+
+ GetDlgItemTextW(
+ DialogHandle,
+ ID_CONNECT_AS,
+ pConnectParam->UserName,
+ pConnectParam->UserNameSize
+ );
+
+ GetDlgItemTextW(
+ DialogHandle,
+ ID_CONNECT_PASSWORD,
+ pConnectParam->Password,
+ pConnectParam->PasswordSize
+ );
+
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("\n\t[OK] was pressed\n"));
+ KdPrint(("\tUserName : %ws\n",
+ pConnectParam->UserName));
+ KdPrint(("\tPassword : %ws\n",
+ pConnectParam->Password));
+ }
+#endif
+
+ EndDialog(DialogHandle, (INT) IDOK); // OK
+
+ return TRUE;
+
+
+ case IDCANCEL:
+
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("\n\t[CANCEL] was pressed\n"));
+ }
+#endif
+
+ EndDialog(DialogHandle, (INT) IDCANCEL); // CANCEL
+
+ return TRUE;
+
+ case IDHELP:
+
+ WinHelp( DialogHandle,
+ NW_HELP_FILE,
+ HELP_CONTEXT,
+ IDH_DLG_NETWORK_CREDENTIAL_HELP );
+
+ return TRUE;
+ }
+ }
+
+ //
+ // We didn't process this message
+ //
+ return FALSE;
+}
+
+
+
+VOID
+NwpCenterDialog(
+ HWND hwnd
+ )
+/*++
+
+Routine Description:
+
+ This routine positions the dialog centered horizontally and 1/3
+ down the screen vertically. It should be called by the dlg proc
+ when processing the WM_INITDIALOG message. This code is stolen
+ from Visual Basic written by GustavJ.
+
+ Screen
+ -----------------------------
+ | 1/3 Above |
+ | --------------- |
+ | | Dialog | |
+ | | | |
+ | --------------- |
+ | 2/3 Below |
+ | |
+ -----------------------------
+
+Arguments:
+
+ hwnd - Supplies the handle to the dialog.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ RECT rect;
+ LONG nx; // New x
+ LONG ny; // New y
+ LONG width;
+ LONG height;
+
+ GetWindowRect( hwnd, &rect );
+
+ width = rect.right - rect.left;
+ height = rect.bottom - rect.top;
+
+ nx = (GetSystemMetrics(SM_CXSCREEN) - width) / 2;
+ ny = (GetSystemMetrics(SM_CYSCREEN) - height) / 3;
+
+ MoveWindow( hwnd, nx, ny, width, height, FALSE );
+}
+
+
+
+HWND
+NwpGetParentHwnd(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This function gets the parent window handle so that a
+ dialog can be displayed in the current context.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Returns the parent window handle if successful; NULL otherwise.
+
+--*/
+{
+ HWND hwnd;
+ LONG lWinStyle;
+
+
+ //
+ // Get the current focus. This is presumably the button
+ // that was last clicked.
+ //
+ hwnd = GetFocus();
+
+ //
+ // We must make sure that we don't return the window handle
+ // for a child window. Hence, we traverse up the ancestors
+ // of this window handle until we find a non-child window.
+ // Then, we return that handle. If we ever find a NULL window
+ // handle before finding a non-child window, we are unsuccessful
+ // and will return NULL.
+ //
+ // Note on the bit manipulation below. A window is either
+ // an overlapped window, a popup window or a child window.
+ // Hence, we OR together the possible bit combinations of these
+ // possibilities. This should tell us which bits are used in
+ // the window style dword (although we know this becomes 0xC000
+ // today, we don't know if these will ever change later). Then,
+ // we AND the bit combination we with the given window style
+ // dword, and compare the result with WS_CHILD. This tells us
+ // whether or not the given window is a child window.
+ //
+ while (hwnd) {
+
+ lWinStyle = GetWindowLong (hwnd, GWL_STYLE);
+
+ if ((lWinStyle & (WS_OVERLAPPED | WS_POPUP | WS_CHILD)) != WS_CHILD) {
+ return hwnd;
+ }
+
+ hwnd = GetParent(hwnd);
+ }
+
+ return NULL;
+}
+
+
+BOOL
+WINAPI
+NwpPasswdPromptDlgProc(
+ HWND DialogHandle,
+ UINT Message,
+ WPARAM WParam,
+ LPARAM LParam
+ )
+/*++
+
+Routine Description:
+
+ This function is the window management message handler for
+ the change password dialog.
+
+Arguments:
+
+ DialogHandle - Supplies a handle to display the dialog.
+
+ Message - Supplies the window management message.
+
+ LParam - Supplies the pointer to a buffer which on input
+ contains the Server string under which the user
+ needs to type in a new credential before browsing. On
+ output, this pointer contains the username and server
+ strings entered to the dialog box.
+
+Return Value:
+
+ TRUE - the message was processed.
+
+ FALSE - the message was not processed.
+
+--*/
+{
+ LPWSTR UserName;
+ LPWSTR ServerName;
+ static LPWSTR Password;
+ static DWORD PasswordSize ;
+ INT Result ;
+ PPROMPTDLGPARAM DlgParams ;
+ DWORD nLength;
+
+ WCHAR szLocation[MAX_PATH];
+
+ szLocation[0] = 0;
+
+
+ switch (Message) {
+
+ case WM_INITDIALOG:
+
+ DlgParams = (PPROMPTDLGPARAM) LParam;
+ UserName = DlgParams->UserName ;
+ ServerName = DlgParams->ServerName ;
+ Password = DlgParams->Password ;
+ PasswordSize = DlgParams->PasswordSize ;
+
+ ASSERT(ServerName) ;
+
+ //
+ // Position dialog
+ //
+ NwpCenterDialog(DialogHandle);
+
+ //
+ // Get the string "Server" or "Context".
+ //
+ nLength = LoadStringW(
+ hmodNW,
+ IS_TREE(ServerName) ? IDS_CONTEXT : IDS_SERVER,
+ szLocation,
+ sizeof(szLocation) / sizeof(szLocation[0])
+ );
+
+ if ( nLength == 0) {
+ szLocation[0] = 0; // missing text, but still works
+ }
+ SetDlgItemTextW(DialogHandle, ID_LOCATION, szLocation);
+
+ //
+ // Format the server/context string. Note we reuse the
+ // location buffer.
+ //
+ RtlZeroMemory(szLocation, sizeof(szLocation)) ;
+ nLength = wcslen(ServerName) ;
+
+ if ( IS_TREE(ServerName) &&
+ (nLength+1 < (sizeof(szLocation)/sizeof(szLocation[0]))))
+ {
+ //
+ // NDS tree & context
+ //
+ WCHAR *pszTmp ;
+
+ wcscpy(szLocation, ServerName+1) ; // skip the * if tree\context
+
+ if (pszTmp = wcschr(szLocation, L'\\'))
+ {
+ *pszTmp = L'(' ;
+ wcscat(szLocation, L")") ;
+ }
+ }
+ else
+ {
+ wcsncpy(szLocation, ServerName, nLength) ;
+ }
+
+ //
+ // show the user and server names.
+ //
+ SetDlgItemTextW(DialogHandle, ID_SERVER, szLocation);
+ SetDlgItemTextW(DialogHandle, ID_USERNAME, UserName);
+
+ //
+ // set limits
+ //
+ SendDlgItemMessageW( DialogHandle,
+ ID_PASSWORD,
+ EM_LIMITTEXT,
+ PasswordSize - 1, // minus space for '\0'
+ 0 );
+
+ return TRUE;
+
+
+ case WM_COMMAND:
+
+ switch (LOWORD(WParam)) {
+
+
+ case IDHELP:
+
+ DialogBoxParamW(
+ hmodNW,
+ MAKEINTRESOURCEW(DLG_ENTER_PASSWORD_HELP),
+ (HWND) DialogHandle,
+ NwpHelpDlgProc,
+ (LPARAM) 0
+ );
+
+ return TRUE;
+
+ case IDOK:
+
+ Result = GetDlgItemTextW( DialogHandle,
+ ID_PASSWORD,
+ Password,
+ PasswordSize
+ );
+
+ EndDialog(DialogHandle, (INT) IDOK); // OK
+
+ return TRUE;
+
+
+ case IDCANCEL:
+
+
+ EndDialog(DialogHandle, (INT) IDCANCEL); // CANCEL
+
+ return TRUE;
+ }
+ }
+
+ //
+ // We didn't process this message
+ //
+ return FALSE;
+}
+
+
+
+BOOL
+WINAPI
+NwpChangePasswordDlgProc(
+ HWND DialogHandle,
+ UINT Message,
+ WPARAM WParam,
+ LPARAM LParam
+ )
+/*++
+
+Routine Description:
+
+ This function is the window management message handler for
+ the change password dialog.
+
+Arguments:
+
+ DialogHandle - Supplies a handle to display the dialog.
+
+ Message - Supplies the window management message.
+
+ LParam - Supplies the pointer to a buffer which on input
+ contains the Server string under which the user
+ needs to type in a new credential before browsing. On
+ output, this pointer contains the username and server
+ strings entered to the dialog box.
+
+Return Value:
+
+ TRUE - the message was processed.
+
+ FALSE - the message was not processed.
+
+--*/
+{
+ static PCHANGE_PASS_DLG_PARAM pChangePassParam ;
+
+ switch (Message)
+ {
+ case WM_INITDIALOG:
+
+ pChangePassParam = (PCHANGE_PASS_DLG_PARAM) LParam;
+
+ NwpCenterDialog(DialogHandle);
+
+
+ SetDlgItemTextW(DialogHandle, ID_SERVER, pChangePassParam->TreeName);
+ SetDlgItemTextW(DialogHandle, ID_USERNAME, pChangePassParam->UserName);
+
+ //
+ // set limits
+ //
+ SendDlgItemMessageW( DialogHandle,
+ ID_OLD_PASSWORD,
+ EM_LIMITTEXT,
+ NW_MAX_PASSWORD_LEN, // minus space for '\0'
+ 0 );
+
+ SendDlgItemMessageW( DialogHandle,
+ ID_NEW_PASSWORD,
+ EM_LIMITTEXT,
+ NW_MAX_PASSWORD_LEN, // minus space for '\0'
+ 0 );
+
+ SendDlgItemMessageW( DialogHandle,
+ ID_CONFIRM_PASSWORD,
+ EM_LIMITTEXT,
+ NW_MAX_PASSWORD_LEN, // minus space for '\0'
+ 0 );
+
+ return TRUE;
+
+
+ case WM_COMMAND:
+
+ switch (LOWORD(WParam))
+ {
+ case IDHELP:
+
+ DialogBoxParamW(
+ hmodNW,
+ MAKEINTRESOURCEW(DLG_CHANGE_PASSWORD_HELP),
+ (HWND) DialogHandle,
+ NwpHelpDlgProc,
+ (LPARAM) 0
+ );
+
+ return TRUE;
+
+ case IDOK:
+ {
+ INT Result;
+ WCHAR szConfirmPassword[NW_MAX_PASSWORD_LEN + 1];
+ UNICODE_STRING OldPasswordStr;
+ UNICODE_STRING NewPasswordStr;
+ UCHAR EncodeSeed = NW_ENCODE_SEED2;
+
+ Result = GetDlgItemTextW( DialogHandle,
+ ID_OLD_PASSWORD,
+ pChangePassParam->OldPassword,
+ NW_MAX_PASSWORD_LEN
+ );
+
+ Result = GetDlgItemTextW( DialogHandle,
+ ID_NEW_PASSWORD,
+ pChangePassParam->NewPassword,
+ NW_MAX_PASSWORD_LEN
+ );
+
+ Result = GetDlgItemTextW( DialogHandle,
+ ID_CONFIRM_PASSWORD,
+ szConfirmPassword,
+ NW_MAX_PASSWORD_LEN
+ );
+
+ if ( wcscmp( pChangePassParam->NewPassword,
+ szConfirmPassword ) )
+ {
+ //
+ // New and Confirm passwords don't match!
+ //
+ (void) NwpMessageBoxError(
+ DialogHandle,
+ IDS_CHANGE_PASSWORD_TITLE,
+ IDS_CHANGE_PASSWORD_CONFLICT,
+ 0,
+ NULL,
+ MB_OK | MB_ICONSTOP );
+
+ SetDlgItemText( DialogHandle,
+ ID_NEW_PASSWORD,
+ L"" );
+
+ SetDlgItemText( DialogHandle,
+ ID_CONFIRM_PASSWORD,
+ L"" );
+
+ SetFocus( GetDlgItem( DialogHandle,
+ ID_NEW_PASSWORD ));
+
+ return TRUE;
+ }
+
+ RtlInitUnicodeString( &OldPasswordStr,
+ pChangePassParam->OldPassword );
+ RtlInitUnicodeString( &NewPasswordStr,
+ pChangePassParam->NewPassword );
+ RtlRunEncodeUnicodeString(&EncodeSeed, &OldPasswordStr);
+ RtlRunEncodeUnicodeString(&EncodeSeed, &NewPasswordStr);
+
+ EndDialog(DialogHandle, (INT) IDOK); // OK
+
+ return TRUE;
+ }
+
+ case IDCANCEL:
+
+ EndDialog(DialogHandle, (INT) IDCANCEL); // CANCEL
+
+ return TRUE;
+
+ default:
+ return FALSE;
+ }
+ }
+
+ //
+ // We didn't process this message
+ //
+ return FALSE;
+}
+
+
+
+BOOL
+WINAPI
+NwpHelpDlgProc(
+ HWND DialogHandle,
+ UINT Message,
+ WPARAM WParam,
+ LPARAM LParam
+ )
+//
+// This dialog is used for both Help and Question dialogs.
+//
+{
+ switch (Message) {
+
+ case WM_INITDIALOG:
+
+ NwpCenterDialog(DialogHandle);
+ return TRUE;
+
+ case WM_COMMAND:
+
+ switch (LOWORD(WParam))
+ {
+
+ case IDOK:
+ case IDCANCEL:
+ EndDialog(DialogHandle, IDOK);
+ return TRUE;
+
+ case IDYES:
+ EndDialog(DialogHandle, IDYES);
+ return TRUE;
+
+ case IDNO:
+ EndDialog(DialogHandle, IDNO);
+ return TRUE;
+
+ default:
+ return FALSE ;
+ }
+ }
+
+ //
+ // We didn't process this message
+ //
+ return FALSE;
+}
+
+
+
+VOID
+NwpGetNoneString(
+ LPWSTR pszNone,
+ DWORD cBufferSize
+ )
+/*++
+
+Routine Description:
+
+ This function gets the <NONE> string from the resource.
+
+Arguments:
+
+ pszNone - Supplies the buffer to store the string.
+
+ cBufferSize - Supplies the buffer size in bytes.
+
+Return Value:
+
+ None.
+--*/
+{
+ INT TextLength;
+
+
+ TextLength = LoadStringW( hmodNW,
+ IDS_NONE,
+ pszNone,
+ cBufferSize / sizeof( WCHAR) );
+
+ if ( TextLength == 0 )
+ *pszNone = 0;
+}
+
+
+
+VOID
+NwpAddNetWareTreeConnectionsToList(
+ IN HWND DialogHandle,
+ IN LPWSTR NtUserName,
+ IN LPDWORD lpdwUserLuid,
+ IN INT ControlId
+ )
+{
+ DWORD status = NO_ERROR;
+ DWORD BufferSize = 2048; // 2KB Buffer
+ BYTE pBuffer[2048];
+ DWORD EntriesRead;
+ INT Result ;
+
+ status = NwGetConnectedTrees( NtUserName,
+ pBuffer,
+ BufferSize,
+ &EntriesRead,
+ lpdwUserLuid );
+
+ if ( status == NO_ERROR && EntriesRead > 0 )
+ {
+ PCONN_INFORMATION pConnInfo = (PCONN_INFORMATION) pBuffer;
+ WCHAR tempTreeName[NW_MAX_TREE_LEN + 1];
+ DWORD dwSize;
+
+ while ( EntriesRead-- )
+ {
+ dwSize = sizeof( CONN_INFORMATION );
+ dwSize += pConnInfo->HostServerLength;
+ dwSize += pConnInfo->UserNameLength;
+
+ RtlZeroMemory( tempTreeName,
+ ( NW_MAX_TREE_LEN + 1 ) * sizeof(WCHAR) );
+
+ wcsncpy( tempTreeName,
+ pConnInfo->HostServer,
+ pConnInfo->HostServerLength / sizeof(WCHAR) );
+
+ CharUpperW( tempTreeName );
+
+ //
+ // Add the tree name to the list only
+ // if it's not added already.
+ //
+ Result = SendDlgItemMessageW( DialogHandle,
+ ControlId,
+ LB_FINDSTRING,
+ (WPARAM) -1,
+ (LPARAM) tempTreeName );
+
+ if (Result == LB_ERR)
+ {
+ Result = SendDlgItemMessageW( DialogHandle,
+ ControlId,
+ LB_ADDSTRING,
+ 0,
+ (LPARAM) tempTreeName );
+
+ if (Result != LB_ERR)
+ {
+ LPWSTR lpNdsUserName = NULL;
+
+ lpNdsUserName = (LPWSTR) LocalAlloc( LMEM_ZEROINIT,
+ pConnInfo->UserNameLength +
+ sizeof(WCHAR) );
+
+ if ( lpNdsUserName )
+ {
+ wcsncpy( lpNdsUserName,
+ pConnInfo->UserName,
+ pConnInfo->UserNameLength / sizeof(WCHAR) );
+
+ SendDlgItemMessageW( DialogHandle,
+ ControlId,
+ LB_SETITEMDATA,
+ (WPARAM) Result, // index of entry
+ (LPARAM) lpNdsUserName );
+ }
+ }
+ }
+
+ pConnInfo = (PCONN_INFORMATION) ( ((BYTE *)pConnInfo) + dwSize );
+ }
+ }
+ else
+ {
+ *lpdwUserLuid = 0;
+ }
+}
+
+
+
+BOOL
+WINAPI
+NwpChangePasswdDlgProc(
+ HWND DialogHandle,
+ UINT Message,
+ WPARAM WParam,
+ LPARAM LParam
+ )
+/*++
+
+Routine Description:
+
+ This function is the window management message handler for
+ the change password dialog.
+
+Arguments:
+
+ DialogHandle - Supplies a handle to display the dialog.
+
+ Message - Supplies the window management message.
+
+ LParam - Supplies the pointer to a buffer which on input
+ contains the Server string under which the user
+ needs to type in a new credential before browsing. On
+ output, this pointer contains the username and server
+ strings entered to the dialog box.
+
+Return Value:
+
+ TRUE - the message was processed.
+
+ FALSE - the message was not processed.
+
+--*/
+{
+ static LPWSTR UserName;
+ static LPWSTR ServerName;
+ static DWORD UserNameSize ;
+ static DWORD ServerNameSize ;
+ INT Result ;
+ PPASSWDDLGPARAM DlgParams ;
+
+ switch (Message) {
+
+ case WM_INITDIALOG:
+
+ DlgParams = (PPASSWDDLGPARAM) LParam;
+ UserName = DlgParams->UserName ;
+ ServerName = DlgParams->ServerName ;
+ UserNameSize = DlgParams->UserNameSize ;
+ ServerNameSize = DlgParams->ServerNameSize ;
+
+ //
+ // Position dialog
+ //
+ NwpCenterDialog(DialogHandle);
+
+
+ //
+ // setup the default user and server names
+ //
+ SetDlgItemTextW(DialogHandle, ID_SERVER, ServerName);
+ SetDlgItemTextW(DialogHandle, ID_USERNAME, UserName);
+
+ //
+ // Username is limited to 256 characters.
+ //
+ SendDlgItemMessageW(DialogHandle,
+ ID_USERNAME,
+ EM_LIMITTEXT,
+ UserNameSize - 1, // minus space for '\0'
+ 0 );
+
+ //
+ // Server is limited to 256 characters.
+ //
+ SendDlgItemMessageW( DialogHandle,
+ ID_SERVER,
+ EM_LIMITTEXT,
+ ServerNameSize - 1, // minus space for '\0'
+ 0 );
+
+ //
+ // Add trees to list
+ //
+ NwpAddToComboBox( DialogHandle,
+ ID_SERVER,
+ NULL,
+ FALSE ) ;
+
+ return TRUE;
+
+
+ case WM_COMMAND:
+
+ switch (LOWORD(WParam)) {
+
+ case IDOK:
+
+ Result = GetDlgItemTextW( DialogHandle,
+ ID_USERNAME,
+ UserName,
+ UserNameSize );
+
+ Result = GetDlgItemTextW( DialogHandle,
+ ID_SERVER,
+ ServerName,
+ ServerNameSize );
+
+ EndDialog(DialogHandle, (INT) IDOK); // OK
+
+ return TRUE;
+
+
+ case IDCANCEL:
+
+ EndDialog(DialogHandle, (INT) IDCANCEL); // CANCEL
+
+ return TRUE;
+ }
+ }
+
+ //
+ // We didn't process this message
+ //
+ return FALSE;
+}
+
+
+
+BOOL
+WINAPI
+NwpOldPasswordDlgProc(
+ HWND DialogHandle,
+ UINT Message,
+ WPARAM WParam,
+ LPARAM LParam
+ )
+//
+// This dialog lets the user retype the old password for a specific
+// server/tree.
+//
+{
+ static POLD_PW_DLG_PARAM OldPwParam;
+
+
+ switch (Message) {
+
+ case WM_INITDIALOG:
+
+ OldPwParam = (POLD_PW_DLG_PARAM) LParam;
+
+ NwpCenterDialog(DialogHandle);
+
+ SetDlgItemTextW(DialogHandle, ID_SERVER, OldPwParam->FailedServer);
+
+ SendDlgItemMessageW(
+ DialogHandle,
+ ID_PASSWORD,
+ EM_LIMITTEXT,
+ NW_MAX_PASSWORD_LEN,
+ 0
+ );
+
+ return TRUE;
+
+ case WM_COMMAND:
+
+ switch (LOWORD(WParam))
+ {
+
+ case IDCANCEL:
+ EndDialog(DialogHandle, IDCANCEL);
+ return TRUE;
+
+ case IDOK:
+ {
+ UCHAR EncodeSeed = NW_ENCODE_SEED2;
+ UNICODE_STRING PasswordStr;
+
+
+ RtlZeroMemory(
+ OldPwParam->OldPassword,
+ NW_MAX_PASSWORD_LEN * sizeof(WCHAR)
+ );
+
+ GetDlgItemTextW(
+ DialogHandle,
+ ID_PASSWORD,
+ OldPwParam->OldPassword,
+ NW_MAX_PASSWORD_LEN
+ );
+
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("NWPROVAU: Retyped password %ws\n",
+ OldPwParam->OldPassword));
+ }
+#endif
+ RtlInitUnicodeString(&PasswordStr, OldPwParam->OldPassword);
+ RtlRunEncodeUnicodeString(&EncodeSeed, &PasswordStr);
+
+ EndDialog(DialogHandle, IDOK);
+ return TRUE;
+ }
+
+ case IDHELP:
+
+ DialogBoxParamW(
+ hmodNW,
+ MAKEINTRESOURCEW(DLG_ENTER_OLD_PW_HELP),
+ (HWND) DialogHandle,
+ NwpHelpDlgProc,
+ (LPARAM) 0
+ );
+ return TRUE;
+
+ default:
+ return FALSE;
+ }
+ }
+
+ //
+ // We didn't process this message
+ //
+ return FALSE;
+}
+
+
+
+BOOL
+WINAPI
+NwpAltUserNameDlgProc(
+ HWND DialogHandle,
+ UINT Message,
+ WPARAM WParam,
+ LPARAM LParam
+ )
+//
+// This dialog lets the user retype an alternate username for a specific
+// server/tree.
+//
+{
+ static PUSERNAME_DLG_PARAM UserNameParam;
+
+ switch (Message)
+ {
+ case WM_INITDIALOG:
+
+ UserNameParam = (PUSERNAME_DLG_PARAM) LParam;
+
+ NwpCenterDialog(DialogHandle);
+
+ //
+ // Display the server/tree.
+ //
+ SetDlgItemTextW(
+ DialogHandle,
+ ID_SERVER,
+ UserNameParam->TreeServerName
+ );
+
+ //
+ // Username is limited to 256 characters.
+ //
+ SendDlgItemMessageW(
+ DialogHandle,
+ ID_USERNAME,
+ EM_LIMITTEXT,
+ 256,
+ 0
+ );
+
+ SetDlgItemTextW(
+ DialogHandle,
+ ID_USERNAME,
+ UserNameParam->UserName
+ );
+
+ return TRUE;
+
+ case WM_COMMAND:
+
+ switch (LOWORD(WParam))
+ {
+
+ case IDCANCEL:
+ EndDialog(DialogHandle, IDCANCEL);
+ return TRUE;
+
+ case IDOK:
+ {
+ RtlZeroMemory(
+ UserNameParam->UserName,
+ 256 * sizeof(WCHAR)
+ );
+
+ GetDlgItemTextW(
+ DialogHandle,
+ ID_USERNAME,
+ UserNameParam->UserName,
+ 256
+ );
+
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("NWPROVAU: Retyped username %ws\n",
+ UserNameParam->UserName));
+ }
+#endif
+
+ EndDialog(DialogHandle, IDOK);
+ return TRUE;
+ }
+
+ case IDHELP:
+
+ DialogBoxParamW(
+ hmodNW,
+ MAKEINTRESOURCEW(DLG_ENTER_ALT_UN_HELP),
+ (HWND) DialogHandle,
+ NwpHelpDlgProc,
+ (LPARAM) 0
+ );
+ return TRUE;
+
+ default:
+ return FALSE;
+ }
+ }
+
+ //
+ // We didn't process this message
+ //
+ return FALSE;
+}
+
+
+VOID
+EnableAddRemove(
+ IN HWND DialogHandle
+ )
+/*++
+
+Routine Description:
+
+ This function enables and disables Add and Remove buttons
+ based on list box selections.
+
+Arguments:
+
+ DialogHandle - Supplies a handle to the windows dialog.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ INT cSel;
+
+
+ cSel = SendDlgItemMessageW(
+ DialogHandle,
+ ID_INACTIVE_LIST,
+ LB_GETSELCOUNT,
+ 0,
+ 0
+ );
+ EnableWindow(GetDlgItem(DialogHandle, ID_ADD), cSel != 0);
+
+ cSel = SendDlgItemMessageW(
+ DialogHandle,
+ ID_ACTIVE_LIST,
+ LB_GETSELCOUNT,
+ 0,
+ 0
+ );
+ EnableWindow(GetDlgItem(DialogHandle, ID_REMOVE), cSel != 0);
+}
+
+
+
+
+BOOL
+WINAPI
+NwpSelectServersDlgProc(
+ HWND DialogHandle,
+ UINT Message,
+ WPARAM WParam,
+ LPARAM LParam
+ )
+/*++
+
+Routine Description:
+
+ This routine displays two listboxes--an active list which includes
+ the trees which the user is currently attached to, and an inactive
+ list which displays the rest of the trees on the net. The user
+ can select trees and move them back and forth between the list
+ boxes. When OK is selected, the password is changed on the trees
+ in the active listbox.
+
+Arguments:
+
+ DialogHandle - Supplies a handle to the login dialog.
+
+ Message - Supplies the window management message.
+
+ LParam - Supplies the user credential: username, old password and
+ new password. The list of trees from the active listbox
+ and the number of entries are returned.
+
+Return Value:
+
+ TRUE - the message was processed.
+
+ FALSE - the message was not processed.
+
+--*/
+{
+ WCHAR szServer[NW_MAX_SERVER_LEN + 1];
+ static PCHANGE_PW_DLG_PARAM Credential;
+ DWORD status;
+ DWORD UserLuid = 0;
+ DWORD ActiveListBoxCount;
+ DWORD InactiveListBoxCount;
+
+ switch (Message) {
+
+ case WM_INITDIALOG:
+
+ //
+ // Get the user credential passed in.
+ //
+ Credential = (PCHANGE_PW_DLG_PARAM) LParam;
+
+ //
+ // Position dialog
+ //
+ NwpCenterDialog(DialogHandle);
+
+ //
+ // Display the username.
+ //
+ SetDlgItemTextW(
+ DialogHandle,
+ ID_USERNAME,
+ Credential->UserName
+ );
+
+ //
+ // Display current NetWare tree connections in the active box.
+ //
+ NwpAddNetWareTreeConnectionsToList(
+ DialogHandle,
+ Credential->UserName,
+ &UserLuid,
+ ID_ACTIVE_LIST
+ );
+
+ //
+ // Display all trees in inactive list box.
+ //
+ NwpAddTreeNamesToControl(
+ DialogHandle,
+ ID_INACTIVE_LIST,
+ LB_ADDSTRING,
+ ID_ACTIVE_LIST,
+ LB_FINDSTRING
+ );
+
+ //
+ // Highlight the first entry of the inactive list.
+ //
+ SetFocus(GetDlgItem(DialogHandle, ID_INACTIVE_LIST));
+ SendDlgItemMessageW(
+ DialogHandle,
+ ID_INACTIVE_LIST,
+ LB_SETSEL,
+ TRUE,
+ 0
+ );
+
+ EnableAddRemove(DialogHandle);
+
+ ActiveListBoxCount = SendDlgItemMessageW( DialogHandle,
+ ID_ACTIVE_LIST,
+ LB_GETCOUNT,
+ 0,
+ 0 );
+
+ InactiveListBoxCount = SendDlgItemMessageW( DialogHandle,
+ ID_INACTIVE_LIST,
+ LB_GETCOUNT,
+ 0,
+ 0 );
+
+ if ( ActiveListBoxCount == 0 &&
+ InactiveListBoxCount == 0 )
+ {
+ (void) NwpMessageBoxError( DialogHandle,
+ IDS_NETWARE_TITLE,
+ IDS_NO_TREES_DETECTED,
+ 0,
+ NULL,
+ MB_OK );
+
+ EndDialog(DialogHandle, (INT) IDOK);
+ }
+
+ return TRUE;
+
+ case WM_COMMAND:
+
+ switch (LOWORD(WParam))
+ {
+ case IDOK:
+ {
+ if ((status = NwpGetTreesAndChangePw(
+ DialogHandle,
+ szServer,
+ UserLuid,
+ Credential
+ ) != NO_ERROR))
+ {
+ //
+ // System error: e.g. out of memory error.
+ //
+ (void) NwpMessageBoxError(
+ DialogHandle,
+ IDS_CHANGE_PASSWORD_TITLE,
+ 0,
+ status,
+ NULL,
+ MB_OK | MB_ICONSTOP );
+
+ EndDialog(DialogHandle, (INT) -1);
+ return TRUE;
+ }
+
+ EndDialog(DialogHandle, (INT) IDOK);
+ return TRUE;
+ }
+
+ case IDCANCEL:
+
+ EndDialog(DialogHandle, (INT) IDCANCEL);
+ return TRUE;
+
+
+ case IDHELP:
+
+ DialogBoxParamW(
+ hmodNW,
+ MAKEINTRESOURCEW(DLG_PW_SELECT_SERVERS_HELP),
+ (HWND) DialogHandle,
+ NwpHelpDlgProc,
+ (LPARAM) 0
+ );
+
+ return TRUE;
+
+
+
+ case ID_ACTIVE_LIST:
+ //
+ // When Remove is pressed the highlights follows
+ // the selected entries over to the other
+ // list box.
+ //
+ if (HIWORD(WParam) == LBN_SELCHANGE) {
+ //
+ // Unselect the other listbox
+ //
+ SendDlgItemMessageW(
+ DialogHandle,
+ ID_INACTIVE_LIST,
+ LB_SETSEL,
+ FALSE,
+ (LPARAM) -1
+ );
+
+ EnableAddRemove(DialogHandle);
+ }
+
+ return TRUE;
+
+ case ID_INACTIVE_LIST:
+
+ //
+ // When Add is pressed the highlights follows
+ // the selected entries over to the other
+ // list box.
+ //
+ if (HIWORD(WParam) == LBN_SELCHANGE) {
+ //
+ // Unselect the other listbox
+ //
+ SendDlgItemMessageW(
+ DialogHandle,
+ ID_ACTIVE_LIST,
+ LB_SETSEL,
+ FALSE,
+ (LPARAM) -1
+ );
+
+ EnableAddRemove(DialogHandle);
+ }
+
+ return TRUE;
+
+ case ID_ADD:
+ case ID_REMOVE:
+ {
+ INT idFrom;
+ INT idTo;
+ INT cSel;
+ INT SelItem;
+ INT iNew;
+ HWND hwndActiveList;
+ HWND hwndInactiveList;
+
+ hwndActiveList = GetDlgItem(DialogHandle, ID_ACTIVE_LIST);
+ hwndInactiveList = GetDlgItem(DialogHandle, ID_INACTIVE_LIST);
+
+ //
+ // Set to NOREDRAW to TRUE
+ //
+ SetWindowLong(hwndActiveList, GWL_STYLE,
+ GetWindowLong(hwndActiveList, GWL_STYLE) | LBS_NOREDRAW);
+ SetWindowLong(hwndInactiveList, GWL_STYLE,
+ GetWindowLong(hwndInactiveList, GWL_STYLE) | LBS_NOREDRAW);
+
+ if (LOWORD(WParam) == ID_ADD)
+ {
+ idFrom = ID_INACTIVE_LIST;
+ idTo = ID_ACTIVE_LIST;
+ }
+ else
+ {
+ idFrom = ID_ACTIVE_LIST;
+ idTo = ID_INACTIVE_LIST;
+ }
+
+ //
+ // Move current selection from idFrom to idTo
+ //
+
+ //
+ // Loop terminates when selection count is zero
+ //
+ for (;;) {
+ //
+ // Get count of selected strings
+ //
+ cSel = SendDlgItemMessageW(
+ DialogHandle,
+ idFrom,
+ LB_GETSELCOUNT,
+ 0,
+ 0
+ );
+
+ if (cSel == 0) {
+ //
+ // No more selection
+ //
+ break;
+ }
+
+ //
+ // To avoid flickering as strings are added and
+ // removed from listboxes, no redraw is set for
+ // both listboxes until we are transfering the
+ // last entry, in which case we reenable redraw
+ // so that both listboxes are updated once.
+ //
+ if (cSel == 1) {
+
+ SetWindowLong(
+ hwndActiveList,
+ GWL_STYLE,
+ GetWindowLong(hwndActiveList, GWL_STYLE) & ~LBS_NOREDRAW
+ );
+
+ SetWindowLong(
+ hwndInactiveList,
+ GWL_STYLE,
+ GetWindowLong(hwndInactiveList, GWL_STYLE) & ~LBS_NOREDRAW
+ );
+ }
+
+ //
+ // Get index of first selected item
+ //
+ SendDlgItemMessageW(
+ DialogHandle,
+ idFrom,
+ LB_GETSELITEMS,
+ 1,
+ (LPARAM) &SelItem
+ );
+
+ //
+ // Get server name from list
+ //
+ SendDlgItemMessageW(
+ DialogHandle,
+ idFrom,
+ LB_GETTEXT,
+ (WPARAM) SelItem,
+ (LPARAM) (LPWSTR) szServer
+ );
+
+ //
+ // Remove entry from list
+ //
+ SendDlgItemMessageW(
+ DialogHandle,
+ idFrom,
+ LB_DELETESTRING,
+ (WPARAM) SelItem,
+ 0
+ );
+
+ //
+ // Add entry to list
+ //
+ iNew = SendDlgItemMessageW(
+ DialogHandle,
+ idTo,
+ LB_ADDSTRING,
+ 0,
+ (LPARAM) (LPWSTR) szServer
+ );
+
+ //
+ // Select the new item
+ //
+ if (iNew != LB_ERR) {
+ SendDlgItemMessageW(
+ DialogHandle,
+ idTo,
+ LB_SETSEL,
+ TRUE,
+ iNew
+ );
+ }
+
+ } // for
+
+ EnableAddRemove(DialogHandle);
+
+ } // ID_ADD or ID_REMOVE
+ }
+
+ }
+
+ //
+ // We didn't process this message
+ //
+ return FALSE;
+}
+
+DWORD
+NwpGetTreesAndChangePw(
+ IN HWND DialogHandle,
+ IN LPWSTR ServerBuf,
+ IN DWORD UserLuid,
+ IN PCHANGE_PW_DLG_PARAM Credential
+ )
+/*++
+
+Routine Description:
+
+ This routine gets the selected trees from the active list box
+ and asks the redirector to change password on them. If a failure
+ is encountered when changing password on a tree, we pop up appropriate
+ dialogs to see if user can fix problem.
+
+Arguments:
+
+ DialogHandle - Supplies a handle to the login dialog.
+
+Return Value:
+
+ TRUE - the message was processed.
+
+ FALSE - the message was not processed.
+
+--*/
+{
+ DWORD status;
+ HCURSOR Cursor;
+ WCHAR tempOldPassword[NW_MAX_PASSWORD_LEN + 1];
+ WCHAR tempNewPassword[NW_MAX_PASSWORD_LEN + 1];
+ WCHAR tempUserName[MAX_NDS_NAME_CHARS];
+
+ //
+ // Turn cursor into hourglass
+ //
+ Cursor = LoadCursor(NULL, IDC_WAIT);
+ if (Cursor != NULL) {
+ SetCursor(Cursor);
+ ShowCursor(TRUE);
+ }
+
+ Credential->ChangedOne = FALSE;
+ Credential->TreeList = NULL;
+ Credential->UserList = NULL;
+
+ //
+ // Get the number of trees we have to change password on.
+ //
+ Credential->Entries = SendDlgItemMessageW(
+ DialogHandle,
+ ID_ACTIVE_LIST,
+ LB_GETCOUNT,
+ 0,
+ 0 );
+
+ if (Credential->Entries != 0) {
+
+ DWORD Entries; // Number of entries in remaining list
+ DWORD FullIndex; // Index to the whole tree list
+ DWORD i;
+ DWORD BytesNeeded = sizeof(LPWSTR) * Credential->Entries +
+ (NW_MAX_SERVER_LEN + 1) * sizeof(WCHAR) * Credential->Entries;
+ LPBYTE FixedPortion;
+ LPWSTR EndOfVariableData;
+ INT Result;
+
+ Entries = Credential->Entries;
+ Credential->TreeList = LocalAlloc(0, BytesNeeded);
+ Credential->UserList = LocalAlloc(0,
+ sizeof(LPWSTR) * Credential->Entries);
+
+ if (Credential->TreeList == NULL)
+ {
+ KdPrint(("NWPROVAU: No memory to change password\n"));
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ if (Credential->UserList == NULL)
+ {
+ KdPrint(("NWPROVAU: No memory to change password\n"));
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ FixedPortion = (LPBYTE) Credential->TreeList;
+ EndOfVariableData = (LPWSTR) ((DWORD) FixedPortion +
+ ROUND_DOWN_COUNT(BytesNeeded, ALIGN_DWORD));
+
+ for (i = 0; i < Entries; i++)
+ {
+ //
+ // Read the user selected list of servers from the dialog.
+ //
+
+ SendDlgItemMessageW(
+ DialogHandle,
+ ID_ACTIVE_LIST,
+ LB_GETTEXT,
+ (WPARAM) i,
+ (LPARAM) (LPWSTR) ServerBuf );
+
+ NwlibCopyStringToBuffer(
+ ServerBuf,
+ wcslen(ServerBuf),
+ (LPCWSTR) FixedPortion,
+ &EndOfVariableData,
+ &(Credential->TreeList)[i] );
+
+ Result = SendDlgItemMessageW( DialogHandle,
+ ID_ACTIVE_LIST,
+ LB_GETITEMDATA,
+ (WPARAM) i,
+ 0 );
+
+ if ( Result != LB_ERR )
+ {
+ (Credential->UserList)[i] = (LPWSTR) Result;
+ }
+ else
+ {
+ (Credential->UserList)[i] = NULL;
+ }
+
+ FixedPortion += sizeof(LPWSTR);
+ }
+
+ FullIndex = 0;
+
+ do
+ {
+ RtlZeroMemory( tempUserName, sizeof(tempUserName) );
+ RtlZeroMemory( tempOldPassword, sizeof(tempOldPassword) );
+ RtlZeroMemory( tempNewPassword, sizeof(tempNewPassword) );
+ RtlCopyMemory( tempOldPassword,
+ Credential->OldPassword,
+ sizeof(tempOldPassword) );
+ RtlCopyMemory( tempNewPassword,
+ Credential->NewPassword,
+ sizeof(tempNewPassword) );
+
+ if ( (Credential->UserList)[FullIndex] == NULL )
+ {
+ // We don't have any connections to tree <current entry>
+ // Prompt user to supply a user name for which account
+ // we are to change password, or skip . . .
+
+ USERNAME_DLG_PARAM UserNameParam;
+ CHANGE_PASS_DLG_PARAM ChangePassParam;
+
+ UserNameParam.UserName = tempUserName;
+ UserNameParam.TreeServerName = (Credential->TreeList)[FullIndex];
+
+ SetCursor(Cursor);
+ ShowCursor(FALSE);
+
+ Result = DialogBoxParamW(
+ hmodNW,
+ MAKEINTRESOURCEW(DLG_ENTER_ALT_USERNAME),
+ (HWND) DialogHandle,
+ NwpAltUserNameDlgProc,
+ (LPARAM) &UserNameParam );
+
+ Cursor = LoadCursor(NULL, IDC_WAIT);
+
+ if (Cursor != NULL)
+ {
+ SetCursor(Cursor);
+ ShowCursor(TRUE);
+ }
+
+ if ( Result != IDOK )
+ {
+ *((Credential->TreeList)[FullIndex]) = L'\0';
+ goto SkipEntry;
+ }
+
+ // Now go reverify the credentials for the user name
+ // entered by user.
+
+ ChangePassParam.UserName = tempUserName;
+ ChangePassParam.TreeName = (Credential->TreeList)[FullIndex];
+ ChangePassParam.OldPassword = tempOldPassword;
+ ChangePassParam.NewPassword = tempNewPassword;
+
+ SetCursor(Cursor);
+ ShowCursor(FALSE);
+
+ Result = DialogBoxParamW(
+ hmodNW,
+ MAKEINTRESOURCEW(DLG_CHANGE_PASSWORD3),
+ (HWND) DialogHandle,
+ NwpChangePasswordDlgProc,
+ (LPARAM) &ChangePassParam );
+
+ Cursor = LoadCursor(NULL, IDC_WAIT);
+
+ if (Cursor != NULL)
+ {
+ SetCursor(Cursor);
+ ShowCursor(TRUE);
+ }
+
+ if ( Result != IDOK )
+ {
+ *((Credential->TreeList)[FullIndex]) = L'\0';
+ goto SkipEntry;
+ }
+
+ goto Next;
+ }
+ else
+ {
+ wcscpy( tempUserName, (Credential->UserList)[FullIndex] );
+ LocalFree( (Credential->UserList)[FullIndex] );
+ (Credential->UserList)[FullIndex] = NULL;
+ }
+
+ // Test tempUserName with the user name in Credential->UserName
+ // to see if they are similar (i.e. The first part of the
+ // NDS distinguish name matches).
+
+ if ( _wcsnicmp( tempUserName + 3,
+ Credential->UserName,
+ wcslen( Credential->UserName ) ) )
+ {
+ // The names are not similar!
+ // Prompt user to ask if they really want to change
+ // passwords for dis-similar user on tree <current entry>
+ // or skip . . .
+
+ USERNAME_DLG_PARAM UserNameParam;
+ CHANGE_PASS_DLG_PARAM ChangePassParam;
+
+ UserNameParam.UserName = tempUserName;
+ UserNameParam.TreeServerName = (Credential->TreeList)[FullIndex];
+
+ SetCursor(Cursor);
+ ShowCursor(FALSE);
+
+ Result = DialogBoxParamW(
+ hmodNW,
+ MAKEINTRESOURCEW(DLG_ENTER_ALT_USERNAME),
+ (HWND) DialogHandle,
+ NwpAltUserNameDlgProc,
+ (LPARAM) &UserNameParam );
+
+ Cursor = LoadCursor(NULL, IDC_WAIT);
+
+ if (Cursor != NULL)
+ {
+ SetCursor(Cursor);
+ ShowCursor(TRUE);
+ }
+
+ if ( Result != IDOK )
+ {
+ *((Credential->TreeList)[FullIndex]) = L'\0';
+ goto SkipEntry;
+ }
+
+ // Now go reverify the credentials for the user name
+ // entered by user.
+
+ ChangePassParam.UserName = tempUserName;
+ ChangePassParam.TreeName = (Credential->TreeList)[FullIndex];
+ ChangePassParam.OldPassword = tempOldPassword;
+ ChangePassParam.NewPassword = tempNewPassword;
+
+ SetCursor(Cursor);
+ ShowCursor(FALSE);
+
+ Result = DialogBoxParamW(
+ hmodNW,
+ MAKEINTRESOURCEW(DLG_CHANGE_PASSWORD3),
+ (HWND) DialogHandle,
+ NwpChangePasswordDlgProc,
+ (LPARAM) &ChangePassParam );
+
+ Cursor = LoadCursor(NULL, IDC_WAIT);
+
+ if (Cursor != NULL)
+ {
+ SetCursor(Cursor);
+ ShowCursor(TRUE);
+ }
+
+ if ( Result != IDOK )
+ {
+ *((Credential->TreeList)[FullIndex]) = L'\0';
+ goto SkipEntry;
+ }
+ }
+
+Next:
+ status = NwrChangePassword(
+ NULL, // Reserved
+ UserLuid,
+ tempUserName,
+ tempOldPassword, // Encoded passwords
+ tempNewPassword,
+ (LPWSTR) (Credential->TreeList)[FullIndex] );
+
+ if (status == ERROR_INVALID_PASSWORD)
+ {
+ OLD_PW_DLG_PARAM OldPasswordParam;
+
+#if DBG
+ IF_DEBUG(LOGON)
+ {
+ KdPrint(("NWPROVAU: First attempt: wrong password on %ws\n",
+ (Credential->TreeList)[FullIndex]));
+ }
+#endif
+
+ //
+ // Display dialog to let user type in an alternate
+ // old password.
+ //
+
+ //
+ // Set up old password buffer to receive from dialog.
+ //
+ OldPasswordParam.OldPassword = tempOldPassword;
+
+ OldPasswordParam.FailedServer = (Credential->TreeList)[FullIndex];
+
+ SetCursor(Cursor);
+ ShowCursor(FALSE);
+
+ Result = DialogBoxParamW(
+ hmodNW,
+ MAKEINTRESOURCEW(DLG_ENTER_OLD_PASSWORD),
+ (HWND) DialogHandle,
+ NwpOldPasswordDlgProc,
+ (LPARAM) &OldPasswordParam );
+
+ Cursor = LoadCursor(NULL, IDC_WAIT);
+
+ if (Cursor != NULL)
+ {
+ SetCursor(Cursor);
+ ShowCursor(TRUE);
+ }
+
+ if (Result == IDOK)
+ {
+ //
+ // Retry change password with alternate old password on
+ // the failed server.
+ //
+ status = NwrChangePassword(
+ NULL, // Reserved
+ UserLuid,
+ tempUserName,
+ tempOldPassword, // Alternate old password
+ tempNewPassword,
+ (LPWSTR) (Credential->TreeList)[FullIndex] );
+ }
+ }
+
+ if (status != NO_ERROR)
+ {
+ //
+ // Either unrecoverable failure or user failed to change
+ // password on second attempt.
+ //
+#if DBG
+ IF_DEBUG(LOGON)
+ {
+ KdPrint(("NWPROVAU: Failed to change password on %ws %lu\n",
+ (Credential->TreeList)[FullIndex], status));
+ }
+#endif
+
+ // Pop up error dialog to let user know that password
+ // could not be changed.
+
+ (void) NwpMessageBoxError(
+ DialogHandle,
+ IDS_CHANGE_PASSWORD_TITLE,
+ IDS_CP_FAILURE_WARNING,
+ status,
+ (LPWSTR) (Credential->TreeList)[FullIndex],
+ MB_OK | MB_ICONSTOP );
+
+ *((Credential->TreeList)[FullIndex]) = L'\0';
+
+ if (status == ERROR_NOT_ENOUGH_MEMORY)
+ return status;
+ }
+
+SkipEntry:
+ //
+ // Continue to change password on the rest of the entries
+ //
+ FullIndex++;
+ Entries = Credential->Entries - FullIndex;
+
+ } while (Entries);
+
+ //
+ // Caller is responsible for freeing TreeList
+ //
+ }
+
+ SetCursor(Cursor);
+ ShowCursor(FALSE);
+
+ return NO_ERROR;
+}
+
+
+BOOL
+WINAPI
+NwpChangePasswordSuccessDlgProc(
+ HWND DialogHandle,
+ UINT Message,
+ WPARAM WParam,
+ LPARAM LParam
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+ DialogHandle - Supplies a handle to the login dialog.
+
+ Message - Supplies the window management message.
+
+Return Value:
+
+ TRUE - the message was processed.
+
+ FALSE - the message was not processed.
+
+--*/
+{
+ static PCHANGE_PW_DLG_PARAM Credential;
+ DWORD Count;
+ DWORD i;
+
+ switch (Message)
+ {
+ case WM_INITDIALOG:
+
+ //
+ // Get the user credential passed in.
+ //
+ Credential = (PCHANGE_PW_DLG_PARAM) LParam;
+
+ //
+ // Position dialog
+ //
+ NwpCenterDialog(DialogHandle);
+
+ //
+ // Put list of NetWare trees that we changed password on in the
+ // list box.
+ // ID_SERVER );
+ for ( i = 0; i < Credential->Entries; i++ )
+ {
+ if ( *((Credential->TreeList)[i]) != L'\0' )
+ {
+ SendDlgItemMessageW( DialogHandle,
+ ID_SERVER,
+ LB_ADDSTRING,
+ 0,
+ (LPARAM) (Credential->TreeList)[i] );
+ }
+ }
+
+ Count = SendDlgItemMessageW( DialogHandle,
+ ID_SERVER,
+ LB_GETCOUNT,
+ 0,
+ 0 );
+
+ if ( Count == 0 )
+ EndDialog(DialogHandle, 0);
+
+ return TRUE;
+
+
+ case WM_COMMAND:
+
+ switch (LOWORD(WParam))
+ {
+ case IDOK:
+ case IDCANCEL:
+ EndDialog(DialogHandle, 0);
+ return TRUE;
+ }
+ }
+
+ //
+ // We didn't process this message
+ //
+ return FALSE;
+}
+
+
diff --git a/private/nw/svcdlls/nwwks/client/nwdlg.h b/private/nw/svcdlls/nwwks/client/nwdlg.h
new file mode 100644
index 000000000..e3c84b3dd
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/nwdlg.h
@@ -0,0 +1,217 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ nwdlg.h
+
+Abstract:
+
+ Dialog ID header for NetWare login dialog.
+
+Author:
+
+ Rita Wong (ritaw) 17-Mar-1993
+
+Revision History:
+
+--*/
+
+#ifndef _NWDLG_INCLUDED_
+#define _NWDLG_INCLUDED_
+
+#include "nwapi.h"
+#include "nwshrc.h"
+
+typedef struct _LOGIN_DLG_PARAMETERS
+{
+ LPWSTR UserName;
+ LPWSTR ServerName;
+ LPWSTR Password;
+ LPWSTR NewUserSid;
+ PLUID pLogonId;
+ DWORD ServerNameSize;
+ DWORD PasswordSize;
+ DWORD LogonScriptOptions;
+
+} LOGINDLGPARAM, *PLOGINDLGPARAM;
+
+typedef struct _PASSWD_DLG_PARAMETERS
+{
+ LPWSTR UserName;
+ LPWSTR ServerName;
+ DWORD UserNameSize;
+ DWORD ServerNameSize;
+
+} PASSWDDLGPARAM, *PPASSWDDLGPARAM;
+
+typedef struct _CHANGE_PW_DLG_PARAM
+{
+ PWCHAR UserName;
+ PWCHAR OldPassword;
+ PWCHAR NewPassword;
+ LPWSTR *TreeList;
+ LPWSTR *UserList;
+ DWORD Entries;
+ BOOL ChangedOne;
+
+} CHANGE_PW_DLG_PARAM, *PCHANGE_PW_DLG_PARAM;
+
+typedef struct _OLD_PW_DLG_PARAM
+{
+ PWCHAR OldPassword;
+ PWCHAR FailedServer;
+
+} OLD_PW_DLG_PARAM, *POLD_PW_DLG_PARAM;
+
+typedef struct _ALT_UN_DLG_PARAM
+{
+ PWCHAR UserName;
+ PWCHAR TreeServerName;
+
+} USERNAME_DLG_PARAM, *PUSERNAME_DLG_PARAM;
+
+typedef struct _PROMPT_DLG_PARAMETERS
+{
+ LPWSTR UserName;
+ LPWSTR ServerName;
+ LPWSTR Password;
+ DWORD PasswordSize;
+
+} PROMPTDLGPARAM, *PPROMPTDLGPARAM;
+
+typedef struct _CONNECT_DLG_PARAMETERS
+{
+ LPWSTR UncPath;
+ LPWSTR ConnectAsUserName;
+ LPWSTR UserName;
+ LPWSTR Password;
+ DWORD UserNameSize;
+ DWORD PasswordSize;
+ DWORD LastConnectionError;
+
+} CONNECTDLGPARAM, *PCONNECTDLGPARAM;
+
+typedef struct _CHANGE_PASS_DLG_PARAM
+{
+ PWCHAR UserName;
+ PWCHAR TreeName;
+ PWCHAR OldPassword;
+ PWCHAR NewPassword;
+
+} CHANGE_PASS_DLG_PARAM, *PCHANGE_PASS_DLG_PARAM;
+
+
+
+#define NW_INVALID_SERVER_CHAR L'.'
+
+BOOL
+WINAPI
+NwpLoginDlgProc(
+ HWND DialogHandle,
+ UINT Message,
+ WPARAM Parameter1,
+ LPARAM Parameter2
+ );
+
+BOOL
+WINAPI
+NwpSelectServersDlgProc(
+ HWND DialogHandle,
+ UINT Message,
+ WPARAM WParam,
+ LPARAM LParam
+ );
+
+BOOL
+WINAPI
+NwpChangePasswdDlgProc(
+ HWND DialogHandle,
+ UINT Message,
+ WPARAM Parameter1,
+ LPARAM Parameter2
+ );
+
+BOOL
+WINAPI
+NwpPasswdPromptDlgProc(
+ HWND DialogHandle,
+ UINT Message,
+ WPARAM Parameter1,
+ LPARAM Parameter2
+ );
+
+BOOL
+WINAPI
+NwpChangePasswordDlgProc(
+ HWND DialogHandle,
+ UINT Message,
+ WPARAM Parameter1,
+ LPARAM Parameter2
+ );
+
+BOOL
+WINAPI
+NwpHelpDlgProc(
+ HWND DialogHandle,
+ UINT Message,
+ WPARAM Parameter1,
+ LPARAM Parameter2
+ );
+
+BOOL
+WINAPI
+NwpChangePasswordSuccessDlgProc(
+ HWND DialogHandle,
+ UINT Message,
+ WPARAM WParam,
+ LPARAM LParam
+ );
+
+INT
+NwpMessageBoxError(
+ IN HWND hwndParent,
+ IN DWORD nTitleId,
+ IN DWORD nBodyId,
+ IN DWORD nParameterId,
+ IN LPWSTR szParameter2,
+ IN UINT nStyle
+ );
+
+INT
+NwpMessageBoxIns(
+ IN HWND hwndParent,
+ IN DWORD TitleId,
+ IN DWORD MessageId,
+ IN LPWSTR *InsertStrings,
+ IN UINT nStyle
+ );
+
+DWORD
+NwpGetUserCredential(
+ IN HWND hwndOwner,
+ IN LPWSTR Unc,
+ IN DWORD LastConnectionError,
+ IN LPWSTR pszConnectAsUserName,
+ OUT LPWSTR *UserName,
+ OUT LPWSTR *Password
+ );
+
+VOID
+NwpSaveLogonCredential(
+ IN LPWSTR NewUser,
+ IN PLUID LogonId OPTIONAL,
+ IN LPWSTR UserName,
+ IN LPWSTR Password,
+ IN LPWSTR PreferredServer OPTIONAL
+ );
+
+DWORD
+NwpSaveLogonScriptOptions(
+ IN LPWSTR CurrentUserSid,
+ IN DWORD LogonScriptOptions
+ );
+
+
+#endif
diff --git a/private/nw/svcdlls/nwwks/client/nwnp.reg b/private/nw/svcdlls/nwwks/client/nwnp.reg
new file mode 100644
index 000000000..4a038b683
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/nwnp.reg
@@ -0,0 +1,47 @@
+\Registry\MACHINE\SOFTWARE\Classes
+ NetWare_or_Compatible_Network
+ shellex
+ ContextMenuHandlers
+ NetWareMenus
+ = {8e9d6600-f84a-11ce-8daa-00aa004a5691}
+ PropertySheetHandlers
+ NetWarePage
+ = {8e9d6600-f84a-11ce-8daa-00aa004a5691}
+
+ CLSID
+ {8e9d6600-f84a-11ce-8daa-00aa004a5691}
+ = NetWare Objects
+ InProcServer32
+ = nwprovau.dll
+ ThreadingModel = Apartment
+
+ {e3f2bac0-099f-11cf-8daa-00aa004a5691}
+ = NetWare UNC Folder Menu
+ InProcServer32
+ = nwprovau.dll
+ ThreadingModel = Apartment
+
+ {52c68510-09a0-11cf-8daa-00aa004a5691}
+ = NetWare Hood Verbs
+ InProcServer32
+ = nwprovau.dll
+ ThreadingModel = Apartment
+
+ {208D2C60-3AEA-1069-A2D7-08002B30309D}
+ shellex
+ ContextMenuHandlers
+ NetWareMenus
+ = {52c68510-09a0-11cf-8daa-00aa004a5691}
+
+ Folder
+ shellex
+ ContextMenuHandlers
+ NetWareUNCMenu
+ = {e3f2bac0-099f-11cf-8daa-00aa004a5691}
+
+\Registry\MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion
+ Shell Extensions
+ Approved
+ {8e9d6600-f84a-11ce-8daa-00aa004a5691} = Shell extensions for NetWare
+ {e3f2bac0-099f-11cf-8daa-00aa004a5691} = Shell extensions for NetWare
+ {52c68510-09a0-11cf-8daa-00aa004a5691} = Shell extensions for NetWare
diff --git a/private/nw/svcdlls/nwwks/client/nwprovau.def b/private/nw/svcdlls/nwwks/client/nwprovau.def
new file mode 100644
index 000000000..d0a522ce9
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/nwprovau.def
@@ -0,0 +1,80 @@
+LIBRARY NWPROVAU
+
+DESCRIPTION 'Client Service for NetWare Provider and Authentication Package DLL'
+
+EXPORTS
+
+ NPGetCaps
+
+ NPGetUser
+
+ NPAddConnection
+ NPAddConnection3
+ NPCancelConnection
+ NPGetConnection
+ NPGetConnectionPerformance
+
+ NPOpenEnum
+ NPEnumResource
+
+ NPGetResourceInformation
+ NPGetResourceParent
+
+ NPCloseEnum
+
+ NPFormatNetworkName
+
+ NPLogonNotify
+ NPPasswordChangeNotify
+
+ NPGetUniversalName
+ NPLoadNameSpaces
+
+ LsaApInitializePackage
+ LsaApLogonUser
+ LsaApCallPackage
+ LsaApLogonTerminated
+
+ NwQueryInfo
+ NwSetInfoInRegistry
+ NwSetInfoInWksta
+ NwSetLogonScript
+ NwValidateUser
+
+ NwEnumConnections
+
+ NwAddGWDevice
+ NwDeleteGWDevice
+ NwEnumGWDevices
+
+ NwQueryGatewayAccount
+ NwSetGatewayAccount
+ NwLogonGatewayAccount
+
+ InitializePrintProvidor
+ InitializeDll
+
+ NwRegisterGatewayShare
+ NwClearGatewayShare
+ NwCleanupGatewayShares
+
+ NwGetUserNameForServer
+ NwEncryptChallenge
+
+ GetServiceItemFromList
+ NwDeregisterService
+ NwRegisterService
+ NwGetService
+ NwInitializeServiceProvider
+ NwTerminateServiceProvider
+
+ NwSetLogonOptionsInRegistry
+ NwQueryLogonOptions
+
+ ; RnR2 entry name
+
+ NSPStartup
+
+ ; The following are for shell extensions
+ DllCanUnloadNow PRIVATE ; DLL unloading
+ DllGetClassObject PRIVATE ; Component object model
diff --git a/private/nw/svcdlls/nwwks/client/nwprovau.dlg b/private/nw/svcdlls/nwwks/client/nwprovau.dlg
new file mode 100644
index 000000000..6ac7eed43
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/nwprovau.dlg
@@ -0,0 +1,385 @@
+1 DLGINCLUDE "nwshrc.h"
+
+DLG_NETWARE_LOGIN DIALOG 47, 26, 202, 156
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Select NetWare Logon"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ LTEXT "Username:", -1, 7, 7, 50, 8
+ LTEXT "", ID_USERNAME, 70, 7, 100, 8
+ GROUPBOX " ", -1, 7, 19, 190, 34
+ AUTORADIOBUTTON "&Preferred Server", ID_PREFERREDSERVER_RB, 12, 18, 70, 10
+ LTEXT "Pr&eferred Server:", -1, 13, 33, 85, 8
+ COMBOBOX ID_SERVER, 100, 31, 90, 56, CBS_DROPDOWN |
+ CBS_AUTOHSCROLL | CBS_SORT | WS_VSCROLL |
+ WS_GROUP | WS_TABSTOP
+ GROUPBOX "" , -1, 7, 59, 190, 54
+ AUTORADIOBUTTON "&Default Tree and Context", ID_DEFAULTCONTEXT_RB, 12, 58, 98, 10
+ LTEXT "&Tree:", -1, 13, 75, 55, 8
+ EDITTEXT ID_DEFAULTTREE, 58, 74, 110, 12, ES_AUTOHSCROLL
+ LTEXT "&Context:", -1, 13, 92, 55, 8
+ EDITTEXT ID_DEFAULTCONTEXT, 58, 91, 110, 12, ES_AUTOHSCROLL
+ AUTOCHECKBOX "&Run Login Script", ID_LOGONSCRIPT, 10, 118, 85, 12
+ DEFPUSHBUTTON "OK", IDOK, 25, 136, 40, 14, WS_GROUP
+ PUSHBUTTON "Cancel", IDCANCEL, 81, 136, 40, 14, WS_GROUP
+ PUSHBUTTON "&Help", IDHELP, 137, 136, 40, 14, WS_GROUP
+END
+
+DLG_NETWORK_CREDENTIAL DIALOG 20, 35, 266, 72
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Enter Network Password"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ LTEXT "Incorrect password or unknown username for:", ID_CONNECT_TEXT, 6, 8, 180, 8
+ LTEXT "Text", ID_VOLUME_PATH, 20, 20, 160, 8
+ LTEXT "&Connect As:", -1, 6, 36, 46, 8
+ EDITTEXT ID_CONNECT_AS, 56, 34, 140, 12, ES_AUTOHSCROLL | WS_GROUP
+ LTEXT "&Password:", -1, 6, 52, 46, 8
+ EDITTEXT ID_CONNECT_PASSWORD, 56, 50, 140, 12, ES_PASSWORD |
+ ES_AUTOHSCROLL
+ DEFPUSHBUTTON "OK", IDOK, 220, 6, 40, 14, WS_GROUP
+ PUSHBUTTON "Cancel", IDCANCEL, 220, 22, 40, 14, WS_GROUP
+ PUSHBUTTON "&Help", IDHELP, 220, 38, 40, 14, WS_GROUP
+END
+
+DLG_PASSWORD_PROMPT DIALOG 79, 48, 219, 74
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Enter Password"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ LTEXT "Incorrect password for:", -1, 10, 10, 78, 8
+ LTEXT "", ID_USERNAME, 90, 10, 90, 8
+ LTEXT "", ID_LOCATION, 10, 23, 78, 8
+ LTEXT "", ID_SERVER, 90, 23, 120, 8
+ LTEXT "&Password:", -1, 10, 36, 70, 8
+ EDITTEXT ID_PASSWORD, 90, 35, 90, 12, ES_PASSWORD |
+ ES_AUTOHSCROLL
+ DEFPUSHBUTTON "OK", IDOK, 18, 54, 40, 14, WS_GROUP
+ PUSHBUTTON "Cancel", IDCANCEL, 74, 54, 40, 14, WS_GROUP
+ PUSHBUTTON "&Help", IDHELP, 130, 54, 40, 14, WS_GROUP
+END
+
+DLG_PREFERRED_SERVER_HELP DIALOG 10, 21, 263, 140
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Select NetWare Logon Help"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ LTEXT "This dialog allows you to select a Netware® logon.",
+ -1, 9, 10, 245, 9
+ LTEXT "If your network uses NDS, click Default Tree And Context, and type in the tree name and your user context in the boxes.",
+ -1, 9, 20, 245, 18
+ LTEXT "If your network does not use NDS, click Preferred Server and type or select the name of your preferred server. You will be automatically connected to your preferred server every time you log on.",
+ -1, 9, 40, 245, 27
+ LTEXT "If you want your NetWare login script to run when you log on, click Run Login Script.",
+ -1, 9, 70, 245, 18
+ DEFPUSHBUTTON "OK", IDOK, 110, 110, 40, 14
+END
+
+DLG_ENTER_PASSWORD_HELP DIALOG 34, 54, 244, 74
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Enter Password Help"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ LTEXT "When you log on to Windows NT, you are also authenticated on your preferred NetWare server.",
+ -1, 10, 10, 230, 18
+ LTEXT "This is done using your Windows NT username and password. If your NetWare password is different, you will be asked to enter it.",
+ -1, 10, 30, 230, 18, NOT WS_GROUP
+ DEFPUSHBUTTON "OK", IDOK, 107, 54, 40, 14
+END
+
+DLG_CHANGE_PASSWORD DIALOG 22, 24, 217, 111
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Change Password on NetWare"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ LTEXT "&Username:", -1, 10, 10, 43, 8
+ EDITTEXT ID_USERNAME, 90, 9, 118, 12, ES_AUTOHSCROLL | WS_GROUP
+ LTEXT "&Server:", -1, 10, 24, 35, 8
+ COMBOBOX ID_SERVER, 90, 23, 118, 56, CBS_DROPDOWN |
+ CBS_AUTOHSCROLL | CBS_SORT | WS_VSCROLL | WS_TABSTOP
+ LTEXT "&Old Password:", -1, 10, 44, 62, 8
+ EDITTEXT ID_OLD_PASSWORD, 90, 43, 118, 12, ES_PASSWORD |
+ ES_AUTOHSCROLL
+ LTEXT "&New Password:", -1, 10, 58, 54, 8
+ EDITTEXT ID_NEW_PASSWORD, 90, 57, 118, 12, ES_PASSWORD |
+ ES_AUTOHSCROLL
+ LTEXT "&Confirm New Password:", -1, 10, 72, 78, 8
+ EDITTEXT ID_CONFIRM_PASSWORD, 90, 71, 118, 12, ES_PASSWORD |
+ ES_AUTOHSCROLL
+ DEFPUSHBUTTON "OK", IDOK, 32, 91, 40, 14, WS_GROUP
+ PUSHBUTTON "Cancel", IDCANCEL, 88, 91, 40, 14, WS_GROUP
+ PUSHBUTTON "&Help", IDHELP, 144, 91, 40, 14, WS_GROUP
+END
+
+DLG_CHANGE_PASSWORD3 DIALOG 22, 24, 217, 111
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Change Password on NetWare"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ LTEXT "&Username:", -1, 10, 10, 40, 8
+ LTEXT "Text", ID_USERNAME, 90, 10, 118, 8
+ LTEXT "&Tree:", -1, 10, 24, 40, 8
+ LTEXT "Text", ID_SERVER, 90, 24, 118, 8
+ LTEXT "&Old Password:", -1, 10, 44, 62, 8
+ EDITTEXT ID_OLD_PASSWORD, 90, 43, 118, 12, ES_PASSWORD |
+ ES_AUTOHSCROLL
+ LTEXT "&New Password:", -1, 10, 58, 54, 8
+ EDITTEXT ID_NEW_PASSWORD, 90, 57, 118, 12, ES_PASSWORD |
+ ES_AUTOHSCROLL
+ LTEXT "&Confirm New Password:", -1, 10, 72, 78, 8
+ EDITTEXT ID_CONFIRM_PASSWORD, 90, 71, 118, 12, ES_PASSWORD |
+ ES_AUTOHSCROLL
+ DEFPUSHBUTTON "OK", IDOK, 32, 91, 40, 14, WS_GROUP
+ PUSHBUTTON "Cancel", IDCANCEL, 88, 91, 40, 14, WS_GROUP
+ PUSHBUTTON "&Help", IDHELP, 144, 91, 40, 14, WS_GROUP
+END
+
+DLG_CHANGE_PASSWORD_HELP DIALOG 34, 54, 244, 74
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Enter Password Help"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ LTEXT "You have indicated that you want to change passwords on a NetWare 4.X NDS tree for a different user. A set of password change credentials are needed for this user.", -1, 10, 10, 230, 40
+ DEFPUSHBUTTON "OK", IDOK, 107, 54, 40, 14
+END
+
+DLG_CHANGE_PASSWORD2 DIALOG 22, 24, 217, 66
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Change Password on NetWare"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ LTEXT "&Username:", -1, 10, 10, 43, 8
+ EDITTEXT ID_USERNAME, 90, 9, 118, 12, ES_AUTOHSCROLL | WS_GROUP
+ LTEXT "&Server:", -1, 10, 24, 35, 8
+ COMBOBOX ID_SERVER, 90, 23, 118, 56, CBS_DROPDOWN |
+ CBS_AUTOHSCROLL | CBS_SORT | WS_VSCROLL | WS_TABSTOP
+ DEFPUSHBUTTON "OK", IDOK, 32, 46, 40, 14, WS_GROUP
+ PUSHBUTTON "Cancel", IDCANCEL, 88, 46, 40, 14, WS_GROUP
+ PUSHBUTTON "&Help", IDHELP, 144, 46, 40, 14, WS_GROUP
+END
+
+DLG_ENTER_OLD_PASSWORD DIALOG 22, 24, 224, 80
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Enter Old Password for NetWare"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ EDITTEXT ID_PASSWORD, 64, 28, 138, 12, ES_PASSWORD |
+ ES_AUTOHSCROLL | WS_GROUP
+ PUSHBUTTON "OK", IDOK, 27, 54, 40, 14
+ PUSHBUTTON "Cancel", IDCANCEL, 90, 54, 40, 14, WS_GROUP
+ LTEXT "Incorrect old password for:", -1, 5, 11, 88, 8
+ LTEXT "TEXT", ID_SERVER, 90, 11, 112, 8
+ PUSHBUTTON "&Help", IDHELP, 153, 54, 40, 14
+ LTEXT "&Old password:", -1, 5, 30, 50, 8
+END
+
+DLG_ENTER_ALT_USERNAME DIALOG 22, 24, 224, 80
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Change Password on NetWare"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ EDITTEXT ID_USERNAME, 10, 22, 125, 12, ES_AUTOHSCROLL | WS_GROUP
+ PUSHBUTTON "OK", IDOK, 27, 54, 40, 14
+ PUSHBUTTON "Cancel", IDCANCEL, 90, 54, 40, 14, WS_GROUP
+ LTEXT "on NetWare NDS tree:", -1, 5, 40, 80, 8
+ LTEXT "TEXT", ID_SERVER, 90, 40, 40, 8
+ PUSHBUTTON "&Help", IDHELP, 153, 54, 40, 14
+ LTEXT "&Enter fully qualified NDS user name:", -1, 5, 10, 140, 8
+END
+
+DLG_PW_SELECT_SERVERS DIALOG 22, 25, 346, 166
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Change Password on NetWare"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ LTEXT "Select NetWare NDS tree to change password on for user:",
+ -1, 5, 18, 200, 17
+ LTEXT "&Do not change password on:", 109, 198, 65, 135, 8
+ LISTBOX ID_INACTIVE_LIST, 198, 77, 143, 83, LBS_SORT |
+ LBS_EXTENDEDSEL | WS_VSCROLL | WS_HSCROLL | WS_GROUP |
+ WS_TABSTOP
+ DEFPUSHBUTTON "OK", IDOK, 301, 7, 40, 14, WS_GROUP
+ PUSHBUTTON "Cancel", IDCANCEL, 301, 26, 40, 14, WS_GROUP
+ PUSHBUTTON "&Help", IDHELP, 301, 45, 40, 14, WS_GROUP
+ LTEXT "&Change password on:", 108, 5, 65, 108, 8
+ LISTBOX ID_ACTIVE_LIST, 5, 76, 143, 83, LBS_SORT |
+ LBS_EXTENDEDSEL | WS_VSCROLL | WS_HSCROLL | WS_GROUP |
+ WS_TABSTOP
+ PUSHBUTTON "<- &Add", ID_ADD, 153, 98, 40, 14
+ PUSHBUTTON "&Remove ->", ID_REMOVE, 153, 121, 40, 14
+ LTEXT "Text", ID_USERNAME, 15, 33, 200, 8
+END
+
+DLG_PW_CHANGED DIALOG 22, 24, 210, 130
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "NetWare Tree Password(s) Changed"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ PUSHBUTTON "OK", IDOK, 85, 108, 40, 14, WS_GROUP
+ LTEXT "Successfully changed passwords on:", -1, 28, 12,
+ 154, 17
+ LISTBOX ID_SERVER, 26, 30, 157, 69, LBS_SORT | WS_VSCROLL |
+ WS_HSCROLL | WS_TABSTOP
+END
+
+DLG_PW_SELECT_SERVERS_HELP DIALOG 22, 38, 244, 154
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Change Password Help"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ LTEXT "This dialog allows you to change your password on one or more NetWare NDS trees.",
+ -1, 10, 10, 230, 18
+ LTEXT "Your Windows NT username and password will be used to change your password on the selected tree.",
+ -1, 10, 79, 230, 18, NOT WS_GROUP
+ DEFPUSHBUTTON "OK", IDOK, 101, 135, 40, 14
+ LTEXT "If your Windows NT old password is not the same as your NetWare NDS tree old password, you will be asked to enter the old password for that particular tree.",
+ -1, 10, 101, 230, 26
+ LTEXT "To select NetWare NDS trees:", -1, 10, 32, 213, 10
+ LTEXT "1. Select one or more trees from the Don't Change Password On box.",
+ -1, 11, 43, 225, 19
+ LTEXT "2. Choose the Add button.", -1, 10, 63, 224, 8
+END
+
+DLG_ENTER_OLD_PW_HELP DIALOG 22, 24, 200, 58
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Enter Old Password Help"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ PUSHBUTTON "OK", IDOK, 78, 38, 40, 14
+ LTEXT "The old password is incorrect. Please retype your old password for the server.",
+ -1, 9, 12, 182, 20
+END
+
+DLG_ENTER_ALT_UN_HELP DIALOG 22, 24, 200, 68
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Enter NetWare User Name Help"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ PUSHBUTTON "OK", IDOK, 78, 48, 40, 14
+ LTEXT "The user name was incorrectly specified. Please retype your user name as a fully qualified distinguished name (DN).",
+ -1, 9, 12, 182, 33
+END
+
+#ifndef NT1057
+DLG_NDS_SUMMARYINFO DIALOG DISCARDABLE 0, 0, 227, 215
+STYLE DS_MODALFRAME | DS_NOIDLEMSG | WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS |
+ WS_CAPTION | WS_SYSMENU |DS_3DLOOK | DS_CONTEXTHELP
+CAPTION "NetWare Directory Services"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ ICON IDI_TREE_ICON,IDC_STATIC,7,7,18,20
+ LTEXT "",IDC_STATIC,7,40,213,1,NOT WS_GROUP
+ LTEXT "Object name:",IDD_NDS_NAME_TXT,7,51,45,8
+ LTEXT "",IDD_NDS_NAME,55,51,134,8
+ LTEXT "Object class:",IDD_NDS_CLASS_TXT,7,70,45,8
+ LTEXT "",IDD_NDS_CLASS,55,70,117,8
+ LTEXT "Comment:",IDD_NDS_COMMENT_TXT,7,89,45,8
+ LTEXT "",IDD_NDS_COMMENT,55,89,117,8
+ LTEXT "",IDD_ERROR,7,130,185,48, NOT WS_VISIBLE
+END
+
+DLG_NDSCONT_SUMMARYINFO DIALOG DISCARDABLE 0, 0, 227, 215
+STYLE DS_MODALFRAME | DS_NOIDLEMSG | WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS |
+ WS_CAPTION | WS_SYSMENU |DS_3DLOOK | DS_CONTEXTHELP
+CAPTION "NetWare Directory Services"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ ICON IDI_NDSCONT_ICON,IDC_STATIC,7,7,18,20
+ LTEXT "",IDC_STATIC,7,40,213,1,NOT WS_GROUP
+ LTEXT "Object name:",IDD_NDS_NAME_TXT,7,51,45,8
+ LTEXT "",IDD_NDS_NAME,55,51,134,8
+ LTEXT "Object class:",IDD_NDS_CLASS_TXT,7,70,45,8
+ LTEXT "",IDD_NDS_CLASS,55,70,117,8
+ LTEXT "Comment:",IDD_NDS_COMMENT_TXT,7,89,45,8
+ LTEXT "",IDD_NDS_COMMENT,55,89,117,8
+ LTEXT "",IDD_ERROR,7,130,185,48, NOT WS_VISIBLE
+END
+
+DLG_SERVER_SUMMARYINFO DIALOG DISCARDABLE 0, 0, 227, 215
+STYLE DS_MODALFRAME | DS_NOIDLEMSG | WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS |
+ WS_CAPTION | WS_SYSMENU |DS_3DLOOK | DS_CONTEXTHELP
+CAPTION "Server information"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ ICON IDI_SERVER_ICON,IDC_STATIC,7,7,18,20
+ LTEXT "",IDD_SERVER_NAME,40,14,150,8
+ LTEXT "",IDC_STATIC,7,40,213,1,NOT WS_GROUP
+ LTEXT "Version :",IDD_SERVER_VERSION_TXT,7,51,40,8
+ LTEXT "",IDD_SERVER_VERSION,55,51,117,8
+ LTEXT "Revision :",IDD_SERVER_REVISION_TXT,7,63,40,8
+ LTEXT "",IDD_SERVER_REVISION,55,63,117,8
+ LTEXT "Comment:",IDD_SERVER_COMMENT_TXT,7,75,40,8, NOT WS_VISIBLE
+ LTEXT "",IDD_SERVER_COMMENT,55,75,117,8, NOT WS_VISIBLE
+ LTEXT "",IDC_STATIC,7,90,213,1,NOT WS_GROUP
+ LTEXT "Connections in use:",IDD_SERVER_CONNECT_TXT,7,98,100,8
+ RTEXT "",IDD_SERVER_CONNECT,130,98,30,8
+ LTEXT "Maximum number of connections:",IDD_SERVER_MAXCON_TXT,7,110,120,8
+ RTEXT "",IDD_SERVER_MAXCON,130,110,30,8
+ LTEXT "",IDD_ERROR,7,150,185,48, NOT WS_VISIBLE
+END
+
+DLG_SHARE_SUMMARYINFO DIALOG DISCARDABLE 0, 0, 227, 215
+STYLE DS_MODALFRAME | DS_NOIDLEMSG | WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS |
+ WS_CAPTION | WS_SYSMENU |DS_3DLOOK | DS_CONTEXTHELP
+CAPTION "General"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ ICON IDI_FOLDER_ICON,IDC_STATIC,7,7,18,20
+ LTEXT "",IDD_SHARE_NAME,40,14,150,8
+ LTEXT "",IDC_STATIC,7,40,213,1,NOT WS_GROUP
+ LTEXT "Server:",IDD_SHARE_SERVER_TXT,7,48,40,8
+ LTEXT "",IDD_SHARE_SERVER,52,48,134,8
+ LTEXT "Path:",IDD_SHARE_PATH_TXT,7,60,40,8
+ LTEXT "",IDD_SHARE_PATH,52,60,134,8,WS_HSCROLL
+ LTEXT "",IDC_STATIC,7,74,185,1,NOT WS_GROUP
+ CONTROL "",IDD_SHARE_USED_SPC_CLR,"Button",BS_OWNERDRAW |
+ WS_DISABLED | WS_BORDER,7,82,10,9
+ LTEXT "Used space:",IDD_SHARE_USED_SPC_TXT,21,82,46,8
+ RTEXT "",IDD_SHARE_USED_SPC,69,82,65,8
+ RTEXT "",IDD_SHARE_USED_SPC_MB,141,82,35,8
+ CONTROL "",IDD_SHARE_FREE_SPC_CLR,"Button",BS_OWNERDRAW |
+ WS_DISABLED | WS_BORDER,7,94,10,9
+ LTEXT "Free space:",IDD_SHARE_FREE_SPC_TXT,21,94,46,8
+ RTEXT "",IDD_SHARE_FREE_SPC,69,94,65,8
+ RTEXT "",IDD_SHARE_FREE_SPC_MB,141,94,35,8
+ LTEXT "",IDC_STATIC,7,106,185,1,NOT WS_GROUP
+ LTEXT "Capacity:",IDD_SHARE_MAX_SPC_TXT,7,118,46,8
+ RTEXT "",IDD_SHARE_MAX_SPC,69,118,65,8
+ RTEXT "",IDD_SHARE_MAX_SPC_MB,141,118,35,8
+ CONTROL "",IDD_SHARE_PIE,"Button",BS_OWNERDRAW | WS_DISABLED,72,
+ 150,82,39
+ LTEXT "Supports long filenames",IDD_SHARE_LFN_TXT,7,130,130,8
+ LTEXT "",IDD_ERROR,7,102,185,48, NOT WS_VISIBLE
+END
+
+DLG_PRINTER_SUMMARYINFO DIALOG DISCARDABLE 0, 0, 227, 215
+STYLE DS_MODALFRAME | DS_NOIDLEMSG | WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS |
+ WS_CAPTION | WS_SYSMENU |DS_3DLOOK | DS_CONTEXTHELP
+CAPTION "General"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ ICON IDI_PRINTER_ICON,IDC_STATIC,7,7,18,20
+ LTEXT "",IDD_PRINTER_NAME,40,14,150,8
+ LTEXT "",IDC_STATIC,7,40,213,1,NOT WS_GROUP
+ LTEXT "Queue file:",IDD_PRINTER_QUEUE_TXT,7,51,42,8
+ LTEXT "",IDD_PRINTER_QUEUE,52,51,134,8
+ LTEXT "",IDD_ERROR,7,102,185,48, NOT WS_VISIBLE
+END
+
+DLG_GLOBAL_WHOAMI DIALOG DISCARDABLE 0, 0, 371, 197
+STYLE DS_MODALFRAME | DS_NOIDLEMSG | WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS |
+ WS_CAPTION | WS_SYSMENU |DS_3DLOOK | DS_CONTEXTHELP | DS_CENTER
+CAPTION "Who Am I"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ ICON IDI_TREE_ICON,IDC_LOGOFRAME,7,7,18,20
+ CTEXT "",IDD_GLOBAL_SERVERLIST_T,40,5,260,32,NOT WS_GROUP
+ CTEXT "Who Am I information for currently attached servers",
+ IDD_GLOBAL_SVRLIST_DESC, 7,38,300,10,NOT WS_GROUP
+ CONTROL "",IDD_GLOBAL_SERVERLIST,"SysListView32",WS_BORDER |
+ WS_VSCROLL | WS_HSCROLL | LVS_REPORT | LVS_SORTASCENDING ,
+ 7,48,357,142
+ DEFPUSHBUTTON "OK",IDOK,314,7,50,14
+ PUSHBUTTON "&Detach",IDD_DETACH,314,25,50,14
+END
+#endif
diff --git a/private/nw/svcdlls/nwwks/client/nwprovau.rc b/private/nw/svcdlls/nwwks/client/nwprovau.rc
new file mode 100644
index 000000000..746f74b37
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/nwprovau.rc
@@ -0,0 +1,136 @@
+#include <windows.h>
+#include <commctrl.h>
+
+#include "nwdlg.h"
+#include "nwshrc.h"
+#include "nwprovau.dlg"
+
+#include <ntverp.h>
+
+STRINGTABLE
+BEGIN
+ IDS_NONE, "<None>"
+ IDS_SERVER, "Server:"
+ IDS_CONTEXT, "Tree(Context):"
+ IDS_NETWARE_PRINT_CAPTION, "NetWare Print Share"
+ IDS_NOTHING_TO_CONFIGURE, "There is nothing to configure for this port."
+ IDS_NETWARE_TITLE, "NetWare Network"
+ IDS_AUTH_FAILURE_TITLE, "NetWare Authentication Failure"
+ IDS_NO_PREFERRED, "You have not been authenticated on any server. No preferred server will be set. Do you want to continue?
+If you choose Yes, you can select a preferred server later in Control Panel."
+ IDS_LOGIN_FAILURE_WARNING, "You cannot be authenticated on %2!ws! due to the following reason: %1!ws!
+Do you want to select another preferred server or context?"
+ IDS_AUTH_FAILURE_WARNING, "You cannot be authenticated on %2!ws! due to the following reason: %1!ws!
+Do you really want to set the preferred server or context to %2!ws!?"
+ IDS_CHANGE_PASSWORD_INFO, "You may need to change your password separately on NetWare 2.X and
+3.X servers that are not part of a password synchronization scheme.
+Use SETPASS (located in the SYS\\PUBLIC directory on NetWare servers)
+to do this."
+ IDS_INVALID_SERVER, "The server name is invalid."
+ IDS_PASSWORD_HAS_EXPIRED, "Your password on %1 has expired with %2 grace logins remaining. Please change it with the SETPASS utility."
+ IDS_PASSWORD_HAS_EXPIRED0, "Your password on %1 has expired with %2 grace logins remaining. Please change it by pressing Ctrl-Alt-Del and choosing the Change Password button."
+ IDS_AUTH_ACC_RESTRICTION, "You cannot be authenticated on %2!ws! because of an account or station restriction. Check that you have not exceeded your account balance or the number of concurrent connections allowed.
+Do you really want to set the preferred server or context to %2!ws!?"
+ IDS_LOGIN_ACC_RESTRICTION, "You cannot be authenticated on %2!ws! because of an account or station restriction. Check that you have not exceeded your account balance or the number of concurrent connections allowed.
+Do you want to select another preferred server or context?"
+ IDS_PASSWORD_HAS_EXPIRED1, "Your password on %1 has expired. Please change it with the SETPASS utility."
+ IDS_PASSWORD_HAS_EXPIRED2, "Your password on %1 has expired. Please change it by pressing Ctrl-Alt-Del and choosing the Change Password button."
+ IDS_BAD_PASSWORDS, "You have not specified the old and new passwords. Your password was not changed."
+ IDS_CHANGE_PASSWORD_TITLE, "NetWare Password Change Failure"
+ IDS_START_WORKSTATION_FIRST, "Please start the Client Service for NetWare before changing your password."
+ IDS_START_WORKSTATION_FIRST_NTAS, "Please start the Gateway Service for NetWare before changing your password."
+ IDS_LOGIN_DISABLED, "Logins to this server has been disabled."
+ IDS_CONNECT_NO_ERROR_TEXT, "Type your password to log in to:"
+ IDS_TREE_NAME_MISSING, "Please provide a NDS Tree name."
+ IDS_CONTEXT_MISSING , "Please provide a NDS Tree context."
+ IDS_SERVER_MISSING , "Please provide a Server name, or select <None>."
+ IDS_CONTEXT_AUTH_FAILURE_WARNING, "You cannot be authenticated on NDS Tree(Context): %1 due to the following reason:\n %2\nDo you really want to set the NDS Tree(Context) to %1?"
+ IDS_CP_FAILURE_WARNING, "Your password could not be changed on %2!ws! due to the following reason: %1!ws!."
+ IDS_CHANGE_PASSWORD_CONFLICT, "The new password entered does not match the confirm password value, please enter new password again."
+ IDS_NO_TREES_DETECTED, "No NetWare NDS trees were detected, unable to perform change password operation."
+END
+
+#ifndef NT1057
+STRINGTABLE DISCARDABLE
+BEGIN
+(IDS_VERBS_BASE + IDO_VERB_WHOAMI), "&Who Am I..."
+(IDS_VERBS_BASE + IDO_VERB_LOGOUT), "&Log Out"
+(IDS_VERBS_BASE + IDO_VERB_ATTACHAS), "&Attach As..."
+(IDS_VERBS_BASE + IDO_VERB_GLOBALWHOAMI), "&Who Am I..."
+(IDS_VERBS_BASE + IDO_VERB_SETDEFAULTCONTEXT), "Set &Current Context"
+(IDS_VERBS_BASE + IDO_VERB_MAPNETWORKDRIVE), "&Map Network Drive..."
+(IDS_VERBS_BASE + IDO_VERB_TREEWHOAMI), "&Who Am I..."
+
+(IDS_VERBS_HELP_BASE + IDO_VERB_WHOAMI), "Displays the status of your connection to the server."
+(IDS_VERBS_HELP_BASE + IDO_VERB_LOGOUT), "Logs your computer out from the server."
+(IDS_VERBS_HELP_BASE + IDO_VERB_ATTACHAS), "Establishes a connection to the server."
+(IDS_VERBS_HELP_BASE + IDO_VERB_GLOBALWHOAMI), "Displays a list of your connections to NetWare servers."
+(IDS_VERBS_HELP_BASE + IDO_VERB_SETDEFAULTCONTEXT), "Sets the selected NDS container as the current context."
+(IDS_VERBS_HELP_BASE + IDO_VERB_MAPNETWORKDRIVE), "Assigns a drive letter to a network resource."
+(IDS_VERBS_HELP_BASE + IDO_VERB_TREEWHOAMI), "Displays the status of your connection to the NDS container."
+END
+
+STRINGTABLE DISCARDABLE
+BEGIN
+IDS_TITLE_LOGOUT, "Logging out"
+IDS_TITLE_WHOAMI, "WhoAmI Information"
+
+IDS_MESSAGE_CONTEXT_CHANGED "Your default name context changed from \n\r %1 \n\r to \n\r %2."
+IDS_MESSAGE_LOGOUT_QUESTION, "You are logged in to this server.\nDo you want to log out first?"
+IDS_MESSAGE_LOGOUT_FAILED, "You were unable to log out. A program may still be using this server."
+IDS_MESSAGE_LOGOUT_FROM_SERVER_FAILED, "You were unable to log out from %1. A program may still be using this server."
+IDS_MESSAGE_LOGOUT_CONFIRM, "If you log out from this server, you will no longer be connected to shared resources on it. Are you sure you want to log out now?"
+IDS_MESSAGE_NOT_ATTACHED, "This server is not attached."
+IDS_MESSAGE_NOT_ATTACHED_TO_TREE, "This NDS container is not attached."
+IDS_MESSAGE_DETACHED, "You have detached from the server."
+IDS_MESSAGE_ATTACHED, "Server:%1\nUser name:%2\nConnection type:%3"
+IDS_MESSAGE_ATTACHED_TO_TREE, "NDS Container:%1\nUser name:%2\nConnection type:%3"
+IDS_BYTES, "%s bytes"
+IDS_ORDERKB, "%sKB"
+IDS_ORDERMB, "%sMB"
+IDS_ORDERGB, "%sGB"
+IDS_ORDERTB, "%sTB"
+IDS_COLUMN_NAME, "Name"
+IDS_COLUMN_CONN_TYPE, "Type"
+IDS_COLUMN_CONN_NUMBER, "Conn #"
+IDS_COLUMN_USER, "User name"
+IDS_COLUMN_STATUS, "Status"
+IDS_STATE_NOT_LOGGED_IN "Not logged in"
+IDS_LOGIN_TYPE_NDS "NDS"
+IDS_LOGIN_TYPE_BINDERY "Bindery"
+IDS_LOGIN_STATUS_SEPARATOR, ", "
+IDS_LOGIN_STATUS_AUTHENTICATED "Authenticated"
+IDS_LOGIN_STATUS_NOT_AUTHENTICATED "Not authenticated"
+IDS_LOGIN_STATUS_LICENSED ", Licensed"
+IDS_LOGIN_STATUS_NOT_LICENSED ", Not licensed"
+IDS_LOGIN_STATUS_LOGGED_IN "Logged in"
+IDS_LOGIN_STATUS_ATTACHED_ONLY "Attached, but not logged in"
+IDS_LOGIN_STATUS_NOT_ATTACHED "Not attached"
+IDS_MESSAGE_CONNINFO_ERROR, "Error %1 occurred while trying to get connection information."
+IDS_MESSAGE_ADDCONN_ERROR, "Error %1 occurred while trying to connect to %2."
+IDS_MESSAGE_CONTEXT_ERROR, "Error %1 occurred while trying to change the current context to %2."
+IDS_MESSAGE_PROPERTIES_ERROR, "Error %1 occurred while trying to get the properties."
+IDS_MESSAGE_LOGGED_IN_TREE, "You are logged in to the directory tree %1 with user name %2.\nThe current workstation name context is %3."
+IDS_MESSAGE_NOT_LOGGED_IN_TREE, "You are not logged in to the directory tree %1.\n"
+IDS_MESSAGE_LOGGED_IN_SERVER, "You are logged in to the server %1 with user name %2.\n"
+IDS_MESSAGE_NOT_LOGGED_IN_SERVER, "You are not logged in to your preferred server %1.\n"
+IDS_MESSAGE_GETINFO_ERROR, "Unable to retrieve information due to the following error: %1"
+END
+
+IDI_TREE_ICON ICON DISCARDABLE "tree.ico"
+IDI_NDSCONT_ICON ICON DISCARDABLE "ndscont.ico"
+IDI_SERVER_ICON ICON DISCARDABLE "server.ico"
+IDI_FOLDER_ICON ICON DISCARDABLE "folderop.ico"
+IDI_PRINTER_ICON ICON DISCARDABLE "print.ico"
+
+IDB_SERVER_ICON BITMAP MOVEABLE PURE "server.bmp"
+IDB_TREE_ICON BITMAP MOVEABLE PURE "tree.bmp"
+
+#endif
+
+#define VER_FILETYPE VFT_DLL
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "Client Service for NetWare Provider and Authentication Package DLL"
+#define VER_INTERNALNAME_STR "nwprovau.dll"
+
+#include "common.ver"
diff --git a/private/nw/svcdlls/nwwks/client/nwprovau.res b/private/nw/svcdlls/nwwks/client/nwprovau.res
new file mode 100644
index 000000000..3aac0734b
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/nwprovau.res
Binary files differ
diff --git a/private/nw/svcdlls/nwwks/client/nwshcmn.h b/private/nw/svcdlls/nwwks/client/nwshcmn.h
new file mode 100644
index 000000000..dd498b9e6
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/nwshcmn.h
@@ -0,0 +1,115 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ nwshcmn.h
+
+Abstract:
+
+ Common header file for shell extensions
+
+Author:
+
+ Yi-Hsin Sung (yihsins) 20-Oct-1995
+
+Revision History:
+
+--*/
+
+#ifndef _NWSHCMN_H_
+#define _NWSHCMN_H_
+
+#if 0
+#define ODS(sz) OutputDebugString(sz)
+#else
+#define ODS(sz)
+#endif
+
+#define TREECHAR L'*'
+
+#define MAX_ONE_NETRES_SIZE 1024
+
+extern "C"
+{
+extern HINSTANCE hmodNW;
+}
+extern LONG g_cRefThisDll; // Reference count of this DLL.
+
+typedef UINT
+(WINAPI *SHELLGETNETRESOURCE)( HNRES hnres,
+ UINT iItem,
+ LPNETRESOURCE pnres,
+ UINT cbMax );
+
+typedef UINT
+(WINAPI *SHELLDRAGQUERYFILE)( HDROP hdrop,
+ UINT iItem,
+ LPWSTR pszItem,
+ UINT cbMax);
+
+typedef VOID
+(WINAPI *SHELLCHANGENOTIFY)( LONG wEventId,
+ UINT uFlags,
+ LPCVOID dwItem1,
+ LPCVOID dwItem2 );
+
+typedef BOOL
+(WINAPI *SHELLEXECUTEEX)( LPSHELLEXECUTEINFOW lpExecInfo );
+
+
+extern SHELLGETNETRESOURCE g_pFuncSHGetNetResource;
+extern SHELLDRAGQUERYFILE g_pFuncSHDragQueryFile;
+extern SHELLCHANGENOTIFY g_pFuncSHChangeNotify;
+extern SHELLEXECUTEEX g_pFuncSHExecuteEx;
+extern WCHAR g_szProviderName[];
+
+VOID HideControl( HWND hwndDlg, WORD wID );
+VOID UnHideControl( HWND hwndDlg, WORD wID );
+VOID EnableDlgItem( HWND hwndDlg, WORD wID, BOOL fEnable);
+
+DWORD MsgBoxPrintf( HWND hwnd, UINT uiMsg, UINT uiTitle, UINT uiFlags,...);
+DWORD MsgBoxErrorPrintf( HWND hwnd, UINT uiMsg, UINT uiTitle, UINT uiFlags, DWORD errNum, LPWSTR pszInsertStr );
+DWORD LoadMsgPrintf( LPWSTR *ppszMessage, UINT uiMsg, ...);
+DWORD LoadMsgErrorPrintf( LPWSTR *ppszMessage, UINT uiMsg, DWORD errNum );
+
+#if 0
+HRESULT
+NWUISetDefaultContext(
+ HWND hParent,
+ LPNETRESOURCE pNetRes
+);
+#endif
+
+HRESULT
+NWUIWhoAmI(
+ HWND hParent,
+ LPNETRESOURCE pNetRes
+);
+
+HRESULT
+NWUILogOut(
+ HWND hParent,
+ LPNETRESOURCE pNetRes,
+ PBOOL pfDisconnected
+);
+
+HRESULT
+NWUIAttachAs(
+ HWND hParent,
+ LPNETRESOURCE pNetRes
+);
+
+HRESULT
+NWUIMapNetworkDrive(
+ HWND hParent,
+ LPNETRESOURCE pNetRes
+);
+
+HRESULT
+NWUIGlobalWhoAmI(
+ HWND hParent
+);
+
+#endif // _NWSHCMN_H_
diff --git a/private/nw/svcdlls/nwwks/client/nwshext.cxx b/private/nw/svcdlls/nwwks/client/nwshext.cxx
new file mode 100644
index 000000000..4818a3823
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/nwshext.cxx
@@ -0,0 +1,854 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ nwshext.cxx
+
+Abstract:
+
+ This module implements the basics of shell extension classes.
+ It includes AddRef(), Release(), QueryInterface() of the
+ following classes.
+ CNWObjContextMenuClassFactory, CNWObjContextMenu
+ CNWFldContextMenuClassFactory, CNWFldContextMenu
+ CNWHoodContextMenuClassFactory, CNWHoodContextMenu
+
+
+Author:
+
+ Yi-Hsin Sung (yihsins) 25-Oct-1995
+
+--*/
+
+extern "C"
+{
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+
+#include <shellapi.h>
+#include <shlobj.h>
+#include <stdio.h>
+#define DONT_WANT_SHELLDEBUG
+#include <shsemip.h>
+
+#include <nwreg.h>
+}
+
+#include "nwshcmn.h"
+#include "nwshext.h"
+
+//
+// Initialize GUIDs (should be done only and at-least once per DLL/EXE)
+//
+
+#pragma data_seg(".text")
+#define INITGUID
+#include <initguid.h>
+#include <shlguid.h>
+#include "nwclsid.h"
+#pragma data_seg()
+
+//
+// Global variables
+//
+LONG g_cRefThisDll = 0; // Reference count of this DLL.
+WCHAR g_szProviderName[256]; // Store the provider name
+
+HINSTANCE g_hShellLibrary = NULL;
+SHELLGETNETRESOURCE g_pFuncSHGetNetResource = NULL;
+SHELLDRAGQUERYFILE g_pFuncSHDragQueryFile = NULL;
+SHELLCHANGENOTIFY g_pFuncSHChangeNotify = NULL;
+SHELLEXECUTEEX g_pFuncSHExecuteEx = NULL;
+
+
+#if DBG
+WCHAR szDebugStr[256]; // For Debug Output
+#endif
+
+BOOL LoadShellDllEntries( VOID );
+
+extern "C"
+{
+//---------------------------------------------------------------------------
+// NwCleanupShellExtension
+//---------------------------------------------------------------------------
+
+VOID NwCleanupShellExtensions( VOID )
+{
+ if ( g_hShellLibrary )
+ {
+ FreeLibrary( g_hShellLibrary );
+ g_hShellLibrary = NULL;
+ }
+}
+}
+
+//---------------------------------------------------------------------------
+// DllCanUnloadNow
+//---------------------------------------------------------------------------
+
+STDAPI DllCanUnloadNow(void)
+{
+#if DBG
+ wsprintf( szDebugStr,L"In DLLCanUnloadNow: g_cRefThisDll = %d\r\n", g_cRefThisDll);
+ ODS( szDebugStr );
+#endif
+
+ return ResultFromScode((g_cRefThisDll == 0) ? S_OK : S_FALSE);
+}
+
+//---------------------------------------------------------------------------
+// DllGetClassObject
+//---------------------------------------------------------------------------
+
+STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppvOut)
+{
+ *ppvOut = NULL;
+
+ if ( !LoadShellDllEntries() )
+ return ResultFromScode(CLASS_E_CLASSNOTAVAILABLE);
+
+ if (IsEqualIID(rclsid, CLSID_NetWareObjectExt))
+ {
+ CNWObjContextMenuClassFactory *pcf = new CNWObjContextMenuClassFactory;
+
+ if ( pcf == NULL )
+ return ResultFromScode(E_OUTOFMEMORY);
+
+ HRESULT hr = pcf->QueryInterface(riid, ppvOut);
+
+ if ( FAILED(hr) )
+ delete pcf;
+
+ return hr;
+ }
+ else if (IsEqualIID(rclsid, CLSID_NetWareFolderMenuExt))
+ {
+ CNWFldContextMenuClassFactory *pcf = new CNWFldContextMenuClassFactory;
+
+ if ( pcf == NULL )
+ return ResultFromScode(E_OUTOFMEMORY);
+
+ HRESULT hr = pcf->QueryInterface(riid, ppvOut);
+
+ if ( FAILED(hr) )
+ delete pcf;
+
+ return hr;
+ }
+ else if (IsEqualIID(rclsid, CLSID_NetworkNeighborhoodMenuExt))
+ {
+ CNWHoodContextMenuClassFactory *pcf= new CNWHoodContextMenuClassFactory;
+
+ if ( pcf == NULL )
+ return ResultFromScode(E_OUTOFMEMORY);
+
+ HRESULT hr = pcf->QueryInterface(riid, ppvOut);
+
+ if ( FAILED(hr) )
+ delete pcf;
+
+ return hr;
+ }
+
+
+ return ResultFromScode(CLASS_E_CLASSNOTAVAILABLE);
+}
+
+BOOL LoadShellDllEntries( VOID )
+{
+ static BOOL s_fLoaded = FALSE;
+
+ if ( !s_fLoaded )
+ {
+ DWORD err;
+ HKEY hkey;
+
+ g_hShellLibrary = LoadLibrary( L"shell32.dll");
+ if ( g_hShellLibrary != NULL )
+ {
+ s_fLoaded = TRUE;
+
+ g_pFuncSHGetNetResource =
+ (SHELLGETNETRESOURCE) GetProcAddress( g_hShellLibrary,
+ (LPCSTR)(MAKELONG(SHGetNetResourceORD, 0)) );
+
+ g_pFuncSHDragQueryFile =
+ (SHELLDRAGQUERYFILE) GetProcAddress( g_hShellLibrary,
+ (LPCSTR) "DragQueryFileW");
+ g_pFuncSHChangeNotify =
+ (SHELLCHANGENOTIFY) GetProcAddress( g_hShellLibrary,
+ (LPCSTR) "SHChangeNotify");
+ g_pFuncSHExecuteEx =
+ (SHELLEXECUTEEX) GetProcAddress( g_hShellLibrary,
+ (LPCSTR) "ShellExecuteExW");
+ }
+
+ // Set the default provider name in case we fail to read
+ // it from the registry.
+ wcscpy( g_szProviderName, L"NetWare or Compatible Network");
+
+ //
+ // Read the Network Provider Name.
+ //
+ // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
+ // \NWCWorkstation\networkprovider
+ //
+ err = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ NW_WORKSTATION_PROVIDER_PATH,
+ REG_OPTION_NON_VOLATILE, // options
+ KEY_READ, // desired access
+ &hkey
+ );
+
+ if ( err == NO_ERROR )
+ {
+ LPWSTR pszProviderName = NULL;
+
+ //
+ // ignore the return code. if fail, pszProviderName is NULL
+ //
+ err = NwReadRegValue(
+ hkey,
+ NW_PROVIDER_VALUENAME,
+ &pszProviderName // free with LocalFree
+ );
+
+ if ( err == NO_ERROR && pszProviderName != NULL )
+ {
+ wcscpy( g_szProviderName, pszProviderName );
+ LocalFree( pszProviderName );
+ }
+
+ RegCloseKey( hkey );
+ }
+ }
+
+ return s_fLoaded;
+}
+
+//---------------------------------------------------------------------------
+// CNWObjContextMenuClassFactory
+//---------------------------------------------------------------------------
+
+CNWObjContextMenuClassFactory::CNWObjContextMenuClassFactory()
+{
+ _cRef = 0L;
+ InterlockedIncrement( &g_cRefThisDll );
+
+#if DBG
+ wsprintf( szDebugStr,L"CNWObjContextMenuClassFactory::CNWObjContextMenuClassFactory(), g_cRefThisDll = %d\r\n", g_cRefThisDll);
+ ODS( szDebugStr );
+#endif
+}
+
+CNWObjContextMenuClassFactory::~CNWObjContextMenuClassFactory()
+{
+ InterlockedDecrement( &g_cRefThisDll );
+
+#if DBG
+ wsprintf( szDebugStr,L"CNWObjContextMenuClassFactory::~CNWObjContextMenuClassFactory(), g_cRefThisDll = %d\r\n", g_cRefThisDll);
+ ODS( szDebugStr );
+#endif
+}
+
+STDMETHODIMP CNWObjContextMenuClassFactory::QueryInterface(REFIID riid,
+ LPVOID FAR *ppv)
+{
+ *ppv = NULL;
+
+ // Any interface on this object is the object pointer
+
+ if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IClassFactory))
+ {
+ *ppv = (LPCLASSFACTORY)this;
+ AddRef();
+ return NOERROR;
+ }
+
+ return ResultFromScode(E_NOINTERFACE);
+}
+
+STDMETHODIMP_(ULONG) CNWObjContextMenuClassFactory::AddRef()
+{
+ return ++_cRef;
+}
+
+STDMETHODIMP_(ULONG) CNWObjContextMenuClassFactory::Release()
+{
+ if (--_cRef)
+ return _cRef;
+
+ delete this;
+
+ return 0L;
+}
+
+STDMETHODIMP CNWObjContextMenuClassFactory::CreateInstance(LPUNKNOWN pUnkOuter,
+ REFIID riid,
+ LPVOID *ppvObj)
+{
+ *ppvObj = NULL;
+
+ // Shell extensions typically don't support aggregation (inheritance)
+
+ if (pUnkOuter)
+ return ResultFromScode(CLASS_E_NOAGGREGATION);
+
+ // Create the main shell extension object. The shell will then call
+ // QueryInterface with IID_IShellExtInit--this is how shell extensions are
+ // initialized.
+
+ LPCNWOBJCONTEXTMENU pShellExt = new CNWObjContextMenu(); // Create the CNWObjContextMenu object
+
+ if (NULL == pShellExt)
+ return ResultFromScode(E_OUTOFMEMORY);
+
+ //
+ // We set the reference count of CNWObjContextMenu to one at initialization.
+ // Hence, we can call Release() after QueryInterface.
+ // So, if QueryInterface failed, Release will free the object.
+ //
+
+ HRESULT hr = pShellExt->QueryInterface(riid, ppvObj);
+ pShellExt->Release();
+
+ return hr;
+}
+
+STDMETHODIMP CNWObjContextMenuClassFactory::LockServer(BOOL fLock)
+{
+ return NOERROR;
+}
+
+//---------------------------------------------------------------------------
+// CNWObjContextMenu
+//---------------------------------------------------------------------------
+
+CNWObjContextMenu::CNWObjContextMenu()
+{
+ _cRef = 1L;
+ _pDataObj = NULL;
+
+ _fGotClusterInfo = FALSE;
+ _dwTotal = 0;
+ _dwFree = 0;
+
+ InterlockedIncrement( &g_cRefThisDll );
+
+#if DBG
+ wsprintf( szDebugStr,L"CNWObjContextMenu::CNWObjContextMenu(), g_cRefThisDll = %d\r\n", g_cRefThisDll);
+ ODS( szDebugStr );
+#endif
+}
+
+CNWObjContextMenu::~CNWObjContextMenu()
+{
+ if (_pDataObj)
+ _pDataObj->Release();
+
+ InterlockedDecrement( &g_cRefThisDll );
+
+#if DBG
+ wsprintf( szDebugStr,L"CNWObjContextMenu::~CNWObjContextMenu(), g_cRefThisDll = %d\r\n", g_cRefThisDll);
+ ODS( szDebugStr );
+#endif
+}
+
+STDMETHODIMP CNWObjContextMenu::QueryInterface(REFIID riid, LPVOID FAR *ppv)
+{
+ *ppv = NULL;
+
+ if (IsEqualIID(riid, IID_IShellExtInit) || IsEqualIID(riid, IID_IUnknown))
+ {
+ *ppv = (LPSHELLEXTINIT)this;
+ }
+ else if (IsEqualIID(riid, IID_IContextMenu))
+ {
+ *ppv = (LPCONTEXTMENU)this;
+ }
+ else if (IsEqualIID(riid, IID_IShellPropSheetExt))
+ {
+ *ppv = (LPSHELLPROPSHEETEXT)this;
+ }
+
+ if (*ppv)
+ {
+ AddRef();
+
+ return NOERROR;
+ }
+
+ return ResultFromScode(E_NOINTERFACE);
+}
+
+STDMETHODIMP_(ULONG) CNWObjContextMenu::AddRef()
+{
+ return ++_cRef;
+}
+
+STDMETHODIMP_(ULONG) CNWObjContextMenu::Release()
+{
+ if (--_cRef)
+ return _cRef;
+
+ delete this;
+
+ return 0L;
+}
+
+//
+// FUNCTION: CNWObjContextMenu::Initialize(LPCITEMIDLIST, LPDATAOBJECT, HKEY)
+//
+// PURPOSE: Called by the shell when initializing a context menu or property
+// sheet extension.
+//
+// PARAMETERS:
+// pIDFolder - Specifies the parent folder
+// pDataObj - Spefifies the set of items selected in that folder.
+// hRegKey - Specifies the type of the focused item in the selection.
+//
+// RETURN VALUE:
+//
+// NOERROR in all cases.
+//
+// COMMENTS: Note that at the time this function is called, we don't know
+// (or care) what type of shell extension is being initialized.
+// It could be a context menu or a property sheet.
+//
+
+STDMETHODIMP CNWObjContextMenu::Initialize( LPCITEMIDLIST pIDFolder,
+ LPDATAOBJECT pDataObj,
+ HKEY hRegKey)
+{
+ // We do not need the registry handle so ignore it.
+
+ // Initialize can be called more than once
+
+ if (_pDataObj)
+ _pDataObj->Release();
+
+ // Duplicate the object pointer
+
+ if (pDataObj)
+ {
+ _pDataObj = pDataObj;
+ pDataObj->AddRef();
+ }
+
+ return NOERROR;
+}
+
+//---------------------------------------------------------------------------
+// CNWFldContextMenuClassFactory
+//---------------------------------------------------------------------------
+
+CNWFldContextMenuClassFactory::CNWFldContextMenuClassFactory()
+{
+ _cRef = 0L;
+ InterlockedIncrement( &g_cRefThisDll );
+
+#if DBG
+ wsprintf( szDebugStr,L"CNWFldContextMenuClassFactory::CNWFldContextMenuClassFactory(), g_cRefThisDll = %d\r\n", g_cRefThisDll);
+ ODS( szDebugStr );
+#endif
+}
+
+CNWFldContextMenuClassFactory::~CNWFldContextMenuClassFactory()
+{
+ InterlockedDecrement( &g_cRefThisDll );
+
+#if DBG
+ wsprintf( szDebugStr,L"CNWFldContextMenuClassFactory::~CNWFldContextMenuClassFactory(), g_cRefThisDll = %d\r\n", g_cRefThisDll);
+ ODS( szDebugStr );
+#endif
+}
+
+STDMETHODIMP CNWFldContextMenuClassFactory::QueryInterface(REFIID riid,
+ LPVOID FAR *ppv)
+{
+ *ppv = NULL;
+
+ // Any interface on this object is the object pointer
+
+ if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IClassFactory))
+ {
+ *ppv = (LPCLASSFACTORY)this;
+ AddRef();
+ return NOERROR;
+ }
+
+ return ResultFromScode(E_NOINTERFACE);
+}
+
+STDMETHODIMP_(ULONG) CNWFldContextMenuClassFactory::AddRef()
+{
+ return ++_cRef;
+}
+
+STDMETHODIMP_(ULONG) CNWFldContextMenuClassFactory::Release()
+{
+ if (--_cRef)
+ return _cRef;
+
+ delete this;
+
+ return 0L;
+}
+
+STDMETHODIMP CNWFldContextMenuClassFactory::CreateInstance(LPUNKNOWN pUnkOuter,
+ REFIID riid,
+ LPVOID *ppvObj)
+{
+ *ppvObj = NULL;
+
+ // Shell extensions typically don't support aggregation (inheritance)
+
+ if (pUnkOuter)
+ return ResultFromScode(CLASS_E_NOAGGREGATION);
+
+ // Create the main shell extension object. The shell will then call
+ // QueryInterface with IID_IShellExtInit--this is how shell extensions are
+ // initialized.
+
+ LPCNWFLDCONTEXTMENU pShellExt = new CNWFldContextMenu(); // Create the CNWFldContextMenu object
+
+ if (NULL == pShellExt)
+ return ResultFromScode(E_OUTOFMEMORY);
+
+ //
+ // We set the reference count of CNWFldContextMenu to one at initialization.
+ // Hence, we can call Release() after QueryInterface.
+ // So, if QueryInterface failed, Release will free the object.
+ //
+
+ HRESULT hr = pShellExt->QueryInterface(riid, ppvObj);
+ pShellExt->Release();
+
+ return hr;
+}
+
+STDMETHODIMP CNWFldContextMenuClassFactory::LockServer(BOOL fLock)
+{
+ return NOERROR;
+}
+
+//---------------------------------------------------------------------------
+// CNWFldContextMenu
+//---------------------------------------------------------------------------
+
+CNWFldContextMenu::CNWFldContextMenu()
+{
+ _cRef = 1L;
+ _pDataObj = NULL;
+
+ InterlockedIncrement( &g_cRefThisDll );
+
+#if DBG
+ wsprintf( szDebugStr,L"CNWFldContextMenu::CNWFldContextMenu(), g_cRefThisDll = %d\r\n", g_cRefThisDll);
+ ODS( szDebugStr );
+#endif
+}
+
+CNWFldContextMenu::~CNWFldContextMenu()
+{
+ if (_pDataObj)
+ _pDataObj->Release();
+
+ InterlockedDecrement( &g_cRefThisDll );
+
+#if DBG
+ wsprintf( szDebugStr,L"CNWFldContextMenu::~CNWFldContextMenu(), g_cRefThisDll = %d\r\n", g_cRefThisDll);
+ ODS( szDebugStr );
+#endif
+}
+
+STDMETHODIMP CNWFldContextMenu::QueryInterface(REFIID riid, LPVOID FAR *ppv)
+{
+ *ppv = NULL;
+
+ if (IsEqualIID(riid, IID_IShellExtInit) || IsEqualIID(riid, IID_IUnknown))
+ {
+ *ppv = (LPSHELLEXTINIT)this;
+ }
+ else if (IsEqualIID(riid, IID_IContextMenu))
+ {
+ *ppv = (LPCONTEXTMENU)this;
+ }
+
+ if (*ppv)
+ {
+ AddRef();
+
+ return NOERROR;
+ }
+
+ return ResultFromScode(E_NOINTERFACE);
+}
+
+STDMETHODIMP_(ULONG) CNWFldContextMenu::AddRef()
+{
+ return ++_cRef;
+}
+
+STDMETHODIMP_(ULONG) CNWFldContextMenu::Release()
+{
+ if (--_cRef)
+ return _cRef;
+
+ delete this;
+
+ return 0L;
+}
+
+//
+// FUNCTION: CNWFldContextMenu::Initialize(LPCITEMIDLIST, LPDATAOBJECT, HKEY)
+//
+// PURPOSE: Called by the shell when initializing a context menu or property
+// sheet extension.
+//
+// PARAMETERS:
+// pIDFolder - Specifies the parent folder
+// pDataObj - Spefifies the set of items selected in that folder.
+// hRegKey - Specifies the type of the focused item in the selection.
+//
+// RETURN VALUE:
+//
+// NOERROR in all cases.
+//
+// COMMENTS: Note that at the time this function is called, we don't know
+// (or care) what type of shell extension is being initialized.
+// It could be a context menu or a property sheet.
+//
+
+STDMETHODIMP CNWFldContextMenu::Initialize( LPCITEMIDLIST pIDFolder,
+ LPDATAOBJECT pDataObj,
+ HKEY hRegKey)
+{
+ // We do not need the registry handle so ignore it.
+
+ // Initialize can be called more than once
+
+ if (_pDataObj)
+ _pDataObj->Release();
+
+ // Duplicate the object pointer
+
+ if (pDataObj)
+ {
+ _pDataObj = pDataObj;
+ pDataObj->AddRef();
+ }
+
+ return NOERROR;
+}
+
+//---------------------------------------------------------------------------
+// CNWHoodContextMenuClassFactory
+//---------------------------------------------------------------------------
+
+CNWHoodContextMenuClassFactory::CNWHoodContextMenuClassFactory()
+{
+ _cRef = 0L;
+ InterlockedIncrement( &g_cRefThisDll );
+
+#if DBG
+ wsprintf( szDebugStr,L"CNWHoodContextMenuClassFactory::CNWHoodContextMenuClassFactory(), g_cRefThisDll = %d\r\n", g_cRefThisDll);
+ ODS( szDebugStr );
+#endif
+}
+
+CNWHoodContextMenuClassFactory::~CNWHoodContextMenuClassFactory()
+{
+ InterlockedDecrement( &g_cRefThisDll );
+
+#if DBG
+ wsprintf( szDebugStr,L"CNWHoodContextMenuClassFactory::~CNWHoodContextMenuClassFactory(), g_cRefThisDll = %d\r\n", g_cRefThisDll);
+ ODS( szDebugStr );
+#endif
+}
+
+STDMETHODIMP CNWHoodContextMenuClassFactory::QueryInterface(REFIID riid,
+ LPVOID FAR *ppv)
+{
+ *ppv = NULL;
+
+ // Any interface on this object is the object pointer
+
+ if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IClassFactory))
+ {
+ *ppv = (LPCLASSFACTORY)this;
+ AddRef();
+ return NOERROR;
+ }
+
+ return ResultFromScode(E_NOINTERFACE);
+}
+
+STDMETHODIMP_(ULONG) CNWHoodContextMenuClassFactory::AddRef()
+{
+ return ++_cRef;
+}
+
+STDMETHODIMP_(ULONG) CNWHoodContextMenuClassFactory::Release()
+{
+ if (--_cRef)
+ return _cRef;
+
+ delete this;
+
+ return 0L;
+}
+
+STDMETHODIMP CNWHoodContextMenuClassFactory::CreateInstance(LPUNKNOWN pUnkOuter,
+ REFIID riid,
+ LPVOID *ppvObj)
+{
+ *ppvObj = NULL;
+
+ // Shell extensions typically don't support aggregation (inheritance)
+
+ if (pUnkOuter)
+ return ResultFromScode(CLASS_E_NOAGGREGATION);
+
+ // Create the main shell extension object. The shell will then call
+ // QueryInterface with IID_IShellExtInit--this is how shell extensions are
+ // initialized.
+
+ LPCNWHOODCONTEXTMENU pShellExt = new CNWHoodContextMenu(); // Create the CNWHoodContextMenu object
+
+ if (NULL == pShellExt)
+ return ResultFromScode(E_OUTOFMEMORY);
+
+ //
+ // We set the reference count of CNWHoodContextMenu to one at initialization.
+ // Hence, we can call Release() after QueryInterface.
+ // So, if QueryInterface failed, Release will free the object.
+ //
+
+ HRESULT hr = pShellExt->QueryInterface(riid, ppvObj);
+ pShellExt->Release();
+
+ return hr;
+}
+
+STDMETHODIMP CNWHoodContextMenuClassFactory::LockServer(BOOL fLock)
+{
+ return NOERROR;
+}
+
+//---------------------------------------------------------------------------
+// CNWHoodContextMenu
+//---------------------------------------------------------------------------
+
+CNWHoodContextMenu::CNWHoodContextMenu()
+{
+ _cRef = 1L;
+ _pDataObj = NULL;
+
+ InterlockedIncrement( &g_cRefThisDll );
+
+#if DBG
+ wsprintf( szDebugStr,L"CNWHoodContextMenu::CNWHoodContextMenu(), g_cRefThisDll = %d\r\n", g_cRefThisDll);
+ ODS( szDebugStr );
+#endif
+}
+
+CNWHoodContextMenu::~CNWHoodContextMenu()
+{
+ if (_pDataObj)
+ _pDataObj->Release();
+
+ InterlockedDecrement( &g_cRefThisDll );
+
+#if DBG
+ wsprintf( szDebugStr,L"CNWHoodContextMenu::~CNWHoodContextMenu(), g_cRefThisDll = %d\r\n", g_cRefThisDll);
+ ODS( szDebugStr );
+#endif
+}
+
+STDMETHODIMP CNWHoodContextMenu::QueryInterface(REFIID riid, LPVOID FAR *ppv)
+{
+ *ppv = NULL;
+
+ if (IsEqualIID(riid, IID_IShellExtInit) || IsEqualIID(riid, IID_IUnknown))
+ {
+ *ppv = (LPSHELLEXTINIT)this;
+ }
+ else if (IsEqualIID(riid, IID_IContextMenu))
+ {
+ *ppv = (LPCONTEXTMENU)this;
+ }
+
+ if (*ppv)
+ {
+ AddRef();
+
+ return NOERROR;
+ }
+
+ return ResultFromScode(E_NOINTERFACE);
+}
+
+STDMETHODIMP_(ULONG) CNWHoodContextMenu::AddRef()
+{
+ return ++_cRef;
+}
+
+STDMETHODIMP_(ULONG) CNWHoodContextMenu::Release()
+{
+ if (--_cRef)
+ return _cRef;
+
+ delete this;
+
+ return 0L;
+}
+
+//
+// FUNCTION: CNWHoodContextMenu::Initialize(LPCITEMIDLIST, LPDATAOBJECT, HKEY)
+//
+// PURPOSE: Called by the shell when initializing a context menu or property
+// sheet extension.
+//
+// PARAMETERS:
+// pIDFolder - Specifies the parent folder
+// pDataObj - Spefifies the set of items selected in that folder.
+// hRegKey - Specifies the type of the focused item in the selection.
+//
+// RETURN VALUE:
+//
+// NOERROR in all cases.
+//
+// COMMENTS: Note that at the time this function is called, we don't know
+// (or care) what type of shell extension is being initialized.
+// It could be a context menu or a property sheet.
+//
+
+STDMETHODIMP CNWHoodContextMenu::Initialize( LPCITEMIDLIST pIDFolder,
+ LPDATAOBJECT pDataObj,
+ HKEY hRegKey)
+{
+ // We do not need the registry handle so ignore it.
+
+ // Initialize can be called more than once
+
+ if (_pDataObj)
+ _pDataObj->Release();
+
+ // Duplicate the object pointer
+
+ if (pDataObj)
+ {
+ _pDataObj = pDataObj;
+ pDataObj->AddRef();
+ }
+
+ return NOERROR;
+}
+
diff --git a/private/nw/svcdlls/nwwks/client/nwshext.h b/private/nw/svcdlls/nwwks/client/nwshext.h
new file mode 100644
index 000000000..8e1230c20
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/nwshext.h
@@ -0,0 +1,279 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ nwshext.h
+
+Abstract:
+
+ All C++ classes used in implementing shell extensions.
+
+Author:
+
+ Yi-Hsin Sung (yihsins) 20-Oct-1995
+
+Revision History:
+
+--*/
+
+#ifndef _NWSHEXT_H_
+#define _NWSHEXT_H_
+
+BOOL
+GetNetResourceFromShell(
+ LPDATAOBJECT pDataObj,
+ LPNETRESOURCE pBuffer,
+ UINT dwBufferSize
+);
+
+/******************************************************************************/
+
+// this class factory object creates context menu handlers for netware objects
+class CNWObjContextMenuClassFactory : public IClassFactory
+{
+protected:
+ ULONG _cRef;
+
+public:
+ CNWObjContextMenuClassFactory();
+ ~CNWObjContextMenuClassFactory();
+
+ // IUnknown members
+
+ STDMETHODIMP QueryInterface( REFIID, LPVOID FAR *);
+ STDMETHODIMP_(ULONG) AddRef();
+ STDMETHODIMP_(ULONG) Release();
+
+ // IClassFactory members
+
+ STDMETHODIMP CreateInstance( LPUNKNOWN, REFIID, LPVOID FAR *);
+ STDMETHODIMP LockServer( BOOL);
+
+};
+
+typedef CNWObjContextMenuClassFactory *LPCNWOBJCONTEXTMENUCLASSFACTORY;
+
+typedef struct _NWMENUITEM
+{
+ UINT idResourceString;
+ UINT idCommand;
+} NWMENUITEM, *LPNWMENUITEM;
+
+// this is the actual context menu handler for netware objects
+class CNWObjContextMenu : public IContextMenu,
+ IShellExtInit,
+ IShellPropSheetExt
+{
+protected:
+ ULONG _cRef;
+ LPDATAOBJECT _pDataObj;
+ NWMENUITEM *_pIdTable;
+ BYTE _buffer[MAX_ONE_NETRES_SIZE];
+
+public:
+ BOOL _fGotClusterInfo;
+ DWORD _dwTotal;
+ DWORD _dwFree;
+
+ DWORD *_paHelpIds;
+
+ CNWObjContextMenu();
+ ~CNWObjContextMenu();
+
+ // IUnknown members
+
+ STDMETHODIMP QueryInterface( REFIID, LPVOID FAR *);
+ STDMETHODIMP_(ULONG) AddRef();
+ STDMETHODIMP_(ULONG) Release();
+
+ // IShellContextMenu members
+
+ STDMETHODIMP QueryContextMenu( HMENU hMenu,
+ UINT indexMenu,
+ UINT idCmdFirst,
+ UINT idCmdLast,
+ UINT uFlags);
+
+ STDMETHODIMP InvokeCommand( LPCMINVOKECOMMANDINFO lpcmi);
+
+ STDMETHODIMP GetCommandString( UINT idCmd,
+ UINT uFlags,
+ UINT FAR *reserved,
+ LPSTR pszName,
+ UINT cchMax);
+
+ // IShellExtInit methods
+
+ STDMETHODIMP Initialize( LPCITEMIDLIST pIDFolder,
+ LPDATAOBJECT pDataObj,
+ HKEY hKeyID);
+
+ // IShellPropSheetExt methods
+
+ STDMETHODIMP AddPages( LPFNADDPROPSHEETPAGE lpfnAddPage,
+ LPARAM lParam);
+
+ STDMETHODIMP ReplacePage( UINT uPageID,
+ LPFNADDPROPSHEETPAGE lpfnReplaceWith,
+ LPARAM lParam);
+
+ VOID FillAndAddPage( LPFNADDPROPSHEETPAGE lpfnAddPage,
+ LPARAM lParam,
+ DLGPROC pfnDlgProc,
+ LPWSTR pszTemplate );
+
+ // Other misc methods
+
+ LPNETRESOURCE QueryNetResource()
+ { return ( LPNETRESOURCE ) _buffer; }
+
+};
+typedef CNWObjContextMenu *LPCNWOBJCONTEXTMENU;
+
+/******************************************************************************/
+
+// this class factory object creates context menu handlers for netware folders
+class CNWFldContextMenuClassFactory : public IClassFactory
+{
+protected:
+ ULONG _cRef;
+
+public:
+ CNWFldContextMenuClassFactory();
+ ~CNWFldContextMenuClassFactory();
+
+ // IUnknown members
+
+ STDMETHODIMP QueryInterface( REFIID, LPVOID FAR *);
+ STDMETHODIMP_(ULONG) AddRef();
+ STDMETHODIMP_(ULONG) Release();
+
+ // IClassFactory members
+
+ STDMETHODIMP CreateInstance( LPUNKNOWN, REFIID, LPVOID FAR *);
+ STDMETHODIMP LockServer( BOOL);
+
+};
+
+typedef CNWFldContextMenuClassFactory *LPCNWFLDCONTEXTMENUCLASSFACTORY;
+
+// this is the actual context menu handler for netware objects
+class CNWFldContextMenu : public IContextMenu,
+ IShellExtInit
+{
+protected:
+ ULONG _cRef;
+ LPDATAOBJECT _pDataObj;
+ BYTE _buffer[MAX_ONE_NETRES_SIZE];
+
+public:
+ CNWFldContextMenu();
+ ~CNWFldContextMenu();
+
+ // IUnknown members
+
+ STDMETHODIMP QueryInterface( REFIID, LPVOID FAR *);
+ STDMETHODIMP_(ULONG) AddRef();
+ STDMETHODIMP_(ULONG) Release();
+
+ // IShellContextMenu members
+
+ STDMETHODIMP QueryContextMenu( HMENU hMenu,
+ UINT indexMenu,
+ UINT idCmdFirst,
+ UINT idCmdLast,
+ UINT uFlags);
+
+ STDMETHODIMP InvokeCommand( LPCMINVOKECOMMANDINFO lpcmi);
+
+ STDMETHODIMP GetCommandString( UINT idCmd,
+ UINT uFlags,
+ UINT FAR *reserved,
+ LPSTR pszName,
+ UINT cchMax);
+
+ // IShellExtInit methods
+
+ STDMETHODIMP Initialize( LPCITEMIDLIST pIDFolder,
+ LPDATAOBJECT pDataObj,
+ HKEY hKeyID);
+
+ BOOL IsNetWareObject( VOID );
+ HRESULT GetFSObject( LPWSTR pszPath, UINT cbMaxPath );
+
+};
+typedef CNWFldContextMenu *LPCNWFLDCONTEXTMENU;
+
+// this class factory object creates context menu handlers
+// for Network Neighborhood
+
+class CNWHoodContextMenuClassFactory : public IClassFactory
+{
+protected:
+ ULONG _cRef;
+
+public:
+ CNWHoodContextMenuClassFactory();
+ ~CNWHoodContextMenuClassFactory();
+
+ // IUnknown members
+
+ STDMETHODIMP QueryInterface( REFIID, LPVOID FAR *);
+ STDMETHODIMP_(ULONG) AddRef();
+ STDMETHODIMP_(ULONG) Release();
+
+ // IClassFactory members
+
+ STDMETHODIMP CreateInstance( LPUNKNOWN, REFIID, LPVOID FAR *);
+ STDMETHODIMP LockServer( BOOL);
+
+};
+
+typedef CNWHoodContextMenuClassFactory *LPCNWHOODCONTEXTMENUCLASSFACTORY;
+
+// this is the actual context menu handler for network neighborhood
+class CNWHoodContextMenu : public IContextMenu,
+ IShellExtInit
+{
+protected:
+ ULONG _cRef;
+ LPDATAOBJECT _pDataObj;
+
+public:
+ CNWHoodContextMenu();
+ ~CNWHoodContextMenu();
+
+ // IUnknown members
+
+ STDMETHODIMP QueryInterface( REFIID, LPVOID FAR *);
+ STDMETHODIMP_(ULONG) AddRef();
+ STDMETHODIMP_(ULONG) Release();
+
+ // IShellContextMenu members
+
+ STDMETHODIMP QueryContextMenu( HMENU hMenu,
+ UINT indexMenu,
+ UINT idCmdFirst,
+ UINT idCmdLast,
+ UINT uFlags);
+
+ STDMETHODIMP InvokeCommand( LPCMINVOKECOMMANDINFO lpcmi);
+
+ STDMETHODIMP GetCommandString( UINT idCmd,
+ UINT uFlags,
+ UINT FAR *reserved,
+ LPSTR pszName,
+ UINT cchMax);
+
+ // IShellExtInit methods
+
+ STDMETHODIMP Initialize( LPCITEMIDLIST pIDFolder,
+ LPDATAOBJECT pDataObj,
+ HKEY hKeyID);
+
+};
+typedef CNWHoodContextMenu *LPCNWHOODCONTEXTMENU;
+
+#endif // _NWSHEXT_H_
diff --git a/private/nw/svcdlls/nwwks/client/nwshhelp.h b/private/nw/svcdlls/nwwks/client/nwshhelp.h
new file mode 100644
index 000000000..214338c14
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/nwshhelp.h
@@ -0,0 +1,78 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ nwshhelp.h
+
+Abstract:
+
+ All help ids used in nwprovau.dll
+
+Author:
+
+ Yi-Hsin Sung (yihsins) 20-Oct-1995
+
+Revision History:
+
+--*/
+
+#ifndef _NWSHHELP_H_
+#define _NWSHHELP_H_
+
+#define NO_HELP ((DWORD) -1) // Disables Help on a control
+
+#define IDH_DLG_NETWORK_CREDENTIAL_HELP 3001
+
+#ifndef NT1057
+
+// Global WhoAmI dialog
+#define IDH_GLOBAL_SERVERLIST 3005
+#define IDH_GLOBAL_CONTEXT 3006
+#define IDH_GLOBAL_DETACH 3007
+#define IDH_GLOBAL_CHGPWD 3008
+
+// Server summary sheet
+#define IDH_SERVERNAME 3020
+#define IDH_COMMENT 3021
+#define IDH_VERSION 3022
+#define IDH_REVISION 3023
+#define IDH_CONNINUSE 3024
+#define IDH_MAXCONNS 3025
+
+// Share summary sheet
+#define IDH_SHARE_NAME 3030
+#define IDH_SHARE_SERVER 3031
+#define IDH_SHARE_PATH 3032
+#define IDH_SHARE_USED_SPC 3034
+#define IDH_SHARE_FREE_SPC 3035
+#define IDH_SHARE_MAX_SPC 3036
+#define IDH_SHARE_LFN_TXT 3037
+#define IDH_SHARE_PIE 3038
+
+// NDS sheet
+#define IDH_NDS_NAME 3061
+#define IDH_NDS_CLASS 3062
+#define IDH_NDS_COMMENT 3063
+
+// Printer summary sheet
+#define IDH_PRINTER_NAME 3070
+#define IDH_PRINTER_QUEUE 3071
+
+#if 0
+// Wkgrp summary sheet
+#define IDH_WKGRP_NAME 3040
+#define IDH_WKGRP_TYPE 3041
+
+// NDS Admin page
+#define IDH_ENABLE_SYSPOL 3100
+#define IDH_VOLUME_LABEL 3101
+#define IDH_VOLUME 3102
+#define IDH_DIRECTORY_LABEL 3103
+#define IDH_DIRECTORY 3104
+
+#endif
+#endif
+
+#endif // _NWSHHELP_H_
diff --git a/private/nw/svcdlls/nwwks/client/nwshmenu.cxx b/private/nw/svcdlls/nwwks/client/nwshmenu.cxx
new file mode 100644
index 000000000..d4cc28bdb
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/nwshmenu.cxx
@@ -0,0 +1,896 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ nwshmenu.cxx
+
+Abstract:
+
+ This module implements the IContextMenu member functions necessary to support
+ the context menu of NetWare shell extension.
+
+Author:
+
+ Yi-Hsin Sung (yihsins) 25-Oct-1995
+
+--*/
+
+
+extern "C"
+{
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+#include <shellapi.h>
+#include <shlobj.h>
+#define DONT_WANT_SHELLDEBUG
+#include <shsemip.h>
+#include <winnetwk.h>
+#include <ntddnwfs.h>
+#include "nwshrc.h"
+#include "nwwks.h"
+#include "nwutil.h"
+}
+
+#include "nwshcmn.h"
+#include "nwshext.h"
+
+#define MAX_VERB_SIZE 128
+#define MAX_SHELL_IDLIST_SIZE 512
+
+BOOL g_cfNetResource = 0; // Clipboard format
+BOOL g_cfIDList = 0;
+
+NWMENUITEM aServerVerbs[] = { { IDO_VERB_WHOAMI, 0 },
+ { IDO_VERB_LOGOUT, 0 },
+ { IDO_VERB_ATTACHAS, 0 },
+ { 0, 0 } };
+
+NWMENUITEM aDSVerbs[] = { { IDO_VERB_TREEWHOAMI, 0 },
+ // { IDO_VERB_SETDEFAULTCONTEXT, 0 },
+ { 0, 0 } };
+
+NWMENUITEM aDSTreeVerbs[] = { { IDO_VERB_TREEWHOAMI, 0 },
+ { 0, 0 } };
+
+NWMENUITEM aGlobalVerbs[] = { { IDO_VERB_GLOBALWHOAMI, 0 },
+ { 0, 0 } };
+
+NWMENUITEM aDirectoryVerbs[] = { { IDO_VERB_MAPNETWORKDRIVE, 0 },
+ { 0, 0 } };
+
+
+NWMENUITEM aHoodVerbs[] = { { IDO_VERB_GLOBALWHOAMI, 0 },
+ { 0, 0 } };
+
+
+HRESULT
+InsertCommandsArray( HMENU hMenu,
+ UINT indexMenu,
+ UINT idCmdFirst,
+ LPNWMENUITEM aVerbs );
+
+UINT
+LookupCommand( LPNWMENUITEM aVerbs,
+ LPCSTR pszCmd );
+
+UINT
+LookupResource( LPNWMENUITEM aVerbs,
+ UINT uiResourceOffset );
+
+UINT WINAPI
+HIDA_GetIDList( LPIDA hida,
+ UINT i,
+ LPITEMIDLIST pidlOut,
+ UINT cbMax);
+
+//
+// FUNCTION: CNWObjContextMenu::QueryContextMenu(HMENU, UINT, UINT, UINT, UINT)
+//
+// PURPOSE: Called by the shell just before the context menu is displayed.
+// This is where you add your specific menu items.
+//
+// PARAMETERS:
+// hMenu - Handle to the context menu
+// indexMenu - Index of where to begin inserting menu items
+// idCmdFirst - Lowest value for new menu ID's
+// idCmtLast - Highest value for new menu ID's
+// uFlags - Specifies the context of the menu event
+//
+// RETURN VALUE:
+//
+//
+// COMMENTS:
+//
+
+STDMETHODIMP CNWObjContextMenu::QueryContextMenu( HMENU hMenu,
+ UINT indexMenu,
+ UINT idCmdFirst,
+ UINT idCmdLast,
+ UINT uFlags )
+{
+ HRESULT hres;
+ LPNETRESOURCE pNetRes = (LPNETRESOURCE) _buffer;
+
+ if ( !::GetNetResourceFromShell( _pDataObj,
+ pNetRes,
+ sizeof( _buffer )))
+ {
+ // We cannot get the net resource of the selected object.
+
+ // Must return number of menu items we added.
+ // Nothing added here.
+ return ResultFromScode(MAKE_SCODE(SEVERITY_SUCCESS, FACILITY_NULL, 0 ));
+ }
+
+ // First, add a menu separator
+ if ( InsertMenu( hMenu, indexMenu, MF_SEPARATOR | MF_BYPOSITION, 0, NULL))
+ indexMenu++;
+
+ // Next, add menu items depending on display types
+ switch ( pNetRes->dwDisplayType )
+ {
+ case RESOURCEDISPLAYTYPE_ROOT:
+ case RESOURCEDISPLAYTYPE_NETWORK:
+ hres = InsertCommandsArray( hMenu, indexMenu,
+ idCmdFirst, _pIdTable = aGlobalVerbs );
+
+ break;
+
+ case RESOURCEDISPLAYTYPE_TREE:
+ hres = InsertCommandsArray( hMenu, indexMenu,
+ idCmdFirst, _pIdTable = aDSTreeVerbs );
+ break;
+
+ case RESOURCEDISPLAYTYPE_NDSCONTAINER:
+ hres = InsertCommandsArray( hMenu, indexMenu,
+ idCmdFirst, _pIdTable = aDSVerbs );
+ break;
+
+ case RESOURCEDISPLAYTYPE_SERVER:
+ {
+ // BUGBUG: Do we need to check if the server name is local
+ // and disallow operation???
+
+ hres = InsertCommandsArray( hMenu, indexMenu,
+ idCmdFirst, _pIdTable = aServerVerbs );
+
+ if (!SUCCEEDED(hres))
+ break;
+
+ LPBYTE pBuffer = NULL;
+ DWORD EntriesRead = 0;
+ DWORD ResumeKey = 0;
+ WCHAR szServerName[MAX_PATH + 1];
+
+ NwExtractServerName( pNetRes->lpRemoteName, szServerName );
+
+ // See if we are connected.
+ DWORD err = NwGetConnectionStatus( szServerName,
+ &ResumeKey,
+ &pBuffer,
+ &EntriesRead );
+
+ if ( err == NO_ERROR && EntriesRead > 0 )
+ {
+ PCONN_STATUS pConnStatus = (PCONN_STATUS) pBuffer;
+
+ ASSERT( EntriesRead == 1 );
+
+ if ( pConnStatus->fPreferred )
+ {
+ // This is a implicit preferred server connection
+ // so, don't show the connection and don't let the user
+ // logout of it since rdr doesn't allow it.
+ ::EnableMenuItem( hMenu,
+ LookupResource( aServerVerbs,
+ IDO_VERB_LOGOUT),
+ MF_GRAYED | MF_BYCOMMAND);
+
+ }
+ else if ( pConnStatus->fNds )
+ {
+ BOOL fInDefaultTree = FALSE;
+
+ err = NwIsServerInDefaultTree( pNetRes->lpRemoteName, &fInDefaultTree );
+
+ if ( (err == NO_ERROR) && fInDefaultTree )
+ {
+ // NDS connection and in the default tree, disable the Attach As button
+ ::EnableMenuItem( hMenu,
+ LookupResource( aServerVerbs,
+ IDO_VERB_ATTACHAS),
+ MF_GRAYED | MF_BYCOMMAND );
+ }
+ }
+ }
+ else
+ {
+ // If we are not attached or if error occurred when getting
+ // connection status, then disable the Logout button.
+ ::EnableMenuItem( hMenu,
+ LookupResource( aServerVerbs,
+ IDO_VERB_LOGOUT),
+ MF_GRAYED | MF_BYCOMMAND);
+ }
+
+ if ( pBuffer != NULL )
+ {
+ LocalFree( pBuffer );
+ pBuffer = NULL;
+ }
+ break;
+ }
+
+ default:
+ // Must return number of menu items we added.
+ // Nothing added here.
+ hres = ResultFromScode(MAKE_SCODE(SEVERITY_SUCCESS, FACILITY_NULL, 0 ));
+ break;
+
+ }
+
+ return hres;
+}
+
+//
+// FUNCTION: CNWObjContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO)
+//
+// PURPOSE: Called by the shell after the user has selected on of the
+// menu items that was added in QueryContextMenu().
+//
+// PARAMETERS:
+// lpcmi - Pointer to an CMINVOKECOMMANDINFO structure
+//
+// RETURN VALUE:
+//
+//
+// COMMENTS:
+//
+
+STDMETHODIMP CNWObjContextMenu::InvokeCommand( LPCMINVOKECOMMANDINFO lpcmi )
+{
+ HRESULT hres = ResultFromScode(E_INVALIDARG);
+ UINT idCmd = LookupCommand( _pIdTable , lpcmi->lpVerb );
+
+ if ( !idCmd )
+ return hres;
+
+ LPNETRESOURCE pNetRes = (LPNETRESOURCE) _buffer;
+
+ switch ( idCmd )
+ {
+ case IDO_VERB_GLOBALWHOAMI:
+ hres = NWUIGlobalWhoAmI( lpcmi->hwnd );
+ break;
+
+ case IDO_VERB_TREEWHOAMI:
+ hres = NWUIWhoAmI( lpcmi->hwnd, pNetRes );
+ break;
+
+#if 0
+ case IDO_VERB_SETDEFAULTCONTEXT:
+ hres = NWUISetDefaultContext( lpcmi->hwnd, pNetRes );
+ break;
+#endif
+
+ case IDO_VERB_WHOAMI:
+ hres = NWUIWhoAmI( lpcmi->hwnd, pNetRes );
+ break;
+
+ case IDO_VERB_LOGOUT:
+ {
+ BOOL fDisconnected = FALSE;
+ hres = NWUILogOut( lpcmi->hwnd, pNetRes, &fDisconnected );
+ if ( hres == NOERROR && fDisconnected )
+ {
+ // Logout is successful, need to notify shell
+
+ FORMATETC fmte = { g_cfIDList ? g_cfIDList
+ : (g_cfIDList=RegisterClipboardFormat( CFSTR_SHELLIDLIST)),
+ (DVTARGETDEVICE FAR *)NULL,
+ DVASPECT_CONTENT,
+ -1,
+ TYMED_HGLOBAL };
+ STGMEDIUM medium;
+
+ hres = _pDataObj->GetData( &fmte, &medium);
+
+ if (SUCCEEDED(hres))
+ {
+ // We got pointer to IDList
+ LPIDA pida = (LPIDA)GlobalLock(medium.hGlobal);
+
+ if ( pida )
+ {
+ BYTE BufIDList[MAX_SHELL_IDLIST_SIZE];
+ LPITEMIDLIST pidl = (LPITEMIDLIST) BufIDList;
+
+ if ( pidl )
+ {
+ // Convert IDA to IDList for this call
+ HIDA_GetIDList( pida,
+ 0, // One object should present
+ pidl ,
+ MAX_SHELL_IDLIST_SIZE);
+
+ // Call SHchangeNotify
+ g_pFuncSHChangeNotify( SHCNE_SERVERDISCONNECT,
+ SHCNF_IDLIST,
+ pidl,
+ NULL);
+ }
+
+ GlobalUnlock(medium.hGlobal);
+ }
+ }
+
+ }
+ break;
+ }
+
+ case IDO_VERB_ATTACHAS:
+ hres = NWUIAttachAs( lpcmi->hwnd, pNetRes );
+ break;
+ }
+
+ return hres;
+}
+
+
+//
+// FUNCTION: CNWObjContextMenu::GetCommandString( UINT, UINT, UINT FAR *, LPSTR, UINT )
+//
+// PURPOSE: Called by the shell after the user has selected on of the
+// menu items that was added in QueryContextMenu().
+//
+// PARAMETERS:
+//
+// RETURN VALUE:
+//
+//
+// COMMENTS:
+//
+
+STDMETHODIMP CNWObjContextMenu::GetCommandString( UINT idCmd,
+ UINT uFlags,
+ UINT FAR *reserved,
+ LPSTR pszName,
+ UINT cchMax )
+{
+ if ( uFlags == GCS_HELPTEXT && _pIdTable != NULL )
+ {
+ ::LoadString( ::hmodNW,
+ IDS_VERBS_HELP_BASE + _pIdTable[idCmd].idResourceString,
+ (LPWSTR) pszName,
+ cchMax );
+
+ return NOERROR;
+ }
+
+ return E_NOTIMPL;
+}
+
+//
+// FUNCTION: CNWFldContextMenu::QueryContextMenu(HMENU, UINT, UINT, UINT, UINT)
+//
+// PURPOSE: Called by the shell just before the context menu is displayed.
+// This is where you add your specific menu items.
+//
+// PARAMETERS:
+// hMenu - Handle to the context menu
+// indexMenu - Index of where to begin inserting menu items
+// idCmdFirst - Lowest value for new menu ID's
+// idCmtLast - Highest value for new menu ID's
+// uFlags - Specifies the context of the menu event
+//
+// RETURN VALUE:
+//
+//
+// COMMENTS:
+//
+
+STDMETHODIMP CNWFldContextMenu::QueryContextMenu( HMENU hMenu,
+ UINT indexMenu,
+ UINT idCmdFirst,
+ UINT idCmdLast,
+ UINT uFlags )
+{
+ UINT idCmd = idCmdFirst;
+
+ if ( IsNetWareObject() )
+ {
+ WCHAR szFullPath[MAX_PATH+1];
+
+ if ( GetFSObject( szFullPath, sizeof( szFullPath )) == NOERROR )
+ {
+ BOOL fUNC = FALSE;
+
+ // Check if the name at least contains the "\\server\share\dir"
+ // We need to add "Map Network Drive" menu in this case.
+ if (( szFullPath[0] == L'\\') && ( szFullPath[1] == L'\\'))
+ {
+ LPWSTR pszLastSlash = wcschr( szFullPath + 2, L'\\');
+ if ( pszLastSlash )
+ pszLastSlash = wcschr( pszLastSlash+1, L'\\');
+
+ if ( pszLastSlash != NULL )
+ fUNC = TRUE;
+ }
+
+ if ( fUNC )
+ {
+ LPNETRESOURCE pNetRes = (LPNETRESOURCE) _buffer;
+ WCHAR szProvider[MAX_PATH+1];
+
+ // Build a net resource that can be used to connect
+
+ // store the provider name first
+ wcscpy( szProvider, pNetRes->lpProvider );
+
+ // zero out the memory cause it is filled by IsNetWareObject
+ RtlZeroMemory( pNetRes, sizeof(NETRESOURCE));
+
+ pNetRes->dwType = RESOURCETYPE_DISK;
+ pNetRes->lpRemoteName = (LPWSTR) ((DWORD)pNetRes + sizeof(NETRESOURCE));
+ wcscpy( pNetRes->lpRemoteName, szFullPath );
+
+ pNetRes->lpProvider = (LPWSTR) ((DWORD)pNetRes->lpRemoteName + (wcslen(szFullPath)+1)*sizeof(WCHAR));
+ wcscpy( pNetRes->lpProvider, szProvider );
+
+ if ( InsertMenu(hMenu, indexMenu, MF_SEPARATOR | MF_BYPOSITION, 0, NULL))
+ {
+ indexMenu++;
+ }
+
+ return InsertCommandsArray( hMenu, indexMenu,
+ idCmdFirst, aDirectoryVerbs );
+ }
+ }
+ }
+
+ // Must return number of menu items we added.
+ // Nothing added here.
+ return ResultFromScode(MAKE_SCODE(SEVERITY_SUCCESS, FACILITY_NULL, 0 ));
+
+}
+
+//
+// FUNCTION: CNWFldContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO)
+//
+// PURPOSE: Called by the shell after the user has selected on of the
+// menu items that was added in QueryContextMenu().
+//
+// PARAMETERS:
+// lpcmi - Pointer to an CMINVOKECOMMANDINFO structure
+//
+// RETURN VALUE:
+//
+//
+// COMMENTS:
+//
+
+STDMETHODIMP CNWFldContextMenu::InvokeCommand( LPCMINVOKECOMMANDINFO lpcmi )
+{
+ HRESULT hres = ResultFromScode(E_INVALIDARG);
+ UINT idCmd = LookupCommand( aDirectoryVerbs , lpcmi->lpVerb );
+
+ if ( !idCmd )
+ return hres;
+
+ LPNETRESOURCE pNetRes = (LPNETRESOURCE) _buffer;
+
+ switch ( idCmd )
+ {
+ case IDO_VERB_MAPNETWORKDRIVE:
+ hres = NWUIMapNetworkDrive( lpcmi->hwnd, pNetRes );
+ break;
+ }
+
+ return hres;
+}
+
+
+//
+// FUNCTION: CNWFldContextMenu::GetCommandString( UINT, UINT, UINT FAR *, LPSTR, UINT )
+//
+// PURPOSE: Called by the shell after the user has selected on of the
+// menu items that was added in QueryContextMenu().
+//
+// PARAMETERS:
+//
+// RETURN VALUE:
+//
+//
+// COMMENTS:
+//
+
+STDMETHODIMP CNWFldContextMenu::GetCommandString( UINT idCmd,
+ UINT uFlags,
+ UINT FAR *reserved,
+ LPSTR pszName,
+ UINT cchMax )
+{
+ if ( uFlags == GCS_HELPTEXT )
+ {
+ ::LoadString( ::hmodNW,
+ IDS_VERBS_HELP_BASE + IDO_VERB_MAPNETWORKDRIVE,
+ (LPWSTR) pszName,
+ cchMax );
+
+ return NOERROR;
+ }
+
+ return E_NOTIMPL;
+}
+
+//
+// Method checks if the selected object belongs the netware provider
+//
+BOOL CNWFldContextMenu::IsNetWareObject( VOID )
+{
+ LPNETRESOURCE pNetRes = (LPNETRESOURCE) _buffer;
+
+ if ( !::GetNetResourceFromShell( _pDataObj,
+ pNetRes,
+ sizeof(_buffer)))
+ {
+ // Cannot get the NETRESOURCE of the selected object,
+ // hence assume that the object is not a NetWare object.
+ return FALSE;
+ }
+
+ if ( ( pNetRes->lpProvider != NULL )
+ && ( _wcsicmp( pNetRes->lpProvider, g_szProviderName ) == 0 )
+ )
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+//
+// Method obtains file system name associated with selected shell object
+//
+HRESULT CNWFldContextMenu::GetFSObject( LPWSTR pszPath, UINT cbMaxPath )
+{
+ FORMATETC fmte = { CF_HDROP,
+ (DVTARGETDEVICE FAR *) NULL,
+ DVASPECT_CONTENT,
+ -1,
+ TYMED_HGLOBAL };
+
+ STGMEDIUM medium;
+ HRESULT hres = _pDataObj->GetData( &fmte, &medium);
+
+ if (SUCCEEDED(hres))
+ {
+ if ( g_pFuncSHDragQueryFile )
+ {
+ HDROP hdrop = (HDROP) medium.hGlobal;
+ UINT cFiles = (*g_pFuncSHDragQueryFile)( hdrop, (UINT)-1, NULL, 0 );
+
+ (*g_pFuncSHDragQueryFile)( hdrop, 0, pszPath, cbMaxPath );
+
+ ODS(L"CNWFldContextMenu::GetFSObject()\n");
+ ODS( pszPath );
+ ODS(L"\n");
+ }
+
+ //
+ // HACK: We are supposed to call ReleaseStgMedium. This is a temporary
+ // hack until OLE 2.01 for Chicago is released.
+ //
+ if (medium.pUnkForRelease)
+ {
+ medium.pUnkForRelease->Release();
+ }
+ else
+ {
+ GlobalFree(medium.hGlobal);
+ }
+ }
+
+ return hres;
+}
+
+
+// FUNCTION: CNWHoodContextMenu::QueryContextMenu(HMENU, UINT, UINT, UINT, UINT)
+//
+// PURPOSE: Called by the shell just before the context menu is displayed.
+// This is where you add your specific menu items.
+//
+// PARAMETERS:
+// hMenu - Handle to the context menu
+// indexMenu - Index of where to begin inserting menu items
+// idCmdFirst - Lowest value for new menu ID's
+// idCmtLast - Highest value for new menu ID's
+// uFlags - Specifies the context of the menu event
+//
+// RETURN VALUE:
+//
+//
+// COMMENTS:
+//
+
+STDMETHODIMP CNWHoodContextMenu::QueryContextMenu( HMENU hMenu,
+ UINT indexMenu,
+ UINT idCmdFirst,
+ UINT idCmdLast,
+ UINT uFlags )
+{
+ // First, insert a menu separator
+ if ( InsertMenu(hMenu, indexMenu, MF_SEPARATOR | MF_BYPOSITION, 0, NULL))
+ {
+ indexMenu++;
+ }
+
+ // Then, insert the verbs
+ return InsertCommandsArray( hMenu, indexMenu,
+ idCmdFirst, aHoodVerbs );
+}
+
+//
+// FUNCTION: CNWHoodContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO)
+//
+// PURPOSE: Called by the shell after the user has selected on of the
+// menu items that was added in QueryContextMenu().
+//
+// PARAMETERS:
+// lpcmi - Pointer to an CMINVOKECOMMANDINFO structure
+//
+// RETURN VALUE:
+//
+//
+// COMMENTS:
+//
+
+STDMETHODIMP CNWHoodContextMenu::InvokeCommand( LPCMINVOKECOMMANDINFO lpcmi )
+{
+ HRESULT hres = ResultFromScode(E_INVALIDARG);
+ UINT idCmd = LookupCommand( aHoodVerbs , lpcmi->lpVerb );
+
+ if ( !idCmd )
+ return hres;
+
+ switch ( idCmd )
+ {
+ case IDO_VERB_GLOBALWHOAMI:
+ hres = NWUIGlobalWhoAmI( lpcmi->hwnd );
+ break;
+ }
+
+ return hres;
+}
+
+
+//
+// FUNCTION: CNWHoodContextMenu::GetCommandString( UINT, UINT, UINT FAR *, LPSTR, UINT)
+//
+// PURPOSE: Called by the shell after the user has selected on of the
+// menu items that was added in QueryContextMenu().
+//
+// PARAMETERS:
+//
+// RETURN VALUE:
+//
+//
+// COMMENTS:
+//
+
+STDMETHODIMP CNWHoodContextMenu::GetCommandString( UINT idCmd,
+ UINT uFlags,
+ UINT FAR *reserved,
+ LPSTR pszName,
+ UINT cchMax )
+{
+ if ( uFlags == GCS_HELPTEXT )
+ {
+ ::LoadString( ::hmodNW,
+ IDS_VERBS_HELP_BASE + IDO_VERB_GLOBALWHOAMI,
+ (LPWSTR) pszName,
+ cchMax );
+
+ return NOERROR;
+ }
+
+ return E_NOTIMPL;
+}
+
+//
+// Method gets the NETRESOURCE of the selected object
+//
+BOOL GetNetResourceFromShell( LPDATAOBJECT pDataObj,
+ LPNETRESOURCE pNetRes,
+ UINT dwBufferSize )
+{
+ FORMATETC fmte = { g_cfNetResource ? g_cfNetResource
+ : (g_cfNetResource=RegisterClipboardFormat(CFSTR_NETRESOURCES)),
+ (DVTARGETDEVICE FAR *) NULL,
+ DVASPECT_CONTENT,
+ -1,
+ TYMED_HGLOBAL };
+
+ STGMEDIUM medium;
+ UINT cItems;
+
+ if ( pNetRes == NULL )
+ return FALSE;
+
+ memset( pNetRes, 0, dwBufferSize );
+
+ if ( !g_pFuncSHGetNetResource ) // Not loaded
+ return FALSE;
+
+ HRESULT hres = pDataObj->GetData( &fmte, &medium );
+
+ if (!SUCCEEDED(hres))
+ return FALSE;
+
+ HNRES hnres = medium.hGlobal;
+
+ // Get the number of selected items
+ cItems = (*g_pFuncSHGetNetResource)( hnres, (UINT)-1, NULL, 0);
+
+ if ( cItems == 0 ) // Nothing selected
+ return FALSE;
+
+ // Get the NETRESOURCE of the first item
+ (*g_pFuncSHGetNetResource)( hnres, 0, pNetRes, dwBufferSize);
+
+#if DBG
+ WCHAR szTemp[32];
+ wsprintf(szTemp, L"DisplayType = %d\n", pNetRes->dwDisplayType );
+
+ ODS(L"\n**** GetNetResourceFromShell ***\n");
+ ODS(pNetRes->lpProvider );
+ ODS(L"\n");
+ ODS(pNetRes->lpRemoteName );
+ ODS(L"\n");
+ ODS(szTemp );
+ ODS(L"\n\n");
+#endif
+
+ //
+ // HACK: We are supposed to call ReleaseStgMedium. This is a temporary
+ // hack until OLE 2.01 for Chicago is released.
+ //
+ if (medium.pUnkForRelease)
+ {
+ medium.pUnkForRelease->Release();
+ }
+ else
+ {
+ GlobalFree(medium.hGlobal);
+ }
+
+ return TRUE;
+}
+
+//-------------------------------------------------------------------//
+
+HRESULT InsertCommandsArray( HMENU hMenu,
+ UINT indexMenu,
+ UINT idCmdFirst,
+ LPNWMENUITEM aVerbs )
+{
+ UINT idNewCmdFirst = idCmdFirst;
+ WCHAR szVerb[MAX_VERB_SIZE];
+
+ for ( int i = 0; aVerbs[i].idResourceString ; i++)
+ {
+ if ( ::LoadString( ::hmodNW,
+ aVerbs[i].idResourceString + IDS_VERBS_BASE,
+ szVerb,
+ sizeof(szVerb)/sizeof(szVerb[0])))
+ {
+ if (::InsertMenu( hMenu,
+ indexMenu,
+ MF_STRING | MF_BYPOSITION,
+ idNewCmdFirst,
+ szVerb))
+ {
+ // Add command id to the array
+ aVerbs[i].idCommand = idNewCmdFirst;
+
+ // Update command id and index
+ idNewCmdFirst++;
+ if (indexMenu != (UINT)-1)
+ indexMenu++;
+ }
+ }
+ }
+
+ return ResultFromScode(MAKE_SCODE(SEVERITY_SUCCESS,
+ FACILITY_NULL,
+ (USHORT)(idNewCmdFirst-idCmdFirst)));
+}
+
+UINT LookupCommand( LPNWMENUITEM aVerbs, LPCSTR pszCmd )
+{
+ if (HIWORD(pszCmd))
+ {
+ // Possible that shell will use string commands, but unlikely
+
+ WCHAR szVerb[MAX_VERB_SIZE];
+ for ( int i=0; aVerbs[i].idResourceString; i++)
+ {
+ if ( ::LoadString( ::hmodNW,
+ aVerbs[i].idResourceString + IDS_VERBS_BASE,
+ szVerb,
+ sizeof(szVerb)/sizeof(szVerb[0])))
+ {
+ if( ::lstrcmpi( (LPCWSTR) pszCmd, szVerb) == 0)
+ return( aVerbs[i].idResourceString);
+ }
+ }
+
+ return 0;
+ }
+ else
+ {
+ return( aVerbs[LOWORD(pszCmd)].idResourceString);
+ }
+}
+
+UINT LookupResource( LPNWMENUITEM aVerbs, UINT uiResourceOffset )
+{
+ for ( int i = 0; aVerbs[i].idResourceString; i++ )
+ {
+ if ( aVerbs[i].idResourceString == uiResourceOffset )
+ return aVerbs[i].idCommand;
+ }
+
+ return 0;
+}
+
+//-------------------------------------------------------------------//
+
+#define _ILSkip(pidl, cb) ((LPITEMIDLIST)(((BYTE*)(pidl))+cb))
+#define _ILNext(pidl) _ILSkip(pidl, (pidl)->mkid.cb)
+
+#define HIDA_GetPIDLFolder(pida) (LPCITEMIDLIST)(((LPBYTE)pida)+(pida)->aoffset[0])
+#define HIDA_GetPIDLItem(pida, i) (LPCITEMIDLIST)(((LPBYTE)pida)+(pida)->aoffset[i+1])
+
+static
+UINT WINAPI MyILGetSize(LPCITEMIDLIST pidl)
+{
+ UINT cbTotal = 0;
+ if (pidl)
+ {
+ cbTotal += sizeof(pidl->mkid.cb); // Null terminator
+ while (pidl->mkid.cb)
+ {
+ cbTotal += pidl->mkid.cb;
+ pidl = _ILNext(pidl);
+ }
+ }
+
+ return cbTotal;
+}
+
+UINT WINAPI HIDA_GetIDList( LPIDA hida, UINT i, LPITEMIDLIST pidlOut, UINT cbMax)
+{
+ LPCITEMIDLIST pidlFolder = HIDA_GetPIDLFolder((LPIDA)hida);
+ LPCITEMIDLIST pidlItem = HIDA_GetPIDLItem((LPIDA)hida, i);
+
+ UINT cbFolder = MyILGetSize(pidlFolder)-sizeof(USHORT);
+ UINT cbItem = MyILGetSize(pidlItem);
+
+ if (cbMax < cbFolder+cbItem)
+ {
+ if (pidlOut)
+ pidlOut->mkid.cb = 0;
+ }
+ else
+ {
+ memmove(pidlOut, pidlFolder, cbFolder);
+ memmove(((LPBYTE)pidlOut)+cbFolder, pidlItem, cbItem);
+ }
+
+ return (cbFolder+cbItem);
+}
diff --git a/private/nw/svcdlls/nwwks/client/nwshmisc.cxx b/private/nw/svcdlls/nwwks/client/nwshmisc.cxx
new file mode 100644
index 000000000..f375d8559
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/nwshmisc.cxx
@@ -0,0 +1,261 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ nwshmisc.cxx
+
+Abstract:
+
+ This module implements misc methods used in the shell extension classes.
+
+Author:
+
+ Yi-Hsin Sung (yihsins) 25-Oct-1995
+
+--*/
+
+extern "C"
+{
+#include <windows.h>
+#include <shellapi.h>
+#include <shlobj.h>
+#define DONT_WANT_SHELLDEBUG
+#include <shsemip.h>
+}
+
+#include "nwshcmn.h"
+
+#define MAX_RESOURCE_STRING_LENGTH 256
+
+VOID HideControl( HWND hwndDlg, WORD wID )
+{
+ HWND hwndTmp = ::GetDlgItem( hwndDlg, wID );
+ ::EnableWindow( hwndTmp, FALSE );
+ ::ShowWindow( hwndTmp, FALSE );
+}
+
+VOID UnHideControl( HWND hwndDlg, WORD wID )
+{
+ HWND hwndTmp = ::GetDlgItem( hwndDlg, wID );
+ ::EnableWindow( hwndTmp, TRUE );
+ ::ShowWindow( hwndTmp, TRUE );
+}
+
+VOID EnableDlgItem( HWND hwndDlg, WORD wID, BOOL fEnable)
+{
+ HWND hwndTmp = ::GetDlgItem( hwndDlg, wID );
+
+ ::EnableWindow( hwndTmp, fEnable);
+}
+
+/*
+ * LoadErrorPrintf
+ * -------------
+ *
+ * Uses normal printf style format string
+ */
+DWORD
+LoadMsgErrorPrintf(
+ LPWSTR *ppszMessage,
+ UINT uiMsg,
+ DWORD errNum
+)
+{
+ DWORD nLen = 0;
+ DWORD err = NO_ERROR;
+ LPWSTR pszError = NULL;
+ WCHAR szError[20];
+
+ *ppszMessage = NULL;
+
+ //
+ // Try to get the error string associated with the given number
+ // from the system. If we cannot find the string, then
+ // just show the number.
+ //
+
+ nLen = ::FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM
+ | FORMAT_MESSAGE_IGNORE_INSERTS
+ | FORMAT_MESSAGE_ALLOCATE_BUFFER,
+ NULL, // ignored
+ errNum, // Message resource id
+ 0, // Language id
+ (LPWSTR) &pszError,
+ // Return pointer to formatted text
+ 256, // Min.length
+ NULL );
+
+ if ( nLen == 0 || pszError == NULL )
+ {
+ wsprintf( szError, L"%d", errNum );
+ }
+
+ err = LoadMsgPrintf( ppszMessage, uiMsg, pszError? pszError : szError );
+
+ if ( pszError )
+ ::LocalFree( pszError );
+
+ return err;
+}
+
+/*
+ * LoadMsgPrintf
+ * -------------
+ *
+ * Uses normal printf style format string
+ */
+DWORD
+LoadMsgPrintf(
+ LPWSTR *ppszMessage,
+ UINT uiMsg,
+ ...
+ )
+{
+ WCHAR szMessage[512];
+ DWORD err = NO_ERROR;
+ DWORD nLen = 0;
+ va_list start;
+
+ va_start( start, uiMsg );
+
+ *ppszMessage = NULL;
+
+ if ( ::LoadString( ::hmodNW, uiMsg, szMessage,
+ sizeof(szMessage)/sizeof(szMessage[0])))
+ {
+ nLen = ::FormatMessage( FORMAT_MESSAGE_FROM_STRING
+ | FORMAT_MESSAGE_ALLOCATE_BUFFER,
+ szMessage,
+ 0, // Message resource id, ignored
+ 0, // Language id
+ (LPWSTR) ppszMessage,
+ // Return pointer to formatted text
+ 256, // Min.length
+ &start );
+
+ if ( nLen == 0 || *ppszMessage == NULL )
+ err = GetLastError();
+
+ }
+
+ va_end(start);
+
+ return err;
+}
+
+/*
+ * MsgBoxErrorPrintf
+ * ------------
+ *
+ * Message box routine
+ *
+ */
+DWORD
+MsgBoxErrorPrintf(
+ HWND hwnd,
+ UINT uiMsg,
+ UINT uiTitle,
+ UINT uiFlags,
+ DWORD errNum,
+ LPWSTR pszInsertStr
+ )
+{
+ DWORD nLen = 0;
+ DWORD err = NO_ERROR;
+ LPWSTR pszError = NULL;
+ WCHAR szError[20];
+
+ nLen = ::FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM
+ | FORMAT_MESSAGE_IGNORE_INSERTS
+ | FORMAT_MESSAGE_ALLOCATE_BUFFER,
+ NULL, // ignored
+ errNum, // Message resource id
+ 0, // Language id
+ (LPWSTR) &pszError,
+ // Return pointer to formatted text
+ 256, // Min.length
+ NULL );
+
+ if ( nLen == 0 || pszError == NULL )
+ wsprintf( szError, L"%d", errNum );
+
+ if ( pszInsertStr )
+ {
+ err = MsgBoxPrintf( hwnd, uiMsg, uiTitle, uiFlags,
+ pszError? pszError : szError, pszInsertStr );
+ }
+ else
+ {
+ err = MsgBoxPrintf( hwnd, uiMsg, uiTitle, uiFlags,
+ pszError? pszError : szError );
+ }
+
+ if ( pszError )
+ ::LocalFree( pszError );
+
+ return err;
+
+}
+
+
+/*
+ * MsgBoxPrintf
+ * ------------
+ *
+ * Message box routine
+ *
+ */
+DWORD
+MsgBoxPrintf(
+ HWND hwnd,
+ UINT uiMsg,
+ UINT uiTitle,
+ UINT uiFlags,
+ ...
+ )
+{
+ WCHAR szTitle[MAX_RESOURCE_STRING_LENGTH];
+ WCHAR szMessage[MAX_RESOURCE_STRING_LENGTH];
+ LPWSTR lpFormattedMessage = NULL;
+ DWORD err = NO_ERROR;
+ va_list start;
+
+ va_start(start,uiFlags);
+
+ if ( ::LoadString( ::hmodNW, uiMsg, szMessage, sizeof(szMessage)/sizeof(szMessage[0]))
+ && ::LoadString( ::hmodNW, uiTitle, szTitle, sizeof(szTitle)/sizeof(szTitle[0]))
+ )
+ {
+ DWORD nLen = ::FormatMessage( FORMAT_MESSAGE_FROM_STRING
+ | FORMAT_MESSAGE_ALLOCATE_BUFFER,
+ szMessage,
+ 0, // Resource Id, ignored
+ NULL, // Language Id, ignored
+ (LPWSTR) &lpFormattedMessage,
+ // Return pointer to formatted text
+ 256, // Min.length
+ &start );
+
+ if ( nLen == 0 || lpFormattedMessage == NULL )
+ {
+ err = GetLastError();
+ goto CleanExit;
+ }
+
+ err = ::MessageBox( hwnd,
+ lpFormattedMessage,
+ szTitle,
+ uiFlags );
+
+ ::LocalFree( lpFormattedMessage );
+ }
+
+CleanExit:
+
+ va_end(start);
+
+ return err;
+
+}
diff --git a/private/nw/svcdlls/nwwks/client/nwshprop.cxx b/private/nw/svcdlls/nwwks/client/nwshprop.cxx
new file mode 100644
index 000000000..01a3773a2
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/nwshprop.cxx
@@ -0,0 +1,1326 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ nwshprop.cxx
+
+Abstract:
+
+ This module implements the property pages of shell extension classes.
+
+Author:
+
+ Yi-Hsin Sung (yihsins) 25-Oct-1995
+
+--*/
+
+extern "C"
+{
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+
+#include <commctrl.h>
+#include <shellapi.h>
+#include <shlobj.h>
+#define DONT_WANT_SHELLDEBUG
+#include <shsemip.h>
+
+#include <nwapi32.h>
+#include <ndsapi32.h>
+#include <nwmisc.h>
+#include <nds.h>
+#include "nwshrc.h"
+#include "nwutil.h"
+#include "drawpie.h"
+}
+
+#include "nwshcmn.h"
+#include "nwshext.h"
+
+
+LPWSTR WINAPI AddCommas( DWORD dw, LPWSTR pszResult, DWORD dwSize );
+LPWSTR WINAPI ShortSizeFormat64( ULONGLONG dw64, LPWSTR szBuf );
+
+#define NAMESPACE_DOS 0
+#define NAMESPACE_MAC 1
+#define NAMESPACE_UNIX 2
+#define NAMESPACE_FTAM 3
+#define NAMESPACE_OS2 4
+
+
+BOOL
+CALLBACK
+NDSPage_DlgProc(
+ HWND hDlg,
+ UINT uMessage,
+ WPARAM wParam ,
+ LPARAM lParam
+);
+
+BOOL
+CALLBACK
+NWPage_DlgProc(
+ HWND hDlg,
+ UINT uMessage,
+ WPARAM wParam ,
+ LPARAM lParam
+);
+
+//
+// FUNCTION: CNWObjContextMenu::AddPages(LPFNADDPROPSHEETPAGE, LPARAM)
+//
+// PURPOSE: Called by the shell just before the property sheet is displayed.
+//
+// PARAMETERS:
+// lpfnAddPage - Pointer to the Shell's AddPage function
+// lParam - Passed as second parameter to lpfnAddPage
+//
+// RETURN VALUE:
+//
+// NOERROR in all cases. If for some reason our pages don't get added,
+// the Shell still needs to bring up the Properties... sheet.
+//
+// COMMENTS:
+//
+
+STDMETHODIMP CNWObjContextMenu::AddPages(LPFNADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam)
+{
+ LPNETRESOURCE pNetRes = (LPNETRESOURCE) _buffer;
+
+ if ( !::GetNetResourceFromShell( _pDataObj, pNetRes, sizeof( _buffer )))
+ {
+ // We could not get the net resource of the current object,
+ // hence we could not add the property pages
+ return NOERROR;
+ }
+
+ DWORD dwDialogId = 0;
+ BOOL fIsNds = NwIsNdsSyntax( pNetRes->lpRemoteName );
+
+ switch ( pNetRes->dwDisplayType )
+ {
+ case RESOURCEDISPLAYTYPE_SERVER:
+ dwDialogId = DLG_SERVER_SUMMARYINFO;
+ break;
+
+ case RESOURCEDISPLAYTYPE_NDSCONTAINER:
+ break;
+
+ case RESOURCEDISPLAYTYPE_TREE:
+ // We need to set fIsNds to TRUE since a tree name "\\tree"
+ // does not look like a NDS name
+ // and hence NwIsNDsSyntax will return FALSE.
+ fIsNds = TRUE;
+ break;
+
+ case RESOURCEDISPLAYTYPE_SHARE:
+ if ( pNetRes->dwType == RESOURCETYPE_PRINT )
+ dwDialogId = DLG_PRINTER_SUMMARYINFO;
+ else
+ dwDialogId = DLG_SHARE_SUMMARYINFO;
+ break;
+
+ case RESOURCEDISPLAYTYPE_ROOT:
+ case RESOURCEDISPLAYTYPE_NETWORK:
+ default:
+ // No property page need to be added here. Just return success.
+ return NOERROR;
+ }
+
+ if ( dwDialogId != 0 )
+ {
+ FillAndAddPage( lpfnAddPage, lParam,
+ (DLGPROC) ::NWPage_DlgProc,
+ MAKEINTRESOURCE( dwDialogId ));
+ }
+
+ // NOTE: Do we need to add another property page contain admin tools
+ // for chicago peer servers? Probably not!
+
+ if ( fIsNds )
+ {
+ FillAndAddPage( lpfnAddPage, lParam,
+ (DLGPROC) ::NDSPage_DlgProc,
+ pNetRes->dwDisplayType == RESOURCEDISPLAYTYPE_TREE ?
+ MAKEINTRESOURCE( DLG_NDS_SUMMARYINFO) :
+ MAKEINTRESOURCE( DLG_NDSCONT_SUMMARYINFO));
+
+ // NOTE: Need to add a page for system policy here if the user has admin privileges
+ // in the NDS tree.
+ }
+
+ return NOERROR;
+}
+
+//
+// FUNCTION: CNWObjContextMenu::ReplacePage(UINT, LPFNADDPROPSHEETPAGE, LPARAM)
+//
+// PURPOSE: Called by the shell only for Control Panel property sheet
+// extensions
+//
+// PARAMETERS:
+// uPageID - ID of page to be replaced
+// lpfnReplaceWith - Pointer to the Shell's Replace function
+// lParam - Passed as second parameter to lpfnReplaceWith
+//
+// RETURN VALUE:
+//
+// E_NOTIMPL, since we don't support this function. It should never be
+// called.
+
+// COMMENTS:
+//
+
+STDMETHODIMP CNWObjContextMenu::ReplacePage(UINT uPageID,
+ LPFNADDPROPSHEETPAGE lpfnReplaceWith,
+ LPARAM lParam)
+{
+ return E_NOTIMPL;
+}
+
+VOID CNWObjContextMenu::FillAndAddPage( LPFNADDPROPSHEETPAGE lpfnAddPage,
+ LPARAM lParam,
+ DLGPROC pfnDlgProc,
+ LPWSTR pszTemplate )
+{
+ PROPSHEETPAGE psp;
+ HPROPSHEETPAGE hpage;
+
+ psp.dwSize = sizeof(psp); // no extra data.
+ psp.dwFlags = PSP_USEREFPARENT;
+ psp.hInstance = ::hmodNW;
+ psp.pfnDlgProc = pfnDlgProc;
+ psp.pcRefParent = (UINT *) &g_cRefThisDll;
+ psp.pszTemplate = pszTemplate;
+ psp.hIcon = 0;
+ psp.pszTitle = NULL;
+ psp.pfnCallback = NULL;
+
+ psp.lParam = (LPARAM) this;
+ this->AddRef();
+
+ hpage = CreatePropertySheetPage(&psp);
+
+ if (hpage)
+ {
+ if (!lpfnAddPage(hpage, lParam))
+ DestroyPropertySheetPage(hpage);
+ }
+
+}
+
+// The following are arrays of help contexts for the property dialogs
+
+static DWORD aServerIds[] = { IDD_SERVER_NAME ,IDH_SERVERNAME,
+ IDD_SERVER_COMMENT_TXT ,IDH_COMMENT,
+ IDD_SERVER_COMMENT ,IDH_COMMENT,
+ IDD_SERVER_VERSION_TXT ,IDH_VERSION,
+ IDD_SERVER_VERSION ,IDH_VERSION,
+ IDD_SERVER_REVISION_TXT,IDH_REVISION,
+ IDD_SERVER_REVISION ,IDH_REVISION,
+ IDD_SERVER_CONNECT_TXT ,IDH_CONNINUSE,
+ IDD_SERVER_CONNECT ,IDH_CONNINUSE,
+ IDD_SERVER_MAXCON_TXT ,IDH_MAXCONNS,
+ IDD_SERVER_MAXCON ,IDH_MAXCONNS,
+ 0, 0 };
+
+static DWORD aPrinterIds[] = { IDD_PRINTER_NAME, IDH_PRINTER_NAME,
+ IDD_PRINTER_QUEUE_TXT, IDH_PRINTER_QUEUE,
+ IDD_PRINTER_QUEUE, IDH_PRINTER_QUEUE,
+ 0, 0 };
+
+static DWORD aNDSIds[] = { IDD_NDS_NAME_TXT, IDH_NDS_NAME,
+ IDD_NDS_NAME, IDH_NDS_NAME,
+ IDD_NDS_CLASS_TXT, IDH_NDS_CLASS,
+ IDD_NDS_CLASS, IDH_NDS_CLASS,
+ IDD_NDS_COMMENT_TXT, IDH_NDS_COMMENT,
+ IDD_NDS_COMMENT, IDH_NDS_COMMENT,
+ 0, 0 };
+
+static DWORD aShareIds[] = { IDD_SHARE_NAME, IDH_SHARE_NAME,
+ IDD_SHARE_SERVER_TXT, IDH_SHARE_SERVER,
+ IDD_SHARE_SERVER, IDH_SHARE_SERVER,
+ IDD_SHARE_PATH_TXT, IDH_SHARE_PATH,
+ IDD_SHARE_PATH, IDH_SHARE_PATH,
+ IDD_SHARE_USED_SPC_CLR,IDH_SHARE_USED_SPC,
+ IDD_SHARE_USED_SPC_TXT,IDH_SHARE_USED_SPC,
+ IDD_SHARE_USED_SPC, IDH_SHARE_USED_SPC,
+ IDD_SHARE_USED_SPC_MB, IDH_SHARE_USED_SPC,
+ IDD_SHARE_FREE_SPC_CLR,IDH_SHARE_FREE_SPC,
+ IDD_SHARE_FREE_SPC_TXT,IDH_SHARE_FREE_SPC,
+ IDD_SHARE_FREE_SPC, IDH_SHARE_FREE_SPC,
+ IDD_SHARE_FREE_SPC_MB, IDH_SHARE_FREE_SPC,
+ IDD_SHARE_MAX_SPC_TXT, IDH_SHARE_MAX_SPC,
+ IDD_SHARE_MAX_SPC, IDH_SHARE_MAX_SPC,
+ IDD_SHARE_MAX_SPC_MB, IDH_SHARE_MAX_SPC,
+ IDD_SHARE_PIE, IDH_SHARE_PIE,
+ IDD_SHARE_LFN_TXT, IDH_SHARE_LFN_TXT,
+ 0,0 };
+
+
+#if 0
+static DWORD aWGIds[] = { IDD_WRKGRP_NAME, IDH_WRKGRP_NAME,
+ IDD_WRKGRP_TYPE_TXT, IDH_WRKGRP_TYPE,
+ IDD_WRKGRP_TYPE, IDH_WRKGRP_TYPE,
+ 0, 0 };
+
+static DWORD aNDSAdminIds[] = { IDD_ENABLE_SYSPOL, IDH_ENABLE_SYSPOL,
+ IDD_VOLUME_LABEL, IDH_VOLUME_LABEL,
+ IDD_VOLUME, IDH_VOLUME,
+ IDD_DIRECTORY_LABEL,IDH_DIRECTORY_LABEL,
+ IDD_DIRECTORY, IDH_DIRECTORY,
+ 0, 0 };
+#endif
+
+
+void NDSPage_InitDialog(
+ HWND hDlg,
+ LPPROPSHEETPAGE psp
+ )
+{
+ CNWObjContextMenu* pPSClass = (CNWObjContextMenu*)psp->lParam;
+ LPNETRESOURCE pnr = NULL;
+ DWORD err = NO_ERROR;
+ NTSTATUS ntstatus = STATUS_SUCCESS;
+ HANDLE hTreeConn = NULL;
+
+ if ( pPSClass )
+ pnr = pPSClass->QueryNetResource();
+
+ if ( pnr == NULL )
+ {
+ ASSERT(FALSE);
+
+ // This should not happen. We can always get the net resource which is queried
+ // during AddPages.
+ return;
+ }
+
+ do { // not a loop, just wanted to break on error
+
+ LPWSTR pszRemoteName = pnr->lpRemoteName;
+
+ if ( pszRemoteName[0] == L' ') // tree names have a space in front " \\mardev"
+ pszRemoteName++;
+
+ if ( pnr->dwDisplayType == RESOURCEDISPLAYTYPE_TREE )
+ {
+ SetDlgItemText( hDlg, IDD_NDS_NAME, pszRemoteName + 2); // get past backslashes
+ }
+ else
+ {
+ SetDlgItemText( hDlg, IDD_NDS_NAME, wcschr( pszRemoteName + 2, L'\\') + 1);
+ }
+
+ DWORD dwOid;
+
+ err = NwOpenAndGetTreeInfo( pszRemoteName,
+ &hTreeConn,
+ &dwOid );
+
+ if ( err != NO_ERROR )
+ {
+ break;
+ }
+
+ BYTE RawResponse[TWO_KB];
+ DWORD RawResponseSize = sizeof(RawResponse);
+
+ ntstatus = NwNdsReadObjectInfo( hTreeConn,
+ dwOid,
+ RawResponse,
+ RawResponseSize );
+
+ if ( !NT_SUCCESS( ntstatus ))
+ {
+ err = RtlNtStatusToDosError(ntstatus);
+ break;
+ }
+
+ LPBYTE pObjectClass = RawResponse;
+
+ pObjectClass += sizeof( NDS_RESPONSE_GET_OBJECT_INFO ) + sizeof(DWORD);
+
+ ::SetDlgItemText( hDlg, IDD_NDS_CLASS, (LPWSTR) pObjectClass );
+
+ // NOTE: The description can only be read successfully with administrative privilege
+
+ DWORD iterHandle = (DWORD) -1;
+ UNICODE_STRING uAttrName;
+ PNDS_RESPONSE_READ_ATTRIBUTE pReadAttrResponse = (PNDS_RESPONSE_READ_ATTRIBUTE) RawResponse;
+
+ RtlInitUnicodeString( &uAttrName, L"Description");
+
+ ntstatus = NwNdsReadAttribute( hTreeConn,
+ dwOid,
+ &iterHandle,
+ &uAttrName,
+ RawResponse,
+ sizeof(RawResponse));
+
+ if ( !NT_SUCCESS( ntstatus )
+ || ( pReadAttrResponse->CompletionCode != 0 )
+ || ( pReadAttrResponse->NumAttributes == 0 )
+ )
+ {
+ // we don't need to set the error since this attribute can only be read by admins and
+ // we might get an error indicating this.
+ break;
+ }
+
+ PNDS_ATTRIBUTE pNdsAttribute = (PNDS_ATTRIBUTE)((DWORD) RawResponse+sizeof(NDS_RESPONSE_READ_ATTRIBUTE));
+
+ LPWSTR pszComment = (LPWSTR) ((DWORD) pNdsAttribute + 3*sizeof(DWORD)
+ + pNdsAttribute->AttribNameLength + sizeof(DWORD));
+ ::SetDlgItemText(hDlg,IDD_NDS_COMMENT, pszComment);
+
+ } while (FALSE);
+
+
+ if ( hTreeConn )
+ CloseHandle( hTreeConn );
+
+ if ( err != NO_ERROR )
+ {
+ LPWSTR pszMessage = NULL;
+
+ if ( ::LoadMsgErrorPrintf( &pszMessage,
+ IDS_MESSAGE_GETINFO_ERROR,
+ err ) == NO_ERROR )
+ {
+ UnHideControl( hDlg, IDD_ERROR );
+ SetDlgItemText( hDlg, IDD_ERROR, pszMessage);
+ ::LocalFree( pszMessage );
+ }
+ }
+
+ return;
+}
+
+BOOL
+CALLBACK
+NDSPage_DlgProc(
+ HWND hDlg,
+ UINT uMessage,
+ WPARAM wParam ,
+ LPARAM lParam)
+{
+
+ LPPROPSHEETPAGE psp = (LPPROPSHEETPAGE)GetWindowLong(hDlg, DWL_USER);
+
+ switch (uMessage)
+ {
+ //
+ // When the shell creates a dialog box for a property sheet page,
+ // it passes the pointer to the PROPSHEETPAGE data structure as
+ // lParam. The dialog procedures of extensions typically store it
+ // in the DWL_USER of the dialog box window.
+ //
+ case WM_INITDIALOG:
+ SetWindowLong(hDlg, DWL_USER, lParam);
+ psp = (LPPROPSHEETPAGE)lParam;
+
+ NDSPage_InitDialog( hDlg, psp);
+
+ break;
+
+ case WM_DESTROY:
+ {
+ CNWObjContextMenu* pPSClass = (CNWObjContextMenu *)(psp->lParam);
+
+ if (pPSClass)
+ pPSClass->Release();
+
+ SetWindowLong(hDlg, DWL_USER, NULL);
+ break;
+ }
+
+ case WM_COMMAND:
+ break;
+
+ case WM_NOTIFY:
+ {
+ switch (((NMHDR *)lParam)->code)
+ {
+ case PSN_SETACTIVE:
+ {
+ CNWObjContextMenu *pPSClass =
+ (CNWObjContextMenu *)(psp->lParam);
+
+ pPSClass->_paHelpIds = aNDSIds;
+ break;
+ }
+
+ default:
+ break;
+ }
+ break;
+ }
+
+ case WM_HELP:
+ {
+ CNWObjContextMenu* pPSClass = (CNWObjContextMenu *)(psp->lParam);
+
+ if ( pPSClass && pPSClass->_paHelpIds )
+ {
+ WinHelp( (HWND) ((LPHELPINFO)lParam)->hItemHandle,
+ NW_HELP_FILE,
+ HELP_WM_HELP,
+ (DWORD)(LPVOID)pPSClass->_paHelpIds );
+ }
+ break;
+ }
+
+
+ case WM_CONTEXTMENU:
+ {
+ CNWObjContextMenu* pPSClass = (CNWObjContextMenu*)(psp->lParam);
+
+ if (pPSClass && pPSClass->_paHelpIds)
+ {
+ WinHelp( (HWND)wParam,
+ NW_HELP_FILE,
+ HELP_CONTEXTMENU,
+ (DWORD)(LPVOID)pPSClass->_paHelpIds );
+ }
+ break;
+ }
+
+ default:
+ return FALSE;
+ }
+
+ return TRUE;
+
+}
+
+#define HIDWORD(_qw) (DWORD)(_qw>>32)
+#define LODWORD(_qw) (DWORD)(_qw)
+
+void Share_InitDialog(HWND hDlg, LPPROPSHEETPAGE psp)
+{
+ CNWObjContextMenu* pPSClass = (CNWObjContextMenu *)psp->lParam;
+ LPNETRESOURCE pnr;
+ DWORD err = NO_ERROR;
+ BOOL fDirectoryMap = FALSE;
+
+ if ( pPSClass )
+ pnr = pPSClass->QueryNetResource();
+
+ if ( pnr == NULL )
+ {
+ ASSERT(FALSE);
+
+ // This should not happen. We can always get the net resource which is queried
+ // during AddPages.
+ return;
+ }
+
+ do { // not a loop, just wanted to break out if error occurred
+
+ WCHAR szShare[MAX_PATH+1];
+ WCHAR szServer[MAX_PATH+1] = L"";
+
+ // Get the share name
+ NwExtractShareName( pnr->lpRemoteName, szShare );
+ SetDlgItemText( hDlg, IDD_SHARE_NAME, szShare );
+
+ HideControl( hDlg, IDD_SHARE_PATH_TXT);
+ HideControl( hDlg, IDD_SHARE_PATH);
+ HideControl( hDlg, IDD_SHARE_LFN_TXT);
+
+ // Get the server name
+ if ( NwIsNdsSyntax( pnr->lpRemoteName ))
+ {
+ NTSTATUS ntstatus = STATUS_SUCCESS;
+ HANDLE hTreeConn = NULL;
+ DWORD dwOid;
+
+ err = NwOpenAndGetTreeInfo( pnr->lpRemoteName,
+ &hTreeConn,
+ &dwOid );
+
+ if ( err != NO_ERROR )
+ break;
+
+ BYTE RawResponse[TWO_KB];
+ DWORD RawResponseSize = sizeof(RawResponse);
+
+ DWORD iterHandle = (DWORD) -1;
+ UNICODE_STRING uAttrName;
+ PNDS_RESPONSE_READ_ATTRIBUTE pReadAttrResponse = (PNDS_RESPONSE_READ_ATTRIBUTE) RawResponse;
+
+ RtlInitUnicodeString( &uAttrName, L"Path");
+
+ ntstatus = NwNdsReadAttribute( hTreeConn,
+ dwOid,
+ &iterHandle,
+ &uAttrName,
+ RawResponse,
+ sizeof(RawResponse));
+
+ CloseHandle( hTreeConn );
+ hTreeConn = NULL;
+
+ if ( NT_SUCCESS( ntstatus )
+ && ( pReadAttrResponse->CompletionCode == 0 )
+ && ( pReadAttrResponse->NumAttributes != 0 )
+ )
+ {
+ // We are successful in reading the attribute. Hence this is a directory map.
+ fDirectoryMap = TRUE;
+
+ PNDS_ATTRIBUTE pNdsAttribute = (PNDS_ATTRIBUTE)((DWORD) RawResponse+sizeof(NDS_RESPONSE_READ_ATTRIBUTE));
+
+ PDWORD pdwNameSpace = (PDWORD) ((DWORD) pNdsAttribute + 3*sizeof(DWORD)
+ + pNdsAttribute->AttribNameLength
+ + sizeof(WORD) // need this due to bug in return value???
+ + sizeof(DWORD));
+
+ // See if the directory supports long file name
+ // BUGBUG: what about other name spaces?
+ // Only on directory map will Win95 show LFN support or not.
+ if ( *pdwNameSpace == NAMESPACE_OS2 )
+ {
+ UnHideControl( hDlg, IDD_SHARE_LFN_TXT );
+ }
+
+ // Now, try to get the volume the directory map is on
+ PDWORD pdwVolumeLen = (PDWORD) ((DWORD) pdwNameSpace + sizeof(DWORD));
+ LPWSTR pszVolume = (LPWSTR) ((DWORD) pdwNameSpace + 2*sizeof(DWORD));
+ LPWSTR pszPath = (LPWSTR) ((DWORD) pszVolume + *pdwVolumeLen
+ + sizeof(WORD) // need this due to bug in return value???
+ + sizeof(DWORD));
+
+
+ WCHAR szFullPath[MAX_PATH+1];
+ LPWSTR pszTemp;
+ wcscpy( szFullPath, pnr->lpRemoteName );
+ if ( pszTemp = wcschr( szFullPath + 2, L'\\'))
+ *(pszTemp + 1) = 0;
+
+ wcscat( szFullPath, pszVolume );
+
+ err = NwGetNdsVolumeInfo( szFullPath, szServer, sizeof(szServer), szShare, sizeof(szShare));
+
+
+ // Now, display the path of the directory map
+ if ( err == NO_ERROR )
+ {
+ wcscpy( szFullPath, szShare );
+ wcscat( szFullPath, L"\\");
+ wcscat( szFullPath, pszPath );
+
+ UnHideControl(hDlg, IDD_SHARE_PATH_TXT);
+ UnHideControl(hDlg, IDD_SHARE_PATH);
+ SetDlgItemText( hDlg, IDD_SHARE_PATH, szFullPath );
+ }
+ }
+ else // this is a volume
+ {
+
+ // For NDS names, the unc path might not contain the server name.
+ // So, we need to get the server name that this share is on.
+ // Also, we need the original volume name like "SYS" instead of "MARS_SRV0_SYS"
+ err = NwGetNdsVolumeInfo( pnr->lpRemoteName, szServer, sizeof(szServer), szShare, sizeof(szShare));
+ }
+
+ if ( err != NO_ERROR )
+ break;
+ }
+ else // in the form \\server\sys
+ {
+ NwExtractServerName( pnr->lpRemoteName, szServer );
+ }
+
+ SetDlgItemText( hDlg, IDD_SHARE_SERVER, szServer);
+
+ NWCONN_HANDLE hConn = NULL;
+ if ( NWCAttachToFileServerW( szServer, 0, &hConn ) != SUCCESSFUL )
+ {
+ err = GetLastError();
+ break;
+ }
+
+ NWVOL_NUM nVolNum;
+ char szAnsiShare[MAX_PATH+1];
+
+ ::CharToOem( szShare, szAnsiShare );
+ if ( NWCGetVolumeNumber( hConn, szAnsiShare, &nVolNum ) != SUCCESSFUL )
+ {
+ err = GetLastError();
+ break;
+ }
+
+ DWORD dwSectorSize = 0x200;
+
+ DWORD dwTotalBlocks = 0;
+ DWORD dwFreeBlocks = 0;
+ DWORD dwPurgeable = 0;
+ DWORD dwNotYetPurged = 0;
+ DWORD dwSectors= 0;
+ DWORD dwTotalDir= 0;
+ DWORD dwAvailDir= 0;
+
+ ULONGLONG qwTot = 0;
+ ULONGLONG qwFree = 0;
+
+ WCHAR szFormat[30];
+ WCHAR szTemp[80];
+ WCHAR szTemp2[30];
+
+
+ // NOTE: 2.x servers does not support NWCGetVolumeUsage.
+ // Hence, for 2.x servers, an error will always be shown
+
+ if ( NWCGetVolumeUsage( hConn,
+ nVolNum,
+ &dwTotalBlocks,
+ &dwFreeBlocks,
+ &dwPurgeable,
+ &dwNotYetPurged,
+ &dwTotalDir,
+ &dwAvailDir,
+ (LPBYTE) &dwSectors ) != SUCCESSFUL )
+ {
+ err = GetLastError();
+ break;
+ }
+
+ dwFreeBlocks += dwPurgeable;
+
+ qwTot = (ULONGLONG) dwSectorSize * (ULONGLONG) dwSectors * (ULONGLONG) dwTotalBlocks;
+
+ qwFree = (ULONGLONG) dwSectorSize * (ULONGLONG) dwSectors * (ULONGLONG) dwFreeBlocks;
+
+ if (::LoadString(::hmodNW, IDS_BYTES, szFormat, sizeof(szFormat)/sizeof(szFormat[0])))
+ {
+ if (!HIDWORD(qwTot-qwFree))
+ {
+ wsprintf(szTemp, szFormat, AddCommas(LODWORD(qwTot) - LODWORD(qwFree), szTemp2, sizeof(szTemp2)/sizeof(szTemp2[0])));
+ SetDlgItemText(hDlg,IDD_SHARE_USED_SPC, szTemp);
+ }
+
+ if (!HIDWORD(qwFree))
+ {
+ wsprintf(szTemp, szFormat, AddCommas(LODWORD(qwFree), szTemp2, sizeof(szTemp2)/sizeof(szTemp2[0])));
+ SetDlgItemText(hDlg, IDD_SHARE_FREE_SPC, szTemp);
+ }
+
+ if (!HIDWORD(qwTot))
+ {
+ wsprintf(szTemp, szFormat, AddCommas(LODWORD(qwTot), szTemp2, sizeof(szTemp2)/sizeof(szTemp2[0])));
+ SetDlgItemText(hDlg, IDD_SHARE_MAX_SPC, szTemp);
+ }
+ }
+
+ ShortSizeFormat64(qwTot-qwFree, szTemp);
+ SetDlgItemText(hDlg, IDD_SHARE_USED_SPC_MB, szTemp);
+
+ ShortSizeFormat64(qwFree, szTemp);
+ SetDlgItemText(hDlg, IDD_SHARE_FREE_SPC_MB, szTemp);
+
+ ShortSizeFormat64(qwTot, szTemp);
+ SetDlgItemText(hDlg, IDD_SHARE_MAX_SPC_MB, szTemp);
+
+ pPSClass->_fGotClusterInfo = TRUE;
+ pPSClass->_dwTotal = dwTotalBlocks;
+ pPSClass->_dwFree = dwFreeBlocks;
+
+ (VOID) NWCDetachFromFileServer( hConn );
+
+ } while (FALSE);
+
+ if ( err != NO_ERROR )
+ {
+ LPWSTR pszMessage = NULL;
+
+ HideControl(hDlg, IDD_SHARE_USED_SPC_CLR);
+ HideControl(hDlg, IDD_SHARE_USED_SPC_TXT);
+ HideControl(hDlg, IDD_SHARE_USED_SPC);
+ HideControl(hDlg, IDD_SHARE_USED_SPC_MB);
+
+ HideControl(hDlg, IDD_SHARE_FREE_SPC_CLR);
+ HideControl(hDlg, IDD_SHARE_FREE_SPC_TXT);
+ HideControl(hDlg, IDD_SHARE_FREE_SPC);
+ HideControl(hDlg, IDD_SHARE_FREE_SPC_MB);
+
+ HideControl(hDlg, IDD_SHARE_MAX_SPC_TXT);
+ HideControl(hDlg, IDD_SHARE_MAX_SPC);
+ HideControl(hDlg, IDD_SHARE_MAX_SPC_MB);
+
+ HideControl(hDlg, IDD_SHARE_PIE);
+
+ if ( ::LoadMsgErrorPrintf( &pszMessage,
+ IDS_MESSAGE_GETINFO_ERROR,
+ err ) == NO_ERROR )
+ {
+ UnHideControl( hDlg, IDD_ERROR );
+ SetDlgItemText( hDlg, IDD_ERROR, pszMessage);
+ ::LocalFree( pszMessage );
+ }
+ }
+
+
+} /* endproc Share_InitDialog */
+
+void Printer_InitDialog(HWND hDlg, LPPROPSHEETPAGE psp)
+{
+ CNWObjContextMenu* pPSClass = (CNWObjContextMenu *)psp->lParam;
+ LPNETRESOURCE pnr;
+ DWORD err = NO_ERROR;
+
+ if ( pPSClass )
+ pnr = pPSClass->QueryNetResource();
+
+ if ( pnr == NULL )
+ {
+ ASSERT(FALSE);
+
+ // This should not happen. We can always get the net resource which is queried
+ // during AddPages.
+ return;
+ }
+
+ do { // not a loop, just wanted to break out if error occurred
+
+ WCHAR szShare[MAX_PATH];
+ NwExtractShareName( pnr->lpRemoteName, szShare );
+
+
+ SetDlgItemText(hDlg,IDD_PRINTER_NAME, szShare);
+
+ if ( NwIsNdsSyntax( pnr->lpRemoteName))
+ {
+ NTSTATUS ntstatus = STATUS_SUCCESS;
+ HANDLE hTreeConn = NULL;
+ DWORD dwOid;
+
+ err = NwOpenAndGetTreeInfo( pnr->lpRemoteName,
+ &hTreeConn,
+ &dwOid );
+
+ if ( err != NO_ERROR )
+ break;
+
+ BYTE RawResponse[TWO_KB];
+ DWORD RawResponseSize = sizeof(RawResponse);
+
+ DWORD iterHandle = (DWORD) -1;
+ UNICODE_STRING uAttrName;
+ PNDS_RESPONSE_READ_ATTRIBUTE pReadAttrResponse = (PNDS_RESPONSE_READ_ATTRIBUTE) RawResponse;
+
+ RtlInitUnicodeString( &uAttrName, L"Queue Directory");
+
+ ntstatus = NwNdsReadAttribute( hTreeConn,
+ dwOid,
+ &iterHandle,
+ &uAttrName,
+ RawResponse,
+ sizeof(RawResponse));
+
+ CloseHandle( hTreeConn );
+ hTreeConn = NULL;
+
+ if ( !NT_SUCCESS( ntstatus )
+ || ( pReadAttrResponse->CompletionCode != 0 )
+ || ( pReadAttrResponse->NumAttributes == 0 )
+ )
+ {
+ // we don't need to set the error since this attribute can only be read by admins and
+ // we might get an error indicating this.
+ break;
+ }
+
+ PNDS_ATTRIBUTE pNdsAttribute = (PNDS_ATTRIBUTE)((DWORD) RawResponse+sizeof(NDS_RESPONSE_READ_ATTRIBUTE));
+
+ LPWSTR pszQueueFile = (LPWSTR) ((DWORD) pNdsAttribute + 3*sizeof(DWORD)
+ + pNdsAttribute->AttribNameLength + sizeof(DWORD));
+ ::SetDlgItemText( hDlg, IDD_PRINTER_QUEUE, pszQueueFile);
+ }
+ else // bindery server
+ {
+ NWCONN_HANDLE hConn = NULL;
+ WCHAR szServer[MAX_PATH+1];
+
+ NwExtractServerName( pnr->lpRemoteName, szServer );
+
+ if ( NWCAttachToFileServerW( szServer, 0, &hConn ) != SUCCESSFUL )
+ err = GetLastError();
+
+ if ( err == NO_ERROR )
+ {
+ char szAnsiShare[MAX_PATH+1];
+ char Buffer[NW_DATA_SIZE];
+ NWFLAGS ucMoreFlag, ucPropertyFlag;
+
+ memset( Buffer, 0, sizeof(Buffer));
+ ::CharToOem( szShare, szAnsiShare );
+
+ if ( NWCReadPropertyValue( hConn,
+ szAnsiShare,
+ OT_PRINT_QUEUE,
+ "Q_DIRECTORY",
+ 1,
+ Buffer,
+ &ucMoreFlag,
+ &ucPropertyFlag ) != SUCCESSFUL )
+ {
+ err = GetLastError();
+ }
+
+ if ( err == NO_ERROR )
+ {
+ WCHAR uBuffer[NW_DATA_SIZE];
+
+ ::OemToChar( Buffer, uBuffer );
+
+ ::SetDlgItemText( hDlg, IDD_PRINTER_QUEUE, (LPWSTR) uBuffer);
+ }
+ else
+ {
+ err = NO_ERROR; // Only supervisor has read/write so don't show the error.
+ }
+
+ (VOID) NWCDetachFromFileServer( hConn );
+ }
+ }
+
+ } while (FALSE);
+
+ if ( err != NO_ERROR )
+ {
+ LPWSTR pszMessage = NULL;
+
+ if ( ::LoadMsgErrorPrintf( &pszMessage,
+ IDS_MESSAGE_GETINFO_ERROR,
+ err ) == NO_ERROR )
+ {
+ UnHideControl( hDlg, IDD_ERROR );
+ SetDlgItemText( hDlg, IDD_ERROR, pszMessage);
+ ::LocalFree( pszMessage );
+ }
+ }
+
+} /* endproc Printer_InitDialog */
+
+void Server_InitDialog(HWND hDlg, LPPROPSHEETPAGE psp)
+{
+ CNWObjContextMenu* pPSClass = (CNWObjContextMenu *)psp->lParam;
+ LPNETRESOURCE pnr;
+ DWORD err = NO_ERROR;
+
+ if ( pPSClass )
+ pnr = pPSClass->QueryNetResource();
+
+ if ( pnr == NULL )
+ {
+ ASSERT(FALSE);
+
+ // This should not happen. We can always get the net resource which is queried
+ // during AddPages.
+ return;
+ }
+
+ do { // not a loop, just wanted to break out if error occurred
+
+ WCHAR szServer[MAX_PATH];
+ NwExtractServerName( pnr->lpRemoteName, szServer );
+
+ SetDlgItemText( hDlg, IDD_SERVER_NAME, szServer );
+
+ //
+ // Get some server information
+ //
+
+ NWCONN_HANDLE hConn = NULL;
+ if ( NWCAttachToFileServerW( szServer, 0, &hConn ) != SUCCESSFUL )
+ {
+ err = GetLastError();
+ break;
+ }
+
+ VERSION_INFO vInfo;
+
+ if ( NWCGetFileServerVersionInfo( hConn, &vInfo ) != SUCCESSFUL )
+ {
+ err = GetLastError();
+ break;
+ }
+
+ WCHAR szTemp[512];
+ char szAnsiCompany[512];
+ char szAnsiVersion[512];
+ char szAnsiRevision[512];
+
+ if ( NWCGetFileServerDescription( hConn, szAnsiCompany, szAnsiVersion,
+ szAnsiRevision ) != SUCCESSFUL )
+ {
+ err = GetLastError();
+ break;
+ }
+
+ // OemToChar( szAnsiCompany, szTemp );
+ // wcscat( szTemp, L" " );
+ // OemToChar( szAnsiVersion, szTemp + wcslen( szTemp ));
+
+ OemToChar( szAnsiVersion, szTemp );
+
+ ::SetDlgItemText( hDlg, IDD_SERVER_VERSION, szTemp);
+
+ OemToChar( szAnsiRevision, szTemp );
+ ::SetDlgItemText( hDlg, IDD_SERVER_REVISION, szTemp );
+
+ WCHAR szNumber[12];
+
+ ::wsprintf(szNumber,L"%d", vInfo.connsInUse );
+ ::SetDlgItemText( hDlg, IDD_SERVER_CONNECT, szNumber);
+
+ ::wsprintf(szNumber,L"%4d", vInfo.ConnsSupported);
+ ::SetDlgItemText( hDlg, IDD_SERVER_MAXCON, szNumber);
+
+ (VOID) NWCDetachFromFileServer( hConn );
+
+#if 0
+ // Now deal with Chicago specific fields
+ if (pPSClass->_fIsPeerServer) {
+
+ pXNCPResp pxresp = (pXNCPResp) pPSClass->_bufServerExInfo.QueryPtr(); ;
+ pXGetServerInfoResp lpInfoPtr = (pXGetServerInfoResp)(pxresp+1);
+ CHAR szString[128];
+ STRING *pNWString;
+
+ // Next field is workgroup name
+ pNWString = (STRING *)(lpInfoPtr->passThruServer.str+lpInfoPtr->passThruServer.len);
+ pNWString = (STRING *)(pNWString->str+pNWString->len);
+
+ // And next after that is comment
+
+ ::OemToCharBuff((LPCSTR)pNWString->str,szString,pNWString->len);
+ szString[pNWString->len] = '\0';
+
+ UnHideControl( hDlg, IDD_SERVER_COMMENT_TXT );
+ UnHideControl( hDlg, IDD_SERVER_COMMENT );
+ ::SetDlgItemText(hDlg,IDD_SERVER_COMMENT,szString);
+
+ } else
+#endif
+
+ } while (FALSE);
+
+ if ( err != NO_ERROR )
+ {
+ LPWSTR pszMessage = NULL;
+
+ if ( ::LoadMsgErrorPrintf( &pszMessage,
+ IDS_MESSAGE_GETINFO_ERROR,
+ err ) == NO_ERROR )
+ {
+ UnHideControl( hDlg, IDD_ERROR );
+ SetDlgItemText( hDlg, IDD_ERROR, pszMessage);
+ ::LocalFree( pszMessage );
+ }
+ }
+
+} /* endproc Server_InitDialog */
+
+#if 0
+void Wrkgrp_InitDialog(HWND hDlg, LPPROPSHEETPAGE psp)
+{
+
+ CNWObjContextMenu *pPSClass = (CNWObjContextMenu *)psp->lParam;
+ LPNETRESOURCE pnr;
+
+ if ( pPSClass )
+ pnr = (LPNETRESOURCE)pPSClass->_bufNR.QueryPtr();
+
+ if ( pnr )
+ {
+ // Set name static control
+ SetDlgItemText(hDlg,IDD_WRKGRP_NAME, pnr->lpRemoteName);
+ }
+
+}
+#endif
+
+COLORREF c_crPieColors[] =
+{
+ RGB( 0, 0, 255), // Blue
+ RGB(255, 0, 255), // Red-Blue
+ RGB( 0, 0, 128), // 1/2 Blue
+ RGB(128, 0, 128), // 1/2 Red-Blue
+} ;
+
+void _DrvPrshtDrawItem(HWND hDlg, LPPROPSHEETPAGE psp, const DRAWITEMSTRUCT * lpdi)
+{
+ COLORREF crDraw;
+ RECT rcItem = lpdi->rcItem;
+ HBRUSH hbDraw, hbOld;
+ SIZE size;
+ HDC hDC;
+ CNWObjContextMenu* pPSClass = (CNWObjContextMenu *)psp->lParam;
+
+ if (pPSClass->_fGotClusterInfo == FALSE)
+ return;
+
+ switch (lpdi->CtlID)
+ {
+ case IDD_SHARE_PIE:
+
+ hDC = GetDC(hDlg);
+ GetTextExtentPoint(hDC, L"W", 1, &size);
+ ReleaseDC(hDlg, hDC);
+
+ DrawPie(lpdi->hDC, &lpdi->rcItem,
+ pPSClass->_dwTotal ? 1000*(pPSClass->_dwTotal-pPSClass->_dwFree)/pPSClass->_dwTotal : 1000,
+ pPSClass->_dwFree==0 || pPSClass->_dwFree==pPSClass->_dwTotal,
+ size.cy*2/3, c_crPieColors);
+
+ break;
+
+ case IDD_SHARE_USED_SPC_CLR:
+ crDraw = c_crPieColors[DP_USEDCOLOR];
+ goto DrawColor;
+
+ case IDD_SHARE_FREE_SPC_CLR:
+ crDraw = c_crPieColors[DP_FREECOLOR];
+ goto DrawColor;
+
+DrawColor:
+ hbDraw = CreateSolidBrush(crDraw);
+ if (hbDraw)
+ {
+ hbOld = (HBRUSH) SelectObject(lpdi->hDC, hbDraw);
+ if (hbOld)
+ {
+ PatBlt(lpdi->hDC, rcItem.left, rcItem.top,
+ rcItem.right-rcItem.left,
+ rcItem.bottom-rcItem.top,
+ PATCOPY);
+
+ SelectObject(lpdi->hDC, hbOld);
+ }
+
+ DeleteObject(hbDraw);
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+BOOL CALLBACK NWPage_DlgProc(HWND hDlg, UINT uMessage, WPARAM wParam , LPARAM lParam)
+{
+ LPPROPSHEETPAGE psp = (LPPROPSHEETPAGE)GetWindowLong(hDlg, DWL_USER);
+
+ switch (uMessage)
+ {
+ //
+ // When the shell creates a dialog box for a property sheet page,
+ // it passes the pointer to the PROPSHEETPAGE data structure as
+ // lParam. The dialog procedures of extensions typically store it
+ // in the DWL_USER of the dialog box window.
+ //
+ case WM_INITDIALOG:
+ SetWindowLong(hDlg, DWL_USER, lParam);
+ psp = (LPPROPSHEETPAGE)lParam;
+
+ if (psp->pszTemplate == MAKEINTRESOURCE(DLG_SERVER_SUMMARYINFO))
+ Server_InitDialog(hDlg, psp);
+ else if (psp->pszTemplate == MAKEINTRESOURCE(DLG_SHARE_SUMMARYINFO))
+ Share_InitDialog(hDlg, psp);
+ else if (psp->pszTemplate == MAKEINTRESOURCE(DLG_PRINTER_SUMMARYINFO))
+ Printer_InitDialog(hDlg, psp);
+#if 0
+ else if (psp->pszTemplate == MAKEINTRESOURCE(DLG_WRKGRP_SUMMARYINFO))
+ Wrkgrp_InitDialog(hDlg, psp);
+#endif
+
+ break;
+
+ case WM_DRAWITEM:
+ _DrvPrshtDrawItem(hDlg, psp, (DRAWITEMSTRUCT *)lParam);
+ break;
+
+ case WM_DESTROY:
+ {
+ CNWObjContextMenu* pPSClass = (CNWObjContextMenu *)(psp->lParam);
+
+ if (pPSClass) {
+ pPSClass->Release();
+ }
+
+ SetWindowLong(hDlg, DWL_USER, NULL);
+ }
+ break;
+
+ case WM_COMMAND:
+ break;
+
+ case WM_NOTIFY:
+ switch (((NMHDR *)lParam)->code) {
+ case PSN_SETACTIVE:
+ {
+ CNWObjContextMenu *pPSClass = (CNWObjContextMenu *)(psp->lParam);
+
+ if (psp->pszTemplate == MAKEINTRESOURCE(DLG_SERVER_SUMMARYINFO))
+ pPSClass->_paHelpIds = aServerIds;
+ else if (psp->pszTemplate == MAKEINTRESOURCE(DLG_SHARE_SUMMARYINFO))
+ pPSClass->_paHelpIds = aShareIds;
+ else if (psp->pszTemplate == MAKEINTRESOURCE(DLG_PRINTER_SUMMARYINFO))
+ pPSClass->_paHelpIds = aPrinterIds;
+#if 0
+ else if (psp->pszTemplate == MAKEINTRESOURCE(DLG_WRKGRP_SUMMARYINFO))
+ pPSClass->_paHelpIds = aWGIds;
+#endif
+
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ break;
+
+ case WM_HELP:
+ {
+ CNWObjContextMenu* pPSClass = (CNWObjContextMenu *)(psp->lParam);
+
+ if (pPSClass && pPSClass->_paHelpIds)
+ {
+ WinHelp( (HWND) ((LPHELPINFO)lParam)->hItemHandle,
+ NW_HELP_FILE,
+ HELP_WM_HELP,
+ (DWORD)(LPVOID)pPSClass->_paHelpIds );
+ }
+ }
+
+ break;
+
+ case WM_CONTEXTMENU:
+ {
+ CNWObjContextMenu* pPSClass = (CNWObjContextMenu*)(psp->lParam);
+
+ if (pPSClass && pPSClass->_paHelpIds)
+ {
+ WinHelp( (HWND)wParam,
+ NW_HELP_FILE,
+ HELP_CONTEXTMENU,
+ (DWORD)(LPVOID)pPSClass->_paHelpIds );
+ }
+ break;
+ }
+
+ default:
+ return(FALSE);
+ }
+
+ return(TRUE);
+
+}
+
+// Regular StrToInt; stops at first non-digit.
+int WINAPI MyStrToInt(LPWSTR lpSrc) // atoi()
+{
+
+#define ISDIGIT(c) ((c) >= '0' && (c) <= '9')
+
+ int n = 0;
+ BOOL bNeg = FALSE;
+
+ if (*lpSrc == L'-') {
+ bNeg = TRUE;
+ lpSrc++;
+ }
+
+ while (ISDIGIT(*lpSrc)) {
+ n *= 10;
+ n += *lpSrc - L'0';
+ lpSrc++;
+ }
+ return bNeg ? -n : n;
+}
+
+// The following functions are stolen from win\core\shell\shelldll
+// takes a DWORD add commas etc to it and puts the result in the buffer
+LPWSTR WINAPI AddCommas( DWORD dw, LPWSTR pszResult, DWORD dwSize )
+{
+ WCHAR szTemp[30];
+ WCHAR szSep[5];
+ NUMBERFMT nfmt;
+
+ nfmt.NumDigits=0;
+ nfmt.LeadingZero=0;
+ GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SGROUPING, szSep, sizeof(szSep)/sizeof(szSep[0]));
+ nfmt.Grouping = MyStrToInt(szSep);
+ GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, szSep, sizeof(szSep)/sizeof(szSep[0]));
+ nfmt.lpDecimalSep = nfmt.lpThousandSep = szSep;
+ nfmt.NegativeOrder= 0;
+
+#pragma data_seg(".text", "CODE")
+ wsprintf(szTemp, L"%lu", dw);
+#pragma data_seg()
+
+ if (GetNumberFormat(LOCALE_USER_DEFAULT, 0, szTemp, &nfmt, pszResult, dwSize) == 0)
+ lstrcpy(pszResult, szTemp);
+
+ return pszResult;
+}
+
+const short pwOrders[] = {IDS_BYTES, IDS_ORDERKB, IDS_ORDERMB, IDS_ORDERGB, IDS_ORDERTB};
+
+LPWSTR WINAPI ShortSizeFormat64(ULONGLONG dw64, LPWSTR szBuf)
+{
+ int i;
+ UINT wInt, wLen, wDec;
+ WCHAR szTemp[10], szOrder[20], szFormat[5];
+
+ if (dw64 < 1000) {
+#pragma data_seg(".text", "CODE")
+ wsprintf(szTemp, L"%d", LODWORD(dw64));
+#pragma data_seg()
+ i = 0;
+ goto AddOrder;
+ }
+
+ for (i = 1; i< sizeof(pwOrders)/sizeof(pwOrders[0])-1 && dw64 >= 1000L * 1024L; dw64 >>= 10, i++);
+ /* do nothing */
+
+ wInt = LODWORD(dw64 >> 10);
+ AddCommas(wInt, szTemp, sizeof(szTemp)/sizeof(szTemp[0]));
+ wLen = lstrlen(szTemp);
+ if (wLen < 3)
+ {
+ wDec = LODWORD(dw64 - (ULONGLONG)wInt * 1024L) * 1000 / 1024;
+ // At this point, wDec should be between 0 and 1000
+ // we want get the top one (or two) digits.
+ wDec /= 10;
+ if (wLen == 2)
+ wDec /= 10;
+
+ // Note that we need to set the format before getting the
+ // intl char.
+#pragma data_seg(".text", "CODE")
+ lstrcpy(szFormat, L"%02d");
+#pragma data_seg()
+
+ szFormat[2] = L'0' + 3 - wLen;
+ GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL,
+ szTemp+wLen, sizeof(szTemp)/sizeof(szTemp[0])-wLen);
+ wLen = lstrlen(szTemp);
+ wLen += wsprintf(szTemp+wLen, szFormat, wDec);
+ }
+
+AddOrder:
+ ::LoadString(::hmodNW, pwOrders[i], szOrder, sizeof(szOrder)/sizeof(szOrder[0]));
+ wsprintf(szBuf, szOrder, (LPSTR)szTemp);
+
+ return szBuf;
+}
diff --git a/private/nw/svcdlls/nwwks/client/nwshrc.h b/private/nw/svcdlls/nwwks/client/nwshrc.h
new file mode 100644
index 000000000..cde22a11d
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/nwshrc.h
@@ -0,0 +1,296 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ nwshrc.h
+
+Abstract:
+
+ All resource ids used in nwprovau.dll
+
+Author:
+
+ Yi-Hsin Sung (yihsins) 20-Oct-1995
+
+Revision History:
+
+--*/
+
+#ifndef _NWSHRC_H_
+#define _NWSHRC_H_
+
+#include "nwshhelp.h"
+
+#define IDC_STATIC -1
+#define IDC_LOGOFRAME 599
+
+//
+// Icon Ids
+//
+
+#define IDI_TREE_ICON 600
+#define IDI_SERVER_ICON 601
+#define IDI_FOLDER_ICON 602
+#define IDI_PRINTER_ICON 603
+#define IDI_NDSCONT_ICON 604
+
+// Bitmap Ids
+#define IDB_SERVER_ICON 701
+#define IDB_TREE_ICON 702
+
+//
+// Dialog Ids
+//
+
+#define DLG_NETWARE_LOGIN 1000
+#define DLG_NETWORK_CREDENTIAL 1001
+#define DLG_CHANGE_PASSWORD 1002
+#define DLG_CHANGE_PASSWORD2 1003
+#define DLG_PASSWORD_CHANGE 1004
+#define DLG_PASSWORD_PROMPT 1005
+#define DLG_PREFERRED_SERVER_HELP 1006
+#define DLG_ENTER_PASSWORD_HELP 1007
+#define DLG_ENTER_OLD_PASSWORD 1009
+#define DLG_PW_SELECT_SERVERS 1010
+#define DLG_PW_CHANGED 1011
+#define DLG_ENTER_OLD_PW_HELP 1012
+#define DLG_PW_SELECT_SERVERS_HELP 1013
+#define DLG_ENTER_ALT_USERNAME 1014
+#define DLG_ENTER_ALT_UN_HELP 1015
+#define DLG_CHANGE_PASSWORD3 1016
+#define DLG_CHANGE_PASSWORD_HELP 1017
+
+//
+// Dialog Ids for Shell Extension
+//
+#define DLG_NDS_SUMMARYINFO 1100
+#define DLG_SERVER_SUMMARYINFO 1101
+#define DLG_SHARE_SUMMARYINFO 1102
+#define DLG_PRINTER_SUMMARYINFO 1103
+#define DLG_GLOBAL_WHOAMI 1104
+#define DLG_NDSCONT_SUMMARYINFO 1105
+
+//
+// Help File and IDs
+//
+#define NW_HELP_FILE TEXT("nwdoc.hlp")
+
+//
+// Control Ids used by all dialogs
+//
+#ifdef NT1057
+#define IDHELP 100
+#endif
+
+//
+// Control Ids used in both the login dialog and the
+// change password dialog
+//
+#define ID_USERNAME 101
+#define ID_SERVER 102
+#define ID_LOCATION 103
+#define ID_PREFERREDSERVER_RB 104
+#define ID_DEFAULTCONTEXT_RB 105
+#define ID_DEFAULTTREE 106
+#define ID_DEFAULTCONTEXT 107
+
+//
+// Control Ids used in the login dialog
+//
+#define ID_PASSWORD 200
+
+//
+// Control Ids used in the change password dialog
+//
+#define ID_OLD_PASSWORD 300
+#define ID_NEW_PASSWORD 301
+#define ID_CONFIRM_PASSWORD 302
+
+#define ID_ADD 304
+#define ID_REMOVE 305
+#define ID_ACTIVE_LIST 306
+#define ID_INACTIVE_LIST 307
+#define ID_ACTIVE_LIST_TITLE 308
+#define ID_INACTIVE_LIST_TITLE 309
+
+//
+// Control Ids used in the network credential dialog
+//
+#define ID_VOLUME_PATH 400
+#define ID_CONNECT_AS 401
+#define ID_CONNECT_PASSWORD 402
+#define ID_CONNECT_TEXT 403
+
+//
+// Login script
+//
+#define ID_LOGONSCRIPT 501
+
+//
+// Controls common to summaryinfo dialogs
+//
+
+#define IDD_ERROR 200
+
+//
+// Controls Ids in DLG_NDS_SUMMARYINFO
+//
+#define IDD_NDS_NAME_TXT 101
+#define IDD_NDS_NAME 102
+#define IDD_NDS_CLASS_TXT 103
+#define IDD_NDS_CLASS 104
+#define IDD_NDS_COMMENT_TXT 105
+#define IDD_NDS_COMMENT 106
+
+//
+// Controls Ids in DLG_SERVER_SUMMARYINFO
+//
+#define IDD_SERVER_NAME 101
+#define IDD_SERVER_VERSION_TXT 102
+#define IDD_SERVER_VERSION 103
+#define IDD_SERVER_REVISION_TXT 104
+#define IDD_SERVER_REVISION 105
+#define IDD_SERVER_COMMENT_TXT 106
+#define IDD_SERVER_COMMENT 107
+#define IDD_SERVER_CONNECT_TXT 108
+#define IDD_SERVER_CONNECT 109
+#define IDD_SERVER_MAXCON_TXT 110
+#define IDD_SERVER_MAXCON 111
+
+//
+// Controls Ids in DLG_SHARE_SUMMARYINFO
+//
+#define IDD_SHARE_NAME 101
+#define IDD_SHARE_SERVER_TXT 102
+#define IDD_SHARE_SERVER 103
+#define IDD_SHARE_PATH_TXT 104
+#define IDD_SHARE_PATH 105
+#define IDD_SHARE_USED_SPC_CLR 106
+#define IDD_SHARE_USED_SPC_TXT 107
+#define IDD_SHARE_USED_SPC 108
+#define IDD_SHARE_USED_SPC_MB 109
+#define IDD_SHARE_FREE_SPC_CLR 110
+#define IDD_SHARE_FREE_SPC_TXT 111
+#define IDD_SHARE_FREE_SPC 112
+#define IDD_SHARE_FREE_SPC_MB 113
+#define IDD_SHARE_MAX_SPC_TXT 114
+#define IDD_SHARE_MAX_SPC 115
+#define IDD_SHARE_MAX_SPC_MB 116
+#define IDD_SHARE_PIE 117
+#define IDD_SHARE_LFN_TXT 118
+
+//
+// Controls Ids in DLG_PRINTER_SUMMARYINFO
+//
+#define IDD_PRINTER_NAME 101
+#define IDD_PRINTER_QUEUE_TXT 102
+#define IDD_PRINTER_QUEUE 103
+
+//
+// Controls Ids in DLG_GLOBAL_WHOAMI
+//
+#define IDD_GLOBAL_SERVERLIST_T 101
+#define IDD_GLOBAL_SERVERLIST 102
+#define IDD_GLOBAL_SVRLIST_DESC 103
+#define IDD_DETACH 104
+#define IDD_REFRESH 105
+
+//
+// String Ids
+//
+#define IDS_START 20000
+#define IDS_NONE (IDS_START + 0)
+#define IDS_NETWARE_PRINT_CAPTION (IDS_START + 1)
+#define IDS_NOTHING_TO_CONFIGURE (IDS_START + 2)
+#define IDS_NETWARE_TITLE (IDS_START + 3)
+#define IDS_AUTH_FAILURE_TITLE (IDS_START + 4)
+#define IDS_NO_PREFERRED (IDS_START + 5)
+#define IDS_LOGIN_FAILURE_WARNING (IDS_START + 6)
+#define IDS_AUTH_FAILURE_WARNING (IDS_START + 7)
+#define IDS_CHANGE_PASSWORD_INFO (IDS_START + 8)
+#define IDS_INVALID_SERVER (IDS_START + 9)
+#define IDS_PASSWORD_HAS_EXPIRED (IDS_START + 10)
+#define IDS_AUTH_ACC_RESTRICTION (IDS_START + 11)
+#define IDS_LOGIN_ACC_RESTRICTION (IDS_START + 12)
+#define IDS_PASSWORD_HAS_EXPIRED1 (IDS_START + 13)
+#define IDS_BAD_PASSWORDS (IDS_START + 14)
+#define IDS_CHANGE_PASSWORD_TITLE (IDS_START + 15)
+#define IDS_START_WORKSTATION_FIRST (IDS_START + 16)
+#define IDS_PASSWORD_HAS_EXPIRED0 (IDS_START + 17)
+#define IDS_PASSWORD_HAS_EXPIRED2 (IDS_START + 18)
+#define IDS_LOGIN_DISABLED (IDS_START + 19)
+#define IDS_START_WORKSTATION_FIRST_NTAS (IDS_START + 20)
+#define IDS_SERVER (IDS_START + 21)
+#define IDS_CONTEXT (IDS_START + 22)
+#define IDS_CONNECT_NO_ERROR_TEXT (IDS_START + 23)
+#define IDS_TITLE_LOGOUT (IDS_START + 24)
+#define IDS_MESSAGE_LOGOUT_QUESTION (IDS_START + 25)
+#define IDS_MESSAGE_LOGOUT_FAILED (IDS_START + 26)
+#define IDS_MESSAGE_NOT_ATTACHED (IDS_START + 27)
+#define IDS_MESSAGE_DETACHED (IDS_START + 28)
+#define IDS_MESSAGE_LOGOUT_CONFIRM (IDS_START + 29)
+#define IDS_TITLE_WHOAMI (IDS_START + 30)
+#define IDS_MESSAGE_ATTACHED (IDS_START + 31)
+#define IDS_BYTES (IDS_START + 32)
+#define IDS_ORDERKB (IDS_START + 33)
+#define IDS_ORDERMB (IDS_START + 34)
+#define IDS_ORDERGB (IDS_START + 35)
+#define IDS_ORDERTB (IDS_START + 36)
+#define IDS_STATE_NOT_LOGGED_IN (IDS_START + 37)
+#define IDS_MESSAGE_NOT_ATTACHED_TO_TREE (IDS_START + 38)
+#define IDS_MESSAGE_ATTACHED_TO_TREE (IDS_START + 39)
+#define IDS_LOGIN_TYPE_NDS (IDS_START + 40)
+#define IDS_LOGIN_TYPE_BINDERY (IDS_START + 41)
+#define IDS_LOGIN_STATUS_SEPARATOR (IDS_START + 42)
+#define IDS_LOGIN_STATUS_AUTHENTICATED (IDS_START + 43)
+#define IDS_LOGIN_STATUS_NOT_AUTHENTICATED (IDS_START + 44)
+#define IDS_LOGIN_STATUS_LICENSED (IDS_START + 45)
+#define IDS_LOGIN_STATUS_NOT_LICENSED (IDS_START + 46)
+#define IDS_LOGIN_STATUS_LOGGED_IN (IDS_START + 47)
+#define IDS_LOGIN_STATUS_ATTACHED_ONLY (IDS_START + 48)
+#define IDS_LOGIN_STATUS_NOT_ATTACHED (IDS_START + 49)
+#define IDS_MESSAGE_CONNINFO_ERROR (IDS_START + 50)
+#define IDS_MESSAGE_ADDCONN_ERROR (IDS_START + 51)
+#define IDS_MESSAGE_CONTEXT_ERROR (IDS_START + 52)
+#define IDS_MESSAGE_LOGGED_IN_TREE (IDS_START + 53)
+#define IDS_MESSAGE_NOT_LOGGED_IN_TREE (IDS_START + 54)
+#define IDS_MESSAGE_LOGGED_IN_SERVER (IDS_START + 55)
+#define IDS_MESSAGE_NOT_LOGGED_IN_SERVER (IDS_START + 56)
+#define IDS_MESSAGE_PROPERTIES_ERROR (IDS_START + 57)
+#define IDS_TREE_NAME_MISSING (IDS_START + 58)
+#define IDS_CONTEXT_MISSING (IDS_START + 59)
+#define IDS_SERVER_MISSING (IDS_START + 60)
+#define IDS_CONTEXT_AUTH_FAILURE_WARNING (IDS_START + 61)
+#define IDS_COLUMN_NAME (IDS_START + 62)
+#define IDS_COLUMN_CONN_TYPE (IDS_START + 63)
+#define IDS_COLUMN_CONN_NUMBER (IDS_START + 64)
+#define IDS_COLUMN_USER (IDS_START + 65)
+#define IDS_COLUMN_STATUS (IDS_START + 66)
+#define IDS_MESSAGE_GETINFO_ERROR (IDS_START + 67)
+#define IDS_CP_FAILURE_WARNING (IDS_START + 68)
+#define IDS_CHANGE_PASSWORD_CONFLICT (IDS_START + 69)
+#define IDS_NO_TREES_DETECTED (IDS_START + 70)
+#define IDS_MESSAGE_LOGOUT_FROM_SERVER_FAILED (IDS_START + 71)
+
+//
+// String Ids for Shell Extension
+//
+#define IDS_MESSAGE_CONTEXT_CHANGED (IDS_START + 100)
+
+#define IDS_VERBS_BASE (IDS_START + 150)
+#define IDS_VERBS_HELP_BASE (IDS_START + 200)
+
+#define IDO_VERB_WHOAMI 1
+#define IDO_VERB_LOGOUT 2
+#define IDO_VERB_ATTACHAS 3
+#define IDO_VERB_GLOBALWHOAMI 4
+#define IDO_VERB_SETDEFAULTCONTEXT 5
+#define IDO_VERB_MAPNETWORKDRIVE 6
+#define IDO_VERB_TREEWHOAMI 7
+
+
+#define IDS_END (IDS_START + 1000)
+
+#endif // _NWSHRC_H_
diff --git a/private/nw/svcdlls/nwwks/client/nwshui.cxx b/private/nw/svcdlls/nwwks/client/nwshui.cxx
new file mode 100644
index 000000000..48dea8f96
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/nwshui.cxx
@@ -0,0 +1,1630 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ nwshui.cxx
+
+Abstract:
+
+ This module implements the context menu actions of shell extension classes.
+
+Author:
+
+ Yi-Hsin Sung (yihsins) 25-Oct-1995
+
+--*/
+
+extern "C"
+{
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+#include <commctrl.h>
+#include <shellapi.h>
+#include <shlobj.h>
+#define DONT_WANT_SHELLDEBUG
+#include <shsemip.h>
+#include <winnetwk.h>
+#include <npapi.h>
+#include <ntddnwfs.h>
+#include <ndsapi32.h>
+#include <nwapi.h>
+#include <nwwks.h>
+#include <nwmisc.h>
+#include "nwutil.h"
+}
+
+#include "nwshcmn.h"
+#include "nwshrc.h"
+
+extern "C"
+{
+NTSTATUS
+NwNdsOpenRdrHandle(
+ OUT PHANDLE phandleRdr );
+}
+
+#define MAX_ONE_CONN_INFORMATION_SIZE 512
+#define NW_ENUM_EXTRA_BYTES 256
+#define GLOBAL_WHOAMI_REFRESH_INTERVAL 30000 // in milliseconds, Win95 uses 10000
+
+DWORD
+LogoutFromServer(
+ LPWSTR pszServer,
+ PBOOL pfDisconnected
+);
+
+BOOL
+CALLBACK
+GlobalWhoAmIDlgProc(
+ HWND hwndDlg,
+ UINT msg,
+ WPARAM wParam,
+ LPARAM lParam );
+
+VOID
+GetConnectionStatusString(
+ PCONN_STATUS pConnStatus,
+ LPBYTE Buffer,
+ DWORD nSize
+);
+
+HRESULT
+NWUIWhoAmI(
+ HWND hParent,
+ LPNETRESOURCE pNetRes
+)
+{
+ DWORD err = NO_ERROR;
+ DWORD ResumeKey = 0;
+ LPBYTE pBuffer = NULL;
+ DWORD EntriesRead = 0;
+ DWORD dwMessageId;
+ WCHAR szUserName[MAX_PATH+1] = L"";
+ WCHAR szConnType[128];
+ WCHAR szRemoteName[MAX_PATH + 1];
+
+ szConnType[0] = 0;
+
+ if ( pNetRes->dwDisplayType == RESOURCEDISPLAYTYPE_SERVER )
+ {
+ // Need to extract the server name from full UNC path
+ NwExtractServerName( pNetRes->lpRemoteName, szRemoteName );
+ dwMessageId = IDS_MESSAGE_NOT_ATTACHED;
+ }
+ else // NDS container name
+ {
+ // Need to extract the tree name from the full UNC path
+ szRemoteName[0] = TREECHAR;
+ NwExtractTreeName( pNetRes->lpRemoteName, szRemoteName+1);
+ dwMessageId = IDS_MESSAGE_NOT_ATTACHED_TO_TREE;
+ }
+
+ err = NwGetConnectionStatus( szRemoteName,
+ &ResumeKey,
+ &pBuffer,
+ &EntriesRead );
+
+ if ( err == NO_ERROR && EntriesRead > 0 )
+ // For trees, we'll get more than one entry
+ {
+ PCONN_STATUS pConnStatus = (PCONN_STATUS) pBuffer;
+ LPWSTR pszStart = szConnType;
+ DWORD nSize = sizeof(szConnType)/sizeof(WCHAR);
+
+ if ( EntriesRead > 1 && szRemoteName[0] == TREECHAR )
+ {
+ // If there is more than one entry for trees,
+ // then we need to find one entry where username is not null
+ // and the login type is NDS.
+ // If we cannot find one, then just use the first one.
+
+ DWORD i;
+ PCONN_STATUS pConnStatusTmp = pConnStatus;
+ PCONN_STATUS pConnStatusUser = NULL;
+ PCONN_STATUS pConnStatusNoUser = NULL;
+
+ for ( i = 0; i < EntriesRead ; i++ )
+ {
+ if ( pConnStatusTmp->fNds )
+ {
+ pConnStatusNoUser = pConnStatusTmp;
+
+ if ( ( pConnStatusTmp->pszUserName != NULL )
+ && ( ( pConnStatusTmp->dwConnType == NW_CONN_NDS_AUTHENTICATED_NO_LICENSE )
+ || ( pConnStatusTmp->dwConnType == NW_CONN_NDS_AUTHENTICATED_LICENSED )
+ )
+ )
+ {
+ // Found it
+ pConnStatusUser = pConnStatusTmp;
+ break;
+ }
+ }
+
+ // Continue with the next item
+ pConnStatusTmp = (PCONN_STATUS) ( (DWORD) pConnStatusTmp
+ + pConnStatusTmp->dwTotalLength);
+ }
+
+ if ( pConnStatusUser ) // found one nds entry with a user name
+ pConnStatus = pConnStatusUser;
+ else if ( pConnStatusNoUser ) // use an nds entry with no user name
+ pConnStatus = pConnStatusNoUser;
+ // else use the first entry
+
+ }
+
+ if ( szRemoteName[0] == TREECHAR // A tree
+ || !pConnStatus->fPreferred // A server but not preferred
+ )
+ {
+ // Show this conneciton only if this is a tree or if this is
+ // not a implicit connection to the preferred server.
+
+ dwMessageId = pNetRes->dwDisplayType == RESOURCEDISPLAYTYPE_SERVER ?
+ IDS_MESSAGE_ATTACHED : IDS_MESSAGE_ATTACHED_TO_TREE;
+
+ if ( pConnStatus->pszUserName )
+ {
+ wcscpy( szUserName, pConnStatus->pszUserName );
+ }
+
+ if ( pConnStatus->fNds ) // NDS
+ {
+ LoadString( ::hmodNW, IDS_LOGIN_TYPE_NDS, pszStart, nSize );
+ nSize -= wcslen( pszStart );
+ pszStart += wcslen( pszStart);
+
+ }
+ else // Bindery
+ {
+ LoadString( ::hmodNW, IDS_LOGIN_TYPE_BINDERY, pszStart, nSize );
+ nSize -= wcslen( pszStart );
+ pszStart += wcslen( pszStart);
+ }
+
+ LoadString( ::hmodNW, IDS_LOGIN_STATUS_SEPARATOR, pszStart, nSize );
+ nSize -= wcslen( pszStart );
+ pszStart += wcslen( pszStart);
+
+ GetConnectionStatusString( pConnStatus, (LPBYTE) pszStart, nSize );
+ }
+ }
+
+ if ( err == NO_ERROR )
+ {
+ // Popup the message now.
+ ::MsgBoxPrintf( hParent,
+ dwMessageId,
+ IDS_TITLE_WHOAMI,
+ MB_OK | MB_SETFOREGROUND | MB_ICONINFORMATION,
+ szRemoteName[0] == TREECHAR? szRemoteName + 1 : szRemoteName,
+ szUserName,
+ szConnType );
+ }
+ else // error occurred
+ {
+ ::MsgBoxErrorPrintf( hParent,
+ IDS_MESSAGE_CONNINFO_ERROR,
+ IDS_TITLE_WHOAMI,
+ MB_OK | MB_SETFOREGROUND | MB_ICONSTOP,
+ err,
+ NULL );
+ }
+
+ if ( pBuffer != NULL )
+ {
+ LocalFree( pBuffer );
+ pBuffer = NULL;
+ }
+
+ return NOERROR;
+}
+
+VOID
+GetConnectionStatusString(
+ PCONN_STATUS pConnStatus,
+ LPBYTE Buffer,
+ DWORD nSize
+)
+{
+ LPWSTR pszStart = (LPWSTR) Buffer;
+ DWORD dwMessageId = 0;
+
+ if ( pConnStatus->fNds ) // NDS
+ {
+ if ( pConnStatus->dwConnType == NW_CONN_NOT_AUTHENTICATED )
+ {
+ dwMessageId = IDS_LOGIN_STATUS_NOT_AUTHENTICATED;
+ }
+ else if ( pConnStatus->dwConnType == NW_CONN_DISCONNECTED )
+ {
+ dwMessageId = IDS_LOGIN_STATUS_NOT_ATTACHED;
+ }
+ else // authenticated, licensed or unlicensed
+ {
+ LoadString( ::hmodNW, IDS_LOGIN_STATUS_AUTHENTICATED,
+ pszStart, nSize );
+ nSize -= wcslen( pszStart );
+ pszStart += wcslen( pszStart);
+
+ if ( pConnStatus->dwConnType == NW_CONN_NDS_AUTHENTICATED_LICENSED )
+ dwMessageId = IDS_LOGIN_STATUS_LICENSED;
+ else // NW_CONN_NDS_AUTHENTICATED_NO_LICENSE
+ dwMessageId = IDS_LOGIN_STATUS_NOT_LICENSED;
+ }
+ }
+ else // Bindery
+ {
+ if ( pConnStatus->dwConnType == NW_CONN_BINDERY_LOGIN )
+ dwMessageId = IDS_LOGIN_STATUS_LOGGED_IN;
+ else if ( pConnStatus->dwConnType == NW_CONN_DISCONNECTED )
+ dwMessageId = IDS_LOGIN_STATUS_NOT_ATTACHED;
+ else
+ dwMessageId = IDS_LOGIN_STATUS_ATTACHED_ONLY;
+ }
+
+ LoadString( ::hmodNW, dwMessageId, pszStart, nSize );
+}
+
+HRESULT
+NWUIGlobalWhoAmI(
+ HWND hParent
+)
+{
+ ::DialogBoxParam( ::hmodNW,
+ MAKEINTRESOURCE(DLG_GLOBAL_WHOAMI),
+ hParent,
+ (DLGPROC) ::GlobalWhoAmIDlgProc,
+ NULL );
+
+ return NOERROR;
+}
+
+HRESULT
+NWUILogOut(
+ HWND hParent,
+ LPNETRESOURCE pNetRes,
+ PBOOL pfDisconnected
+)
+{
+ DWORD err = NO_ERROR;
+ WCHAR szServer[MAX_PATH+1];
+ BOOL fAttached;
+ BOOL fAuthenticated;
+ DWORD dwMessageId;
+
+ *pfDisconnected = FALSE;
+
+ // Need to extract the server name from full UNC path
+ NwExtractServerName( pNetRes->lpRemoteName, szServer );
+
+ err = NwIsServerOrTreeAttached( szServer, &fAttached, &fAuthenticated );
+
+ if ( err == NO_ERROR && !fAttached )
+ {
+ dwMessageId = IDS_MESSAGE_NOT_ATTACHED;
+ }
+ else if ( err == NO_ERROR ) // attached
+ {
+ int nRet = ::MsgBoxPrintf( hParent,
+ IDS_MESSAGE_LOGOUT_CONFIRM,
+ IDS_TITLE_LOGOUT,
+ MB_YESNO | MB_SETFOREGROUND | MB_ICONQUESTION);
+
+ if ( nRet != IDYES )
+ return NOERROR;
+
+ err = LogoutFromServer( szServer, pfDisconnected );
+
+ if ( err == NO_ERROR )
+ dwMessageId = IDS_MESSAGE_DETACHED;
+ else
+ dwMessageId = IDS_MESSAGE_LOGOUT_FAILED;
+ }
+ else // error occurred
+ {
+ ::MsgBoxErrorPrintf( hParent,
+ IDS_MESSAGE_CONNINFO_ERROR,
+ IDS_TITLE_LOGOUT,
+ MB_OK | MB_SETFOREGROUND | MB_ICONSTOP,
+ err,
+ NULL );
+
+ return NOERROR;
+ }
+
+ ::MsgBoxPrintf( hParent,
+ dwMessageId,
+ IDS_TITLE_LOGOUT,
+ MB_OK | MB_SETFOREGROUND | MB_ICONINFORMATION );
+
+ return NOERROR;
+}
+
+DWORD
+LogoutFromServer(
+ LPWSTR pszServer,
+ PBOOL pfDisconnected
+)
+{
+ DWORD err = NO_ERROR;
+ HANDLE EnumHandle = (HANDLE) NULL;
+
+ LPNETRESOURCE NetR = NULL;
+ LPNETRESOURCEW SavePtr;
+
+ DWORD BytesNeeded = MAX_ONE_NETRES_SIZE;
+ DWORD EntriesRead = 0;
+ DWORD i;
+
+ *pfDisconnected = FALSE;
+
+ err = NPOpenEnum( RESOURCE_CONNECTED,
+ 0,
+ 0,
+ NULL,
+ &EnumHandle );
+
+ if ( err != NO_ERROR)
+ {
+ EnumHandle = (HANDLE) NULL;
+ goto CleanExit;
+ }
+
+ //
+ // Allocate buffer to get server list.
+ //
+ if ((NetR = (LPNETRESOURCE) LocalAlloc( 0, BytesNeeded )) == NULL)
+ {
+ err = ERROR_NOT_ENOUGH_MEMORY;
+ goto CleanExit;
+ }
+
+ do {
+
+ EntriesRead = 0xFFFFFFFF; // Read as many as possible
+
+ err = NwEnumConnections( EnumHandle,
+ &EntriesRead,
+ (LPVOID) NetR,
+ &BytesNeeded,
+ TRUE );
+
+
+ if ( err == WN_SUCCESS)
+ {
+ SavePtr = NetR;
+
+ for (i = 0; i < EntriesRead; i++, NetR++)
+ {
+ BYTE Buffer[MAX_ONE_CONN_INFORMATION_SIZE];
+ BOOL fImplicit;
+ LPWSTR pszCurrentServer;
+
+ fImplicit = FALSE;
+
+ if ( NwIsNdsSyntax( NetR->lpRemoteName))
+ {
+ // For Nds name, the server name might not be in the full UNC name.
+ // Hence we need to get the server name from the Rdr.
+
+ DWORD err1 = NwGetConnectionInformation(
+ NetR->lpLocalName? NetR->lpLocalName : NetR->lpRemoteName,
+ Buffer,
+ sizeof(Buffer));
+
+ if ( err1 != NO_ERROR )
+ continue; // continue with the next entry if error occurred
+
+ pszCurrentServer = ((PCONN_INFORMATION) Buffer)->HostServer;
+
+ // Need to NULL terminate the server name, this will probably used the space
+ // occupied by UserName but since we are not using it, it is probably ok.
+ LPWSTR pszTemp = (LPWSTR) ((DWORD) pszCurrentServer
+ + ((PCONN_INFORMATION) Buffer)->HostServerLength );
+ *pszTemp = 0;
+
+ }
+ else // in the form \\server\sys
+ {
+ LPWSTR pszTemp;
+ wcscpy( (LPWSTR) Buffer, NetR->lpRemoteName + 2 ); // go past two backslashes
+
+ if ( pszTemp = wcschr( (LPWSTR) Buffer, L'\\' ))
+ *pszTemp = 0;
+ else
+ {
+ // The remote name contains only \\server, hence if the local name
+ // is null, this is a implicit connection
+ if ( NetR->lpLocalName == NULL )
+ fImplicit = TRUE;
+ }
+
+ pszCurrentServer = (LPWSTR) Buffer;
+ }
+
+
+ if ( _wcsicmp( pszCurrentServer, pszServer ) == 0 )
+ {
+
+ do {
+
+ // for implicit connections, we need to try and disconnect until
+ // we deleted all the implicit connections, i.e. until we
+ // get the invalid handle error
+
+ // NOTE: If we don't pass in CONNECT_UPDATE_PROFILE, shell won't update
+ // the windows that got disconnected. What do we want to do here?
+ err = WNetCancelConnection2(
+ NetR->lpLocalName? NetR->lpLocalName : NetR->lpRemoteName,
+ 0, // CONNECT_UPDATE_PROFILE,
+ TRUE );
+
+ if ( err == NO_ERROR )
+ *pfDisconnected = TRUE;
+
+ } while ( fImplicit && ( err == NO_ERROR));
+
+ if ( err == ERROR_INVALID_HANDLE )
+ {
+ // implicit connection will sometimes return this if the explicit connection
+ // is already disconnected
+ err = NO_ERROR;
+ }
+
+ if ( err != NO_ERROR )
+ {
+ NetR = SavePtr;
+ goto CleanExit;
+ }
+ }
+ }
+
+ NetR = SavePtr;
+ }
+ else if ( err != WN_NO_MORE_ENTRIES)
+ {
+ if ( err == WN_MORE_DATA)
+ {
+
+ //
+ // Original buffer was too small. Free it and allocate
+ // the recommended size and then some to get as many
+ // entries as possible.
+ //
+
+ (void) LocalFree((HLOCAL) NetR);
+
+ BytesNeeded += NW_ENUM_EXTRA_BYTES;
+
+ if ((NetR = (LPNETRESOURCE) LocalAlloc( 0, BytesNeeded )) == NULL)
+ {
+ err = ERROR_NOT_ENOUGH_MEMORY;
+ goto CleanExit;
+ }
+ }
+ else
+ {
+ goto CleanExit;
+ }
+ }
+
+ } while (err != WN_NO_MORE_ENTRIES);
+
+ if ( err == WN_NO_MORE_ENTRIES)
+ err = NO_ERROR;
+
+CleanExit:
+
+ if (EnumHandle != (HANDLE) NULL)
+ (void) NPCloseEnum( EnumHandle);
+
+ if (NetR != NULL)
+ {
+ (void) LocalFree( (HLOCAL) NetR);
+ NetR = NULL;
+ }
+
+ return err;
+}
+
+HRESULT
+NWUIAttachAs(
+ HWND hParent,
+ LPNETRESOURCE pNetRes
+)
+{
+ DWORD err = NO_ERROR;
+ WCHAR szServerName[MAX_PATH+1];
+ BOOL fAttached;
+ BOOL fAuthenticated;
+
+ // First, see if we are attached to the server.
+ // Note, Attach as menu will be disabled on the NDS servers that we are already logged into.
+
+ // Need to extract the server name from full UNC path
+ szServerName[0] = szServerName[1] = L'\\';
+ NwExtractServerName( pNetRes->lpRemoteName, szServerName + 2 );
+
+ err = NwIsServerOrTreeAttached( szServerName + 2, &fAttached, &fAuthenticated );
+
+ if ( err == NO_ERROR && fAttached && fAuthenticated )
+ {
+ // Already attached and authenticated to the server.
+ // So, ask the user if he wants to log out first.
+
+ int nRet = ::MsgBoxPrintf( hParent,
+ IDS_MESSAGE_LOGOUT_QUESTION,
+ IDS_TITLE_LOGOUT,
+ MB_YESNO | MB_SETFOREGROUND | MB_ICONQUESTION );
+
+ if ( nRet != IDYES )
+ return NOERROR;
+
+ BOOL fDisconnected = FALSE; // can be used to refresh the shell if needed
+ err = LogoutFromServer( szServerName + 2, &fDisconnected );
+
+ if ( err != NO_ERROR )
+ {
+ ::MsgBoxPrintf( hParent,
+ IDS_MESSAGE_LOGOUT_FAILED,
+ IDS_TITLE_LOGOUT,
+ MB_OK | MB_SETFOREGROUND | MB_ICONSTOP );
+
+ return NOERROR;
+ }
+ }
+
+ // If error occurred, just assume we are not attached to the server and continue on
+ // to login to the server.
+
+ DWORD dwConnFlags = CONNECT_INTERACTIVE | CONNECT_PROMPT;
+
+ // See if the server is in the default context tree.
+ // If yes, then we don't need to prompt the password first before connecting
+ // in WNetAddConnection3.
+
+ BOOL fInDefaultTree = FALSE;
+ err = NwIsServerInDefaultTree( pNetRes->lpRemoteName, &fInDefaultTree );
+
+ if ( (err == NO_ERROR ) && fInDefaultTree )
+ dwConnFlags = CONNECT_INTERACTIVE;
+
+ //
+ // Now call WNetAddConnection3
+ //
+
+ // NOTE: net use \\mars_srv0 will succeed
+ // but net use \\marsdev\cn=mars_srv0... will failed
+ // Hence, just use the server name to connect.
+ LPWSTR pszSave = pNetRes->lpRemoteName;
+ pNetRes->lpRemoteName = szServerName;
+
+ err = WNetAddConnection3( hParent,
+ pNetRes,
+ NULL,
+ NULL,
+ dwConnFlags );
+
+ if ( err != WN_SUCCESS && err != WN_CANCEL )
+ {
+ ::MsgBoxErrorPrintf( hParent,
+ IDS_MESSAGE_ADDCONN_ERROR,
+ IDS_NETWARE_TITLE,
+ MB_OK | MB_SETFOREGROUND | MB_ICONSTOP,
+ err,
+ pNetRes->lpRemoteName );
+ }
+
+ pNetRes->lpRemoteName = pszSave; // restore the original remote name just in case
+
+ return NOERROR;
+}
+
+
+#if 0
+HRESULT
+NWUISetDefaultContext(
+ HWND hParent,
+ LPNETRESOURCE pNetRes
+)
+{
+ DWORD dwPrintOptions;
+ LPWSTR pszCurrentContext = NULL;
+ WCHAR szNewContext[MAX_PATH+1];
+
+ DWORD err = NO_ERROR;
+ HANDLE handleRdr = NULL;
+
+ UNICODE_STRING uTree;
+ UNICODE_STRING uContext;
+ LPWSTR pszContext = NULL;
+
+ // Open a handle to the redirector
+ err = RtlNtStatusToDosError( ::NwNdsOpenRdrHandle( &handleRdr ));
+
+ if ( err != NO_ERROR )
+ goto CleanExit;
+
+ // Get the print option so that we can use it later
+ err = ::NwQueryInfo( &dwPrintOptions, &pszCurrentContext );
+
+ if ( err != NO_ERROR )
+ goto CleanExit;
+
+ wcscpy( szNewContext, pNetRes->lpRemoteName + 1 ); // get past 1st '\\'
+ szNewContext[0] = TREECHAR; // in the format "*TREE\CONTEXT"
+
+ if ( (pszContext = wcschr( szNewContext, L'\\' )) != NULL )
+ {
+ *pszContext = 0;
+ RtlInitUnicodeString( &uContext, pszContext + 1 );
+ }
+ else
+ RtlInitUnicodeString( &uContext, L"");
+
+ RtlInitUnicodeString( &uTree, szNewContext+1 );
+
+ if ( (err = RtlNtStatusToDosError( ::NwNdsSetTreeContext( handleRdr, &uTree, &uContext)))
+ == NO_ERROR )
+ {
+ *pszContext = L'\\';
+
+ if ((err = ::NwSetInfoInRegistry( dwPrintOptions, szNewContext )) == NO_ERROR )
+ {
+ ::MsgBoxPrintf( hParent,
+ IDS_MESSAGE_CONTEXT_CHANGED,
+ IDS_NETWARE_TITLE,
+ MB_OK | MB_SETFOREGROUND | MB_ICONINFORMATION,
+ pszCurrentContext
+ ? ( *pszCurrentContext == TREECHAR
+ ? pszCurrentContext + 1
+ : pszCurrentContext )
+ : L"",
+ szNewContext+1 );
+ }
+ }
+
+CleanExit:
+
+ if ( err != NO_ERROR )
+ {
+ ::MsgBoxErrorPrintf( hParent,
+ IDS_MESSAGE_CONTEXT_ERROR,
+ IDS_NETWARE_TITLE,
+ MB_OK | MB_SETFOREGROUND | MB_ICONSTOP,
+ err,
+ szNewContext+1);
+ }
+
+ if ( pszCurrentContext != NULL )
+ {
+ LocalFree( pszCurrentContext );
+ pszCurrentContext = NULL;
+ }
+
+ if ( handleRdr != NULL )
+ {
+ ::NtClose( handleRdr );
+ handleRdr = NULL;
+ }
+
+ return NOERROR;
+}
+#endif
+
+HRESULT
+NWUIMapNetworkDrive(
+ HWND hParent,
+ LPNETRESOURCE pNetRes
+)
+{
+ HRESULT hres;
+ CONNECTDLGSTRUCT cds;
+
+ cds.cbStructure = sizeof(cds);
+ cds.hwndOwner = hParent;
+ cds.lpConnRes = pNetRes;
+ cds.dwFlags = CONNDLG_RO_PATH;
+
+ if ( (( hres = WNetConnectionDialog1( &cds )) == WN_SUCCESS )
+ && ( g_pFuncSHChangeNotify )
+ )
+ {
+ WCHAR szPath[4];
+
+ szPath[0] = ((USHORT) cds.dwDevNum) - 1 + L'A';
+ szPath[1] = L':';
+ szPath[2] = L'\\';
+ szPath[3] = 0;
+
+ // Notify shell about added redirection
+ (*g_pFuncSHChangeNotify)( SHCNE_DRIVEADD,
+ SHCNF_FLUSH | SHCNF_PATH,
+ szPath,
+ NULL );
+
+ // And we need to open shell window on this path
+ if (g_pFuncSHExecuteEx)
+ {
+ SHELLEXECUTEINFO ExecInfo;
+
+ ::memset(&ExecInfo,0,sizeof(ExecInfo));
+
+ ExecInfo.hwnd = hParent;
+ ExecInfo.lpVerb = 0;
+ ExecInfo.lpFile = szPath;
+ ExecInfo.lpParameters = NULL;
+ ExecInfo.lpDirectory = NULL;
+ ExecInfo.nShow = SW_NORMAL | SW_SHOW;
+ ExecInfo.fMask = SEE_MASK_CLASSNAME;
+ ExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
+ ExecInfo.lpClass = (LPWSTR) L"Folder";
+ ExecInfo.hkeyClass = NULL;
+
+ (*g_pFuncSHExecuteEx)(&ExecInfo);
+ }
+ }
+
+ return hres;
+}
+
+
+
+#define LB_PCT_NAME 25
+#define LB_PCT_TYPE 11
+#define LB_PCT_CONN 11
+#define LB_PCT_USER 25
+#define LB_PCT_STATUS 27
+
+#define BITMAP_WIDTH 16
+#define BITMAP_HEIGHT 16
+
+#define MAPCOLOR RGB(0, 255, 0)
+
+static UINT uiNDSIconIndex = 0;
+static UINT uiServerIconIndex = 0;
+
+/*
+ * FillConnectionsListView
+ * -----------------------
+ *
+ * Fill list box with information on active connections
+ */
+VOID
+FillConnectionsListView(
+ HWND hwndDlg
+)
+{
+ HWND hwndLV = ::GetDlgItem(hwndDlg,IDD_GLOBAL_SERVERLIST);
+ LV_ITEM lvI;
+ WCHAR szSubItemText[MAX_PATH+1];
+ UINT uiInsertedIndex;
+
+ DWORD ResumeKey = 0;
+ LPBYTE pConnBuffer = NULL;
+ DWORD EntriesRead = 0;
+ DWORD err = NO_ERROR;
+
+ // Prepare ListView structure
+ lvI.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM | LVIF_STATE;
+ lvI.state = 0;
+ lvI.stateMask = 0;
+
+ do {
+
+ if ( pConnBuffer != NULL )
+ {
+ LocalFree( pConnBuffer );
+ pConnBuffer = NULL;
+ }
+
+ err = NwGetConnectionStatus( NULL,
+ &ResumeKey,
+ &pConnBuffer,
+ &EntriesRead );
+
+
+ if ( ( err != NO_ERROR )
+ || ( EntriesRead == 0 )
+ )
+ {
+ goto CleanExit;
+ }
+
+ PCONN_STATUS pConnStatus = (PCONN_STATUS) pConnBuffer;
+
+ for ( DWORD i = 0; i < EntriesRead; i++)
+ {
+ // Allocate and initialize new item structure for use in the listbox
+
+ DWORD dwSize;
+
+ //
+ // Don't need to show preferred server with only implicit
+ // connections since we can't disconnect from it.
+ //
+ if ( pConnStatus->fPreferred )
+ {
+ // Continue with the next item
+ pConnStatus = (PCONN_STATUS) ( (DWORD) pConnStatus
+ + pConnStatus->dwTotalLength);
+ continue;
+ }
+
+ //
+ // Allocate and copy the connection information to be store with
+ // the listbox
+ //
+ PCONN_STATUS pConnStatusKeep =
+ (PCONN_STATUS) LocalAlloc( LMEM_ZEROINIT, pConnStatus->dwTotalLength );
+ if ( pConnStatusKeep == NULL )
+ {
+ err = ERROR_NOT_ENOUGH_MEMORY;
+ goto CleanExit;
+ }
+
+ memcpy( pConnStatusKeep, pConnStatus, pConnStatus->dwTotalLength );
+
+ dwSize = sizeof(CONN_STATUS);
+
+ if ( pConnStatus->pszServerName )
+ {
+ pConnStatusKeep->pszServerName =
+ (LPWSTR) ((DWORD)pConnStatusKeep + dwSize );
+
+ NwMakePrettyDisplayName( pConnStatusKeep->pszServerName );
+
+ dwSize += (wcslen(pConnStatus->pszServerName)+1)*sizeof(WCHAR);
+ }
+
+ if ( pConnStatus->pszUserName )
+ {
+ pConnStatusKeep->pszUserName =
+ (LPWSTR) ((DWORD)pConnStatusKeep + dwSize );
+
+ dwSize += (wcslen(pConnStatus->pszUserName)+1) * sizeof(WCHAR);
+
+ NwAbbreviateUserName( pConnStatus->pszUserName,
+ pConnStatusKeep->pszUserName );
+
+ NwMakePrettyDisplayName( pConnStatusKeep->pszUserName );
+ }
+
+ if ( pConnStatus->pszTreeName )
+ {
+ pConnStatusKeep->pszTreeName =
+ (LPWSTR) ((DWORD)pConnStatusKeep + dwSize );
+ }
+
+ //
+ // Construct the item to add to the listbox
+ //
+ lvI.iItem = i;
+ lvI.iSubItem = 0;
+ lvI.pszText = szSubItemText;
+ lvI.cchTextMax = sizeof(szSubItemText);
+
+ // Select proper icon
+ lvI.iImage = pConnStatusKeep->fNds? uiNDSIconIndex
+ : uiServerIconIndex;
+
+ lvI.lParam = (LPARAM) pConnStatusKeep;
+
+ wcscpy( szSubItemText, pConnStatusKeep->pszServerName );
+
+ // Insert the item
+ uiInsertedIndex = ListView_InsertItem( hwndLV, &lvI);
+
+ if ( uiInsertedIndex != -1 )
+ {
+ // Added item itself - now for specific columns
+
+ // First, add the column indicating bindery or nds connection
+ if ( pConnStatusKeep->fNds )
+ {
+ LoadString( ::hmodNW, IDS_LOGIN_TYPE_NDS, szSubItemText,
+ sizeof(szSubItemText)/sizeof(szSubItemText[0]));
+ }
+ else
+ {
+ LoadString( ::hmodNW, IDS_LOGIN_TYPE_BINDERY, szSubItemText,
+ sizeof(szSubItemText)/sizeof(szSubItemText[0]));
+ }
+
+ ListView_SetItemText( hwndLV,
+ uiInsertedIndex,
+ 1, // SubItem id
+ szSubItemText );
+
+ // Next, Add the column indicating the connection number
+
+ if ( ( pConnStatusKeep->pszServerName[0] != TREECHAR )
+ && ( pConnStatusKeep->dwConnType != NW_CONN_DISCONNECTED )
+ )
+ {
+ // Add connection number only if it is a connection
+ // to a server and not a tree.
+ // A connection number only makes sense when not disconnected
+ ::wsprintf(szSubItemText,L"%4d",pConnStatusKeep->nConnNum );
+ ListView_SetItemText( hwndLV,
+ uiInsertedIndex,
+ 2, // SubItem id
+ szSubItemText );
+ }
+
+ // Then, add the column indicating the user name
+
+ *szSubItemText = L'\0';
+ if ( pConnStatusKeep->pszUserName )
+ {
+ wcscpy( szSubItemText, pConnStatusKeep->pszUserName );
+
+ ListView_SetItemText( hwndLV,
+ uiInsertedIndex,
+ 3, // SubItem id
+ szSubItemText );
+ }
+
+ // Last of all, add the column indicating the connection status
+
+ *szSubItemText = L'\0';
+ GetConnectionStatusString( pConnStatusKeep,
+ (LPBYTE) szSubItemText,
+ sizeof(szSubItemText)/sizeof(szSubItemText[0]));
+
+ ListView_SetItemText( hwndLV,
+ uiInsertedIndex,
+ 4, // SubItem id
+ szSubItemText );
+
+ }
+ else
+ {
+ // Failed inserting item in the list,
+ // need to delete the allocated CONN_STATUS
+ ASSERT( FALSE );
+ LocalFree( pConnStatusKeep );
+ pConnStatusKeep = NULL;
+ }
+
+ // Continue with the next item
+ pConnStatus = (PCONN_STATUS) ( (DWORD) pConnStatus
+ + pConnStatus->dwTotalLength);
+ }
+
+ } while ( ResumeKey != 0 );
+
+CleanExit:
+
+ if ( pConnBuffer != NULL )
+ {
+ LocalFree( pConnBuffer );
+ pConnBuffer = NULL;
+ }
+
+ if ( err != NO_ERROR )
+ {
+ // If error occurred, we will end the dialog
+
+ ::MsgBoxErrorPrintf( hwndDlg,
+ IDS_MESSAGE_CONNINFO_ERROR,
+ IDS_NETWARE_TITLE,
+ MB_OK | MB_SETFOREGROUND | MB_ICONSTOP,
+ err,
+ NULL );
+
+ ::EndDialog( hwndDlg, FALSE);
+ }
+}
+
+
+/*
+ * CreateConnectionsListView
+ * -------------------------
+ *
+ * Initialize the column headers of the list box
+ */
+VOID
+CreateConnectionsListView(
+ HWND hwndDlg
+)
+{
+ HWND hwndLV;
+ HIMAGELIST hSmall;
+ UINT uiLBoxWidth;
+
+ InitCommonControls();
+
+ // Get the handle of the listbox
+ hwndLV = ::GetDlgItem(hwndDlg,IDD_GLOBAL_SERVERLIST);
+
+ RECT rc;
+ ::GetWindowRect( ::GetDlgItem( hwndDlg, IDD_GLOBAL_SERVERLIST ), &rc );
+ uiLBoxWidth = rc.right - rc.left;
+
+ // Load image list
+ hSmall = ::ImageList_Create(BITMAP_WIDTH,BITMAP_HEIGHT,ILC_MASK,4,0);
+
+ // Load bitmap of the tree/server icon
+ HBITMAP hbm;
+ hbm = ::LoadBitmap(::hmodNW,MAKEINTRESOURCE(IDB_TREE_ICON));
+ if ((uiNDSIconIndex = ImageList_AddMasked(hSmall,hbm,MAPCOLOR)) == -1)
+ ASSERT(FALSE);
+
+ hbm = ::LoadBitmap(::hmodNW,MAKEINTRESOURCE(IDB_SERVER_ICON));
+ if ((uiServerIconIndex = ImageList_AddMasked(hSmall,hbm,MAPCOLOR)) == -1)
+ ASSERT(FALSE);
+
+ ImageList_SetBkColor(hSmall, CLR_NONE);
+
+ // Associate image list with list view control
+ ListView_SetImageList(hwndLV,hSmall,LVSIL_SMALL);
+
+ // Initialize columns in header
+ LV_COLUMN lvC;
+ UINT uiColumnIndex = 0;
+ WCHAR szColumnName[128];
+
+ lvC.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
+ lvC.fmt = LVCFMT_LEFT;
+ lvC.cx = (uiLBoxWidth*LB_PCT_NAME)/100;
+ lvC.pszText = szColumnName;
+
+ // Add the column header representing the server name
+ *szColumnName = L'\0';
+ ::LoadString( ::hmodNW,
+ IDS_COLUMN_NAME,
+ szColumnName,
+ sizeof(szColumnName)/sizeof(szColumnName[0]));
+ if ( ListView_InsertColumn(hwndLV,uiColumnIndex++,&lvC) == -1)
+ ASSERT(FALSE);
+
+
+ // Add the column header representing the conneciton type
+ *szColumnName = L'\0';
+ lvC.cx = (uiLBoxWidth*LB_PCT_TYPE)/100;
+ ::LoadString( ::hmodNW,
+ IDS_COLUMN_CONN_TYPE,
+ szColumnName,
+ sizeof(szColumnName)/sizeof(szColumnName[0]));
+ if ( ListView_InsertColumn(hwndLV,uiColumnIndex++,&lvC) == -1)
+ ASSERT(FALSE);
+
+ // Add the column header representing the connection number
+ *szColumnName = L'\0';
+ lvC.cx = (uiLBoxWidth*LB_PCT_CONN)/100;
+ lvC.fmt = LVCFMT_RIGHT;
+
+ ::LoadString( ::hmodNW,
+ IDS_COLUMN_CONN_NUMBER,
+ szColumnName,
+ sizeof(szColumnName)/sizeof(szColumnName[0]));
+ if ( ListView_InsertColumn(hwndLV,uiColumnIndex++,&lvC) == -1)
+ ASSERT(FALSE);
+
+ // Add the column header representing the name of the user
+ *szColumnName = L'\0';
+ lvC.cx = (uiLBoxWidth*LB_PCT_USER)/100;
+ lvC.fmt = LVCFMT_LEFT;
+
+ ::LoadString( ::hmodNW,
+ IDS_COLUMN_USER,
+ szColumnName,
+ sizeof(szColumnName)/sizeof(szColumnName[0]));
+
+ if ( ListView_InsertColumn(hwndLV,uiColumnIndex++,&lvC) == -1)
+ ASSERT(FALSE);
+
+ // Add the column header representing the status of the connection
+ *szColumnName = L'\0';
+ lvC.cx = (uiLBoxWidth*LB_PCT_STATUS)/100;
+ lvC.fmt = LVCFMT_LEFT;
+
+ ::LoadString( ::hmodNW,
+ IDS_COLUMN_STATUS,
+ szColumnName,
+ sizeof(szColumnName)/sizeof(szColumnName[0]));
+
+ if ( ListView_InsertColumn(hwndLV,uiColumnIndex++,&lvC) == -1)
+ ASSERT(FALSE);
+
+ // Now fill list view window with connection information
+ FillConnectionsListView( hwndDlg );
+
+} /* endproc CreateConnectionsListView */
+
+
+/*
+ * GlobalWhoAmI_ListViewCompareProc
+ * --------------------------------
+ *
+ */
+LRESULT CALLBACK
+GlobalWhoAmI_ListViewCompareProc(
+ LPARAM lParam1,
+ LPARAM lParam2,
+ LPARAM lParamSort
+)
+{
+ PCONN_STATUS pConnItem1 = (PCONN_STATUS)lParam1;
+ PCONN_STATUS pConnItem2 = (PCONN_STATUS)lParam2;
+
+ int iResult = 0;
+
+ if ( pConnItem1 && pConnItem2 )
+ {
+ switch(lParamSort)
+ {
+ case 0:
+ iResult = ::_wcsicmp( pConnItem1->pszServerName,
+ pConnItem2->pszServerName);
+ break;
+
+ case 1:
+ iResult = pConnItem1->fNds - pConnItem2->fNds;
+ break;
+
+ case 2:
+ iResult = pConnItem1->nConnNum - pConnItem2->nConnNum;
+ break;
+
+ case 3:
+ {
+ // pszUserName might be NULL, so we need to be careful when
+ // comparing them.
+ if ( pConnItem1->pszUserName )
+ {
+ if ( pConnItem2->pszUserName )
+ {
+ iResult = ::_wcsicmp( pConnItem1->pszUserName,
+ pConnItem2->pszUserName);
+ }
+ else
+ {
+ iResult = 1;
+ }
+ }
+ else
+ {
+ iResult = -1;
+ }
+ break;
+ }
+
+ case 4:
+ iResult = pConnItem1->dwConnType - pConnItem2->dwConnType;
+ break;
+
+ default:
+ iResult = 0;
+ break;
+ }
+ }
+
+ return (iResult);
+
+}
+
+LRESULT
+NotifyHandler(
+ HWND hwndDlg,
+ UINT uMsg,
+ WPARAM wParam,
+ LPARAM lParam
+)
+{
+ NM_LISTVIEW *lpNm = (NM_LISTVIEW *)lParam;
+ NMHDR *lphdr = &lpNm->hdr;
+
+ HWND hwndLV = ::GetDlgItem(hwndDlg,IDD_GLOBAL_SERVERLIST);
+
+ switch(lphdr->code)
+ {
+ case LVN_ITEMCHANGED:
+ // Check for change in item state, make sure item has received focus
+ if (lpNm->uChanged & LVIF_STATE)
+ {
+ UINT uiSelectedItems = 0;
+
+ uiSelectedItems = ListView_GetSelectedCount(hwndLV);
+
+ EnableWindow( GetDlgItem(hwndDlg, IDD_DETACH),
+ uiSelectedItems? TRUE : FALSE);
+
+ return TRUE;
+ }
+ break;
+
+ case LVN_COLUMNCLICK:
+ ListView_SortItems( hwndLV,
+ GlobalWhoAmI_ListViewCompareProc,
+ (LPARAM)(lpNm->iSubItem));
+ return TRUE; /* we processed a message */
+
+ case LVN_DELETEITEM:
+ // Free memory
+ LocalFree( (HLOCAL) lpNm->lParam );
+ lpNm->lParam = NULL;
+ break;
+
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+
+BOOL
+DetachResourceProc(
+ HWND hwndDlg
+)
+{
+ BOOL fDetached = FALSE;
+ LV_ITEM lvitem;
+ int index;
+ DWORD err;
+
+ HWND hwndLV = ::GetDlgItem( hwndDlg, IDD_GLOBAL_SERVERLIST);
+
+ index = -1; // Start at beginning of item list.
+
+ while ((index = ListView_GetNextItem(hwndLV, index, LVNI_SELECTED)) != -1)
+ {
+ lvitem.iItem = index;
+ lvitem.mask = LVIF_PARAM;
+ lvitem.iSubItem = 0;
+
+ if ( ListView_GetItem( hwndLV, &lvitem ))
+ {
+ PCONN_STATUS pConnStatus = (PCONN_STATUS) lvitem.lParam;
+ BOOL fDisconnected = FALSE; // Can be used to refresh
+ // the shell if needed
+
+ err = LogoutFromServer( pConnStatus->pszServerName,
+ &fDisconnected );
+
+ if ( err == NO_ERROR )
+ {
+ fDetached = TRUE;
+ }
+ else
+ {
+ NwMakePrettyDisplayName(pConnStatus->pszServerName);
+ ::MsgBoxPrintf( hwndDlg,
+ IDS_MESSAGE_LOGOUT_FROM_SERVER_FAILED,
+ IDS_TITLE_LOGOUT,
+ MB_OK | MB_SETFOREGROUND | MB_ICONINFORMATION,
+ pConnStatus->pszServerName );
+ }
+ }
+ }
+
+ return fDetached;
+}
+
+static DWORD aWhoAmIIds[] = { IDC_LOGOFRAME, NO_HELP,
+ IDD_GLOBAL_SERVERLIST_T,IDH_GLOBAL_SERVERLIST,
+ IDD_GLOBAL_SERVERLIST, IDH_GLOBAL_SERVERLIST,
+ IDD_DETACH, IDH_GLOBAL_DETACH,
+ IDD_GLOBAL_SVRLIST_DESC,IDH_GLOBAL_SERVERLIST,
+ 0, 0 };
+
+/*
+ * GlobalWhoAmIDlgProc
+ * -------------------
+ *
+ * WhoAmI information for list of attached servers
+ */
+BOOL
+CALLBACK
+GlobalWhoAmIDlgProc(
+ HWND hwndDlg,
+ UINT msg,
+ WPARAM wParam,
+ LPARAM lParam
+)
+{
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ {
+ LPWSTR pszCurrentContext = NULL;
+ DWORD dwPrintOptions;
+
+ // Get the current default tree or server name
+ DWORD err = ::NwQueryInfo( &dwPrintOptions, &pszCurrentContext );
+ if ( err == NO_ERROR )
+ {
+ LPWSTR pszName;
+ WCHAR szUserName[MAX_PATH+1] = L"";
+ WCHAR szNoName[2] = L"";
+ DWORD ResumeKey = 0;
+ LPBYTE pBuffer = NULL;
+ DWORD EntriesRead = 0;
+
+ DWORD dwMessageId;
+
+ UNICODE_STRING uContext;
+ WCHAR szContext[MAX_PATH+1];
+
+ szContext[0] = 0;
+ uContext.Buffer = szContext;
+ uContext.Length = uContext.MaximumLength
+ = sizeof(szContext)/sizeof(szContext[0]);
+
+ if ( pszCurrentContext )
+ {
+ pszName = pszCurrentContext;
+ }
+ else
+ {
+ pszName = szNoName;
+ }
+
+ if ( pszName[0] == TREECHAR )
+ {
+ // Get the tree name from the full name *TREE\CONTEXT
+
+ LPWSTR pszTemp;
+ if ( pszTemp = wcschr( pszName, L'\\' ))
+ *pszTemp = 0;
+
+ dwMessageId = IDS_MESSAGE_NOT_LOGGED_IN_TREE;
+ }
+ else
+ {
+ dwMessageId = IDS_MESSAGE_NOT_LOGGED_IN_SERVER;
+ }
+
+ if ( pszName[0] != 0 ) // there is preferred server/tree
+ {
+ err = NwGetConnectionStatus( pszName,
+ &ResumeKey,
+ &pBuffer,
+ &EntriesRead );
+ }
+
+ if ( err == NO_ERROR && EntriesRead > 0 )
+ // For trees, we'll get more than one entry
+ {
+ PCONN_STATUS pConnStatus = (PCONN_STATUS) pBuffer;
+
+ if ( EntriesRead > 1 && pszName[0] == TREECHAR )
+ {
+ // If there is more than one entry for trees,
+ // then we need to find one entry where username is not null.
+ // If we cannot find one, then just use the first one.
+
+ DWORD i;
+ PCONN_STATUS pConnStatusTmp = pConnStatus;
+ PCONN_STATUS pConnStatusUser = NULL;
+ PCONN_STATUS pConnStatusNoUser = NULL;
+
+ for ( i = 0; i < EntriesRead ; i++ )
+ {
+ if ( pConnStatusTmp->fNds )
+ {
+ pConnStatusNoUser = pConnStatusTmp;
+
+ if ( ( pConnStatusTmp->pszUserName != NULL )
+ && ( ( pConnStatusTmp->dwConnType
+ == NW_CONN_NDS_AUTHENTICATED_NO_LICENSE )
+ || ( pConnStatusTmp->dwConnType
+ == NW_CONN_NDS_AUTHENTICATED_LICENSED )
+ )
+ )
+ {
+ // Found it
+ pConnStatusUser = pConnStatusTmp;
+ break;
+ }
+ }
+
+ // Continue with the next item
+ pConnStatusTmp = (PCONN_STATUS)
+ ( (DWORD) pConnStatusTmp
+ + pConnStatusTmp->dwTotalLength);
+ }
+
+ if ( pConnStatusUser )
+ {
+ // found one nds entry with a user name
+ pConnStatus = pConnStatusUser;
+ }
+ else if ( pConnStatusNoUser )
+ {
+ // use an nds entry with no user name
+ pConnStatus = pConnStatusNoUser;
+ }
+ // else use the first entry
+ }
+
+ if ( ( pConnStatus->pszUserName )
+ && ( pConnStatus->pszUserName[0] != 0 )
+ )
+ {
+ NwAbbreviateUserName( pConnStatus->pszUserName,
+ szUserName);
+
+ NwMakePrettyDisplayName( szUserName );
+
+ if ( pszName[0] != TREECHAR )
+ {
+ dwMessageId = IDS_MESSAGE_LOGGED_IN_SERVER;
+ }
+ else
+ {
+ dwMessageId = IDS_MESSAGE_LOGGED_IN_TREE;
+ }
+ }
+
+ if ( pszName[0] == TREECHAR )
+ {
+ // For trees, we need to get the current context
+
+ // Open a handle to the redirector
+ HANDLE handleRdr = NULL;
+ err = RtlNtStatusToDosError(
+ ::NwNdsOpenRdrHandle( &handleRdr ));
+
+ if ( err == NO_ERROR )
+ {
+ UNICODE_STRING uTree;
+ RtlInitUnicodeString( &uTree, pszName+1 ); // get past '*'
+
+ // Get the current context in the default tree
+ err = RtlNtStatusToDosError(
+ ::NwNdsGetTreeContext( handleRdr,
+ &uTree,
+ &uContext));
+ }
+
+ if ( handleRdr != NULL )
+ ::NtClose( handleRdr );
+ }
+ }
+
+ if ( !err )
+ {
+ LPWSTR pszText = NULL;
+ err = ::LoadMsgPrintf( &pszText,
+ dwMessageId,
+ pszName[0] == TREECHAR?
+ pszName + 1 : pszName,
+ szUserName,
+ szContext );
+
+ if ( err == NO_ERROR )
+ {
+ ::SetDlgItemText( hwndDlg, IDD_GLOBAL_SERVERLIST_T,
+ pszText);
+ ::LocalFree( pszText );
+ }
+ }
+
+ if ( pBuffer != NULL )
+ {
+ LocalFree( pBuffer );
+ pBuffer = NULL;
+ }
+ }
+
+ if ( pszCurrentContext != NULL )
+ {
+ LocalFree( pszCurrentContext );
+ pszCurrentContext = NULL;
+ }
+
+ if ( err != NO_ERROR )
+ {
+ ::MsgBoxErrorPrintf( hwndDlg,
+ IDS_MESSAGE_CONNINFO_ERROR,
+ IDS_NETWARE_TITLE,
+ MB_OK | MB_SETFOREGROUND | MB_ICONSTOP,
+ err,
+ NULL );
+ ::EndDialog( hwndDlg, FALSE);
+ return TRUE;
+ }
+
+ // Fill listview control with connection parameters
+ CreateConnectionsListView(hwndDlg);
+
+ UnHideControl( hwndDlg, IDD_DETACH);
+
+ // List view fill defaults to no selected server, disable Detach.
+ EnableWindow( GetDlgItem( hwndDlg, IDD_DETACH), FALSE);
+
+ // Set up timer for automatic refresh interval
+ ::SetTimer( hwndDlg, 1, GLOBAL_WHOAMI_REFRESH_INTERVAL, NULL);
+
+ // Set focus to list box
+ ::SetFocus( ::GetDlgItem( hwndDlg, IDD_GLOBAL_SERVERLIST));
+
+ return FALSE; /* we set the focus */
+ }
+
+ case WM_DESTROY:
+ ::KillTimer( hwndDlg, 1);
+ break;
+
+ case WM_COMMAND:
+
+ switch (wParam)
+ {
+ case IDOK:
+ case IDCANCEL:
+ ::EndDialog( hwndDlg, FALSE);
+ return TRUE; /* we processed a message */
+
+ case IDD_DETACH:
+ // Attempt to detach server connection currently selected
+ if ( DetachResourceProc( hwndDlg ))
+ {
+ // If succeeded - refresh window
+ ::SendMessage(hwndDlg,WM_COMMAND,IDD_REFRESH,0L);
+ }
+
+ return TRUE; /* we processed a message */
+
+ case IDD_REFRESH:
+ {
+ // Refresh connection listbox
+
+ HWND hwndLV = ::GetDlgItem( hwndDlg, IDD_GLOBAL_SERVERLIST);
+
+ ::SetFocus( hwndLV );
+
+ // Clear list
+ ListView_DeleteAllItems( hwndLV );
+
+ // Now refill list view window
+ FillConnectionsListView( hwndDlg );
+
+ // List view refill unsets selected server focus, disable Detach.
+ EnableWindow( GetDlgItem( hwndDlg, IDD_DETACH ), FALSE );
+
+ return TRUE; /* we processed a message */
+ }
+
+ default:
+ break;
+ }
+ break;
+
+ case WM_NOTIFY:
+ // Handle notifications
+ if ( NotifyHandler( hwndDlg, msg, wParam, lParam))
+ return TRUE; /* we processed a message */
+ break;
+
+ case WM_TIMER:
+ ::SendMessage( hwndDlg, WM_COMMAND, IDD_REFRESH, 0L);
+ break;
+
+ case WM_HELP:
+ ::WinHelp( (HWND) ((LPHELPINFO)lParam)->hItemHandle,
+ NW_HELP_FILE,
+ HELP_WM_HELP,
+ (DWORD) (LPVOID) aWhoAmIIds );
+ return TRUE;
+
+ case WM_CONTEXTMENU:
+ ::WinHelp( (HWND) wParam,
+ NW_HELP_FILE,
+ HELP_CONTEXTMENU,
+ (DWORD) (LPVOID) aWhoAmIIds );
+ return TRUE;
+
+ }
+
+ return FALSE; /* we didn't process the message */
+
+}
diff --git a/private/nw/svcdlls/nwwks/client/nwspl.c b/private/nw/svcdlls/nwwks/client/nwspl.c
new file mode 100644
index 000000000..e92ad0f5e
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/nwspl.c
@@ -0,0 +1,2924 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ nwspl.c
+
+Abstract:
+
+ This module contains the Netware print provider.
+
+Author:
+
+ Yi-Hsin Sung (yihsins) 15-Apr-1993
+
+Revision History:
+ Yi-Hsin Sung (yihsins) 15-May-1993
+ Moved most of the functionality to the server side
+
+ Ram Viswanathan (ramv) 09-Aug-1995
+ Added functionality to Add and Delete Printer.
+
+
+--*/
+
+#include <stdio.h>
+
+#include <nwclient.h>
+#include <winspool.h>
+#include <winsplp.h>
+#include <ntlsa.h>
+
+#include <nwpkstr.h>
+#include <splutil.h>
+#include <nwreg.h>
+#include <nwspl.h>
+#include <nwmisc.h>
+
+//------------------------------------------------------------------
+//
+// Local Functions
+//
+//------------------------------------------------------------------
+
+DWORD
+InitializePortNames(
+ VOID
+);
+
+VOID
+NwpGetUserInfo(
+ LPWSTR *ppszUser,
+ BOOL *pfGateway
+);
+
+DWORD
+NwpGetThreadUserInfo(
+ LPWSTR *ppszUser,
+ LPWSTR *ppszUserSid
+);
+
+DWORD
+NwpGetUserNameFromSid(
+ PSID pUserSid,
+ LPWSTR *ppszUserName
+);
+
+DWORD
+NwpGetLogonUserInfo(
+ LPWSTR *ppszUserSid
+);
+
+VOID
+pFreeAllContexts();
+
+//------------------------------------------------------------------
+//
+// Global Variables
+//
+//------------------------------------------------------------------
+
+HMODULE hmodNW = NULL;
+BOOL fIsWinnt = FALSE ;
+WCHAR *pszRegistryPath = NULL;
+WCHAR *pszRegistryPortNames=L"PortNames";
+WCHAR szMachineName[MAX_COMPUTERNAME_LENGTH + 3];
+PNWPORT pNwFirstPort = NULL;
+CRITICAL_SECTION NwSplSem;
+CRITICAL_SECTION NwServiceListCriticalSection; // Used to protect linked
+ // list of registered services
+STATIC HANDLE handleDummy; // This is a dummy handle used to
+ // return to the clients if we have previously
+ // opened the given printer successfully
+ // and the netware workstation service is not
+ // currently available.
+
+STATIC
+PRINTPROVIDOR PrintProvidor = { OpenPrinter,
+ SetJob,
+ GetJob,
+ EnumJobs,
+ AddPrinter, // NOT SUPPORTED
+ DeletePrinter, // NOT SUPPORTED
+ SetPrinter,
+ GetPrinter,
+ EnumPrinters,
+ AddPrinterDriver, // NOT SUPPORTED
+ EnumPrinterDrivers, // NOT SUPPORTED
+ GetPrinterDriverW, // NOT SUPPORTED
+ GetPrinterDriverDirectory, // NOT SUPPORTED
+ DeletePrinterDriver, // NOT SUPPORTED
+ AddPrintProcessor, // NOT SUPPORTED
+ EnumPrintProcessors, // NOT SUPPORTED
+ GetPrintProcessorDirectory, // NOT SUPPORTED
+ DeletePrintProcessor, // NOT SUPPORTED
+ EnumPrintProcessorDatatypes,// NOT SUPPORTED
+ StartDocPrinter,
+ StartPagePrinter, // NOT SUPPORTED
+ WritePrinter,
+ EndPagePrinter, // NOT SUPPORTED
+ AbortPrinter,
+ ReadPrinter, // NOT SUPPORTED
+ EndDocPrinter,
+ AddJob,
+ ScheduleJob,
+ GetPrinterData, // NOT SUPPORTED
+ SetPrinterData, // NOT SUPPORTED
+ WaitForPrinterChange,
+ ClosePrinter,
+ AddForm, // NOT SUPPORTED
+ DeleteForm, // NOT SUPPORTED
+ GetForm, // NOT SUPPORTED
+ SetForm, // NOT SUPPORTED
+ EnumForms, // NOT SUPPORTED
+ EnumMonitors, // NOT SUPPORTED
+ EnumPorts,
+ AddPort, // NOT SUPPORTED
+ ConfigurePort,
+ DeletePort,
+ CreatePrinterIC, // NOT SUPPORTED
+ PlayGdiScriptOnPrinterIC, // NOT SUPPORTED
+ DeletePrinterIC, // NOT SUPPORTED
+ AddPrinterConnection, // NOT SUPPORTED
+ DeletePrinterConnection, // NOT SUPPORTED
+ PrinterMessageBox, // NOT SUPPORTED
+ AddMonitor, // NOT SUPPORTED
+ DeleteMonitor // NOT SUPPORTED
+};
+
+
+//------------------------------------------------------------------
+//
+// Initialization Functions
+//
+//------------------------------------------------------------------
+
+
+BOOL InitializeDll(
+ HINSTANCE hdll,
+ DWORD dwReason,
+ LPVOID lpReserved
+)
+{
+ NT_PRODUCT_TYPE ProductType ;
+
+ UNREFERENCED_PARAMETER( lpReserved );
+
+ if ( dwReason == DLL_PROCESS_ATTACH )
+ {
+ DisableThreadLibraryCalls( hdll );
+
+ hmodNW = hdll;
+
+ //
+ // are we a winnt machine?
+ //
+ fIsWinnt = RtlGetNtProductType(&ProductType) ?
+ (ProductType == NtProductWinNt) :
+ FALSE ;
+
+ //
+ // Initialize the critical section for maintaining the registered
+ // service list
+ //
+ InitializeCriticalSection( &NwServiceListCriticalSection );
+ }
+ else if ( dwReason == DLL_PROCESS_DETACH )
+ {
+ //
+ // Free up memories used by the port link list
+ //
+ DeleteAllPortEntries();
+
+ //
+ // Get rid of Service List and Shutdown SAP library
+ //
+ NwTerminateServiceProvider();
+
+#ifndef NT1057
+ //
+ // Clean up shell extensions
+ //
+ NwCleanupShellExtensions();
+#endif
+ pFreeAllContexts(); // clean up RNR stuff
+ DeleteCriticalSection( &NwServiceListCriticalSection );
+ }
+
+ return TRUE;
+}
+
+
+
+DWORD
+InitializePortNames(
+ VOID
+)
+/*++
+
+Routine Description:
+
+ This is called by the InitializePrintProvidor to initialize the ports
+ names used in this providor.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Returns NO_ERROR or the error that occurred.
+
+--*/
+{
+ DWORD err;
+ HKEY hkeyPath;
+ HKEY hkeyPortNames;
+
+ err = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
+ pszRegistryPath,
+ 0,
+ KEY_READ,
+ &hkeyPath );
+
+ if ( !err )
+ {
+ err = RegOpenKeyEx( hkeyPath,
+ pszRegistryPortNames,
+ 0,
+ KEY_READ,
+ &hkeyPortNames );
+
+ if ( !err )
+ {
+ DWORD i = 0;
+ WCHAR Buffer[MAX_PATH];
+ DWORD BufferSize;
+
+ while ( !err )
+ {
+ BufferSize = sizeof Buffer;
+
+ err = RegEnumValue( hkeyPortNames,
+ i,
+ Buffer,
+ &BufferSize,
+ NULL,
+ NULL,
+ NULL,
+ NULL );
+
+ if ( !err )
+ CreatePortEntry( Buffer );
+
+ i++;
+ }
+
+ /* We expect RegEnumKeyEx to return ERROR_NO_MORE_ITEMS
+ * when it gets to the end of the keys, so reset the status:
+ */
+ if( err == ERROR_NO_MORE_ITEMS )
+ err = NO_ERROR;
+
+ RegCloseKey( hkeyPortNames );
+ }
+#if DBG
+ else
+ {
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [RegOpenKeyEx] (%ws) failed: Error = %d\n",
+ pszRegistryPortNames, err ));
+ }
+#endif
+
+ RegCloseKey( hkeyPath );
+ }
+#if DBG
+ else
+ {
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [RegOpenKeyEx] (%ws) failed: Error = %d\n",
+ pszRegistryPath, err ));
+ }
+#endif
+
+ return err;
+}
+
+//------------------------------------------------------------------
+//
+// Print Provider Functions supported by NetWare provider
+//
+//------------------------------------------------------------------
+
+
+BOOL
+InitializePrintProvidor(
+ LPPRINTPROVIDOR pPrintProvidor,
+ DWORD cbPrintProvidor,
+ LPWSTR pszFullRegistryPath
+)
+/*++
+
+Routine Description:
+
+ This is called by the spooler subsystem to initialize the print
+ providor.
+
+Arguments:
+
+ pPrintProvidor - Pointer to the print providor structure to be
+ filled in by this function
+ cbPrintProvidor - Count of bytes of the print providor structure
+ pszFullRegistryPath - Full path to the registry key of this print providor
+
+Return Value:
+
+ Always TRUE.
+
+--*/
+{
+ DWORD dwLen;
+
+ if ( !pPrintProvidor || !pszFullRegistryPath || !*pszFullRegistryPath )
+ {
+ SetLastError( ERROR_INVALID_PARAMETER );
+ return FALSE;
+ }
+
+ memcpy( pPrintProvidor,
+ &PrintProvidor,
+ min( sizeof(PRINTPROVIDOR), cbPrintProvidor) );
+
+ //
+ // Store the registry path for this print providor
+ //
+ if ( !(pszRegistryPath = AllocNwSplStr(pszFullRegistryPath)) )
+ return FALSE;
+
+ //
+ // Store the local machine name
+ //
+ szMachineName[0] = szMachineName[1] = L'\\';
+ dwLen = MAX_COMPUTERNAME_LENGTH;
+ GetComputerName( szMachineName + 2, &dwLen );
+
+#if DBG
+ IF_DEBUG(PRINT)
+ {
+ KdPrint(("NWSPL [InitializePrintProvidor] "));
+ KdPrint(("RegistryPath = %ws, ComputerName = %ws\n",
+ pszRegistryPath, szMachineName ));
+ }
+#endif
+
+ InitializeCriticalSection( &NwSplSem );
+
+ //
+ // Ignore the error returned from InitializePortNames.
+ // The provider can still function if we cannot get all the port
+ // names.
+ //
+ InitializePortNames();
+
+ return TRUE;
+}
+
+
+
+BOOL
+OpenPrinterW(
+ LPWSTR pszPrinterName,
+ LPHANDLE phPrinter,
+ LPPRINTER_DEFAULTS pDefault
+)
+/*++
+
+Routine Description:
+
+ This routine retrieves a handle identifying the specified printer.
+
+Arguments:
+
+ pszPrinterName - Name of the printer
+ phPrinter - Receives the handle that identifies the given printer
+ pDefault - Points to a PRINTER_DEFAULTS structure. Can be NULL.
+
+Return Value:
+
+ TRUE if the function succeeds, FALSE otherwise. Use GetLastError() for
+ extended error information.
+
+--*/
+{
+ DWORD err;
+
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(( "NWSPL [OpenPrinter] Name = %ws\n", pszPrinterName ));
+#endif
+
+ UNREFERENCED_PARAMETER( pDefault );
+
+ if ( !pszPrinterName )
+ {
+ SetLastError( ERROR_INVALID_NAME );
+ return FALSE;
+ }
+
+ RpcTryExcept
+ {
+ err = NwrOpenPrinter( NULL,
+ pszPrinterName,
+ PortKnown( pszPrinterName ),
+ (LPNWWKSTA_PRINTER_CONTEXT) phPrinter );
+
+ //
+ // Make sure there is a port of this name so that
+ // EnumPorts will return it.
+ //
+
+ if ( !err )
+ {
+
+ if ( !PortExists( pszPrinterName, &err ) && !err )
+ {
+ //
+ // We will ignore the errors since it is
+ // still OK if we can't add the port.
+ // BUG BUG :: Cannot delete once created,dont create
+ // We should not create port entry and registry entry
+
+ if ( CreatePortEntry( pszPrinterName ) )
+ CreateRegistryEntry( pszPrinterName );
+
+ }
+
+ }
+
+ }
+ RpcExcept(1)
+ {
+ DWORD code = RpcExceptionCode();
+
+ if ( code == RPC_S_SERVER_UNAVAILABLE )
+ {
+ if ( PortKnown( pszPrinterName ))
+ {
+ *phPrinter = &handleDummy;
+ err = NO_ERROR;
+ }
+ else
+ {
+ err = ERROR_INVALID_NAME;
+ }
+ }
+ else
+ {
+ err = NwpMapRpcError( code );
+ }
+ }
+ RpcEndExcept
+
+ if ( err )
+ {
+ SetLastError( err );
+
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [OpenPrinter] err = %d\n", err));
+#endif
+ }
+
+ return ( err == NO_ERROR );
+
+}
+
+
+
+BOOL
+ClosePrinter(
+ HANDLE hPrinter
+)
+/*++
+
+Routine Description:
+
+ This routine closes the given printer object.
+
+Arguments:
+
+ hPrinter - Handle of the printer object
+
+Return Value:
+
+ TRUE if the function succeeds, FALSE otherwise. Use GetLastError() for
+ extended error information.
+
+--*/
+{
+ DWORD err;
+
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(( "NWSPL [ClosePrinter]\n"));
+#endif
+
+ //
+ // Just return success if the handle is a dummy one
+ //
+ if ( hPrinter == &handleDummy )
+ return TRUE;
+
+ RpcTryExcept
+ {
+ err = NwrClosePrinter( (LPNWWKSTA_PRINTER_CONTEXT) &hPrinter );
+ }
+ RpcExcept(1)
+ {
+ DWORD code = RpcExceptionCode();
+
+ if ( code == RPC_S_SERVER_UNAVAILABLE )
+ err = ERROR_INVALID_HANDLE;
+ else
+ err = NwpMapRpcError( code );
+ }
+ RpcEndExcept
+
+ if ( err )
+ SetLastError( err );
+
+ return err == NO_ERROR;
+
+}
+
+
+
+BOOL
+GetPrinter(
+ HANDLE hPrinter,
+ DWORD dwLevel,
+ LPBYTE pbPrinter,
+ DWORD cbBuf,
+ LPDWORD pcbNeeded
+)
+/*++
+
+Routine Description:
+
+ The routine retrieves information about the given printer.
+
+Arguments:
+
+ hPrinter - Handle of the printer
+ dwLevel - Specifies the level of the structure to which pbPrinter points.
+ pbPrinter - Points to a buffer that receives the PRINTER_INFO object.
+ cbBuf - Size, in bytes of the array pbPrinter points to.
+ pcbNeeded - Points to a value which specifies the number of bytes copied
+ if the function succeeds or the number of bytes required if
+ cbBuf was too small.
+
+Return Value:
+
+ TRUE if the function succeeds and FALSE otherwise. GetLastError() can be
+ used to retrieve extended error information.
+
+--*/
+{
+ DWORD err;
+
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(( "NWSPL [GetPrinter] Level = %d\n", dwLevel ));
+#endif
+
+ if ( hPrinter == &handleDummy )
+ {
+ SetLastError( ERROR_NO_NETWORK );
+ return FALSE;
+ }
+ else if ( ( dwLevel != 1 ) && ( dwLevel != 2 ) && (dwLevel != 3 ))
+ {
+ SetLastError( ERROR_INVALID_LEVEL );
+ return FALSE;
+ }
+
+ RpcTryExcept
+ {
+ err = NwrGetPrinter( (NWWKSTA_PRINTER_CONTEXT) hPrinter,
+ dwLevel,
+ pbPrinter,
+ cbBuf,
+ pcbNeeded );
+
+ if ( !err )
+ {
+ if ( dwLevel == 1 )
+ MarshallUpStructure( pbPrinter, PrinterInfo1Offsets, pbPrinter);
+ else
+ MarshallUpStructure( pbPrinter, PrinterInfo2Offsets, pbPrinter);
+ }
+
+ }
+ RpcExcept(1)
+ {
+ DWORD code = RpcExceptionCode();
+
+ if ( code == RPC_S_SERVER_UNAVAILABLE )
+ err = ERROR_INVALID_HANDLE;
+ else
+ err = NwpMapRpcError( code );
+ }
+ RpcEndExcept
+
+ if ( err )
+ SetLastError( err );
+
+ return err == NO_ERROR;
+}
+
+
+
+BOOL
+SetPrinter(
+ HANDLE hPrinter,
+ DWORD dwLevel,
+ LPBYTE pbPrinter,
+ DWORD dwCommand
+)
+/*++
+
+Routine Description:
+
+ The routine sets the specified by pausing printing, resuming printing, or
+ clearing all print jobs.
+
+Arguments:
+
+ hPrinter - Handle of the printer
+ dwLevel - Specifies the level of the structure to which pbPrinter points.
+ pbPrinter - Points to a buffer that supplies the PRINTER_INFO object.
+ dwCommand - Specifies the new printer state.
+
+Return Value:
+
+ TRUE if the function succeeds and FALSE otherwise. GetLastError() can be
+ used to retrieve extended error information.
+
+--*/
+{
+ DWORD err = NO_ERROR;
+
+ UNREFERENCED_PARAMETER( pbPrinter );
+
+#if DBG
+ IF_DEBUG(PRINT)
+ {
+ KdPrint(( "NWSPL [SetPrinter] Level = %d Command = %d\n",
+ dwLevel, dwCommand ));
+ }
+#endif
+
+ if ( hPrinter == &handleDummy )
+ {
+ SetLastError( ERROR_NO_NETWORK );
+ return FALSE;
+ }
+
+ switch ( dwLevel )
+ {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ break;
+
+ default:
+ SetLastError( ERROR_INVALID_LEVEL );
+ return FALSE;
+ }
+
+ RpcTryExcept
+ {
+ err = NwrSetPrinter( (NWWKSTA_PRINTER_CONTEXT) hPrinter,
+ dwCommand );
+
+ }
+ RpcExcept(1)
+ {
+ DWORD code = RpcExceptionCode();
+
+ if ( code == RPC_S_SERVER_UNAVAILABLE )
+ err = ERROR_INVALID_HANDLE;
+ else
+ err = NwpMapRpcError( code );
+ }
+ RpcEndExcept
+
+ if ( err )
+ SetLastError( err );
+
+ return err == NO_ERROR;
+}
+
+
+
+BOOL
+EnumPrintersW(
+ DWORD dwFlags,
+ LPWSTR pszName,
+ DWORD dwLevel,
+ LPBYTE pbPrinter,
+ DWORD cbBuf,
+ LPDWORD pcbNeeded,
+ LPDWORD pcReturned
+)
+/*++
+
+Routine Description:
+
+ This routine enumerates the available providers, servers, printers
+ depending on the given pszName.
+
+Arguments:
+
+ dwFlags - Printer type requested
+ pszName - The name of the container object
+ dwLevel - The structure level requested
+ pbPrinter - Points to the array to receive the PRINTER_INFO objects
+ cbBuf - Size, in bytes of pbPrinter
+ pcbNeeded - Count of bytes needed
+ pcReturned - Count of PRINTER_INFO objects
+
+Return Value:
+
+ TRUE if the function succeeds, FALSE otherwise.
+
+--*/
+{
+ DWORD err = NO_ERROR;
+
+#if DBG
+ IF_DEBUG(PRINT)
+ {
+ KdPrint(("NWSPL [EnumPrinters] Flags = %d Level = %d",dwFlags,dwLevel));
+ if ( pszName )
+ KdPrint((" PrinterName = %ws\n", pszName ));
+ else
+ KdPrint(("\n"));
+ }
+#endif
+
+ if ( (dwLevel != 1) && (dwLevel != 2) )
+ {
+ SetLastError( ERROR_INVALID_NAME ); // should be level, but winspool
+ // is silly.
+ return FALSE;
+ }
+ else if ( !pcbNeeded || !pcReturned )
+ {
+ SetLastError( ERROR_INVALID_PARAMETER );
+ return FALSE;
+ }
+
+ RpcTryExcept
+ {
+ *pcReturned = 0;
+ *pcbNeeded = 0;
+
+ if ( ( dwFlags & PRINTER_ENUM_NAME )
+ && ( dwLevel == 1 )
+ )
+ {
+ err = NwrEnumPrinters( NULL,
+ pszName,
+ pbPrinter,
+ cbBuf,
+ pcbNeeded,
+ pcReturned );
+
+ if ( !err )
+ {
+ DWORD i;
+ for ( i = 0; i < *pcReturned; i++ )
+ MarshallUpStructure( pbPrinter + i*sizeof(PRINTER_INFO_1W),
+ PrinterInfo1Offsets,
+ pbPrinter );
+ }
+ }
+ else
+ {
+ err = ERROR_INVALID_NAME;
+ }
+ }
+ RpcExcept(1)
+ {
+ DWORD code = RpcExceptionCode();
+
+ if ( code == RPC_S_SERVER_UNAVAILABLE )
+ err = ERROR_INVALID_NAME;
+ else
+ err = NwpMapRpcError( code );
+ }
+ RpcEndExcept
+
+ if ( err )
+ SetLastError( err );
+
+ return err == NO_ERROR;
+}
+
+
+
+DWORD
+StartDocPrinter(
+ HANDLE hPrinter,
+ DWORD dwLevel,
+ LPBYTE lpbDocInfo
+)
+/*++
+
+Routine Description:
+
+ This routine informs the print spooler that a document is to be spooled
+ for printing.
+
+Arguments:
+
+ hPrinter - Handle of the printer
+ dwLevel - Level of the structure pointed to by lpbDocInfo. Must be 1.
+ lpbDocInfo - Points to the DOC_INFO_1 object
+
+Return Value:
+
+ TRUE if the function succeeds, FALSE otherwise. The extended error
+ can be retrieved through GetLastError().
+
+--*/
+{
+ DWORD err;
+ DOC_INFO_1 *pDocInfo1 = (DOC_INFO_1 *) lpbDocInfo;
+ LPWSTR pszUser = NULL;
+ BOOL fGateway = FALSE;
+
+#if DBG
+ IF_DEBUG(PRINT)
+ {
+ KdPrint(( "NWSPL [StartDocPrinter] " ));
+ if ( pDocInfo1 )
+ {
+ if ( pDocInfo1->pDocName )
+ KdPrint(("Document %ws", pDocInfo1->pDocName ));
+ if ( pDocInfo1->pOutputFile )
+ KdPrint(("OutputFile %ws", pDocInfo1->pOutputFile ));
+ if ( pDocInfo1->pDatatype )
+ KdPrint(("Datatype %ws", pDocInfo1->pDatatype ));
+ }
+ KdPrint(("\n"));
+ }
+#endif
+
+ if ( hPrinter == &handleDummy )
+ {
+ SetLastError( ERROR_NO_NETWORK );
+ return FALSE;
+ }
+ else if ( dwLevel != 1 )
+ {
+ SetLastError( ERROR_INVALID_PARAMETER );
+ return FALSE;
+ }
+
+ // ignore the error, just use default value
+ NwpGetUserInfo( &pszUser, &fGateway );
+
+ RpcTryExcept
+ {
+ err = NwrStartDocPrinter( (NWWKSTA_PRINTER_CONTEXT) hPrinter,
+ pDocInfo1? pDocInfo1->pDocName : NULL,
+ pszUser,
+ fGateway );
+
+ }
+ RpcExcept(1)
+ {
+ DWORD code = RpcExceptionCode();
+
+ if ( code == RPC_S_SERVER_UNAVAILABLE )
+ err = ERROR_INVALID_HANDLE;
+ else
+ err = NwpMapRpcError( code );
+ }
+ RpcEndExcept
+
+ LocalFree( pszUser );
+
+ if ( err )
+ SetLastError( err );
+
+ return err == NO_ERROR;
+}
+
+
+
+BOOL
+WritePrinter(
+ HANDLE hPrinter,
+ LPVOID pBuf,
+ DWORD cbBuf,
+ LPDWORD pcbWritten
+)
+/*++
+
+Routine Description:
+
+ This routine informs the print spooler that the specified data should be
+ written to the given printer.
+
+Arguments:
+
+ hPrinter - Handle of the printer object
+ pBuf - Address of array that contains printer data
+ cbBuf - Size, in bytes of pBuf
+ pcbWritten - Receives the number of bytes actually written to the printer
+
+Return Value:
+
+ TRUE if it succeeds, FALSE otherwise. Use GetLastError() to get extended
+ error.
+
+--*/
+{
+ DWORD err;
+
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(( "NWSPL [WritePrinter]\n"));
+
+ if ( hPrinter == &handleDummy )
+ {
+ SetLastError( ERROR_NO_NETWORK );
+ return FALSE;
+ }
+#endif
+
+ RpcTryExcept
+ {
+ err = NwrWritePrinter( (NWWKSTA_PRINTER_CONTEXT) hPrinter,
+ pBuf,
+ cbBuf,
+ pcbWritten );
+ }
+ RpcExcept(1)
+ {
+ DWORD code = RpcExceptionCode();
+
+ if ( code == RPC_S_SERVER_UNAVAILABLE )
+ err = ERROR_INVALID_HANDLE;
+ else
+ err = NwpMapRpcError( code );
+ }
+ RpcEndExcept
+
+ if ( err )
+ SetLastError( err );
+
+ return err == NO_ERROR;
+}
+
+
+
+BOOL
+AbortPrinter(
+ HANDLE hPrinter
+)
+/*++
+
+Routine Description:
+
+ This routine deletes a printer's spool file if the printer is configured
+ for spooling.
+
+Arguments:
+
+ hPrinter - Handle of the printer object
+
+Return Value:
+
+ TRUE if the function succeeds, FALSE otherwise.
+
+--*/
+{
+ DWORD err;
+
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(( "NWSPL [AbortPrinter]\n"));
+
+ if ( hPrinter == &handleDummy )
+ {
+ SetLastError( ERROR_NO_NETWORK );
+ return FALSE;
+ }
+#endif
+
+ RpcTryExcept
+ {
+ err = NwrAbortPrinter( (NWWKSTA_PRINTER_CONTEXT) hPrinter );
+ }
+ RpcExcept(1)
+ {
+ DWORD code = RpcExceptionCode();
+
+ if ( code == RPC_S_SERVER_UNAVAILABLE )
+ err = ERROR_INVALID_HANDLE;
+ else
+ err = NwpMapRpcError( code );
+ }
+ RpcEndExcept
+
+ if ( err )
+ SetLastError( err );
+
+ return err == NO_ERROR;
+
+}
+
+
+
+BOOL
+EndDocPrinter(
+ HANDLE hPrinter
+)
+/*++
+
+Routine Description:
+
+ This routine ends the print job for the given printer.
+
+Arguments:
+
+ hPrinter - Handle of the printer object
+
+Return Value:
+
+ TRUE if the function succeeds, FALSE otherwise.
+
+--*/
+{
+ DWORD err;
+
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(( "NWSPL [EndDocPrinter]\n"));
+#endif
+
+ if ( hPrinter == &handleDummy )
+ {
+ SetLastError( ERROR_NO_NETWORK );
+ return FALSE;
+ }
+
+ RpcTryExcept
+ {
+ err = NwrEndDocPrinter( (NWWKSTA_PRINTER_CONTEXT) hPrinter );
+ }
+ RpcExcept(1)
+ {
+ DWORD code = RpcExceptionCode();
+
+ if ( code == RPC_S_SERVER_UNAVAILABLE )
+ err = ERROR_INVALID_HANDLE;
+ else
+ err = NwpMapRpcError( code );
+ }
+ RpcEndExcept
+
+ if ( err )
+ SetLastError( err );
+
+ return err == NO_ERROR;
+}
+
+
+
+BOOL
+GetJob(
+ HANDLE hPrinter,
+ DWORD dwJobId,
+ DWORD dwLevel,
+ LPBYTE pbJob,
+ DWORD cbBuf,
+ LPDWORD pcbNeeded
+)
+/*++
+
+Routine Description:
+
+ This routine retrieves print-job data for the given printer.
+
+Arguments:
+
+ hPrinter - Handle of the printer
+ dwJobId - Job identifition number
+ dwLevel - Data structure level of pbJob
+ pbJob - Address of data-structure array
+ cbBuf - Count of bytes in array
+ pcbNeeded - Count of bytes retrieved or required
+
+Return Value:
+
+ TRUE if the function succeeds, FALSE otherwise.
+
+--*/
+{
+ DWORD err;
+
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [GetJob] JobId = %d Level = %d\n", dwJobId, dwLevel));
+#endif
+
+ if ( hPrinter == &handleDummy )
+ {
+ SetLastError( ERROR_NO_NETWORK );
+ return FALSE;
+ }
+ else if (( dwLevel != 1 ) && ( dwLevel != 2 ))
+ {
+ SetLastError( ERROR_INVALID_LEVEL );
+ return FALSE;
+ }
+
+ RpcTryExcept
+ {
+ err = NwrGetJob( (NWWKSTA_PRINTER_CONTEXT) hPrinter,
+ dwJobId,
+ dwLevel,
+ pbJob,
+ cbBuf,
+ pcbNeeded );
+
+ if ( !err )
+ {
+ if ( dwLevel == 1 )
+ MarshallUpStructure( pbJob, JobInfo1Offsets, pbJob );
+ else
+ MarshallUpStructure( pbJob, JobInfo2Offsets, pbJob );
+ }
+ }
+ RpcExcept(1)
+ {
+ DWORD code = RpcExceptionCode();
+
+ if ( code == RPC_S_SERVER_UNAVAILABLE )
+ err = ERROR_INVALID_HANDLE;
+ else
+ err = NwpMapRpcError( code );
+ }
+ RpcEndExcept
+
+ if ( err )
+ SetLastError( err );
+
+ return err == NO_ERROR;
+
+}
+
+
+
+BOOL
+EnumJobs(
+ HANDLE hPrinter,
+ DWORD dwFirstJob,
+ DWORD dwNoJobs,
+ DWORD dwLevel,
+ LPBYTE pbJob,
+ DWORD cbBuf,
+ LPDWORD pcbNeeded,
+ LPDWORD pcReturned
+)
+/*++
+
+Routine Description:
+
+ This routine initializes the array of JOB_INFO_1 or JOB_INFO_2 structures
+ with data describing the specified print jobs for the given printer.
+
+Arguments:
+
+ hPrinter - Handle of the printer
+ dwFirstJob - Location of first job in the printer
+ dwNoJobs - Number of jobs to enumerate
+ dwLevel - Data structure level
+ pbJob - Address of structure array
+ cbBuf - Size of pbJob, in bytes
+ pcbNeeded - Receives the number of bytes copied or required
+ pcReturned - Receives the number of jobs copied
+
+Return Value:
+
+ TRUE if the function succeeds, FALSE otherwise.
+
+--*/
+{
+ DWORD err;
+
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [EnumJobs] Level = %d FirstJob = %d NoJobs = %d\n",
+ dwLevel, dwFirstJob, dwNoJobs));
+#endif
+
+ if ( hPrinter == &handleDummy )
+ {
+ SetLastError( ERROR_NO_NETWORK );
+ return FALSE;
+ }
+ else if ( ( dwLevel != 1 ) && ( dwLevel != 2 ) )
+ {
+ SetLastError( ERROR_INVALID_LEVEL );
+ return FALSE;
+ }
+
+ RpcTryExcept
+ {
+ *pcReturned = 0;
+ *pcbNeeded = 0;
+
+ err = NwrEnumJobs( (NWWKSTA_PRINTER_CONTEXT) hPrinter,
+ dwFirstJob,
+ dwNoJobs,
+ dwLevel,
+ pbJob,
+ cbBuf,
+ pcbNeeded,
+ pcReturned );
+
+ if ( !err )
+ {
+ DWORD i;
+ DWORD cbStruct;
+ DWORD *pOffsets;
+
+ if ( dwLevel == 1 )
+ {
+ cbStruct = sizeof( JOB_INFO_1W );
+ pOffsets = JobInfo1Offsets;
+ }
+ else // dwLevel == 2
+ {
+ cbStruct = sizeof( JOB_INFO_2W );
+ pOffsets = JobInfo2Offsets;
+ }
+
+ for ( i = 0; i < *pcReturned; i++ )
+ MarshallUpStructure( pbJob + i * cbStruct, pOffsets, pbJob );
+ }
+
+ }
+ RpcExcept(1)
+ {
+ DWORD code = RpcExceptionCode();
+
+ if ( code == RPC_S_SERVER_UNAVAILABLE )
+ err = ERROR_INVALID_HANDLE;
+ else
+ err = NwpMapRpcError( code );
+ }
+ RpcEndExcept
+
+ if ( err )
+ SetLastError( err );
+
+ return err == NO_ERROR;
+
+}
+
+
+
+BOOL
+SetJob(
+ HANDLE hPrinter,
+ DWORD dwJobId,
+ DWORD dwLevel,
+ LPBYTE pbJob,
+ DWORD dwCommand
+)
+/*++
+
+Routine Description:
+
+ This routine pauses, cancels, resumes, restarts the specified print job
+ in the given printer. The function can also be used to set print job
+ parameters such as job position, and so on.
+
+Arguments:
+
+ hPrinter - Handle of the printer
+ dwJobId - Job indentification number
+ dwLevel - Data structure level
+ pbJob - Address of data structure
+ dwCommand - Specify the operation to be performed
+
+Return Value:
+
+ TRUE if the function succeeds, FALSE otherwise.
+
+--*/
+{
+ DWORD err;
+
+#if DBG
+ IF_DEBUG(PRINT)
+ {
+ KdPrint(("NWSPL [SetJob] Level = %d JobId = %d Command = %d\n",
+ dwLevel, dwJobId, dwCommand));
+ }
+#endif
+
+ if ( hPrinter == &handleDummy )
+ {
+ SetLastError( ERROR_NO_NETWORK );
+ return FALSE;
+ }
+ else if ( ( dwLevel != 0 ) && ( dwLevel != 1 ) && ( dwLevel != 2 ) )
+ {
+ SetLastError( ERROR_INVALID_LEVEL );
+ return FALSE;
+ }
+ else if ( ( dwLevel == 0 ) && ( pbJob != NULL ) )
+ {
+ SetLastError( ERROR_INVALID_PARAMETER );
+ return FALSE;
+ }
+
+ RpcTryExcept
+ {
+ NW_JOB_INFO NwJobInfo;
+
+ if ( dwLevel == 1 )
+ {
+ NwJobInfo.nPosition = ((LPJOB_INFO_1W) pbJob)->Position;
+ NwJobInfo.pUserName = ((LPJOB_INFO_1W) pbJob)->pUserName;
+ NwJobInfo.pDocument = ((LPJOB_INFO_1W) pbJob)->pDocument;
+ }
+ else if ( dwLevel == 2 )
+ {
+ NwJobInfo.nPosition = ((LPJOB_INFO_2W) pbJob)->Position;
+ NwJobInfo.pUserName = ((LPJOB_INFO_2W) pbJob)->pUserName;
+ NwJobInfo.pDocument = ((LPJOB_INFO_2W) pbJob)->pDocument;
+ }
+
+ err = NwrSetJob( (NWWKSTA_PRINTER_CONTEXT) hPrinter,
+ dwJobId,
+ dwLevel,
+ dwLevel == 0 ? NULL : &NwJobInfo,
+ dwCommand );
+
+ }
+ RpcExcept(1)
+ {
+ DWORD code = RpcExceptionCode();
+
+ if ( code == RPC_S_SERVER_UNAVAILABLE )
+ err = ERROR_INVALID_HANDLE;
+ else
+ err = NwpMapRpcError( code );
+ }
+ RpcEndExcept
+
+ if ( err )
+ SetLastError( err );
+
+ return err == NO_ERROR;
+
+}
+
+
+
+BOOL
+AddJob(
+ HANDLE hPrinter,
+ DWORD dwLevel,
+ LPBYTE pbAddJob,
+ DWORD cbBuf,
+ LPDWORD pcbNeeded
+)
+/*++
+
+Routine Description:
+
+ This routine returns a full path and filename of a file that can be used
+ to store a print job.
+
+Arguments:
+
+ hPrinter - Handle of the printer
+ dwLevel - Data structure level
+ pbAddJob - Points to a ADD_INFO_1 structure
+ cbBuf - Size of pbAddJob, in bytes
+ pcbNeeded - Receives the bytes copied or required
+
+Return Value:
+
+ TRUE if the function succeeds, FALSE otherwise.
+
+--*/
+{
+ DWORD err;
+
+ ADDJOB_INFO_1W TempBuffer;
+ PADDJOB_INFO_1W OutputBuffer;
+ DWORD OutputBufferSize;
+
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(( "NWSPL [AddJob]\n"));
+#endif
+
+ if ( hPrinter == &handleDummy )
+ {
+ SetLastError( ERROR_NO_NETWORK );
+ return FALSE;
+ }
+ else if ( dwLevel != 1 )
+ {
+ SetLastError( ERROR_INVALID_PARAMETER );
+ return FALSE;
+ }
+
+ //
+ // The output buffer size must be at least the size of the fixed
+ // portion of the structure marshalled by RPC or RPC will not
+ // call the server-side to get the pcbNeeded. Use our own temporary
+ // buffer to force RPC to call the server-side if output buffer
+ // specified by the caller is too small.
+ //
+ if (cbBuf < sizeof(ADDJOB_INFO_1W)) {
+ OutputBuffer = &TempBuffer;
+ OutputBufferSize = sizeof(ADDJOB_INFO_1W);
+ }
+ else {
+ OutputBuffer = (LPADDJOB_INFO_1W) pbAddJob;
+ OutputBufferSize = cbBuf;
+ }
+
+ RpcTryExcept
+ {
+ err = NwrAddJob( (NWWKSTA_PRINTER_CONTEXT) hPrinter,
+ OutputBuffer,
+ OutputBufferSize,
+ pcbNeeded );
+
+ }
+ RpcExcept(1)
+ {
+ DWORD code = RpcExceptionCode();
+
+ if ( code == RPC_S_SERVER_UNAVAILABLE )
+ err = ERROR_INVALID_HANDLE;
+ else
+ err = NwpMapRpcError( code );
+ }
+ RpcEndExcept
+
+ if ( err )
+ SetLastError( err );
+
+ return err == NO_ERROR;
+}
+
+
+
+BOOL
+ScheduleJob(
+ HANDLE hPrinter,
+ DWORD dwJobId
+)
+/*++
+
+Routine Description:
+
+ This routine informs the print spooler that the specified job can be
+ scheduled for spooling.
+
+Arguments:
+
+ hPrinter - Handle of the printer
+ dwJobId - Job number that can be scheduled
+
+Return Value:
+
+ TRUE if the function succeeds, FALSE otherwise.
+
+--*/
+{
+ DWORD err;
+
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(( "NWSPL [ScheduleJob] JobId = %d\n", dwJobId ));
+#endif
+
+ if ( hPrinter == &handleDummy )
+ {
+ SetLastError( ERROR_NO_NETWORK );
+ return FALSE;
+ }
+
+ RpcTryExcept
+ {
+ err = NwrScheduleJob( (NWWKSTA_PRINTER_CONTEXT) hPrinter,
+ dwJobId );
+
+ }
+ RpcExcept(1)
+ {
+ DWORD code = RpcExceptionCode();
+
+ if ( code == RPC_S_SERVER_UNAVAILABLE )
+ err = ERROR_INVALID_HANDLE;
+ else
+ err = NwpMapRpcError( code );
+ }
+ RpcEndExcept
+
+ if ( err )
+ SetLastError( err );
+
+ return err == NO_ERROR;
+}
+
+
+
+DWORD
+WaitForPrinterChange(
+ HANDLE hPrinter,
+ DWORD dwFlags
+)
+/*++
+
+Routine Description:
+
+ This function returns when one or more requested changes occur on a
+ print server or if the function times out.
+
+Arguments:
+
+ hPrinter - Handle of the printer to wait on
+ dwFlags - A bitmask that specifies the changes that the application
+ wishes to be notified of.
+
+Return Value:
+
+ Return a bitmask that indicates the changes that occurred.
+
+--*/
+{
+ DWORD err;
+
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [WaitForPrinterChange] Flags = %d\n", dwFlags));
+#endif
+
+ if ( hPrinter == &handleDummy )
+ {
+ SetLastError( ERROR_NO_NETWORK );
+ return 0;
+ }
+
+ RpcTryExcept
+ {
+ err = NwrWaitForPrinterChange( (NWWKSTA_PRINTER_CONTEXT) hPrinter,
+ &dwFlags );
+
+ }
+ RpcExcept(1)
+ {
+ DWORD code = RpcExceptionCode();
+
+ if ( code == RPC_S_SERVER_UNAVAILABLE )
+ err = ERROR_INVALID_HANDLE;
+ else
+ err = NwpMapRpcError( code );
+ }
+ RpcEndExcept
+
+ if ( err )
+ {
+ SetLastError( err );
+ return 0;
+ }
+
+ return dwFlags;
+}
+
+
+
+BOOL
+EnumPortsW(
+ LPWSTR pszName,
+ DWORD dwLevel,
+ LPBYTE pbPort,
+ DWORD cbBuf,
+ LPDWORD pcbNeeded,
+ LPDWORD pcReturned
+)
+/*++
+
+Routine Description:
+
+ This function enumerates the ports available for printing on a
+ specified server.
+
+Arguments:
+
+ pszName - Name of the server to enumerate on
+ dwLevel - Structure level
+ pbPort - Address of array to receive the port information
+ cbBuf - Size, in bytes, of pbPort
+ pcbNeeded - Address to store the number of bytes needed or copied
+ pcReturned - Address to store the number of entries copied
+
+Return Value:
+
+ TRUE if the function succeeds, FALSE otherwise.
+
+--*/
+{
+ DWORD err = NO_ERROR;
+ DWORD cb = 0;
+ PNWPORT pNwPort;
+ LPPORT_INFO_1W pPortInfo1;
+ LPBYTE pEnd = pbPort + cbBuf;
+ LPBYTE pFixedDataEnd = pbPort;
+ BOOL FitInBuffer;
+
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [EnumPorts]\n"));
+#endif
+
+ if ( dwLevel != 1 )
+ {
+ SetLastError( ERROR_INVALID_NAME );
+ return FALSE;
+ }
+ else if ( !IsLocalMachine( pszName ) )
+ {
+ SetLastError( ERROR_INVALID_NAME );
+ return FALSE;
+ }
+
+ EnterCriticalSection( &NwSplSem );
+
+ pNwPort = pNwFirstPort;
+ while ( pNwPort )
+ {
+ cb += sizeof(PORT_INFO_1W) + ( wcslen( pNwPort->pName)+1)*sizeof(WCHAR);
+ pNwPort = pNwPort->pNext;
+ }
+
+ *pcbNeeded = cb;
+ *pcReturned = 0;
+
+ if ( cb <= cbBuf )
+ {
+ pEnd = pbPort + cbBuf;
+
+ pNwPort = pNwFirstPort;
+ while ( pNwPort )
+ {
+ pPortInfo1 = (LPPORT_INFO_1W) pFixedDataEnd;
+ pFixedDataEnd += sizeof( PORT_INFO_1W );
+
+ FitInBuffer = NwlibCopyStringToBuffer( pNwPort->pName,
+ wcslen( pNwPort->pName),
+ (LPCWSTR) pFixedDataEnd,
+ (LPWSTR *) &pEnd,
+ &pPortInfo1->pName );
+ ASSERT( FitInBuffer );
+
+ pNwPort = pNwPort->pNext;
+ (*pcReturned)++;
+ }
+ }
+ else
+ {
+ err = ERROR_INSUFFICIENT_BUFFER;
+ }
+
+ LeaveCriticalSection( &NwSplSem );
+
+ if ( err )
+ SetLastError( err );
+
+ return err == NO_ERROR;
+}
+
+
+
+BOOL
+DeletePortW(
+ LPWSTR pszName,
+ HWND hWnd,
+ LPWSTR pszPortName
+)
+/*++
+
+Routine Description:
+
+ This routine deletes the port given on the server. A dialog can
+ be displayed if needed.
+
+Arguments:
+
+ pszName - Name of the server for which the port should be deleted
+ hWnd - Parent window
+ pszPortName - The name of the port to delete
+
+Return Value:
+
+ TRUE if the function succeeds, FALSE otherwise.
+
+--*/
+{
+ DWORD err;
+ BOOL fPortDeleted;
+
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [DeletePort]\n"));
+#endif
+
+ if ( !IsLocalMachine( pszName ) )
+ {
+ SetLastError( ERROR_NOT_SUPPORTED );
+ return FALSE;
+ }
+
+ fPortDeleted = DeletePortEntry( pszPortName );
+
+ if ( fPortDeleted )
+ {
+ err = DeleteRegistryEntry( pszPortName );
+ }
+ else
+ {
+ err = ERROR_UNKNOWN_PORT;
+ }
+
+ if ( err )
+ SetLastError( err );
+
+ return err == NO_ERROR;
+
+}
+
+
+
+BOOL
+ConfigurePortW(
+ LPWSTR pszName,
+ HWND hWnd,
+ LPWSTR pszPortName
+)
+/*++
+
+Routine Description:
+
+ This routine displays the port configuration dialog box
+ for the given port on the given server.
+
+Arguments:
+
+ pszName - Name of the server on which the given port exist
+ hWnd - Parent window
+ pszPortName - The name of the port to be configured
+
+Return Value:
+
+ TRUE if the function succeeds, FALSE otherwise.
+
+--*/
+{
+ DWORD nCurrentThreadId;
+ DWORD nWindowThreadId;
+ WCHAR szCaption[MAX_PATH];
+ WCHAR szMessage[MAX_PATH];
+
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [ConfigurePort] PortName = %ws\n", pszPortName));
+#endif
+
+ if ( !IsLocalMachine( pszName ) )
+ {
+ SetLastError( ERROR_NOT_SUPPORTED );
+ return FALSE;
+ }
+ else if ( !PortKnown( pszPortName ) )
+ {
+ SetLastError( ERROR_UNKNOWN_PORT );
+ return FALSE;
+ }
+
+ nCurrentThreadId = GetCurrentThreadId();
+ nWindowThreadId = GetWindowThreadProcessId( hWnd, NULL );
+
+ if ( !AttachThreadInput( nCurrentThreadId, nWindowThreadId, TRUE ))
+ KdPrint(("[NWSPL] AttachThreadInput failed with %d.\n",GetLastError()));
+
+ if ( LoadStringW( hmodNW,
+ IDS_NETWARE_PRINT_CAPTION,
+ szCaption,
+ sizeof( szCaption ) / sizeof( WCHAR )))
+ {
+ if ( LoadStringW( hmodNW,
+ IDS_NOTHING_TO_CONFIGURE,
+ szMessage,
+ sizeof( szMessage ) / sizeof( WCHAR )))
+ {
+ MessageBox( hWnd, szMessage, szCaption,
+ MB_OK | MB_ICONINFORMATION );
+ }
+ else
+ {
+ KdPrint(("[NWSPL] LoadString failed with %d.\n",GetLastError()));
+ }
+ }
+ else
+ {
+ KdPrint(("[NWSPL] LoadString failed with %d.\n",GetLastError()));
+ }
+
+ if ( !AttachThreadInput( nCurrentThreadId, nWindowThreadId, FALSE ))
+ KdPrint(("[NWSPL] DetachThreadInput failed with %d.\n",GetLastError()));
+
+ return TRUE;
+}
+
+//------------------------------------------------------------------
+//
+// Print Provider Functions not supported by NetWare provider
+//
+//------------------------------------------------------------------
+
+BOOL
+AddPrinterConnectionW(
+ LPWSTR pszPrinterName
+)
+{
+#if DBG
+ IF_DEBUG(PRINT)
+ {
+ KdPrint(("NWSPL [AddPrinterConnection] PrinterName = %ws\n",
+ pszPrinterName));
+ }
+#endif
+
+ SetLastError( ERROR_INVALID_NAME );
+ return FALSE;
+}
+
+BOOL
+EnumMonitorsW(
+ LPWSTR pszName,
+ DWORD dwLevel,
+ LPBYTE pbMonitor,
+ DWORD cbBuf,
+ LPDWORD pcbNeeded,
+ LPDWORD pcReturned
+)
+{
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [EnumMonitors]\n"));
+#endif
+
+ SetLastError( ERROR_INVALID_NAME );
+ return FALSE;
+}
+
+
+BOOL
+AddPortW(
+ LPWSTR pszName,
+ HWND hWnd,
+ LPWSTR pszMonitorName
+)
+{
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [AddPort]\n"));
+#endif
+
+ SetLastError( ERROR_NOT_SUPPORTED );
+ return FALSE;
+}
+
+
+HANDLE
+AddPrinterW(
+ LPWSTR pszName,
+ DWORD dwLevel,
+ LPBYTE pbPrinter
+)
+
+// Creates a print queue on the netware server and returns a handle to it
+{
+#ifdef NOT_USED
+
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [AddPrinterW]\n"));
+#endif
+
+ SetLastError(ERROR_NOT_SUPPORTED);
+ return FALSE;
+#else
+
+ LPTSTR pszPrinterName = NULL;
+ LPTSTR pszPServer = NULL;
+ LPTSTR pszQueue = NULL;
+ HANDLE hPrinter = NULL;
+ DWORD err;
+ PPRINTER_INFO_2 pPrinterInfo;
+
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [AddPrinterW]\n"));
+#endif
+
+ pPrinterInfo = (PPRINTER_INFO_2)pbPrinter;
+
+
+ if (dwLevel != 2)
+ {
+ err = ERROR_INVALID_PARAMETER;
+ goto ErrorExit;
+ }
+
+
+ if (!(pszPrinterName = (LPTSTR)LocalAlloc(LPTR, (wcslen(((PRINTER_INFO_2 *)pbPrinter)->pPrinterName)+1)* sizeof(WCHAR))))
+ {
+ err = ERROR_NOT_ENOUGH_MEMORY;
+ goto ErrorExit;
+ }
+
+ wcscpy(pszPrinterName,pPrinterInfo->pPrinterName);
+
+ // PrinterName is the name represented as \\server\share
+ //The pszPServer parameter could have multiple fields separated by semicolons
+
+
+ if ( ( !ValidateUNCName( pszPrinterName ) )
+ || ( (pszQueue = wcschr( pszPrinterName + 2, L'\\')) == NULL )
+ || ( pszQueue == (pszPrinterName + 2) )
+ || ( *(pszQueue + 1) == L'\0' )
+ )
+ {
+ err = ERROR_INVALID_NAME;
+ goto ErrorExit;
+ }
+
+
+
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(( "NWSPL [AddPrinter] Name = %ws\n",pszPServer));
+#endif
+
+ if ( pszPrinterName == NULL )
+//PrinterName is a mandatory field but not the list of PServers
+ {
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(( "NWSPL [AddPrinter] Printername not supplied\n" ));
+#endif
+
+ SetLastError( ERROR_INVALID_NAME );
+ goto ErrorExit;
+ }
+
+ //Check to see if there is a port of the same name
+ // If so, abort the addprinter operation.
+ // This code was commented earlier
+
+ if (PortExists(pszPrinterName, &err ) && !err )
+ {
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(( "NWSPL [AddPrinter], = %ws; Port exists with same name\n", pszPrinterName ));
+#endif
+ SetLastError(ERROR_ALREADY_ASSIGNED);
+ goto ErrorExit;
+ }
+
+
+ // Put all the relevant information into the PRINTER_INFO_2 structure
+
+ RpcTryExcept
+ {
+ err = NwrAddPrinter ( NULL,
+ (LPPRINTER_INFO_2W) pPrinterInfo,
+ (LPNWWKSTA_PRINTER_CONTEXT) &hPrinter
+ );
+ if (!err)
+ {
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(( "NWSPL [AddPrinter] Name = %ws\n", pszPrinterName ));
+#endif
+ goto ErrorExit;
+ }
+ }
+ RpcExcept(1)
+ {
+ DWORD code = RpcExceptionCode();
+ err = NwpMapRpcError( code );
+ goto ErrorExit;
+ }
+ RpcEndExcept
+ if ( !pszPrinterName)
+ (void) LocalFree((HLOCAL)pszPrinterName);
+
+ return hPrinter;
+
+ErrorExit:
+ if ( !pszPrinterName)
+ (void) LocalFree((HLOCAL)pszPrinterName);
+
+ SetLastError( err);
+ return (HANDLE)0x0;
+
+#endif // #ifdef NOT_USED
+}
+
+BOOL
+DeletePrinter(
+ HANDLE hPrinter
+)
+{
+#ifdef NOT_USED
+
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [DeletePrinter]\n"));
+#endif
+
+ SetLastError(ERROR_NOT_SUPPORTED);
+ return FALSE;
+#else
+ LPWSTR pszPrinterName = NULL ; // Used to delete entry from registry
+ DWORD err = NO_ERROR;
+ DWORD DoesPortExist;
+
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [DeletePrinter]\n"));
+#endif
+
+ pszPrinterName = (LPWSTR)LocalAlloc(LPTR,sizeof(WCHAR)*MAX_PATH);
+
+ if(pszPrinterName == NULL)
+ {
+ err = ERROR_NOT_ENOUGH_MEMORY;
+ return FALSE;
+ }
+ //FIXFIX Should you delete the registry and port entries
+ //
+ // Just return success if the handle is a dummy one
+ //
+ if ( hPrinter == &handleDummy )
+ {
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [DeletePrinter] Dummy handle \n"));
+#endif
+ SetLastError(ERROR_NO_NETWORK);
+ return FALSE;
+ }
+ RpcTryExcept
+ {
+
+
+ err = NwrDeletePrinter( NULL,
+ // pszPrinterName,
+ (LPNWWKSTA_PRINTER_CONTEXT) &hPrinter );
+
+ }
+ RpcExcept(1)
+ {
+ DWORD code = RpcExceptionCode();
+
+ if ( code == RPC_S_SERVER_UNAVAILABLE )
+ err = ERROR_INVALID_HANDLE;
+ else
+ err = NwpMapRpcError( code );
+ }
+ RpcEndExcept
+
+ if (!err && PortExists(pszPrinterName, &DoesPortExist) && DoesPortExist)
+ {
+
+ if ( DeleteRegistryEntry (pszPrinterName))
+ (void) DeletePortEntry(pszPrinterName);
+
+ }
+
+
+ if ( err )
+ SetLastError( err );
+
+ return err == NO_ERROR;
+
+#endif // #ifdef NOT_USED
+}
+
+
+BOOL
+DeletePrinterConnectionW(
+ LPWSTR pszName
+)
+{
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [DeletePrinterConnection]\n"));
+#endif
+
+ SetLastError( ERROR_INVALID_NAME );
+ return FALSE;
+}
+
+
+BOOL
+AddPrinterDriverW(
+ LPWSTR pszName,
+ DWORD dwLevel,
+ LPBYTE pbPrinter
+)
+{
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [AddPrinterDriver]\n"));
+#endif
+
+ SetLastError( ERROR_INVALID_NAME );
+ return FALSE;
+}
+
+BOOL
+EnumPrinterDriversW(
+ LPWSTR pszName,
+ LPWSTR pszEnvironment,
+ DWORD dwLevel,
+ LPBYTE pbDriverInfo,
+ DWORD cbBuf,
+ LPDWORD pcbNeeded,
+ LPDWORD pcReturned
+)
+{
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [EnumPrinterDrivers]\n"));
+#endif
+
+ SetLastError( ERROR_INVALID_NAME );
+ return FALSE;
+}
+
+BOOL
+GetPrinterDriverW(
+ HANDLE hPrinter,
+ LPWSTR pszEnvironment,
+ DWORD dwLevel,
+ LPBYTE pbDriverInfo,
+ DWORD cbBuf,
+ LPDWORD pcbNeeded
+)
+{
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [GetPrinterDriver]\n"));
+#endif
+
+ SetLastError( ERROR_NOT_SUPPORTED );
+ return FALSE;
+}
+
+BOOL
+GetPrinterDriverDirectoryW(
+ LPWSTR pszName,
+ LPWSTR pszEnvironment,
+ DWORD dwLevel,
+ LPBYTE pbDriverDirectory,
+ DWORD cbBuf,
+ LPDWORD pcbNeeded
+)
+{
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [GetPrinterDriverDirectory]\n"));
+#endif
+
+ SetLastError( ERROR_INVALID_NAME );
+ return FALSE;
+}
+
+BOOL
+DeletePrinterDriverW(
+ LPWSTR pszName,
+ LPWSTR pszEnvironment,
+ LPWSTR pszDriverName
+)
+{
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [DeletePrinterDriver]\n"));
+#endif
+
+ SetLastError( ERROR_INVALID_NAME );
+ return FALSE;
+}
+
+BOOL
+AddPrintProcessorW(
+ LPWSTR pszName,
+ LPWSTR pszEnvironment,
+ LPWSTR pszPathName,
+ LPWSTR pszPrintProcessorName
+)
+{
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [AddPrintProcessor]\n"));
+#endif
+
+ SetLastError( ERROR_INVALID_NAME );
+ return FALSE;
+}
+
+BOOL
+EnumPrintProcessorsW(
+ LPWSTR pszName,
+ LPWSTR pszEnvironment,
+ DWORD dwLevel,
+ LPBYTE pbPrintProcessorInfo,
+ DWORD cbBuf,
+ LPDWORD pcbNeeded,
+ LPDWORD pcReturned
+)
+{
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [EnumPrintProcessors]\n"));
+#endif
+
+ SetLastError( ERROR_INVALID_NAME );
+ return FALSE;
+}
+
+BOOL
+EnumPrintProcessorDatatypesW(
+ LPWSTR pszName,
+ LPWSTR pszPrintProcessorName,
+ DWORD dwLevel,
+ LPBYTE pbDatatypes,
+ DWORD cbBuf,
+ LPDWORD pcbNeeded,
+ LPDWORD pcReturned
+)
+{
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [EnumPrintProcessorDatatypes]\n"));
+#endif
+
+ SetLastError( ERROR_INVALID_NAME );
+ return FALSE;
+}
+
+BOOL
+GetPrintProcessorDirectoryW(
+ LPWSTR pszName,
+ LPWSTR pszEnvironment,
+ DWORD dwLevel,
+ LPBYTE pbPrintProcessorDirectory,
+ DWORD cbBuf,
+ LPDWORD pcbNeeded
+)
+{
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [GetPrintProcessorDirectory]\n"));
+#endif
+
+ SetLastError( ERROR_INVALID_NAME );
+ return FALSE;
+}
+
+BOOL
+StartPagePrinter(
+ HANDLE hPrinter
+)
+{
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [StartPagePrinter]\n"));
+#endif
+
+ SetLastError( ERROR_NOT_SUPPORTED );
+ return FALSE;
+}
+
+BOOL
+EndPagePrinter(
+ HANDLE hPrinter
+)
+{
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [EndPagePrinter]\n"));
+#endif
+
+ SetLastError( ERROR_NOT_SUPPORTED );
+ return FALSE;
+}
+
+BOOL
+ReadPrinter(
+ HANDLE hPrinter,
+ LPVOID pBuf,
+ DWORD cbBuf,
+ LPDWORD pcbRead
+)
+{
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [ReadPrinter]\n"));
+#endif
+
+ SetLastError( ERROR_NOT_SUPPORTED );
+ return FALSE;
+}
+
+DWORD
+GetPrinterDataW(
+ HANDLE hPrinter,
+ LPWSTR pszValueName,
+ LPDWORD pdwType,
+ LPBYTE pbData,
+ DWORD cbBuf,
+ LPDWORD pcbNeeded
+)
+{
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [GetPrinterData]\n"));
+#endif
+
+ SetLastError( ERROR_NOT_SUPPORTED );
+ return FALSE;
+}
+
+DWORD
+SetPrinterDataW(
+ HANDLE hPrinter,
+ LPWSTR pszValueName,
+ DWORD dwType,
+ LPBYTE pbData,
+ DWORD cbData
+)
+{
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [SetPrinterData]\n"));
+#endif
+
+ SetLastError( ERROR_NOT_SUPPORTED );
+ return FALSE;
+}
+
+BOOL
+AddForm(
+ HANDLE hPrinter,
+ DWORD dwLevel,
+ LPBYTE pbForm
+)
+{
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [AddForm]\n"));
+#endif
+
+ SetLastError( ERROR_NOT_SUPPORTED );
+ return FALSE;
+}
+
+BOOL
+DeleteFormW(
+ HANDLE hPrinter,
+ LPWSTR pszFormName
+)
+{
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [DeleteForm]\n"));
+#endif
+
+ SetLastError( ERROR_NOT_SUPPORTED );
+ return FALSE;
+}
+
+BOOL
+GetFormW(
+ HANDLE hPrinter,
+ LPWSTR pszFormName,
+ DWORD dwLevel,
+ LPBYTE pbForm,
+ DWORD cbBuf,
+ LPDWORD pcbNeeded
+)
+{
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [GetForm]\n"));
+#endif
+
+ SetLastError( ERROR_NOT_SUPPORTED );
+ return FALSE;
+}
+
+BOOL
+SetFormW(
+ HANDLE hPrinter,
+ LPWSTR pszFormName,
+ DWORD dwLevel,
+ LPBYTE pbForm
+)
+{
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [SetForm]\n"));
+#endif
+
+ SetLastError( ERROR_NOT_SUPPORTED );
+ return FALSE;
+}
+
+BOOL
+EnumForms(
+ HANDLE hPrinter,
+ DWORD dwLevel,
+ LPBYTE pbForm,
+ DWORD cbBuf,
+ LPDWORD pcbNeeded,
+ LPDWORD pcReturned
+)
+{
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [EnumForms]\n"));
+#endif
+
+ SetLastError( ERROR_NOT_SUPPORTED );
+ return FALSE;
+}
+
+
+HANDLE
+CreatePrinterIC(
+ HANDLE hPrinter,
+ LPDEVMODE pDevMode
+)
+{
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [CreatePrinterIC]\n"));
+#endif
+
+ SetLastError( ERROR_NOT_SUPPORTED );
+ return FALSE;
+}
+
+BOOL
+PlayGdiScriptOnPrinterIC(
+ HANDLE hPrinterIC,
+ LPBYTE pbIn,
+ DWORD cbIn,
+ LPBYTE pbOut,
+ DWORD cbOut,
+ DWORD ul
+)
+{
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [PlayGdiScriptOnPrinterIC]\n"));
+#endif
+
+ SetLastError( ERROR_NOT_SUPPORTED );
+ return FALSE;
+}
+
+BOOL
+DeletePrinterIC(
+ HANDLE hPrinterIC
+)
+{
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [DeletePrinterIC]\n"));
+#endif
+
+ SetLastError( ERROR_NOT_SUPPORTED );
+ return FALSE;
+}
+
+DWORD
+PrinterMessageBoxW(
+ HANDLE hPrinter,
+ DWORD dwError,
+ HWND hWnd,
+ LPWSTR pszText,
+ LPWSTR pszCaption,
+ DWORD dwType
+)
+{
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [PrinterMessageBox]\n"));
+#endif
+
+ SetLastError( ERROR_NOT_SUPPORTED );
+ return FALSE;
+}
+
+BOOL
+AddMonitorW(
+ LPWSTR pszName,
+ DWORD dwLevel,
+ LPBYTE pbMonitorInfo
+)
+{
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [AddMonitor]\n"));
+#endif
+
+ SetLastError( ERROR_INVALID_NAME );
+ return FALSE;
+}
+
+BOOL
+DeleteMonitorW(
+ LPWSTR pszName,
+ LPWSTR pszEnvironment,
+ LPWSTR pszMonitorName
+)
+{
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [DeleteMonitor]\n"));
+#endif
+
+ SetLastError( ERROR_INVALID_NAME );
+ return FALSE;
+}
+
+BOOL
+DeletePrintProcessorW(
+ LPWSTR pszName,
+ LPWSTR pszEnvironment,
+ LPWSTR pszPrintProcessorName
+)
+{
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [DeletePrintProcessor]\n"));
+#endif
+
+ SetLastError( ERROR_INVALID_NAME );
+ return FALSE;
+}
+
+//------------------------------------------------------------------
+//
+// Print Provider Miscellaneous Functions
+//
+//------------------------------------------------------------------
+
+VOID
+NwpGetUserInfo(
+ LPWSTR *ppszUser,
+ BOOL *pfGateway
+)
+/*++
+
+Routine Description:
+
+ Get the user information of the impersonating client.
+
+Arguments:
+
+ ppszUser - A pointer to buffer to store the Unicode string if
+ the impersonated client's user name can be looked up
+ successfully. If the conversion was unsuccessful, it points
+ to NULL.
+
+ pfGateway - A pointer to a boolean to store whether the user is
+ printing through a gateway or not. We assume that
+ if the user sid and the current logon user sid is not
+ the same, then the user is printing through gateway.
+ Else the user is printing locally.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ DWORD err;
+ LPWSTR pszUserSid = NULL;
+ LPWSTR pszLogonUserSid = NULL;
+
+ //
+ // If any error occurs while trying to get the username, just
+ // assume no user name and not gateway printing.
+ //
+ *pfGateway = TRUE;
+ *ppszUser = NULL;
+
+ if ( ((err = NwpGetThreadUserInfo( ppszUser, &pszUserSid )) == NO_ERROR)
+ && ((err = NwpGetLogonUserInfo( &pszLogonUserSid )) == NO_ERROR)
+ )
+ {
+ if ( _wcsicmp( pszUserSid, pszLogonUserSid ) == 0 )
+ *pfGateway = FALSE;
+
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NwpGetUserInfo: Thread User= %ws, Thread SID = %ws,\nCurrent SID = %ws fGateway = %d\n",
+ *ppszUser, pszUserSid, pszLogonUserSid, *pfGateway ));
+#endif
+
+ LocalFree( pszUserSid );
+ LocalFree( pszLogonUserSid );
+ }
+
+}
+
+#define SIZE_OF_TOKEN_INFORMATION \
+ sizeof( TOKEN_USER ) \
+ + sizeof( SID ) \
+ + sizeof( ULONG ) * SID_MAX_SUB_AUTHORITIES
+
+DWORD
+NwpGetThreadUserInfo(
+ LPWSTR *ppszUser,
+ LPWSTR *ppszUserSid
+)
+/*++
+
+Routine Description:
+
+ Get the user name and user sid string of the impersonating client.
+
+Arguments:
+
+ ppszUser - A pointer to buffer to store the Unicode string
+ if the impersonated client's can be looked up. If the
+ lookup was unsuccessful, it points to NULL.
+
+ ppszUserSid - A pointer to buffer to store the string if the impersonated
+ client's SID can be expanded successfully into unicode string.
+ If the conversion was unsuccessful, it points to NULL.
+
+Return Value:
+
+ The error code.
+
+--*/
+{
+ DWORD err;
+ HANDLE TokenHandle;
+ UCHAR TokenInformation[ SIZE_OF_TOKEN_INFORMATION ];
+ ULONG ReturnLength;
+
+ *ppszUser = NULL;
+ *ppszUserSid = NULL;
+
+ // We can use OpenThreadToken because this server thread
+ // is impersonating a client
+
+ if ( !OpenThreadToken( GetCurrentThread(),
+ TOKEN_READ,
+ TRUE, /* Open as self */
+ &TokenHandle ))
+ {
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NwpGetThreadUserInfo: OpenThreadToken failed: Error %d\n",
+ GetLastError()));
+#endif
+ return(GetLastError());
+ }
+
+ // notice that we've allocated enough space for the
+ // TokenInformation structure. so if we fail, we
+ // return a NULL pointer indicating failure
+
+
+ if ( !GetTokenInformation( TokenHandle,
+ TokenUser,
+ TokenInformation,
+ sizeof( TokenInformation ),
+ &ReturnLength ))
+ {
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NwpGetThreadUserInfo: GetTokenInformation failed: Error %d\n",
+ GetLastError()));
+#endif
+ return(GetLastError());
+ }
+
+ CloseHandle( TokenHandle );
+
+ // convert the Sid (pointed to by pSid) to its
+ // equivalent Unicode string representation.
+
+ err = NwpGetUserNameFromSid( ((PTOKEN_USER)TokenInformation)->User.Sid,
+ ppszUser );
+ err = err? err : NwpConvertSid( ((PTOKEN_USER)TokenInformation)->User.Sid,
+ ppszUserSid );
+
+ if ( err )
+ {
+ if ( *ppszUser )
+ LocalFree( *ppszUser );
+
+ if ( *ppszUserSid )
+ LocalFree( *ppszUserSid );
+ }
+
+ return err;
+}
+
+DWORD
+NwpGetUserNameFromSid(
+ PSID pUserSid,
+ LPWSTR *ppszUserName
+)
+/*++
+
+Routine Description:
+
+ Lookup the user name given the user SID.
+
+Arguments:
+
+ pUserSid - Points to the user sid to be looked up
+
+ ppszUserName - A pointer to buffer to store the string if the impersonated
+ client's SID can be expanded successfully into unicode string.
+ If the conversion was unsuccessful, it points to NULL.
+
+Return Value:
+
+ The error code.
+
+--*/
+{
+ NTSTATUS ntstatus;
+
+ LSA_HANDLE hlsa;
+ OBJECT_ATTRIBUTES oa;
+ SECURITY_QUALITY_OF_SERVICE sqos;
+ PLSA_REFERENCED_DOMAIN_LIST plsardl = NULL;
+ PLSA_TRANSLATED_NAME plsatn = NULL;
+
+ sqos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
+ sqos.ImpersonationLevel = SecurityImpersonation;
+ sqos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
+ sqos.EffectiveOnly = FALSE;
+ InitializeObjectAttributes( &oa, NULL, 0L, NULL, NULL );
+ oa.SecurityQualityOfService = &sqos;
+
+ ntstatus = LsaOpenPolicy( NULL,
+ &oa,
+ POLICY_LOOKUP_NAMES,
+ &hlsa );
+
+ if ( NT_SUCCESS( ntstatus ))
+ {
+ ntstatus = LsaLookupSids( hlsa,
+ 1,
+ &pUserSid,
+ &plsardl,
+ &plsatn );
+
+ if ( NT_SUCCESS( ntstatus ))
+ {
+ UNICODE_STRING *pUnicodeStr = &((*plsatn).Name);
+
+ *ppszUserName = LocalAlloc( LMEM_ZEROINIT,
+ pUnicodeStr->Length+sizeof(WCHAR));
+
+ if ( *ppszUserName != NULL )
+ {
+ memcpy( *ppszUserName, pUnicodeStr->Buffer, pUnicodeStr->Length );
+ }
+ else
+ {
+ ntstatus = STATUS_NO_MEMORY;
+ }
+
+ LsaFreeMemory( plsardl );
+ LsaFreeMemory( plsatn );
+ }
+#if DBG
+ else
+ {
+ KdPrint(("NwpGetUserNameFromSid: LsaLookupSids failed: Error = %d\n",
+ GetLastError()));
+ }
+#endif
+
+ LsaClose( hlsa );
+ }
+#if DBG
+ else
+ {
+ KdPrint(("NwpGetUserNameFromSid: LsaOpenPolicy failed: Error = %d\n",
+ GetLastError()));
+ }
+#endif
+
+ return RtlNtStatusToDosError( ntstatus );
+
+}
+
+DWORD
+NwpGetLogonUserInfo(
+ LPWSTR *ppszUserSid
+)
+/*++
+
+Routine Description:
+
+ Get the logon user sid string from the registry.
+
+Arguments:
+
+ ppszUserSid - On return, this points to the current logon user
+ sid string.
+
+Return Value:
+
+ The error code.
+
+--*/
+{
+ DWORD err;
+ HKEY WkstaKey;
+
+ LPWSTR CurrentUser = NULL;
+
+ //
+ // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
+ // \NWCWorkstation\Parameters to get the sid of the CurrentUser
+ //
+ err = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ NW_WORKSTATION_REGKEY,
+ REG_OPTION_NON_VOLATILE,
+ KEY_READ,
+ &WkstaKey
+ );
+
+ if ( err == NO_ERROR) {
+
+ //
+ // Read the current user SID string so that we
+ // know which key is the current user key to open.
+ //
+ err = NwReadRegValue(
+ WkstaKey,
+ NW_CURRENTUSER_VALUENAME,
+ &CurrentUser
+ );
+
+ RegCloseKey( WkstaKey );
+
+ if ( err == NO_ERROR) {
+ *ppszUserSid = CurrentUser;
+ }
+ }
+
+ return(err);
+}
diff --git a/private/nw/svcdlls/nwwks/client/nwspl.h b/private/nw/svcdlls/nwwks/client/nwspl.h
new file mode 100644
index 000000000..2a67f1aa4
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/nwspl.h
@@ -0,0 +1,76 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ nwspl.h
+
+Abstract:
+
+ Common header for print provider client-side code.
+
+Author:
+
+ Yi-Hsin Sung (yihsins) 15-May-1993
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+--*/
+
+#ifndef _NWSPL_INCLUDED_
+#define _NWSPL_INCLUDED_
+
+#include "nwdlg.h"
+
+typedef struct _NWPORT {
+ DWORD cb;
+ struct _NWPORT *pNext;
+ LPWSTR pName;
+} NWPORT, *PNWPORT;
+
+extern LPWSTR pszRegistryPath;
+extern LPWSTR pszRegistryPortNames;
+extern WCHAR szMachineName[];
+extern PNWPORT pNwFirstPort;
+extern CRITICAL_SECTION NwSplSem;
+
+BOOL IsLocalMachine(
+ LPWSTR pszName
+);
+
+BOOL PortExists(
+ LPWSTR pszPortName,
+ LPDWORD pError
+);
+
+BOOL PortKnown(
+ LPWSTR pszPortName
+);
+
+PNWPORT CreatePortEntry(
+ LPWSTR pszPortName
+);
+
+BOOL DeletePortEntry(
+ LPWSTR pszPortName
+);
+
+VOID DeleteAllPortEntries(
+ VOID
+);
+
+DWORD CreateRegistryEntry(
+ LPWSTR pszPortName
+);
+
+DWORD DeleteRegistryEntry(
+ LPWSTR pszPortName
+);
+
+
+#endif // _NWSPL_INCLUDED_
diff --git a/private/nw/svcdlls/nwwks/client/nwutil.c b/private/nw/svcdlls/nwwks/client/nwutil.c
new file mode 100644
index 000000000..f39a0de0d
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/nwutil.c
@@ -0,0 +1,872 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ nwutil.c
+
+Abstract:
+
+ Contains some misc functions used by shell extensions
+
+Author:
+
+ Yi-Hsin Sung (yihsins) 25-Oct-1995
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <ntddnwfs.h>
+#include <ndsapi32.h>
+#include <nwmisc.h>
+#include "nwclient.h"
+#include "nwapi.h"
+#include "nwutil.h"
+
+#define EXTRA_BYTES 256
+
+BOOL
+NwIsNdsSyntax(
+ IN LPWSTR lpstrUnc
+)
+{
+ HANDLE hTreeConn;
+ DWORD dwOid;
+ DWORD status = NO_ERROR;
+
+ if ( lpstrUnc == NULL )
+ return FALSE;
+
+ status = NwOpenAndGetTreeInfo( lpstrUnc, &hTreeConn, &dwOid );
+
+ if ( status != NO_ERROR )
+ {
+ return FALSE;
+ }
+
+ CloseHandle( hTreeConn );
+
+ return TRUE;
+}
+
+VOID
+NwAbbreviateUserName(
+ IN LPWSTR pszFullName,
+ OUT LPWSTR pszUserName
+)
+{
+ if ( pszUserName == NULL )
+ return;
+
+ if ( NwIsNdsSyntax( pszFullName ))
+ {
+ //
+ // BUGBUG - This part of the code never gets called due to the
+ // change in how NwIsNdsSyntax works. Post NT 4.0, get rid of the
+ // NwIsNdsSyntax test and run this section of code no matter what.
+ // This bug was not fixed in NT4.0 due to the extremely close time
+ // to the ship date.
+ //
+ LPWSTR pszTemp = pszFullName;
+ LPWSTR pszLast = pszTemp;
+
+ *pszUserName = 0;
+
+ while ( pszTemp = wcschr( pszTemp, L'='))
+ {
+ WCHAR NextChar;
+
+ NextChar = *(++pszTemp);
+
+ while ( NextChar != 0 && NextChar != L'.' )
+ {
+ *(pszUserName++) = *pszTemp;
+ NextChar = *(++pszTemp);
+ }
+
+ if ( NextChar == 0 )
+ {
+ pszLast = NULL;
+ break;
+ }
+
+ *(pszUserName++) = *pszTemp; // put back the '.'
+ pszLast = ++pszTemp;
+ }
+
+ if ( pszLast != NULL )
+ {
+ while ( *pszLast != 0 )
+ *(pszUserName++) = *(pszLast++);
+ }
+
+ *pszUserName = 0;
+ }
+ else
+ {
+ wcscpy( pszUserName, pszFullName );
+ }
+}
+
+VOID
+NwMakePrettyDisplayName(
+ IN LPWSTR pszName
+)
+{
+ if ( pszName )
+ {
+ CharLower( pszName );
+ CharUpperBuff( pszName, 1);
+ }
+}
+
+VOID
+NwExtractTreeName(
+ IN LPWSTR pszUNCPath,
+ OUT LPWSTR pszTreeName
+)
+{
+ LPWSTR pszTemp = NULL;
+
+ if ( pszTreeName == NULL )
+ return;
+
+ pszTreeName[0] = 0;
+
+ if ( pszUNCPath == NULL )
+ return;
+
+ if ( pszUNCPath[0] == L' ')
+ pszUNCPath++;
+
+ if ( ( pszUNCPath[0] != L'\\')
+ || ( pszUNCPath[1] != L'\\')
+ )
+ {
+ return;
+ }
+
+ wcscpy( pszTreeName, pszUNCPath + 2 ); // get past "\\"
+
+ if ( pszTemp = wcschr( pszTreeName, L'\\' ))
+ *pszTemp = 0;
+}
+
+VOID
+NwExtractServerName(
+ IN LPWSTR pszUNCPath,
+ OUT LPWSTR pszServerName
+)
+{
+ LPWSTR pszTemp = NULL;
+
+ if ( pszServerName == NULL )
+ return;
+
+ pszServerName[0] = 0;
+
+ if ( ( pszUNCPath == NULL )
+ || ( pszUNCPath[0] != L'\\')
+ || ( pszUNCPath[1] != L'\\')
+ )
+ {
+ return;
+ }
+
+ if ( NwIsNdsSyntax( pszUNCPath ))
+ {
+ pszTemp = wcschr( pszUNCPath + 2, L'\\' ); // get past "\\"
+ wcscpy( pszServerName, pszTemp + 1 ); // get past "\"
+
+ if ( pszTemp = wcschr( pszServerName, L'.' ))
+ *pszTemp = 0;
+
+ }
+ else
+ {
+ wcscpy( pszServerName, pszUNCPath + 2 ); // get past "\\"
+
+ if ( pszTemp = wcschr( pszServerName, L'\\' ))
+ *pszTemp = 0;
+ }
+}
+
+VOID
+NwExtractShareName(
+ IN LPWSTR pszUNCPath,
+ OUT LPWSTR pszShareName
+)
+{
+ LPWSTR pszTemp = NULL;
+
+ if ( pszShareName == NULL )
+ return;
+
+ pszShareName[0] = 0;
+
+ if ( ( pszUNCPath == NULL )
+ || ( pszUNCPath[0] != L'\\')
+ || ( pszUNCPath[1] != L'\\')
+ )
+ {
+ return;
+ }
+
+ if ( NwIsNdsSyntax( pszUNCPath ))
+ {
+ pszTemp = wcschr( pszUNCPath + 2, L'\\' ); // get past "\\"
+ wcscpy( pszShareName, pszTemp + 1 ); // get past "\"
+
+ if ( pszTemp = wcschr( pszShareName, L'.' ))
+ *pszTemp = 0;
+ }
+ else
+ {
+ pszTemp = wcschr( pszUNCPath + 2, L'\\' ); // get past "\\"
+ wcscpy( pszShareName, pszTemp + 1); // get past "\"
+
+ if ( pszTemp = wcschr( pszShareName, L'\\' ))
+ *pszTemp = 0;
+ }
+}
+
+DWORD
+NwIsServerInDefaultTree(
+ IN LPWSTR pszFullServerName,
+ OUT BOOL *pfInDefaultTree
+)
+{
+ DWORD err = NO_ERROR;
+ LPWSTR pszCurrentContext = NULL;
+ DWORD dwPrintOptions;
+ WCHAR szTreeName[MAX_PATH + 1];
+
+ *pfInDefaultTree = FALSE;
+
+ if ( !NwIsNdsSyntax( pszFullServerName ))
+ {
+ // The full server name does not contain any NDS information
+ // In this case, assume the server is not in the tree.
+ // If a server belongs the default tree, we would get the full
+ // NDS information.
+ return NO_ERROR;
+ }
+
+ // Get the current default tree or server name
+ err = NwQueryInfo( &dwPrintOptions, &pszCurrentContext );
+
+ if ( (err == NO_ERROR) && ( *pszCurrentContext == TREECHAR))
+ {
+ // Yes, there is a default tree.
+ // So, get the tree name out of *TREE\CONTEXT
+ LPWSTR pszTemp = wcschr( pszCurrentContext, L'\\');
+ if ( pszTemp )
+ *pszTemp = 0;
+
+ // Need to extract the tree name from full UNC path
+ NwExtractTreeName( pszFullServerName, szTreeName );
+
+ if ( _wcsicmp( szTreeName,
+ pszCurrentContext + 1) == 0 ) // get past the tree char
+ {
+ *pfInDefaultTree = TRUE;
+ }
+ }
+
+ if ( pszCurrentContext != NULL )
+ LocalFree( pszCurrentContext );
+
+ return err;
+}
+
+DWORD
+NwIsServerOrTreeAttached(
+ IN LPWSTR pszName,
+ OUT BOOL *pfAttached,
+ OUT BOOL *pfAuthenticated
+)
+{
+ DWORD err = NO_ERROR;
+ DWORD EntriesRead = 0;
+ DWORD ResumeKey = 0;
+ LPBYTE Buffer = NULL;
+
+ err = NwGetConnectionStatus(
+ pszName,
+ &ResumeKey,
+ &Buffer,
+ &EntriesRead );
+
+ *pfAttached = FALSE;
+ *pfAuthenticated = FALSE;
+
+ if (( err == NO_ERROR ) && ( EntriesRead > 0 ))
+ {
+ // For trees, we might get more than one entries back.
+
+ PCONN_STATUS pConnStatus = (PCONN_STATUS) Buffer;
+
+ if ( !pConnStatus->fPreferred )
+ {
+ // We only need to return as attached if this is not a preferred
+ // server implicit connection since we don't want the user to know
+ // about this connection ( which rdr does not allow user to delete)
+
+ *pfAttached = TRUE;
+ *pfAuthenticated = (pConnStatus->dwConnType != NW_CONN_NOT_AUTHENTICATED);
+ }
+ }
+
+ if ( Buffer != NULL )
+ {
+ LocalFree( Buffer );
+ Buffer = NULL;
+ }
+
+ return err;
+}
+
+DWORD
+NwGetConnectionInformation(
+ IN LPWSTR pszName,
+ OUT LPBYTE Buffer,
+ IN DWORD BufferSize
+)
+{
+ NTSTATUS ntstatus = STATUS_SUCCESS;
+ HANDLE handleRdr = NULL;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ IO_STATUS_BLOCK IoStatusBlock;
+ UNICODE_STRING uRdrName;
+ WCHAR RdrPrefix[] = L"\\Device\\NwRdr\\*";
+
+ PNWR_REQUEST_PACKET RequestPacket = NULL;
+ DWORD RequestPacketSize = 0;
+ DWORD dwNameLen = 0;
+
+ if ( pszName == NULL )
+ return ERROR_INVALID_PARAMETER;
+
+ //
+ // Set up the object attributes.
+ //
+
+ RtlInitUnicodeString( &uRdrName, RdrPrefix );
+
+ InitializeObjectAttributes( &ObjectAttributes,
+ &uRdrName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL );
+
+ ntstatus = NtOpenFile( &handleRdr,
+ SYNCHRONIZE | FILE_LIST_DIRECTORY,
+ &ObjectAttributes,
+ &IoStatusBlock,
+ FILE_SHARE_VALID_FLAGS,
+ FILE_SYNCHRONOUS_IO_NONALERT );
+
+ if ( !NT_SUCCESS(ntstatus) )
+ goto CleanExit;
+
+ dwNameLen = wcslen(pszName) * sizeof(WCHAR);
+
+ RequestPacketSize = sizeof( NWR_REQUEST_PACKET ) + dwNameLen;
+
+ RequestPacket = (PNWR_REQUEST_PACKET) LocalAlloc( LMEM_ZEROINIT,
+ RequestPacketSize );
+
+ if ( RequestPacket == NULL )
+ {
+ ntstatus = STATUS_NO_MEMORY;
+ goto CleanExit;
+ }
+
+ //
+ // Fill out the request packet for FSCTL_NWR_GET_CONN_INFO.
+ //
+
+ RequestPacket->Version = REQUEST_PACKET_VERSION;
+ RequestPacket->Parameters.GetConnInfo.ConnectionNameLength = dwNameLen;
+
+ RtlCopyMemory( &(RequestPacket->Parameters.GetConnInfo.ConnectionName[0]),
+ pszName,
+ dwNameLen );
+
+ ntstatus = NtFsControlFile( handleRdr,
+ NULL,
+ NULL,
+ NULL,
+ &IoStatusBlock,
+ FSCTL_NWR_GET_CONN_INFO,
+ (PVOID) RequestPacket,
+ RequestPacketSize,
+ (PVOID) Buffer,
+ BufferSize );
+
+ if ( NT_SUCCESS( ntstatus ))
+ ntstatus = IoStatusBlock.Status;
+
+CleanExit:
+
+ if ( handleRdr != NULL )
+ NtClose( handleRdr );
+
+ if ( RequestPacket != NULL )
+ LocalFree( RequestPacket );
+
+ return RtlNtStatusToDosError( ntstatus );
+}
+
+DWORD
+NWPGetConnectionStatus(
+ IN LPWSTR pszRemoteName,
+ IN OUT PDWORD ResumeKey,
+ OUT LPBYTE Buffer,
+ IN DWORD BufferSize,
+ OUT PDWORD BytesNeeded,
+ OUT PDWORD EntriesRead
+)
+{
+ NTSTATUS ntstatus = STATUS_SUCCESS;
+ HANDLE handleRdr = NULL;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ IO_STATUS_BLOCK IoStatusBlock;
+ UNICODE_STRING uRdrName;
+ WCHAR RdrPrefix[] = L"\\Device\\NwRdr\\*";
+
+ PNWR_REQUEST_PACKET RequestPacket = NULL;
+ DWORD RequestPacketSize = 0;
+ DWORD dwRemoteNameLen = 0;
+
+ //
+ // Set up the object attributes.
+ //
+
+ RtlInitUnicodeString( &uRdrName, RdrPrefix );
+
+ InitializeObjectAttributes( &ObjectAttributes,
+ &uRdrName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL );
+
+ ntstatus = NtOpenFile( &handleRdr,
+ SYNCHRONIZE | FILE_LIST_DIRECTORY,
+ &ObjectAttributes,
+ &IoStatusBlock,
+ FILE_SHARE_VALID_FLAGS,
+ FILE_SYNCHRONOUS_IO_NONALERT );
+
+ if ( !NT_SUCCESS(ntstatus) )
+ goto CleanExit;
+
+ dwRemoteNameLen = pszRemoteName? wcslen(pszRemoteName)*sizeof(WCHAR) : 0;
+
+ RequestPacketSize = sizeof( NWR_REQUEST_PACKET ) + dwRemoteNameLen;
+
+ RequestPacket = (PNWR_REQUEST_PACKET) LocalAlloc( LMEM_ZEROINIT,
+ RequestPacketSize );
+
+ if ( RequestPacket == NULL )
+ {
+ ntstatus = STATUS_NO_MEMORY;
+ goto CleanExit;
+ }
+
+ //
+ // Fill out the request packet for FSCTL_NWR_GET_CONN_STATUS.
+ //
+
+ RequestPacket->Parameters.GetConnStatus.ResumeKey = *ResumeKey;
+
+ RequestPacket->Version = REQUEST_PACKET_VERSION;
+ RequestPacket->Parameters.GetConnStatus.ConnectionNameLength = dwRemoteNameLen;
+
+ RtlCopyMemory( &(RequestPacket->Parameters.GetConnStatus.ConnectionName[0]),
+ pszRemoteName,
+ dwRemoteNameLen );
+
+ ntstatus = NtFsControlFile( handleRdr,
+ NULL,
+ NULL,
+ NULL,
+ &IoStatusBlock,
+ FSCTL_NWR_GET_CONN_STATUS,
+ (PVOID) RequestPacket,
+ RequestPacketSize,
+ (PVOID) Buffer,
+ BufferSize );
+
+ if ( NT_SUCCESS( ntstatus ))
+ ntstatus = IoStatusBlock.Status;
+
+ *EntriesRead = RequestPacket->Parameters.GetConnStatus.EntriesReturned;
+ *ResumeKey = RequestPacket->Parameters.GetConnStatus.ResumeKey;
+ *BytesNeeded = RequestPacket->Parameters.GetConnStatus.BytesNeeded;
+
+CleanExit:
+
+ if ( handleRdr != NULL )
+ NtClose( handleRdr );
+
+ if ( RequestPacket != NULL )
+ LocalFree( RequestPacket );
+
+ return RtlNtStatusToDosError( ntstatus );
+}
+
+
+DWORD
+NwGetConnectionStatus(
+ IN LPWSTR pszRemoteName,
+ OUT PDWORD ResumeKey,
+ OUT LPBYTE *Buffer,
+ OUT PDWORD EntriesRead
+)
+{
+ DWORD err = NO_ERROR;
+ DWORD dwBytesNeeded = 0;
+ DWORD dwBufferSize = TWO_KB;
+
+ *Buffer = NULL;
+ *EntriesRead = 0;
+
+ do {
+
+ *Buffer = (LPBYTE) LocalAlloc( LMEM_ZEROINIT, dwBufferSize );
+
+ if ( *Buffer == NULL )
+ return ERROR_NOT_ENOUGH_MEMORY;
+
+ err = NWPGetConnectionStatus( pszRemoteName,
+ ResumeKey,
+ *Buffer,
+ dwBufferSize,
+ &dwBytesNeeded,
+ EntriesRead );
+
+ if ( err == ERROR_INSUFFICIENT_BUFFER )
+ {
+ dwBufferSize = dwBytesNeeded + EXTRA_BYTES;
+ LocalFree( *Buffer );
+ *Buffer = NULL;
+ }
+
+ } while ( err == ERROR_INSUFFICIENT_BUFFER );
+
+ if ( err == ERROR_INVALID_PARAMETER ) // not attached
+ {
+ err = NO_ERROR;
+ *EntriesRead = 0;
+ }
+
+ return err;
+}
+
+DWORD
+NwGetNdsVolumeInfo(
+ IN LPWSTR pszName,
+ OUT LPWSTR pszServerBuffer,
+ IN WORD wServerBufferSize, // in bytes
+ OUT LPWSTR pszVolumeBuffer,
+ IN WORD wVolumeBufferSize // in bytes
+)
+{
+ NTSTATUS ntstatus = STATUS_SUCCESS;
+ HANDLE handleNdsTree;
+
+ LPWSTR pszTree, pszVolume, pszTemp;
+ UNICODE_STRING uTree, uVolume;
+
+ UNICODE_STRING uHostServer, uHostVolume;
+ WCHAR HostVolumeBuffer[256];
+
+ pszTree = pszName + 2; // get past two backslashes
+
+ pszTemp = wcschr( pszTree, L'\\' );
+ if ( pszTemp )
+ *pszTemp = 0;
+ else
+ return ERROR_INVALID_PARAMETER;
+
+ pszVolume = pszTemp + 1;
+
+ RtlInitUnicodeString( &uTree, pszTree );
+ RtlInitUnicodeString( &uVolume, pszVolume );
+
+ //
+ // Open up a handle to the tree.
+ //
+
+ ntstatus = NwNdsOpenTreeHandle( &uTree,
+ &handleNdsTree );
+
+ if ( !NT_SUCCESS( ntstatus ))
+ goto CleanExit;
+
+ //
+ // Set up the reply strings.
+ //
+
+ uHostServer.Length = 0;
+ uHostServer.MaximumLength = wServerBufferSize;
+ uHostServer.Buffer = pszServerBuffer;
+
+ RtlZeroMemory( pszServerBuffer, wServerBufferSize );
+
+ if ( pszVolumeBuffer != NULL )
+ {
+ uHostVolume.Length = 0;
+ uHostVolume.MaximumLength = wVolumeBufferSize;
+ uHostVolume.Buffer = pszVolumeBuffer;
+
+ RtlZeroMemory( pszVolumeBuffer, wVolumeBufferSize );
+ }
+ else
+ {
+ uHostVolume.Length = 0;
+ uHostVolume.MaximumLength = sizeof( HostVolumeBuffer );
+ uHostVolume.Buffer = HostVolumeBuffer;
+ }
+
+ ntstatus = NwNdsGetVolumeInformation( handleNdsTree,
+ &uVolume,
+ &uHostServer,
+ &uHostVolume );
+
+ CloseHandle( handleNdsTree );
+
+CleanExit:
+
+ if ( pszTemp )
+ *pszTemp = L'\\';
+
+ return RtlNtStatusToDosError( ntstatus );
+}
+
+DWORD
+NwOpenAndGetTreeInfo(
+ LPWSTR pszNdsUNCPath,
+ HANDLE *phTreeConn,
+ DWORD *pdwOid
+)
+{
+ NTSTATUS ntstatus = STATUS_SUCCESS;
+ WCHAR lpServerName[NW_MAX_SERVER_LEN];
+ UNICODE_STRING ServerName;
+
+ UNICODE_STRING ObjectName;
+
+ *phTreeConn = NULL;
+
+ ServerName.Length = 0;
+ ServerName.MaximumLength = sizeof( lpServerName );
+ ServerName.Buffer = lpServerName;
+
+ ObjectName.Buffer = NULL;
+ ObjectName.MaximumLength = ( wcslen( pszNdsUNCPath) + 1 ) * sizeof( WCHAR );
+
+ ObjectName.Length = NwParseNdsUncPath( (LPWSTR *) &ObjectName.Buffer,
+ pszNdsUNCPath,
+ PARSE_NDS_GET_TREE_NAME );
+
+ if ( ObjectName.Length == 0 || ObjectName.Buffer == NULL )
+ {
+ return ERROR_PATH_NOT_FOUND;
+ }
+
+ //
+ // Open a NDS tree connection handle to \\treename
+ //
+ ntstatus = NwNdsOpenTreeHandle( &ObjectName, phTreeConn );
+
+ if ( !NT_SUCCESS( ntstatus ))
+ {
+ return RtlNtStatusToDosError( ntstatus );
+ }
+
+ //
+ // Get the path to the container to open.
+ //
+ ObjectName.Length = NwParseNdsUncPath( (LPWSTR *) &ObjectName.Buffer,
+ pszNdsUNCPath,
+ PARSE_NDS_GET_PATH_NAME );
+
+ if ( ObjectName.Length == 0 )
+ {
+ UNICODE_STRING Root;
+
+ RtlInitUnicodeString(&Root, L"[Root]");
+
+ //
+ // Resolve the path to get a NDS object id.
+ //
+ ntstatus = NwNdsResolveName( *phTreeConn,
+ &Root,
+ pdwOid,
+ &ServerName,
+ NULL,
+ 0 );
+
+ }
+ else
+ {
+ //
+ // Resolve the path to get a NDS object id.
+ //
+ ntstatus = NwNdsResolveName( *phTreeConn,
+ &ObjectName,
+ pdwOid,
+ &ServerName,
+ NULL,
+ 0 );
+
+ }
+
+ if ( ntstatus == STATUS_SUCCESS && ServerName.Length )
+ {
+ DWORD dwHandleType;
+
+ //
+ // NwNdsResolveName succeeded, but we were referred to
+ // another server, though pdwOid is still valid.
+
+ if ( *phTreeConn )
+ CloseHandle( *phTreeConn );
+
+ *phTreeConn = NULL;
+
+ //
+ // Open a NDS generic connection handle to \\ServerName
+ //
+ ntstatus = NwNdsOpenGenericHandle( &ServerName,
+ &dwHandleType,
+ phTreeConn );
+
+ if ( ntstatus != STATUS_SUCCESS )
+ {
+ return RtlNtStatusToDosError(ntstatus);
+ }
+
+ ASSERT( dwHandleType != HANDLE_TYPE_NCP_SERVER );
+ }
+
+ if ( !NT_SUCCESS( ntstatus ))
+ {
+
+ if ( *phTreeConn != NULL )
+ {
+ CloseHandle( *phTreeConn );
+ *phTreeConn = NULL;
+ }
+ return RtlNtStatusToDosError(ntstatus);
+ }
+
+ return NO_ERROR;
+
+}
+
+DWORD
+NwGetConnectedTrees(
+ IN LPWSTR pszNtUserName,
+ OUT LPBYTE Buffer,
+ IN DWORD BufferSize,
+ OUT LPDWORD lpEntriesRead,
+ OUT LPDWORD lpUserLUID
+)
+{
+ NTSTATUS ntstatus = STATUS_SUCCESS;
+ HANDLE handleRdr = NULL;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ IO_STATUS_BLOCK IoStatusBlock;
+ WCHAR RdrPrefix[] = L"\\Device\\NwRdr\\*";
+ UNICODE_STRING uRdrName;
+ UNICODE_STRING uNtUserName;
+
+ PNWR_NDS_REQUEST_PACKET Request = NULL;
+ BYTE RequestBuffer[2048];
+ DWORD RequestSize = 0;
+
+ *lpEntriesRead = 0;
+
+ //
+ // Convert the user name to unicode.
+ //
+
+ RtlInitUnicodeString( &uNtUserName, pszNtUserName );
+
+ //
+ // Set up the object attributes.
+ //
+
+ RtlInitUnicodeString( &uRdrName, RdrPrefix );
+
+ InitializeObjectAttributes( &ObjectAttributes,
+ &uRdrName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL );
+
+ ntstatus = NtOpenFile( &handleRdr,
+ SYNCHRONIZE | FILE_LIST_DIRECTORY,
+ &ObjectAttributes,
+ &IoStatusBlock,
+ FILE_SHARE_VALID_FLAGS,
+ FILE_SYNCHRONOUS_IO_NONALERT );
+
+ if ( !NT_SUCCESS(ntstatus) )
+ goto CleanExit;
+
+ //
+ // Fill out the request packet for FSCTL_NWR_NDS_LIST_TREES;
+ //
+
+ Request = ( PNWR_NDS_REQUEST_PACKET ) RequestBuffer;
+
+ Request->Parameters.ListTrees.NtUserNameLength = uNtUserName.Length;
+
+ RtlCopyMemory( &(Request->Parameters.ListTrees.NtUserName[0]),
+ uNtUserName.Buffer,
+ uNtUserName.Length );
+
+ RequestSize = sizeof( Request->Parameters.ListTrees ) +
+ uNtUserName.Length +
+ sizeof( DWORD );
+
+ ntstatus = NtFsControlFile( handleRdr,
+ NULL,
+ NULL,
+ NULL,
+ &IoStatusBlock,
+ FSCTL_NWR_NDS_LIST_TREES,
+ (PVOID) Request,
+ RequestSize,
+ (PVOID) Buffer,
+ BufferSize );
+
+ if ( NT_SUCCESS( ntstatus ))
+ {
+ ntstatus = IoStatusBlock.Status;
+ *lpEntriesRead = Request->Parameters.ListTrees.TreesReturned;
+ *lpUserLUID = Request->Parameters.ListTrees.UserLuid.LowPart;
+ }
+
+CleanExit:
+
+ if ( handleRdr != NULL )
+ NtClose( handleRdr );
+
+ return RtlNtStatusToDosError( ntstatus );
+}
+
+
diff --git a/private/nw/svcdlls/nwwks/client/nwutil.h b/private/nw/svcdlls/nwwks/client/nwutil.h
new file mode 100644
index 000000000..bf94a62c5
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/nwutil.h
@@ -0,0 +1,120 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ nwutil.h
+
+Abstract:
+
+ Common header for Workstation client-side code.
+
+Author:
+
+ Yi-Hsin Sung (yihsins) 25-Oct-1995
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+--*/
+
+#ifndef _NWUTIL_H_
+#define _NWUTIL_H_
+
+#define TREECHAR L'*'
+#define TWO_KB 2048
+
+BOOL
+NwIsNdsSyntax(
+ IN LPWSTR lpstrUnc
+);
+
+VOID
+NwAbbreviateUserName(
+ IN LPWSTR pszFullName,
+ OUT LPWSTR pszUserName
+);
+
+VOID
+NwMakePrettyDisplayName(
+ IN LPWSTR pszName
+);
+
+VOID
+NwExtractTreeName(
+ IN LPWSTR pszUNCPath,
+ OUT LPWSTR pszTreeName
+);
+
+
+VOID
+NwExtractServerName(
+ IN LPWSTR pszUNCPath,
+ OUT LPWSTR pszServerName
+);
+
+
+VOID
+NwExtractShareName(
+ IN LPWSTR pszUNCPath,
+ OUT LPWSTR pszShareName
+);
+
+DWORD
+NwIsServerInDefaultTree(
+ IN LPWSTR pszFullServerName,
+ OUT BOOL *pfInDefaultTree
+);
+
+DWORD
+NwIsServerOrTreeAttached(
+ IN LPWSTR pszServerName,
+ OUT BOOL *pfAttached,
+ OUT BOOL *pfAuthenticated
+);
+
+DWORD
+NwGetConnectionInformation(
+ IN LPWSTR pszName,
+ OUT LPBYTE Buffer,
+ IN DWORD BufferSize
+);
+
+DWORD
+NwGetConnectionStatus(
+ IN LPWSTR pszServerName,
+ IN OUT PDWORD ResumeKey,
+ OUT LPBYTE *Buffer,
+ OUT PDWORD EntriesRead
+);
+
+DWORD
+NwGetNdsVolumeInfo(
+ IN LPWSTR pszName,
+ OUT LPWSTR pszServerBuffer,
+ IN WORD wServerBufferSize, // in bytes
+ OUT LPWSTR pszVolumeBuffer,
+ IN WORD wVolumeBufferSize // in bytes
+);
+
+DWORD
+NwOpenAndGetTreeInfo(
+ LPWSTR pszNdsUNCPath,
+ HANDLE *phTreeConn,
+ DWORD *pdwOid
+);
+
+DWORD
+NwGetConnectedTrees(
+ IN LPWSTR pszNtUserName,
+ OUT LPBYTE Buffer,
+ IN DWORD BufferSize,
+ OUT LPDWORD lpEntriesRead,
+ OUT LPDWORD lpUserLUID
+);
+
+#endif // _NWUTIL_H_
diff --git a/private/nw/svcdlls/nwwks/client/port.c b/private/nw/svcdlls/nwwks/client/port.c
new file mode 100644
index 000000000..4cd287e5c
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/port.c
@@ -0,0 +1,449 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ port.c
+
+Abstract:
+
+ This module contains the code for port handling
+
+Author:
+
+ Yi-Hsin Sung (yihsins) 15-May-1993
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+
+#include <windef.h>
+#include <winbase.h>
+#include <winreg.h>
+#include <wingdi.h>
+#include <winspool.h>
+
+#include <splutil.h>
+#include <nwspl.h>
+
+//------------------------------------------------------------------
+//
+// Local Functions
+//
+//------------------------------------------------------------------
+
+HMODULE hSpoolssDll = NULL;
+FARPROC pfnSpoolssEnumPorts = NULL;
+
+HANDLE
+RevertToPrinterSelf(
+ VOID
+);
+
+BOOL
+ImpersonatePrinterClient(
+ HANDLE hToken
+);
+
+
+
+BOOL
+IsLocalMachine(
+ LPWSTR pszName
+)
+{
+ if ( !pszName || !*pszName )
+ return TRUE;
+
+ if ( *pszName == L'\\' && *(pszName+1) == L'\\')
+ if ( !lstrcmpi( pszName, szMachineName) )
+ return TRUE;
+
+ return FALSE;
+
+}
+
+
+BOOL
+PortExists(
+ LPWSTR pPortName,
+ LPDWORD pError
+)
+/* PortExists
+ *
+ * Calls EnumPorts to check whether the port name already exists.
+ * This asks every monitor, rather than just this one.
+ * The function will return TRUE if the specified port is in the list.
+ * If an error occurs, the return is FALSE and the variable pointed
+ * to by pError contains the return from GetLastError().
+ * The caller must therefore always check that *pError == NO_ERROR.
+ */
+{
+ DWORD cbNeeded;
+ DWORD cReturned;
+ DWORD cbPorts;
+ LPPORT_INFO_1W pPorts;
+ DWORD i;
+ BOOL Found = FALSE;
+
+ *pError = NO_ERROR;
+
+ if ( !hSpoolssDll )
+ {
+ if ( hSpoolssDll = LoadLibrary( L"SPOOLSS.DLL" ))
+ {
+ pfnSpoolssEnumPorts = GetProcAddress(hSpoolssDll, "EnumPortsW");
+ if ( !pfnSpoolssEnumPorts )
+ {
+ *pError = GetLastError();
+ FreeLibrary( hSpoolssDll );
+ hSpoolssDll = NULL;
+ }
+ }
+ else
+ {
+ *pError = GetLastError();
+ }
+ }
+
+ if ( !pfnSpoolssEnumPorts )
+ return FALSE;
+
+ if ( !(*pfnSpoolssEnumPorts)( NULL, 1, NULL, 0, &cbNeeded, &cReturned) )
+ {
+ if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER )
+ {
+ cbPorts = cbNeeded;
+
+ EnterCriticalSection( &NwSplSem );
+
+ pPorts = AllocNwSplMem( LMEM_ZEROINIT, cbPorts );
+ if ( pPorts )
+ {
+ if ( (*pfnSpoolssEnumPorts)( NULL, 1, (LPBYTE)pPorts, cbPorts,
+ &cbNeeded, &cReturned))
+ {
+ for ( i = 0; i < cReturned; i++)
+ {
+ if ( !lstrcmpi( pPorts[i].pName, pPortName) )
+ Found = TRUE;
+ }
+ }
+ else
+ {
+ *pError = GetLastError();
+ }
+
+ FreeNwSplMem( pPorts, cbPorts );
+ }
+ else
+ {
+ *pError = ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ LeaveCriticalSection( &NwSplSem );
+
+ }
+ else
+ {
+ *pError = GetLastError();
+ }
+ }
+
+ return Found;
+}
+
+
+
+BOOL
+PortKnown(
+ LPWSTR pPortName
+)
+{
+ PNWPORT pNwPort;
+
+ EnterCriticalSection( &NwSplSem );
+
+ pNwPort = pNwFirstPort;
+
+ while ( pNwPort )
+ {
+ if ( !lstrcmpi( pNwPort->pName, pPortName ) )
+ {
+ LeaveCriticalSection( &NwSplSem );
+ return TRUE;
+ }
+
+ pNwPort = pNwPort->pNext;
+ }
+
+ LeaveCriticalSection( &NwSplSem );
+ return FALSE;
+
+}
+
+
+
+PNWPORT
+CreatePortEntry(
+ LPWSTR pPortName
+)
+{
+ PNWPORT pNwPort, pPort;
+ DWORD cb = sizeof(NWPORT) + (wcslen(pPortName) + 1) * sizeof(WCHAR);
+
+ if ( pNwPort = AllocNwSplMem( LMEM_ZEROINIT, cb))
+ {
+ pNwPort->pName = wcscpy((LPWSTR)(pNwPort+1), pPortName);
+ pNwPort->cb = cb;
+ pNwPort->pNext = NULL;
+
+ EnterCriticalSection( &NwSplSem );
+
+ if ( pPort = pNwFirstPort )
+ {
+ while ( pPort->pNext )
+ pPort = pPort->pNext;
+
+ pPort->pNext = pNwPort;
+ }
+ else
+ {
+ pNwFirstPort = pNwPort;
+ }
+
+ LeaveCriticalSection( &NwSplSem );
+ }
+
+ return pNwPort;
+}
+
+
+
+BOOL
+DeletePortEntry(
+ LPWSTR pPortName
+)
+/*
+ Return TRUE when the port name is found and deleted. FALSE otherwise.
+*/
+{
+ BOOL fRetVal;
+ PNWPORT pPort, pPrevPort;
+
+ EnterCriticalSection( &NwSplSem );
+
+ pPort = pNwFirstPort;
+ while ( pPort && lstrcmpi(pPort->pName, pPortName))
+ {
+ pPrevPort = pPort;
+ pPort = pPort->pNext;
+ }
+
+ if (pPort)
+ {
+ if (pPort == pNwFirstPort)
+ {
+ pNwFirstPort = pPort->pNext;
+ }
+ else
+ {
+ pPrevPort->pNext = pPort->pNext;
+ }
+
+ FreeNwSplMem( pPort, pPort->cb );
+ fRetVal = TRUE;
+ }
+ else
+ {
+ fRetVal = FALSE;
+ }
+
+ LeaveCriticalSection( &NwSplSem );
+
+ return fRetVal;
+}
+
+
+
+VOID
+DeleteAllPortEntries(
+ VOID
+)
+{
+ PNWPORT pPort, pNextPort;
+
+ for ( pPort = pNwFirstPort; pPort; pPort = pNextPort )
+ {
+ pNextPort = pPort->pNext;
+ FreeNwSplMem( pPort, pPort->cb );
+ }
+}
+
+
+
+DWORD
+CreateRegistryEntry(
+ LPWSTR pPortName
+)
+{
+ DWORD err;
+ HANDLE hToken;
+ HKEY hkeyPath;
+ HKEY hkeyPortNames;
+
+ hToken = RevertToPrinterSelf();
+
+ err = RegCreateKeyEx( HKEY_LOCAL_MACHINE, pszRegistryPath, 0,
+ NULL, 0, KEY_WRITE, NULL, &hkeyPath, NULL );
+
+ if ( !err )
+ {
+ err = RegCreateKeyEx( hkeyPath, pszRegistryPortNames, 0,
+ NULL, 0, KEY_WRITE, NULL, &hkeyPortNames, NULL );
+
+ if ( !err )
+ {
+ err = RegSetValueEx( hkeyPortNames,
+ pPortName,
+ 0,
+ REG_SZ,
+ (LPBYTE) L"",
+ 0 );
+
+ RegCloseKey( hkeyPortNames );
+ }
+ else
+ {
+ KdPrint(("RegCreateKeyEx (%ws) failed: Error = %d\n",
+ pszRegistryPortNames, err ) );
+ }
+
+ RegCloseKey( hkeyPath );
+ }
+ else
+ {
+ KdPrint(("RegCreateKeyEx (%ws) failed: Error = %d\n",
+ pszRegistryPath, err ) );
+ }
+
+ if ( hToken )
+ ImpersonatePrinterClient(hToken);
+
+ return err;
+}
+
+
+
+DWORD
+DeleteRegistryEntry(
+ LPWSTR pPortName
+)
+{
+ DWORD err;
+ HANDLE hToken;
+ HKEY hkeyPath;
+ HKEY hkeyPortNames;
+
+ hToken = RevertToPrinterSelf();
+
+ err = RegOpenKeyEx( HKEY_LOCAL_MACHINE, pszRegistryPath, 0,
+ KEY_WRITE, &hkeyPath );
+
+ if ( !err )
+ {
+
+ err = RegOpenKeyEx( hkeyPath, pszRegistryPortNames, 0,
+ KEY_WRITE, &hkeyPortNames );
+
+ if ( !err )
+ {
+ err = RegDeleteValue( hkeyPortNames, pPortName );
+ RegCloseKey( hkeyPortNames );
+ }
+ else
+ {
+ KdPrint(("RegOpenKeyEx (%ws) failed: Error = %d\n",
+ pszRegistryPortNames, err ) );
+ }
+
+ RegCloseKey( hkeyPath );
+
+ }
+ else
+ {
+ KdPrint(("RegOpenKeyEx (%ws) failed: Error = %d\n",
+ pszRegistryPath, err ) );
+ }
+
+ if ( hToken )
+ ImpersonatePrinterClient(hToken);
+
+ return err;
+}
+
+
+
+HANDLE
+RevertToPrinterSelf(
+ VOID
+)
+{
+ HANDLE NewToken = NULL;
+ HANDLE OldToken;
+ NTSTATUS ntstatus;
+
+ ntstatus = NtOpenThreadToken(
+ NtCurrentThread(),
+ TOKEN_IMPERSONATE,
+ TRUE,
+ &OldToken
+ );
+
+ if ( !NT_SUCCESS(ntstatus) ) {
+ SetLastError(ntstatus);
+ return FALSE;
+ }
+
+ ntstatus = NtSetInformationThread(
+ NtCurrentThread(),
+ ThreadImpersonationToken,
+ (PVOID)&NewToken,
+ (ULONG)sizeof(HANDLE)
+ );
+
+ if ( !NT_SUCCESS(ntstatus) ) {
+ SetLastError(ntstatus);
+ return FALSE;
+ }
+
+ return OldToken;
+}
+
+
+
+BOOL
+ImpersonatePrinterClient(
+ HANDLE hToken
+)
+{
+ NTSTATUS ntstatus = NtSetInformationThread(
+ NtCurrentThread(),
+ ThreadImpersonationToken,
+ (PVOID) &hToken,
+ (ULONG) sizeof(HANDLE));
+
+ if ( !NT_SUCCESS(ntstatus) ) {
+ SetLastError( ntstatus );
+ return FALSE;
+ }
+
+ (VOID) NtClose(hToken);
+
+ return TRUE;
+}
diff --git a/private/nw/svcdlls/nwwks/client/print.ico b/private/nw/svcdlls/nwwks/client/print.ico
new file mode 100644
index 000000000..81ee083bc
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/print.ico
Binary files differ
diff --git a/private/nw/svcdlls/nwwks/client/provider.c b/private/nw/svcdlls/nwwks/client/provider.c
new file mode 100644
index 000000000..4f8bf2c30
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/provider.c
@@ -0,0 +1,3051 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ provider.c
+
+Abstract:
+
+ This module contains NetWare Network Provider code. It is the
+ client-side wrapper for APIs supported by the Workstation service.
+
+Author:
+
+ Rita Wong (ritaw) 15-Feb-1993
+
+Revision History:
+
+ Yi-Hsin Sung (yihsins) 10-July-1993
+ Moved all dialog handling to nwdlg.c
+
+--*/
+
+#include <nwclient.h>
+#include <nwsnames.h>
+#include <nwcanon.h>
+#include <validc.h>
+#include <nwevent.h>
+#include <ntmsv1_0.h>
+#include <nwdlg.h>
+#include <nwreg.h>
+#include <nwauth.h>
+#include <mpr.h> // WNFMT_ manifests
+#include <nwmisc.h>
+
+#ifndef NT1057
+#include <nwutil.h>
+#endif
+
+//-------------------------------------------------------------------//
+// //
+// Local Function Prototypes //
+// //
+//-------------------------------------------------------------------//
+
+STATIC
+BOOL
+NwpWorkstationStarted(
+ VOID
+ );
+
+STATIC
+DWORD
+NwpMapNameToUNC(
+ IN LPWSTR pszName,
+ OUT LPWSTR *ppszUNC
+ );
+
+STATIC
+VOID
+NwpGetUncInfo(
+ IN LPWSTR lpstrUnc,
+ OUT WORD * slashCount,
+ OUT BOOL * isNdsUnc
+ );
+
+STATIC
+DWORD
+NwpGetUncObjectName(
+ IN LPWSTR ContainerName
+ );
+
+//-------------------------------------------------------------------//
+// //
+// Global variables //
+// //
+//-------------------------------------------------------------------//
+
+#if DBG
+DWORD NwProviderTrace = 0;
+#endif
+
+
+DWORD
+APIENTRY
+NPGetCaps(
+ IN DWORD QueryVal
+ )
+/*++
+
+Routine Description:
+
+ This function returns the functionality supported by this network
+ provider.
+
+Arguments:
+
+ QueryVal - Supplies a value which determines the type of information
+ queried regarding the network provider's support in this area.
+
+Return Value:
+
+ Returns a value which indicates the level of support given by this
+ provider.
+
+--*/
+{
+
+#if DBG
+ IF_DEBUG(INIT) {
+ KdPrint(("\nNWPROVAU: NPGetCaps %lu\n", QueryVal));
+ }
+#endif
+
+ switch (QueryVal) {
+
+ case WNNC_SPEC_VERSION:
+ return 0x00040000;
+
+ case WNNC_NET_TYPE:
+ return WNNC_NET_NETWARE ;
+
+ case WNNC_USER:
+ return WNNC_USR_GETUSER;
+
+ case WNNC_CONNECTION:
+ return (WNNC_CON_ADDCONNECTION |
+ WNNC_CON_ADDCONNECTION3 |
+ WNNC_CON_CANCELCONNECTION |
+ WNNC_CON_GETPERFORMANCE |
+ WNNC_CON_GETCONNECTIONS);
+
+ case WNNC_ENUMERATION:
+ return ( WNNC_ENUM_GLOBAL |
+ WNNC_ENUM_CONTEXT |
+ WNNC_ENUM_LOCAL );
+
+ case WNNC_START:
+ if (NwpWorkstationStarted()) {
+ return 1;
+ }
+ else {
+ return 0xffffffff; // don't know
+ }
+
+ case WNNC_DIALOG:
+ return WNNC_DLG_FORMATNETWORKNAME
+#ifdef NT1057
+ ;
+#else
+ | WNNC_DLG_GETRESOURCEPARENT | WNNC_DLG_GETRESOURCEINFORMATION;
+#endif
+
+ //
+ // The rest are not supported by the NetWare provider
+ //
+ default:
+ return 0;
+ }
+
+}
+
+#define NW_EVENT_MESSAGE_FILE L"nwevent.dll"
+
+
+
+DWORD
+APIENTRY
+NPGetUser(
+ LPWSTR lpName,
+ LPWSTR lpUserName,
+ LPDWORD lpUserNameLen
+ )
+/*++
+
+Routine Description:
+
+ This is used to determine either the current default username, or the
+ username used to establish a network connection.
+
+Arguments:
+
+ lpName - Contains the name of the local device the caller is interested
+ in, or a network name that the user has made a connection to. This
+ may be NULL or the empty string if the caller is interested in the
+ name of the user currently logged on to the system. If a network
+ name is passed in, and the user is connected to that resource using
+ different names, it is possible that a provider cannont resolve
+ which username to return. In this case the provider may make an
+ arbitrary choice amonst the possible usernames.
+
+ lpUserName - Points to a buffer to receive the user name. this should
+ be a name that can be passed into the NPAddConnection or
+ NPAddConnection3 function to re-establish the connection with the
+ same user name.
+
+ lpBufferSize - This is used to specify the size (in characters) of the
+ buffer passed in. If the call fails because the buffer is not big
+ enough, this location will be used to return the required buffer size.
+
+Return Value:
+
+ WN_SUCCESS - If the call is successful. Otherwise, an error code is,
+ returned, which may include:
+
+ WN_NOT_CONNECTED - lpName not a redirected device nor a connected network
+ name.
+
+ WN_MORE_DATA - The buffer is too small.
+
+ WN_NO_NETWORK - Network not present.
+
+--*/
+{
+ DWORD status;
+ DWORD dwUserNameBufferSize = *lpUserNameLen * sizeof(WCHAR);
+ DWORD CharsRequired = 0;
+
+ if (lpName == NULL)
+ {
+ return WN_NOT_CONNECTED;
+ }
+
+ RtlZeroMemory( lpUserName, dwUserNameBufferSize );
+
+#if DBG
+ IF_DEBUG(CONNECT)
+ {
+ KdPrint(("\nNWPROVAU: NPGetUser %ws\n", lpName));
+ }
+#endif
+
+ RpcTryExcept
+ {
+ status = NwrGetUser(
+ NULL,
+ lpName,
+ (LPBYTE) lpUserName,
+ dwUserNameBufferSize,
+ &CharsRequired
+ );
+
+ if (status == WN_MORE_DATA)
+ {
+ //
+ // Output buffer too small.
+ //
+ *lpUserNameLen = CharsRequired;
+ }
+ }
+ RpcExcept(1)
+ {
+ status = NwpMapRpcError(RpcExceptionCode());
+ }
+ RpcEndExcept
+
+ if (status != NO_ERROR)
+ {
+ SetLastError(status);
+ }
+
+ return status;
+}
+
+
+DWORD
+APIENTRY
+NPAddConnection(
+ LPNETRESOURCEW lpNetResource,
+ LPWSTR lpPassword,
+ LPWSTR lpUserName
+ )
+/*++
+
+Routine Description:
+
+ This function creates a remote connection.
+
+Arguments:
+
+ lpNetResource - Supplies the NETRESOURCE structure which specifies
+ the local DOS device to map, the remote resource to connect to
+ and other attributes related to the connection.
+
+ lpPassword - Supplies the password to connect with.
+
+ lpUserName - Supplies the username to connect with.
+
+Return Value:
+
+ NO_ERROR - Successful.
+
+ WN_BAD_VALUE - Invalid value specifed in lpNetResource.
+
+ WN_BAD_NETNAME - Invalid remote resource name.
+
+ WN_BAD_LOCALNAME - Invalid local DOS device name.
+
+ WN_BAD_PASSWORD - Invalid password.
+
+ WN_ALREADY_CONNECTED - Local DOS device name is already in use.
+
+ Other network errors.
+
+--*/
+{
+ DWORD status = NO_ERROR;
+ LPWSTR pszRemoteName = NULL;
+
+ UCHAR EncodeSeed = NW_ENCODE_SEED3;
+ UNICODE_STRING PasswordStr;
+
+ LPWSTR CachedUserName = NULL ;
+ LPWSTR CachedPassword = NULL ;
+
+ PasswordStr.Length = 0;
+
+ status = NwpMapNameToUNC(
+ lpNetResource->lpRemoteName,
+ &pszRemoteName );
+
+ if (status != NO_ERROR)
+ {
+ SetLastError(status);
+ return status;
+ }
+
+#if DBG
+ IF_DEBUG(CONNECT) {
+ KdPrint(("\nNWPROVAU: NPAddConnection %ws\n", pszRemoteName));
+ }
+#endif
+
+ RpcTryExcept
+ {
+ if (lpNetResource->dwType != RESOURCETYPE_ANY &&
+ lpNetResource->dwType != RESOURCETYPE_DISK &&
+ lpNetResource->dwType != RESOURCETYPE_PRINT)
+ {
+ status = WN_BAD_VALUE;
+ }
+ else
+ {
+#ifdef NT1057
+ //
+ // no credentials specified, see if we have cached credentials
+ //
+ if (!lpPassword && !lpUserName)
+ {
+ (void) NwpRetrieveCachedCredentials(
+ pszRemoteName,
+ &CachedUserName,
+ &CachedPassword) ;
+
+ //
+ // these values will be NULL still if nothing found
+ //
+ lpPassword = CachedPassword ;
+ lpUserName = CachedUserName ;
+ }
+#endif
+
+ //
+ // Encode password.
+ //
+ RtlInitUnicodeString(&PasswordStr, lpPassword);
+ RtlRunEncodeUnicodeString(&EncodeSeed, &PasswordStr);
+
+ status = NwrCreateConnection(
+ NULL,
+ lpNetResource->lpLocalName,
+ pszRemoteName,
+ lpNetResource->dwType,
+ lpPassword,
+ lpUserName
+ );
+
+ if (CachedUserName)
+ {
+ (void)LocalFree((HLOCAL)CachedUserName);
+ }
+
+ if (CachedPassword)
+ {
+ RtlZeroMemory(CachedPassword,
+ wcslen(CachedPassword) *
+ sizeof(WCHAR)) ;
+ (void)LocalFree((HLOCAL)CachedPassword);
+ }
+ }
+ }
+ RpcExcept(1)
+ {
+ status = NwpMapRpcError(RpcExceptionCode());
+ }
+ RpcEndExcept
+
+ if (PasswordStr.Length != 0 && !CachedPassword)
+ {
+ //
+ // Restore password to original state
+ //
+ RtlRunDecodeUnicodeString(NW_ENCODE_SEED3, &PasswordStr);
+ }
+
+ if (status == ERROR_SHARING_PAUSED)
+ {
+ HMODULE MessageDll;
+ WCHAR Buffer[1024];
+ DWORD MessageLength;
+ DWORD err;
+ HKEY hkey;
+ LPWSTR pszProviderName = NULL;
+
+ //
+ // Load the netware message file DLL
+ //
+ MessageDll = LoadLibraryW(NW_EVENT_MESSAGE_FILE);
+
+ if (MessageDll == NULL)
+ {
+ goto ExitPoint ;
+ }
+
+ //
+ // Read the Network Provider Name.
+ //
+ // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
+ // \NWCWorkstation\networkprovider
+ //
+ err = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ NW_WORKSTATION_PROVIDER_PATH,
+ REG_OPTION_NON_VOLATILE, // options
+ KEY_READ, // desired access
+ &hkey
+ );
+
+ if ( !err )
+ {
+ //
+ // ignore the return code. if fail, pszProviderName is NULL
+ //
+ err = NwReadRegValue(
+ hkey,
+ NW_PROVIDER_VALUENAME,
+ &pszProviderName // free with LocalFree
+ );
+
+ RegCloseKey( hkey );
+ }
+
+ if (err)
+ {
+ (void) FreeLibrary(MessageDll);
+ goto ExitPoint ;
+ }
+
+ RtlZeroMemory(Buffer, sizeof(Buffer)) ;
+
+ //
+ // Get string from message file
+ //
+ MessageLength = FormatMessageW(
+ FORMAT_MESSAGE_FROM_HMODULE,
+ (LPVOID) MessageDll,
+ NW_LOGIN_DISABLED,
+ 0,
+ Buffer,
+ sizeof(Buffer) / sizeof(WCHAR),
+ NULL
+ );
+
+ if (MessageLength != 0)
+ {
+ status = WN_EXTENDED_ERROR ;
+ WNetSetLastErrorW(NW_LOGIN_DISABLED,
+ Buffer,
+ pszProviderName) ;
+ }
+
+ (void) LocalFree( (HLOCAL) pszProviderName );
+ (void) FreeLibrary(MessageDll);
+
+ }
+
+ExitPoint:
+
+ if (status != NO_ERROR)
+ {
+ SetLastError(status);
+ }
+
+ LocalFree( (HLOCAL) pszRemoteName );
+ return status;
+}
+
+
+DWORD
+APIENTRY
+NPAddConnection3(
+ HWND hwndOwner,
+ LPNETRESOURCEW lpNetResource,
+ LPWSTR lpPassword,
+ LPWSTR lpUserName,
+ DWORD dwConnFlags
+ )
+/*++
+
+Routine Description:
+
+ This function creates a remote connection.
+
+Arguments:
+
+ hwndOwner - Owner window handle for dialog boxes
+
+ lpNetResource - Supplies the NETRESOURCE structure which specifies
+ the local DOS device to map, the remote resource to connect to
+ and other attributes related to the connection.
+
+ lpPassword - Supplies the password to connect with.
+
+ lpUserName - Supplies the username to connect with.
+
+ dwConnFlags - CONNECT_UPDATE_PROFILE...
+
+Return Value:
+
+ NO_ERROR - Successful.
+
+ WN_BAD_VALUE - Invalid value specifed in lpNetResource.
+
+ WN_BAD_NETNAME - Invalid remote resource name.
+
+ WN_BAD_LOCALNAME - Invalid local DOS device name.
+
+ WN_BAD_PASSWORD - Invalid password.
+
+ WN_ALREADY_CONNECTED - Local DOS device name is already in use.
+
+ Other network errors.
+
+--*/
+{
+ DWORD err = NO_ERROR;
+ LPWSTR UserName = NULL;
+ LPWSTR Password = NULL;
+
+ if ( ( dwConnFlags & CONNECT_PROMPT )
+ && !( dwConnFlags & CONNECT_INTERACTIVE )
+ )
+ {
+ return WN_BAD_VALUE;
+ }
+
+ if ( !(dwConnFlags & CONNECT_PROMPT ))
+ {
+ err = NPAddConnection( lpNetResource,
+ lpPassword,
+ lpUserName );
+
+ if ( ( err == NO_ERROR )
+ || !( dwConnFlags & CONNECT_INTERACTIVE ) // Cannot popup dialog
+ )
+ {
+ return err;
+ }
+ }
+
+ for (;;)
+ {
+ if ( ( err != NO_ERROR ) // CONNECT_PROMPT
+ && ( err != WN_BAD_PASSWORD )
+ && ( err != WN_ACCESS_DENIED )
+ && ( err != ERROR_NO_SUCH_USER )
+ )
+ {
+ // Errors not related to access problems
+ break;
+ }
+
+ if ( UserName )
+ {
+ (void) LocalFree( UserName );
+ UserName = NULL;
+ }
+
+ if ( Password )
+ {
+ memset( Password, 0, wcslen(Password) * sizeof(WCHAR));
+ (void) LocalFree( Password );
+ Password = NULL;
+ }
+
+ //
+ // Put up dialog to get username
+ // and password.
+ //
+ err = NwpGetUserCredential( hwndOwner,
+ lpNetResource->lpRemoteName,
+ err,
+ lpUserName,
+ &UserName,
+ &Password );
+
+ if ( err != NO_ERROR )
+ break;
+
+ err = NPAddConnection( lpNetResource,
+ Password,
+ UserName );
+
+ if ( err == NO_ERROR )
+ {
+#if 0
+ if ( (UserName != NULL) && (Password != NULL))
+ {
+ // Checking UserName and Password is to make sure that
+ // we have prompted for password
+ (VOID) NwpCacheCredentials( lpNetResource->lpRemoteName,
+ UserName,
+ Password ) ;
+ }
+#endif
+ break;
+ }
+ }
+
+ if ( UserName )
+ (void) LocalFree( UserName );
+
+ if ( Password )
+ {
+ memset( Password, 0, wcslen(Password) * sizeof(WCHAR));
+ (void) LocalFree( Password );
+ }
+
+ return err;
+}
+
+
+
+DWORD
+APIENTRY
+NPCancelConnection(
+ LPWSTR lpName,
+ BOOL fForce
+ )
+/*++
+
+Routine Description:
+
+ This function deletes a remote connection.
+
+Arguments:
+
+ lpName - Supplies the local DOS device, or the remote resource name
+ if it is a UNC connection to delete.
+
+ fForce - Supplies the force level to break the connection. TRUE means
+ to forcefully delete the connection, FALSE means end the connection
+ only if there are no opened files.
+
+Return Value:
+
+ NO_ERROR - Successful.
+
+ WN_BAD_NETNAME - Invalid remote resource name.
+
+ WN_NOT_CONNECTED - Connection could not be found.
+
+ WN_OPEN_FILES - fForce is FALSE and there are opened files on the
+ connection.
+
+ Other network errors.
+
+--*/
+{
+ DWORD status = NO_ERROR;
+ LPWSTR pszName = NULL;
+
+ //
+ // We only need to map remote resource name
+ //
+
+ if ( NwLibValidateLocalName( lpName ) != NO_ERROR )
+ {
+ status = NwpMapNameToUNC(
+ lpName,
+ &pszName
+ );
+
+ if (status != NO_ERROR) {
+ SetLastError(status);
+ return status;
+ }
+ }
+
+#if DBG
+ IF_DEBUG(CONNECT) {
+ KdPrint(("\nNWPROVAU: NPCancelConnection %ws, Force %u\n",
+ pszName? pszName : lpName, fForce));
+ }
+#endif
+
+ RpcTryExcept {
+
+ status = NwrDeleteConnection(
+ NULL,
+ pszName? pszName : lpName,
+ (DWORD) fForce
+ );
+
+ }
+ RpcExcept(1) {
+ status = NwpMapRpcError(RpcExceptionCode());
+ }
+ RpcEndExcept
+
+ if (status != NO_ERROR) {
+ SetLastError(status);
+ }
+
+ LocalFree( (HLOCAL) pszName );
+ return status;
+
+}
+
+
+
+DWORD
+APIENTRY
+NPGetConnection(
+ LPWSTR lpLocalName,
+ LPWSTR lpRemoteName,
+ LPDWORD lpnBufferLen
+ )
+/*++
+
+Routine Description:
+
+ This function returns the remote resource name for a given local
+ DOS device.
+
+Arguments:
+
+ lpLocalName - Supplies the local DOS device to look up.
+
+ lpRemoteName - Output buffer to receive the remote resource name
+ mapped to lpLocalName.
+
+ lpnBufferLen - On input, supplies length of the lpRemoteName buffer
+ in number of characters. On output, if error returned is
+ WN_MORE_DATA, receives the number of characters required of
+ the output buffer to hold the output string.
+
+Return Value:
+
+ NO_ERROR - Successful.
+
+ WN_BAD_LOCALNAME - Invalid local DOS device.
+
+ WN_NOT_CONNECTED - Connection could not be found.
+
+ WN_MORE_DATA - Output buffer is too small.
+
+ Other network errors.
+
+--*/
+{
+
+ DWORD status = NO_ERROR;
+ DWORD CharsRequired;
+
+#if DBG
+ IF_DEBUG(CONNECT) {
+ KdPrint(("\nNWPROVAU: NPGetConnection %ws\n", lpLocalName));
+ }
+#endif
+
+ RpcTryExcept {
+
+ if (lpRemoteName && *lpnBufferLen)
+ *lpRemoteName = 0 ;
+
+ status = NwrQueryServerResource(
+ NULL,
+ lpLocalName,
+ (*lpnBufferLen == 0? NULL : lpRemoteName),
+ *lpnBufferLen,
+ &CharsRequired
+ );
+
+ if (status == ERROR_INSUFFICIENT_BUFFER)
+ status = WN_MORE_DATA;
+
+ if (status == WN_MORE_DATA) {
+ *lpnBufferLen = CharsRequired;
+ }
+
+ }
+ RpcExcept(1) {
+ status = NwpMapRpcError(RpcExceptionCode());
+ }
+ RpcEndExcept
+
+ if (status != NO_ERROR) {
+ SetLastError(status);
+ }
+
+#if DBG
+ IF_DEBUG(CONNECT) {
+ KdPrint(("\nNWPROVAU: NPGetConnection returns %lu\n", status));
+ if (status == NO_ERROR) {
+ KdPrint((" %ws, BufferLen %lu, CharsRequired %lu\n", lpRemoteName, *lpnBufferLen, CharsRequired));
+
+ }
+ }
+#endif
+
+ return status;
+}
+
+
+DWORD
+APIENTRY
+NPGetConnectionPerformance(
+ LPCWSTR lpRemoteName,
+ LPNETCONNECTINFOSTRUCT lpNetConnectInfo
+ )
+/*++
+
+Routine Description:
+
+ This function returns information about the expected performance of a
+ connection used to access a network resource. The request can only be
+ for a network resource to which there is currently a connection.
+
+Arguments:
+
+ lpRemoteName - Contains the local name or remote name for a resource
+ for which a connection exists.
+
+ lpNetConnectInfo - This is a pointer to a NETCONNECTINFOSTRUCT structure
+ which is to be filled if the connection performance
+ of connection lpRemoteName can be determined.
+
+Return Value:
+
+ NO_ERROR - Successful.
+
+ WN_NOT_CONNECTED - Connection could not be found.
+
+ WN_NONETWORK - Network is not present.
+
+ Other network errors.
+
+--*/
+{
+ DWORD status = NO_ERROR;
+ LPWSTR pszRemoteName;
+
+ if ( lpNetConnectInfo == NULL )
+ {
+ status = ERROR_INVALID_PARAMETER;
+ SetLastError(status);
+ return status;
+ }
+
+ pszRemoteName = (LPWSTR) LocalAlloc( LMEM_ZEROINIT,
+ ( wcslen(lpRemoteName) + 1 ) *
+ sizeof(WCHAR) );
+
+ if ( pszRemoteName == NULL )
+ {
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ SetLastError(status);
+ return status;
+ }
+
+ wcscpy( pszRemoteName, lpRemoteName );
+ _wcsupr( pszRemoteName );
+
+#if DBG
+ IF_DEBUG(CONNECT) {
+ KdPrint(("\nNWPROVAU: NPGetConnectionPerformance %ws\n", pszRemoteName));
+ }
+#endif
+
+ RpcTryExcept {
+
+ status = NwrGetConnectionPerformance(
+ NULL,
+ pszRemoteName,
+ (LPBYTE) lpNetConnectInfo,
+ sizeof(NETCONNECTINFOSTRUCT) );
+
+ }
+ RpcExcept(1)
+ {
+ status = NwpMapRpcError(RpcExceptionCode());
+ }
+ RpcEndExcept
+
+ if (status != NO_ERROR)
+ {
+ SetLastError(status);
+ }
+
+ LocalFree( (HLOCAL) pszRemoteName );
+ return status;
+}
+
+
+
+DWORD
+APIENTRY
+NPGetUniversalName(
+#ifdef NT1057
+ LPWSTR lpLocalPath,
+#else
+ LPCWSTR lpLocalPath,
+#endif
+ DWORD dwInfoLevel,
+ LPVOID lpBuffer,
+ LPDWORD lpBufferSize
+ )
+/*++
+
+Routine Description:
+
+ This function returns the universal resource name for a given local
+ path.
+
+Arguments:
+
+ lpLocalPath - Supplies the local DOS Path to look up.
+
+ dwInfoLevel - Info level requested.
+
+ lpBuffer - Output buffer to receive the appropruatye structure.
+
+ lpBufferLen - On input, supplies length of the buffer in number of
+ bytes. On output, if error returned is WN_MORE_DATA, receives
+ the number of bytes required of the output buffer.
+
+Return Value:
+
+ NO_ERROR - Successful.
+
+ WN_BAD_LOCALNAME - Invalid local DOS device.
+
+ WN_NOT_CONNECTED - Connection could not be found.
+
+ WN_MORE_DATA - Output buffer is too small.
+
+ Other network errors.
+
+--*/
+{
+
+ DWORD status = NO_ERROR;
+ DWORD dwCharsRequired = MAX_PATH + 1 ;
+ DWORD dwBytesNeeded ;
+ DWORD dwLocalLength ;
+ LPWSTR lpRemoteBuffer ;
+ WCHAR szDrive[3] ;
+
+ //
+ // check for bad info level
+ //
+ if ((dwInfoLevel != UNIVERSAL_NAME_INFO_LEVEL) &&
+ (dwInfoLevel != REMOTE_NAME_INFO_LEVEL))
+ {
+ return WN_BAD_VALUE ;
+ }
+
+ //
+ // check for bad pointers
+ //
+ if (!lpLocalPath || !lpBuffer || !lpBufferSize)
+ {
+ return WN_BAD_POINTER ;
+ }
+
+ //
+ // local path must at least have "X:"
+ //
+ if (((dwLocalLength = wcslen(lpLocalPath)) < 2) ||
+ (lpLocalPath[1] != L':') ||
+ ((dwLocalLength > 2) && (lpLocalPath[2] != L'\\')))
+ {
+ return WN_BAD_VALUE ;
+ }
+
+ //
+ // preallocate some memory
+ //
+ if (!(lpRemoteBuffer = (LPWSTR) LocalAlloc(
+ LPTR,
+ dwCharsRequired * sizeof(WCHAR))))
+ {
+ status = GetLastError() ;
+ goto ErrorExit ;
+ }
+
+ szDrive[2] = 0 ;
+ wcsncpy(szDrive, lpLocalPath, 2) ;
+
+ //
+ // get the remote path by calling the existing API
+ //
+ status = NPGetConnection(
+ szDrive,
+ lpRemoteBuffer,
+ &dwCharsRequired) ;
+
+ if (status == WN_MORE_DATA)
+ {
+ //
+ // reallocate the correct size
+ //
+
+ if (!(lpRemoteBuffer = (LPWSTR) LocalReAlloc(
+ (HLOCAL) lpRemoteBuffer,
+ dwCharsRequired * sizeof(WCHAR),
+ LMEM_MOVEABLE)))
+ {
+ status = GetLastError() ;
+ goto ErrorExit ;
+ }
+
+ status = NPGetConnection(
+ szDrive,
+ lpRemoteBuffer,
+ &dwCharsRequired) ;
+ }
+
+ if (status != WN_SUCCESS)
+ {
+ goto ErrorExit ;
+ }
+
+ //
+ // at minimum we will need this size of the UNC name
+ // the -2 is because we loose the drive letter & colon.
+ //
+ dwBytesNeeded = (wcslen(lpRemoteBuffer) +
+ dwLocalLength - 2 + 1) * sizeof(WCHAR) ;
+
+ switch (dwInfoLevel)
+ {
+ case UNIVERSAL_NAME_INFO_LEVEL:
+ {
+ LPUNIVERSAL_NAME_INFO lpUniversalNameInfo ;
+
+ //
+ // calculate how many bytes we really need
+ //
+ dwBytesNeeded += sizeof(UNIVERSAL_NAME_INFO) ;
+
+ if (*lpBufferSize < dwBytesNeeded)
+ {
+ *lpBufferSize = dwBytesNeeded ;
+ status = WN_MORE_DATA ;
+ break ;
+ }
+
+ //
+ // now we are all set. just stick the data in the buffer
+ //
+ lpUniversalNameInfo = (LPUNIVERSAL_NAME_INFO) lpBuffer ;
+
+ lpUniversalNameInfo->lpUniversalName = (LPWSTR)
+ (((LPBYTE)lpBuffer) + sizeof(UNIVERSAL_NAME_INFO)) ;
+ wcscpy(lpUniversalNameInfo->lpUniversalName,
+ lpRemoteBuffer) ;
+ wcscat(lpUniversalNameInfo->lpUniversalName,
+ lpLocalPath+2) ;
+
+ break ;
+ }
+
+ case REMOTE_NAME_INFO_LEVEL :
+ {
+ LPREMOTE_NAME_INFO lpRemoteNameInfo ;
+
+ //
+ // calculate how many bytes we really need
+ //
+ dwBytesNeeded *= 2 ; // essentially twice the info + terminator
+ dwBytesNeeded += (sizeof(REMOTE_NAME_INFO) + sizeof(WCHAR)) ;
+
+ if (*lpBufferSize < dwBytesNeeded)
+ {
+ *lpBufferSize = dwBytesNeeded ;
+ status = WN_MORE_DATA ;
+ break ;
+ }
+
+ //
+ // now we are all set. just stick the data in the buffer
+ //
+ lpRemoteNameInfo = (LPREMOTE_NAME_INFO) lpBuffer ;
+
+ lpRemoteNameInfo->lpUniversalName = (LPWSTR)
+ (((LPBYTE)lpBuffer) + sizeof(REMOTE_NAME_INFO)) ;
+ wcscpy(lpRemoteNameInfo->lpUniversalName,
+ lpRemoteBuffer) ;
+ wcscat(lpRemoteNameInfo->lpUniversalName,
+ lpLocalPath+2) ;
+
+ lpRemoteNameInfo->lpConnectionName =
+ lpRemoteNameInfo->lpUniversalName +
+ wcslen(lpRemoteNameInfo->lpUniversalName) + 1 ;
+ wcscpy(lpRemoteNameInfo->lpConnectionName,
+ lpRemoteBuffer) ;
+
+ lpRemoteNameInfo->lpRemainingPath =
+ lpRemoteNameInfo->lpConnectionName +
+ wcslen(lpRemoteNameInfo->lpConnectionName) + 1 ;
+ wcscpy(lpRemoteNameInfo->lpRemainingPath,
+ lpLocalPath+2) ;
+
+ break ;
+ }
+
+ default:
+ //
+ // yikes!
+ //
+ status = WN_BAD_VALUE ;
+ ASSERT(FALSE);
+ }
+
+ErrorExit:
+
+ if (lpRemoteBuffer)
+ {
+ (void) LocalFree((HLOCAL)lpRemoteBuffer) ;
+ }
+ return status;
+}
+
+
+
+DWORD
+APIENTRY
+NPOpenEnum(
+ DWORD dwScope,
+ DWORD dwType,
+ DWORD dwUsage,
+ LPNETRESOURCEW lpNetResource,
+ LPHANDLE lphEnum
+ )
+/*++
+
+Routine Description:
+
+ This function initiates an enumeration of either connections, or
+ browsing of network resource.
+
+Arguments:
+
+ dwScope - Supplies the category of enumeration to do--either
+ connection or network browsing.
+
+ dwType - Supplies the type of resource to get--either disk,
+ print, or it does not matter.
+
+ dwUsage - Supplies the object type to get--either container,
+ or connectable usage.
+
+ lpNetResource - Supplies, in the lpRemoteName field, the container
+ name to enumerate under.
+
+ lphEnum - Receives the resumable context handle to be used on all
+ subsequent calls to get the list of objects under the container.
+
+Return Value:
+
+ NO_ERROR - Successful.
+
+ WN_BAD_VALUE - Either the dwScope, dwType, or the dwUsage specified
+ is not acceptable.
+
+ WN_BAD_NETNAME - Invalid remote resource name.
+
+ WN_NOT_CONTAINER - Remote resource name is not a container.
+
+ Other network errors.
+
+--*/
+{
+ DWORD status = NO_ERROR;
+
+#if DBG
+ IF_DEBUG(ENUM)
+ {
+ KdPrint(("\nNWPROVAU: NPOpenEnum\n"));
+ }
+#endif
+
+
+ RpcTryExcept
+ {
+ if ( ( dwType & RESOURCETYPE_DISK ) ||
+ ( dwType & RESOURCETYPE_PRINT ) ||
+ ( dwType == RESOURCETYPE_ANY ) )
+ {
+ switch ( dwScope )
+ {
+ case RESOURCE_CONNECTED:
+
+ status = NwrOpenEnumConnections( NULL,
+ dwType,
+ (LPNWWKSTA_CONTEXT_HANDLE) lphEnum );
+ break;
+
+ case RESOURCE_CONTEXT:
+
+ status = NwrOpenEnumContextInfo( NULL,
+ dwType,
+ (LPNWWKSTA_CONTEXT_HANDLE) lphEnum );
+ break;
+
+ case RESOURCE_GLOBALNET:
+
+ if ( lpNetResource == NULL )
+ {
+ //
+ // Enumerating servers and NDS trees
+ //
+ if ( dwUsage & RESOURCEUSAGE_CONTAINER || dwUsage == 0 )
+ {
+ status = NwrOpenEnumServersAndNdsTrees( NULL,
+ (LPNWWKSTA_CONTEXT_HANDLE) lphEnum );
+ }
+ else
+ {
+ //
+ // There is no such thing as a connectable server
+ // object.
+ //
+ status = WN_BAD_VALUE;
+ }
+ }
+ else
+ {
+ BOOL IsEnumVolumes = TRUE;
+ LPWSTR pszRemoteName = NULL;
+ WORD slashCount;
+ BOOL isNdsUnc;
+
+ NwpGetUncInfo( lpNetResource->lpRemoteName,
+ &slashCount,
+ &isNdsUnc );
+
+ //
+ // Either enumerating volumes, directories, or NDS subtrees
+ //
+
+ if ( dwUsage & RESOURCEUSAGE_CONNECTABLE ||
+ dwUsage & RESOURCEUSAGE_CONTAINER ||
+ dwUsage == 0 )
+ {
+ LPWSTR tempStrPtr = lpNetResource->lpRemoteName;
+ DWORD dwClassType = 0;
+
+ if ( tempStrPtr[0] == L' ' &&
+ tempStrPtr[1] == L'\\' &&
+ tempStrPtr[2] == L'\\' )
+ tempStrPtr = &tempStrPtr[1];
+
+ if ( lpNetResource->dwDisplayType == RESOURCEDISPLAYTYPE_TREE )
+ {
+ if ( ( dwType == RESOURCETYPE_ANY ) ||
+ ( ( dwType & RESOURCETYPE_DISK ) &&
+ ( dwType & RESOURCETYPE_PRINT ) ) )
+ {
+ status = NwrOpenEnumNdsSubTrees_Any( NULL,
+ tempStrPtr,
+ NULL,
+ (LPNWWKSTA_CONTEXT_HANDLE) lphEnum );
+ }
+ else if ( dwType & RESOURCETYPE_DISK )
+ {
+ status = NwrOpenEnumNdsSubTrees_Disk( NULL,
+ tempStrPtr,
+ NULL,
+ (LPNWWKSTA_CONTEXT_HANDLE) lphEnum );
+ }
+ else if ( dwType & RESOURCETYPE_PRINT )
+ {
+ status = NwrOpenEnumNdsSubTrees_Print( NULL,
+ tempStrPtr,
+ NULL,
+ (LPNWWKSTA_CONTEXT_HANDLE) lphEnum );
+ }
+ else
+ {
+ KdPrint(("NWOpenEnum: Unhandled dwType %lu\n", dwType));
+ }
+ }
+ else if (
+ ( slashCount < 4 ) &&
+ ( ( dwType == RESOURCETYPE_ANY ) ||
+ ( ( dwType & RESOURCETYPE_DISK ) &&
+ ( dwType & RESOURCETYPE_PRINT ) ) ) &&
+ ( ( status = NwrOpenEnumNdsSubTrees_Any( NULL,
+ tempStrPtr,
+ &dwClassType,
+ (LPNWWKSTA_CONTEXT_HANDLE) lphEnum ) )
+ ==NO_ERROR )
+ )
+ {
+ status = NO_ERROR;
+ }
+ else if (
+ ( slashCount < 4 ) &&
+ ( dwType & RESOURCETYPE_DISK ) &&
+ ( ( status = NwrOpenEnumNdsSubTrees_Disk( NULL,
+ tempStrPtr,
+ &dwClassType,
+ (LPNWWKSTA_CONTEXT_HANDLE) lphEnum ) )
+ ==NO_ERROR )
+ )
+ {
+ status = NO_ERROR;
+ }
+ else if (
+ ( slashCount < 4 ) &&
+ ( dwType & RESOURCETYPE_PRINT ) &&
+ ( ( status = NwrOpenEnumNdsSubTrees_Print( NULL,
+ tempStrPtr,
+ &dwClassType,
+ (LPNWWKSTA_CONTEXT_HANDLE) lphEnum ) )
+ ==NO_ERROR )
+ )
+ {
+ status = NO_ERROR;
+ }
+ else if (
+ (slashCount < 4
+ &&
+ (status == ERROR_NETWORK_ACCESS_DENIED
+ ||
+ status == ERROR_GEN_FAILURE
+ ||
+ status == ERROR_ACCESS_DENIED
+ ||
+ status == ERROR_BAD_NETPATH
+ ||
+ status == WN_BAD_NETNAME
+ ||
+ status == ERROR_INVALID_NAME))
+ ||
+ ( slashCount > 3 && status == NO_ERROR )
+ )
+ {
+ if (( status == ERROR_NETWORK_ACCESS_DENIED ) &&
+ ( dwClassType == CLASS_TYPE_NCP_SERVER ))
+ {
+ status = NO_ERROR;
+ isNdsUnc = TRUE;
+ IsEnumVolumes = TRUE;
+ }
+ else if ( ( status == ERROR_NETWORK_ACCESS_DENIED ) &&
+ ( ( dwClassType == CLASS_TYPE_VOLUME ) ||
+ ( dwClassType == CLASS_TYPE_DIRECTORY_MAP ) ) )
+ {
+ status = NO_ERROR;
+ isNdsUnc = TRUE;
+ IsEnumVolumes = FALSE;
+ }
+ else
+ {
+ //
+ // A third backslash means that we want to
+ // enumerate the directories.
+ //
+
+ if ( isNdsUnc && slashCount > 3 )
+ IsEnumVolumes = FALSE;
+
+ if ( !isNdsUnc && slashCount > 2 )
+ IsEnumVolumes = FALSE;
+
+ if ( lpNetResource->dwDisplayType == RESOURCEDISPLAYTYPE_SHARE )
+ IsEnumVolumes = FALSE;
+ }
+
+ status = NwpMapNameToUNC( tempStrPtr,
+ &pszRemoteName );
+
+ if ( status == NO_ERROR )
+ {
+ if ( IsEnumVolumes )
+ {
+ LPWSTR pszServerName = pszRemoteName;
+
+ // BUGBUG - The following 10 lines are a hack to
+ // allow the provider to browse past the CN=<server>
+ // object in an NDS tree.
+ if ( slashCount == 3 && isNdsUnc == TRUE )
+ {
+ pszServerName = (LPWSTR)
+ NwpGetUncObjectName( pszRemoteName );
+
+ if ( pszServerName == NULL )
+ pszServerName = pszRemoteName;
+ }
+ else if ( dwUsage & RESOURCEUSAGE_ATTACHED )
+ {
+#ifndef NT1057
+ // This is a bindery server.
+ // Return WN_NOT_AUTHENTICATED if
+ // we are not already attached so
+ // that clients ( explorer ) will
+ // do NPAddConnection3 to make
+ // a connection to the server.
+ BOOL fAttached;
+ BOOL fAuthenticated;
+
+ status = NwIsServerOrTreeAttached(
+ pszServerName + 2,
+ &fAttached,
+ &fAuthenticated );
+
+ if ( status != NO_ERROR )
+ break;
+
+ if ( !fAttached || !fAuthenticated)
+ {
+ // See if the server belongs to
+ // our provider.
+ status = NwrOpenEnumVolumes(
+ NULL,
+ pszServerName,
+ (LPNWWKSTA_CONTEXT_HANDLE) lphEnum );
+
+ if ( status == NO_ERROR )
+ {
+ // The server belongs to us.
+ // Close the handle and
+ // return not attached if
+ // callee passed in dwUsage
+ // flag:
+ // RESOURCEUSAGE_ATTACHED.
+ // Note: handle will be null
+ // after return from
+ // NwrCloseEnum
+
+ NwrCloseEnum( (LPNWWKSTA_CONTEXT_HANDLE) lphEnum );
+
+ status = WN_NOT_AUTHENTICATED;
+ }
+ else
+ {
+ // else the server does not
+ // belong to us.
+ status = WN_BAD_NETNAME;
+ }
+ break;
+ }
+#endif
+ } // else, this is a bindery server and
+ // client does not care whether we
+ // are bindery authenticated.
+
+ if ( ( dwType == RESOURCETYPE_ANY ) ||
+ ( ( dwType & RESOURCETYPE_DISK ) &&
+ ( dwType & RESOURCETYPE_PRINT ) ) )
+ {
+ status = NwrOpenEnumVolumesQueues(
+ NULL,
+ pszServerName,
+ (LPNWWKSTA_CONTEXT_HANDLE) lphEnum );
+ }
+ else if ( dwType & RESOURCETYPE_DISK )
+ {
+ status = NwrOpenEnumVolumes(
+ NULL,
+ pszServerName,
+ (LPNWWKSTA_CONTEXT_HANDLE) lphEnum );
+ }
+ else if ( dwType & RESOURCETYPE_PRINT )
+ {
+ status = NwrOpenEnumQueues(
+ NULL,
+ pszServerName,
+ (LPNWWKSTA_CONTEXT_HANDLE) lphEnum );
+ }
+ }
+ else
+ {
+ LPWSTR CachedUserName = NULL ;
+ LPWSTR CachedPassword = NULL ;
+
+#ifdef NT1057 // Make OpenEnum not interactive on SUR
+ (void) NwpRetrieveCachedCredentials( pszRemoteName,
+ &CachedUserName,
+ &CachedPassword );
+
+#endif
+ status = NwrOpenEnumDirectories(
+ NULL,
+ pszRemoteName,
+ CachedUserName,
+ CachedPassword,
+ (LPNWWKSTA_CONTEXT_HANDLE) lphEnum );
+
+#ifndef NT1057 // Make OpenEnum not interactive on SUR
+ if ( (status == ERROR_INVALID_PASSWORD)
+ || (status == ERROR_NO_SUCH_USER )
+ )
+ {
+ status = WN_NOT_AUTHENTICATED;
+ break;
+ }
+
+#else
+ if ( CachedUserName )
+ {
+ (void) LocalFree( (HLOCAL) CachedUserName );
+ }
+
+ if ( CachedPassword )
+ {
+ RtlZeroMemory( CachedPassword,
+ wcslen(CachedPassword) *
+ sizeof( WCHAR ) );
+
+ (void) LocalFree( ( HLOCAL ) CachedPassword );
+ }
+
+ if ( ( status == ERROR_INVALID_PASSWORD ) ||
+ ( status == ERROR_NO_SUCH_USER ) )
+ {
+ LPWSTR UserName;
+ LPWSTR Password;
+ LPWSTR TmpPtr;
+
+ //
+ // Put up dialog to get username
+ // and password.
+ //
+ status = NwpGetUserCredential( NULL,
+ tempStrPtr,
+ status,
+ NULL,
+ &UserName,
+ &Password);
+
+ if ( status == NO_ERROR )
+ {
+ status = NwrOpenEnumDirectories(
+ NULL,
+ pszRemoteName,
+ UserName,
+ Password,
+ (LPNWWKSTA_CONTEXT_HANDLE) lphEnum );
+
+ if ( status == NO_ERROR )
+ {
+ status = NwpCacheCredentials(
+ pszRemoteName,
+ UserName,
+ Password ) ;
+ }
+
+ (void) LocalFree( UserName );
+
+ //
+ // Clear the password
+ //
+ TmpPtr = Password;
+ while ( *TmpPtr != 0 )
+ *TmpPtr++ = 0;
+
+ (void) LocalFree( Password );
+ }
+ else if ( status == ERROR_WINDOW_NOT_DIALOG )
+ {
+ //
+ // Caller is not a GUI app.
+ //
+ status = ERROR_INVALID_PASSWORD;
+ }
+ else if ( status == WN_CANCEL )
+ {
+ //
+ // Cancel was pressed but we still
+ // have to return success or MPR
+ // will popup the error. Return
+ // a bogus enum handle.
+ //
+ *lphEnum = (HANDLE) 0xFFFFFFFF;
+ status = NO_ERROR;
+ }
+ }
+#endif
+ }
+ }
+ else
+ {
+ status = WN_BAD_NETNAME;
+ }
+ }
+ }
+ else
+ {
+ status = WN_BAD_VALUE;
+ }
+
+ if ( pszRemoteName != NULL )
+ LocalFree( (HLOCAL) pszRemoteName );
+ }
+
+ break;
+
+ default:
+ KdPrint(("NWPROVIDER: Invalid dwScope %lu\n", dwScope));
+ status = WN_BAD_VALUE;
+ } // end switch
+ }
+ else
+ {
+ status = WN_BAD_VALUE;
+ }
+ }
+ RpcExcept( 1 )
+ {
+ status = NwpMapRpcError(RpcExceptionCode());
+ }
+ RpcEndExcept
+
+ if ( status == ERROR_FILE_NOT_FOUND )
+ status = WN_BAD_NETNAME;
+
+ if ( status != NO_ERROR )
+ {
+ SetLastError( status );
+ }
+
+ return status;
+}
+
+
+DWORD
+APIENTRY
+NPEnumResource(
+ HANDLE hEnum,
+ LPDWORD lpcCount,
+ LPVOID lpBuffer,
+ LPDWORD lpBufferSize
+ )
+/*++
+
+Routine Description:
+
+ This function returns a lists of objects within the container
+ specified by the enumeration context handle.
+
+Arguments:
+
+ hEnum - Supplies the resumable enumeration context handle.
+
+ NOTE: If this value is 0xFFFFFFFF, it is not a context
+ handle and this routine is required to return
+ WN_NO_MORE_ENTRIES. This hack is to handle the
+ case where the user cancelled out of the network
+ credential dialog on NwrOpenEnumDirectories and we
+ cannot return an error there or we generate an error
+ popup.
+
+ lpcCount - On input, supplies the number of entries to get.
+ On output, if NO_ERROR is returned, receives the number
+ of entries NETRESOURCE returned in lpBuffer.
+
+ lpBuffer - Receives an array of NETRESOURCE entries, each
+ entry describes an object within the container.
+
+ lpBufferSize - On input, supplies the size of lpBuffer in
+ bytes. On output, if WN_MORE_DATA is returned, receives
+ the number of bytes needed in the buffer to get the
+ next entry.
+
+Return Value:
+
+
+ NO_ERROR - Successfully returned at least one entry.
+
+ WN_NO_MORE_ENTRIES - Reached the end of enumeration and nothing
+ is returned.
+
+ WN_MORE_DATA - lpBuffer is too small to even get one entry.
+
+ WN_BAD_HANDLE - The enumeration handle is invalid.
+
+ Other network errors.
+
+--*/
+{
+ DWORD status = NO_ERROR;
+ DWORD BytesNeeded = 0;
+ DWORD EntriesRead = 0;
+
+#if DBG
+ IF_DEBUG(ENUM) {
+ KdPrint(("\nNWPROVAU: NPEnumResource\n"));
+ }
+#endif
+
+ RpcTryExcept {
+
+ if (hEnum == (HANDLE) 0xFFFFFFFF) {
+ status = WN_NO_MORE_ENTRIES;
+ goto EndOfTry;
+ }
+
+ status = NwrEnum(
+ (NWWKSTA_CONTEXT_HANDLE) hEnum,
+ *lpcCount,
+ (LPBYTE) lpBuffer,
+ *lpBufferSize,
+ &BytesNeeded,
+ &EntriesRead
+ );
+
+ if (status == WN_MORE_DATA) {
+
+ //
+ // Output buffer too small to fit a single entry.
+ //
+ *lpBufferSize = BytesNeeded;
+ }
+ else if (status == NO_ERROR) {
+ *lpcCount = EntriesRead;
+ }
+
+EndOfTry: ;
+
+ }
+ RpcExcept(1) {
+ status = NwpMapRpcError(RpcExceptionCode());
+ }
+ RpcEndExcept
+
+ if (status != NO_ERROR && status != WN_NO_MORE_ENTRIES) {
+ SetLastError(status);
+ }
+ else
+ {
+
+ //
+ // Convert offsets of strings to pointers
+ //
+ if (EntriesRead > 0) {
+
+ DWORD i;
+ LPNETRESOURCEW NetR;
+
+
+ NetR = lpBuffer;
+
+ for (i = 0; i < EntriesRead; i++, NetR++) {
+
+ if (NetR->lpLocalName != NULL) {
+ NetR->lpLocalName = (LPWSTR) ((DWORD) lpBuffer +
+ (DWORD) NetR->lpLocalName);
+ }
+
+ NetR->lpRemoteName = (LPWSTR) ((DWORD) lpBuffer +
+ (DWORD) NetR->lpRemoteName);
+
+ if (NetR->lpComment != NULL) {
+ NetR->lpComment = (LPWSTR) ((DWORD) lpBuffer +
+ (DWORD) NetR->lpComment);
+ }
+
+ if (NetR->lpProvider != NULL) {
+ NetR->lpProvider = (LPWSTR) ((DWORD) lpBuffer +
+ (DWORD) NetR->lpProvider);
+ }
+ }
+ }
+ }
+
+ return status;
+}
+
+
+DWORD
+APIENTRY
+NPGetResourceInformation(
+ LPNETRESOURCEW lpNetResource,
+ LPVOID lpBuffer,
+ LPDWORD cbBuffer,
+ LPWSTR * lplpSystem
+ )
+/*++
+
+Routine Description:
+
+ This function returns an object which details information
+ about a specified network resource.
+
+Arguments:
+
+ lpNetResource - This specifies the network resource for which the
+ information is required. The lpRemoteName field of the NETRESOURCE
+ specifies the remote name of the network resource whose information
+ is required. If the calling program knows the values for the
+ lpProvider and dwType fields, then it should fill them in, otherwise,
+ it should set them to NULL. All other fields in the NETRESOURCE are
+ ignored and are not initialized.
+
+ lpBuffer - A pointer to the buffer to receive the result, which is
+ returned as a single NETRESOURCE entry representing the parent
+ resource. The lpRemoteName, lpProvider, dwType, and dwUsage fields
+ are returned, all other fields being set to NULL. The remote name
+ returned should be in the same syntax as that returned from an
+ enumeration, so that the caller can do a case sensitive string
+ compare to determine whether an enumerated resource is this resource.
+ If the provider owns a parent of the network resource, (in other
+ words is known to be the correct network to respond to this request),
+ then lpProvider should be filled in with a non-null entry. If it is
+ known that a network owns a parent of the resource, but that the
+ resource itself is not valid, then lpProvider is returned as a
+ non-null value together with a return status of WN_BAD_VALUE. dwScope
+ is returned as RESOURCE_CONTEXT if the network resource is part of
+ the user's network context, otherwise it is returned as zero.
+
+ cbBuffer - This specifies the size in bytes of the buffer passed to the
+ function call. If the result is WN_MORE_DATA, this will contain the
+ buffer size required (in bytes) to hold the NETRESOURCE information.
+
+ lplpSystem - Returned pointer to a string in the buffer pointed to by
+ lpBuffer that specifies the part of the resource that is accessed
+ through resource type specific system APIs rather than WNet APIs.
+ For example, if the input remote resource name was
+ "\\server\share\dir", then lpRemoteName is returned pointing to
+ "\\server\share" and lplpSystem points to "\dir", both strings
+ being stored in the buffer pointed to by lpBuffer.
+
+Return Value:
+
+
+ WN_SUCCESS - If the call is successful.
+
+ WN_MORE_DATA - If input buffer is too small.
+
+ WN_BAD_VALUE - Invalid dwScope or dwUsage or dwType, or bad combination
+ of parameters is specified (e.g. lpRemoteName does not correspond
+ to dwType).
+
+ WN_BAD_NETNAME - The resource is not recognized by this provider.
+
+--*/
+{
+ DWORD status;
+ LPWSTR pszRemoteName = NULL;
+ DWORD BytesNeeded = 0;
+ DWORD SystemOffset = 0;
+
+ *lplpSystem = NULL;
+
+ status = NwpMapNameToUNC( lpNetResource->lpRemoteName, &pszRemoteName );
+
+ if (status != NO_ERROR)
+ {
+ SetLastError(status);
+ return status;
+ }
+
+#if DBG
+ IF_DEBUG(CONNECT)
+ {
+ KdPrint(("\nNWPROVAU: NPGetResourceInformation %ws\n", pszRemoteName));
+ }
+#endif
+
+ RpcTryExcept
+ {
+ if (lpNetResource->dwType != RESOURCETYPE_ANY &&
+ lpNetResource->dwType != RESOURCETYPE_DISK &&
+ lpNetResource->dwType != RESOURCETYPE_PRINT)
+ {
+ status = WN_BAD_VALUE;
+ }
+ else
+ {
+ status = NwrGetResourceInformation(
+ NULL,
+ pszRemoteName,
+ lpNetResource->dwType,
+ (LPBYTE) lpBuffer,
+ *cbBuffer,
+ &BytesNeeded,
+ &SystemOffset
+ );
+
+ if (status == WN_MORE_DATA)
+ {
+ //
+ // Output buffer too small.
+ //
+ *cbBuffer = BytesNeeded;
+ }
+ }
+ }
+ RpcExcept(1)
+ {
+ status = NwpMapRpcError(RpcExceptionCode());
+ }
+ RpcEndExcept
+ if ( pszRemoteName )
+ LocalFree( (HLOCAL) pszRemoteName );
+
+ if (status != NO_ERROR)
+ {
+ SetLastError(status);
+ }
+ else
+ {
+ //
+ // Convert offsets of strings to pointers
+ //
+ DWORD i;
+ LPNETRESOURCEW NetR = lpBuffer;
+
+ if (NetR->lpLocalName != NULL)
+ {
+ NetR->lpLocalName = (LPWSTR) ((DWORD) lpBuffer +
+ (DWORD) NetR->lpLocalName);
+ }
+
+ NetR->lpRemoteName = (LPWSTR) ((DWORD) lpBuffer +
+ (DWORD) NetR->lpRemoteName);
+
+ if (NetR->lpComment != NULL)
+ {
+ NetR->lpComment = (LPWSTR) ((DWORD) lpBuffer +
+ (DWORD) NetR->lpComment);
+ }
+
+ if (NetR->lpProvider != NULL)
+ {
+ NetR->lpProvider = (LPWSTR) ((DWORD) lpBuffer +
+ (DWORD) NetR->lpProvider);
+ }
+
+ if (SystemOffset != 0)
+ {
+ *lplpSystem = (LPWSTR) ((DWORD) lpBuffer + SystemOffset);
+ }
+ }
+
+ return status;
+}
+
+
+
+DWORD
+APIENTRY
+NPGetResourceParent(
+ LPNETRESOURCEW lpNetResource,
+ LPVOID lpBuffer,
+ LPDWORD cbBuffer
+ )
+/*++
+
+Routine Description:
+
+ This function returns an object which details information
+ about the parent of a specified network resource.
+
+Arguments:
+
+ lpNetResource - This specifies the network resource for which the
+ parent name is required. The NETRESOURCE could have been obtained via
+ previous NPEnumResource, or constructed by the caller. The lpRemoteName
+ field of the NETRESOURCE specifies the remote name of the network
+ resouce whose parent name is required. If the calling program knows
+ the values for the lpProvider and dwType fields, then it can fill
+ them in, otherwise, they are set to NULL. If the lpProvider field is
+ not NULL, then the network provider DLL can assume that the resource
+ is owned by its network, but if it is NULL, then it must assume
+ that the resource could be for some other network and do whatever
+ checking is neccessary to ensure that the result returned is accurate.
+ For example, if being asked for the parent of a server, and the server
+ is not part of a workgroup, the the network provider DLL should check
+ to ensure that the server is part of its network and, if so, return
+ its provider name. All other fields in the NETRESOURCE are ignored and
+ are not initialized.
+
+ lpBuffer - A pointer to the buffer to receive the result, which is
+ returned as a single NETRESOURCE entry representing the parent
+ resource. The lpRemoteName, lpProvider, dwType, and dwUsage fields
+ are returned, all other fields being set to NULL. lpProvider should
+ be set to NULL if the provider has only done a syntactic check (i.e.
+ does not know that the resource is specific to its network). If the
+ provider owns a parent of the network resource, (in other words is
+ known to be the correct network to respond to this request), then
+ lpProvider should be filled in with a non-null entry, even if the
+ return is WN_BAD_VALUE. The remote name returned should be in the
+ same syntax as that returned from an enumeration, so that the caller
+ can do a case sensitive string compare to determine whether an
+ enumerated resource is this resource. If a resource has no browse
+ parent on the network, the lpRemoteName is returned as NULL. The
+ RESOURCEUSAGE_CONNECTABLE value in the dwUsage field does not
+ indicate that the resource can currently be connected to, but that
+ the resource is connectable when it is available on the network.
+
+ cbBuffer - This specifies the size in bytes of the buffer passed to the
+ function call. If the result is WN_MORE_DATA, this will contain the
+ buffer size required (in bytes) to hold the NETRESOURCE information.
+
+Return Value:
+
+ WN_SUCCESS - If the call is successful.
+
+ WN_MORE_DATA - If input buffer is too small.
+
+ WN_BAD_VALUE - Invalid dwScope or dwUsage or dwType, or bad combination
+ of parameters is specified (e.g. lpRemoteName does not correspond
+ to dwType).
+
+--*/
+{
+ DWORD status;
+ LPWSTR pszRemoteName = NULL;
+ DWORD BytesNeeded = 0;
+
+ status = NwpMapNameToUNC( lpNetResource->lpRemoteName, &pszRemoteName );
+
+ if (status != NO_ERROR)
+ {
+ SetLastError(status);
+ return status;
+ }
+
+#if DBG
+ IF_DEBUG(CONNECT)
+ {
+ KdPrint(("\nNWPROVAU: NPGetResourceParent %ws\n", pszRemoteName));
+ }
+#endif
+
+ RpcTryExcept
+ {
+ if (lpNetResource->dwType != RESOURCETYPE_ANY &&
+ lpNetResource->dwType != RESOURCETYPE_DISK &&
+ lpNetResource->dwType != RESOURCETYPE_PRINT)
+ {
+ status = WN_BAD_VALUE;
+ }
+ else
+ {
+ status = NwrGetResourceParent(
+ NULL,
+ pszRemoteName,
+ lpNetResource->dwType,
+ (LPBYTE) lpBuffer,
+ *cbBuffer,
+ &BytesNeeded
+ );
+
+ if (status == WN_MORE_DATA)
+ {
+ //
+ // Output buffer too small.
+ //
+ *cbBuffer = BytesNeeded;
+ }
+ }
+ }
+ RpcExcept(1)
+ {
+ status = NwpMapRpcError(RpcExceptionCode());
+ }
+ RpcEndExcept
+ if ( pszRemoteName )
+ LocalFree( (HLOCAL) pszRemoteName );
+
+
+ if (status != NO_ERROR)
+ {
+ SetLastError(status);
+ }
+ else
+ {
+ //
+ // Convert offsets of strings to pointers
+ //
+ DWORD i;
+ LPNETRESOURCEW NetR = lpBuffer;
+
+ if (NetR->lpLocalName != NULL)
+ {
+ NetR->lpLocalName = (LPWSTR) ((DWORD) lpBuffer +
+ (DWORD) NetR->lpLocalName);
+ }
+
+ NetR->lpRemoteName = (LPWSTR) ((DWORD) lpBuffer +
+ (DWORD) NetR->lpRemoteName);
+
+ if (NetR->lpComment != NULL)
+ {
+ NetR->lpComment = (LPWSTR) ((DWORD) lpBuffer +
+ (DWORD) NetR->lpComment);
+ }
+
+ if (NetR->lpProvider != NULL)
+ {
+ NetR->lpProvider = (LPWSTR) ((DWORD) lpBuffer +
+ (DWORD) NetR->lpProvider);
+ }
+ }
+
+ return status;
+}
+
+
+
+DWORD
+APIENTRY
+NwEnumConnections(
+ HANDLE hEnum,
+ LPDWORD lpcCount,
+ LPVOID lpBuffer,
+ LPDWORD lpBufferSize,
+ BOOL fImplicitConnections
+ )
+/*++
+
+Routine Description:
+
+ This function returns a lists of connections.
+
+Arguments:
+
+ hEnum - Supplies the resumable enumeration context handle.
+
+ NOTE: If this value is 0xFFFFFFFF, it is not a context
+ handle and this routine is required to return
+ WN_NO_MORE_ENTRIES. This hack is to handle the
+ case where the user cancelled out of the network
+ credential dialog on NwrOpenEnumDirectories and we
+ cannot return an error there or we generate an error
+ popup.
+
+ lpcCount - On input, supplies the number of entries to get.
+ On output, if NO_ERROR is returned, receives the number
+ of entries NETRESOURCE returned in lpBuffer.
+
+ lpBuffer - Receives an array of NETRESOURCE entries, each
+ entry describes an object within the container.
+
+ lpBufferSize - On input, supplies the size of lpBuffer in
+ bytes. On output, if WN_MORE_DATA is returned, receives
+ the number of bytes needed in the buffer to get the
+ next entry.
+
+ fImplicitConnections - TRUE is we also want all implicit connections,
+ FALSE otherwise.
+
+Return Value:
+
+
+ NO_ERROR - Successfully returned at least one entry.
+
+ WN_NO_MORE_ENTRIES - Reached the end of enumeration and nothing
+ is returned.
+
+ WN_MORE_DATA - lpBuffer is too small to even get one entry.
+
+ WN_BAD_HANDLE - The enumeration handle is invalid.
+
+ Other network errors.
+
+--*/
+{
+ DWORD status = NO_ERROR;
+ DWORD BytesNeeded = 0;
+ DWORD EntriesRead = 0;
+
+#if DBG
+ IF_DEBUG(ENUM) {
+ KdPrint(("\nNWPROVAU: NPEnumResource\n"));
+ }
+#endif
+
+ RpcTryExcept {
+
+ if (hEnum == (HANDLE) 0xFFFFFFFF) {
+ status = WN_NO_MORE_ENTRIES;
+ goto EndOfTry;
+ }
+
+ status = NwrEnumConnections(
+ (NWWKSTA_CONTEXT_HANDLE) hEnum,
+ *lpcCount,
+ (LPBYTE) lpBuffer,
+ *lpBufferSize,
+ &BytesNeeded,
+ &EntriesRead,
+ fImplicitConnections
+ );
+
+ if (status == WN_MORE_DATA) {
+
+ //
+ // Output buffer too small to fit a single entry.
+ //
+ *lpBufferSize = BytesNeeded;
+ }
+ else if (status == NO_ERROR) {
+ *lpcCount = EntriesRead;
+ }
+
+EndOfTry: ;
+
+ }
+ RpcExcept(1) {
+ status = NwpMapRpcError(RpcExceptionCode());
+ }
+ RpcEndExcept
+
+ if (status != NO_ERROR && status != WN_NO_MORE_ENTRIES) {
+ SetLastError(status);
+ }
+
+ //
+ // Convert offsets of strings to pointers
+ //
+ if (EntriesRead > 0) {
+
+ DWORD i;
+ LPNETRESOURCEW NetR;
+
+
+ NetR = lpBuffer;
+
+ for (i = 0; i < EntriesRead; i++, NetR++) {
+
+ if (NetR->lpLocalName != NULL) {
+ NetR->lpLocalName = (LPWSTR) ((DWORD) lpBuffer +
+ (DWORD) NetR->lpLocalName);
+ }
+
+ NetR->lpRemoteName = (LPWSTR) ((DWORD) lpBuffer +
+ (DWORD) NetR->lpRemoteName);
+
+ if (NetR->lpComment != NULL) {
+ NetR->lpComment = (LPWSTR) ((DWORD) lpBuffer +
+ (DWORD) NetR->lpComment);
+ }
+
+ if (NetR->lpProvider != NULL) {
+ NetR->lpProvider = (LPWSTR) ((DWORD) lpBuffer +
+ (DWORD) NetR->lpProvider);
+ }
+ }
+ }
+
+ return status;
+}
+
+
+DWORD
+APIENTRY
+NPCloseEnum(
+ HANDLE hEnum
+ )
+/*++
+
+Routine Description:
+
+ This function closes the enumeration context handle.
+
+Arguments:
+
+ hEnum - Supplies the enumeration context handle.
+
+ NOTE: If this value is 0xFFFFFFFF, it is not a context
+ handle. Just return success.
+
+Return Value:
+
+ NO_ERROR - Successfully returned at least one entry.
+
+ WN_BAD_HANDLE - The enumeration handle is invalid.
+
+--*/
+{
+ DWORD status = NO_ERROR;
+
+#if DBG
+ IF_DEBUG(ENUM) {
+ KdPrint(("\nNWPROVAU: NPCloseEnum\n"));
+ }
+#endif
+
+ RpcTryExcept
+ {
+ if (hEnum == (HANDLE) 0xFFFFFFFF) {
+ status = NO_ERROR;
+ }
+ else {
+ status = NwrCloseEnum(
+ (LPNWWKSTA_CONTEXT_HANDLE) &hEnum
+ );
+ }
+ }
+ RpcExcept(1) {
+ status = NwpMapRpcError(RpcExceptionCode());
+ }
+ RpcEndExcept
+
+ if (status != NO_ERROR) {
+ SetLastError(status);
+ }
+ return status;
+}
+
+
+DWORD
+APIENTRY
+NPFormatNetworkName(
+ LPWSTR lpRemoteName,
+ LPWSTR lpFormattedName,
+ LPDWORD lpnLength,
+ DWORD dwFlags,
+ DWORD dwAveCharPerLine
+ )
+/*++
+
+Routine Description:
+
+ This function takes a fully-qualified UNC name and formats it
+ into a shorter form for display. Only the name of the object
+ within the container is returned for display.
+
+ We only support formatting of the remote resource name to the
+ abbreviated form for display during enumeration where the container
+ name is displayed prior to the object within it.
+
+Arguments:
+
+ lpRemoteName - Supplies the fully-qualified UNC name.
+
+ lpFormatedName - Output buffer to receive the formatted name.
+
+ lpnLength - On input, supplies the length of the lpFormattedName
+ buffer in characters. On output, if WN_MORE_DATA is returned,
+ receives the length in number of characters required of the
+ output buffer to hold the formatted name.
+
+ dwFlags - Supplies a bitwise set of flags indicating the type
+ of formatting required on lpRemoteName.
+
+ dwAveCharPerLine - Ignored.
+
+Return Value:
+
+ NO_ERROR - Successfully returned at least one entry.
+
+ WN_MORE_DATA - lpFormattedName buffer is too small.
+
+ WN_BAD_VALUE - lpRemoteName is NULL.
+
+ ERROR_NOT_SUPPORTED - dwFlags that does not contain the
+ WNFMT_INENUM bit.
+
+--*/
+{
+ DWORD status = NO_ERROR;
+
+ LPWSTR NextBackSlash;
+ LPWSTR Source;
+ DWORD SourceLen;
+
+#if DBG
+ IF_DEBUG(OTHER)
+ KdPrint(("\nNWPROVAU: NPFormatNetworkName\n"));
+#endif
+
+ if (lpRemoteName == NULL)
+ {
+ status = WN_BAD_VALUE;
+ goto CleanExit;
+ }
+
+ if (dwFlags & WNFMT_INENUM)
+ {
+ BYTE i;
+ WORD length = wcslen( lpRemoteName );
+ WORD slashCount = 0;
+ WORD dotCount = 0;
+ WORD Start = 0;
+ WORD End = length;
+ BOOL isNdsUnc = FALSE;
+
+ for ( i = 0; i < length; i++ )
+ {
+ if ( lpRemoteName[i] == L'\\' )
+ {
+ slashCount++;
+ if ( i + 1 < length )
+ {
+ Start = i + 1;
+ }
+ }
+
+ if ( ( lpRemoteName[i] == L'.' ) ||
+ ( lpRemoteName[i] == L'=' ) )
+ isNdsUnc = TRUE;
+
+ if ( dotCount < 1 && isNdsUnc && lpRemoteName[i] == L'.' )
+ {
+ End = i - 1;
+ dotCount++;
+ }
+ }
+
+ if ( i > length )
+ End = length - 1;
+
+ if ( slashCount > 3 || ( isNdsUnc != TRUE && slashCount != 3 && dotCount == 0 ) )
+ End = i - 1;
+
+ Source = &lpRemoteName[Start];
+ SourceLen = End - Start + 1;
+
+ if ( SourceLen + 1 > *lpnLength )
+ {
+ *lpnLength = SourceLen + 1;
+ status = WN_MORE_DATA;
+ }
+ else
+ {
+ wcsncpy( lpFormattedName, Source, SourceLen );
+ lpFormattedName[SourceLen] = 0x00000000;
+ status = NO_ERROR;
+ }
+ }
+ else if ( dwFlags & WNFMT_MULTILINE )
+ {
+
+ DWORD i, j, k = 0;
+ DWORD nLastBackSlash = 0;
+ DWORD BytesNeeded = ( wcslen( lpRemoteName ) + 1 +
+ 2 * wcslen( lpRemoteName ) / dwAveCharPerLine
+ ) * sizeof( WCHAR);
+
+ if ( *lpnLength < (BytesNeeded/sizeof(WCHAR)) )
+ {
+ *lpnLength = BytesNeeded/sizeof(WCHAR);
+ status = WN_MORE_DATA;
+ goto CleanExit;
+ }
+
+ for ( i = 0, j = 0; lpRemoteName[i] != 0; i++, j++ )
+ {
+ if ( lpRemoteName[i] == L'\\' )
+ nLastBackSlash = i;
+
+ if ( k == dwAveCharPerLine )
+ {
+ if ( lpRemoteName[i] != L'\\' )
+ {
+ DWORD m, n;
+ for ( n = nLastBackSlash, m = ++j ; n <= i ; n++, m-- )
+ {
+ lpFormattedName[m] = lpFormattedName[m-1];
+ }
+ lpFormattedName[m] = L'\n';
+ k = i - nLastBackSlash - 1;
+ }
+ else
+ {
+ lpFormattedName[j++] = L'\n';
+ k = 0;
+ }
+ }
+
+ lpFormattedName[j] = lpRemoteName[i];
+ k++;
+ }
+
+ lpFormattedName[j] = 0;
+
+ }
+ else if ( dwFlags & WNFMT_ABBREVIATED )
+ {
+ //
+ // we dont support abbreviated form for now because we look bad
+ // in comdlg (fileopen) if we do.
+ //
+
+ DWORD nLength;
+ nLength = wcslen( lpRemoteName ) + 1 ;
+ if (nLength > *lpnLength)
+ {
+ *lpnLength = nLength;
+ status = WN_MORE_DATA;
+ goto CleanExit;
+ }
+ else
+ {
+ wcscpy( lpFormattedName, lpRemoteName );
+ }
+
+#if 0
+ DWORD i, j, k;
+ DWORD BytesNeeded = dwAveCharPerLine * sizeof( WCHAR);
+ DWORD nLength;
+
+ if ( *lpnLength < BytesNeeded )
+ {
+ *lpnLength = BytesNeeded;
+ status = WN_MORE_DATA;
+ goto CleanExit;
+ }
+
+ nLength = wcslen( lpRemoteName );
+ if ( ( nLength + 1) <= dwAveCharPerLine )
+ {
+ wcscpy( lpFormattedName, lpRemoteName );
+ }
+ else
+ {
+ lpFormattedName[0] = lpRemoteName[0];
+ lpFormattedName[1] = lpRemoteName[1];
+
+ for ( i = 2; lpRemoteName[i] != L'\\'; i++ )
+ lpFormattedName[i] = lpRemoteName[i];
+
+ for ( j = dwAveCharPerLine-1, k = nLength; j >= (i+3); j--, k-- )
+ {
+ lpFormattedName[j] = lpRemoteName[k];
+ if ( lpRemoteName[k] == L'\\' )
+ {
+ j--;
+ break;
+ }
+ }
+
+ lpFormattedName[j] = lpFormattedName[j-1] = lpFormattedName[j-2] = L'.';
+
+ for ( k = i; k < (j-2); k++ )
+ lpFormattedName[k] = lpRemoteName[k];
+
+ }
+
+#endif
+
+ }
+ else // some unknown flags
+ {
+ status = ERROR_NOT_SUPPORTED;
+ }
+
+CleanExit:
+
+ if (status != NO_ERROR)
+ SetLastError(status);
+
+ return status;
+}
+
+
+STATIC
+BOOL
+NwpWorkstationStarted(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This function queries the service controller to see if the
+ NetWare workstation service has started. If in doubt, it returns
+ FALSE.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Returns TRUE if the NetWare workstation service has started,
+ FALSE otherwise.
+
+--*/
+{
+ SC_HANDLE ScManager;
+ SC_HANDLE Service;
+ SERVICE_STATUS ServiceStatus;
+ BOOL IsStarted = FALSE;
+
+ ScManager = OpenSCManagerW(
+ NULL,
+ NULL,
+ SC_MANAGER_CONNECT
+ );
+
+ if (ScManager == NULL) {
+ return FALSE;
+ }
+
+ Service = OpenServiceW(
+ ScManager,
+ NW_WORKSTATION_SERVICE,
+ SERVICE_QUERY_STATUS
+ );
+
+ if (Service == NULL) {
+ CloseServiceHandle(ScManager);
+ return FALSE;
+ }
+
+ if (! QueryServiceStatus(Service, &ServiceStatus)) {
+ CloseServiceHandle(ScManager);
+ CloseServiceHandle(Service);
+ return FALSE;
+ }
+
+
+ if ( (ServiceStatus.dwCurrentState == SERVICE_RUNNING) ||
+ (ServiceStatus.dwCurrentState == SERVICE_CONTINUE_PENDING) ||
+ (ServiceStatus.dwCurrentState == SERVICE_PAUSE_PENDING) ||
+ (ServiceStatus.dwCurrentState == SERVICE_PAUSED) ) {
+
+ IsStarted = TRUE;
+ }
+
+ CloseServiceHandle(ScManager);
+ CloseServiceHandle(Service);
+
+ return IsStarted;
+}
+
+
+
+DWORD
+NwpMapNameToUNC(
+ IN LPWSTR pszName,
+ OUT LPWSTR *ppszUNC
+ )
+/*++
+
+Routine Description:
+
+ This routine validates the given name as a netwarepath or UNC path.
+ If it is a netware path, this routine will convert the
+ Netware path name to UNC name.
+
+Arguments:
+
+ pszName - Supplies the netware name or UNC name
+ ppszUNC - Points to the converted UNC name
+
+Return Value:
+
+ NO_ERROR or the error that occurred.
+
+--*/
+{
+ DWORD err = NO_ERROR;
+
+ LPWSTR pszSrc = pszName;
+ LPWSTR pszDest;
+
+ BOOL fSlash = FALSE;
+ BOOL fColon = FALSE;
+ DWORD nServerLen = 0;
+ DWORD nVolLen = 0;
+ BOOL fFirstToken = TRUE;
+
+ *ppszUNC = NULL;
+
+#if DBG
+ IF_DEBUG(CONNECT)
+ KdPrint(("NwpMapNameToUNC: Source = %ws\n", pszName ));
+#endif
+
+
+ //
+ // Check if the given name is a valid UNC name
+ //
+ err = NwLibCanonRemoteName( NULL, // "\\Server" is valid UNC path
+ pszName,
+ ppszUNC,
+ NULL );
+
+ //
+ // The given name is a valid UNC name, so return success!
+ //
+ if ( err == NO_ERROR )
+ return err;
+
+ //
+ // The name cannot be NULL or empty string
+ //
+ if ( pszName == NULL || *pszName == 0)
+ return WN_BAD_NETNAME;
+
+ //
+ // Allocate the buffer to store the mapped UNC name
+ // We allocate 3 extra characters, two for the backslashes in front
+ // and one for the ease of parsing below.
+ //
+ if ((*ppszUNC = (LPVOID) LocalAlloc(
+ LMEM_ZEROINIT,
+ (wcslen( pszName) + 4) * sizeof( WCHAR)
+ )) == NULL )
+ {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ wcscpy( *ppszUNC, L"\\\\" );
+ pszDest = *ppszUNC + 2; // Skip past two backslashes
+
+ //
+ // Parse the given string and put the converted string into *ppszUNC
+ // In the converted string, we will substitute 0 for all slashes
+ // for the time being.
+ //
+ for ( ; *pszSrc != 0; pszSrc++ )
+ {
+ if ( ( *pszSrc == L'/' )
+ || ( *pszSrc == L'\\' )
+ )
+ {
+ //
+ // Two consecutive backslashes are bad
+ //
+ if ( (*(pszSrc+1) == L'/') || (*(pszSrc+1) == L'\\'))
+ {
+ LocalFree( *ppszUNC );
+ *ppszUNC = NULL;
+ return WN_BAD_NETNAME;
+ }
+
+ if ( !fSlash )
+ fSlash = TRUE;
+
+ *pszDest++ = 0;
+ }
+ else if ( (*pszSrc == L':') && fSlash && !fColon )
+ {
+ fColon = TRUE;
+ if ( *(pszSrc+1) != 0 )
+ *pszDest++ = 0;
+
+ }
+ else
+ {
+ *pszDest++ = *pszSrc;
+ if (( fSlash ) && ( !fColon))
+ nVolLen++;
+ else if ( !fSlash )
+ nServerLen++;
+ }
+ }
+
+ //
+ // Note: *ppszUNC is already terminated with two '\0' because we initialized
+ // the whole buffer to zero.
+ //
+
+ if ( ( nServerLen == 0 )
+ || ( fSlash && nVolLen == 0 )
+ || ( fSlash && nVolLen != 0 && !fColon )
+ )
+ {
+ LocalFree( *ppszUNC );
+ *ppszUNC = NULL;
+ return WN_BAD_NETNAME;
+ }
+
+ //
+ // At this point, we know the name is a valid Netware syntax
+ // i.e. SERVER[/VOL:/dir]
+ // We now need to validate that all the characters used in the
+ // servername, volume, directory are valid characters
+ //
+
+ pszDest = *ppszUNC + 2; // Skip past the first two backslashes
+ while ( *pszDest != 0 )
+ {
+ DWORD nLen = wcslen( pszDest );
+
+ if ( ( fFirstToken && !IS_VALID_SERVER_TOKEN( pszDest, nLen ))
+ || ( !fFirstToken && !IS_VALID_TOKEN( pszDest, nLen ))
+ )
+ {
+ LocalFree( *ppszUNC );
+ *ppszUNC = NULL;
+ return WN_BAD_NETNAME;
+ }
+
+ fFirstToken = FALSE;
+ pszDest += nLen + 1;
+ }
+
+ //
+ // The netware name is valid! Convert 0 back to backslash in
+ // converted string.
+ //
+
+ pszDest = *ppszUNC + 2; // Skip past the first two backslashes
+ while ( *pszDest != 0 )
+ {
+ if ( (*(pszDest+1) == 0 ) && (*(pszDest+2) != 0 ) )
+ {
+ *(pszDest+1) = L'\\';
+ }
+ pszDest++;
+ }
+
+#if DBG
+ IF_DEBUG(CONNECT)
+ KdPrint(("NwpMapNameToUNC: Destination = %ws\n", *ppszUNC ));
+#endif
+ return NO_ERROR;
+}
+
+
+STATIC
+VOID
+NwpGetUncInfo(
+ IN LPWSTR lpstrUnc,
+ OUT WORD * slashCount,
+ OUT BOOL * isNdsUnc
+ )
+{
+ BYTE i;
+ WORD length = wcslen( lpstrUnc );
+
+ *isNdsUnc = FALSE;
+ *slashCount = 0;
+
+ for ( i = 0; i < length; i++ )
+ {
+ if ( ( lpstrUnc[i] == L'.' ) && ( *slashCount == 3 ) )
+ {
+ *isNdsUnc = TRUE;
+ }
+
+ if ( lpstrUnc[i] == L'\\' )
+ {
+ *slashCount += 1;
+ }
+ }
+}
+
+
+STATIC
+DWORD
+NwpGetUncObjectName(
+ IN LPWSTR ContainerName
+)
+{
+ WORD length = 2;
+ WORD totalLength = wcslen( ContainerName );
+
+ if ( totalLength < 2 )
+ return 0;
+
+ while ( length < totalLength )
+ {
+ if ( ContainerName[length] == L'.' )
+ ContainerName[length] = L'\0';
+
+ length++;
+ }
+
+ length = 2;
+
+ while ( length < totalLength && ContainerName[length] != L'\\' )
+ {
+ length++;
+ }
+
+ if ( ( ContainerName[length + 1] == L'C' ||
+ ContainerName[length + 1] == L'c' ) &&
+ ( ContainerName[length + 2] == L'N' ||
+ ContainerName[length + 2] == L'n' ) &&
+ ContainerName[length + 3] == L'=' )
+ {
+ ContainerName[length + 2] = L'\\';
+ ContainerName[length + 3] = L'\\';
+
+ return (DWORD) (ContainerName + length + 2);
+ }
+
+ ContainerName[length - 1] = L'\\';
+
+ return (DWORD) (ContainerName + length - 1);
+}
+
+
+STATIC
+WORD
+NwpGetSlashCount(
+ IN LPWSTR lpstrUnc
+ )
+{
+ WORD count = 0;
+ BYTE i;
+ WORD length = wcslen( lpstrUnc );
+
+ for ( i = 0; i < length; i++ )
+ {
+ if ( lpstrUnc[i] == L'\\' )
+ {
+ count++;
+ }
+ }
+
+ return count;
+}
+
+
+DWORD
+NwpMapRpcError(
+ IN DWORD RpcError
+ )
+/*++
+
+Routine Description:
+
+ This routine maps the RPC error into a more meaningful windows
+ error for the caller.
+
+Arguments:
+
+ RpcError - Supplies the exception error raised by RPC
+
+Return Value:
+
+ Returns the mapped error.
+
+--*/
+{
+
+ switch (RpcError) {
+
+ case RPC_S_UNKNOWN_IF:
+ case RPC_S_SERVER_UNAVAILABLE:
+ return WN_NO_NETWORK;
+
+ case RPC_S_INVALID_BINDING:
+ case RPC_X_SS_IN_NULL_CONTEXT:
+ case RPC_X_SS_CONTEXT_DAMAGED:
+ case RPC_X_SS_HANDLES_MISMATCH:
+ case ERROR_INVALID_HANDLE:
+ return ERROR_INVALID_HANDLE;
+
+ case RPC_X_NULL_REF_POINTER:
+ return ERROR_INVALID_PARAMETER;
+
+ case EXCEPTION_ACCESS_VIOLATION:
+ return ERROR_INVALID_ADDRESS;
+
+ default:
+ return RpcError;
+ }
+}
+
+DWORD
+NwRegisterGatewayShare(
+ IN LPWSTR ShareName,
+ IN LPWSTR DriveName
+ )
+/*++
+
+Routine Description:
+
+ This routine remembers that a gateway share has been created so
+ that it can be cleanup up when NWCS is uninstalled.
+
+Arguments:
+
+ ShareName - name of share
+ DriveName - name of drive that is shared
+
+Return Status:
+
+ Win32 error of any failure.
+
+--*/
+{
+ return ( NwpRegisterGatewayShare(ShareName, DriveName) ) ;
+}
+
+DWORD
+NwCleanupGatewayShares(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This routine cleans up all persistent share info and also tidies
+ up the registry for NWCS. Later is not needed in uninstall, but is
+ there so we have a single routine that cvompletely disables the
+ gateway.
+
+Arguments:
+
+ None.
+
+Return Status:
+
+ Win32 error for failed APIs.
+
+--*/
+{
+ return ( NwpCleanupGatewayShares() ) ;
+}
+
+DWORD
+NwClearGatewayShare(
+ IN LPWSTR ShareName
+ )
+/*++
+
+Routine Description:
+
+ This routine deletes a specific share from the remembered gateway
+ shares in the registry.
+
+Arguments:
+
+ ShareName - share value to delete
+
+Return Status:
+
+ Win32 status code.
+
+--*/
+{
+ return ( NwpClearGatewayShare( ShareName ) ) ;
+}
diff --git a/private/nw/svcdlls/nwwks/client/server.bmp b/private/nw/svcdlls/nwwks/client/server.bmp
new file mode 100644
index 000000000..200dc12ba
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/server.bmp
Binary files differ
diff --git a/private/nw/svcdlls/nwwks/client/server.ico b/private/nw/svcdlls/nwwks/client/server.ico
new file mode 100644
index 000000000..1d628da58
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/server.ico
Binary files differ
diff --git a/private/nw/svcdlls/nwwks/client/sources b/private/nw/svcdlls/nwwks/client/sources
new file mode 100644
index 000000000..97d12a350
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/sources
@@ -0,0 +1,101 @@
+!IF 0
+
+Copyright (c) 1989-93 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Rita Wong (ritaw) 14-Feb-1993
+
+
+Revision History:
+
+!ENDIF
+
+MAJORCOMP=nw
+MINORCOMP=service
+
+TARGETNAME=nwprovau
+TARGETPATH=\nt\public\sdk\lib
+TARGETTYPE=DYNLINK
+DLLBASE = 0x69900000
+
+
+TARGETLIBS= \
+ \nt\public\sdk\lib\*\rpcrt4.lib \
+ \nt\public\sdk\lib\*\advapi32.lib \
+ \nt\public\sdk\lib\*\kernel32.lib \
+ \nt\public\sdk\lib\*\netapi32.lib \
+ \nt\public\sdk\lib\*\rpcutil.lib \
+ \nt\public\sdk\lib\*\netlib.lib\
+ ..\lib\obj\*\nwwlib.lib \
+ ..\..\..\nwlib\obj\*\nwlib.lib \
+ \nt\public\sdk\lib\*\user32.lib \
+ \nt\public\sdk\lib\*\lsadll.lib \
+ \nt\public\sdk\lib\*\mpr.lib \
+ \nt\public\sdk\lib\*\ws2_32.lib \
+ \nt\public\sdk\lib\*\nwsaplib.lib \
+ \nt\public\sdk\lib\*\ole32.lib \
+ \nt\public\sdk\lib\*\uuid.lib \
+ \nt\public\sdk\lib\*\gdi32.lib \
+ \nt\public\sdk\lib\*\comctl32.lib \
+ obj\*\nwprovau.res
+
+
+DLLENTRY=InitializeDll
+
+INCLUDES=.;..\inc;..\..\..\inc;$(_NTROOT)\private\inc; $(_NTROOT)\private\net\inc;$(_NTROOT)\private\windows\inc;$(_NTROOT)\private\windows\inc16
+
+NTDEBUGTYPE=both
+
+UNICODE=1
+
+SOURCES= \
+ bind.c \
+ logon.c \
+ provider.c \
+ nwdlg.c \
+ authpkg.c \
+ nwspl.c \
+ port.c \
+ nwapi.c \
+ ccache.c \
+ getaddr.c \
+ gtadrnr.c \
+ nwwks_c.c \
+ nwutil.c \
+ nwshext.cxx \
+ nwshprop.cxx \
+ nwshmenu.cxx \
+ nwshmisc.cxx \
+ drawpie.c \
+ nwshui.cxx \
+ caddress.c
+
+USE_CRTDLL=1
+
+MSC_WARNING_LEVEL=/W3 /WX
+
+NET_C_DEFINES=-DRPC_NO_WINDOWS_H -DUNICODE -DNOT_USED
+DLLDEF=nwprovau.def
+
+
+UMTYPE=console
+UMTESt=tconn
+UMLIBS= \
+ \nt\public\sdk\lib\*\nwprovau.lib
+
+NTTARGETFILE0=nwprovau.rc
+RCCODEPAGE=1252
+
diff --git a/private/nw/svcdlls/nwwks/client/tconn.c b/private/nw/svcdlls/nwwks/client/tconn.c
new file mode 100644
index 000000000..36057cc3e
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/tconn.c
@@ -0,0 +1,556 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ tconnect.c
+
+Abstract:
+
+ Test for workstation connection APIs.
+
+Author:
+
+ Rita Wong (ritaw) 17-Feb-1993
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+--*/
+
+#ifndef UNICODE
+#define UNICODE
+#endif
+
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windef.h>
+#include <winbase.h>
+#include <winnetwk.h>
+#include <npapi.h>
+
+#define REDIR_NOT_READY
+
+DWORD
+TestCreateConnection(
+ IN LPWSTR LocalName OPTIONAL,
+ IN LPWSTR RemoteName,
+ IN LPWSTR Password OPTIONAL,
+ IN LPWSTR UserName OPTIONAL,
+ IN DWORD ExpectedError
+ );
+
+DWORD
+TestDeleteConnection(
+ IN LPWSTR ConnectionName,
+ IN BOOL ForceFlag,
+ IN DWORD ExpectedError
+ );
+
+DWORD
+TestOpenEnum(
+ IN DWORD Scope,
+ IN LPNETRESOURCEW NetR OPTIONAL,
+ OUT LPHANDLE EnumHandle,
+ IN DWORD ExpectedError
+ );
+
+DWORD
+TestEnum(
+ IN HANDLE EnumHandle,
+ IN DWORD EntriesRequested,
+ IN LPVOID Buffer,
+ IN DWORD BufferSize,
+ OUT LPDWORD BytesNeeded,
+ IN DWORD ExpectedError
+ );
+
+VOID
+PrintNetResource(
+ LPNETRESOURCE NetR
+ );
+
+
+BYTE WorkBuffer[1024];
+BYTE WorkBuffer2[1024];
+
+void _CRTAPI1
+main(
+ int argc,
+ char *argv[]
+ )
+{
+ DWORD status;
+
+ LPWSTR LocalName;
+ LPWSTR RemoteName;
+ LPWSTR Password;
+ LPWSTR UserName;
+
+ NETRESOURCEW NetR;
+ HANDLE EnumHandle;
+ DWORD BytesNeeded;
+
+
+ LocalName = L"E:";
+ RemoteName = L"\\\\MyServerName\\A_Volume\\A_Directory";
+ Password = L"MyPassword";
+ UserName = L"MyUserName";
+
+
+ TestCreateConnection(
+ LocalName,
+ RemoteName,
+ Password,
+ UserName,
+ WN_SUCCESS
+ );
+
+ TestDeleteConnection(
+ LocalName,
+ TRUE,
+ WN_SUCCESS
+ );
+
+ //-------------------------//
+
+ TestCreateConnection(
+ NULL,
+ RemoteName,
+ NULL,
+ NULL,
+ WN_SUCCESS
+ );
+
+ TestDeleteConnection(
+ RemoteName,
+ TRUE,
+ WN_SUCCESS
+ );
+
+ //-------------------------//
+
+ TestCreateConnection(
+ L"LPT1",
+ RemoteName,
+ NULL,
+ NULL,
+ ERROR_INVALID_PARAMETER
+ );
+
+ //-------------------------//
+
+ TestCreateConnection(
+ LocalName,
+ L"\\\\Server",
+ NULL,
+ NULL,
+ ERROR_INVALID_NAME
+ );
+
+ //-------------------------//
+
+ printf("\n");
+
+ //-------------------------//
+
+#ifdef REDIR_NOT_READY
+
+ if (argc == 2) {
+
+ ANSI_STRING AStr;
+ UNICODE_STRING UStr;
+
+
+ RtlZeroMemory(WorkBuffer2, sizeof(WorkBuffer2));
+ UStr.Buffer = WorkBuffer2;
+ UStr.MaximumLength = sizeof(WorkBuffer2);
+
+ RtlInitString(&AStr, argv[1]);
+
+ RtlAnsiStringToUnicodeString(
+ &UStr,
+ &AStr,
+ FALSE
+ );
+
+ NetR.lpRemoteName = UStr.Buffer;
+ }
+ else {
+ NetR.lpRemoteName = L"lanman";
+ }
+
+ //-------------------------//
+
+ TestOpenEnum(
+ RESOURCE_GLOBALNET,
+ &NetR,
+ &EnumHandle,
+ WN_SUCCESS
+ );
+
+ TestEnum(
+ EnumHandle,
+ 0xFFFFFFFF,
+ WorkBuffer,
+ sizeof(WorkBuffer),
+ &BytesNeeded,
+ WN_SUCCESS
+ );
+
+ TestEnum(
+ EnumHandle,
+ 0xFFFFFFFF,
+ WorkBuffer,
+ sizeof(WorkBuffer),
+ &BytesNeeded,
+ WN_NO_MORE_ENTRIES
+ );
+
+ (void) NPCloseEnum(EnumHandle);
+
+ //-------------------------//
+
+ printf("\n");
+
+ //-------------------------//
+
+ TestOpenEnum(
+ RESOURCE_GLOBALNET,
+ &NetR,
+ &EnumHandle,
+ WN_SUCCESS
+ );
+
+ TestEnum(
+ EnumHandle,
+ 0xFFFFFFFF,
+ WorkBuffer,
+ 200,
+ &BytesNeeded,
+ WN_SUCCESS
+ );
+
+ TestEnum(
+ EnumHandle,
+ 0xFFFFFFFF,
+ WorkBuffer,
+ sizeof(NETRESOURCEW) + 5,
+ &BytesNeeded,
+ WN_MORE_DATA
+ );
+
+ TestEnum(
+ EnumHandle,
+ 0xFFFFFFFF,
+ WorkBuffer,
+ BytesNeeded,
+ &BytesNeeded,
+ WN_SUCCESS
+ );
+
+ TestEnum(
+ EnumHandle,
+ 0xFFFFFFFF,
+ WorkBuffer,
+ sizeof(WorkBuffer),
+ &BytesNeeded,
+ WN_SUCCESS
+ );
+
+ TestEnum(
+ EnumHandle,
+ 0xFFFFFFFF,
+ WorkBuffer,
+ 0,
+ &BytesNeeded,
+ WN_NO_MORE_ENTRIES
+ );
+
+ (void) NPCloseEnum(EnumHandle);
+
+#else
+
+ NetR.lpRemoteName = L"\\\\S";
+
+ TestOpenEnum(
+ RESOURCE_GLOBALNET,
+ &NetR,
+ &EnumHandle,
+ WN_SUCCESS
+ );
+
+ //-------------------------//
+
+ NetR.lpRemoteName = L"\\\\A Long Server Name";
+
+ TestOpenEnum(
+ RESOURCE_GLOBALNET,
+ &NetR,
+ &EnumHandle,
+ WN_SUCCESS
+ );
+
+ //-------------------------//
+
+ NetR.lpRemoteName = L"\\\\S\\";
+
+ TestOpenEnum(
+ RESOURCE_GLOBALNET,
+ &NetR,
+ &EnumHandle,
+ ERROR_INVALID_NAME
+ );
+
+ //-------------------------//
+
+ NetR.lpRemoteName = L"lanman";
+
+ TestOpenEnum(
+ RESOURCE_GLOBALNET,
+ &NetR,
+ &EnumHandle,
+ ERROR_INVALID_NAME
+ );
+
+ //-------------------------//
+
+ NetR.lpRemoteName = L"\\\\S\\Y";
+
+ TestOpenEnum(
+ RESOURCE_GLOBALNET,
+ &NetR,
+ &EnumHandle,
+ WN_SUCCESS
+ );
+
+ //-------------------------//
+
+ NetR.lpRemoteName = L"\\\\Server\\Volume\\Dir";
+
+ TestOpenEnum(
+ RESOURCE_GLOBALNET,
+ &NetR,
+ &EnumHandle,
+ WN_SUCCESS
+ );
+
+#endif
+
+}
+
+
+DWORD
+TestCreateConnection(
+ IN LPWSTR LocalName OPTIONAL,
+ IN LPWSTR RemoteName,
+ IN LPWSTR Password OPTIONAL,
+ IN LPWSTR UserName OPTIONAL,
+ IN DWORD ExpectedError
+ )
+{
+ DWORD status;
+ NETRESOURCEW NetR;
+
+
+ printf("\nTestCreateConnection: Local %ws, Remote %ws", LocalName, RemoteName);
+
+ if (ARGUMENT_PRESENT(UserName)) {
+ printf(" UserName %ws", UserName);
+ }
+
+ if (ARGUMENT_PRESENT(Password)) {
+ printf(" Password %ws", Password);
+
+ }
+
+ printf("\n");
+
+ NetR.lpLocalName = LocalName;
+ NetR.lpRemoteName = RemoteName;
+
+ NetR.dwType = RESOURCETYPE_ANY;
+
+ status = NPAddConnection(
+ &NetR,
+ Password,
+ UserName
+ );
+
+ if (status != WN_SUCCESS) {
+ status = GetLastError();
+ }
+
+ if (status != ExpectedError) {
+ printf(" FAILED: expected %lu got %lu\n", ExpectedError, status);
+ }
+ else {
+ printf(" SUCCESS: got %lu as expected\n", status);
+ }
+
+ return status;
+
+}
+
+DWORD
+TestDeleteConnection(
+ IN LPWSTR ConnectionName,
+ IN BOOL ForceFlag,
+ IN DWORD ExpectedError
+ )
+{
+ DWORD status;
+
+
+ printf("\nTestDeleteConnection: Connection %ws, ForceFlag %u\n",
+ ConnectionName, ForceFlag);
+
+ status = NPCancelConnection(
+ ConnectionName,
+ ForceFlag
+ );
+
+ if (status != WN_SUCCESS) {
+ status = GetLastError();
+ }
+
+ if (status != ExpectedError) {
+ printf(" FAILED: expected %lu got %lu\n", ExpectedError, status);
+ }
+ else {
+ printf(" SUCCESS: got %lu as expected\n", status);
+ }
+
+ return status;
+}
+
+DWORD
+TestOpenEnum(
+ IN DWORD Scope,
+ IN LPNETRESOURCEW NetR OPTIONAL,
+ OUT LPHANDLE EnumHandle,
+ IN DWORD ExpectedError
+ )
+{
+ DWORD status;
+
+
+ if (NetR != NULL) {
+ printf("\nTestOpenEnum: Remote %ws\n", NetR->lpRemoteName);
+ }
+
+ status = NPOpenEnum(
+ Scope,
+ 0,
+ 0,
+ NetR,
+ EnumHandle
+ );
+
+
+ if (status != WN_SUCCESS) {
+ status = GetLastError();
+ }
+
+ if (status != ExpectedError) {
+ printf(" FAILED: expected %lu got %lu\n", ExpectedError, status);
+ }
+ else {
+ printf(" SUCCESS: got %lu as expected\n", status);
+ }
+
+ return status;
+}
+
+DWORD
+TestEnum(
+ IN HANDLE EnumHandle,
+ IN DWORD EntriesRequested,
+ IN LPVOID Buffer,
+ IN DWORD BufferSize,
+ OUT LPDWORD BytesNeeded,
+ IN DWORD ExpectedError
+ )
+{
+ DWORD status;
+ DWORD EntriesRead = EntriesRequested;
+
+ DWORD i;
+ LPNETRESOURCE NetR = Buffer;
+
+
+ *BytesNeeded = BufferSize;
+
+ printf("\nTestEnum: EntriesRequested x%08lx, BufferSize %lu\n",
+ EntriesRead, *BytesNeeded);
+
+ status = NPEnumResource(
+ EnumHandle,
+ &EntriesRead,
+ Buffer,
+ BytesNeeded
+ );
+
+ if (status == WN_SUCCESS) {
+
+ printf(" EntriesRead is %lu\n", EntriesRead);
+
+ for (i = 0; i < EntriesRead; i++, NetR++) {
+ PrintNetResource(NetR);
+ }
+
+ }
+ else if (status != WN_NO_MORE_ENTRIES) {
+
+ status = GetLastError();
+
+ if (status == WN_MORE_DATA) {
+ printf(" BytesNeeded is %lu\n", *BytesNeeded);
+ }
+ }
+
+ if (status != ExpectedError) {
+ printf(" FAILED: expected %lu got %lu\n", ExpectedError, status);
+ }
+ else {
+ printf(" SUCCESS: got %lu as expected\n", status);
+ }
+
+ return status;
+}
+
+VOID
+PrintNetResource(
+ LPNETRESOURCE NetR
+ )
+{
+ if (NetR->lpLocalName != NULL) {
+ printf("%-7ws", NetR->lpLocalName);
+ }
+
+ printf(" %-ws\n", NetR->lpRemoteName);
+
+ if (NetR->lpComment != NULL) {
+ printf(" %-ws\n", NetR->lpComment);
+ }
+
+ if (NetR->lpProvider != NULL) {
+ printf(" %-ws\n", NetR->lpProvider);
+ }
+
+ printf("Scope: x%lx", NetR->dwScope);
+ printf(" Type: x%lx", NetR->dwType);
+ printf(" DisplayType: x%lx", NetR->dwDisplayType);
+ printf(" Usage: x%lx\n", NetR->dwUsage);
+
+
+}
diff --git a/private/nw/svcdlls/nwwks/client/tree.bmp b/private/nw/svcdlls/nwwks/client/tree.bmp
new file mode 100644
index 000000000..e63395971
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/tree.bmp
Binary files differ
diff --git a/private/nw/svcdlls/nwwks/client/tree.ico b/private/nw/svcdlls/nwwks/client/tree.ico
new file mode 100644
index 000000000..23ea78077
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/tree.ico
Binary files differ