/*++

Copyright (c) 1991  Microsoft Corporation

Module Name:

    infostub.c

Abstract:

    Client stubs of the Internet Info Server Admin APIs.

Author:

    Madan Appiah (madana) 10-Oct-1993

Environment:

    User Mode - Win32

--*/

#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>

#include "info_cli.h"

#include <ntsam.h>
#include <ntlsa.h>

#include <ftpd.h>
#include <w3svc.h>
#include <pop3s.h>
#include <nntps.h>
#include <smtps.h>
#include <chat.h>
#include <ldaps.h>
#include <imaps.h>

//
//  Quick macro to initialize a unicode string
//

WCHAR  g_wchUnicodeNull[] = L"";

#define _InitUnicodeString( pUnicode, pwch )                       \
   {                                                               \
        (pUnicode)->Buffer    = pwch;                              \
        (pUnicode)->Length    = wcslen( pwch ) * sizeof(WCHAR);    \
        (pUnicode)->MaximumLength = (pUnicode)->Length + sizeof(WCHAR); \
   }

# define InitUnicodeString( pUnicode, pwch)  \
   if (pwch == NULL) { _InitUnicodeString( pUnicode, g_wchUnicodeNull); } \
   else              { _InitUnicodeString( pUnicode, pwch);             } \


DWORD
GetSecret(
    IN  LPWSTR       Server,
    IN  LPWSTR       SecretName,
    OUT LPWSTR *     ppSecret
    );

DWORD
SetSecret(
    IN  LPWSTR       Server,
    IN  LPWSTR       SecretName,
    IN  LPWSTR       pSecret,
    IN  DWORD        cbSecret
    );

//
//  This is a table of LSA secret names for the servers this API supports
//

struct SRV_SECRET_NAMES
{
    DWORD   dwID;
    LPWSTR  SecretName;
    LPWSTR  RootSecretName;
}
aSrvSecrets[] =
{
    INET_FTP,     FTPD_ANONYMOUS_SECRET_W,     FTPD_ROOT_SECRET_W,
    INET_HTTP,    W3_ANONYMOUS_SECRET_W,       W3_ROOT_SECRET_W,
    INET_GOPHER,  GOPHERD_ANONYMOUS_SECRET_W , GOPHERD_ROOT_SECRET_W,
    INET_CHAT,    CHAT_ANONYMOUS_SECRET_W,     CHAT_ROOT_SECRET_W,
    INET_NNTP,    NNTP_ANONYMOUS_SECRET_W,     NNTP_ROOT_SECRET_W,
    INET_SMTP,    SMTP_ANONYMOUS_SECRET_W,     SMTP_ROOT_SECRET_W,
    INET_POP3,    POP3_ANONYMOUS_SECRET_W,     POP3_ROOT_SECRET_W,
    INET_LDAP,    LDAP_ANONYMOUS_SECRET_W,     LDAP_ROOT_SECRET_W,
    INET_IMAP,    IMAP_ANONYMOUS_SECRET_W,     IMAP_ROOT_SECRET_W,
    0,            NULL,                        NULL
};

//
//  Returns a unicode empty string if the string is NULL
//

#define EMPTY_IF_NULL(str)      (str ? str : L"")




NET_API_STATUS
NET_API_FUNCTION
InetInfoGetVersion(
    IN  LPWSTR   Server OPTIONAL,
    IN  DWORD    dwReserved,
    OUT DWORD *  pdwVersion
    )
{
    NET_API_STATUS status;

    RpcTryExcept {

        //
        // Try RPC (local or remote) version of API.
        //
        status = R_InetInfoGetVersion(
                     Server,
                     dwReserved,
                     pdwVersion
                     );
    }
    RpcExcept (1) {
        status = RpcExceptionCode();
    }
    RpcEndExcept

    return (status);

} // InetInfoGetVersion()




NET_API_STATUS
NET_API_FUNCTION
InetInfoGetServerCapabilities(
    IN  LPWSTR   Server OPTIONAL,
    IN  DWORD    dwReserved,
    OUT LPINET_INFO_CAPABILITIES * ppCap
    )
{
    NET_API_STATUS status;

    RpcTryExcept {

        //
        // Try RPC (local or remote) version of API.
        //
        status = R_InetInfoGetServerCapabilities(
                     Server,
                     dwReserved,
                     (LPINET_INFO_CAPABILITIES_STRUCT *)ppCap
                     );
    }
    RpcExcept (1) {
        status = RpcExceptionCode();
    }
    RpcEndExcept

    return (status);

} // InetInfoServerCapabilities()




NET_API_STATUS
NET_API_FUNCTION
InetInfoQueryStatistics(
    IN  LPWSTR   pszServer OPTIONAL,
    IN  DWORD    Level,
    IN  DWORD    dwServerMask,
    OUT LPBYTE * Buffer
    )
{
    NET_API_STATUS status;

    RpcTryExcept {

        //
        // Try RPC (local or remote) version of API.
        //
        status = R_InetInfoQueryStatistics(
                     pszServer,
                     Level,
                     dwServerMask,
                     (LPINET_INFO_STATISTICS_INFO) Buffer
                     );
    }
    RpcExcept (1) {
        status = RpcExceptionCode();
    }
    RpcEndExcept

    return (status);
} // InetInfoQueryStatistics()




NET_API_STATUS
NET_API_FUNCTION
InetInfoClearStatistics(
    IN  LPWSTR pszServer OPTIONAL,
    IN  DWORD  dwServerMask
    )
{
    NET_API_STATUS status;

    RpcTryExcept {

        //
        // Try RPC (local or remote) version of API.
        //
        status = R_InetInfoClearStatistics(
                     pszServer,
                     dwServerMask
                     );
    }
    RpcExcept (1) {
        status = RpcExceptionCode();
    }
    RpcEndExcept

    return (status);
} // InetInfoClearStatistics()




NET_API_STATUS
NET_API_FUNCTION
InetInfoFlushMemoryCache(
    IN  LPWSTR pszServer OPTIONAL,
    IN  DWORD  dwServerMask
    )
{
    NET_API_STATUS status;

    RpcTryExcept {

        //
        // Try RPC (local or remote) version of API.
        //
        status = R_InetInfoFlushMemoryCache(
                     pszServer,
                     dwServerMask
                     );
    }
    RpcExcept (1) {
        status = RpcExceptionCode();
    }
    RpcEndExcept

    return (status);
} // InetInfoFlushMemoryCache()



NET_API_STATUS
NET_API_FUNCTION
InetInfoGetGlobalAdminInformation(
    IN  LPWSTR                       Server OPTIONAL,
    IN  DWORD                        dwReserved,
    OUT LPINET_INFO_GLOBAL_CONFIG_INFO * ppConfig
    )
{
    NET_API_STATUS status;

    RpcTryExcept {

        status = R_InetInfoGetGlobalAdminInformation(
                     Server,
                     dwReserved,
                     ppConfig
                     );
    }
    RpcExcept (1) {
        status = RpcExceptionCode();
    }
    RpcEndExcept

    return status;
} // InetInfoGetGlobalAdminInformation()



NET_API_STATUS
NET_API_FUNCTION
InetInfoSetGlobalAdminInformation(
    IN  LPWSTR                     Server OPTIONAL,
    IN  DWORD                      dwReserved,
    IN  INET_INFO_GLOBAL_CONFIG_INFO * pConfig
    )
{
    NET_API_STATUS status;

    RpcTryExcept {

        status = R_InetInfoSetGlobalAdminInformation(
                     Server,
                     dwReserved,
                     pConfig
                     );
    }
    RpcExcept (1) {
        status = RpcExceptionCode();
    }
    RpcEndExcept

    return status;
} // InetInfoSetGlobalAdminInformation()



NET_API_STATUS
NET_API_FUNCTION
InetInfoGetAdminInformation(
    IN  LPWSTR                Server OPTIONAL,
    IN  DWORD                 dwServerMask,
    OUT LPINET_INFO_CONFIG_INFO * ppConfig
    )
{
    NET_API_STATUS             status;
    BOOL                       fGetPassword = TRUE;
    LPWSTR                     pSecret;
    DWORD                      i = 0;
    DWORD                      j;
    LPWSTR                     pszCurrent;
    INET_INFO_VIRTUAL_ROOT_ENTRY * pVirtRoot;

    RpcTryExcept {

        status = R_InetInfoGetAdminInformation(
                     Server,
                     dwServerMask,
                     ppConfig
                     );
    }
    RpcExcept (1) {
        status = RpcExceptionCode();
    }
    RpcEndExcept

    if ( status )
        return status;

#ifndef CHICAGO

    //
    //  Get the anonymous account password
    //

    while ( aSrvSecrets[i].dwID &&
            !(aSrvSecrets[i].dwID & dwServerMask ) )
    {
        i++;
    }

    // Note: Only the service corresponding to first mask is chosen.

    if ( !aSrvSecrets[i].dwID )
        return ERROR_INVALID_PARAMETER;

    status = GetSecret( Server,
                        aSrvSecrets[i].SecretName,
                        &pSecret );

    if ( status )
        return status;

    memcpy( (*ppConfig)->szAnonPassword,
            pSecret,
            sizeof(WCHAR) * min( wcslen( pSecret ), PWLEN ));

    (*ppConfig)->szAnonPassword[PWLEN] = L'\0';

    LocalFree( pSecret );

    //
    //  Zero terminate all of the passwords in case there is no associated
    //  secret
    //

    for ( j = 0; j < (*ppConfig)->VirtualRoots->cEntries; j++ )
    {
        *(*ppConfig)->VirtualRoots->aVirtRootEntry[j].AccountPassword = L'\0';
    }

    status = GetSecret( Server,
                        aSrvSecrets[i].RootSecretName,
                        &pSecret );

    if ( status )
        return status;

    pszCurrent = pSecret;

    while ( *pszCurrent )
    {
        LPWSTR pszRoot;
        LPWSTR pszPassword;
        LPWSTR pszAddress;
        LPWSTR pszNextLine = pszCurrent + wcslen(pszCurrent) + 1;


        //
        //  The list is in the form:
        //
        //     <Root>,<Addresss>=<Password>\0
        //     <Root>,<Addresss>=<Password>\0
        //     \0
        //

        pszRoot = pszCurrent;

        pszPassword = wcschr( pszCurrent, L'=' );

        if ( !pszPassword )
        {
            //
            //  Bad list format, skip this one
            //

            goto NextLine;
        }

        *pszPassword = L'\0';
        pszPassword++;

        pszAddress = wcschr( pszRoot, L',');

        if ( !pszAddress )
        {
            goto NextLine;

        }

        *pszAddress = L'\0';
        pszAddress++;

        //
        //  Now look for this root and address in the virtual root list
        //  so we can set the password
        //

        for ( i = 0; i < (*ppConfig)->VirtualRoots->cEntries; i++ )
        {
            pVirtRoot = &(*ppConfig)->VirtualRoots->aVirtRootEntry[i];

            if ( !_wcsicmp( pszRoot, pVirtRoot->pszRoot ) &&
                 (!pszAddress || !_wcsicmp( pszAddress, pVirtRoot->pszAddress)))
            {
                //
                //  If the password length is invalid, we just ignore it.
                //  This shouldn't happen because we check before setting the
                //  password
                //

                if ( wcslen( pszPassword ) <= PWLEN )
                {
                    wcscpy( pVirtRoot->AccountPassword,
                            pszPassword );
                    break;
                }
            }
        }

NextLine:

        pszCurrent = pszNextLine;
    }

    LocalFree( pSecret );
#else // CHICAGO
    //
    //  Zero terminate all of the passwords in case there is no associated
    //  secret
    //

    for ( j = 0; j < (*ppConfig)->VirtualRoots->cEntries; j++ )
    {
        *(*ppConfig)->VirtualRoots->aVirtRootEntry[j].AccountPassword = L'\0';
    }
#endif // CHICAGO
    return status;
} // InetInfoGetAdminInformation()



NET_API_STATUS
NET_API_FUNCTION
InetInfoSetAdminInformation(
    IN  LPWSTR              Server OPTIONAL,
    IN  DWORD               dwServerMask,
    IN  INET_INFO_CONFIG_INFO * pConfig
    )
{
    NET_API_STATUS status;
    WCHAR          szAnonPassword[PWLEN+1];
    LPWSTR         pszRootPasswords = NULL;
    LPWSTR         pszPassword;
    DWORD          i, j;

#ifndef CHICAGO

    //
    //  Enumerate the LSA secret names for the specified servers.  We set the
    //  secrets first so the anonymous user name password can be refreshed
    //  in the server side InetInfoSetAdminInformation
    //

    i = 0;
    while ( aSrvSecrets[i].dwID )
    {
        if ( !(aSrvSecrets[i].dwID & dwServerMask ))
        {
            i++;
            continue;
        }

        if ( IsFieldSet( pConfig->FieldControl, FC_INET_INFO_ANON_PASSWORD ))
        {
            status = SetSecret( Server,
                                aSrvSecrets[i].SecretName,
                                pConfig->szAnonPassword,
                                (wcslen( pConfig->szAnonPassword ) + 1)
                                    * sizeof(WCHAR));

            if ( status )
                return status;
        }

        if ( IsFieldSet( pConfig->FieldControl, FC_INET_INFO_VIRTUAL_ROOTS ))
        {
            DWORD                      cbNeeded = sizeof(WCHAR);
            INET_INFO_VIRTUAL_ROOT_ENTRY * pVirtRoot;
            LPWSTR                     psz;
            LPWSTR                     pszSecret;

            //
            //  Build a string that looks like:
            //
            //     <Root>,<Addresss>=<Password>\0
            //     <Root>,<Addresss>=<Password>\0
            //     \0
            //

            //
            //  Do a first pass to figure the buffer size we need to build
            //

            for ( j = 0; j < pConfig->VirtualRoots->cEntries; j++ )
            {
                pVirtRoot = &pConfig->VirtualRoots->aVirtRootEntry[j];

                cbNeeded += (wcslen( pVirtRoot->pszRoot ) +
                             wcslen( EMPTY_IF_NULL(pVirtRoot->pszAddress)) +
                             wcslen( pVirtRoot->AccountPassword ) +
                             (PWLEN + 3)) * sizeof(WCHAR);
            }

            //
            //  We always allocate at least enough room for a '\0'
            //

            pszSecret = LocalAlloc( LPTR, cbNeeded + sizeof(WCHAR) );

            if ( !pszSecret )
                return ERROR_NOT_ENOUGH_MEMORY;

            psz = pszSecret;

            //
            //  Now build the string
            //

            for ( j = 0; j < pConfig->VirtualRoots->cEntries; j++ )
            {
                pVirtRoot = &pConfig->VirtualRoots->aVirtRootEntry[j];

                psz += wsprintfW( psz,
                                  L"%ls,%ls=%ls",
                                  pVirtRoot->pszRoot,
                                  EMPTY_IF_NULL(pVirtRoot->pszAddress),
                                  pVirtRoot->AccountPassword );
                psz++;
            }

            //
            //  Add the list terminating NULL
            //

            *psz = L'\0';

            status = SetSecret( Server,
                                aSrvSecrets[i].RootSecretName,
                                pszSecret,
                                cbNeeded );

            LocalFree( pszSecret );

            if ( status )
                return status;
        }

        i++;
    }
#endif // CHICAGO

    //
    //  Set the passwords to NULL so it doesn't go out on the
    //  wire.  We set them as a secrets above
    //

    if ( IsFieldSet( pConfig->FieldControl, FC_INET_INFO_VIRTUAL_ROOTS ))
    {
        pszRootPasswords = LocalAlloc( LPTR,
                                       pConfig->VirtualRoots->cEntries *
                                       (PWLEN + 1) * sizeof(WCHAR) );

        if ( !pszRootPasswords )
            return ERROR_NOT_ENOUGH_MEMORY;

        for ( i = 0; i < pConfig->VirtualRoots->cEntries; i++ )
        {
            pszPassword = pConfig->VirtualRoots->aVirtRootEntry[i].AccountPassword;

            if ( wcslen( pszPassword ) > PWLEN )
            {
                LocalFree( pszRootPasswords );
                return ERROR_INVALID_PARAMETER;
            }

            wcscpy( pszRootPasswords + i * (PWLEN + 1),
                    pszPassword );

            memset( pszPassword,
                    0,
                    sizeof( pConfig->VirtualRoots->aVirtRootEntry[i].AccountPassword ));
        }
    }

    if ( IsFieldSet( pConfig->FieldControl, FC_INET_INFO_ANON_PASSWORD ))
    {
        memcpy( szAnonPassword,
                pConfig->szAnonPassword,
                sizeof(pConfig->szAnonPassword) );

        memset( pConfig->szAnonPassword,
                0,
                sizeof(pConfig->szAnonPassword) );
    }

    RpcTryExcept {

        status = R_InetInfoSetAdminInformation(
                     Server,
                     dwServerMask,
                     pConfig
                     );
    }
    RpcExcept (1) {
        status = RpcExceptionCode();
    }
    RpcEndExcept

    //
    //  Restore the structure we just mucked with
    //

    if ( IsFieldSet( pConfig->FieldControl, FC_INET_INFO_ANON_PASSWORD ))
    {
        memcpy( pConfig->szAnonPassword,
                szAnonPassword,
                sizeof(pConfig->szAnonPassword) );

        memset( szAnonPassword, 0, sizeof( szAnonPassword ));
    }

    if ( IsFieldSet( pConfig->FieldControl, FC_INET_INFO_VIRTUAL_ROOTS ))
    {
        for ( i = 0; i < pConfig->VirtualRoots->cEntries; i++ )
        {
            pszPassword = pConfig->VirtualRoots->aVirtRootEntry[i].AccountPassword;

            wcscpy( pszPassword,
                    pszRootPasswords + i * (PWLEN + 1) );
        }

        memset( pszRootPasswords,
                0,
                pConfig->VirtualRoots->cEntries * (PWLEN + 1) * sizeof(WCHAR));

        LocalFree( pszRootPasswords );

        pszRootPasswords = NULL;
    }

    return status;
} // InetInfoSetAdminInformation()



#ifndef CHICAGO
DWORD
GetSecret(
    IN  LPWSTR       Server,
    IN  LPWSTR       SecretName,
    OUT LPWSTR *     ppSecret
    )
/*++

   Description

     Gets the specified LSA secret

   Arguments:

     Server - Server name (or NULL) secret lives on
     SecretName - Name of the LSA secret
     ppSecret - Receives an allocated block of memory containing the secret.
        Must be freed with LocalFree.

   Note:

--*/
{
    LSA_HANDLE        hPolicy;
    UNICODE_STRING *  punicodePassword;
    UNICODE_STRING    unicodeServer;
    NTSTATUS          ntStatus;
    OBJECT_ATTRIBUTES ObjectAttributes;
    UNICODE_STRING    unicodeSecret;


    InitUnicodeString( &unicodeServer,
                       Server );

    //
    //  Open a policy to the remote LSA
    //

    InitializeObjectAttributes( &ObjectAttributes,
                                NULL,
                                0L,
                                NULL,
                                NULL );

    ntStatus = LsaOpenPolicy( &unicodeServer,
                              &ObjectAttributes,
                              POLICY_ALL_ACCESS,
                              &hPolicy );

    if ( !NT_SUCCESS( ntStatus ) )
        return LsaNtStatusToWinError( ntStatus );

    InitUnicodeString( &unicodeSecret,
                       SecretName );


    //
    //  Query the secret value
    //

    ntStatus = LsaRetrievePrivateData( hPolicy,
                                       &unicodeSecret,
                                       &punicodePassword );

    LsaClose( hPolicy );

    if ( !NT_SUCCESS( ntStatus ))
        return LsaNtStatusToWinError( ntStatus );

    *ppSecret = LocalAlloc( LPTR, punicodePassword->Length + sizeof(WCHAR) );

    if ( !*ppSecret )
    {
        RtlZeroMemory( punicodePassword->Buffer,
                       punicodePassword->MaximumLength );

        LsaFreeMemory( (PVOID) punicodePassword );
        return ERROR_NOT_ENOUGH_MEMORY;
    }

    //
    //  Copy it into the buffer, Length is count of bytes
    //

    memcpy( *ppSecret,
            punicodePassword->Buffer,
            punicodePassword->Length );

    (*ppSecret)[punicodePassword->Length/sizeof(WCHAR)] = L'\0';

    RtlZeroMemory( punicodePassword->Buffer,
                   punicodePassword->MaximumLength );

    LsaFreeMemory( (PVOID) punicodePassword );

    return NO_ERROR;
} // GetSecret()




DWORD
SetSecret(
    IN  LPWSTR       Server,
    IN  LPWSTR       SecretName,
    IN  LPWSTR       pSecret,
    IN  DWORD        cbSecret
    )
/*++

   Description

     Sets the specified LSA secret

   Arguments:

     Server - Server name (or NULL) secret lives on
     SecretName - Name of the LSA secret
     pSecret - Pointer to secret memory
     cbSecret - Size of pSecret memory block

   Note:

--*/
{
    LSA_HANDLE        hPolicy;
    UNICODE_STRING    unicodePassword;
    UNICODE_STRING    unicodeServer;
    NTSTATUS          ntStatus;
    OBJECT_ATTRIBUTES ObjectAttributes;
    UNICODE_STRING    unicodeSecret;


    InitUnicodeString( &unicodeServer,
                       Server );

    //
    //  Initialize the unicode string by hand so we can handle '\0' in the
    //  string
    //

    unicodePassword.Buffer        = pSecret;
    unicodePassword.Length        = (USHORT) cbSecret;
    unicodePassword.MaximumLength = (USHORT) cbSecret;

    //
    //  Open a policy to the remote LSA
    //

    InitializeObjectAttributes( &ObjectAttributes,
                                NULL,
                                0L,
                                NULL,
                                NULL );

    ntStatus = LsaOpenPolicy( &unicodeServer,
                              &ObjectAttributes,
                              POLICY_ALL_ACCESS,
                              &hPolicy );

    if ( !NT_SUCCESS( ntStatus ) )
        return LsaNtStatusToWinError( ntStatus );

    //
    //  Create or open the LSA secret
    //

    InitUnicodeString( &unicodeSecret,
                       SecretName );

    ntStatus = LsaStorePrivateData( hPolicy,
                                    &unicodeSecret,
                                    &unicodePassword );

    LsaClose( hPolicy );

    if ( !NT_SUCCESS( ntStatus ))
    {
        return LsaNtStatusToWinError( ntStatus );
    }

    return NO_ERROR;
} // SetSecret()


#else // CHICAGO



DWORD
GetSecret(
    IN  LPWSTR       Server,
    IN  LPWSTR       SecretName,
    OUT LPWSTR *     ppSecret
    )
{
    return(NO_ERROR);
}

DWORD
SetSecret(
    IN  LPWSTR       Server,
    IN  LPWSTR       SecretName,
    IN  LPWSTR       pSecret,
    IN  DWORD        cbSecret
    )
{
    return(NO_ERROR);
}
#endif // CHICAGO

