
/******************************************************************************
**
**  This program is free software; you can redistribute it and/or
**  modify it, however, you cannot sell it.
**
**  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.
**
**  You should have received a copy of the license attached to the
**  use of this software.  If not, visit www.shmoo.com/osiris for
**  details.
**
******************************************************************************/

/*****************************************************************************
**
**  File:    config.c
**  Date:    June 3, 2002
**
**  Author:  Brian Wotring
**  Purpose: interact with local file system to read in config information
**           from hosts directory.
**
******************************************************************************/

#include "libosiris.h"
#include "libosirism.h"
#include "libosirisdb.h"
#include "libosirisctl.h"
#include "libfileapi.h"

#include "md_hosts.h"
#include "md_config.h"
#include "md_compare.h"

#include "common.h"
#include "logging.h"

extern char root_path[MAX_PATH_LENGTH];


configuration * osi_host_read_config_with_name( const char *host,
                                                const char *name )
{
    configuration *cfg = NULL;

    char path[MAX_PATH_LENGTH];
    char path_separator[2] = { PATH_SEPARATOR, '\0' };

    if( ( root_path != NULL ) && ( host != NULL ) && ( name != NULL ) )
    {
        cfg = osi_config_new();

        /* construct full path to specified config file. */

        osi_strlcpy( path, root_path, sizeof( path ) );
        osi_strlcat( path, path_separator, sizeof( path ) );
        osi_strlcat( path, HOSTS_DIRECTORY_NAME, sizeof( path ) );
        osi_strlcat( path, path_separator, sizeof( path ) );
        osi_strlcat( path, host, sizeof( path ) );
        osi_strlcat( path, path_separator, sizeof( path ) );
        osi_strlcat( path, HOST_CONFIG_DIRECTORY_NAME, sizeof( path ) );
        osi_strlcat( path, path_separator, sizeof( path ) );
        osi_strlcat( path, name, sizeof( path ) );

        /* now try to read in the configuration. */

        if( osi_config_read_from_file( cfg, path ) == FALSE )
        {
            osi_config_destroy( cfg );
            cfg = NULL;
        }
    }

    return cfg;
}

int osi_config_write_with_name( configuration *cfg, const char *host,
                                const char *name )
{
    int result = OSI_ERROR_NULL_ARGUMENT;

    if( ( cfg != NULL ) && ( root_path != NULL ) &&
        ( host != NULL ) && ( name != NULL ) )
    {
        int index;
        char *line;

        FILE *config_file = NULL;

        char path[MAX_PATH_LENGTH];
        char path_separator[2] = { PATH_SEPARATOR, '\0' };

        if( ( cfg->data == NULL ) || ( cfg->data->size == 0 ) )
        {
            return OSI_ERROR_CONFIG_IS_EMPTY;
        }

        /* make sure this host exists. */

        if( osi_host_exists( host ) == FALSE )
        {
            return OSI_ERROR_HOST_DOES_NOT_EXIST;
        }

        /* construct path to file. */

        osi_strlcpy( path, root_path, sizeof( path ) );
        osi_strlcat( path, path_separator, sizeof( path ) );
        osi_strlcat( path, HOSTS_DIRECTORY_NAME, sizeof( path ) );
        osi_strlcat( path, path_separator, sizeof( path ) );
        osi_strlcat( path, host, sizeof( path ) );
        osi_strlcat( path, path_separator, sizeof( path ) );
        osi_strlcat( path, HOST_CONFIG_DIRECTORY_NAME, sizeof( path ) );
        osi_strlcat( path, path_separator, sizeof( path ) );
        osi_strlcat( path, name, sizeof( path ) );

        config_file = osi_fopen( path, "w+", OSI_DEFAULT_PERMISSIONS );

        if( config_file == NULL )
        {
            return OSI_ERROR_UNABLE_TO_CREATE_FILE;
        }

        for( index = 0; (unsigned int)index < cfg->data->size; index++ )
        {
            line = cfg->data->list[index];

            if( line != NULL )
            {
                fwrite( line, sizeof( char ), strlen( line ), config_file );
                fwrite( "\n", sizeof( char ), 1, config_file );
            }

            else
            {
                fwrite( "\n", sizeof( char ), 1, config_file );
            }
        }

        result = OSI_OK;
        fclose( config_file );
    }

    return result;
}

configuration * osi_host_read_config_with_id( const char *host, const char *id )
{
    configuration *cfg = NULL;

    char path[MAX_PATH_LENGTH];
    char path_separator[2] = { PATH_SEPARATOR, '\0' };

    char file_path[MAX_PATH_LENGTH];
    
    OSI_DIRECTORY directory;
    memset( &directory, 0, sizeof( OSI_DIRECTORY ) );

    if( ( root_path == NULL ) || ( host == NULL ) ||  ( id == NULL ) )
    {
        return NULL;
    }

    /* first, we construct path to configs directory. */

    osi_strlcpy( path, root_path, sizeof( path ) );
    osi_strlcat( path, path_separator, sizeof( path ) );
    osi_strlcat( path, HOSTS_DIRECTORY_NAME, sizeof( path ) );
    osi_strlcat( path, path_separator, sizeof( path ) );
    osi_strlcat( path, host, sizeof( path ) );
    osi_strlcat( path, path_separator, sizeof( path ) );
    osi_strlcat( path, HOST_CONFIG_DIRECTORY_NAME, sizeof( path ) );

    log_info( LOG_HIGH, host, "looking for config: %s.", id );

    /* now traverse the directory, creating config objects and */
    /* adding them to this host's list of configs.             */

    if( osi_open_directory( path, &directory ) == FALSE )
    {
        log_error( LOG_MEDIUM, host, "config directory does not exist." );
        return NULL;
    }

    log_info( LOG_HIGH, host, "opened config directory." );

    do
    {
        /* create full path to the current filename. */

        osi_strlcpy( file_path, path, sizeof( file_path ) );
        osi_strlcat( file_path, path_separator, sizeof( file_path ) );

        osi_strlcat( file_path, directory.filename,
                     sizeof( file_path ) );

        if( osi_file_is_regular_file( file_path ) )
        {
            cfg = osi_config_new();

            if( osi_config_read_from_file( cfg, file_path ) )
            {
                /* compare id of this config to the requested one. */
                        
                if( strcmp( cfg->id, id ) == 0 )
                {
                    osi_strlcpy( cfg->name, directory.filename,
                                 sizeof( cfg->name ) );

                    break;
                }
            }

            osi_config_destroy( cfg );
            cfg = NULL;
        }

    } while( osi_get_next_file( &directory ) );

    osi_close_directory( &directory );
    return cfg;
}

configuration * osi_host_read_default_config( const char *os_name )
{
    configuration *cfg = NULL;
    char path_separator[2] = { PATH_SEPARATOR, '\0' };

    char os[MAX_STATUS_OS_NAME_LENGTH];
    char path[MAX_PATH_LENGTH];

    OSI_DIRECTORY directory;

    if( ( root_path == NULL ) || ( os_name == NULL ) )
    {
        return cfg;
    }

    osi_strlcpy( path, root_path, sizeof( path ) );
    osi_strlcat( path, path_separator, sizeof( path ) );
    osi_strlcat( path, DEFAULT_CONFIGS_DIRECTORY_NAME, sizeof( path ) );

    /* now find a config file that matches the os we were given. */

    if( osi_open_directory( path, &directory ) != OSI_OK )
    {
        return cfg;
    }

    /* copy passed in os name so we can lowercase it.    */
    /* some unames need to be modified because of weird  */
    /* characters, like "BSD/OS"                         */

    osi_strlcpy( os, os_name, sizeof( os ) );
    lowercase_string( os );

    if( strncmp( os, "bsd/os", 6 ) == 0 )
    {
        osi_strlcpy( os, "bsdos", sizeof( os ) );
    }

    do
    {
        char temp[MAX_PATH_LENGTH];

        osi_strlcpy( temp, directory.filename, sizeof( temp ) );
        lowercase_string( temp );
    
        /* the filename matches the os name passed in. */

        if( strncmp( os, temp, strlen( os ) ) == 0 )
        {
            osi_strlcat( path, path_separator, sizeof( path ) );
            osi_strlcat( path, directory.filename, sizeof( path ) );    

            break;
        }

    } while( osi_get_next_file( &directory ) );

    osi_close_directory( &directory );

    /* now open that config file and parse it into the cfg file .*/

    if( osi_file_is_regular_file( path ) == FALSE )
    {
        return cfg;
    }

    cfg = osi_config_new();

    /* now try to read in the configuration. */

    if( osi_config_read_from_file( cfg, path ) == FALSE )
    {
        osi_config_destroy( cfg );
        cfg = NULL;
    }

    return cfg;
}



OSI_CONFIG_BRIEF * osi_host_read_config_brief_with_name( const char *host,
                                                         const char *name )
{
    OSI_CONFIG_BRIEF *config_brief = NULL;
    configuration *cfg = NULL;

    cfg = osi_host_read_config_with_name( host, name );

    if( cfg != NULL )
    {
        config_brief = osi_malloc( sizeof( OSI_CONFIG_BRIEF ) );
        memset( config_brief, 0, sizeof( OSI_CONFIG_BRIEF ) );

        osi_strlcpy( config_brief->name, name, sizeof( config_brief->name ) );
        osi_strlcpy( config_brief->id, cfg->id, sizeof( config_brief->id ) );

        if( cfg->error_count == 0 )
        {
            config_brief->valid = 1;
        }

        osi_config_destroy( cfg );
    }

    return config_brief;
}


int osi_host_read_configs( OSI_HOST *host )
{
    char path[MAX_PATH_LENGTH];
    char path_separator[2] = { PATH_SEPARATOR, '\0' };

    OSI_DIRECTORY directory;
    memset( &directory, 0, sizeof( OSI_DIRECTORY ) );

    if( ( root_path != NULL ) && ( host != NULL ) )
    {
        /* first, we construct path to configs directory. */

        osi_strlcpy( path, root_path, sizeof( path ) );
        osi_strlcat( path, path_separator, sizeof( path ) );
        osi_strlcat( path, HOSTS_DIRECTORY_NAME, sizeof( path ) );
        osi_strlcat( path, path_separator, sizeof( path ) );
        osi_strlcat( path, host->name, sizeof( path ) );
        osi_strlcat( path, path_separator, sizeof( path ) );
        osi_strlcat( path, HOST_CONFIG_DIRECTORY_NAME, sizeof( path ) );

        log_info( LOG_HIGH, host->name, "looking for configs." );

        /* now traverse the directory, creating config objects and */
        /* adding them to this host's list of configs.             */

        if( osi_open_directory( path, &directory ) )
        {
            char file_path[MAX_PATH_LENGTH];
            log_info( LOG_HIGH, host->name, "opened config directory." );

            do
            {
                /* create full path to the current filename. */

                osi_strlcpy( file_path, path, sizeof( file_path ) );
                osi_strlcat( file_path, path_separator, sizeof( file_path ) );

                osi_strlcat( file_path, directory.filename,
                             sizeof( file_path ) );

                if( osi_file_is_regular_file( file_path ) )
                {
                    configuration *cfg = osi_config_new();

                    if( osi_config_read_from_file( cfg, file_path ) )
                    {
                        /* the name for this config will be the filename. */

                        osi_strlcpy( cfg->name, directory.filename,
                                     sizeof( cfg->name ) );

                        list_add( host->configs, cfg );
                    }

                    else
                    {
                        log_error( LOG_MEDIUM, host->name,
                                   "cannot read config file: %s.", file_path );

                        osi_config_destroy( cfg );
                    }
                }

            } while( osi_get_next_file( &directory ) );

            osi_close_directory( &directory );
        }

        else
        {
            log_info( LOG_MEDIUM, host->name, "no config directory." );
        }
    }

    return ( list_get_size( host->configs ) );
}


int osi_host_read_config_briefs( OSI_HOST *host )
{
    char path[MAX_PATH_LENGTH];
    char path_separator[2] = { PATH_SEPARATOR, '\0' };

    OSI_DIRECTORY directory;
    memset( &directory, 0, sizeof( OSI_DIRECTORY ) );

    if( ( root_path != NULL ) && ( host != NULL ) )
    {
        /* first, we construct path to configs directory. */

        osi_strlcpy( path, root_path, sizeof( path ) );
        osi_strlcat( path, path_separator, sizeof( path ) );
        osi_strlcat( path, HOSTS_DIRECTORY_NAME, sizeof( path ) );
        osi_strlcat( path, path_separator, sizeof( path ) );
        osi_strlcat( path, host->name, sizeof( path ) );
        osi_strlcat( path, path_separator, sizeof( path ) );
        osi_strlcat( path, HOST_CONFIG_DIRECTORY_NAME, sizeof( path ) );

        log_info( LOG_HIGH, host->name, "looking for configs." );

        /* now traverse the directory, creating config objects and */
        /* adding them to this host's list of configs.             */

        if( osi_open_directory( path, &directory ) )
        {
            char file_path[MAX_PATH_LENGTH];
            log_info( LOG_HIGH, host->name, "opened config directory." );

            do
            {
                /* create full path to the current filename. */

                osi_strlcpy( file_path, path, sizeof( file_path ) );
                osi_strlcat( file_path, path_separator, sizeof( file_path ) );

                osi_strlcat( file_path, directory.filename,
                             sizeof( file_path ) );

                if( osi_file_is_regular_file( file_path ) )
                {
                    OSI_CONFIG_BRIEF *config_brief;

                    if( ( config_brief = osi_host_read_config_brief_with_name(
                          host->name, directory.filename ) ) != NULL )
                    {
                        list_add( host->configs, config_brief );
                    }

                    else
                    {
                        log_error( LOG_MEDIUM, host->name,
                                   "reading config file: %s.", file_path );
                    }
                }

            } while( osi_get_next_file( &directory ) );

            osi_close_directory( &directory );
        }

        else
        {
            log_error( LOG_MEDIUM, host->name, "has no config directory." );
        }
    }

    return ( list_get_size( host->configs ) );
}


