
/******************************************************************************
**
**  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:    osiris.c
**  Date:    April 8, 2002
**
**  Author:  Brian Wotring
**  Purpose: cli management application.
**
******************************************************************************/


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

#ifdef WIN32
#include <conio.h>
#endif

#include <signal.h>
#include "osiris.h"

#ifdef HAVE_TERMIOS_H
#include <termios.h>
#endif

#define SCHEDULE_TIME_FORMAT "%m/%d/%Y %H:%M"


/*****************************************************************************
**
**    Function: main
**
**    Purpose:  system driver for this application, read in arguments,
**		        initialize ssl, connect to mangagement host, and process
**              commands forever.
**
******************************************************************************/

int main( int argument_count, char *argument_list[] )
{
    init();

    parse_arguments( argument_count, argument_list );
    print_version();

    locate_root_cert_file();
    initialize_ssl();

    if( connect_to_management_host() == FALSE )
    {
        halt(-1);
    }

    for( ;; )
    {
        process_command();
    }

    halt(0);
    return 0;
}


/*****************************************************************************
**
**    Function: init
**
**    Purpose:  start winsock and signal handlers, initialize any global
**              structures we use.
**
*****************************************************************************/

void init()
{
    startup_winsock();
    initialize_signals();
    locate_editor();

    memset( (void *)&ctl_context, 0, sizeof( ctl_context ) );
}

void locate_editor()
{
#ifdef WIN32
    osi_strlcpy( editor_path, PATH_DEFAULT_EDITOR, sizeof( editor_path ) );
#else

    struct stat stats;

    /* try to find vi. */

    osi_strlcpy( editor_name, name, sizeof( editor_name ) );

    if( osi_file_exists( "/usr/bin/vi" ) == TRUE )
    {
        osi_strlcpy( editor_path, "/usr/bin/vi", sizeof( editor_path ) );
    }

    else if( osi_file_exists( "/bin/vi" ) == TRUE )
    {
        osi_strlcpy( editor_path, "/bin/vi", sizeof( editor_path ) );
    }

    else if( osi_file_exists( "/usr/bin/vim" ) == TRUE )
    {
        osi_strlcpy( editor_path, "/usr/bin/vim", sizeof( editor_path ) );
        osi_strlcpy( editor_name, "vim", sizeof( editor_name ) );
    }

    else
    {
        osi_strlcpy( editor_path, PATH_DEFAULT_EDITOR, sizeof( editor_path ) );
        osi_strlcpy( editor_name, NAME_DEFAULT_EDITOR, sizeof( editor_name ) );
    }

    if( osi_file_exists( editor_path ) == FALSE )
    {
        osi_print_stderr( "warning: unable to locate an editor." );
        return;
    }

    /* now verify ownership and perms on this editor. */

    if( stat( PATH_DEFAULT_EDITOR, &stats ) == 0 )
    {
        if( stats.st_uid != 0 )
        {
            osi_print_stderr(
                    "warning: editor not owned by root, not trusted." );
            return;
        }

        if( ( stats.st_mode & S_IWGRP ) ||
            ( stats.st_mode & S_IWOTH ) )
        {
            osi_print_stderr(
                      "warning: invalid permissions on editor, not trusted." );
            return;
        } 
    }

#endif
}


/*****************************************************************************
**
**    Function: initialize_ssl
**
**    Purpose:  initializes the SSL library, and creates a connection
**		context that we will use for all connections.
**
*****************************************************************************/

void initialize_ssl()
{
    SSL_METHOD *method = NULL;
    bio_error = BIO_new_fp( stderr, BIO_NOCLOSE );

    SSL_library_init();
    SSL_load_error_strings();

    method = SSLv23_method();
    ssl_context = SSL_CTX_new( method );

    /* create our global SSL context for this server. */

    if( ssl_context == NULL )
    {
        ERR_print_errors( bio_error );
        osi_print_stderr( "unable to initialize SSL support." );

        halt( -1 );
    }

    ctl_context.ssl_context = ssl_context;

    /* if we can't load the certificate, we won't do any authentication */
    /* on the server cert, we will prompt user to trust it initially.   */
    /* then save it locally, otherwise, we require authentication.      */

    if( SSL_CTX_load_verify_locations( ssl_context,
                                       root_cert_file, NULL ) != 1 )
    {
        osi_print_stderr( "unable to load root cert for management host:\n(%s)",
                          root_cert_file );

    	SSL_CTX_set_verify( ssl_context, SSL_VERIFY_NONE, NULL );

        /* connect and get cert from management daemon, if the     */
	    /* user allowed this, we now have a root cert locally      */
	    /* loaded otherwise, they disallowed it, and we exit here. */

    	if( get_peer_certificate() == FALSE )
    	{
	        halt( -1 );
	    }
        
        /* now we need to reload the root cert that we acquired. */
        
        if( SSL_CTX_load_verify_locations( ssl_context,
                                           root_cert_file, NULL ) != 1 )
        {
            osi_print_stderr(
                        "unable to load root cert for management host (%s).",
                        root_cert_file );            

            halt( -1 );
        }
    }

    SSL_CTX_set_verify( ssl_context, SSL_VERIFY_PEER, ssl_verify_callback );
    SSL_CTX_set_verify_depth( ssl_context, OSIRIS_SERVER_CERT_CHAIN_DEPTH );
    SSL_CTX_set_options( ssl_context, OSIRIS_SSL_OPTIONS );

    if( SSL_CTX_set_cipher_list( ssl_context, OSIRIS_CIPHER_LIST ) != 1 )
    {
        osi_print_stderr( "unable to set SSL cipher list." );
        halt( -1 );
    }
}

/*****************************************************************************
**
**    Function: ssl_verify_callback
**
**    Purpose:  callback to get and display detailed error information
**		in the case of a handshake failure, probably to send it
**		to the logs.
**
*****************************************************************************/

int ssl_verify_callback( int ok, X509_STORE_CTX *store )
{
    char data[256];
    
    if( !ok )
    {
        X509 *cert = X509_STORE_CTX_get_current_cert( store );
        
        int depth = X509_STORE_CTX_get_error_depth( store );
        int err = X509_STORE_CTX_get_error( store );
        
        osi_print_stderr( "" );
        
        osi_print_stderr(
            "WARNING : certificate authentication failed.  The certificate" );

        osi_print_stderr(
            "presented by the mangaement host (%s) was not signed by", mhost );

        osi_print_stderr( "the root cert loaded from (%s).\n", root_cert_file );
                
        osi_print_stderr( "[ presented certificate ]\n" );

        X509_NAME_oneline( X509_get_issuer_name( cert ), data, sizeof( data ) );
        osi_print_stderr( "    issuer = %s", data );

        X509_NAME_oneline( X509_get_subject_name( cert ),
                           data,
                           sizeof( data ) );

        osi_print_stderr( "   subject = %s", data );

        if( osi_get_x509_fingerprint( cert, data, sizeof( data ) ) != NULL )
        {
            osi_print_stderr( "untrusted MD5 fingerprint: %s.\n", data );
        }
        
        osi_print_stderr(
            "If you trust this certificate, delete the root cert and start" );

        osi_print_stderr( "this application again.\n" );
        
        halt( -1 );
    }

    return ok;
}

/******************************************************************************
**
**    Function: shutdown_ssl
**
**    Purpose:  perform necessary cleanup for SSL library.
**
******************************************************************************/

void shutdown_ssl()
{
    if( ssl_context != NULL )
    {
        SSL_CTX_free( ssl_context );
    }

    ERR_free_strings();
    ERR_remove_state(0);

    if( bio_error != NULL )
    {
        BIO_free( bio_error );
    }
}

/******************************************************************************
**
**    Function: initialize_signals
**
**    Purpose:  add various common signals to the signal handler.
**
******************************************************************************/

void initialize_signals()
{
    signal( SIGINT, signal_handler );
}

/****************************************************************************
**
**    Function: signal_handler
**
**    Purpose:  handle common signals, this is mostly for unix clients.
**
******************************************************************************/

void signal_handler( int signal )
{
    switch( signal )
    {
        case SIGINT:

          /* if we are in a loop, we just stop the loop. */

          if( continue_polling )
          {
              continue_polling = FALSE;

              fprintf( stdout,
                       "\r...                                         " );

              fprintf( stdout, "                        " );
              fflush( stdout );

              break;
          }

          /* otherwise, we die. */

          else
          {
              fprintf( stdout, "\n" );
              fflush( stdout );
              halt(0);
          }

        default:
            break;
    }
}


/*****************************************************************************
**
**    Function: locate_root_cert_file
**
**    Purpose:  look for root cert in local user's osiris directory.  If
**		not there, we look in the default system directory.  If
**              we can't find a cert, we set our root path to be the local
**		directory so we can save it when we receive it from the 
**		host we connect to.
**
*****************************************************************************/

void locate_root_cert_file()
{
    char path[MAX_PATH_LENGTH];
    char path_separator[2] = { PATH_SEPARATOR, '\0' };
    
    /* if user specified a file, we don't do anything this time. */
    
    if( strlen( root_cert_file ) > 0 )
    {
        return;
    }

    /* construct path to user's home osiris directory. */
    
    if( osi_get_user_home_directory( path, sizeof( path ) ) )
    {
        osi_strlcat( path, path_separator, sizeof( path ) );
        osi_strlcat( path, PATH_USER_DIR, sizeof( path ) );
    }
    
    else
    {
        osi_print_stderr( "unable to determine home directory." );
        return;
    }
        
    /* before we do anything, we create our local osiris directory if  */
    /* it doesn't already exist.                                       */
    
    if( osi_directory_exists( path ) == FALSE )
    {
        if( osi_create_directory( path ) != OSI_OK )
        {
            osi_print_stderr( "unable to create osiris directory (%s).",
                              PATH_USER_DIR );
        }
    }
    
    /* first, look in our home directory for the root cert, if we find it  */
    /* then we save our root path value to be our home directory and bail. */
    
    osi_strlcat( path, path_separator, sizeof( path ) );
    osi_strlcat( path, FILE_ROOT_CERT, sizeof( path ) );
    osi_strlcpy( root_cert_file, path, sizeof( root_cert_file ) );
}


osi_bool connect_to_management_host()
{
    prompt_for_mhost_auth_information();
    osi_strlcpy( ctl_context.mhost, mhost, sizeof( ctl_context.mhost ) );

    ssl = ctl_connect( &ctl_context );

    if( ssl != NULL )
    {
        /* we connected, now we say hello to the mhost. */

        process_hello_mhost();

        /* save our current user we connected with. */

        osi_strlcpy( current_user, ctl_context.username,
                     sizeof( current_user ) );

        /* clobber login information. */

        memset( ctl_context.username, 0, sizeof( ctl_context.username ) );
        memset( ctl_context.password, 0, sizeof( ctl_context.password ) );

        return TRUE;
    }

    osi_print_stderr( "error: contacting management host (%s) failed:\n",
                      ctl_context.mhost );

    osi_print_stderr( "    - management host unreachable." );
    osi_print_stderr( "    - user authentication failure." );
    osi_print_stderr( "    - this host is not on the allowed client list.");
    osi_print_stderr( "" );

    memset( mhost, 0, sizeof( mhost ) );

    return FALSE;
}

osi_bool disconnect_from_management_host()
{
    return ctl_disconnect( &ctl_context ); 
}

osi_bool reconnect_to_management_host()
{
    disconnect_from_management_host();
    return connect_to_management_host();
}

/*****************************************************************************
**
**    Function: get_peer_certificate
**
**    Purpose:  contact the remote host and get the X509 from it.
**              print info and ask user if they want to trust it, if
**              trusted, we attempt to write it to disk for future
**              use, otherwise, we ignore it and return false.
**
*****************************************************************************/

osi_bool get_peer_certificate()
{
    osi_bool result = FALSE;

    X509 *peer = NULL;
    EVP_PKEY *pubkey;

    char data[512] = "";
    char buffer[10] = "";

    
    osi_print_stdout( "fetching root certificate from management host (%s).",
                      mhost );

    osi_strlcpy( ctl_context.mhost, mhost, sizeof( ctl_context.mhost ) );
    peer = ctl_get_peer_certificate( &ctl_context );

    if( peer == NULL )
    {
        osi_print_stderr( "==> error: unable to obtain root cert." );
        osi_print_stderr(
                    "==> verify this host is in the allowed client list.\n" );

        return FALSE;
    }

    osi_print_stdout( "\nThe authenticity of host '%s' can't be established.",
                      mhost );

    osi_print_stdout( "\n  [ server certificate ]\n");
    X509_NAME_oneline( X509_get_subject_name( peer ), data, sizeof( data ) );
    osi_print_stdout( "      subject = %s", data );
    X509_NAME_oneline( X509_get_issuer_name( peer ), data, sizeof( data ) );
    osi_print_stdout( "      issuer  = %s\n", data );

    // get and print key size and fingerprint for this certificate.

    pubkey = X509_get_pubkey( peer );

    if( pubkey != NULL )
    {
            fprintf( stdout, "            " ); 
            osi_print_stdout( "key size: %d bit", EVP_PKEY_bits( pubkey ));
            EVP_PKEY_free( pubkey );
    }


    if( osi_get_x509_fingerprint( peer, data, sizeof( data ) ) != NULL )
    {
            osi_print_stdout( "      MD5 fingerprint: %s\n", data );
    }

    /* ask user for permission to trust this cert. */

    fprintf( stdout, "%s\n%s",
                    "Verify the fingerprint specified above.",
                    "Are you sure you want to continue connecting (yes/no)? " );

    if( get_user_input( buffer, sizeof( buffer ) ) != 0 )
    {
            lowercase_string( buffer );

            if( ( strcmp( buffer, "y" ) == 0 ) ||
                ( strcmp( buffer, "yes" ) == 0 ) )
            {
                    if( osi_write_x509_to_path( peer,
                                            root_cert_file,
                                            ROOT_CERT_PERMISSIONS ) == OSI_OK )
                    {
                            result = TRUE;
                    }

                    else
                    {
                        osi_print_stderr( "unable to write cert to path (%s)",
                                           root_cert_file );
                    }
            }
    }

    X509_free( peer );
    return result;
}


void parse_arguments( int argument_count, char *argument_list[] )
{
    int count;

    for( count = 1; count < argument_count; count++ )
    {    
        /* read in path to root cert file to use. */
        
        if( ( strcmp( argument_list[count], "--file" ) == 0 ||
              strcmp( argument_list[count], "-f" ) == 0 ) &&
            ( count < ( argument_count - 1 ) ) )
        {
            count++;

            if( osi_file_exists( argument_list[count] ) )
            {
                osi_strlcpy( root_cert_file,
                             argument_list[count],
                             sizeof( root_cert_file ) );
            }

            else
            {
                osi_print_stderr(
                            "osirismd: error, unable to locate file (%s).",
                            argument_list[count] );

                exit( -1 );
            }
        }

        /* help. */
        
        else if( strcmp( argument_list[count], "--help" ) == 0 ||
            strcmp( argument_list[count], "-h" ) == 0 )
        {
            print_usage();
            exit( 0 );
        }

        /* user name. */

        else if( ( strcmp( argument_list[count], "-u" ) == 0 ) &&
                 ( count < ( argument_count - 1 ) ) )
        {
            count++;
            osi_strlcpy( current_user, argument_list[count],
                         sizeof( current_user ) );
        }

        /* if this argument doesn't begin with a dash, and it is    */
        /* the last argument, we consider it a management hostname. */

        else if( ( argument_list[count][0] != '-' ) &&
                 ( count == ( argument_count - 1 ) ) )
        {
            osi_strlcpy( mhost, argument_list[1], sizeof( mhost ) );
        }

        else
        {
            print_usage();
            exit( -1 );
        }
    }
}

int get_user_input( char *buffer, int buffer_size )
{
    int length = 0;

    if( ( buffer != NULL ) && ( buffer_size > 0 ) )
    {
        if( fgets( buffer, ( buffer_size - 1 ), stdin ) != NULL )
        {
            int index;

            /* remove newline at end of input. */

            for( index = 0; (unsigned int)index < strlen( buffer ); index++ )
            {
                if( buffer[index] == '\n')
                {
                    buffer[index] = '\0';
                }
            }

            length = strlen( buffer );
        }
    }

    return length;
}

int get_password_input( char *buffer, int buffer_size, char *prompt )
{
#ifdef WIN32

    int ch;
    char *p;
    char *end;

    if( ( buffer == NULL ) || ( buffer_size <= 0 ) || ( prompt == NULL ) )
    {
        return 0;
    }

    end = ( buffer + buffer_size - 1 );
    p = buffer;

    memset( buffer, 0, buffer_size );

    fprintf( stdout, prompt );

    while( ( ch = getch() ) && ( ch != '\r' ) && ( ch != '\n' ) )
    {
        /* handle backspaces. */

        if( ( ch == '\b' ) && ( strlen( buffer ) > 0 ) )
        {
            fprintf( stdout, "\b \b" );
            buffer[strlen(buffer)-1] = '\0';
            p--;
        }

        if( ( p < end ) && ( ch != '\b' ) )
        {
            (*p++) = ch;
            putch( '*' );
        }
    }

    fprintf( stdout, "\n" );
    (*p) = '\0';

    return ( p - buffer );

#else

    int input;
    int output;

    size_t burst;

    char *end;
    char *p;
    char ch;

#ifdef HAVE_TERMIOS_H
    struct termios term;
    struct termios oterm;
#endif

    if( ( buffer == NULL ) || ( buffer_size <= 0 ) || ( prompt == NULL ) )
    {
        return 0;
    }

#ifdef HAVE_TERMIOS_H
    if( ( input = output = open( PATH_TTY, O_RDWR ) ) == -1 )
#endif
    {
        input = STDIN_FILENO;
        output = STDERR_FILENO;

        fprintf( stderr,
              "** unable to get tty! (using stdin), password will echo **\n" );
    }

    /* turn off echo if possible. */

#ifdef HAVE_TERMIOS_H
    if( ( input != STDIN_FILENO ) && ( tcgetattr( input, &oterm ) == 0 ) )
    {
        memcpy( &term, &oterm, sizeof( term ) );

        term.c_lflag &= ~( ECHO | ECHONL );
        tcsetattr( input, TCSAFLUSH, &term );
    }

    else
    {
        memset( &term, 0, sizeof( term ) );
        term.c_lflag |= ECHO;
        memset( &oterm, 0, sizeof( oterm ) );
        oterm.c_lflag |= ECHO;
    }
#endif

    write( output, prompt, strlen( prompt ) );

    /* now read the input. */

    end = ( buffer + buffer_size - 1 );

    for( p = buffer; ( ( burst = read( input, &ch, 1 ) ) == 1 ) &&
                       ( ch != '\n' ) && ( ch != '\r' ); )
    {
        if( p < end )
        {
            (*p++) = ch;
        }
    }

    (*p) = '\0';

#ifdef HAVE_TERMIOS_H
    if( !( term.c_lflag & ECHO ) )
    {
        write( output, "\n", 1 );
    }

    /* restore old terminal settings if different. */

    if( memcmp( &term, &oterm, sizeof( term ) ) != 0 )
    {
        tcsetattr( input, TCSAFLUSH, &oterm );
    }
#endif

    if( input != STDIN_FILENO )
    {
        close( input );
    }

    return ( p - buffer );

#endif /* WIN32 */
}

void process_command()
{
    print_prompt();
    command = read_and_parse_command();

    switch( command )
    {
        case COMMAND_STATUS_REQUEST:
            process_status_request();
            break;

        case COMMAND_NEW_USER:
            process_new_user();
            break;

        case COMMAND_EDIT_USER:
            process_edit_user();
            break;

        case COMMAND_LIST_USERS:
            process_list_users();
            break;

        case COMMAND_DELETE_USER:
            process_delete_user();
            break;

        case COMMAND_START_SCAN:
            process_start_scan();
            break;

        case COMMAND_STOP_SCAN:
            process_stop_scan();
            break;

        case COMMAND_WATCH_HOST:
            process_watch_host();
            break;

        case COMMAND_PUSH_CONFIG:
            process_push_config();
            break;

        case COMMAND_IMPORT_CONFIG:
            process_import_config();
            break;

        case COMMAND_EDIT_CONFIG:
            process_edit_config();
            break;

        case COMMAND_LIST_HOSTS:
            process_list_hosts();
            break;

        case COMMAND_LIST_CONFIGS:
            process_list_configs();
            break;

        case COMMAND_LIST_DATABASES:
            process_list_databases();
            break;

        case COMMAND_LIST_LOGS:
            process_list_logs();
            break;

        case COMMAND_PRINT_DB:
            process_print_db();
            break;

        case COMMAND_PRINT_LOG:
            process_print_log();
            break;

        case COMMAND_PRINT_DB_ERRORS:
            process_print_db_errors();
            break;

        case COMMAND_PRINT_CONFIG:
            process_print_config();
            break;

        case COMMAND_PRINT_HOST_CONFIG:
            process_print_host_config();
            break;
            
        case COMMAND_PRINT_MANAGEMENT_CONFIG:
            process_print_mhost_config();
            break;
            
        case COMMAND_HOST:
            process_host();
            break;

        case COMMAND_HOST_DETAILS:
            process_host_details();
            break;

        case COMMAND_NEW_HOST:
            process_new_host();
            break;

        case COMMAND_EDIT_MHOST_CONFIG:
            process_edit_mhost_config();
            break;

        case COMMAND_EDIT_HOST:
            process_edit_host();
            break;

        case COMMAND_INITIALIZE_HOST:
            process_initialize_host();
            break;

        case COMMAND_ENABLE_HOST:
            process_enable_host();
            break;

        case COMMAND_DISABLE_HOST:
            process_disable_host();
            break;

        case COMMAND_DROP_CONFIG:
            process_drop_config();
            break;

        case COMMAND_SET_BASE_DB:
            process_set_base_db();
            break;

        case COMMAND_SHOW_BASE_DB:
            process_show_base_db();
            break;

        case COMMAND_MANAGEMENT_HOST:
            process_mhost();
            break;

        case COMMAND_EDIT_FILTERS:
            process_edit_filters();
            break;

        case COMMAND_PRINT_FILTERS:
            process_print_filters();
            break;

        case COMMAND_SEND_NOTIFY_TEST:
            process_send_notify_test();
            break;

        case COMMAND_VERSION:
            print_version();
            break;

        case COMMAND_PRINT_SSL:
            print_ssl();
            break;

        case COMMAND_EXIT:
            process_quit();
            break;

        case COMMAND_NONE:
            break;

        case COMMAND_HELP:
            process_help();
            break;

        case COMMAND_UNKNOWN:
        default:
            print_commands();
            break;
    }

    /* now restore the original host in case it was clobbered. */

    osi_strlcpy( ctl_context.host, host, sizeof( ctl_context.host ) );
}

int read_and_parse_command()
{
    int command_type = COMMAND_NONE;

    static char buffer[MAX_COMMAND_LENGTH];
    static char token[MAX_TOKEN_LENGTH];
    
    memset( buffer, 0, sizeof( buffer ) );
    memset( token, 0, sizeof( token ) );

    /* zero out the command data buffer and password buffer. */

    memset( ctl_context.buffer, 0, sizeof( ctl_context.buffer ) );
    memset( ctl_context.password, 0, sizeof( ctl_context.password ) );
    memset( name, 0, sizeof( name ) );

    if( get_user_input( buffer, sizeof( buffer ) ) != 0 )
    {
        char *next_token;

        /* first, get the command. */

        next_token = get_token( buffer, token, sizeof( token ) );
        command_type = determine_command_type( token );

        /* now read any argument. */

        if( next_token != NULL )
        {
            get_token( next_token, token, sizeof( token ) );
            osi_strlcpy( name, token, sizeof( name ) );
        }

        /* different commands will do different things with the argument. */
        /* some will use it as the name of a host, others, as the buffer. */

        switch( command_type )
        {
            case COMMAND_HELP:

                /* if a command was specified, we save it so we  */
                /* know what to print help on, otherwise we just */
                /* set the command to none.                      */

                if( strlen( name ) > 0 )
                {
                    help_command = determine_command_type( token );
                }

                else
                {
                    help_command = COMMAND_NONE;
                }

                break;

            /* these commands all use the argument as the name of a host. */

            case COMMAND_STATUS_REQUEST:
            case COMMAND_START_SCAN:
            case COMMAND_STOP_SCAN:
            case COMMAND_WATCH_HOST:
            case COMMAND_LIST_CONFIGS:
            case COMMAND_LIST_DATABASES:
            case COMMAND_LIST_LOGS:
            case COMMAND_PRINT_HOST_CONFIG:
            case COMMAND_HOST_DETAILS:
            case COMMAND_EDIT_HOST:
            case COMMAND_INITIALIZE_HOST:
            case COMMAND_ENABLE_HOST:
            case COMMAND_DISABLE_HOST:
            case COMMAND_DROP_CONFIG:
            case COMMAND_SHOW_BASE_DB:

                if( strlen( name ) > 0 )
                {
                    osi_strlcpy( ctl_context.host, name,
                                 sizeof( ctl_context.host ) );
                }

                break;

            /* these commands all use the argument as the name of something. */
            /* thus we put it in the buffer for the context.                 */

            case COMMAND_EDIT_USER:
            case COMMAND_DELETE_USER:
            case COMMAND_PUSH_CONFIG:
            case COMMAND_EDIT_CONFIG:
            case COMMAND_PRINT_CONFIG:
            case COMMAND_PRINT_DB:
            case COMMAND_PRINT_LOG:
            case COMMAND_PRINT_DB_ERRORS:
            case COMMAND_SET_BASE_DB:
    
                if( strlen( name ) > 0 )
                {
                    osi_strlcpy( ctl_context.buffer, name,
                                 sizeof( ctl_context.buffer ) );
                }

                break;

            case COMMAND_UNKNOWN:
            default:
                break;
        }
    }

    return command_type;
}

int determine_command_type( const char *buffer )
{
    int type = COMMAND_UNKNOWN;
    int index;

    if( buffer != NULL )
    {
        for( index = 0; command_keywords[index].word != NULL; index++ )
        {
            int command_type   = command_keywords[index].type;
            char *command_name = command_keywords[index].word;

            if( strcmp( buffer, command_name ) == 0 )
            {
                type = command_type;
                break;
            }
        }
    }

    if( strlen( buffer ) == 0 )
    {
        type = COMMAND_NONE;
    }

    return type;
}

void run_editor( const char *filepath )
{
#ifdef WIN32

    char command[MAX_PATH_LENGTH];

    STARTUPINFO si;
    PROCESS_INFORMATION pi;

    if( filepath == NULL )  
    {
        return;
    }

    ZeroMemory( &si, sizeof(si) );
    si.cb = sizeof(si);
    ZeroMemory( &pi, sizeof(pi) );

    osi_strlcpy( command, editor_path, sizeof( command ) );
    osi_strlcat( command, " ", sizeof( command ) );
    osi_strlcat( command, filepath, sizeof( command ) ); 

    /* start the editor process. */ 

    if( !CreateProcess( NULL, // No module name (use command line). 
        command,          // Command line. 
        NULL,             // Process handle not inheritable. 
        NULL,             // Thread handle not inheritable. 
        FALSE,            // Set handle inheritance to FALSE. 
        0,                // No creation flags. 
        NULL,             // Use parent's environment block. 
        NULL,             // Use parent's starting directory. 
        &si,              // Pointer to STARTUPINFO structure.
        &pi )             // Pointer to PROCESS_INFORMATION structure.
    ) 
    {
        osi_print_stderr( "unable to create editor process!" );
        return;
    }

    /* wait until editor exits. */

    WaitForSingleObject( pi.hProcess, INFINITE );

    /* close the process thread handles. */

    CloseHandle( pi.hProcess );
    CloseHandle( pi.hThread );

#else

    pid_t pid;

    int status;
    int finished;

    if( filepath == NULL )
    {
        return;
    }

    switch( pid=fork() )
    {
        case -1:
            osi_print_stderr( "unable to fork editor process!" );
            return;

        case 0 :
            execl( editor_path, editor_name, filepath, 0 ); 

        default:
            finished = wait( &status ); // parent
    }
#endif
}

void print_current_host()
{
    if( strlen( host ) > 0 )
    {
        osi_print_stdout( "current host: (%s)", host );
    }

    else
    {
        osi_print_stdout( "no host has been specified." );
    }
}

void print_current_mhost()
{
    if( strlen( mhost ) > 0 )
    {
        osi_print_stdout( "current management host: (%s)", mhost );
    }

    else
    {
        osi_print_stdout( "not connected to any management host." );
    }
}

void print_mhost_response( OSI_HELLO_RESPONSE *response )
{
    if( response == NULL )
    {
        return;
    }

    osi_print_stdout( "connected to management daemon, code version (%s).",
                       response->version );

    if( strlen( response->message ) > 0 )
    {
        osi_print_stdout( "%s", response->message );
    }

    osi_print_stdout( "" );
}

void print_prompt()
{
    fprintf( stdout, "\rosiris-%s", PROGRAM_VERSION );
    
    if( strlen( host ) > 0 )
    {
        fprintf( stdout, "[%s]", host );
    }
    
    fprintf( stdout, ": " );
}

void print_version()
{
    osi_print_stdout( "%s - version %s", PROGRAM_NAME, PROGRAM_VERSION );
}

void print_ssl()
{
    SSL *temp_ssl      = NULL;
    SSL_CIPHER *cipher = NULL;

    X509 *peer         = NULL;
    char data[512]     = "";

    int connection_socket = 0;
    temp_ssl = osi_ssl_connect_to_host_on_port( mhost,
                                                MANAGEMENT_CONTROL_PORT,
                                                ssl_context, TRUE );

    if( temp_ssl != NULL )
    {
        osi_print_stdout( "" );
        osi_print_stdout( "connected to (%s)\n", mhost );

        connection_socket = SSL_get_fd( temp_ssl );
        cipher = SSL_get_current_cipher( temp_ssl );

        if( cipher != NULL )
        {
            osi_print_stdout( "   protocols allowed: %s",
                              SSL_CIPHER_get_version( cipher ) );

            osi_print_stdout( "         cipher list: %s",
                              SSL_CIPHER_get_name( cipher ) );
        }

        else
        {
            osi_print_stderr( "unable to retrieve cipher list." );
        }

        peer = SSL_get_peer_certificate( temp_ssl );

        if( peer != NULL )
        {
            EVP_PKEY *pubkey;

            osi_print_stdout( "\n  [ server certificate ]\n");
            X509_NAME_oneline( X509_get_subject_name(peer),
                               data, sizeof(data) );

            osi_print_stdout( "      subject = %s", data );
            X509_NAME_oneline( X509_get_issuer_name(peer),
                               data, sizeof(data) );
            osi_print_stdout( "      issuer  = %s\n", data);
            
            pubkey = X509_get_pubkey( peer );

            if( pubkey != NULL )
            {
                osi_print_stdout( "      public key size: %d bit",
                                  EVP_PKEY_bits( pubkey ));

                EVP_PKEY_free( pubkey );
            }
                    
            if( osi_get_x509_fingerprint( peer, data, sizeof( data ) ) != NULL )
            {
                osi_print_stdout( "      MD5 fingerprint: %s", data );
            }

            X509_free( peer );
        }

        else
        {
            osi_print_stderr( "unable to retrieve peer certificate." );
        }

        SSL_shutdown( temp_ssl );

        osi_close_socket( connection_socket );
        osi_ssl_destroy( &temp_ssl );

        osi_print_stdout( "\n" );
    }

    else
    {
        osi_print_stderr( "error: not connected to management host (%s)",
                          mhost );
    }
}

void print_usage()
{
    osi_print_stdout( "" );
    print_version();
    osi_print_stdout( "" );

    osi_print_stdout( "    usage: osiris <management host> [options]\n" );

    osi_print_stdout( "      -f <file>  specify a root cert file to \
authenticate management host." );

    osi_print_stdout( "      -u <user>  login to the management host with the specified user." );
    osi_print_stdout( "      -h         print this usage statement.\n" );
}

void print_commands()
{
    osi_print_stdout( "" );

    osi_print_stdout( "[ Managment Commands ]" );
    osi_print_stdout( "    mhost          list-hosts          new-host         edit-host" );
    osi_print_stdout( "    host           print-mhost-config  new-user         edit-user" );
    osi_print_stdout( "    list-users     delete-user         edit-mhost       edit-filters" );
    osi_print_stdout( "    print-filters  test-notify" );

    osi_print_stdout( "" );

    osi_print_stdout( "[ Host commands ]" );
    osi_print_stdout( "    status         host-details       enable-host     disable-host" );
    osi_print_stdout( "    watch-host     list-configs       list-db         list-logs" );
    osi_print_stdout( "    import-config  push-config        drop-config     base-db" );
    osi_print_stdout( "    set-base-db    start-scan         stop-scan       print-log" );
    osi_print_stdout( "    print-config   print-host-config  print-db        print-db-errors" );

    osi_print_stdout( "" );

    osi_print_stdout( "[ Misc commands ]" );
    osi_print_stdout( "    help           version             quit              ssl" );

    osi_print_stdout( "" );
    osi_print_stdout( "  For help with a specific command, try: help <command>" );
    osi_print_stdout( "" );

}

void process_quit()
{
    disconnect_from_management_host();
    halt(0);
}

void process_host()
{
    OSI_STATUS *status;

    /* no host specified, print current. */

    if( strlen( name ) == 0 )
    {
        print_current_host();
        return;
    }

    /* we were given a new host to switch to. */

    osi_strlcpy( ctl_context.host, name, sizeof( ctl_context.host ) );
    status = ctl_get_status( &ctl_context );

    /* switch was good. */

    if( status != NULL )
    {
        osi_print_stdout( "%s is alive.", name );
        osi_free( status );

        osi_strlcpy( host, name, sizeof( host ) );
    }

    /* switch failed. */

    else
    {
        print_context_error( &ctl_context );

        /* different hosts, restore previous host. */

        if( strcmp( host, name ) != 0 )
        {
            osi_strlcpy( ctl_context.host, host, sizeof( ctl_context.host ) );
        }

        /* same host, not responding, clear hosts. */

        else
        {
            memset( host, 0, sizeof( host ) );
            memset( ctl_context.host, 0, sizeof( ctl_context.host ) );
        }
    }
}

void process_mhost()
{
    /* no new mhost specified, print our current one. */

    if( strlen( name ) == 0 )
    {
        print_current_mhost();
        return;
    }

    /* we were given a new mhost to try to connect to. */

    disconnect_from_management_host();

    /* zero out our current user. */

    memset( ctl_context.username, 0, sizeof( ctl_context.username ) );
    memset( current_user, 0, sizeof( current_user ) );

    /* copy in our new mhost value. */

    osi_strlcpy( mhost, name, sizeof( mhost ) );

    if( connect_to_management_host() == FALSE )
    {
        osi_print_stderr( "unable to contact management host: %s.", name );
        halt(-1);
    }
}

void process_hello_mhost()
{
    OSI_HELLO_RESPONSE *response = ctl_send_hello( &ctl_context );

    if( response != NULL )
    {
        print_mhost_response( response );
        osi_free( response );
    }

    else
    {
        print_context_error( &ctl_context );
    }
}


void process_status_request()
{
    OSI_STATUS *status = ctl_get_status( &ctl_context );

    if( status != NULL )
    {
        print_status( status );
        osi_free( status );
    }

    else
    {
        print_context_error( &ctl_context );
    }
}

void process_new_user()
{
    osi_bool result;
    prompt_for_user_information();

    /* now call our password handling routine. */

    result = ctl_set_user( &ctl_context );

    if( result )
    {
        osi_print_stdout( "user: (%s) added.", ctl_context.username );
    }

    else
    {
        print_context_error( &ctl_context );
    }
}

void process_edit_user()
{
    osi_bool result;
    prompt_for_user_information();

    /* now call our password handling routine. */

    result = ctl_set_user( &ctl_context );

    if( result )
    {
        osi_print_stdout( "user: (%s) updated.", ctl_context.username );

        /* if we edited our current login, we reset our login  */
        /* cache and prompt for new auth information.          */

        if( strcmp( ctl_context.buffer, current_user ) == 0 )
        {
            osi_print_stdout(
                    "current login was edited, you must re-authenticate." );

            if( reconnect_to_management_host() == FALSE )
            {
                halt(-1);
            }
        }
    }

    else
    {
        print_context_error( &ctl_context );
    }
}

void process_delete_user()
{
    osi_bool result;
    char buffer[MAX_COMMAND_LENGTH];

    if( strlen( ctl_context.buffer ) == 0 )
    {
        process_list_users();
        fprintf( stdout, "User to delete: " );

        if( get_user_input( buffer, sizeof( buffer ) ) != 0 )
        {
            osi_strlcpy( ctl_context.buffer, buffer,
                         sizeof( ctl_context.buffer ) );
        }
    }

    /* user was specified, we copy it into the context buffer. */

    else
    {
        osi_strlcpy( ctl_context.buffer, name, sizeof( ctl_context.buffer ) );
    }

    /* now call our password handling routine. */

    result = ctl_delete_user( &ctl_context );

    if( result )
    {
        osi_print_stdout( "user: (%s) deleted.", ctl_context.buffer );

        /* if we deleted our current login, we reset our login */
        /* cache and prompt for new auth information.          */

        if( strcmp( ctl_context.buffer, current_user ) == 0 )
        {
            osi_print_stdout(
                    "current login was deleted, you must re-authenticate." );

            if( reconnect_to_management_host() == FALSE )
            {
                halt(-1);
            }
        }
    }

    else
    {
        print_context_error( &ctl_context );
    }
}

void process_list_users()
{
    osi_list users = ctl_get_user_list( &ctl_context );

    if( users != NULL )
    {
        int user_count;
        OSI_AUTH_CONTEXT *auth;

        /* print hosts here */

        user_count = list_get_size( users );

        if( user_count > 0 )
        {
            osi_list head = list_get_first_node( users );
            osi_print_stdout( "" );

            fprintf( stdout, "  %-19.19s\n\n", "[ name ]" );

            while( head )
            {
                if( ( auth = (OSI_AUTH_CONTEXT *)head->data ) != NULL )
                {
                    osi_print_stdout( "  %s", auth->auth_user );
                }

                head = list_get_next_node( head );
            }

            osi_print_stdout( "\ntotal: %d users.\n", user_count );
        }

        else
        {
            osi_print_stdout( "no users." );
        }

        /* now free the list of hosts. */

        list_destroy_with_function( users, (void (*)(void *))&osi_free );
    }

    else
    {
        print_context_error( &ctl_context );
    }
}

void process_start_scan()
{
    osi_bool scan_started = ctl_start_scan( &ctl_context );

    if( scan_started )
    {
        osi_print_stdout( "scanning process was started on host: %s",
                          ctl_context.host );
    }

    else
    {
        print_context_error( &ctl_context );
    }
}


void process_stop_scan()
{
    osi_bool scan_stopped;

    /* now call our status handling routine. */

    scan_stopped = ctl_stop_scan( &ctl_context );

    if( scan_stopped )
    {
        osi_print_stdout( "scanning process was halted on host: %s",
                           ctl_context.host );
    }

    else
    {
        print_context_error( &ctl_context );
    }
}

void process_push_config()
{
    osi_bool push_status;
    prompt_for_config_information( &ctl_context );

    /* now call our status handling routine. */

    push_status = ctl_push_config( &ctl_context );

    if( push_status )
    {
        osi_print_stdout( "the config: %s was succesfully pushed to host: %s",
                          ctl_context.buffer, ctl_context.host );
    }

    else
    {
        print_context_error( &ctl_context );
    }
}

void process_set_base_db()
{
    osi_bool status;
    prompt_for_set_base_db_information();

    /* now call our status handling routine. */

    status = ctl_set_base_db( &ctl_context );

    if( status )
    {
        osi_print_stdout( "database: %s is now base db for host: %s",
                          ctl_context.buffer, ctl_context.host );
    }

    else
    {
        print_context_error( &ctl_context );
    }
}

void process_watch_host()
{
    OSI_STATUS *status;


    /* loop on status requests, sleep between queries. */

    continue_polling = TRUE;   /* signal handlers will reset this */

    do
    {
        status = ctl_get_status( &ctl_context );

        if( status != NULL )
        {
            char *host = ctl_context.host;

            if( continue_polling == FALSE )
            {
                break;
            }

            switch( status->daemon_state )
            {
                case DAEMON_STATE_IDLE:
                    fprintf( stdout, "\r[%s] is idle...            ", host );
                    break;
    
                case DAEMON_STATE_RECEIVING:
                    fprintf( stdout, "\r[%s] is receiving a config...", host );
                    break;
    
                case DAEMON_STATE_SCANNING:
                    fprintf( stdout, "\r[%s] is scanning...        ", host );
                    break;

                default:
                    fprintf( stdout, "\r[%s] in unknown state!", host );
                    break;
            }

            fprintf( stdout, "(ctrl-c to abort)" );
            fflush( stdout );

            osi_free( status );
        }

        else
        {
            print_context_error( &ctl_context );
            break;
        }

        osi_sleep( 2 );

    } while( continue_polling );

    /* clear line. */

    fprintf( stdout, "\r                                                   " );
    fflush( stdout );
}

void process_import_config()
{
    osi_bool config_imported;
    prompt_for_import_config_information( &ctl_context );

    if( ctl_context.config == NULL )
    {
        return;
    }

    config_imported = ctl_import_config( &ctl_context );

    if( config_imported )
    {
        osi_print_stdout(
                "the config: %s was succesfully imported for host: %s",
                 ctl_context.buffer, ctl_context.host );
    }

    else
    {
        print_context_error( &ctl_context );
    }

    osi_config_destroy( ctl_context.config );
}

void process_edit_config()
{
    configuration *cfg;
    osi_bool config_imported = FALSE;

    struct stat stats;
    unsigned long save_time = 0; 

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

    prompt_for_config_information();
    cfg = ctl_get_config( &ctl_context );

    if( cfg == NULL )
    {
        print_context_error( &ctl_context );
        return;
    }

    /* we have a config, save it to a temporary file */
    /* in our local .osiris directory and open it in */
    /* text editor.  If it is saved, we import it.   */

    /* construct path to temp config file. */

    if( osi_get_user_home_directory( path, sizeof( path ) ) )
    {
        osi_strlcat( path, path_separator, sizeof( path ) );
        osi_strlcat( path, PATH_USER_DIR, sizeof( path ) );
        osi_strlcat( path, path_separator, sizeof( path ) );
        osi_strlcat( path, FILE_TEMP_CONFIG, sizeof( path ) );
    }

    else
    {
        osi_print_stderr( "unable to determine home directory." );
        goto exit_gracefully;
    }

    /* now save the config file. */

    if( osi_config_write_to_file( cfg, path ) == FALSE )
    {
        osi_print_stderr( "unable to save config to temporary file!." );
        goto exit_gracefully;        
    }

    /* now stat the file and remember the mtime. */

    if( stat( path, &stats ) == 0 )
    {
        save_time = stats.st_mtime; 
    }

    /* now fork the editor and wait for it to finish. */

    run_editor( path );

    /* now check mtime, if mtime values are different then */
    /* the file has been saved and so we import it.        */

    if( ( stat( path, &stats ) != 0 ) || 
        ( (unsigned long)stats.st_mtime == save_time ) )
    {
        osi_print_stdout( "config file not changed." );
        goto exit_gracefully;
    }

    osi_print_stdout( "config file changed, updating..." );
    ctl_context.config = osi_config_new();

    /* read in temp file. */

    if( osi_config_read_from_file( ctl_context.config, path ) == FALSE )
    {
        osi_print_stdout( " * error: unable to read local file (%s).", path );

        osi_config_destroy( ctl_context.config );
        ctl_context.config = NULL;
            
        goto exit_gracefully;
    }

    /* push it to the management host. */

    config_imported = ctl_import_config( &ctl_context );

    if( config_imported )
    {
        osi_print_stdout( "the config: %s was succesfully updated.",
                          ctl_context.buffer );
    }

    else
    {
        print_context_error( &ctl_context );
    }

    osi_config_destroy( ctl_context.config );

exit_gracefully:

    osi_config_destroy( cfg );
}

void process_list_hosts()
{
    int host_count;
    OSI_HOST_BRIEF *host_brief;

    osi_list hosts = ctl_get_host_brief_list( &ctl_context );

    if( hosts == NULL )
    {
        print_context_error( &ctl_context );
        return;
    }

    /* print hosts here */

    host_count = list_get_size( hosts );

    if( host_count > 0 )
    {
        osi_list head = list_get_first_node( hosts );
        osi_print_stdout( "" );

        fprintf( stdout, "  %-19.19s", "[ name ]" );
        fprintf( stdout, "  %-30.30s", "[ description ]" );
        fprintf( stdout, "  %-15.15s\n\n", "[ enabled ]" );

        while( head )
        {
            if( ( host_brief = (OSI_HOST_BRIEF *)head->data ) != NULL )
            {
                print_host_brief( host_brief );
            }

            head = list_get_next_node( head );
        }

        osi_print_stdout( "\ntotal: %d\n", host_count );
    }

    else
    {
        osi_print_stdout( "no hosts." );
    }

    /* now free the list of hosts. */

    list_destroy_with_function( hosts, (void (*)(void *))&osi_free );
}

void process_list_configs()
{
    osi_list configs = ctl_get_config_brief_list( &ctl_context );

    if( configs != NULL )
    {
        int config_count;
        OSI_CONFIG_BRIEF *config_brief;

        /* print hosts here */

        config_count = list_get_size( configs );

        if( config_count > 0 )
        {
            osi_list head = list_get_first_node( configs );
            osi_print_stdout( "" );
            
            fprintf( stdout, "  %-20.20s", "[ name ]" );
            fprintf( stdout, "  %-33.33s\n\n", "[ id ]" );

            while( head )
            {
                if( ( config_brief = (OSI_CONFIG_BRIEF *)head->data ) != NULL )
                {
                    print_config_brief( config_brief );
                }

                head = list_get_next_node( head );
            }

            osi_print_stdout( "\ntotal: %d\n", config_count );
        }

        else
        {
            osi_print_stdout( "no configs exist for this host." );
        }

        /* now free the list of hosts. */

        list_destroy_with_function( configs, (void (*)(void *))&osi_free );
    }

    else
    {
        print_context_error( &ctl_context );
    }
}

void process_list_databases()
{
    osi_list databases;
    osi_list head;

    int database_count;
    OSI_DATABASE_BRIEF *database_brief;

    osi_print_stdout( "This may take a while..." );
    databases = ctl_get_database_brief_list( &ctl_context );

    if( databases == NULL )
    {
        print_context_error( &ctl_context );
        return; 
    }

    /* get default database name so we can highlight it. */

    get_base_db_name_for_host( name, sizeof( name ), ctl_context.host );

    /* print databases here */

    database_count = list_get_size( databases );

    if( database_count == 0 )
    {
        osi_print_stdout( "no databases exist for this host." );
        return;
    }

    head = list_get_first_node( databases );
    osi_print_stdout( "" );

    fprintf( stdout, "  %-30.30s", "[ name ]" );
    fprintf( stdout, "  %-30.30s\n\n", "[ created ]" );

    while( head )
    {
        database_brief = (OSI_DATABASE_BRIEF *)head->data;

        if( database_brief != NULL )
        {
            print_database_brief( database_brief );
        }

        head = list_get_next_node( head );
    }

    osi_print_stdout( "\ntotal: %d", database_count );
    osi_print_stdout( "(*) denotes the base database for this host.\n" );

    /* now free the list of hosts. */

    list_destroy_with_function( databases, (void (*)(void *))&osi_free );
}

void process_list_logs()
{
    int log_count = 0;

    osi_list logs;
    osi_list head;

    OSI_LOG_BRIEF *log_brief;

    osi_print_stdout( "This may take a while..." );
    logs = ctl_get_log_brief_list( &ctl_context );

    if( logs == NULL )
    {
        print_context_error( &ctl_context );
        return;
    }

    head = list_get_first_node( logs );
    osi_print_stdout( "" );

    fprintf( stdout, "  %-30.30s", "[ name ]" );
    fprintf( stdout, "  %-30.30s\n\n", "[ date ]" );

    while( head )
    {
        log_brief = (OSI_LOG_BRIEF *)head->data;

        if( log_brief != NULL )
        {
            print_log_brief( log_brief );
            log_count++;
        }

        head = list_get_next_node( head );
    }

    osi_print_stdout( "\ntotal: %d\n", log_count );
    list_destroy_with_function( logs, (void (*)(void *))&osi_free );
}

void process_host_details()
{
    OSI_HOST_BRIEF *host_brief = ctl_get_host( &ctl_context );

    if( host_brief != NULL )
    {
        print_host_brief_expanded( host_brief );
        osi_free( host_brief );
    }

    else
    {
        print_context_error( &ctl_context );
    }
}

void process_show_base_db()
{
    char base_db_name[MAX_HOSTNAME_LENGTH];

    if( get_base_db_name_for_host( base_db_name, sizeof( base_db_name ),
                                   ctl_context.host ) )
    {
        osi_print_stdout( "Base DB: %s", base_db_name );
    }
}

void process_print_db()
{
    OSI_DATABASE_BRIEF *database_brief;
    database_brief = ctl_get_db_results( &ctl_context );

    if( database_brief != NULL )
    {
        print_database_brief_expanded( database_brief );
        osi_free( database_brief );
    }

    else
    {
        print_context_error( &ctl_context );
    }
}

void process_send_notify_test()
{
    osi_bool sent;

    fprintf( stdout,  "connecting..." );
    fflush( stdout );
    
    sent = ctl_send_notify_test( &ctl_context );

    if( sent == TRUE )
    {
        osi_print_stdout( "notification test message sent." );
    }

    else
    {
        print_context_error( &ctl_context );
    }
}

void process_print_filters()
{
    osi_list filters = ctl_get_notify_filters_list( &ctl_context );
    print_notify_filters_list( filters );

    if( filters != NULL )
    {
        list_destroy_with_function( filters, (void (*)(void *))&osi_free );
    }
}

void process_edit_filters()
{
    osi_list filters = NULL;
    char buffer[MAX_COMMAND_LENGTH];

    osi_bool editing_filters = TRUE;
    osi_bool filters_saved   = FALSE;
    osi_bool need_to_save    = FALSE;

    filters = ctl_get_notify_filters_list( &ctl_context );

    /* no filters yet, create list. */

    if( filters == NULL )
    {
        filters = list_new();
    }

    while( editing_filters )
    {
        /* print the menu. */

        osi_print_stdout( "" );
        osi_print_stdout( "    s) show current filters." );
        osi_print_stdout( "    a) add a new filter." );
        osi_print_stdout( "    r) remove filter." );
        osi_print_stdout( "    e) exit\n" );

        fprintf( stdout,  "    > " );

        if( get_user_input( buffer, sizeof( buffer ) ) != 0 )
        {
            switch( (int)buffer[0] )
            {
                case 'a':
                case 'A':

                    ctl_context.notify_filters = filters;
                    prompt_for_notify_filter_information( &ctl_context );

                    need_to_save = TRUE;

                    break;

                case 'r':
                case 'R':

                    print_numbered_filter_list( filters );

                    if( list_get_size( filters ) > 0 )
                    {
                        fprintf( stdout, "\n\nfilter to remove: " );

                        if( get_user_input( buffer, sizeof( buffer ) ) != 0 )
                        {
                            int entry = atoi( buffer );
                            list_remove_index( filters, entry );

                            need_to_save = TRUE;
                        }
                    }

                    break;

                case 's':
                case 'S':

                    print_notify_filters_list( filters );
                    break;

                case 'q':
                case 'Q':
                case 'e':
                case 'E':

                    editing_filters = FALSE;
                    break;

                default:
                    break;
            }
        }

    }  /* end while editing authorized list. */

    if( need_to_save )
    {
        ctl_context.notify_filters = filters;
        filters_saved = ctl_save_notify_filters( &ctl_context );

        if( filters_saved )
        {
            osi_print_stdout( "notify filter list sucessfully saved." );
        }

        else
        {
            print_context_error( &ctl_context );
        }
    }

    else
    {
        osi_print_stdout( "no changes made to notification filter list." );
    }

    list_destroy_with_function( filters, (void (*)(void *))&osi_free );
    ctl_context.notify_filters = NULL;
}

void process_print_log()
{
    string_list *log_data;
    log_data = ctl_get_log( &ctl_context );

    if( ( log_data != NULL ) && ( log_data->size > 0 ) )
    {
        print_log_file( log_data );
        string_list_destroy( log_data );
    }

    else
    {
        print_context_error( &ctl_context );
    }
}

void process_print_db_errors()
{
    osi_list errors = ctl_get_database_errors( &ctl_context );

    if( errors != NULL )
    {
        int error_count;
        OSI_ERROR *error;

        /* print errors here */

        error_count = list_get_size( errors );

        if( error_count > 0 )
        {
            osi_list head = list_get_first_node( errors );
            osi_print_stdout( "" );

            while( head )
            {
                if( ( error = (OSI_ERROR *)head->data ) != NULL )
                {
                    print_osi_error( error );
                }

                head = list_get_next_node( head );
            }

            osi_print_stdout( "\ntotal: %d\n", error_count );
        }

        else
        {
            osi_print_stdout( "this database contains no errors." );
        }

        /* now free the list of hosts. */

        list_destroy_with_function( errors, (void (*)(void *))&osi_free );
    }

    else
    {
        print_context_error( &ctl_context );
    }
}


void process_print_config()
{
    configuration *cfg;

    prompt_for_config_information();
    cfg = ctl_get_config( &ctl_context );

    if( cfg != NULL )
    {
        print_config( cfg );
        osi_config_destroy( cfg );
    }

    else
    {
        print_context_error( &ctl_context );
    }
}

void process_print_host_config()
{
    OSI_HOST_CONFIG *cfg = ctl_get_host_config( &ctl_context );

    if( cfg != NULL )
    {
        print_host_config( cfg );
        osi_host_config_destroy( cfg );
    }

    else
    {
        print_context_error( &ctl_context );
    }
}

void process_print_mhost_config()
{
    OSI_MANAGEMENT_CONFIG *cfg = ctl_get_management_config( &ctl_context );

    if( cfg != NULL )
    {
        print_mhost_config( cfg );
        osi_management_config_destroy( cfg );
    }

    else
    {
        print_context_error( &ctl_context );
    }
}

void process_edit_mhost_config()
{
    osi_bool config_saved;
    OSI_MANAGEMENT_CONFIG *cfg = ctl_get_management_config( &ctl_context );

    if( cfg == NULL )
    {
        print_context_error( &ctl_context );
        return;
    }

    /* put the mhost config into the context and ask for changes. */

    ctl_context.mhost_config = cfg;
    prompt_for_edit_mhost_config_information( &ctl_context );

    /* send config back to the management host. */

    config_saved = ctl_save_management_config( &ctl_context );

    if( config_saved )
    {
        osi_print_stdout( "management host config sucessfully saved." );
    }

    else
    {
        print_context_error( &ctl_context );
    }

    osi_management_config_destroy( cfg );
}

void process_new_host()
{
    osi_bool host_created;
    char buffer[MAX_COMMAND_LENGTH];

    OSI_HOST_BRIEF host_brief;
    ctl_context.host_brief = &host_brief;

    prompt_for_new_host_information( &ctl_context );
    host_created = ctl_new_host( &ctl_context );

    if( host_created )
    {
        osi_print_stdout( "the new host (%s) was sucessfully created.",
                          host_brief.name );

        fprintf( stdout, "initialize this host? (yes/no): " );

        if( get_user_input( buffer, sizeof( buffer ) ) != 0 )
        {
            lowercase_string( buffer );

            if( strcmp( buffer, "yes" ) == 0 || strcmp( buffer, "y" ) == 0 )
            {
                osi_strlcpy( ctl_context.host, host_brief.name,
                             sizeof( ctl_context.host ) );

                process_initialize_host();
            }
        }
    }

    else
    {
        print_context_error( &ctl_context );
    }
}

void process_edit_host()
{
    osi_bool host_saved;
    OSI_HOST_BRIEF *host_brief;

    host_brief = ctl_get_host( &ctl_context );

    if( host_brief == NULL )
    {
        print_context_error( &ctl_context );
        return;
    }

    /* now get information from user, and send back to management daemon. */

    ctl_context.host_brief = host_brief;
    prompt_for_edit_host_information( &ctl_context );

    /* now call our status handling routine. */

    host_saved = ctl_edit_host_brief( &ctl_context );

    if( host_saved )
    {
        osi_print_stdout( "the host (%s) was sucessfully saved.",
                          host_brief->name );
    }

    else
    {
        print_context_error( &ctl_context );
    }

    osi_free( host_brief );
}

void process_initialize_host()
{
    osi_bool use_default_config;
    char buffer[MAX_COMMAND_LENGTH];
    OSI_STATUS *status;

    /* first, we find out if this host is even up yet. */

    status = ctl_get_status( &ctl_context );

    if( status == NULL )
    {
        osi_print_stderr( "unable to contact host: %s initialization halted.",
                           ctl_context.host );

        return;
    }

    /* make sure user knows what's going on here. */

    fprintf( stdout, "\n" );
    fprintf( stdout, "Initializing a host will push over a config, start\n" );
    fprintf( stdout, "a scan, and set the created database to be the\n" );
    fprintf( stdout, "trusted database.\n\n" );
    fprintf( stdout,
            "Are you sure you want to initialize this host (yes/no): " );

    if( get_user_input( buffer, sizeof( buffer ) ) != 0 )
    {
        lowercase_string( buffer );

        if( strcmp( buffer, "yes" ) != 0 && strcmp( buffer, "y" ) != 0 )
        {
            osi_print_stdout( "initialization cancelled by user." );
            goto exit_gracefully;
        }
    }

    /* if we know the OS, we print the name and version and ask     */
    /* the user if they want to use a default config for this host. */

    if( strlen( status->os_name ) == 0 )
    {
        osi_print_stdout(
        "this host is of an unknown platform type, no default config exists." );
    }

    else
    {
        osi_print_stdout( "" );
        osi_print_stdout( "OS Name: %s", status->os_name );
       
        fprintf( stdout, "OS Version: " );

        ( strlen( status->os_version ) > 0 ) ? 
            osi_print_stdout( "%s", status->os_version ) :
            osi_print_stdout( "(unknown)" );

        fprintf( stdout, "\n" );
        fprintf( stdout, "use the default config for this OS? (yes/no): " );

        if( get_user_input( buffer, sizeof( buffer ) ) != 0 )
        {
            lowercase_string( buffer );

            if( strcmp( buffer, "yes" ) == 0 || strcmp( buffer, "y" ) == 0 )
            {
                use_default_config = TRUE;
            }

            else
            {
                use_default_config = FALSE;
            }
        }
    }

    /* now, we have a config stored under this host, and we push it */
    /* to the host so that the remote daemon has it.                */

    if( use_default_config )
    {
        osi_strlcpy( ctl_context.buffer, status->os_name,
                     sizeof( ctl_context.buffer ) );

        if( ctl_import_default_config( &ctl_context ) == FALSE )
        {
            osi_print_stderr( "unable to import default config!" );
            goto exit_gracefully;
        }

        osi_print_stdout( "default config for (%s) imported.",
                          status->os_name );

        osi_strlcpy( ctl_context.buffer, DEFAULT_CONFIG_NAME,
                     sizeof( ctl_context.buffer ) );

        ctl_push_config( &ctl_context );
    }
    
    else
    {
        process_import_config();
        process_push_config();
    }

    /* now, we ask to start a scan and essentially create a new base */
    /* db by which to base subsequent scans on.                      */

    fprintf( stdout,
             "perform an initial scan and database for this host? (yes/no): " );

    if( get_user_input( buffer, sizeof( buffer ) ) != 0 )
    {
        lowercase_string( buffer );

        if( strcmp( buffer, "yes" ) == 0 || strcmp( buffer, "y" ) == 0 )
        {
            /* unset the trusted database so it will be flagged as the */
            /* one we create when we kick off the scan.                */

            ctl_unset_base_db( &ctl_context );
            process_start_scan();
        }
    }

exit_gracefully:
    
    osi_free( status );
}

void process_enable_host()
{
    osi_bool host_saved;
    OSI_HOST_BRIEF *host_brief;

    /* first, get the host to edit, we perform this as an edit        */
    /* so that the scheduler of the management host will get updated. */

    host_brief = ctl_get_host( &ctl_context );

    if( host_brief == NULL )
    {
        print_context_error( &ctl_context );
        return;
    }

    /* enable the host. */
    
    host_brief->enabled = 1;
    ctl_context.host_brief = host_brief;

    /* now call our status handling routine. */

    host_saved = ctl_edit_host_brief( &ctl_context );

    if( host_saved )
    {
        osi_print_stdout( "%s is now enabled.", host_brief->name );
    }

    else
    {
        print_context_error( &ctl_context );
    }

    osi_free( host_brief );
}

void process_disable_host()
{
    osi_bool host_saved;
    OSI_HOST_BRIEF *host_brief;

    /* first, get the host to edit, we perform this as an edit        */
    /* so that the scheduler of the management host will get updated. */

    host_brief = ctl_get_host( &ctl_context );

    if( host_brief == NULL )
    {
        print_context_error( &ctl_context );
        return;
    }

    /* enable the host. */

    host_brief->enabled = 0;
    ctl_context.host_brief = host_brief;

    /* now call our status handling routine. */

    host_saved = ctl_edit_host_brief( &ctl_context );

    if( host_saved )
    {
        osi_print_stdout( "%s is now disabled.", host_brief->name );
    }

    else
    {
        print_context_error( &ctl_context );
    }

    osi_free( host_brief );
}

void process_drop_config()
{
    osi_bool config_dropped;

    /* now call our status handling routine. */

    config_dropped = ctl_drop_config( &ctl_context );

    if( config_dropped )
    {
        osi_print_stdout( "the host (%s) has dropped it's current config.",
                          ctl_context.host );
    }

    else
    {
        print_context_error( &ctl_context );
    }
}

void process_help()
{
    if( ( help_command == COMMAND_NONE ) ||
        ( help_command == COMMAND_UNKNOWN ) )
    {
        print_commands();
    }

    else
    {
        print_command_usage( help_command );
    }
}

void prompt_for_new_host_information( CTL_CONTEXT *context )
{
    char buffer[MAX_COMMAND_LENGTH];
    OSI_HOST_BRIEF *host;

    osi_bool approved = FALSE;
    osi_bool proceed = FALSE;

    if( ( context == NULL ) || ( context->host_brief == NULL ) )
    {
        return;
    }

    host = context->host_brief;
    memset( host, 0, sizeof( OSI_HOST_BRIEF ) );
    host->type = OSI_HOST_TYPE_GENERIC;

    do
    {
        osi_bool use_input = TRUE;

        /* acquire basic host information from the user, we get */
        /* the name, ip, description, and enabled status.       */

        osi_print_stdout( "\n[ new host ]\n" );

        do
        {
            fprintf( stdout, "  > name this host [%s]:  ", host->name );

            if( get_user_input( buffer, sizeof( buffer ) ) == 0 )
            {
                osi_print_stdout( " * error: name is required." );
                continue;
            }

            if( strstr( buffer, " " ) > 0 )
            {
                osi_print_stdout( " * error: name cannot contain a space." );
                continue;
            }

            /* we already got one! */

            if( host->name[0] != '\0' )
            {
                use_input = FALSE;
            }

            proceed = TRUE;

        } while( proceed == FALSE );

        if( use_input )
        {
            osi_strlcpy( host->name, buffer, sizeof( host->name ) );
        }

        /* get ip address. */

        fprintf( stdout, "  > hostname/IP address [%s]: ", host->host );

        if( get_user_input( buffer, sizeof( buffer ) ) != 0 )
        {
            osi_strlcpy( host->host, buffer, sizeof( host->host ) );
        }

        /* get description. */

        fprintf( stdout, "  > description [%s]: ", host->description );

        if( get_user_input( buffer, sizeof( buffer ) ) != 0 )
        {
            osi_strlcpy( host->description,
                         buffer, sizeof( host->description ) );
        }

        /* get host type. */

        fprintf( stdout, "  > host type (generic/router) [%s]: ",
                 get_name_from_host_type( (int)host->type ) );

        if( get_user_input( buffer, sizeof( buffer ) ) != 0 )
        {
            lowercase_string( buffer );

            if( strcmp( buffer, OSI_HOST_TYPE_NAME_ROUTER ) == 0 )
            {
                host->type = OSI_HOST_TYPE_ROUTER;
            }

            else
            {
                host->type = OSI_HOST_TYPE_GENERIC;               
            }
        }

        /* ask to enable the file logging for scans. */

        fprintf( stdout, "  > enable scan logging for this host? (yes/no) " );
        
        if( host->file_log_enabled )
        {
            fprintf( stdout, "[yes]: " );
        }

        else
        {
            fprintf( stdout, "[no]: " );
        }

        if( get_user_input( buffer, sizeof( buffer ) ) != 0 )
        {
            lowercase_string( buffer );

            if( strcmp( buffer, "yes" ) == 0 ||
                strcmp( buffer, "y" ) == 0 )
            {
                host->file_log_enabled = 1;
            }

            else
            {
                host->file_log_enabled = 0;
            }
        }

        /* ask to enable archiving of database files. */

        fprintf( stdout,
                 "  > archive scan databases for this host?  (yes/no) " );

        if( host->archive_databases )
        {
            fprintf( stdout, "[yes]: " );
        }

        else
        {
            fprintf( stdout, "[no]: " );
        }

        if( get_user_input( buffer, sizeof( buffer ) ) != 0 )
        {
            lowercase_string( buffer );

            if( strcmp( buffer, "yes" ) == 0 ||
                strcmp( buffer, "y" ) == 0 )
            {
                host->archive_databases = 1;
            }

            else
            {
                host->archive_databases = 0;
            }
        }

        /* ask to enable the admin notification for this host. */

        fprintf( stdout,
            "  > enable admin email notification for this host? (yes/no) " );

        if( host->notify_enabled )
        {
            fprintf( stdout, "[yes]: " );
        }

        else
        {
            fprintf( stdout, "[no]: " );
        }

        if( get_user_input( buffer, sizeof( buffer ) ) != 0 )
        {
            lowercase_string( buffer );

            if( strcmp( buffer, "yes" ) == 0 ||
                strcmp( buffer, "y" ) == 0 )
            {
                host->notify_enabled = 1;
            }

            else
            {
                host->notify_enabled = 0;
            }
        }

        fprintf( stdout,
       "  > send scan notification, even when no changes detected  (yes/no) " );

        if( host->notify_always )
        {
            fprintf( stdout, "[yes]: " );
        }

        else
        {
            fprintf( stdout, "[no]: " );
        }

        if( get_user_input( buffer, sizeof( buffer ) ) != 0 )
        {
            lowercase_string( buffer );

            if( strcmp( buffer, "yes" ) == 0 ||
                strcmp( buffer, "y" ) == 0 )
            {
                host->notify_always = 1;
            }

            else
            {
                host->notify_always = 0;
            }
        }

        /* ask for scheduling information. */

        fprintf( stdout,
                "  > configure scan scheduling information? (yes/no) [no]: " );

        if( get_user_input( buffer, sizeof( buffer ) ) != 0 )
        {
            lowercase_string( buffer );

            if( strcmp( buffer, "yes" ) == 0 ||
                strcmp( buffer, "y" ) == 0 )
            {
                prompt_for_scheduling_information( context );
            }
        }

        /* ask to enable. */

        fprintf( stdout, "  > activate this host? (yes/no) " );

    
        if( host->enabled )
        {
            fprintf( stdout, "[yes]: " );
        }

        else
        {
            fprintf( stdout, "[no]: " );
        }

        if( get_user_input( buffer, sizeof( buffer ) ) != 0 )
        {
            lowercase_string( buffer );

            if( strcmp( buffer, "yes" ) == 0 ||
                strcmp( buffer, "y" ) == 0 )
            {
                host->enabled = 1;
            }

            else
            {
                host->enabled = 0;
            }
        }

        print_host_brief_overview( host );
        fprintf( stdout, "Is this correct (y/n)? " );

        if( get_user_input( buffer, sizeof( buffer ) ) != 0 )
        {
            if( buffer[0] == 'y' || buffer[0] == 'Y' )
            {
                approved = TRUE; 
            }
        }

    } while ( approved == FALSE );
}

void prompt_for_edit_mhost_config_information( CTL_CONTEXT *context )
{
    char buffer[MAX_COMMAND_LENGTH];
    OSI_MANAGEMENT_CONFIG *cfg;

    osi_bool approved = FALSE;
    osi_bool allow_approved = TRUE;

    if( ( context == NULL ) || ( context->mhost_config == NULL ) )
    {
        return;
    }

    cfg = context->mhost_config;

    fprintf( stdout, "\n[ edit management host (%s) ]\n\n", mhost );

    do
    {
        const char *log_intensity;

        log_intensity = get_log_intensity_name( cfg->log_intensity );

        fprintf( stdout, "  > syslog facility [%s]: ", 
                 ( cfg->syslog_facility ? cfg->syslog_facility : "" ) );

        if( get_user_input( buffer, sizeof( buffer ) ) != 0 )
        {
            osi_strlcpy( cfg->syslog_facility, buffer,
                         sizeof( cfg->syslog_facility ) );
        }

        fprintf( stdout, "  > syslog level [%s]: ", 
                 ( cfg->syslog_level ? cfg->syslog_level : "" ) );

        if( get_user_input( buffer, sizeof( buffer ) ) != 0 )
        {
            osi_strlcpy( cfg->syslog_level, buffer,
                         sizeof( cfg->syslog_level ) );
        }

        fprintf( stdout, "  > log intensity [%s]: ",
                 ( log_intensity ? log_intensity : "" ) );

        if( get_user_input( buffer, sizeof( buffer ) ) != 0 )
        {
            int intensity = get_log_intensity_number( buffer );

            if( intensity != -1 )
            {
                cfg->log_intensity = intensity;
            }
        } 
        
        fprintf( stdout, "  > control port [%d]: ", cfg->control_port );

        if( get_user_input( buffer, sizeof( buffer ) ) != 0 )
        {
            cfg->control_port = atoi( buffer );
        }

        if ( cfg->http_port )
        {
            fprintf( stdout,
                     "  > http host name (uses system name by default) [%s]: ",
                     cfg->http_host );
 
            if( get_user_input( buffer, sizeof( buffer ) ) != 0 )
            {
                osi_strlcpy(cfg->http_host, buffer, sizeof(cfg-> http_host));
            }
        }
 
        fprintf( stdout, "  > http control port [%d]: ", cfg->http_port );

        if( get_user_input( buffer, sizeof( buffer ) ) != 0 )
        {
            cfg->http_port = atoi( buffer );
        }

        fprintf( stdout, "  > notification email [%s]: ", cfg->notify_email );

        if( get_user_input( buffer, sizeof( buffer ) ) != 0 )
        {
            osi_strlcpy( cfg->notify_email, buffer,
                         sizeof( cfg->notify_email ) );
        }

        fprintf( stdout, "  > notification smtp host [%s]: ",
                 cfg->notify_smtp_host );

        if( get_user_input( buffer, sizeof( buffer ) ) != 0 )
        {
            osi_strlcpy( cfg->notify_smtp_host, buffer,
                         sizeof( cfg->notify_smtp_host ) );
        }

        fprintf( stdout, "  > notification smtp port [%d]: ",
                 cfg->notify_smtp_port );

        if( get_user_input( buffer, sizeof( buffer ) ) != 0 )
        {
            cfg->notify_smtp_port = atoi( buffer );
        }

        /* now we show the allow list and allow the user to */
        /* add/delete entries in it.                        */

        print_mhost_allow_list( cfg );

        fprintf( stdout, "\n  Modify authorization list (y/n)? [n] " );

        if( get_user_input( buffer, sizeof( buffer ) ) != 0 )
        {
            if( buffer[0] == 'y' || buffer[0] == 'Y' )
            {
                allow_approved = FALSE;
            }
        }

        while( allow_approved == FALSE )
        {
            /* print the menu. */

            osi_print_stdout( "" );
            osi_print_stdout( "    s) show current listing." );
            osi_print_stdout( "    a) add a new authorized host." );
            osi_print_stdout( "    r) remove authorized host." );
            osi_print_stdout( "    e) exit\n" );

            fprintf( stdout,  "    > " );

            if( get_user_input( buffer, sizeof( buffer ) ) != 0 )
            {
                switch( (int)buffer[0] )
                {
                    case 'a':
                    case 'A':

                        fprintf( stdout,
                                "    > authorized hostname/IP (*=wildcard): " );

                        if( get_user_input( buffer, sizeof( buffer ) ) != 0 )
                        {
                            string_list_add_item( cfg->authorized_hosts,
                                                  buffer );
                        }

                        break;

                    case 'r':
                    case 'R':

                        print_numbered_mhost_allow_list( cfg );
                        fprintf( stdout, "\n  Remove which host: " );

                        if( get_user_input( buffer, sizeof( buffer ) ) != 0 )
                        {
                            int entry = atoi( buffer );
                            string_list_remove_index( cfg->authorized_hosts,
                                                      entry );
                        }

                        break;

                    case 's':
                    case 'S':

                        print_mhost_allow_list( cfg );
                        break;

                    case 'q':
                    case 'Q':
                    case 'e':
                    case 'E':

                        allow_approved = TRUE;
                        break;

                    default:
                        break;
                }
            }

        }  /* end while editing authorized list. */

        /* sync the data portion of the config with the attributes */
        /* and then print for verification.                        */

        osi_management_config_sync( cfg );        
        print_mhost_config( cfg );

        fprintf( stdout, "Is this correct (y/n)? " );

        if( get_user_input( buffer, sizeof( buffer ) ) != 0 )
        {
            if( buffer[0] == 'y' || buffer[0] == 'Y' )
            {
                approved = TRUE;
            }
        }

    } while ( approved == FALSE );
}


void prompt_for_edit_host_information( CTL_CONTEXT *context )
{
    char buffer[MAX_COMMAND_LENGTH];
    OSI_HOST_BRIEF *host;

    osi_bool approved = FALSE;

    if( ( context != NULL ) && ( context->host_brief != NULL ) )
    {
        host = context->host_brief;

        /* acquire basic host information from the user, we get */
        /* the name, ip, description, and enabled status.       */

        fprintf( stdout, "\n[ edit host (%s) ]\n\n", host->name );

        do
        {
            /* get ip address. */

            fprintf( stdout, "  > hostname/IP address [%s]: ", host->host );

            if( get_user_input( buffer, sizeof( buffer ) ) != 0 )
            {
                osi_strlcpy( host->host, buffer, sizeof( host->host ) );
            }

            /* get description. */

            fprintf( stdout, "  > description [%s]: ", host->description );

            if( get_user_input( buffer, sizeof( buffer ) ) != 0 )
            {
                osi_strlcpy( host->description,
                             buffer, sizeof( host->description ) );
            }

            /* get host type. */

            fprintf( stdout, "  > type [%s]: ",
                     get_name_from_host_type( (int)host->type ) );

            if( get_user_input( buffer, sizeof( buffer ) ) != 0 )
            {
                lowercase_string( buffer );
                host->type = OSI_HOST_TYPE_GENERIC;

                if( strcmp( buffer, OSI_HOST_TYPE_NAME_ROUTER ) == 0 )
                {
                    host->type = OSI_HOST_TYPE_ROUTER;
                }
            }

            /* ask to enable the file log for this host's scans. */

            fprintf( stdout,
                     "  > enable scan logging for this host? (yes/no) " );
 
            if( host->file_log_enabled )
            {
                fprintf( stdout, "[yes]: " );
            }

            else
            {
                fprintf( stdout, "[no]: " );
            } 

            if( get_user_input( buffer, sizeof( buffer ) ) != 0 )
            {
                if( strcmp( buffer, "yes" ) == 0 )
                {
                    host->file_log_enabled = 1;
                }

                else
                {
                    host->file_log_enabled = 0;
                }
            }

            /* ask to enable the archival of database scans. */

            fprintf( stdout,
                     "  > archive scan databases this host? (yes/no) " );

            if( host->archive_databases )
            {
                fprintf( stdout, "[yes]: " );
            }

            else
            {
                fprintf( stdout, "[no]: " );
            }

            if( get_user_input( buffer, sizeof( buffer ) ) != 0 )
            {
                if( strcmp( buffer, "yes" ) == 0 )
                {
                    host->archive_databases = 1;
                }

                else
                {
                    host->archive_databases = 0;
                }
            }

            /* ask to enable admin email notification. */

            fprintf( stdout,
               "  > enable admin email notification for this host? (yes/no) " );

            if( host->notify_enabled )
            {
                fprintf( stdout, "[yes]: " );
            }

            else
            {
                fprintf( stdout, "[no]: " );
            }

            if( get_user_input( buffer, sizeof( buffer ) ) != 0 )
            {
                if( strcmp( buffer, "yes" ) == 0 )
                {
                    host->notify_enabled = 1;
                }

                else
                {
                    host->notify_enabled = 0;
                }
            }

            fprintf( stdout,
       "  > send scan notification, even when no changes detected? (yes/no) " );

            if( host->notify_always )
            {
                fprintf( stdout, "[yes]: " );
            }

            else
            {
                fprintf( stdout, "[no]: " );
            }

            if( get_user_input( buffer, sizeof( buffer ) ) != 0 )
            {
                if( strcmp( buffer, "yes" ) == 0 )
                {
                    host->notify_always = 1;
                }

                else
                {
                    host->notify_always = 0;
                }
            }

            /* ask for scheduling information. */

            fprintf( stdout,
                 "  > configure scan scheduling information? (yes/no) [no]: " );

            if( get_user_input( buffer, sizeof( buffer ) ) != 0 )
            {
                lowercase_string( buffer );

                if( strcmp( buffer, "yes" ) == 0 ||
                    strcmp( buffer, "y" ) == 0 )
                {
                    prompt_for_scheduling_information( context );
                }
            }

            /* ask to enable. */

            fprintf( stdout, "  > activate this host? (yes/no) " );
        
            if( host->enabled )
            {
                fprintf( stdout, "[yes]: " );
            }

            else
            {
                fprintf( stdout, "[no]: " );
            }

            if( get_user_input( buffer, sizeof( buffer ) ) != 0 )
            {
                if( strcmp( buffer, "yes" ) == 0 ||
                    strcmp( buffer, "y" ) == 0 )
                {
                    host->enabled = 1;
                }

                else
                {
                    host->enabled = 0;
                }
            }

            print_host_brief_overview( host );
            fprintf( stdout, "Is this correct (y/n)? " );

            if( get_user_input( buffer, sizeof( buffer ) ) != 0 )
            {
                if( buffer[0] == 'y' || buffer[0] == 'Y' )
                {
                    approved = TRUE;
                }
            }

        } while ( approved == FALSE );
    }
}

void prompt_for_import_config_information( CTL_CONTEXT *context )
{
    char buffer[MAX_COMMAND_LENGTH];

    if( context != NULL )
    {
        context->config = NULL;

        fprintf( stdout, "local config file to import: " );

        if( get_user_input( buffer, sizeof( buffer ) ) == 0 )
        {
            osi_print_stdout( " * error: no file specified.", buffer );
            return;
        }

        /* see if this file exists. */

        if( osi_file_exists( buffer ) == FALSE )
        {
            osi_print_stdout( " * error: local file (%s) doesn't exist.",
                              buffer );
            return;
        }

        context->config = osi_config_new();

        if( osi_config_read_from_file( context->config, buffer ) == FALSE )
        {
            osi_print_stdout( " * error: unable to read local file (%s).",
                              buffer );

            osi_config_destroy( context->config );
            context->config = NULL;

            return;
        }

        /* now as for a name for this config. */

        fprintf( stdout, "provide a name for this config: " );

        while( get_user_input( buffer, sizeof( buffer ) ) == 0 )
        {
            osi_print_stdout( " * error: a name is required." );
            osi_print_stdout( "provide a name for this config: " );
        }

        osi_strlcpy( ctl_context.buffer, buffer, sizeof( ctl_context.buffer ) );
    }
}

void prompt_for_config_information()
{
    char buffer[MAX_COMMAND_LENGTH];

    if( strlen( name ) == 0 )
    {
        /* give user a list of config names. */

        process_list_configs();

        /* get name of file to import. */

        fprintf( stdout, "name of config file: " );

        if( get_user_input( buffer, sizeof( buffer ) ) == 0 )
        {
            osi_print_stdout( " * error: no config specified.", buffer );
            return;
        }

        osi_strlcpy( ctl_context.buffer, buffer, sizeof( ctl_context.buffer ) );
    }
}

void prompt_for_set_base_db_information()
{
    char buffer[MAX_COMMAND_LENGTH];

    if( strlen( name ) == 0 )
    {
        /* give user a list of db names. */

        process_list_databases();

        /* get name of database to set as the base. */

        fprintf( stdout, "enter database name: " );

        if( get_user_input( buffer, sizeof( buffer ) ) == 0 )
        {
            osi_print_stdout( " * error: no database specified.", buffer );
            return;
        }

        osi_strlcpy( ctl_context.buffer, buffer, sizeof( ctl_context.buffer ) );
    }
}

void prompt_for_scheduling_information( CTL_CONTEXT *context )
{
    OSI_HOST_BRIEF *host = NULL;
    osi_bool approved = FALSE;

    char buffer[MAX_COMMAND_LENGTH];

    long default_time = osi_get_time();
    long default_period = DEFAULT_SCAN_PERIOD_VALUE;

    if( ( context == NULL ) || ( context->host_brief == NULL ) )
    {
        return;
    }

    /* create the default time format. */

    host = context->host_brief;

    osi_print_stdout( "" );
    osi_print_stdout( "    [ scheduling information for %s ]", host->name );
    osi_print_stdout( "" );

    fprintf( stdout, "    Scheduling information consists of a start time " );
    fprintf( stdout, "and a frequency value.\n    The frequency is a " );
    fprintf( stdout, "specified number of minutes between each scan, " );
    fprintf( stdout, "starting\n    from the start time.  The default is " );
    fprintf( stdout, "the current time.  Specify the start\n    " );
    fprintf( stdout, "time in the following format: mm/dd/yyyy HH:MM" );

    osi_print_stdout( "\n" );

    do
    {
        char *time_string;

        if( host->schedule_start > 0 )
        {
            time_string = osi_time_to_string( (long)host->schedule_start );
        }

        else
        {
            time_string = osi_time_to_string( (long)default_time ); 
        }

        fprintf( stdout, "    enter the start date and time \n" );
        fprintf( stdout, "    using 'MM/DD/YYYY hh:mm' format: [%s] ",
                 time_string );

        if( get_user_input( buffer, sizeof( buffer ) ) != 0 )
        {
            struct tm t;
            time_t time_value;

            memset( &t, 0, sizeof( struct tm ) );

            strptime( buffer, SCHEDULE_TIME_FORMAT, &t );
            time_value = mktime( &t );

            /* verify with the user. */

            host->schedule_start = time_value;
    
            fprintf( stdout, "    does this look correct ==> (%s) ? (y/n) ",
                     osi_time_to_string( time_value ) );

            if( get_user_input( buffer, sizeof( buffer ) ) != 0 )
            {
                lowercase_string( buffer );

                if( buffer[0] == 'y' || buffer[0] == 'Y' )
                {
                    approved = TRUE;
                }
            }
        }

        else
        {
            if( host->schedule_start == 0 )
            {
                host->schedule_start = default_time;
            }

            approved = TRUE;
        }

    } while( approved == FALSE );

    fprintf( stdout, "    enter scan frequency in minutes: " );
    
    if( host->schedule_period > 0 )
    {
        fprintf( stdout, "[%llu] ", host->schedule_period );
    }

    else
    {
        fprintf( stdout, "[daily (%d)] ", default_period );
    }

    if( get_user_input( buffer, sizeof( buffer ) ) != 0 )
    {
        host->schedule_period = (osi_uint64)atoi( buffer );
    }

    else
    {
        if( host->schedule_period == 0 )
        {
            host->schedule_period = (osi_uint64)default_period;
        }
    }

    osi_print_stdout( "" );
}

void prompt_for_notify_filter_information( CTL_CONTEXT *context )
{
    osi_list filters = NULL;
    osi_bool keep_editing = TRUE;

    OSI_NOTIFY_FILTER *filter;
    char buffer[MAX_COMMAND_LENGTH];
   
    if( ( context == NULL ) || ( context->notify_filters == NULL ) )
    {
        return;
    }
    
    filter = osi_malloc( sizeof( OSI_NOTIFY_FILTER ) );
    memset( filter, 0, sizeof( OSI_NOTIFY_FILTER ) );

    while( keep_editing )
    {
        fprintf( stdout, "\n  > host (*=all hosts): " );    
        
        if( strlen( filter->host ) )
        {
            fprintf( stdout, "[%s] ", filter->host );
        }

        if( get_user_input( buffer, sizeof( buffer ) ) != 0 )
        {
            osi_strlcpy( filter->host, buffer,
                            sizeof( filter->host ) );
        }

        fprintf( stdout, "  > path (*=any path): " );

        if( strlen( filter->path ) )
        {
            fprintf( stdout, "[%s] ", filter->path );
        }

        if( get_user_input( buffer, sizeof( buffer ) ) != 0 )
        {
            osi_strlcpy( filter->path, buffer,
                            sizeof( filter->path ) );
        }

        fprintf( stdout, "\n" );
        fprintf( stdout,
                 "  1) Exclude (ignore changes to certain attributes)\n" );

        fprintf( stdout,
         "  2) Include Only (monitor changes only to certain attributes)\n\n" );

        filter->type = NOTIFY_FILTER_TYPE_EXCLUDE;
        fprintf( stdout, "  > filter type: " );

        if( filter->type >  0 )
        {
            fprintf( stdout, "[%llu] ", filter->type );
        }

        if( get_user_input( buffer, sizeof( buffer ) ) != 0 )
        {
            if( atoi( buffer ) == 2 )
            {
                filter->type = NOTIFY_FILTER_TYPE_INCLUDE_ONLY;
            }
        }

        print_valid_notify_filter_attributes();
        fprintf( stdout, "\n  > attributes (comma separated): " );

        if( filter->mask > 0 )
        {
            fprintf( stdout, "[" );
            print_notify_attribute_mask( filter->mask );
            fprintf( stdout, "] " );
        }

        if( get_user_input( buffer, sizeof( buffer ) ) != 0 )
        {
            filter->mask = mask_from_notify_keywords( buffer );
        }

        /* verify this filter with the user. */

        fprintf( stdout, "\n  does this look correct:\n    ==> " );
        print_notify_filter( filter );
        fprintf( stdout, "  (y/n)? " );

        if( get_user_input( buffer, sizeof( buffer ) ) != 0 )
        {
            if( strcmp( buffer, "yes" ) == 0 ||
                strcmp( buffer, "y" ) == 0 )
            {
                keep_editing = FALSE;                            
                fprintf( stdout, "\nfilter added.\n" );
            }
        }
    }

    list_add( context->notify_filters, filter );
}

void prompt_for_mhost_auth_information()
{
    char buffer[MAX_AUTH_USERNAME_LENGTH];

    osi_print_stdout( "authenticating to (%s)\n", mhost );

    if( strlen( ctl_context.username ) == 0 )
    {
        fprintf( stdout, PROMPT_USERNAME );

        if( get_user_input( buffer, sizeof( buffer ) ) != 0 )
        {
            osi_strlcpy( ctl_context.username, buffer,
                         sizeof( ctl_context.username ) );
        }
    }

    if( get_password_input( buffer, sizeof( buffer ), PROMPT_PASSWORD ) != 0 )
    {
        osi_strlcpy( ctl_context.password, buffer,
                     sizeof( ctl_context.password ) );
    }

    memset( buffer, 0, sizeof( buffer ) );
    osi_print_stdout( "" );
}

void prompt_for_user_information()
{
    char buffer[MAX_AUTH_USERNAME_LENGTH];

    if( strlen( name ) == 0 )
    {
        fprintf( stdout, PROMPT_USERNAME );

        if( get_user_input( buffer, sizeof( buffer ) ) != 0 )
        {
            osi_strlcpy( ctl_context.username, buffer,
                         sizeof( ctl_context.username ) );
        }
    }

    /* user didn't specify it, we copy the buffer into the username field. */

    else
    {
        osi_strlcpy( ctl_context.username, name,
                     sizeof( ctl_context.username ) );
    }

    if( get_password_input( buffer, sizeof( buffer ), PROMPT_PASSWORD ) != 0 )
    {
        osi_strlcpy( ctl_context.password, buffer,
                     sizeof( ctl_context.password ) );
    }
}


void print_context_error( CTL_CONTEXT *context )
{
    if( context != NULL )
    {
        fprintf( stderr, "error: " );

        /* print message if exists. */

        if( strlen( context->error.message ) > 0 )
        {
            osi_print_stderr( "%s", context->error.message );
        }

        /* otherwise, try to look up the code for this error. */

        else if( context->error.type > 0 )
        {
            osi_print_stderr( "%s",
                              osi_get_name_for_error( context->error.type ) );
        }
    }
}

void print_numbered_mhost_allow_list( OSI_MANAGEMENT_CONFIG *cfg )
{
    int index;

    if( ( cfg == NULL ) || ( cfg->authorized_hosts == NULL ) )
    {
        return;
    }

    osi_print_stdout( "%s", "\n  > authorized hosts:\n" );

    for( index = 0;
         (unsigned int)index < ( cfg->authorized_hosts->size ); index++ )
    {
        osi_print_stdout( "   %d -  %s", index,
                           cfg->authorized_hosts->list[index] );
    }

}

void print_numbered_filter_list( osi_list filters )
{
    int index = 0;
    osi_list head = NULL;

    if( filters == NULL )
    {
        osi_print_stdout( "  <no filters>" );
        return;
    }

    osi_print_stdout( "%s", "\n  > current filters:\n" );

    head = list_get_first_node( filters );

    while( head )
    {
        OSI_NOTIFY_FILTER *filter;

        if( ( filter = (OSI_NOTIFY_FILTER *)head->data ) != NULL )
        {
            fprintf( stdout, " %d) ", index );
            print_notify_filter( filter );
            fprintf( stdout, "\n" );

            index++;
        }

        head = list_get_next_node( head );
    }

    if( index == 0 )
    {
        osi_print_stdout( "  <no filters>" );
    }
}


void print_mhost_allow_list( OSI_MANAGEMENT_CONFIG *cfg )
{
    int index;

    if( ( cfg == NULL ) || ( cfg->authorized_hosts == NULL ) )
    {
        return;
    }

    osi_print_stdout( "%s", "\n  > authorized hosts:\n" );

    for( index = 0;
         (unsigned int)index < ( cfg->authorized_hosts->size ); index++ )
    {
        osi_print_stdout( "   %s", cfg->authorized_hosts->list[index] );
    }
}

void print_notify_filters_list( osi_list filters )
{
    int index = 0;
    osi_list head;

    if( ( filters == NULL ) || ( list_get_size( filters ) == 0 ) )
    {
        fprintf( stdout, "  <no filters>\n" );
        return;
    }

    head = list_get_first_node( filters );
    fprintf( stdout, "\n" );
            
    while( head )
    {
        OSI_NOTIFY_FILTER *filter;

        if( ( filter = (OSI_NOTIFY_FILTER *)head->data ) != NULL )
        {
            print_notify_filter( filter );
            fprintf( stdout, "\n" );
            index++;
        }

        head = list_get_next_node( head );
    }    

    if( index == 0 )
    {
        osi_print_stdout( "\n  <no filters>" );
    }

    else
    {
        osi_print_stdout( "\n%d notification filters.", index );
    }
}



void print_database_brief_expanded( OSI_DATABASE_BRIEF *database )
{
    if( database != NULL )
    {
        osi_print_stdout( "\n  DATABASE:  %s\n", name );

        if( database->complete == 1 )
        {
            osi_print_stdout( "  status: complete" );
        }

        else
        {
            osi_print_stdout( "  status: incomplete" );
        }

        osi_print_stdout( "  errors: %llu", database->error_count );
        osi_print_stdout( " records: %llu", database->record_count );

        if( strlen( database->config_name ) > 0 )
        {
            fprintf( stdout, "  config: %s", database->config_name );
        }

        if( strlen( database->config_id ) > 0 )
        {
            osi_print_stdout( " (%s)", database->config_id );
        }

        if( database->complete == 1 )
        {
            osi_print_stdout( "\n  SCAN RESULTS:" );
            print_scan_results( &( database->scan_results ) );
        }

        else
        {
          osi_print_stdout( "" );
          osi_print_stdout( "    ** database is incomplete, no scan results." );
        }
    }
}


void print_status( OSI_STATUS *status )
{
    time_t current_time;
    time_t start_time;
    time_t config_time;

    char *osiris_version = "(unknown)";

    if( status != NULL )
    {
        current_time = status->current_time;
        start_time   = status->start_time;
        config_time  = status->config_time;

        osi_print_stdout( "" );
        osi_print_stdout( "[ current status of host: %s ]\n",
                          ctl_context.host );

        fprintf( stdout, "     current time: %s", ctime( &current_time ) );
        fprintf( stdout, "         up since: %s\n", ctime( &start_time ) );

        if( status->config_state == CONFIG_STATE_VALID )
        {
            fprintf( stdout, " last config push: %s", ctime( &config_time ) );
            osi_print_stdout( " configuration id: %s\n", status->config_id );
        }

        osi_print_stdout( "    daemon status: %s.",
                          get_name_for_daemon_status( status->daemon_state ) );

        osi_print_stdout( "    config status: %s.",
                          get_name_for_config_status( status->config_state ) );

        /* get values for osiris and uname type values. */

        if( strlen( status->osiris_version ) > 0 )
        {
            osiris_version = status->osiris_version;        
        }

        osi_print_stdout( "   osiris version: %s", osiris_version );

        fprintf( stdout, "               OS: " );

        if( strlen( status->os_name ) > 0 )
        {
            fprintf( stdout, "%s ", status->os_name );
        }

        else
        {
            fprintf( stdout, "%s", "(unknown) " );
        }

        if( strlen( status->os_version ) > 0 )
        {
            osi_print_stdout( "%s", status->os_version );
        }
        
        osi_print_stdout( "" );
    }
}

void print_host_config( OSI_HOST_CONFIG *config )
{
    if( config != NULL )
    {
        int index;
        
        osi_print_stdout( "\n[ host config (%s) ]\n", ctl_context.host );
        
        for( index = 0; (unsigned int)index < ( config->data->size ); index++ )
        {
            osi_print_stdout( config->data->list[index] );
        }
    }
    
    else
    {
        osi_print_stderr( "no config data." );
    }
    
    osi_print_stdout( "\n" );
}

void print_mhost_config( OSI_MANAGEMENT_CONFIG *config )
{
    if( config != NULL )
    {
        int index;
        
        osi_print_stdout( "\n[ management config (%s) ]\n", mhost );
        
        for( index = 0; (unsigned int)index < ( config->data->size ); index++ )
        {
            osi_print_stdout( config->data->list[index] );
        }
    }
    
    else
    {
        osi_print_stderr( "no config data." );
    }

    osi_print_stdout( "\n" );
}

void print_config_brief( OSI_CONFIG_BRIEF *config )
{
    if( config != NULL )
    {
        fprintf( stdout, "  %-20.20s", config->name );

        if( ( config->valid ) && ( strlen( config->id ) > 0 ) )
        {
            fprintf( stdout, "  %-33.33s", config->id );
        }

        else
        {
            fprintf( stdout, "  (invalid)" );
        }

        fprintf( stdout, "\n" );
    }
}

void print_log_brief( OSI_LOG_BRIEF *log )
{
    time_t log_time;

    if( log == NULL )
    {
        return;
    }

    log_time = (time_t)log->timestamp;

    fprintf( stdout, "    %-31.31s", log->name );
    fprintf( stdout, " %-20.20s", ctime( &log_time ) );

    fprintf( stdout, "\n" );
}

void print_database_brief( OSI_DATABASE_BRIEF *database )
{
    time_t scan_start_time;

    if( database != NULL )
    {
        /* print name, show base if it has one. */

        if( strcmp( name, database->name ) == 0 ) 
        {
            fprintf( stdout, "  * " );
            fprintf( stdout, "%-29.31s  ", database->name );
        }

        else
        {
            fprintf( stdout, "    %-31.31s", database->name );
        }


        /* show the timestamp. */

        if( database->complete == 1 )
        {
            scan_start_time = ( database->scan_results ).start_time;
            fprintf( stdout, " %-20.20s", ctime( &scan_start_time ) );
        }

        else
        {
            fprintf( stdout, "(incomplete)" );
        }

        fprintf( stdout, "\n" );
    }
}

void print_host_brief( OSI_HOST_BRIEF *host )
{
    if( host != NULL )
    {
        fprintf( stdout, "  %-20.20s", host->name );

        if( strlen( host->description ) > 40 )
        {
            fprintf( stdout, " %-27.27s...", host->description );
        }

        else
        {
            fprintf( stdout, " %-30.30s", host->description );
        }

        if( host->enabled == 1 )
        {
            fprintf( stdout, "      yes" );
        }

        else
        {
            fprintf( stdout, "      no" );
        }

        fprintf( stdout, "\n" );
    }
}

void print_host_brief_expanded( OSI_HOST_BRIEF *host )
{
    if( host != NULL )
    {
        osi_print_stdout( "" );
        osi_print_stdout( "[ host details for: (%s) ]\n", host->name );

        fprintf( stdout, "  enabled        : " );

        if( host->enabled == 1 )
        {
            fprintf( stdout, "yes\n" );
        }

        else
        {
            fprintf( stdout, "no\n" );
        }

        osi_print_stdout( "  hostname/IP    : %s", host->host );
        osi_print_stdout( "  configs        : %llu", host->config_count );
        osi_print_stdout( "  databases      : %llu", host->database_count );

        osi_print_stdout( "  host type      : %s",
                          get_name_from_host_type( (int)host->type ) );
        
        if( host->file_log_enabled )
        {
            osi_print_stdout( "  log enabled    : yes" );
        }

        else
        {
            osi_print_stdout( "  log enabled    : no" );
        }

        if( host->archive_databases )
        {
            osi_print_stdout( "  archive scans  : yes" );
        }

        else
        {
            osi_print_stdout( "  archive scans  : no" );
        }

        if( host->notify_enabled )
        {
            osi_print_stdout( "  notify enabled : yes" );
        }

        else
        {
            osi_print_stdout( "  notify enabled : no" );
        }

        if( host->notify_always )
        {
            osi_print_stdout( "  notify always  : yes" );
        }

        else
        {
            osi_print_stdout( "  notify always  : no" );
        }

        if( host->schedule_start > 0 )
        {
            osi_print_stdout( "  scans start    : %s",
                             osi_time_to_string( (long)host->schedule_start ) );
        }

        else
        {
            osi_print_stdout( "  scans start    : (not configured)" );
        }

        if( host->schedule_period > 0 )
        {
            osi_print_stdout( "  scan period    : every %llu minutes",
                              host->schedule_period );
        }

        else
        {
            osi_print_stdout( "  scan period    : (not configured)" );
        }

        osi_print_stdout( "  base DB        : %s", host->base_db );
        osi_print_stdout( "  description    : %s", host->description );
        osi_print_stdout( "" );
    }
}

void print_host_brief_overview( OSI_HOST_BRIEF *host )
{
    if( host == NULL )
    {
        return;
    }

    osi_print_stdout( "" );

    osi_print_stdout( "host                  => %s", host->name );
    osi_print_stdout( "hostname/IP address   => %s", host->host );
    osi_print_stdout( "description           => %s", host->description );

    if( host->type == OSI_HOST_TYPE_GENERIC )
    {
        osi_print_stdout( "host type             => generic" );
    }

    else if( host->type == OSI_HOST_TYPE_ROUTER )
    {
        osi_print_stdout( "host type             => router" );
    }

    if( host->file_log_enabled )
    {
        osi_print_stdout( "log enabled           => yes" );
    }

    else
    {
        osi_print_stdout( "log enabled           => no" );
    }

    if( host->archive_databases )
    {
        osi_print_stdout( "archive scans         => yes" );
    }

    else
    {
        osi_print_stdout( "archive scans         => no" );
    }

    if( host->notify_enabled )
    {
        osi_print_stdout( "notifications enabled => yes" );
    }

    else
    {
        osi_print_stdout( "notifications enabled => no" );
    }

    if( host->notify_always )
    {
        osi_print_stdout( "notifications always  => yes" );
    }

    else
    {
        osi_print_stdout( "notifications always  => no" );
    }

    if( host->schedule_start > 0 )
    {
        long timevalue = (long)host->schedule_start;

        osi_print_stdout( "scans starting on     => %s",
                           osi_time_to_string( timevalue ) );
    }

    else
    {
        osi_print_stdout( "scans starting on     => (not configured)" );
    }

    if( host->schedule_period > 0 )
    {
        fprintf( stdout, "scan frequency        => " );

        if( host->schedule_period == 60 )
        {
            osi_print_stdout( "hourly (every 60 minutes)." );
        }

        else if( host->schedule_period == 1440 )
        {
            osi_print_stdout( "daily (every 1440 minutes)." );
        }

        else
        {
            osi_print_stdout( "every %llu minutes", host->schedule_period );
        }
    }

    else
    {
        osi_print_stdout( "scan frequency        => (not configured)" );
    }

    if( host->enabled )
    {
        osi_print_stdout( "enabled               => yes" );
    }

    else
    {
        osi_print_stdout( "enabled               => no" );
    }

    osi_print_stdout( "" );
}

void print_config( configuration *cfg )
{
    if( cfg != NULL )
    {
        osi_print_stdout( "" );
        osi_print_stdout( " config name:  %s", name );
        osi_print_stdout( "          ID:  %s", cfg->id );
        osi_print_stdout( "      status:  %s", ( cfg->error_count == 0 ) ?
                                               "valid" : "invalid" );


        osi_print_stdout( "      errors:  %d", cfg->error_count );
        osi_print_stdout( "    warnings:  %d", cfg->warning_count );

        if( cfg->data )
        {
            osi_print_stdout( "       lines:  %d", cfg->data->size );
        }

        osi_print_stdout( "" );

        if( cfg->data != NULL )
        {
            int index;

            osi_print_stdout( "-------- begin config file --------\n" );

            for( index = 0; (unsigned int)index < cfg->data->size; index++ )
            {
                osi_print_stdout( "%s", cfg->data->list[index] );
            }

            osi_print_stdout( "\n--------  end config file  --------\n" );
        }
    }
}

void print_log_file( string_list *log_data )
{
    if( log_data == NULL )
    {
        return;
    }

    if( log_data->list != NULL )
    {
        int index;

        osi_print_stdout( "" );
        osi_print_stdout( "-------- begin log file --------\n" );

        for( index = 0; (unsigned int)index < log_data->size; index++ )
        {
            fprintf( stdout, "%s", log_data->list[index] );
        }

        osi_print_stdout( "\n--------  end log file  --------\n" );
    }
}

void print_command_usage( int command )
{
    osi_print_stdout( "" );

    switch( command )
    {
        case COMMAND_STATUS_REQUEST:

            osi_print_stdout( "    usage: status [host]\n" );
            osi_print_stdout( "    request status information from the currently selected host.  the management" );
            osi_print_stdout( "    host will attempt to contact the daemon and issue a status request." );
            break;

        case COMMAND_WATCH_HOST:

            osi_print_stdout( "    usage: watch-host [host]\n" );
            osi_print_stdout( "    continually get and show status of a host.  This is useful for watching  scanning" );
            osi_print_stdout( "    host until the scan completes.\n" );
            osi_print_stdout( "    aliases: watch" );
            break;

        case COMMAND_HOST_DETAILS:

            osi_print_stdout( "    usage: host-details [host]\n" );
            osi_print_stdout( "    request information about a specific host.  this includes all information in" );
            osi_print_stdout( "    the host.conf file on the management host.  if no host is specified, the current" );
            osi_print_stdout( "    host details are printed.\n" );
            osi_print_stdout( "    aliases: details" );

            break;

        case COMMAND_DROP_CONFIG:

            osi_print_stdout( "    usage: drop-config [host]\n" );
            osi_print_stdout( "    attempt to cause the host to drop it's currently loaded config file.  the" );
            osi_print_stdout( "    daemon will not drop a config while it is scanning.  if no host is specified, the" );
            osi_print_stdout( "    current host will be assumed." );
            break;

        case COMMAND_START_SCAN:

            osi_print_stdout( "    usage: start-scan\n" );
            osi_print_stdout( "    attempt to start a scan on the currently selected host.  the management" );
            osi_print_stdout( "    host will relay any errors that occured during the attempt.\n" );
            osi_print_stdout( "    aliases: scan-host, scan" );
            break;

        case COMMAND_STOP_SCAN:

            osi_print_stdout( "    usage: stop-scan\n" );
            osi_print_stdout( "    attempt to stop a scan on the currently selected host.  the management" );
            osi_print_stdout( "    host will relay any errors that occured during the attempt." );
            break;

        case COMMAND_NEW_HOST:

            osi_print_stdout( "    usage: new-host\n" );
            osi_print_stdout( "    create a new host profile on the management host.  This will create the" );
            osi_print_stdout( "    config file for the host, and by default, this host will be disabled" );
            osi_print_stdout( "    unless otherwise specified.\n" );
            osi_print_stdout( "    aliases: add-host" );

            break;

        case COMMAND_EDIT_MHOST_CONFIG:

            osi_print_stdout( "    usage: edit-mhost-config\n" );
            osi_print_stdout( "    edit the management host configuration file.\n" );
            osi_print_stdout( "    aliases: edit-mhost" );

            break;

        case COMMAND_ENABLE_HOST:

            osi_print_stdout( "    usage: enable-host [host]\n" );
            osi_print_stdout( "    enable the currently selected host, or the host specified." );
            osi_print_stdout( "    A host must be enabled in order to be perform any scheduled scans.\n" );
            osi_print_stdout( "    aliases: enable" );

            break;

        case COMMAND_DISABLE_HOST:

            osi_print_stdout( "    usage: disable-host [host]\n" );
            osi_print_stdout( "    disable the currently selected host, or the host specified." );
            osi_print_stdout( "    When disabled, a host will NOT perform any scheduled scans.\n" );
            osi_print_stdout( "    aliases: disable" );

            break;

        case COMMAND_SET_BASE_DB:

            osi_print_stdout( "    usage: set-base-db <database name>\n" );
            osi_print_stdout( "    set the base database.  The base database is the database that is" );
            osi_print_stdout( "    considered the known good database that is used, by default, on" );
            osi_print_stdout( "    regular scans." );

            break;

        case COMMAND_SHOW_BASE_DB:

            osi_print_stdout( "    usage: show-base-db [host]\n" );
            osi_print_stdout( "    show the base database for the specied host, or current host.\n" );
            osi_print_stdout( "    aliases: base-db" );

            break;

        case COMMAND_EDIT_HOST:

            osi_print_stdout( "    usage: edit-host [host]\n" );
            osi_print_stdout( "    edit the configuration information for the specified host, or the current" );
            osi_print_stdout( "    host, the current values are displayed.\n" );
            osi_print_stdout( "    aliases: edit, modify, modify-host" );

            break;

        case COMMAND_INITIALIZE_HOST:
    
            osi_print_stdout( "    usage: init-host [host]\n" );
            osi_print_stdout( "    setup an initial config and perform a base scan for the" );
            osi_print_stdout( "    current or specified host.\n" );
            osi_print_stdout( "    aliases: init, initialize-host" );

            break;

        case COMMAND_PUSH_CONFIG:

            osi_print_stdout( "    usage: push-config <config-name>\n" );
            osi_print_stdout( "    cause the management host to push the specified configuration file to the" );
            osi_print_stdout( "    currently selected host." );
            break;

        case COMMAND_IMPORT_CONFIG:

            osi_print_stdout( "    usage: import-config <filename>\n" );
            osi_print_stdout( "    cause the management host to receive the specified configuration file from" );
            osi_print_stdout( "    the local file system and store it under the configs database for the" );
            osi_print_stdout( "    currently selected host." );
            break;

        case COMMAND_VERSION:

            osi_print_stdout( "    usage: version\n" );
            osi_print_stdout( "    print program name and version information.\n" );
            osi_print_stdout( "    aliases: ver" );

            break;

        case COMMAND_PRINT_SSL:

            osi_print_stdout( "    usage: print-ssl\n" );
            osi_print_stdout( "    print current SSL tunnel details.\n" );
            osi_print_stdout( "    aliases: ssl" );

            break;

        case COMMAND_EXIT:

            osi_print_stdout( "    usage: quit\n" );
            osi_print_stdout( "    exit this osiris management application.\n" );
            osi_print_stdout( "    aliases: exit,bye,q,asta" );

            break;

        case COMMAND_HELP:

            osi_print_stdout( "    usage: help [command]\n" );
            osi_print_stdout( "    print program help, if a command is specified, prints help about that command.\n" );
            osi_print_stdout( "    aliases: h" );

            break;

        case COMMAND_HOST:

            osi_print_stdout( "    usage: host <new host>\n" );
            osi_print_stdout( "    set the current host." );
            osi_print_stdout( "    printed. A fully qualified hostname or IP address can be used." );
            break;

        case COMMAND_MANAGEMENT_HOST:

            osi_print_stdout( "    usage: mhost [new mhost]\n" );
            osi_print_stdout( "    connect to specified management host.  If no host is" );
            osi_print_stdout( "    specified, a reconnect attempt is made of the current host.  A fully" );
            osi_print_stdout( "    qualified hostname or IP address can be used." );
            break;

        case COMMAND_NEW_USER:

            osi_print_stdout( "    usage: new-user [username]\n" );
            osi_print_stdout( "    create a new user to login to the current management host.\n" );
            osi_print_stdout( "    aliases: add-user" );
            break;

        case COMMAND_EDIT_USER:

            osi_print_stdout( "    usage: edit-user [username]\n" );
            osi_print_stdout( "    edit the specified user for the current management host.\n" );
            osi_print_stdout( "    aliases: modify-user, password, passwd" );
            break;

        case COMMAND_LIST_USERS:

            osi_print_stdout( "    usage: list-users \n" );
            osi_print_stdout( "    print the current list of user accounts for this management host.\n" );
            osi_print_stdout( "    aliases: users" );
            break;

        case COMMAND_EDIT_FILTERS:

            osi_print_stdout( "    usage: edit-filters \n" );
            osi_print_stdout( "    edit the notification filter list.\n" );
            osi_print_stdout( "    aliases: new-filter, edit-filter" );
            break;

        case COMMAND_PRINT_FILTERS:

            osi_print_stdout( "    usage: print-filters \n" );
            osi_print_stdout( "    display the currently active notification filter list.\n" );
            osi_print_stdout( "    aliases: filters, print-filter" );
            break;

        case COMMAND_SEND_NOTIFY_TEST:

            osi_print_stdout( "    usage: test-notify \n" );
            osi_print_stdout( "    send a test email notification to the administrator address specified" );
            osi_print_stdout( "    in the mhost config." );
            break;

        case COMMAND_LIST_HOSTS:

            osi_print_stdout( "    usage: list-hosts\n" );
            osi_print_stdout( "    request a full listing of hosts recognized by the management host.  this" );
            osi_print_stdout( "    includes all of the disabled hosts as well as enabled hosts.\n" );
            osi_print_stdout( "    aliases: hosts" );

            break;

        case COMMAND_DELETE_USER:

            osi_print_stdout( "    usage: delete-user [username]\n" );
            osi_print_stdout( "    delete the specified user from the current account database for this" );
            osi_print_stdout( "    management host.\n" );
            osi_print_stdout( "    aliases: del-user, kill-user, whack-user" );

            break;

        case COMMAND_LIST_CONFIGS:

            osi_print_stdout( "    usage: list-configs\n" );
            osi_print_stdout( "    request a full listing of configuration files that are associated with the" );
            osi_print_stdout( "    current host ( this includes invalid config files ).\n" );
            osi_print_stdout( "    aliases: configs" );

            break;

        case COMMAND_LIST_DATABASES:

            osi_print_stdout( "    usage: list-databases\n" );
            osi_print_stdout( "    request a full listing of database files that are associated with the" );
            osi_print_stdout( "    current host ( this includes incomplete database files ).\n" );
            osi_print_stdout( "    aliases: databases" );

            break;

        case COMMAND_LIST_LOGS:

            osi_print_stdout( "    usage: list-logs\n" );
            osi_print_stdout( "    request a full listing of log files that are associated with the" );
            osi_print_stdout( "    current host.\n" );
            osi_print_stdout( "    aliases: logs" );

            break;

        case COMMAND_PRINT_DB:

            osi_print_stdout( "    usage: print-db <db name>\n" );
            osi_print_stdout( "    print the database header for the specified database.  if the database is" );
            osi_print_stdout( "    incomplete, no scan results will be printed." );
            break;

        case COMMAND_PRINT_DB_ERRORS:

            osi_print_stdout( "    usage: print-db-errors <db name>\n" );
            osi_print_stdout( "    print any errors that may exist in the database." );
            break;

        case COMMAND_PRINT_CONFIG:

            osi_print_stdout( "    usage: print-config <config name>\n" );
            osi_print_stdout( "    print the configuration file and it's various stats, this includes" );
            osi_print_stdout( "    invalid configuration files." );
            break;
            
        case COMMAND_PRINT_HOST_CONFIG:

            osi_print_stdout( "    usage: print-host-config [host]\n" );
            osi_print_stdout( "    print the entire host.conf file for the current host, or the" );
            osi_print_stdout( "    host that was specified.\n" );
            osi_print_stdout( "    aliases: host-config" );

            break;
            
        case COMMAND_PRINT_MANAGEMENT_CONFIG:

            osi_print_stdout( "    usage: print-mhost-config [mhost]\n" );
            osi_print_stdout( "    print the entire osirismd.conf file for the current management host" );
            osi_print_stdout( "    or the management host that was specified.\n" );
            osi_print_stdout( "    aliases: mhost-config" );

            break;

        case COMMAND_PRINT_LOG:
    
            osi_print_stdout( "   usage: print-log <log>\n" );
            osi_print_stdout( "   retrieve and disaplay the entire contents of the specified log file." );
            
            break;

        default:
            osi_print_stdout( "unknown command specified." );
            break;
    }

    osi_print_stdout( "\n" );
}

osi_bool get_base_db_name_for_host( char *name, int name_length,
                                    const char *host )
{
    OSI_HOST_BRIEF *host_brief;
    osi_bool result = FALSE;

    /* now get the host brief. */

    host_brief = ctl_get_host( &ctl_context );

    if( host_brief != NULL )
    {
        osi_strlcpy( name, host_brief->base_db, name_length );
        osi_free( host_brief );

        result = TRUE;
    }

    else
    {
        print_context_error( &ctl_context );
    }

    return result;
}


void halt( int code )
{
#ifdef WIN32
    WSACleanup();
#endif

    shutdown_ssl();
    exit( code );
}


void startup_winsock()
{
#ifdef WIN32

    WORD wVersionRequested;
    WSADATA wsaData;
    int err;

    wVersionRequested = MAKEWORD( 2, 2 );

    err = WSAStartup( wVersionRequested, &wsaData );

    if ( err != 0 )
    {
        /* Tell the user that we could not find a usable */
        /* WinSock DLL.                                  */

        halt(-1);
    }

    /* Confirm that the WinSock DLL supports 2.2.        */
    /* Note that if the DLL supports versions greater    */
    /* than 2.2 in addition to 2.2, it will still return */
    /* 2.2 in wVersion since that is the version we      */
    /* requested.                                        */

    if ( LOBYTE( wsaData.wVersion ) != 2 || HIBYTE( wsaData.wVersion ) != 2 )
    {
        /* Tell the user that we could not find a usable */
        /* WinSock DLL.                                  */

        WSACleanup( );
        halt(-1);;
    }
#endif
}

