#include "precomp.h"

BOOL
UserServerInfoIsInit(
    IN  RASMON_SERVERINFO * pServerInfo)
{
    return ((pServerInfo->hServer) || 
            (pServerInfo->dwBuild == RASMONTR_OS_BUILD_NT40));
}

DWORD
UserServerInfoInit(
    IN RASMON_SERVERINFO * pServerInfo)
{
    DWORD dwErr = NO_ERROR;
    BOOL bInit = UserServerInfoIsInit(pServerInfo);

    // If we're already initailized, return
    //
    if (bInit)
    {
        return NO_ERROR;
    }

    if ((pServerInfo->dwBuild != RASMONTR_OS_BUILD_NT40) &&
        (pServerInfo->hServer == NULL))
    {
        //
        // first time connecting to user server
        //
        MprAdminUserServerConnect (
            pServerInfo->pszServer, 
            TRUE, 
            &(pServerInfo->hServer));
    }

    return dwErr;
}

DWORD 
UserServerInfoUninit(
    IN RASMON_SERVERINFO * pServerInfo)
{
    // Release the reference to the user server
    if (g_pServerInfo->hServer)
    {
        MprAdminUserServerDisconnect(g_pServerInfo->hServer);
        g_pServerInfo->hServer = NULL;
    }

    return NO_ERROR;    
}

DWORD
UserGetRasProperties (
    IN  RASMON_SERVERINFO * pServerInfo,
    IN  LPCWSTR pwszUser,
    IN  RAS_USER_0* pUser0)
{
    HANDLE  hUser = NULL;
    DWORD   dwErr;
    BOOL    bInit = UserServerInfoIsInit(pServerInfo);

    do 
    {
        UserServerInfoInit(pServerInfo);
        
        // Get the information using nt40 apis
        //
        if (pServerInfo->dwBuild == RASMONTR_OS_BUILD_NT40)
        {
            dwErr = MprAdminUserGetInfo(
                        pServerInfo->pszServer,
                        pwszUser,
                        0,
                        (LPBYTE)pUser0);
            if (dwErr != NO_ERROR)
            {
                break;
            }
        }

        // Or get it using nt50 apis
        else
        {
            // Get a reference to the given user
            //
            dwErr = MprAdminUserOpen(
                        pServerInfo->hServer,
                        (LPWSTR)pwszUser,
                        &hUser);
            if (dwErr isnot NO_ERROR)
            {
                break;
            }

            // Set the information
            //
            dwErr = MprAdminUserRead(
                        hUser,
                        1,      // Gives us RASPRIV_DialinPolicy
                        (LPBYTE)pUser0);
            if (dwErr isnot NO_ERROR)
            {
                break;
            }
        }
        
    } while (FALSE);       
                    
    // Cleanup
    //
    {
        if(hUser)
        {
            MprAdminUserClose(hUser);
        }
        if (!bInit)
        {
            UserServerInfoUninit(pServerInfo);
        }            
    }

    return dwErr;
}

DWORD
UserSetRasProperties (
    IN  RASMON_SERVERINFO * pServerInfo,
    IN  LPCWSTR pwszUser,
    IN  RAS_USER_0* pUser0)
{
    HANDLE  hUser = NULL;
    DWORD   dwErr;
    BOOL    bInit = UserServerInfoIsInit(pServerInfo);

    do 
    {
        UserServerInfoInit(pServerInfo);
        
        // Set the information using nt40 apis
        //
        if (pServerInfo->dwBuild == RASMONTR_OS_BUILD_NT40)
        {
            dwErr = MprAdminUserSetInfo(
                        pServerInfo->pszServer,
                        pwszUser,
                        0,
                        (LPBYTE)pUser0);
            if (dwErr != NO_ERROR)
            {
                break;
            }
        }

        // Or get it using nt50 apis
        else
        {
            // Get a reference to the given user
            //
            dwErr = MprAdminUserOpen(
                        pServerInfo->hServer,
                        (LPWSTR)pwszUser,
                        &hUser);
            if (dwErr isnot NO_ERROR)
            {
                break;
            }

            // Set the information
            //
            dwErr = MprAdminUserWrite(
                        hUser,
                        1,      // Gives us RASPRIV_DialinPolicy
                        (LPBYTE)pUser0);
            if (dwErr isnot NO_ERROR)
            {
                break;
            }
        }
        
    } while (FALSE);       
                    
    // Cleanup
    //
    {
        if(hUser)
        {
            MprAdminUserClose(hUser);
        }
        if (!bInit)
        {
            UserServerInfoUninit(pServerInfo);
        }            
    }

    return dwErr;
}

DWORD
UserAdd(
    IN LPCWSTR           pwszServer,
    IN PRASUSER_DATA     pUser)
    
/*++

Routine Description:

    Adds the given user to the system

--*/

{
    NET_API_STATUS nStatus;
    USER_INFO_2 *  pUser2;
    LPCWSTR        pwszFmtServer = pwszServer;

    // Initialize the base user information
    USER_INFO_1 UserInfo1 = 
    {
        pUser->pszUsername,
        pUser->pszPassword,
        0,
        USER_PRIV_USER,
        L"",
        L"",
        UF_SCRIPT | UF_DONT_EXPIRE_PASSWD | UF_NORMAL_ACCOUNT,
        L""
    };

    // Add the user
    nStatus = NetUserAdd(
                pwszFmtServer,
                1,
                (LPBYTE)&UserInfo1,
                NULL);

    // If the user wasn't added, find out why
    if (nStatus != NERR_Success) 
    {
        switch (nStatus) 
        {
            case ERROR_ACCESS_DENIED:
                return ERROR_ACCESS_DENIED;
                
            case NERR_UserExists:
                return ERROR_USER_EXISTS;
                
            case NERR_PasswordTooShort:
                return ERROR_INVALID_PASSWORDNAME;
        }
        
        return ERROR_CAN_NOT_COMPLETE;
    }

    // Add the user's full name if provided
    if (pUser->pszFullname)
    {
        // add the user's full name
        nStatus = NetUserGetInfo(
                        pwszFmtServer, 
                        pUser->pszUsername, 
                        2, 
                        (LPBYTE*)&pUser2);
                        
        if (nStatus is NERR_Success) 
        {
            // Modify the full name in the structure
            pUser2->usri2_full_name = pUser->pszFullname;
            NetUserSetInfo(
                pwszFmtServer, 
                pUser->pszUsername, 
                2, 
                (LPBYTE)pUser2, 
                NULL);
                
            NetApiBufferFree((LPBYTE)pUser2);

            return NO_ERROR;
        }
        
        return ERROR_CAN_NOT_COMPLETE;
    }                

    return NO_ERROR;
}

DWORD
UserDelete(
    IN LPCWSTR           pwszServer,
    IN PRASUSER_DATA     pUser)

/*++

Routine Description:

    Deletes the given user from the system

--*/
{
    NET_API_STATUS nStatus;
    
    // Delete the user and return the status code.  If the
    // specified user is not in the user database, consider
    // it a success
    nStatus = NetUserDel(
                pwszServer,
                pUser->pszUsername);
    if (nStatus is NERR_UserNotFound)
    {
        return NO_ERROR;
    }

    return (nStatus is NERR_Success) ? NO_ERROR : ERROR_CAN_NOT_COMPLETE;
}
    
DWORD
UserDumpConfig(
    IN  HANDLE hFile
    )

/*++

Routine Description:

    Dumps a script to set the ras USER information to 
    the given text file.

Arguments:


Return Value:

    NO_ERROR

--*/

{
    DWORD dwErr;

    // Enumerate the users dumping them as we go
    dwErr = UserEnumUsers(
                g_pServerInfo,
                UserShowSet,
                (HANDLE)hFile);
    if (dwErr isnot NO_ERROR)
    {
        DisplayMessage(
            g_hModule,
            EMSG_UNABLE_TO_ENUM_USERS);
    }

    return dwErr;
}
    
BOOL 
UserShowReport(
    IN  PRASUSER_DATA          pUser,
    IN  HANDLE              hFile
    )

/*++

Routine Description:

    Prints ras user information to the display or a file if specified.
    This function can be used as a callback function (see UserEnumUsers).

Arguments:

    pUser       - The user
    hFile       - The file

Return Value:

    TRUE - continue enumeration
    FALSE - stop enumeration

--*/

{
    DWORD   dwErr, dwSize;
    WCHAR   rgwcIfDesc[MAX_INTERFACE_NAME_LEN + 1];
    PWCHAR  pwszDialin   = NULL, 
            pwszCbPolicy = NULL, 
            pwszCbNumber = NULL,
            pwszSetCmd   = NULL;

    // Initialize the set command
    //
    pwszSetCmd = DMP_RASUSER_SET;

    // Initialize the dialin string
    //
    if (pUser->User0.bfPrivilege & RASPRIV_DialinPolicy)
    {
        pwszDialin = TOKEN_POLICY;
    }
    else if (pUser->User0.bfPrivilege & RASPRIV_DialinPrivilege)
    {
        pwszDialin = TOKEN_PERMIT;
    }
    else 
    {
        pwszDialin = TOKEN_DENY;
    }

    // Initialize the callback policy string
    //
    if (pUser->User0.bfPrivilege & RASPRIV_NoCallback)
    {
        pwszCbPolicy = TOKEN_NONE;
    }
    else if (pUser->User0.bfPrivilege & RASPRIV_CallerSetCallback)
    {
        pwszCbPolicy = TOKEN_CALLER;
    }
    else
    {
        pwszCbPolicy = TOKEN_ADMIN;
    }

    // Initialize the callback number string
    //
    pwszCbNumber   = pUser->User0.wszPhoneNumber;

    do
    {
        if(!pwszSetCmd              or
           !pUser->pszUsername      or
           !pwszDialin              or
           !pwszCbNumber 
          )
        {

            DisplayError(NULL,
                         ERROR_NOT_ENOUGH_MEMORY);

            break;
        }

        DisplayMessage(g_hModule,
                       MSG_RASUSER_RASINFO,
                       pUser->pszUsername,
                       pwszDialin,
                       pwszCbPolicy,
                       pwszCbNumber);
    
    } while(FALSE);

    return TRUE;
}    

BOOL 
UserShowSet(
    IN  PRASUSER_DATA          pUser,
    IN  HANDLE              hFile
    )

/*++

Routine Description:

    Prints ras user information to the display or a file if specified.
    This function can be used as a callback function (see UserEnumUsers).

Arguments:

    pUser       - The user
    hFile       - The file

Return Value:

    TRUE - continue enumeration
    FALSE - stop enumeration

--*/

{
    DWORD   dwErr, dwSize;
    WCHAR   rgwcIfDesc[MAX_INTERFACE_NAME_LEN + 1];
    PWCHAR  pwszName     = NULL, 
            pwszDialin   = NULL, 
            pwszCbPolicy = NULL, 
            pwszCbNumber = NULL,
            pwszSetCmd   = NULL;

    // Initialize the set command
    //
    pwszSetCmd = DMP_RASUSER_SET;

    // Initialize the dialin string
    //
    if (pUser->User0.bfPrivilege & RASPRIV_DialinPolicy)
    {
        pwszDialin = RutlAssignmentFromTokens(
                        g_hModule, 
                        TOKEN_DIALIN, 
                        TOKEN_POLICY);
    }
    else if (pUser->User0.bfPrivilege & RASPRIV_DialinPrivilege)
    {
        pwszDialin = RutlAssignmentFromTokens(
                        g_hModule, 
                        TOKEN_DIALIN, 
                        TOKEN_PERMIT);
    }
    else 
    {
        pwszDialin = RutlAssignmentFromTokens(
                        g_hModule, 
                        TOKEN_DIALIN, 
                        TOKEN_DENY);
    }

    // Initialize the callback policy string
    //
    if (pUser->User0.bfPrivilege & RASPRIV_NoCallback)
    {
        pwszCbPolicy = RutlAssignmentFromTokens(
                            g_hModule, 
                            TOKEN_CBPOLICY, 
                            TOKEN_NONE);
    }
    else if (pUser->User0.bfPrivilege & RASPRIV_CallerSetCallback)
    {
        pwszCbPolicy = RutlAssignmentFromTokens(
                            g_hModule, 
                            TOKEN_CBPOLICY, 
                            TOKEN_CALLER);
    }
    else
    {
        pwszCbPolicy = RutlAssignmentFromTokens(
                            g_hModule, 
                            TOKEN_CBPOLICY, 
                            TOKEN_CALLER);
    }

    // Initialize the callback number string
    //
    if (*(pUser->User0.wszPhoneNumber))
    {
        pwszCbNumber = RutlAssignmentFromTokens(
                            g_hModule,
                            TOKEN_CBNUMBER,
                            pUser->User0.wszPhoneNumber);
    }
    else
    {
        pwszCbNumber = NULL;
    }
                        
    pwszName = RutlAssignmentFromTokens(
                        g_hModule,
                        TOKEN_NAME,
                        pUser->pszUsername);
    
    do
    {
        if(!pwszSetCmd              or
           !pwszName                or
           !pwszDialin              or
           !pwszCbPolicy            
          )
        {

            DisplayError(NULL,
                         ERROR_NOT_ENOUGH_MEMORY);

            break;
        }

        DisplayMessage(g_hModule,
                       MSG_RASUSER_SET_CMD,
                       pwszSetCmd,
                       pwszName,
                       pwszDialin,
                       pwszCbPolicy,
                       (pwszCbNumber) ? pwszCbNumber : L"");

    } while(FALSE);

    // Callback
    {
        if (pwszDialin)
        {
            RutlFree(pwszDialin);
        }
        if (pwszCbPolicy)
        {
            RutlFree(pwszCbPolicy);
        }
        if (pwszCbNumber)
        {
            RutlFree(pwszCbNumber);
        }
        if (pwszName)
        {
            RutlFree(pwszName);
        }
    }

    return TRUE;
}    

BOOL 
UserShowPermit(
    IN  PRASUSER_DATA          pUser,
    IN  HANDLE              hFile
    )
{
    if (pUser->User0.bfPrivilege & RASPRIV_DialinPrivilege)
    {
        return UserShowReport(pUser, hFile);
    }

    return TRUE;
}

DWORD 
UserEnumUsers(
    IN RASMON_SERVERINFO* pServerInfo,
    IN PFN_RASUSER_ENUM_CB pEnumFn,
    IN HANDLE hData
    )
    
/*++

Routine Description:

    Enumerates all users by calling the given callback function and 
    passing it the user information and some user defined data.

    Enumeration stops when all the users have been enumerated or when
    the enumeration function returns FALSE.

Arguments:

    pwszServer  - The server on which the users should be enumerated
    pEnumFn     - Enumeration function
    hData       - Caller defined opaque data blob

Return Value:

    NO_ERROR

--*/

{
    DWORD dwErr, dwIndex = 0, dwCount = 100, dwEntriesRead, i;
    NET_DISPLAY_USER  * pUsers;
    NET_API_STATUS nStatus;
    RAS_USER_0 RasUser0;
    HANDLE hUser = NULL;
    RASUSER_DATA UserData, *pUserData = &UserData;
    BOOL bInit = UserServerInfoIsInit(pServerInfo);

    UserServerInfoInit(pServerInfo);
    
    // Enumerate the users
    //
    while (TRUE) 
    {
        // Read in the next block of user names
        nStatus = NetQueryDisplayInformation(
                    pServerInfo->pszServer,
                    1,
                    dwIndex,
                    dwCount,
                    dwCount * sizeof(NET_DISPLAY_USER),    
                    &dwEntriesRead,
                    &pUsers);
                    
        // Get out if there's an error getting user names
        if ((nStatus isnot NERR_Success) and 
            (nStatus isnot ERROR_MORE_DATA))
        {
            break;
        }

        for (i = 0; i < dwEntriesRead; i++) 
        {
            // Initialize the user data
            ZeroMemory(pUserData, sizeof(RASUSER_DATA));
        
            // Read in the old information
            dwErr = UserGetRasProperties (
                        pServerInfo, 
                        pUsers[i].usri1_name,
                        &(pUserData->User0));
            if (dwErr isnot NO_ERROR)
            {
                continue;
            }

            // Initialize the rest of the data structure
            pUserData->pszUsername = pUsers[i].usri1_name;
            pUserData->pszFullname = pUsers[i].usri1_full_name;

            // Call the enumeration callback
            if (! ((*(pEnumFn))(pUserData, hData)))
            {
                nStatus = NO_ERROR;
                break;
            }
        }

        // Set the index to read in the next set of users
        dwIndex = pUsers[dwEntriesRead - 1].usri1_next_index;  
        
        // Free the users buffer
        NetApiBufferFree (pUsers);

        // If we've read in everybody, go ahead and break
        if (nStatus isnot ERROR_MORE_DATA)
        {
            break;
        }
    }

    if (!bInit)
    {
        UserServerInfoUninit(pServerInfo);
    }        

    return NO_ERROR;
}