
/******************************************************************************
**
**  Copyright (C) 2001  - the shmoo group -
**
**  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.
**
******************************************************************************/

/******************************************************************************
**
**    The Shmoo Group (TSG)
**
**    File:      utilities.c
**    Author:    Brian Wotring
**
**    Date:      March 13, 2002.
**    Project:   osiris
**
******************************************************************************/

#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>

#include <string.h>
#include <fcntl.h>
#include <ctype.h>

#include <sys/stat.h>
#include <sys/types.h>


#ifdef WIN32
#include <winsock.h>
#include <io.h>
#include <time.h>
#else
#include <unistd.h>
#include <sys/time.h>
#endif

#include <openssl/md5.h>
#include <openssl/sha.h>
#include <openssl/ripemd.h>

#include "utilities.h"
#include "error.h"


/******************************************************************************
**
**    Function: osi_malloc
**
**    Purpose:  exit wrapper around malloc
**
******************************************************************************/

void * osi_malloc( size_t size )
{
    void *new;

    if( size <= 0 )
    {
        size = 1;
    }

    new = (void *)malloc( size );

    if( new == NULL )
    {
        exit (1);
    }

    return new;
}

/******************************************************************************
**
**    Function: osi_free
**
**    Purpose:  safe wrapper around fre
**
******************************************************************************/

void osi_free( void *memory )
{
    if( memory > 0 )
    {
        free( memory );
    }
}

/******************************************************************************
**
**    Function: trim_white_space
**
**    Purpose:  remove any non printable characters from beginning and
**              end of string, if it's the NULL string, returns NULL
**              otherwise, return pointer to beginning of string.
**
******************************************************************************/

char *trim_white_space( char *string )
{
    int length;

    if( string == NULL || *string == '\0' )
    {
        return NULL;
    }

    /* remove any leading white space. */

    while( *string && !isgraph(*string) )
    {
        string++;
    }

    if( *string == '\0' )
    {
        return NULL;
    }

    /* remove any trailing white space */

    length = strlen( string );

    while( !isgraph( string[length] ) && ( length > 1 ) )
    {
        string[length] = '\0';
        length--;
    }

    if( *string == '\0' )
    {
        return NULL;
    }

    return string;
}


/******************************************************************************
**
**    Function: get_token
**
**    Purpose:  puts next non-white space sequence from string into token
**              and returns pointer to next token in string.
**              token must be as large as string.
**
******************************************************************************/

char *get_token( char *string, char *buffer, int buffer_size )
{
    int bytes_copied = 0;
    string = trim_white_space( string );

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

    while( (*string) && ( isgraph(*string) ) )
    {
        *buffer = *string;

        buffer++;
        string++;

        bytes_copied++;

        if( bytes_copied >= buffer_size )
        {
            break;
        }
    }

    *buffer = '\0';
    string = trim_white_space( string );

    return string;
}

/******************************************************************************
**
**    Function: lowercase_string
**
**    Purpose:  turns every character in the string into its lowercase form.
**
******************************************************************************/

void lowercase_string( char *string )
{
    if( string != NULL )
    {
        while( *string )
        {
            *string = tolower ( (int)*string );
            string++;
        }
    }
}

/******************************************************************************
**
**    Function: get_string_up_to_token
**
**    Purpose:  get the string up to the token, returns the
**              start of the string after the token, NULL if not found.
**              caller is responsible for knowing if there is sufficient
**              space in the result buffer!
**
******************************************************************************/

char * get_string_up_to_token( char *string, char *result,
                               int result_size, char token )
{
    char *temp;

    int length;
    int copy_length;

    if( string == NULL || result == NULL )
    {
        return FALSE;
    }

    temp = strchr( string, token );

    if( temp == NULL )
    {
        return NULL;
    }

    length = ( ( temp - string ) + 1 );
    copy_length = MINIMUM( length, result_size );

    osi_strlcpy( result, string, copy_length );

    /* terminate the buffer, move past the token in string  */
    /* and return a pointer to that string after the token. */

    result[length] = '\0';
    temp++;

    return temp;
}


/******************************************************************************
**
**    Function: escape_filename
**
**    Purpose:  turn control characters into their escaped form so we have
**		        a readable null terminated string representing a file path.
**
******************************************************************************/

char * escape_filename( char *file_path, char *buffer, int buffer_size )
{
    char escaped[MAX_PATH_LENGTH * 3] = "";

    char *input  = (char *)file_path;
    char *output = (char *)escaped;

    static char *octals[] =
    {
	"000", "001", "002", "003", "004", "005", "006", "007",
	"010", "011", "012", "013", "014", "015", "016", "017",
	"020", "021", "022", "023", "024", "025", "026", "027",
	"030", "031", "032", "033", "034", "035", "036", "037",
	"040", "041", "042", "043", "044", "045", "046", "047",
	"050", "051", "052", "053", "054", "055", "056", "057",
	"060", "061", "062", "063", "064", "065", "066", "067",
	"070", "071", "072", "073", "074", "075", "076", "077",
	"100", "101", "102", "103", "104", "105", "106", "107",
	"110", "111", "112", "113", "114", "115", "116", "117",
	"120", "121", "122", "123", "124", "125", "126", "127",
	"130", "131", "132", "133", "134", "135", "136", "137",
	"140", "141", "142", "143", "144", "145", "146", "147",
	"150", "151", "152", "153", "154", "155", "156", "157",
	"160", "161", "162", "163", "164", "165", "166", "167",
	"170", "171", "172", "173", "174", "175", "176", "177",
	"200", "201", "202", "203", "204", "205", "206", "207",
	"210", "211", "212", "213", "214", "215", "216", "217",
	"220", "221", "222", "223", "224", "225", "226", "227",
	"230", "231", "232", "233", "234", "235", "236", "237",
	"240", "241", "242", "243", "244", "245", "246", "247",
	"250", "251", "252", "253", "254", "255", "256", "257",
	"260", "261", "262", "263", "264", "265", "266", "267",
	"270", "271", "272", "273", "274", "275", "276", "277",
	"300", "301", "302", "303", "304", "305", "306", "307",
	"310", "311", "312", "313", "314", "315", "316", "317",
	"320", "321", "322", "323", "324", "325", "326", "327",
	"330", "331", "332", "333", "334", "335", "336", "337",
	"340", "341", "342", "343", "344", "345", "346", "347",
	"350", "351", "352", "353", "354", "355", "356", "357",
	"360", "361", "362", "363", "364", "365", "366", "367",
	"370", "371", "372", "373", "374", "375", "376", "377",
    };

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

    while( *input )
    {
        if( iscntrl( *input ) )
        {
            *output++ = '\\';
            *output++ = octals[*input][0];
            *output++ = octals[*input][1];
            *output++ = octals[*input][2];

            input++;
	}

	else
        {
	    *output++ = *input++;
        }
    }

    *output = '\0';
    osi_strlcpy( buffer, escaped, buffer_size );

    return buffer;
}




size_t osi_strnlen( const char *str, size_t nchars )
{
    size_t i = 0;
    size_t result = 0;

    if( str && nchars > 0 )
    {
        for( i = 0; i < nchars && str[i] != '\0'; i++ )
        {
            result++;
        }
    }

    return result;
}


size_t osi_strlcpy( char * dst, const char *src, size_t nchars )
{
    size_t cpy_len;
    size_t src_len = 0;

    if( src == NULL )
    {
        goto exit_gracefully;
    }

    // while nchars doesn't strictly apply to src, we're not going to
    // be able to copy more than that many characters from it anyway...

    src_len = osi_strnlen( src, nchars );

    if( dst == NULL )
    {
        goto exit_gracefully;
    }

    if (nchars > 0)
    {
        cpy_len = MINIMUM( src_len, ( nchars - 1 ) );
        memcpy( dst, src, cpy_len );

        dst[cpy_len] = '\0';
    }

exit_gracefully:

    //note: this is src because we're returning how much
    //we would have written, not how much we did write.

    return src_len;
}


size_t osi_strlcat( char *dst, const char *src, size_t nchars )
{
    size_t orig_dst_len = 0;
    size_t src_len = 0;
    size_t result = nchars;
    size_t space_left, cpy_len;
    char * cpy_start;

    if (!dst)
    {
        goto exit_gracefully;
    }

    //from the openbsd strlcat manpage:
    // Note however, that if strlcat() traverses size characters without finding
    // a NUL, the length of the string is considered to be size and the destina-
    // tion string will not be NUL-terminated (since there was no space for the
    // NUL).  This keeps strlcat() from running off the end of a string.  In
    // practice this should not happen (as it means that either size is incor-
    // rect or that dst is not a proper ``C'' string).  The check exists to pre-
    // vent potential security problems in incorrect code.

    orig_dst_len = osi_strnlen(dst, nchars);

    if (!src)
    {
        goto exit_gracefully;
    }

    // while nchars doesn't strictly apply to src, we're not going to
    // be able to copy more than that many characters from it anyway...

    src_len = osi_strnlen(src,nchars);
    space_left = nchars - orig_dst_len;

    if (space_left > 0)
    {
        cpy_start = dst + orig_dst_len;
        cpy_len = MINIMUM(src_len, space_left - 1);
        memcpy(cpy_start, src, cpy_len);
        cpy_start[cpy_len] = '\0';

        //remember, how big the write *would* have been...

        result = orig_dst_len + src_len;
    }

exit_gracefully:
    return result;
}


size_t osi_vsnprintf( char *str, size_t nchars,
                      const char * format, va_list ap )
{
    size_t result = nchars;

    if (nchars < 1 || !str || !format)
    {
        goto exit_gracefully;
    }

#ifdef _WIN32
    result = _vsnprintf(str,nchars,format,ap);
#else
    result = vsnprintf(str,nchars,format,ap);
#endif /* _WIN32 */

    //some (v)snprintf implmentations don't write a NUL at the end
    //of the buffer if it is overrun. So, ensure that the last char
    //in the buffer is NUL so that we can check if it was overwritten
    //by snprintf.

    str[nchars - 1] = '\0';

    //some (v)snprintf implementations return -1 on overrun. instead,
    //return act like the C99 standard: overrun means
    //return val is greater than or equal to the buffersize. However,
    //C99 says snprintf should return how much buffer space *would*
    //have been needed. We can't easily calculate that if the underlying
    //snprintf doesn't do it for us.

    if (result < 0)
    {
        result = nchars;
    }

exit_gracefully:
    return result;
}


size_t osi_snprintf( char *str, size_t nchars, const char *format, ... )
{
    size_t result;
    va_list args;

    va_start(args,format);
    result = osi_vsnprintf(str,nchars,format,args);
    va_end(args);

    return result;
}

/******************************************************************************
**
**    Function: osi_fopen
**
**    Purpose:  safe wrapper around fopen.
**
******************************************************************************/

FILE *osi_fopen( const char *file_name, const char *flags, mode_t mode )
{
    FILE *file = NULL;

    int fd     = 0;
    int open_flags;

    struct stat pre_stats;
    struct stat post_stats;

    osi_bool create = FALSE;

    if( file_name == NULL || flags == NULL )
    {
        return NULL;
    }

#ifdef WIN32
    if( stat( file_name, &pre_stats ) != 0 )
    {
        create = TRUE;
    }
#else
    if( lstat( file_name, &pre_stats ) != 0 )
    {
        create = TRUE;
    }
#endif

    /* make sure the file is a regular file, unless it doesn't exist yet. */

    if( create == FALSE )
    {

#ifdef WIN32
    if( ( pre_stats.st_mode & S_IFMT ) != S_IFREG )
	{
            return NULL;
	}
#else
    if( !S_ISREG( pre_stats.st_mode ) )
	{
            return NULL;
	}
#endif

    }

    if( strcmp( flags, "r" ) == 0 )
    {
        open_flags = O_RDONLY;
        fd = open( file_name, open_flags, mode );
    }

    else if( strcmp( flags, "r+" ) == 0 )
    {
        open_flags = O_RDWR;
        fd = open( file_name, open_flags, mode );
    }

    else if( strcmp( flags, "w" ) == 0 )
    {
        open_flags = ( O_WRONLY | O_TRUNC );

        if( create )
        {
            open_flags = ( open_flags | O_CREAT | O_EXCL );
        }

        fd = open( file_name, open_flags, mode );
    }

    else if( strcmp( flags, "w+" ) == 0 )
    {
        open_flags = ( O_RDWR | O_TRUNC );

        if( create )
        {
            open_flags = ( open_flags | O_CREAT | O_EXCL );
        }

        fd = open( file_name, open_flags, mode );
    }

    else if( strcmp( flags, "a" ) == 0 )
    {
        open_flags = ( O_WRONLY | O_APPEND );

        if( create )
        {
            open_flags = ( open_flags | O_CREAT | O_EXCL );
        }

        fd = open( file_name, open_flags, mode );
    }

    else if( strcmp( flags, "a+" ) == 0 )
    {
        open_flags = ( O_RDWR | O_APPEND );

        if( create )
        {
            open_flags = ( open_flags | O_CREAT | O_EXCL );
        }

        fd = open( file_name, open_flags, mode );
    }

    if( fd <= 0 )
    {
        return NULL;
    }

    if( fstat( fd, &post_stats ) != 0 )
    {
        close(fd);
        return NULL;
    }

    /* here we make sure the inode and device numbers are the     */
    /* same in the file we actually opened, compared to the file  */
    /* we performed the initial lstat() call on.                  */

#ifndef WIN32

    if( create == FALSE )
    {
        if( ( pre_stats.st_ino != post_stats.st_ino ) ||
            ( pre_stats.st_dev != post_stats.st_dev ) )
        {
            close(fd);
            return NULL;
        }
    }

#endif

    return fdopen( fd, flags );
}


/******************************************************************************
**
**    Function: get_file_attribute_string
**
**    Purpose:  get the permissions string for the file in the
**              standard 10 char form -rwxrwxrwx
**
******************************************************************************/

#ifndef WIN32

void get_file_attribute_string( char *buffer, int buffer_size,
                                mode_t attributes )
{
    if( ( buffer != NULL ) && ( buffer_size >= PERMISSIONS_STRING_LENGTH ) )
    {
        memset( buffer, '-', PERMISSIONS_STRING_LENGTH );

        /* first, determine type. */

        if( S_ISREG( attributes ) )
        {
            buffer[0] = '-';
        }

        else if( S_ISCHR( attributes ) )
        {
            buffer[0] = 'c';
        }

        else if( S_ISDIR( attributes ) )
        {
            buffer[0] = 'd';
        }

        else if( S_ISBLK( attributes ) )
        {
            buffer[0] = 'b';
        }

        else if( S_ISLNK( attributes ) )
        {
            buffer[0] = 'l';
        }

        else if( S_ISFIFO( attributes ) )
        {
            buffer[0] = 'p';
        }

        else if( S_ISSOCK( attributes ) )
        {
            buffer[0] = 's';
        }

        else
        {
            buffer[0] = '?';
        }

        /* set fields for user/group/other bits in the string. */

        if ( attributes & S_IRUSR )
        {
            buffer[1] = 'r';
        }

        if ( attributes & S_IWUSR )
        {
            buffer[2] = 'w';
        }

        if( attributes & S_IXUSR )
        {
            if ( attributes & S_ISUID )
            {
                buffer[3] = 's';
            }

            else
            {
                buffer[3] = 'x';
            }
        }

        else if( attributes & S_ISUID )
        {
            buffer[3] = 'S';
        }

        /* set the group bit fields. */

        if ( attributes & S_IRGRP )
        {
            buffer[4] = 'r';
        }

        if ( attributes & S_IWGRP )
        {
            buffer[5] = 'w';
        }

        if( attributes & S_IXUSR )
        {
            if ( attributes & S_ISGID )
            {
                buffer[6] = 's';
            }

            else
            {
                buffer[6] = 'x';
            }
        }

        else if( attributes & S_ISGID )
        {
            buffer[6] = 'S';
        }


        /* set the other bit fields. */

        if( attributes & S_IROTH )
        {
            buffer[7] = 'r';
        }

        if( attributes & S_IWOTH )
        {
            buffer[8] = 'w';
        }

#ifdef S_ISVTX

        if( attributes & S_IXOTH )
        {
            if( attributes & S_ISVTX )
            {
                buffer[9] = 't';
            }

            else
            {
                buffer[9] = 'x';
            }
        }

        else if( attributes & S_ISVTX )
        {
            buffer[9] = 'T';
        }

#else
        if( attributes & S_IXOTH )
        {
            buffer[9] = 'x';
        }
#endif
        buffer[10] = '\0';
    }
}

#endif

/******************************************************************************
**
**    Function: byte_swap64
**
**    Purpose:  swap byte order in a 64 bit integer, for use
**              in NTOHLL and HTONLL macros.
**
******************************************************************************/

osi_uint64 byte_swap64( osi_uint64 value )
{
    int index;
    int length = 8;

    osi_uint64 swapped = value;
    unsigned char *q = (unsigned char *)&swapped;

    for( index = 0; ( index < (length/2) ); index++ )
    {
        unsigned char temp = q[index];
        q[index] = q[length - index - 1];
        q[length - index - 1] = temp;
    }

    return swapped;
}


/******************************************************************************
**
**    Function: osi_sleep
**
**    Purpose:  wrapper for sleep function, argument in seconds.
**
******************************************************************************/

void osi_sleep( int seconds )
{
#ifdef WIN32
    Sleep( seconds * 1000 );
#else
    sleep( seconds );
#endif
}

/******************************************************************************
**
**    Function: osi_get_time
**
**    Purpose:  wrapper for gettimeofday function, returns microseconds.
**
******************************************************************************/

long osi_get_time()
{
    long result = -1;

#ifdef WIN32

    time_t current_time;

    time( &current_time );
    result = (long)current_time;

#else

    struct timeval current_time;

    if( gettimeofday( &current_time, NULL ) == 0 )
    {
        result = current_time.tv_sec;
    }

#endif

    return result;
}


/******************************************************************************
**
**    Function: osi_time_to_string
**
**    Purpose:  use ctime to turn a long into a char * representation
**              of a time value, and remove the newline character from
**              the end of the string.
**
******************************************************************************/

char * osi_time_to_string( long time )
{
    static char time_buffer[25];
    time_t time_value = time;
    char *time_string = (char *)ctime( &time_value );

    time_buffer[0] = '\0';

    if( time_string != NULL )
    {
        osi_strlcpy( time_buffer, time_string, sizeof( time_buffer ) );
        time_buffer[24] = '\0';
    }

    return time_buffer;
}



/******************************************************************************
**
**    Function: osi_fork_process
**
**    Purpose:  wrapper for creating a new process.
**
******************************************************************************/

int osi_create_process()
{
    int result = -1;

#ifdef WIN32

#else

    result = fork();

    if( result < 0 )
    {
        result = OSI_ERROR_UNABLE_TO_CREATE_PROCESS;
    }

#endif

    return result;
}


/******************************************************************************
**
**    Function: void osi_print_stdout
**
**    Purpose:  log variable argument list to standard output.
**
**    Return:   none.
**
**
******************************************************************************/

void osi_print_stdout( const char *message, ... )
{
    va_list ap;
    va_start( ap, message );

    vfprintf( stdout, message, ap );

#ifdef WIN32
    fprintf( stdout, "\r" );
#endif

    fprintf( stdout, "\n" );

    va_end( ap );
}

/******************************************************************************
**
**    Function: void osi_print_stderr
**
**    Purpose:  log variable argument list to standard error.
**
**    Return:   none.
**
******************************************************************************/

void osi_print_stderr( const char *message, ... )
{
    va_list ap;
    va_start( ap, message );

    vfprintf( stderr, message, ap );
    fprintf( stderr, "\n" );

    va_end( ap );
}



