/*++

   Copyright    (c)    1994    Microsoft Corporation

   Module  Name:

        rpcsupp.cxx

   Abstract:

        Contains RPC functions for Gopher server administration.

   Author:

           Murali R. Krishnan    ( MuraliK )     17-Nov-1994 
   
   Project:

          Gopher Server DLL

   Functions Exported:

   DWORD  R_GdEnumerateUsers(
              IN GOPHERD_IMPERSONATE_HANDLE  pszServer,
              OUT LPGOPHERD_USER_ENUM_STRUCT lpUserBuffer);

   DWORD  R_GdDisconnectUser(
              IN GOPHERD_IMPERSONATE_HANDLE pszServer,
              IN DWORD                      dwIdUser)
        
   DWORD  R_GdGetStatistics(
              IN GOPHERD_IMPERSONATE_HANDLE pszServer,
              IN LPGOPHERD_STATISTICS_INFO  lpStat)

   DWORD  R_GdClearStatistics(
              IN GOPHERD_IMPERSONATE_HANDLE pszServer)
              
   DWORD  R_GdGetAdminInformation(
              IN  GOPHERD_IMPERSONATE_HANDLE pszServer,
              OUT LPGOPHERD_CONFIG_INFO     * ppConfigInfo)

   DWORD  R_GdSetAdminInformation(
              IN GOPHERD_IMPERSONATE_HANDLE pszServer,
              IN LPGOPHERD_CONFIG_INFO pConfigInfo)

   Revision History:
   
          MuraliK  17-Feb-1995   Removed Virtual Volumes APIs 
                               ( they are moved to services common APIs).

 --*/


/************************************************************
 *     Include Headers
 ************************************************************/

# include "gdpriv.h"

extern "C" {
# include "gd_srv.h"
# include "inetinfo.h"
};


# include "gdglobal.hxx"

/************************************************************
 *    Functions 
 ************************************************************/







extern "C"
DWORD
NET_API_FUNCTION
R_GdEnumerateUsers(
    IN GOPHERD_IMPERSONATE_HANDLE  pszServer,
    OUT LPGOPHERD_USER_ENUM_STRUCT lpUserBuffer
    )
/*++
    Description:
        This function enumerates the user information in a buffer.
        ( This is server side API function for RPC)

    Arguments:
        
        pszServer  pointer to string containing the target server ( unused)

        lpUserBuffer   pointer to enumeration buffer

    Returns:
        Win32 error code.
        NO_ERROR on success.
--*/

{
    DWORD  err = NO_ERROR;
    DWORD  cbBuffer;

    ASSERT( lpUserBuffer != NULL );

    UNREFERENCED_PARAMETER(pszServer);

    
    DEBUG_IF( RPC, {

        DBGPRINTF( ( DBG_CONTEXT, "Enumerating Users() ( RPC call)\n"));
    });

    //
    // This functionality is not yet implemented.
    //

    err = ERROR_CALL_NOT_IMPLEMENTED;

    return err;

}   // R_GdEnumerateUsers()



extern "C"
DWORD
NET_API_FUNCTION
R_GdDisconnectUser(
    IN GOPHERD_IMPERSONATE_HANDLE pszServer,
    IN DWORD                      dwIdUser
    )
/*++
    Description:
        Disconnects a specified user from the server. 
        This is a server-side worker function for RPC.

    Arguments:
        
        pszServer 
            pointer to string containing target server ( unused)

        dwIduser
            id for the user which is to be disconnected. 
            If 0 disconnect all the users.

    Returns:
        Win32 error code. NO_ERROR on success.

--*/
{
    DWORD err = NO_ERROR;

    UNREFERENCED_PARAMETER(pszServer);

    DEBUG_IF( RPC, {
    
        DBGPRINTF( ( DBG_CONTEXT, "R_GdDisconnectUser() called for user %d\n",
                    dwIdUser));
    });

    err = ERROR_CALL_NOT_IMPLEMENTED;

    return err;

}   // R_GdDisconnectUser()





extern "C"
DWORD
NET_API_FUNCTION
R_GdGetStatistics(
    IN GOPHERD_IMPERSONATE_HANDLE pszServer,
    IN LPGOPHERD_STATISTICS_INFO  lpStat
    )
/*++
    Description:
        Copies the server statistics into the buffer passed in.
        This is a server-side worker function for RPC.

    Arguments:
        pszServer
            pointer to string containing server name ( unused)

        lpStat
            pointer to statistics information buffer that will contain
             the resulting statistics data.

    Returns:
        Win32 error code. NO_ERROR on success.
    
--*/
{
    DWORD err;

    TCP_ASSERT( lpStat != NULL );

    UNREFERENCED_PARAMETER(pszServer);

    DEBUG_IF( RPC, {
        
        DBGPRINTF( ( DBG_CONTEXT, "R_GdGetStatistics() called \n"));
    
    });

    //
    //  Check for proper access.
    //

    err = TsApiAccessCheck( TCP_QUERY_STATISTICS );

    if( err != NO_ERROR ) {

        DEBUG_IF( RPC, {
            DBGPRINTF( ( DBG_CONTEXT,
                        "R_GdGetStatistics failed access check, error %lu\n",
                        err ));
        });

        return err;
    }

    err = g_pstat->CopyToStatBuffer( lpStat);

    DEBUG_IF( RPC, {
        
        DBGPRINTF( ( DBG_CONTEXT,
                    "R_GdGetStatistics()  returning statistics. Error = %d \n",
                    err));
    });

    return ( err);

} // R_GdGetStatistics();




extern "C"
DWORD
NET_API_FUNCTION
R_GdClearStatistics(
    IN GOPHERD_IMPERSONATE_HANDLE pszServer
    )
/*++
    Description:
        Clears the current server statistics. 
        This is a server-side worker function for RPC.

    Arguments:
        pszServer
            pointer to string containing the server name ( unused)

    Returns:
        Win32 error code. NO_ERROR on success.
--*/
{
    DWORD err;

    UNREFERENCED_PARAMETER(pszServer);

    DEBUG_IF( RPC, {
    
        DBGPRINTF( ( DBG_CONTEXT, " R_GdClearStatistics() called\n"));
    });


    //
    //  Check for proper access.
    //

    err = TsApiAccessCheck( TCP_CLEAR_STATISTICS );

    if( err != NO_ERROR )
    {
        
        DEBUG_IF( RPC, {

            DBGPRINTF( ( DBG_CONTEXT, 
                        "R_GdClearStatistics() failed access check."
                        " Error = %u\n",
                        err ));
        });

        return err;
    }

    //
    //  Clear the statistics.
    //

    g_pstat->ClearStatistics();

    DEBUG_IF( RPC, {
        
        DBGPRINTF( ( DBG_CONTEXT,
                    "R_GdClearStatistics()  returns Error = %d \n",
                    err));
    });

    return ( err);

}   // R_GdClearStatistics()




extern "C"
DWORD
NET_API_FUNCTION
R_GdGetAdminInformation(
    IN  GOPHERD_IMPERSONATE_HANDLE pszServer,
    OUT LPGOPHERD_CONFIG_INFO     * ppConfigInfo
    )
/*++
    Description:
       This functions obtains the current configuration information for server.

    Arguments:
        
        pszServer
            pointer to null-terminated string with the name of the server.

        ppConfigInfo
            pointer to a location where the pointer to Config data is returned.
            The caller should free the pointer after use.

    Returns:
        Win32 error code. NO_ERROR on success.

--*/
{
    DWORD err = NO_ERROR;
    LPGOPHERD_CONFIG_INFO  pLocalConfigInfo;

    UNREFERENCED_PARAMETER(pszServer);

    DEBUG_IF( RPC, {

        DBGPRINTF( ( DBG_CONTEXT, "R_GdGetAdminInformation() is called.\n" ));
    });

    if ( ppConfigInfo == NULL || *ppConfigInfo != NULL ) {
    
        return ( ERROR_INVALID_PARAMETER);
    }

    //
    //  Check for proper access.
    //

    err = TsApiAccessCheck( TCP_QUERY_ADMIN_INFORMATION);

    if( err != NO_ERROR ) {

        DEBUG_IF( RPC, {

            DBGPRINTF( ( DBG_CONTEXT, 
                        "R_GdGetAdminInformation() failed access check."
                        " Error = %u\n",
                        err));
        });
        
        return err;
    }

    //
    //  Inquire the configuration information from the GSERVER_CONFIG object
    //

    pLocalConfigInfo = ( LPGOPHERD_CONFIG_INFO ) 
                         MIDL_user_allocate( sizeof ( GOPHERD_CONFIG_INFO));

    if ( pLocalConfigInfo == NULL) {

        return ( ERROR_NOT_ENOUGH_MEMORY);
    }


    err = g_pGserverConfig->GetConfigInformation( pLocalConfigInfo);

    if ( err == NO_ERROR) {
        
        //
        // Successful in getting the config information. 
        //  Store in *ppConfigInfo for return
        //
        *ppConfigInfo = pLocalConfigInfo;
    
    } else {

        //
        // Cleanup the memory allocated for storing data
        //
        
        // Individual Cleanup is done by GSERVER_CONFIG::GetConfigInformation()

        MIDL_user_free( pLocalConfigInfo);
    }

    DEBUG_IF( RPC, {
        
        DBGPRINTF( ( DBG_CONTEXT,
                    "R_GdGetAdminInformation() returns Error = %u \n",
                    err));
    });

    return ( err);

}   // R_GdGetAdminInformation()




extern "C"
DWORD
NET_API_FUNCTION
R_GdSetAdminInformation(
    IN GOPHERD_IMPERSONATE_HANDLE pszServer,
    IN LPGOPHERD_CONFIG_INFO pConfigInfo
    )
/*++
    Description:
        Sets the configuration information for Gopher server.
        Several of the configuration data will become 
            effective immediately.

        The following parameters require a restart of the service, after
            successfully setting their new values.

            GDA_PORT_NUMBER

    Arguments:
        
        pszServer
            pointer to null-terminated string with the name of the server.

        FieldsToSet
            bitmap indicating which fields are to be set.

        pConfigInfo
            pointer to GOPHERD_CONFIG_INFO containing values for fields whose
            value are to be changed.

    Returns:
        Win32 error code. NO_ERROR on success.

--*/
{
    DWORD err;
    
    UNREFERENCED_PARAMETER(pszServer);

    DEBUG_IF( RPC, {

        DBGPRINTF( ( DBG_CONTEXT, "R_GdGetAdminInformation() is called.\n" ));
    });

    if ( pConfigInfo == NULL) {
    
        return ( ERROR_INVALID_PARAMETER);
    }

    //
    //  Check for proper access.
    //

    err = TsApiAccessCheck( TCP_SET_ADMIN_INFORMATION);

    if( err != NO_ERROR ) {

        DEBUG_IF( RPC, {

            DBGPRINTF( ( DBG_CONTEXT,
                        "R_GdSetAdminInformation() failed access check."
                        " Error = %u\n",
                        err));
        });
        
        return ( err);
    }


    if ( pConfigInfo->FieldControl == 0x0) {

        return ( NO_ERROR);     // No work to do. Redundant call
    }

# if DBG
    if ( err == NO_ERROR  && pConfigInfo->FieldControl & GDA_DEBUG_FLAGS) {

        //
        // Set Debugging flags to new value.
        //

        err = WriteRegistryDword( g_hkeyGdParams,
                                  GOPHERD_DEBUG_FLAGS,
                                  pConfigInfo->dwDebugFlags);
        
        // Only Debug Flags is directly set here
        g_GdDebugFlags = pConfigInfo->dwDebugFlags; 
        SET_DEBUG_FLAGS( pConfigInfo->dwDebugFlags);
    }
# endif // DBG

    err = g_pGserverConfig->
                SetConfigInformation( pConfigInfo);

    DEBUG_IF( RPC, {
        
        DBGPRINTF( ( DBG_CONTEXT,
                    "R_GdSetAdminInformation()  returns Error = %d \n",
                    err));
    });

    return ( err);

}   // R_GdSetAdminInformation()




/**********************************************************
 *  Local functions for RPC interface
 **********************************************************/

inline
DWORD BoolToDword( BOOL fValue)
{
    return ( fValue) ? 1 : 0;
} // BoolToDword()


inline 
VOID GdRpcFree( LPWSTR * ppszBuffer)
{
    if ( *ppszBuffer != NULL) {      
        MIDL_user_free( *ppszBuffer);
        *ppszBuffer = NULL;      
    }

    return;
} // GdRpcFree()




BOOL
GdRpcUnicodeCopy( 
    OUT LPWSTR * ppszDst,
    IN STR    & strSrc)
/*++
    Description:
        Makes a unicode copy of the string given into a 
        new buffer allocated using MIDL_user_allocate
         for RPC transfer.

    Arguments:
        ppszDst     pointer to a location where to store the copied string

        strSrc      string containing the source data

    Returns:
        BOOL. TRUE on success and FALSE on failure.
--*/
{
    DWORD cchBuffer = 0;

    // get count of bytes
    GOPHERD_REQUIRE( strSrc.CopyToBuffer( NULL, &cchBuffer)); 
    
    //
    // allocate the buffer for copy
    //
    *ppszDst = (LPWSTR ) MIDL_user_allocate( cchBuffer * sizeof( WCHAR));

    if ( *ppszDst == NULL) {
    
        SetLastError( ERROR_NOT_ENOUGH_MEMORY);    
        return ( FALSE);
    }
                
    //
    // make a copy of the string
    //
    return strSrc.CopyToBuffer( *ppszDst, &cchBuffer);
} // GdRpcUnicodeCopy()




DWORD
GSERVER_CONFIG::GetConfigInformation( 
    OUT LPGOPHERD_CONFIG_INFO pConfigInfo
    )
/*++
    Description:
        Obtains the configuration information from Gopher Server config and
        stores it in GOPHERD_CONFIG_INFO object

    Arguments:
        pConfigInfo 
            pointer to location where the data for gopher server configuration
            is stored on successful return.

    Returns:
        Win32 error code. NO_ERROR on success.
--*/
{
    DWORD   dwErr = NO_ERROR;
    STR     strUnicode;
    LPWSTR  lpszUnicode;

    //
    //  Initialize contents of *pConfigInfo to be NULL 
    //
    memset( (LPVOID) pConfigInfo, 0, sizeof( GOPHERD_CONFIG_INFO));

    //
    // Presently we dont lock config on the assumption that the data we read 
    //  from config structure can't change unless R_GdSetAdminInfo() is called,
    //  which is queued up behind R_GdGetAdminInfo()
    // No Locking is done for performance reasons.
    //
    
    //  LockConfig();

    if ( !GdRpcUnicodeCopy( & pConfigInfo->lpszSite,       m_strSite)        ||
         !GdRpcUnicodeCopy( & pConfigInfo->lpszOrganization,
                            m_strOrganization)                               ||
         !GdRpcUnicodeCopy( & pConfigInfo->lpszLocation,   m_strLocation)    ||
         !GdRpcUnicodeCopy( & pConfigInfo->lpszLanguage,   m_strLanguage)    ||
         !GdRpcUnicodeCopy( & pConfigInfo->lpszGeography,  m_strGeography)
        ) {

        dwErr = ERROR_NOT_ENOUGH_MEMORY;

        //
        // Free up the strings that may have been allocated
        //

        GdRpcFree( & pConfigInfo->lpszSite);
        GdRpcFree( & pConfigInfo->lpszOrganization);
        GdRpcFree( & pConfigInfo->lpszLocation);
        GdRpcFree( & pConfigInfo->lpszLanguage);
        GdRpcFree( & pConfigInfo->lpszGeography);
    
    } else {

        //
        // Success in copying the strings. Copy the dword data
        //

        pConfigInfo->fCheckForWaisDb = m_fCheckForWaisDb;

# if DBG
        pConfigInfo->dwDebugFlags    = g_GdDebugFlags;
# else 
        pConfigInfo->dwDebugFlags    = 0;
# endif // DBG

    }
    
    // UnlockConfig();

    return ( dwErr);

} // GSERVER_CONFIG::GetConfigInformation()




inline
DWORD
WriteConfigDword( 
    IN LPCTSTR lpszValueName,
    IN DWORD    dwNewValue)
/*++
    Description:
        Thin wrapper for WriteRegistryDword() to enable printing
        a log message for errors if any.

    Arguments:
        lpszValueName   name of the registry entry under reg key
                        to be modified.
        dwNewValue      new value for the entry

    Returns:
        Win32 error codes

--*/
{
    DWORD err = WriteRegistryDword( 
                    g_hkeyGdParams,
                    lpszValueName,
                    dwNewValue);

    DEBUG_IF( RPC, {

        if ( err != NO_ERROR) {

            DBGPRINTF( ( DBG_CONTEXT,
                        " WriteRegistryDword() failed for %s( %d)\n",
                        lpszValueName,
                        dwNewValue));
        }
    });
    
    return  ( err);    

} // WriteConfigDword()




DWORD
WriteConfigString( 
    IN LPCTSTR  lpszValueName,
    IN LPWSTR   lpszNewValue,
    IN DWORD    fdwType)
/*++
    Description:
        Writes the new configuration ( string) value to registry name
            given using the global registry handle ( g_hkeyGdParams)
        A thin wrapper around WriteRegistryStringA() which
        converts given string into ANSI and writes data to registry
        on success.

        If no new value is specified, do nothing and return NO_ERROR.

    Arguments:
        lpszValueName       name of the registry entry to be modified.
        lpszNewValue        new value for given entry
        fdwType             type of registry entry

    Returns:
        Win32 error code. NO_ERROR on success.

--*/
{
    DWORD err = NO_ERROR;
    char * pszNewValueAnsi;

    if ( lpszNewValue == NULL) {
    
        //
        // Ignore this field. Dont reset to any new value.
        //

        return ( NO_ERROR);
    
    }    

    ASSERT( g_hkeyGdParams != NULL);

    pszNewValueAnsi = ConvertUnicodeToAnsi( lpszNewValue, NULL);

    if ( pszNewValueAnsi != NULL) {
        err = WriteRegistryStringA( 
                g_hkeyGdParams,
                lpszValueName,
                pszNewValueAnsi,
                strlen( pszNewValueAnsi) + 1,
                REG_SZ);
    
        TCP_FREE( pszNewValueAnsi);       // free the new value
    } else {
            
        err = ERROR_NOT_ENOUGH_MEMORY;
    }

    DEBUG_IF( RPC, {
        
        if ( err != NO_ERROR) {

            DBGPRINTF( ( DBG_CONTEXT,
                        " WriteRegistryString() failed for %s( %S)\n",
                        lpszValueName, lpszNewValue));
        }
    });

    return ( err);
} // WriteConfigString()






DWORD
GSERVER_CONFIG::SetConfigInformation( 
    IN  LPGOPHERD_CONFIG_INFO pConfigInfo
    )
/*++
    Description:
        Sets the configuration information for Gopher server.
        Several of the configuration data will become 
            effective immediately.

        The following parameters require a restart of the service, after
            successfully setting their new values.

            GDA_PORT_NUMBER

    Arguments:

        pConfigInfo
            pointer to GOPHERD_CONFIG_INFO containing values for fields whose
            value are to be changed.

    Returns:
        Win32 error code. NO_ERROR on success.

    Note:
        Error checking is limited to one at a time. 
        No undo is performed if an error is detected during the updations.
                                                        
--*/
{
    DWORD  err = NO_ERROR;
    DWORD  FieldsToSet = pConfigInfo->FieldControl;

    ASSERT( FieldsToSet != 0x0);

    //
    //  The tradeoffs involved here are:
    //  1) we need to update the registry before setting it in current config
    //  2) multiple attributes being set ==> can lead to compound errors
    //  3) We fail at first error
    //  4) We dont do any cleanups if we fail half way thru
    //  5)  i.e. atomicity is not guaranteed.
    //  6) Memory allocated is completely freed --> no memory leakages occur
    //  7) For performance reasons we may not want to do
    //      bulk registry reads to reinitialize values. 
    //      So we use the FieldControl to selectively read from registry
    //


    //
    // Phase 1: Do writes of the new data to registry
    //

    //
    //  Write all string data to registry 
    //

    if ( err == NO_ERROR && IsFieldSet( FieldsToSet, GDA_CHECK_FOR_WAISDB)) {

        err = WriteConfigDword( GOPHERD_CHECK_FOR_WAISDB,
                               pConfigInfo->fCheckForWaisDb
                               );
    }

    if ( err == NO_ERROR && IsFieldSet( FieldsToSet, GDA_SITE)) {

        err = WriteConfigString( GOPHERD_SITE,
                                 pConfigInfo->lpszSite,
                                 REG_SZ);
    } 

    if ( err == NO_ERROR && IsFieldSet( FieldsToSet, GDA_ORGANIZATION)) {

        err = WriteConfigString( GOPHERD_ORGANIZATION,
                                 pConfigInfo->lpszOrganization,
                                 REG_SZ);
    } 

    if ( err == NO_ERROR && IsFieldSet( FieldsToSet, GDA_LOCATION)) {

        err = WriteConfigString( GOPHERD_LOCATION,
                                 pConfigInfo->lpszLocation,
                                 REG_SZ);
    } 

    if ( err == NO_ERROR && IsFieldSet( FieldsToSet, GDA_GEOGRAPHY)) {

        err = WriteConfigString( GOPHERD_GEOGRAPHY,
                                 pConfigInfo->lpszGeography,
                                 REG_SZ);
    } 

    if ( err == NO_ERROR && IsFieldSet( FieldsToSet, GDA_LANGUAGE)) {

        err = WriteConfigString( GOPHERD_LANGUAGE,
                                 pConfigInfo->lpszLanguage,
                                 REG_SZ);
    } 

    //
    // Phase 2: Reinitialize the configuration information.
    //  By using the InitFromRegistry() member function
    //

    if ( err == NO_ERROR) {

        DEBUG_IF( RPC, {
            
            DBGPRINTF( ( DBG_CONTEXT,
                        "Successful writing to registry for Fields ( %08x)\n",
                        FieldsToSet));
        });
        
        err = InitFromRegistry( g_hkeyGdParams, FieldsToSet);
    }

   return ( err);
} // GSERVER_CONFIG::SetConfigInformation()


/************************ End of File ***********************/