/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*-
 * ex: set softtabstop=4 tabstop=8 expandtab shiftwidth=4: *
 * Editor Settings: expandtabs and use 4 spaces for indentation */

/*
 * Copyright Likewise Software
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 *
 * This program 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 General Public License
 * for more details.  You should have received a copy of the GNU 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
 * 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:
 *
 *        samdbmem.c
 *
 * Abstract:
 *
 *
 *      Likewise SAM Database Provider
 *
 *      Memory allocation routines
 *
 * Authors: Rafal Szczesniak (rafal@likewise.com)
 *
 */

#include "includes.h"


static
DWORD
SamDbReallocBuffer(
    size_t* pBufferSize,
    PSTR *pBuffer,
    size_t MaxBufferSize
    );

static
VOID
SamDbFreeUserObject(
    PSAMDB_USER_INFO User
    );

static
VOID
SamDbFreeLocalGroupObject(
    PSAMDB_LOCAL_GROUP_INFO LocalGroup
    );


DWORD
SamDbPrintfString(
    IN OUT PSTR* Buffer,
    IN OUT size_t* Size,
    IN OUT size_t* Length,
    IN size_t MaxSize,
    IN PCSTR Format,
    ...
    )
{
    DWORD error = ERROR_SUCCESS;
    PSTR buffer = *Buffer;
    size_t size = *Size;
    size_t length = *Length;
    size_t writtenLength = 0;
    va_list argList;

    va_start(argList, Format);
    error = LwPrintfStringV(
                  &buffer[length],
                  size - length,
                  &writtenLength,
                  Format,
                  argList);
    va_end(argList);

    while (error == ENOMEM)
    {
        error = SamDbReallocBuffer(
                      &size,
                      &buffer,
                      MaxSize);
        BAIL_ON_SAMDB_ERROR(error);

        va_start(argList, Format);
        error = LwPrintfStringV(
                      &buffer[length],
                      size - length,
                      &writtenLength,
                      Format,
                      argList);
        va_end(argList);

        if (error != 0 &&
            error != ENOMEM)
        {
            error = LwErrnoToWin32Error(error);
            BAIL_ON_SAMDB_ERROR(error);
        }
    }

    length += writtenLength;

    if (error)
    {
        error = LwErrnoToWin32Error(error);
        BAIL_ON_SAMDB_ERROR(error);
    }

cleanup:
    *Buffer = buffer;
    *Size   = size;
    *Length = length;

    return error;
}


static
DWORD
SamDbReallocBuffer(
    size_t* pBufferSize,
    PSTR *pBuffer,
    size_t MaxBufferSize
    )
{
    DWORD error = ERROR_SUCCESS;
    size_t bufferSize = *pBufferSize;
    PSTR buffer = *pBuffer;
    PSTR newBuffer = NULL;

    bufferSize *= 2;
    if (bufferSize > MaxBufferSize)
    {
        error = ERROR_INTERNAL_ERROR;
        SAMDB_LOG_ERROR("Required space for SQL statement buffer is larger "
                        "than allowed (bufferSize = %u, "
                        "MaxBufferSize = %u)",
                        bufferSize,
                        MaxBufferSize);
        goto cleanup;
    }

    error = LwReallocMemory(
                     buffer,
                     OUT_PPVOID(&newBuffer),
                     sizeof(buffer[0]) * bufferSize);
    BAIL_ON_SAMDB_ERROR(error);

    buffer    = newBuffer;
    newBuffer = NULL;

cleanup:
    if (error)
    {
        LW_SAFE_FREE_MEMORY(newBuffer);
    }

    if (buffer)
    {
        *pBuffer = buffer;
        buffer = NULL;

        *pBufferSize = bufferSize;
    }

    return error;
}


VOID
SamDbFreeObject(
    PSAMDB_OBJECT_INFO Object
    )
{
    SamDbReleaseObject(Object);
    LW_SAFE_FREE_MEMORY(Object);
}


VOID
SamDbReleaseObject(
    PSAMDB_OBJECT_INFO Object
    )
{
    SAMDB_OBJECT_TYPE objectType = Object->Type;

    LW_SAFE_FREE_MEMORY(Object->Sid);
    LW_SAFE_FREE_MEMORY(Object->Name);

    switch (objectType)
    {
    case SAMDB_OBJECT_TYPE_USER:
        SamDbFreeUserObject(&Object->Info.User);
        break;

    case SAMDB_OBJECT_TYPE_LOCAL_GROUP:
        SamDbFreeLocalGroupObject(&Object->Info.LocalGroup);
        break;

    default:
        break;
    }
}


static
VOID
SamDbFreeUserObject(
    PSAMDB_USER_INFO User
    )
{
    DWORD i = 0;

    LW_SAFE_FREE_MEMORY(User->FullName);
    LW_SAFE_FREE_MEMORY(User->HomeDirectory);
    LW_SAFE_FREE_MEMORY(User->HomeDirectoryDrive);
    LW_SAFE_FREE_MEMORY(User->ScriptPath);
    LW_SAFE_FREE_MEMORY(User->ProfilePath);
    LW_SAFE_FREE_MEMORY(User->Description);

    for (i = 0; i < User->Workstations.Count; i++)
    {
        LW_SAFE_FREE_MEMORY(User->Workstations.String[i]);
    }
    LW_SAFE_FREE_MEMORY(User->Workstations.String);

    LW_SAFE_FREE_MEMORY(User->UserComment);

    if (User->LmOwfPassword.IsPresent)
    {
        memset(User->LmOwfPassword.Data,
               0,
               sizeof(User->LmOwfPassword.Data));
    }

    if (User->NtOwfPassword.IsPresent)
    {
        memset(User->NtOwfPassword.Data,
               0,
               sizeof(User->NtOwfPassword.Data));
    }

    if (User->Parameters.Size > 0)
    {
        LW_SAFE_FREE_MEMORY(User->Parameters.Data);
    }

    if (User->LogonHours.UnitsPerWeek)
    {
        LW_SAFE_FREE_MEMORY(User->LogonHours.LogonHours);
    }

    if (User->UnixInfo)
    {
        LW_SAFE_FREE_MEMORY(User->UnixInfo->HomeDirectory);
        LW_SAFE_FREE_MEMORY(User->UnixInfo->Shell);
        LW_SAFE_FREE_MEMORY(User->UnixInfo->Gecos);
    }
    LW_SAFE_FREE_MEMORY(User->UnixInfo);
}


static
VOID
SamDbFreeLocalGroupObject(
    PSAMDB_LOCAL_GROUP_INFO LocalGroup
    )
{
    LW_SAFE_FREE_MEMORY(LocalGroup->Description);
    LW_SAFE_FREE_MEMORY(LocalGroup->UnixInfo);
}


VOID
SamDbFreeObjects(
    IN DWORD ObjectsCount,
    IN PSAMDB_OBJECT_INFO* Objects
    )
{
    DWORD objectIndex = 0;

    for (objectIndex = 0; objectIndex < ObjectsCount; objectIndex++)
    {
        if (Objects[objectIndex] != NULL)
        {
            SamDbFreeObject(Objects[objectIndex]);
        }
    }

    LW_SAFE_FREE_MEMORY(Objects);
}


VOID
SamDbFree(
    IN PVOID Pointer
    )
{
    LW_SAFE_FREE_MEMORY(Pointer);
}


VOID
SamDbFreeSidList(
    IN DWORD SidsCount,
    IN PSID* Sids
    )
{
    DWORD sidIndex = 0;

    for (sidIndex = 0; sidIndex < SidsCount; sidIndex++)
    {
        LW_SAFE_FREE_MEMORY(Sids[sidIndex]);
    }

    LW_SAFE_FREE_MEMORY(Sids);
}
