/* Editor Settings: expandtabs and use 4 spaces for indentation
 * ex: set softtabstop=4 tabstop=8 expandtab shiftwidth=4: *
 */

/*
 * Copyright Likewise Software    2004-2009
 * All rights reserved.
 *
 * This library is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the license, or (at
 * your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
 * General Public License for more details.  You should have received a copy
 * of the GNU Lesser General Public License along with this program.  If
 * not, see <http://www.gnu.org/licenses/>.
 *
 * LIKEWISE SOFTWARE MAKES THIS SOFTWARE AVAILABLE UNDER OTHER LICENSING
 * TERMS AS WELL.  IF YOU HAVE ENTERED INTO A SEPARATE LICENSE AGREEMENT
 * WITH LIKEWISE SOFTWARE, THEN YOU MAY ELECT TO USE THE SOFTWARE UNDER THE
 * TERMS OF THAT SOFTWARE LICENSE AGREEMENT INSTEAD OF THE TERMS OF THE GNU
 * LESSER GENERAL PUBLIC LICENSE, NOTWITHSTANDING THE ABOVE NOTICE.  IF YOU
 * HAVE QUESTIONS, OR WISH TO REQUEST A COPY OF THE ALTERNATE LICENSING
 * TERMS OFFERED BY LIKEWISE SOFTWARE, PLEASE CONTACT LIKEWISE SOFTWARE AT
 * license@likewisesoftware.com
 */

/*
 * Copyright (C) Likewise Software. All rights reserved.
 *
 * Module Name:
 *
 *        samr_queryuserinfo.c
 *
 * Abstract:
 *
 *        Remote Procedure Call (RPC) Server Interface
 *
 *        SamrQueryUserInfo function
 *
 * Authors: Rafal Szczesniak (rafal@likewise.com)
 */

#include "includes.h"

#define CONVERT_LOGON_HOURS(inSamDbLogonHours, outSamrLogonHours)       \
    {                                                                   \
        DWORD dwLogonHoursSize = 1260;                                  \
        DWORD dwLogonHoursLen = 0;                                      \
        DWORD dwUnitsPerWeek = 0;                                       \
        DWORD iUnit = 0;                                                \
        DWORD iByte = 0;                                                \
                                                                        \
        ntStatus = SamrSrvAllocateMemory(                               \
                        OUT_PPVOID(&(outSamrLogonHours)->units),        \
                        dwLogonHoursSize);                              \
        BAIL_ON_NT_STATUS(ntStatus);                               \
                                                                        \
        memset((outSamrLogonHours)->units, 0, dwLogonHoursSize);        \
                                                                        \
        /*                                                              \
         * Each byte is a unit in inSamDbLogonHours while               \
         * in outSamrLogonHours it is one bit                           \
         */                                                             \
        dwUnitsPerWeek  = (inSamDbLogonHours)->UnitsPerWeek;            \
        /* NOTE: We should round up if number of units per week         \
           becomes arbitrary value */                                   \
        dwLogonHoursLen = dwUnitsPerWeek / 8;                           \
                                                                        \
        for (iUnit = 0, iByte = 0;                                      \
             iUnit < dwLogonHoursLen && iByte < dwUnitsPerWeek;         \
             iUnit++, iByte += 8)                                       \
        {                                                               \
            BYTE Unit = 0;                                              \
                                                                        \
            Unit |= (inSamDbLogonHours)->LogonHours[iByte];             \
            Unit |= (inSamDbLogonHours)->LogonHours[iByte+1] << 1;      \
            Unit |= (inSamDbLogonHours)->LogonHours[iByte+2] << 2;      \
            Unit |= (inSamDbLogonHours)->LogonHours[iByte+3] << 3;      \
            Unit |= (inSamDbLogonHours)->LogonHours[iByte+4] << 4;      \
            Unit |= (inSamDbLogonHours)->LogonHours[iByte+5] << 5;      \
            Unit |= (inSamDbLogonHours)->LogonHours[iByte+6] << 6;      \
            Unit |= (inSamDbLogonHours)->LogonHours[iByte+7] << 7;      \
                                                                        \
            (outSamrLogonHours)->units[iUnit] = Unit;                   \
        }                                                               \
                                                                        \
        (outSamrLogonHours)->units_per_week = dwUnitsPerWeek;           \
    }


static
NTSTATUS
SamrFillUserInfo1(
        PSAMDB_OBJECT_INFO pObjectInfo,
        UserInfo *pInfo
        );


static
NTSTATUS
SamrFillUserInfo2(
    PSAMDB_OBJECT_INFO pObjectInfo,
    UserInfo *pInfo
    );


static
NTSTATUS
SamrFillUserInfo3(
    PACCOUNT_CONTEXT pAcctCtx,
    PSAMDB_OBJECT_INFO pObjectInfo,
    UserInfo *pInfo
    );


static
NTSTATUS
SamrFillUserInfo4(
    PSAMDB_OBJECT_INFO pObjectInfo,
    UserInfo *pInfo
    );


static
NTSTATUS
SamrFillUserInfo5(
    PSAMDB_OBJECT_INFO pObjectInfo,
    UserInfo *pInfo
    );


static
NTSTATUS
SamrFillUserInfo6(
    PSAMDB_OBJECT_INFO pObjectInfo,
    UserInfo *pInfo
    );


static
NTSTATUS
SamrFillUserInfo7(
    PSAMDB_OBJECT_INFO pObjectInfo,
    UserInfo *pInfo
    );


static
NTSTATUS
SamrFillUserInfo8(
    PSAMDB_OBJECT_INFO pObjectInfo,
    UserInfo *pInfo
    );


static
NTSTATUS
SamrFillUserInfo9(
    PSAMDB_OBJECT_INFO pObjectInfo,
    UserInfo *pInfo
    );


static
NTSTATUS
SamrFillUserInfo10(
    PSAMDB_OBJECT_INFO pObjectInfo,
    UserInfo *pInfo
    );


static
NTSTATUS
SamrFillUserInfo11(
    PSAMDB_OBJECT_INFO pObjectInfo,
    UserInfo *pInfo
    );


static
NTSTATUS
SamrFillUserInfo12(
    PSAMDB_OBJECT_INFO pObjectInfo,
    UserInfo *pInfo
    );


static
NTSTATUS
SamrFillUserInfo13(
    PSAMDB_OBJECT_INFO pObjectInfo,
    UserInfo *pInfo
    );


static
NTSTATUS
SamrFillUserInfo14(
    PSAMDB_OBJECT_INFO pObjectInfo,
    UserInfo *pInfo
    );


static
NTSTATUS
SamrFillUserInfo16(
    PSAMDB_OBJECT_INFO pObjectInfo,
    UserInfo *pInfo
    );


static
NTSTATUS
SamrFillUserInfo17(
    PSAMDB_OBJECT_INFO pObjectInfo,
    UserInfo *pInfo
    );


static
NTSTATUS
SamrFillUserInfo20(
    PSAMDB_OBJECT_INFO pObjectInfo,
    UserInfo *pInfo
    );


static
NTSTATUS
SamrFillUserInfo21(
    PACCOUNT_CONTEXT pAcctCtx,
    PSAMDB_OBJECT_INFO pObjectInfo,
    UserInfo *pInfo
    );


NTSTATUS
SamrSrvQueryUserInfo(
    IN handle_t hBinding,
    IN ACCOUNT_HANDLE hUser,
    IN UINT16 level,
    OUT UserInfo **info
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    DWORD dwError = ERROR_SUCCESS;
    PACCOUNT_CONTEXT pAcctCtx = NULL;
    PDOMAIN_CONTEXT pDomCtx = NULL;
    UserInfo *pUserInfo = NULL;
    SAMDB_QUERY SamDbQuery = {0};
    PSAMDB_OBJECT_INFO *ppObjects = NULL;
    PSAMDB_OBJECT_INFO userObject = NULL;

    pAcctCtx = (PACCOUNT_CONTEXT)hUser;

    if (pAcctCtx == NULL || pAcctCtx->Type != SamrContextAccount)
    {
        ntStatus = STATUS_INVALID_HANDLE;
        BAIL_ON_NT_STATUS(ntStatus);
    }

    switch (level)
    {
    case 15:
    case 18:
    case 19:
        ntStatus = STATUS_INVALID_INFO_CLASS;
        break;

    default:
        ntStatus = STATUS_SUCCESS;
    }
    BAIL_ON_NT_STATUS(ntStatus);

    pDomCtx  = pAcctCtx->pDomCtx;

    if (pAcctCtx->pSid == NULL)
    {
        ntStatus = STATUS_INVALID_HANDLE;
        BAIL_ON_NT_STATUS(ntStatus);
    }

    SamDbQuery.Sid = pAcctCtx->pSid;
    dwError = SamDbFindObjects(SAMDB_OBJECT_TYPE_USER,
                               SAMDB_QUERY_TYPE_SID,
                               1,
                               &SamDbQuery,
                               &ppObjects);
    BAIL_ON_LSA_ERROR(dwError);

    userObject = ppObjects[0];
    if (userObject == NULL)
    {
        ntStatus = STATUS_INVALID_HANDLE;
        BAIL_ON_NT_STATUS(ntStatus);
    }

    ntStatus = SamrSrvAllocateMemory((void**)&pUserInfo,
                                     sizeof(*pUserInfo));
    BAIL_ON_NT_STATUS(ntStatus);

    switch (level)
    {
    case 1:
    case 6:
    case 7:
    case 8:
    case 13:
        if (!(pAcctCtx->dwAccessGranted & USER_ACCESS_GET_NAME_ETC) ||
            !(pAcctCtx->dwAccessGranted & USER_ACCESS_GET_ATTRIBUTES))
        {
            ntStatus = STATUS_ACCESS_DENIED;
            BAIL_ON_NT_STATUS(ntStatus);
        }
        break;

    case 2:
        if (!(pAcctCtx->dwAccessGranted & USER_ACCESS_GET_LOCALE) ||
            !(pAcctCtx->dwAccessGranted & USER_ACCESS_GET_ATTRIBUTES))
        {
            ntStatus = STATUS_ACCESS_DENIED;
            BAIL_ON_NT_STATUS(ntStatus);
        }
        break;

    case 4:
    case 5:
    case 10:
    case 11:
    case 12:
    case 14:
    case 17:
        if (!(pAcctCtx->dwAccessGranted & USER_ACCESS_GET_LOGONINFO) ||
            !(pAcctCtx->dwAccessGranted & USER_ACCESS_GET_ATTRIBUTES))
        {
            ntStatus = STATUS_ACCESS_DENIED;
            BAIL_ON_NT_STATUS(ntStatus);
        }
        break;

    case 3:
    case 9:
    case 16:
    case 20:
    case 21:
        if (!(pAcctCtx->dwAccessGranted & USER_ACCESS_GET_ATTRIBUTES))
        {
            ntStatus = STATUS_ACCESS_DENIED;
            BAIL_ON_NT_STATUS(ntStatus);
        }
        break;
    }

    switch (level) {
    case 1:
        ntStatus = SamrFillUserInfo1(userObject, pUserInfo);
        break;

    case 2:
        ntStatus = SamrFillUserInfo2(userObject, pUserInfo);
        break;

    case 3:
        ntStatus = SamrFillUserInfo3(pAcctCtx, userObject, pUserInfo);
        break;

    case 4:
        ntStatus = SamrFillUserInfo4(userObject, pUserInfo);
        break;

    case 5:
        ntStatus = SamrFillUserInfo5(userObject, pUserInfo);
        break;

    case 6:
        ntStatus = SamrFillUserInfo6(userObject, pUserInfo);
        break;

    case 7:
        ntStatus = SamrFillUserInfo7(userObject, pUserInfo);
        break;

    case 8:
        ntStatus = SamrFillUserInfo8(userObject, pUserInfo);
        break;

    case 9:
        ntStatus = SamrFillUserInfo9(userObject, pUserInfo);
        break;

    case 10:
        ntStatus = SamrFillUserInfo10(userObject, pUserInfo);
        break;

    case 11:
        ntStatus = SamrFillUserInfo11(userObject, pUserInfo);
        break;

    case 12:
        ntStatus = SamrFillUserInfo12(userObject, pUserInfo);
        break;

    case 13:
        ntStatus = SamrFillUserInfo13(userObject, pUserInfo);
        break;

    case 14:
        ntStatus = SamrFillUserInfo14(userObject, pUserInfo);
        break;

    case 16:
        ntStatus = SamrFillUserInfo16(userObject, pUserInfo);
        break;

    case 17:
        ntStatus = SamrFillUserInfo17(userObject, pUserInfo);
        break;

    case 20:
        ntStatus = SamrFillUserInfo20(userObject, pUserInfo);
        break;

    case 21:
        ntStatus = SamrFillUserInfo21(pAcctCtx, userObject, pUserInfo);
        break;

    default:
        ntStatus = STATUS_INVALID_INFO_CLASS;
    }

    BAIL_ON_NT_STATUS(ntStatus);

    *info = pUserInfo;

error:
    if (ntStatus)
    {
        // NOTE: We are freeing allocated strings (name, domain) on error
        //       within each SamrFillUserInfoX().  If we add other error paths,
        //       we may need to free these here too.
        if (pUserInfo) {
            SamrSrvFreeMemory(pUserInfo);
        }
        *info = NULL;
    }

    if (ppObjects)
    {
        SamDbFreeObjects(1, ppObjects);
    }

    if (ntStatus == STATUS_SUCCESS &&
        dwError != ERROR_SUCCESS)
    {
        ntStatus = LwWin32ErrorToNtStatus(dwError);
    }

    return ntStatus;
}


static
NTSTATUS
SamrFillUserInfo1(
    PSAMDB_OBJECT_INFO pObjectInfo,
    UserInfo *pInfo
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    UserInfo1 *pInfo1 = NULL;
    PSAMDB_USER_INFO pUserInfo = NULL;

    pUserInfo = &pObjectInfo->Info.User;
    pInfo1 = &(pInfo->info1);

    ntStatus = SamrSrvInitUnicodeString(&pInfo1->account_name,
                                        pObjectInfo->Name);
    BAIL_ON_NT_STATUS(ntStatus);

    ntStatus = SamrSrvInitUnicodeString(&pInfo1->full_name,
                                        pUserInfo->FullName);
    BAIL_ON_NT_STATUS(ntStatus);

    pInfo1->primary_gid = pUserInfo->PrimaryGroupRid;

    ntStatus = SamrSrvInitUnicodeString(&pInfo1->description,
                                        pUserInfo->Description);
    BAIL_ON_NT_STATUS(ntStatus);

    ntStatus = SamrSrvInitUnicodeString(&pInfo1->comment,
                                        pUserInfo->UserComment);
    BAIL_ON_NT_STATUS(ntStatus);

error:
    if (ntStatus)
    {
        SamrSrvFreeUnicodeString(&pInfo1->account_name);
        SamrSrvFreeUnicodeString(&pInfo1->full_name);
        SamrSrvFreeUnicodeString(&pInfo1->description);
        SamrSrvFreeUnicodeString(&pInfo1->comment);

        memset(pInfo1, 0, sizeof(*pInfo1));
    }

    return ntStatus;
}


static
NTSTATUS
SamrFillUserInfo2(
    PSAMDB_OBJECT_INFO pObjectInfo,
    UserInfo *pInfo
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    UserInfo2 *pInfo2 = NULL;
    PSAMDB_USER_INFO pUserInfo = NULL;

    pUserInfo = &pObjectInfo->Info.User;
    pInfo2 = &(pInfo->info2);

    ntStatus = SamrSrvInitUnicodeString(&pInfo2->comment,
                                        pUserInfo->UserComment);
    BAIL_ON_NT_STATUS(ntStatus);

    /* unknown1 */

    pInfo2->country_code = pUserInfo->CountryCode;
    pInfo2->code_page = pUserInfo->CodePage;

error:

    if (ntStatus)
    {
        SamrSrvFreeUnicodeString(&pInfo2->comment);

        memset(pInfo2, 0, sizeof(*pInfo2));
    }

    return ntStatus;
}


static
NTSTATUS
SamrFillUserInfo3(
    PACCOUNT_CONTEXT pAcctCtx,
    PSAMDB_OBJECT_INFO pObjectInfo,
    UserInfo *pInfo
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    UserInfo3 *pInfo3 = NULL;
    PSAMDB_USER_INFO pUserInfo = NULL;
    LONG64 ntMinPasswordAge = pAcctCtx->pDomCtx->ntMinPasswordAge;
    LONG64 ntMaxPasswordAge = pAcctCtx->pDomCtx->ntMinPasswordAge;

    pUserInfo = &pObjectInfo->Info.User;
    pInfo3 = &(pInfo->info3);

    ntStatus = SamrSrvInitUnicodeString(&pInfo3->account_name,
                                        pObjectInfo->Name);
    BAIL_ON_NT_STATUS(ntStatus);

    ntStatus = SamrSrvInitUnicodeString(&pInfo3->full_name,
                                        pUserInfo->FullName);
    BAIL_ON_NT_STATUS(ntStatus);

    ntStatus = RtlGetRidSid(&pInfo3->rid, pObjectInfo->Sid);
    BAIL_ON_NT_STATUS(ntStatus);

    pInfo3->primary_gid = pUserInfo->PrimaryGroupRid;

    ntStatus = SamrSrvInitUnicodeString(&pInfo3->home_directory,
                                        pUserInfo->HomeDirectory);
    BAIL_ON_NT_STATUS(ntStatus);

    ntStatus = SamrSrvInitUnicodeString(&pInfo3->home_drive,
                                        pUserInfo->HomeDirectoryDrive);
    BAIL_ON_NT_STATUS(ntStatus);

    ntStatus = SamrSrvInitUnicodeString(&pInfo3->logon_script,
                                        pUserInfo->ScriptPath);
    BAIL_ON_NT_STATUS(ntStatus);

    ntStatus = SamrSrvInitUnicodeString(&pInfo3->profile_path,
                                        pUserInfo->ProfilePath);
    BAIL_ON_NT_STATUS(ntStatus);

    // Worstations is currently not supported
    ntStatus = SamrSrvInitUnicodeString(&pInfo3->workstations,
                                        NULL);
    BAIL_ON_NT_STATUS(ntStatus);

    pInfo3->last_logon = pUserInfo->LastLogon;
    pInfo3->last_logoff = pUserInfo->LastLogoff;
    pInfo3->last_password_change = pUserInfo->PasswordLastSet;

    pInfo3->allow_password_change = pInfo3->last_password_change +
                                    ntMinPasswordAge;
    pInfo3->force_password_change = pInfo3->last_password_change +
                                    ntMaxPasswordAge;

    CONVERT_LOGON_HOURS(&pUserInfo->LogonHours, &pInfo3->logon_hours);

    pInfo3->bad_password_count = pUserInfo->BadPasswordCount;
    pInfo3->logon_count = pUserInfo->LogonCount;

    pInfo3->account_flags = SamDbMapDatabaseToSamrFlags(
                                pUserInfo->UserAccountControl,
                                NULL);

error:
    if (ntStatus)
    {
        SamrSrvFreeUnicodeString(&pInfo3->account_name);
        SamrSrvFreeUnicodeString(&pInfo3->full_name);
        SamrSrvFreeUnicodeString(&pInfo3->home_directory);
        SamrSrvFreeUnicodeString(&pInfo3->home_drive);
        SamrSrvFreeUnicodeString(&pInfo3->logon_script);
        SamrSrvFreeUnicodeString(&pInfo3->profile_path);
        SamrSrvFreeUnicodeString(&pInfo3->workstations);

        memset(pInfo3, 0, sizeof(*pInfo3));
    }

    return ntStatus;
}


static
NTSTATUS
SamrFillUserInfo4(
    PSAMDB_OBJECT_INFO pObjectInfo,
    UserInfo *pInfo
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    UserInfo4 *pInfo4 = NULL;
    PSAMDB_USER_INFO pUserInfo = NULL;

    pUserInfo = &pObjectInfo->Info.User;
    pInfo4 = &(pInfo->info4);

    CONVERT_LOGON_HOURS(&pUserInfo->LogonHours, &pInfo4->logon_hours);

error:
    if (ntStatus)
    {
        memset(pInfo4, 0, sizeof(*pInfo4));
    }

    return ntStatus;
}


static
NTSTATUS
SamrFillUserInfo5(
    PSAMDB_OBJECT_INFO pObjectInfo,
    UserInfo *pInfo
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    UserInfo5 *pInfo5 = NULL;
    PSAMDB_USER_INFO pUserInfo = NULL;

    pUserInfo = &pObjectInfo->Info.User;
    pInfo5 = &(pInfo->info5);


    ntStatus = SamrSrvInitUnicodeString(&pInfo5->account_name,
                                        pObjectInfo->Name);
    BAIL_ON_NT_STATUS(ntStatus);

    ntStatus = SamrSrvInitUnicodeString(&pInfo5->full_name,
                                        pUserInfo->FullName);
    BAIL_ON_NT_STATUS(ntStatus);

    ntStatus = RtlGetRidSid(&pInfo5->rid, pObjectInfo->Sid);
    BAIL_ON_NT_STATUS(ntStatus);

    pInfo5->primary_gid = pUserInfo->PrimaryGroupRid;

    ntStatus = SamrSrvInitUnicodeString(&pInfo5->home_directory,
                                        pUserInfo->HomeDirectory);
    BAIL_ON_NT_STATUS(ntStatus);

    ntStatus = SamrSrvInitUnicodeString(&pInfo5->home_drive,
                                        pUserInfo->HomeDirectoryDrive);
    BAIL_ON_NT_STATUS(ntStatus);

    ntStatus = SamrSrvInitUnicodeString(&pInfo5->logon_script,
                                        pUserInfo->ScriptPath);
    BAIL_ON_NT_STATUS(ntStatus);

    ntStatus = SamrSrvInitUnicodeString(&pInfo5->profile_path,
                                        pUserInfo->ProfilePath);
    BAIL_ON_NT_STATUS(ntStatus);

    ntStatus = SamrSrvInitUnicodeString(&pInfo5->description,
                                        pUserInfo->Description);
    BAIL_ON_NT_STATUS(ntStatus);

    // Worstations is currently not supported
    ntStatus = SamrSrvInitUnicodeString(&pInfo5->workstations,
                                        NULL);
    BAIL_ON_NT_STATUS(ntStatus);

    pInfo5->last_logon = pUserInfo->LastLogon;
    pInfo5->last_logoff = pUserInfo->LastLogoff;

    CONVERT_LOGON_HOURS(&pUserInfo->LogonHours, &pInfo5->logon_hours);

    pInfo5->bad_password_count = pUserInfo->BadPasswordCount;
    pInfo5->logon_count = pUserInfo->LogonCount;
    pInfo5->last_password_change = pUserInfo->PasswordLastSet;
    pInfo5->account_expiry = 0;
    pInfo5->account_flags = SamDbMapDatabaseToSamrFlags(
                                pUserInfo->UserAccountControl,
                                NULL);

error:
    if (ntStatus)
    {
        SamrSrvFreeUnicodeString(&pInfo5->account_name);
        SamrSrvFreeUnicodeString(&pInfo5->full_name);
        SamrSrvFreeUnicodeString(&pInfo5->home_directory);
        SamrSrvFreeUnicodeString(&pInfo5->logon_script);
        SamrSrvFreeUnicodeString(&pInfo5->profile_path);
        SamrSrvFreeUnicodeString(&pInfo5->description);
        SamrSrvFreeUnicodeString(&pInfo5->workstations);

        memset(pInfo5, 0, sizeof(*pInfo5));
    }

    return ntStatus;
}


static
NTSTATUS
SamrFillUserInfo6(
    PSAMDB_OBJECT_INFO pObjectInfo,
    UserInfo *pInfo
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    UserInfo6 *pInfo6 = NULL;
    PSAMDB_USER_INFO pUserInfo = NULL;

    pUserInfo = &pObjectInfo->Info.User;
    pInfo6 = &(pInfo->info6);

    ntStatus = SamrSrvInitUnicodeString(&pInfo6->account_name,
                                        pObjectInfo->Name);
    BAIL_ON_NT_STATUS(ntStatus);

    ntStatus = SamrSrvInitUnicodeString(&pInfo6->full_name,
                                        pUserInfo->FullName);
    BAIL_ON_NT_STATUS(ntStatus);

error:
    if (ntStatus)
    {
        SamrSrvFreeUnicodeString(&pInfo6->account_name);
        SamrSrvFreeUnicodeString(&pInfo6->full_name);

        memset(pInfo6, 0, sizeof(*pInfo6));
    }

    return ntStatus;
}


static
NTSTATUS
SamrFillUserInfo7(
    PSAMDB_OBJECT_INFO pObjectInfo,
    UserInfo *pInfo
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    UserInfo7 *pInfo7 = NULL;
    PSAMDB_USER_INFO pUserInfo = NULL;

    pUserInfo = &pObjectInfo->Info.User;
    pInfo7 = &(pInfo->info7);

    ntStatus = SamrSrvInitUnicodeString(&pInfo7->account_name,
                                        pObjectInfo->Name);
    BAIL_ON_NT_STATUS(ntStatus);

error:
    if (ntStatus)
    {
        SamrSrvFreeUnicodeString(&pInfo7->account_name);

        memset(pInfo7, 0, sizeof(*pInfo7));
    }

    return ntStatus;
}


static
NTSTATUS
SamrFillUserInfo8(
    PSAMDB_OBJECT_INFO pObjectInfo,
    UserInfo *pInfo
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    UserInfo8 *pInfo8 = NULL;
    PSAMDB_USER_INFO pUserInfo = NULL;

    pUserInfo = &pObjectInfo->Info.User;
    pInfo8 = &(pInfo->info8);

    ntStatus = SamrSrvInitUnicodeString(&pInfo8->full_name,
                                        pUserInfo->FullName);
    BAIL_ON_NT_STATUS(ntStatus);

error:
    if (ntStatus)
    {
        SamrSrvFreeUnicodeString(&pInfo8->full_name);

        memset(pInfo8, 0, sizeof(*pInfo8));
    }

    return ntStatus;
}


static
NTSTATUS
SamrFillUserInfo9(
    PSAMDB_OBJECT_INFO pObjectInfo,
    UserInfo *pInfo
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    UserInfo9 *pInfo9 = NULL;
    PSAMDB_USER_INFO pUserInfo = NULL;

    pUserInfo = &pObjectInfo->Info.User;
    pInfo9 = &(pInfo->info9);

    pInfo9->primary_gid = pUserInfo->PrimaryGroupRid;

    return ntStatus;
}


static
NTSTATUS
SamrFillUserInfo10(
    PSAMDB_OBJECT_INFO pObjectInfo,
    UserInfo *pInfo
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    UserInfo10 *pInfo10 = NULL;
    PSAMDB_USER_INFO pUserInfo = NULL;

    pUserInfo = &pObjectInfo->Info.User;
    pInfo10 = &(pInfo->info10);

    ntStatus = SamrSrvInitUnicodeString(&pInfo10->home_directory,
                                        pUserInfo->HomeDirectory);
    BAIL_ON_NT_STATUS(ntStatus);

    ntStatus = SamrSrvInitUnicodeString(&pInfo10->home_drive,
                                        pUserInfo->HomeDirectoryDrive);
    BAIL_ON_NT_STATUS(ntStatus);

error:
    if (ntStatus)
    {
        SamrSrvFreeUnicodeString(&pInfo10->home_directory);
        SamrSrvFreeUnicodeString(&pInfo10->home_drive);

        memset(pInfo10, 0, sizeof(*pInfo10));
    }

    return ntStatus;
}


static
NTSTATUS
SamrFillUserInfo11(
    PSAMDB_OBJECT_INFO pObjectInfo,
    UserInfo *pInfo
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    UserInfo11 *pInfo11 = NULL;
    PSAMDB_USER_INFO pUserInfo = NULL;

    pUserInfo = &pObjectInfo->Info.User;
    pInfo11 = &(pInfo->info11);

    ntStatus = SamrSrvInitUnicodeString(&pInfo11->logon_script,
                                        pUserInfo->ScriptPath);
    BAIL_ON_NT_STATUS(ntStatus);

error:
    if (ntStatus)
    {
        SamrSrvFreeUnicodeString(&pInfo11->logon_script);

        memset(pInfo11, 0, sizeof(*pInfo11));
    }

    return ntStatus;
}


static
NTSTATUS
SamrFillUserInfo12(
    PSAMDB_OBJECT_INFO pObjectInfo,
    UserInfo *pInfo
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    UserInfo12 *pInfo12 = NULL;
    PSAMDB_USER_INFO pUserInfo = NULL;

    pUserInfo = &pObjectInfo->Info.User;
    pInfo12 = &(pInfo->info12);

    ntStatus = SamrSrvInitUnicodeString(&pInfo12->profile_path,
                                        pUserInfo->ProfilePath);
    BAIL_ON_NT_STATUS(ntStatus);

error:
    if (ntStatus)
    {
        SamrSrvFreeUnicodeString(&pInfo12->profile_path);

        memset(pInfo12, 0, sizeof(*pInfo12));
    }

    return ntStatus;
}


static
NTSTATUS
SamrFillUserInfo13(
    PSAMDB_OBJECT_INFO pObjectInfo,
    UserInfo *pInfo
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    UserInfo13 *pInfo13 = NULL;
    PSAMDB_USER_INFO pUserInfo = NULL;

    pUserInfo = &pObjectInfo->Info.User;
    pInfo13 = &(pInfo->info13);

    ntStatus = SamrSrvInitUnicodeString(&pInfo13->description,
                                        pUserInfo->Description);
    BAIL_ON_NT_STATUS(ntStatus);

error:
    if (ntStatus)
    {
        SamrSrvFreeUnicodeString(&pInfo13->description);

        memset(pInfo13, 0, sizeof(*pInfo13));
    }

    return ntStatus;
}


static
NTSTATUS
SamrFillUserInfo14(
    PSAMDB_OBJECT_INFO pObjectInfo,
    UserInfo *pInfo
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    UserInfo14 *pInfo14 = NULL;
    PSAMDB_USER_INFO pUserInfo = NULL;

    pUserInfo = &pObjectInfo->Info.User;
    pInfo14 = &(pInfo->info14);

    //
    // Worstations is currently not supported so return an empty string
    //
    ntStatus = SamrSrvInitUnicodeString(&pInfo14->workstations,
                                        NULL);
    BAIL_ON_NT_STATUS(ntStatus);

error:
    return ntStatus;
}


static
NTSTATUS
SamrFillUserInfo16(
    PSAMDB_OBJECT_INFO pObjectInfo,
    UserInfo *pInfo
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    UserInfo16 *pInfo16 = NULL;
    PSAMDB_USER_INFO pUserInfo = NULL;

    pUserInfo = &pObjectInfo->Info.User;
    pInfo16 = &(pInfo->info16);

    pInfo16->account_flags = SamDbMapDatabaseToSamrFlags(
                                pUserInfo->UserAccountControl,
                                NULL);
    return ntStatus;
}


static
NTSTATUS
SamrFillUserInfo17(
    PSAMDB_OBJECT_INFO pObjectInfo,
    UserInfo *pInfo
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    UserInfo17 *pInfo17 = NULL;

    pInfo17 = &(pInfo->info17);

    pInfo17->account_expiry = 0;

    return ntStatus;
}


static
NTSTATUS
SamrFillUserInfo20(
    PSAMDB_OBJECT_INFO pObjectInfo,
    UserInfo *pInfo
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    UserInfo20 *pInfo20 = NULL;
    PSAMDB_USER_INFO pUserInfo = NULL;

    pUserInfo = &pObjectInfo->Info.User;
    pInfo20 = &(pInfo->info20);

    //
    // Parameters is currently not supported so return an empty string
    //
    ntStatus = SamrSrvInitUnicodeString(&pInfo20->parameters,
                                        NULL);
    BAIL_ON_NT_STATUS(ntStatus);

error:
    return ntStatus;
}


static
NTSTATUS
SamrFillUserInfo21(
    PACCOUNT_CONTEXT pAcctCtx,
    PSAMDB_OBJECT_INFO pObjectInfo,
    UserInfo *pInfo
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    DWORD dwError = 0;
    UserInfo21 *pInfo21 = NULL;
    PSAMDB_USER_INFO pUserInfo = NULL;
    LONG64 ntMinPasswordAge = pAcctCtx->pDomCtx->ntMinPasswordAge;
    LONG64 ntMaxPasswordAge = pAcctCtx->pDomCtx->ntMaxPasswordAge;
    LONG64 ntCurrentTime = 0;

    pUserInfo = &pObjectInfo->Info.User;
    pInfo21 = &(pInfo->info21);

    pInfo21->last_logon = pUserInfo->LastLogon;
    pInfo21->fields_present |= SAMR_FIELD_LAST_LOGON;

    pInfo21->last_logoff = pUserInfo->LastLogoff;
    pInfo21->fields_present |= SAMR_FIELD_LAST_LOGOFF;

    pInfo21->last_password_change = pUserInfo->PasswordLastSet;
    pInfo21->fields_present |= SAMR_FIELD_LAST_PWD_CHANGE;

    pInfo21->account_expiry = 0;
    pInfo21->fields_present |= SAMR_FIELD_ACCT_EXPIRY;

    pInfo21->allow_password_change = pInfo21->last_password_change +
                                     ntMinPasswordAge;
    pInfo21->fields_present |= SAMR_FIELD_ALLOW_PWD_CHANGE;

    pInfo21->force_password_change = pInfo21->last_password_change +
                                     ntMaxPasswordAge;
    pInfo21->fields_present |= SAMR_FIELD_FORCE_PWD_CHANGE;

    ntStatus = SamrSrvInitUnicodeString(&pInfo21->account_name,
                                        pObjectInfo->Name);
    BAIL_ON_NT_STATUS(ntStatus);
    pInfo21->fields_present |= SAMR_FIELD_ACCOUNT_NAME;

    ntStatus = SamrSrvInitUnicodeString(&pInfo21->full_name,
                                        pUserInfo->FullName);
    BAIL_ON_NT_STATUS(ntStatus);
    pInfo21->fields_present |= SAMR_FIELD_FULL_NAME;

    ntStatus = SamrSrvInitUnicodeString(&pInfo21->home_directory,
                                        pUserInfo->HomeDirectory);
    BAIL_ON_NT_STATUS(ntStatus);
    pInfo21->fields_present |= SAMR_FIELD_HOME_DIRECTORY;

    ntStatus = SamrSrvInitUnicodeString(&pInfo21->home_drive,
                                        pUserInfo->HomeDirectoryDrive);
    BAIL_ON_NT_STATUS(ntStatus);
    pInfo21->fields_present |= SAMR_FIELD_HOME_DRIVE;

    ntStatus = SamrSrvInitUnicodeString(&pInfo21->logon_script,
                                        pUserInfo->ScriptPath);
    BAIL_ON_NT_STATUS(ntStatus);
    pInfo21->fields_present |= SAMR_FIELD_LOGON_SCRIPT;

    ntStatus = SamrSrvInitUnicodeString(&pInfo21->profile_path,
                                        pUserInfo->ProfilePath);
    BAIL_ON_NT_STATUS(ntStatus);
    pInfo21->fields_present |= SAMR_FIELD_PROFILE_PATH;

    ntStatus = SamrSrvInitUnicodeString(&pInfo21->description,
                                        pUserInfo->Description);
    BAIL_ON_NT_STATUS(ntStatus);
    pInfo21->fields_present |= SAMR_FIELD_DESCRIPTION;

    // Worstations is currently not supported
    ntStatus = SamrSrvInitUnicodeString(&pInfo21->workstations,
                                        NULL);
    BAIL_ON_NT_STATUS(ntStatus);
    pInfo21->fields_present |= SAMR_FIELD_WORKSTATIONS;

    ntStatus = SamrSrvInitUnicodeString(&pInfo21->comment,
                                        pUserInfo->UserComment);
    BAIL_ON_NT_STATUS(ntStatus);
    pInfo21->fields_present |= SAMR_FIELD_COMMENT;

    // Parameters is currently not supported
    ntStatus = SamrSrvInitUnicodeString(&pInfo21->parameters,
                                        NULL);
    BAIL_ON_NT_STATUS(ntStatus);
    pInfo21->fields_present |= SAMR_FIELD_PARAMETERS;

    /* unknown1 */
    /* unknown2 */
    /* unknown3 */

    ntStatus = RtlGetRidSid(&pInfo21->rid, pObjectInfo->Sid);
    BAIL_ON_NT_STATUS(ntStatus);
    pInfo21->fields_present |= SAMR_FIELD_RID;

    pInfo21->primary_gid = pUserInfo->PrimaryGroupRid;
    pInfo21->fields_present |= SAMR_FIELD_PRIMARY_GID;

    pInfo21->account_flags = SamDbMapDatabaseToSamrFlags(
                                pUserInfo->UserAccountControl,
                                NULL);
    pInfo21->fields_present |= SAMR_FIELD_ACCT_FLAGS;

    CONVERT_LOGON_HOURS(&pUserInfo->LogonHours, &pInfo21->logon_hours);
    pInfo21->fields_present |= SAMR_FIELD_LOGON_HOURS;

    pInfo21->bad_password_count = pUserInfo->BadPasswordCount;
    pInfo21->fields_present |= SAMR_FIELD_BAD_PWD_COUNT;

    pInfo21->logon_count = pUserInfo->LogonCount;
    pInfo21->fields_present |= SAMR_FIELD_NUM_LOGONS;

    pInfo21->country_code = pUserInfo->CountryCode;
    pInfo21->fields_present |= SAMR_FIELD_COUNTRY_CODE;

    pInfo21->code_page = pUserInfo->CountryCode;
    pInfo21->fields_present |= SAMR_FIELD_CODE_PAGE;

    dwError = LwGetNtTime((PULONG64)&ntCurrentTime);
    BAIL_ON_LSA_ERROR(dwError);

    if (ntCurrentTime >= pInfo21->last_password_change + ntMaxPasswordAge)
    {
        pInfo21->password_expired = 1;
    }

error:
    if (ntStatus == STATUS_SUCCESS &&
        dwError != ERROR_SUCCESS)
    {
        ntStatus = LwWin32ErrorToNtStatus(dwError);
    }

    if (ntStatus)
    {
        SamrSrvFreeUnicodeString(&pInfo21->account_name);
        SamrSrvFreeUnicodeString(&pInfo21->full_name);
        SamrSrvFreeUnicodeString(&pInfo21->home_directory);
        SamrSrvFreeUnicodeString(&pInfo21->home_drive);
        SamrSrvFreeUnicodeString(&pInfo21->logon_script);
        SamrSrvFreeUnicodeString(&pInfo21->profile_path);
        SamrSrvFreeUnicodeString(&pInfo21->description);
        SamrSrvFreeUnicodeString(&pInfo21->workstations);
        SamrSrvFreeUnicodeString(&pInfo21->comment);
        SamrSrvFreeUnicodeString(&pInfo21->parameters);

        memset(pInfo21, 0, sizeof(*pInfo21));
    }

    return ntStatus;
}


/*
local variables:
mode: c
c-basic-offset: 4
indent-tabs-mode: nil
tab-width: 4
end:
*/
