/*++

Copyright (c) 1990  Microsoft Corporation

Module Name:

    srvnls.c

Abstract:

    This file contains the NLS Server-Side routines.

Author:

    Julie Bennett (JulieB) 02-Dec-1992

Revision History:

--*/



#include "basesrv.h"




/*
 *  Constant Declarations.
 */
#define MAX_PATH_LEN        512        /* max length of path name */

#define MAX_KEY_VALUE_PARTINFO                                              \
    ( FIELD_OFFSET( KEY_VALUE_PARTIAL_INFORMATION, Data ) + MAX_REG_VAL_SIZE )


#define NLS_VALUE_STIMEFORMAT      L"sTimeFormat"
#define NLS_VALUE_STIME            L"sTime"
#define NLS_VALUE_ITIME            L"iTime"
#define NLS_VALUE_ITLZERO          L"iTLZero"
#define NLS_VALUE_ITIMEMARKPOSN    L"iTimePrefix"
#define NLS_VALUE_SSHORTDATE       L"sShortDate"
#define NLS_VALUE_SDATE            L"sDate"
#define NLS_VALUE_IDATE            L"iDate"



/*
 *  Typedef Declarations.
 */

/*
 *  These MUST remain in the same order as the NLS_USER_INFO structure.
 */
LPWSTR pCPanelRegValues[] =
{
    L"sLanguage",
    L"iCountry",
    L"sCountry",
    L"sList",
    L"iMeasure",
    L"sDecimal",
    L"sThousand",
    L"sGrouping",
    L"iDigits",
    L"iLZero",
    L"iNegNumber",
    L"sCurrency",
    L"sMonDecimalSep",
    L"sMonThousandSep",
    L"sMonGrouping",
    L"iCurrDigits",
    L"iCurrency",
    L"iNegCurr",
    L"sPositiveSign",
    L"sNegativeSign",
    L"sTimeFormat",
    L"sTime",
    L"iTime",
    L"iTLZero",
    L"iTimePrefix",
    L"s1159",
    L"s2359",
    L"sShortDate",
    L"sDate",
    L"iDate",
    L"sLongDate",
    L"iCalendarType",
    L"iFirstDayOfWeek",
    L"iFirstWeekOfYear",
    L"Locale"
};

int NumCPanelRegValues = ( sizeof(pCPanelRegValues) / sizeof(LPWSTR) );



/*
 *  Global Variables.
 */
HANDLE hNlsCacheMutant;
HANDLE hCPanelIntlKeyRead = NULL;
HANDLE hCPanelIntlKeyWrite = NULL;
PNLS_USER_INFO pNlsRegUserInfo = NULL;
ULONG NlsChangeBuffer;
IO_STATUS_BLOCK IoStatusBlock;



/*
 *  Forward Declarations.
 */
ULONG
NlsSetRegAndCache(
    LPWSTR pValue,
    LPWSTR pCacheString,
    LPWSTR pData,
    ULONG DataLength);

VOID
NlsUpdateCacheInfo(VOID);

ULONG
CreateSecurityDescriptor(
    ULONG *pSecurityDescriptor,
    PSID *ppWorldSid,
    ACCESS_MASK AccessMask);




/***************************************************************************\
* BaseSrvNLSInit
*
* This routine creates the shared heap for the nls information.
*
* 08-19-94    JulieB    Created.
\***************************************************************************/

NTSTATUS
BaseSrvNLSInit(
    PBASE_STATIC_SERVER_DATA pStaticServerData)
{
    ULONG rc;                     /* return code */


    /*
     *  Create a mutant to protect the cache.
     */
    rc = NtCreateMutant( &hNlsCacheMutant,
                         MUTANT_ALL_ACCESS,
                         NULL,
                         FALSE );
    if (!NT_SUCCESS( rc ))
    {
        KdPrint(("NLSAPI (BaseSrv): Could NOT Create Cache Mutex - %lx.\n", rc));
        return ( rc );
    }

    /*
     *  Initialize the cache to zero.
     */
    pNlsRegUserInfo = &(pStaticServerData->NlsUserInfo);
    RtlZeroMemory( pNlsRegUserInfo, sizeof(NLS_USER_INFO) );

    /*
     * Make the system locale the user locale.
     */
    NtQueryDefaultLocale( FALSE, &(pNlsRegUserInfo->UserLocaleId) );

    /*
     *  Return success.
     */
    return ( STATUS_SUCCESS );
}


/***************************************************************************\
* BaseSrvNLSConnect
*
* This routine duplicates the mutant handle for the client.
*
* 08-19-94    JulieB    Created.
\***************************************************************************/

NTSTATUS
BaseSrvNlsConnect(
    PCSR_PROCESS Process,
    PVOID pConnectionInfo,
    PULONG pConnectionInfoLength)
{
    /*
     *  Duplicate the mutant handle.
     */
    return ( NtDuplicateObject(NtCurrentProcess(),
                               hNlsCacheMutant,
                               Process->ProcessHandle,
                               (PHANDLE)pConnectionInfo,
                               SYNCHRONIZE,
                               0L,
                               0L ) );
}

/***************************************************************************\
* BaseSrvNlsLogon
*
* This routine initializes the heap for the nls information.  If fLogon is
* TRUE, then it opens registry key, initializes the heap information, and
* registers the key for notification.  If fLogon is FALSE, then it
* unregisters the key for notification, zeros out the heap information,
* and closes the registry key.
*
* 08-19-94    JulieB    Created.
\***************************************************************************/

NTSTATUS
BaseSrvNlsLogon(
    BOOL fLogon)
{
    HANDLE hUserHandle;                /* HKEY_CURRENT_USER equivalent */
    HANDLE hKeyHandle;
    OBJECT_ATTRIBUTES ObjA;            /* object attributes structure */
    UNICODE_STRING ObKeyName;          /* key name */
    ULONG rc = 0L;                     /* return code */


    if (fLogon)
    {
        /*
         *  Logging ON.
         *     - open keys
         *
         *  NOTE: Registry Notification is done by the RIT in user server.
         */
        rc = RtlOpenCurrentUser( MAXIMUM_ALLOWED, &hUserHandle );
        if (!NT_SUCCESS( rc ))
        {
            KdPrint(("NLSAPI (BaseSrv): Could NOT Open HKEY_CURRENT_USER - %lx.\n", rc));
            return ( rc );
        }


        RtlInitUnicodeString( &ObKeyName, L"Control Panel\\International" );
        InitializeObjectAttributes( &ObjA,
                                    &ObKeyName,
                                    OBJ_CASE_INSENSITIVE,
                                    hUserHandle,
                                    NULL );

        /*
         *  Open key for READ and NOTIFY access.
         */
        rc = NtOpenKey( &hCPanelIntlKeyRead,
                        KEY_READ | KEY_NOTIFY,
                        &ObjA );

        /*
         *  Open key for WRITE access.
         */
        if (!NT_SUCCESS( NtOpenKey( &hCPanelIntlKeyWrite,
                                    KEY_WRITE,
                                    &ObjA ) ))
        {
            KdPrint(("NLSAPI (BaseSrv): Could NOT Open Registry Key %wZ for Write - %lx.\n",
                     &ObKeyName, rc));
            hCPanelIntlKeyWrite = NULL;
        }

        /*
         *  Close the handle to the current user (HKEY_CURRENT_USER).
         */
        NtClose( hUserHandle );

        /*
         *  Check for error from first NtOpenKey.
         */
        if (!NT_SUCCESS( rc ))
        {
            KdPrint(("NLSAPI (BaseSrv): Could NOT Open Registry Key %wZ for Read - %lx.\n",
                     &ObKeyName, rc));
            hCPanelIntlKeyRead = NULL;

            if (hCPanelIntlKeyWrite != NULL)
            {
                NtClose( hCPanelIntlKeyWrite );
                hCPanelIntlKeyWrite = NULL;
            }
            return ( rc );
        }
    }
    else
    {
        /*
         *  Logging OFF.
         *     - close keys
         *     - zero out info
         */
        if (hCPanelIntlKeyRead != NULL)
        {
            NtClose( hCPanelIntlKeyRead );
            hCPanelIntlKeyRead = NULL;
        }

        if (hCPanelIntlKeyWrite != NULL)
        {
            NtClose( hCPanelIntlKeyWrite );
            hCPanelIntlKeyWrite = NULL;
        }

        /*
         *  Get the cache mutant.
         */
        NtWaitForSingleObject( hNlsCacheMutant, FALSE, NULL );

        /*
         *  Set the cache to be invalid.
         */
        pNlsRegUserInfo->fCacheValid = FALSE;

        /*
         *  Zero out info.
         */
        RtlZeroMemory(pNlsRegUserInfo, sizeof(NLS_USER_INFO));

        /*
         * Make the system locale the user locale.
         */
        NtQueryDefaultLocale( FALSE, &(pNlsRegUserInfo->UserLocaleId) );

        /*
         *  Release the cache mutant.
         */
        NtReleaseMutant( hNlsCacheMutant, NULL );
    }

    /*
     *  Return success.
     */
    return ( STATUS_SUCCESS );
}


/***************************************************************************\
* BaseSrvNlsUpdateRegistryCache
*
* This routine updates the NLS cache when a registry notification occurs.
*
* 08-19-94    JulieB    Created.
\***************************************************************************/

VOID
BaseSrvNlsUpdateRegistryCache(
    PVOID ApcContext,
    PIO_STATUS_BLOCK pIoStatusBlock)
{
    ULONG rc = 0L;                     /* return code */


    if (hCPanelIntlKeyRead != NULL)
    {
        /*
         *  Update the cache information.
         */
        NlsUpdateCacheInfo();

        /*
         *  Call NtNotifyChangeKey.
         */
        rc = NtNotifyChangeKey( hCPanelIntlKeyRead,
                                NULL,
                                (PIO_APC_ROUTINE)BaseSrvNlsUpdateRegistryCache,
                                NULL,
                                &IoStatusBlock,
                                REG_NOTIFY_CHANGE_LAST_SET | REG_NOTIFY_CHANGE_NAME,
                                FALSE,
                                &NlsChangeBuffer,
                                sizeof(NlsChangeBuffer),
                                TRUE );

#ifdef DBG
        /*
         *  Check for error from NtNotifyChangeKey.
         */
        if (!NT_SUCCESS( rc ))
        {
            KdPrint(("NLSAPI (BaseSrv): Could NOT Set Notification of Control Panel International Registry Key - %lx.\n",
                     rc));
        }
#endif
    }
}


/***************************************************************************\
* NlsSetRegAndCache
*
* This routine sets the registry with the appropriate string and then
* updates the cache.
*
* NOTE: Must already own the mutant for the cache before calling this
*       routine.
*
* 08-19-94    JulieB    Created.
\***************************************************************************/

ULONG
NlsSetRegAndCache(
    LPWSTR pValue,
    LPWSTR pCacheString,
    LPWSTR pData,
    ULONG DataLength)
{
    UNICODE_STRING ObValueName;             /* value name */

    ULONG rc;                               /* return code */


    if (hCPanelIntlKeyWrite != NULL)
    {
        /*
         *  Set the value in the registry.
         */
        RtlInitUnicodeString( &ObValueName, pValue );

        rc = NtSetValueKey( hCPanelIntlKeyWrite,
                            &ObValueName,
                            0,
                            REG_SZ,
                            (PVOID)pData,
                            DataLength );

        /*
         *  Copy the new string to the cache.
         */
        if (NT_SUCCESS( rc ))
        {
            wcsncpy( pCacheString, pData, DataLength );
            pCacheString[DataLength / sizeof(WCHAR)] = 0;
        }

        /*
         *  Return the result.
         */
        return ( rc );
    }

    /*
     *  Return access denied, since the key is not open for write access.
     */
    return ( (ULONG)STATUS_ACCESS_DENIED );
}


/***************************************************************************\
* BaseSrvNlsSetUserInfo
*
* This routine sets a particular value in the NLS cache and updates the
* registry entry.
*
* 08-19-94    JulieB    Created.
\***************************************************************************/

ULONG
BaseSrvNlsSetUserInfo(
    IN OUT PCSR_API_MSG m,
    IN OUT PCSR_REPLY_STATUS ReplyStatus)
{
    PBASE_NLS_SET_USER_INFO_MSG a =
        (PBASE_NLS_SET_USER_INFO_MSG)&m->u.ApiMessageData;

    ULONG rc;                               /* return code */


    /*
     *  Get the cache mutant.
     */
    NtWaitForSingleObject( hNlsCacheMutant, FALSE, NULL );

    /*
     *  Set the value in the registry and update the cache.
     */
    rc = NlsSetRegAndCache( a->pValue,
                            a->pCacheString,
                            a->pData,
                            a->DataLength );

    /*
     *  Release the cache mutant.
     */
    NtReleaseMutant( hNlsCacheMutant, NULL );

    /*
     *  Return the result of NtSetValueKey.
     */
    return (rc);

    ReplyStatus;    // get rid of unreferenced parameter warning message
}


/***************************************************************************\
* BaseSrvNlsSetMultipleUserInfo
*
* This routine sets the date/time strings in the NLS cache and updates the
* registry entries.
*
* This call is done so that only one client/server transition is needed
* when setting multiple entries.
*
* 08-19-94    JulieB    Created.
\***************************************************************************/

ULONG
BaseSrvNlsSetMultipleUserInfo(
    IN OUT PCSR_API_MSG m,
    IN OUT PCSR_REPLY_STATUS ReplyStatus)
{
    PBASE_NLS_SET_MULTIPLE_USER_INFO_MSG a =
        (PBASE_NLS_SET_MULTIPLE_USER_INFO_MSG)&m->u.ApiMessageData;

    ULONG rc = 0L;                     /* return code */


    /*
     *  Get the cache mutant.
     */
    NtWaitForSingleObject( hNlsCacheMutant, FALSE, NULL );

    switch (a->Flags)
    {
        case ( LOCALE_STIMEFORMAT ) :
        {
            rc = NlsSetRegAndCache( NLS_VALUE_STIMEFORMAT,
                                    pNlsRegUserInfo->sTimeFormat,
                                    a->pPicture,
                                    a->DataLength );
            if (NT_SUCCESS( rc ))
            {
                rc = NlsSetRegAndCache( NLS_VALUE_STIME,
                                        pNlsRegUserInfo->sTime,
                                        a->pSeparator,
                                        (wcslen(a->pSeparator) + 1) * sizeof(WCHAR) );
            }
            if (NT_SUCCESS( rc ))
            {
                rc = NlsSetRegAndCache( NLS_VALUE_ITIME,
                                        pNlsRegUserInfo->iTime,
                                        a->pOrder,
                                        (wcslen(a->pOrder) + 1) * sizeof(WCHAR) );
            }
            if (NT_SUCCESS( rc ))
            {
                rc = NlsSetRegAndCache( NLS_VALUE_ITLZERO,
                                        pNlsRegUserInfo->iTLZero,
                                        a->pTLZero,
                                        (wcslen(a->pTLZero) + 1) * sizeof(WCHAR) );
            }
            if (NT_SUCCESS( rc ))
            {
                rc = NlsSetRegAndCache( NLS_VALUE_ITIMEMARKPOSN,
                                        pNlsRegUserInfo->iTimeMarkPosn,
                                        a->pTimeMarkPosn,
                                        (wcslen(a->pTimeMarkPosn) + 1) * sizeof(WCHAR) );
            }

            break;
        }

        case ( LOCALE_STIME ) :
        {
            rc = NlsSetRegAndCache( NLS_VALUE_STIME,
                                    pNlsRegUserInfo->sTime,
                                    a->pSeparator,
                                    a->DataLength );
            if (NT_SUCCESS( rc ))
            {
                rc = NlsSetRegAndCache( NLS_VALUE_STIMEFORMAT,
                                        pNlsRegUserInfo->sTimeFormat,
                                        a->pPicture,
                                        (wcslen(a->pPicture) + 1) * sizeof(WCHAR) );
            }

            break;
        }

        case ( LOCALE_ITIME ) :
        {
            rc = NlsSetRegAndCache( NLS_VALUE_ITIME,
                                    pNlsRegUserInfo->iTime,
                                    a->pOrder,
                                    a->DataLength );
            if (NT_SUCCESS( rc ))
            {
                rc = NlsSetRegAndCache( NLS_VALUE_STIMEFORMAT,
                                        pNlsRegUserInfo->sTimeFormat,
                                        a->pPicture,
                                        (wcslen(a->pPicture) + 1) * sizeof(WCHAR) );
            }

            break;
        }

        case ( LOCALE_SSHORTDATE ) :
        {
            rc = NlsSetRegAndCache( NLS_VALUE_SSHORTDATE,
                                    pNlsRegUserInfo->sShortDate,
                                    a->pPicture,
                                    a->DataLength );
            if (NT_SUCCESS( rc ))
            {
                rc = NlsSetRegAndCache( NLS_VALUE_SDATE,
                                        pNlsRegUserInfo->sDate,
                                        a->pSeparator,
                                        (wcslen(a->pSeparator) + 1) * sizeof(WCHAR) );
            }
            if (NT_SUCCESS( rc ))
            {
                rc = NlsSetRegAndCache( NLS_VALUE_IDATE,
                                        pNlsRegUserInfo->iDate,
                                        a->pOrder,
                                        (wcslen(a->pOrder) + 1) * sizeof(WCHAR) );
            }

            break;
        }

        case ( LOCALE_SDATE ) :
        {
            rc = NlsSetRegAndCache( NLS_VALUE_SDATE,
                                    pNlsRegUserInfo->sDate,
                                    a->pSeparator,
                                    a->DataLength );
            if (NT_SUCCESS( rc ))
            {
                rc = NlsSetRegAndCache( NLS_VALUE_SSHORTDATE,
                                        pNlsRegUserInfo->sShortDate,
                                        a->pPicture,
                                        (wcslen(a->pPicture) + 1) * sizeof(WCHAR) );
            }

            break;
        }
    }

    /*
     *  Release the cache mutant.
     */
    NtReleaseMutant( hNlsCacheMutant, NULL );

    /*
     *  Return the result.
     */
    return (rc);


    ReplyStatus;    // get rid of unreferenced parameter warning message
}


/***************************************************************************\
* NlsUpdateCacheInfo
*
* This routine updates the NLS cache when a registry notification occurs.
*
* 08-19-94    JulieB    Created.
\***************************************************************************/

VOID
NlsUpdateCacheInfo()
{
    LCID Locale;                       /* locale id */
    UNICODE_STRING ObKeyName;          /* key name */
    LPWSTR pTmp;                       /* tmp string pointer */
    int ctr;                           /* loop counter */
    ULONG ResultLength;                /* result length */
    ULONG rc = 0L;                     /* return code */

    BYTE KeyValuePart[MAX_KEY_VALUE_PARTINFO];
    PKEY_VALUE_PARTIAL_INFORMATION pValuePart;


    /*
     *  Get the cache mutant.
     */
    NtWaitForSingleObject( hNlsCacheMutant, FALSE, NULL );

    /*
     *  Update the cache information.
     */
    pTmp = (LPWSTR)pNlsRegUserInfo;
    pValuePart = (PKEY_VALUE_PARTIAL_INFORMATION)KeyValuePart;
    for (ctr = 0; ctr < NumCPanelRegValues; ctr++)
    {
        RtlInitUnicodeString( &ObKeyName, pCPanelRegValues[ctr] );
        rc = NtQueryValueKey( hCPanelIntlKeyRead,
                              &ObKeyName,
                              KeyValuePartialInformation,
                              pValuePart,
                              MAX_KEY_VALUE_PARTINFO,
                              &ResultLength );
        if (NT_SUCCESS( rc ))
        {
            ((LPBYTE)pValuePart)[ResultLength] = UNICODE_NULL;
            wcscpy(pTmp, (LPWSTR)(pValuePart->Data));
        }
        else
        {
            *pTmp = NLS_INVALID_INFO_CHAR;
            *(pTmp + 1) = UNICODE_NULL;
        }

        /*
         *  Increment pointer to cache structure.
         */
        pTmp += MAX_REG_VAL_SIZE;
    }

    /*
     *  Convert the user locale id string to a dword value and store
     *  it in the cache.
     */
    pNlsRegUserInfo->UserLocaleId = (LCID)0;
    if ((pNlsRegUserInfo->sLocale)[0] != NLS_INVALID_INFO_CHAR)
    {
        RtlInitUnicodeString( &ObKeyName, pNlsRegUserInfo->sLocale );
        if (NT_SUCCESS(RtlUnicodeStringToInteger( &ObKeyName, 16, &Locale )))
        {
            pNlsRegUserInfo->UserLocaleId = Locale;
        }
    }

    /*
     *  Make sure the user locale id was found.  Otherwise, set it to
     *  the system locale.
     */
    if (pNlsRegUserInfo->UserLocaleId == 0)
    {
        NtQueryDefaultLocale( FALSE, &(pNlsRegUserInfo->UserLocaleId) );
    }

    /*
     *  Set the cache to be valid.
     */
    pNlsRegUserInfo->fCacheValid = TRUE;

    /*
     *  Release the cache mutant.
     */
    NtReleaseMutant( hNlsCacheMutant, NULL );
}


/***************************************************************************\
* BaseSrvNlsCreateSortSection
*
* This routine creates a named memory mapped section with the given
* section name, and has both READ and WRITE access.  The size of the
* section should be the same as the default section - NLSSECTION_SORTKEY.
*
* 12-02-92    JulieB    Created.
\***************************************************************************/

ULONG
BaseSrvNlsCreateSortSection(
    IN OUT PCSR_API_MSG m,
    IN OUT PCSR_REPLY_STATUS ReplyStatus)
{
    PBASE_NLS_CREATE_SORT_SECTION_MSG a =
        (PBASE_NLS_CREATE_SORT_SECTION_MSG)&m->u.ApiMessageData;

    HANDLE hNewSec = (HANDLE)0;              /* new section handle */
    HANDLE hProcess = (HANDLE)0;             /* process handle */
    OBJECT_ATTRIBUTES ObjA;                  /* object attributes structure */
    NTSTATUS rc = 0L;                        /* return code */
    ULONG pSecurityDescriptor[MAX_PATH_LEN]; /* security descriptor buffer */
    PSID pWorldSid;                          /* ptr to world SID */


    /*
     *  Set the handles to null.
     */
    a->hNewSection = NULL;

    /*
     *  Create the NEW Section for Read and Write access.
     *  Add a ReadOnly security descriptor so that only the
     *  initial creating process may write to the section.
     */
    if (rc = CreateSecurityDescriptor(pSecurityDescriptor,
                                      &pWorldSid,
                                      GENERIC_READ))
    {
        return (rc);
    }

    InitializeObjectAttributes(&ObjA,
                               &(a->SectionName),
                               OBJ_PERMANENT | OBJ_CASE_INSENSITIVE,
                               NULL,
                               pSecurityDescriptor);

    rc = NtCreateSection(&hNewSec,
                         SECTION_MAP_READ | SECTION_MAP_WRITE,
                         &ObjA,
                         &(a->SectionSize),
                         PAGE_READWRITE,
                         SEC_COMMIT,
                         NULL);

    /*
     *  Check for error from NtCreateSection.
     */
    if (!NT_SUCCESS(rc))
    {
        /*
         *  If the name has already been created, ignore the error.
         */
        if (rc != STATUS_OBJECT_NAME_COLLISION)
        {
            KdPrint(("NLSAPI (BaseSrv): Could NOT Create Section %wZ - %lx.\n",
                     &(a->SectionName), rc));
            return (rc);
        }
    }

    /*
     *  Duplicate the new section handle for the client.
     *  The client will map a view of the section and fill in the data.
     */
    InitializeObjectAttributes(&ObjA,
                               NULL,
                               0,
                               NULL,
                               NULL);

    rc = NtOpenProcess(&hProcess,
                       PROCESS_DUP_HANDLE,
                       &ObjA,
                       &m->h.ClientId);

    if (!NT_SUCCESS(rc))
    {
        KdPrint(("NLSAPI (BaseSrv): Could NOT Open Process - %lx.\n", rc));
        return (rc);
    }

    rc = NtDuplicateObject(NtCurrentProcess(),
                           hNewSec,
                           hProcess,
                           &(a->hNewSection),
                           0L,
                           0L,
                           DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);

    /*
     *  Return the return value from NtDuplicateObject.
     */
    return (rc);

    ReplyStatus;    // get rid of unreferenced parameter warning message
}


/***************************************************************************\
* BaseSrvNlsPreserveSection
*
* This routine preserves a created section by duplicating the handle
* into CSR and never closing it.
*
* 03-12-93    JulieB    Created.
\***************************************************************************/

ULONG
BaseSrvNlsPreserveSection(
    IN OUT PCSR_API_MSG m,
    IN OUT PCSR_REPLY_STATUS ReplyStatus)
{
    PBASE_NLS_PRESERVE_SECTION_MSG a =
        (PBASE_NLS_PRESERVE_SECTION_MSG)&m->u.ApiMessageData;

    HANDLE hSection = (HANDLE)0;            /* section handle */
    HANDLE hProcess = (HANDLE)0;            /* process handle */
    OBJECT_ATTRIBUTES ObjA;                 /* object attributes structure */
    NTSTATUS rc = 0L;                       /* return code */


    /*
     *  Duplicate the section handle for the server.
     */
    InitializeObjectAttributes(&ObjA,
                               NULL,
                               0,
                               NULL,
                               NULL);

    rc = NtOpenProcess(&hProcess,
                       PROCESS_DUP_HANDLE,
                       &ObjA,
                       &m->h.ClientId);

    if (!NT_SUCCESS(rc))
    {
        KdPrint(("NLSAPI (BaseSrv): Could NOT Open Process - %lx.\n", rc));
        return (rc);
    }

    /*
     *  The hSection value will not be used for anything.  However,
     *  it must remain open so that the object remains permanent.
     */
    rc = NtDuplicateObject(hProcess,
                           a->hSection,
                           NtCurrentProcess(),
                           &hSection,
                           0L,
                           0L,
                           DUPLICATE_SAME_ACCESS);

    /*
     *  Return the return value from NtDuplicateObject.
     */
    return (rc);

    ReplyStatus;    // get rid of unreferenced parameter warning message
}


/***************************************************************************\
* CreateSecurityDescriptor
*
* This routine creates the security descriptor needed to create the
* memory mapped section for a data file and returns the world SID.
*
* 12-02-92    JulieB    Created.
\***************************************************************************/

ULONG CreateSecurityDescriptor(
    ULONG *pSecurityDescriptor,
    PSID *ppWorldSid,
    ACCESS_MASK AccessMask)
{
    ULONG rc = 0L;                     /* return code */
    PACL pAclBuffer;                   /* ptr to ACL buffer */
    ULONG SidLength;                   /* length of SID - 1 sub authority */
    PSID pWSid;                        /* ptr to world SID */
    SID_IDENTIFIER_AUTHORITY SidAuth = SECURITY_WORLD_SID_AUTHORITY;


    /*
     *  Create World SID.
     */
    SidLength = RtlLengthRequiredSid(1);

    if ((pWSid = (PSID)RtlAllocateHeap(RtlProcessHeap(),
                                       MAKE_TAG( TMP_TAG ) | HEAP_ZERO_MEMORY,
                                       SidLength)) == NULL)
    {
        *ppWorldSid = NULL;
        KdPrint(("NLSAPI (BaseSrv): Could NOT Allocate SID Buffer.\n"));
        return (ERROR_OUTOFMEMORY);
    }
    *ppWorldSid = pWSid;

    RtlInitializeSid(pWSid, &SidAuth, 1);

    *(RtlSubAuthoritySid(pWSid, 0)) = SECURITY_WORLD_RID;

    /*
     *  Initialize Security Descriptor.
     */
    rc = RtlCreateSecurityDescriptor(pSecurityDescriptor,
                                     SECURITY_DESCRIPTOR_REVISION);
    if (!NT_SUCCESS(rc))
    {
        KdPrint(("NLSAPI (BaseSrv): Could NOT Create Security Descriptor - %lx.\n",
                 rc));
        RtlFreeHeap(RtlProcessHeap(), 0, (PVOID)pWSid);
        return (rc);
    }

    /*
     *  Initialize ACL.
     */
    pAclBuffer = (PACL)((PBYTE)pSecurityDescriptor + SECURITY_DESCRIPTOR_MIN_LENGTH);
    rc = RtlCreateAcl((PACL)pAclBuffer,
                      MAX_PATH_LEN * sizeof(ULONG),
                      ACL_REVISION2);
    if (!NT_SUCCESS(rc))
    {
        KdPrint(("NLSAPI (BaseSrv): Could NOT Create ACL - %lx.\n", rc));
        RtlFreeHeap(RtlProcessHeap(), 0, (PVOID)pWSid);
        return (rc);
    }

    /*
     *  Add an ACE to the ACL that allows World GENERIC_READ to the
     *  section object.
     */
    rc = RtlAddAccessAllowedAce((PACL)pAclBuffer,
                                ACL_REVISION2,
                                AccessMask,
                                pWSid);
    if (!NT_SUCCESS(rc))
    {
        KdPrint(("NLSAPI (BaseSrv): Could NOT Add Access Allowed ACE - %lx.\n",
                 rc));
        RtlFreeHeap(RtlProcessHeap(), 0, (PVOID)pWSid);
        return (rc);
    }

    /*
     *  Assign the DACL to the security descriptor.
     */
    rc = RtlSetDaclSecurityDescriptor((PSECURITY_DESCRIPTOR)pSecurityDescriptor,
                                      (BOOLEAN)TRUE,
                                      (PACL)pAclBuffer,
                                      (BOOLEAN)FALSE);
    if (!NT_SUCCESS(rc))
    {
        KdPrint(("NLSAPI (BaseSrv): Could NOT Set DACL Security Descriptor - %lx.\n",
                 rc));
        RtlFreeHeap(RtlProcessHeap(), 0, (PVOID)pWSid);
        return (rc);
    }

    /*
     *  Return success.
     */
    return (NO_ERROR);
}