#ifndef _DWC_PROTO_C
#define _DWC_PROTO_C

/*--------------------------------------------------------------------------
 *  Copyright (c) 2001-2004 Igor Daniloff. 
 *	All rights reserved.
 *
 * By using this file, you agree to the terms and conditions set
 * forth in the LICENSE file which can be found at this directory.
 *------------------------------------------------------------------------*/
 
#ifndef lint
static const char rcsid[] = "$Id: dwc_proto.c,v 1.17 2007/12/17 14:15:03 akorkov Exp $";
#endif

#include <sys/types.h>
#if defined(__WIN32_DW)
#include <winsock.h> /* for ntohX() */
#else /* __WIN32_DW */
#include <unistd.h>
#include <sys/stat.h>
#include <netinet/in.h> /* for ntohX() */
#endif /* __WIN32_DW */
#include <netinet/in.h> /* for ntohX() */
#include "dwc_proto.h"
#include "dwc_lib.h"

#if defined(__GNUC__) && !defined(Solaris_DW) && !defined(EMX_DW)

#define ntohl64(x)  htonl64(x)

/* ---------------------------------------------------------------------- */
uint64_t htonl64( uint64_t x )
{
    uint64_t  y, z;
    uint32_t  y1, z1;

    y = x & 0xFFFFFFFF00000000LL;
    y1 = y >> 32;
    y1 = htonl(y1);
    y = y1;
    z1 = x & 0xFFFFFFFFLL;
    z = htonl(z1);
    z = z << 32;
    return z + y;
}

#endif /* GNUC and not SOLARIS and not EMX */

/* ---------------------------------------------------------------------- */
dwc_bool_t dw_proto_write_int( dwc_ipc_t ipc, dwc_proto_int_t value )
{
    if( !ipc )
    {
        printLib( DW_LL_ERROR, "dwlib: proto::write_int bad parameters was passed" );
        return FALSE;
    }
    value = htonl( value );
    return dw_proto_write_buf( ipc, sizeof(dwc_proto_int_t), (dwc_pointer_t) &value );
}

/* ---------------------------------------------------------------------- */
dwc_bool_t dw_proto_read_int( dwc_ipc_t ipc, dwc_proto_int_t* pvalue )
{
    if( !ipc || !pvalue )
    {
        printLib( DW_LL_ERROR, "dwlib: proto::read_int bad parameters (%d.%d) were passed", !ipc, !pvalue );
        return FALSE;
    }
    if( !dw_proto_read_buf( ipc, sizeof(dwc_proto_int_t), pvalue ) )
        return FALSE;
    *pvalue = ntohl((*pvalue));
    return TRUE;
}

#if defined(__GNUC__) && !defined(Solaris_DW) && !defined(EMX_DW)

/* ---------------------------------------------------------------------- */
dwc_bool_t dw_proto_write_int64( dwc_ipc_t ipc, dwc_proto_int64_t value )
{
    if( !ipc )
    {
        printLib( DW_LL_ERROR, "dwlib: proto::write_int64 bad parameters was passed" );
        return FALSE;
    }
    value = htonl64(value);
    return dw_proto_write_buf( ipc, sizeof(dwc_proto_int64_t), (dwc_pointer_t) &value );
}

/* ---------------------------------------------------------------------- */
dwc_bool_t dw_proto_read_int64( dwc_ipc_t ipc, dwc_proto_int64_t* pvalue )
{
    if( !ipc || !pvalue )
    {
        printLib( DW_LL_ERROR, "dwlib: proto::read_int64 bad parameters (%d.%d) were passed", !ipc, !pvalue );
        return FALSE;
    }
    if( !dw_proto_read_buf( ipc, sizeof(dwc_proto_int64_t), pvalue ) )
        return FALSE;
    *pvalue = ntohl64((*pvalue));
    return TRUE;
}

#endif /* GNUC and not SOLARIS and not EMX */

/* ---------------------------------------------------------------------- */
dwc_bool_t dw_proto_write_bytestring( dwc_ipc_t ipc, dwc_length_t buf_len, const char* buf )
{
    if( !ipc )
    {
        printLib( DW_LL_ERROR, "dwlib: proto::write_bytestring bad parameter was passed" );
        return FALSE;
    }
    if( !buf )
        return dw_proto_write_int( ipc, 0 );
    if( buf_len < 0 )
    {
        printLib( DW_LL_ERROR, "dwlib: proto::write_bytestring bad length was passed" );
        return FALSE;    
    }
    if( !dw_proto_write_int( ipc, buf_len ) )
        return FALSE;
    if( 0 == buf_len )
        return TRUE;
    return dw_proto_write_buf( ipc, buf_len, buf );
}

/* ---------------------------------------------------------------------- */
dwc_bool_t dw_proto_read_bytestring( dwc_ipc_t ipc, dwc_length_t* pbuf_len, dwc_proto_bytestring_t* pbuf, dwc_bool_t add_null )
{    
    dwc_proto_int_t size;

    if( !ipc || !pbuf || !pbuf_len )
    {
        printLib( DW_LL_ERROR, "dwlib: proto::read_bytestring bad parameters (%d.%d.%d) were passed", !ipc, !pbuf_len, !pbuf );
        return FALSE;
    }
    *pbuf_len = 0;
    *pbuf = NULL;
    if( !dw_proto_read_int( ipc, &size ) )
        return FALSE;
    if( (0 == size) && !add_null )
    {
        printLib( DW_LL_DEBUG, "dwlib: proto::read_bytestring null bytestring readed" );
        return TRUE;
    }
    *pbuf_len = size;
    if( add_null )
    {
        size += 1;
    }
    *pbuf = DWC_Malloc( dwc_membuf_t, size );        
    if( !*pbuf )
    {
        printLib( DW_LL_ERROR, "dwlib: proto::read_bytestring cannot allocate memory (%d[%#X] bytes) for bytestring", size, size );
        return FALSE;
    }
    if( (*pbuf_len > 0) && !dw_proto_read_buf( ipc, *pbuf_len, *pbuf ) )
    {
        DWC_Free( *pbuf );
        *pbuf_len = 0;
        return FALSE;
    }
    if( add_null )
    {
        (*pbuf)[*pbuf_len] = NULLCHAR;
        (*pbuf_len) += 1;
    }
    return TRUE;
}

/* ---------------------------------------------------------------------- */
dwc_bool_t dw_proto_write_buf( dwc_ipc_t ipc, dwc_length_t buf_len, const char* buf )
{
    if( !ipc || !buf || (buf_len < 0) )
    {
        printLib( DW_LL_ERROR, "dwlib: proto::write_buf bad parameters (%d.%d.%d) were passed", !ipc, !buf, buf_len < 0 );
        return FALSE;
    }
    return ( DWC_NOERROR == send_ipc( ipc, buf, &buf_len ) ? TRUE : FALSE );
}

/* ---------------------------------------------------------------------- */
dwc_bool_t dw_proto_read_buf( dwc_ipc_t ipc, dwc_length_t buf_len, dwc_pointer_t buf )
{
    if( !ipc || !buf || (buf_len < 0) )
    {
        printLib( DW_LL_ERROR, "dwlib: proto::read_buf bad parameters (%d.%d.%d) were passed", !ipc, !buf, buf_len < 0 );
        return FALSE;
    }
    return ( DWC_NOERROR == recv_ipc( ipc, buf, &buf_len ) ? TRUE : FALSE );
}

/* ------------------------------------------------------------------------ */
dwc_bool_t dw_proto_write_list( dwc_ipc_t ipc, dwc_multistring_t list, dwc_bool_t add_null )
{
    dwc_string_t    str;
    dwc_length_t    count, len;
    
    if( !ipc )
    {
        printLib( DW_LL_ERROR, "dwlib: proto::write_list bad parameter was passed" );
        return FALSE;
    }    
    if( !list )
    {
        return dw_proto_write_int( ipc, 0 );
    }
    count = dw_multistring_count( list );
    printLib( DW_LL_DEBUG, "dwlib: proto::write_list %d strings should be written", count );
    if( !dw_proto_write_int( ipc, count ) )
    {
        return FALSE;
    }
    dw_multistring_rewind( list, DW_SD_FORWARD );
    while( NULL != (str = dw_multistring_getnext( list ) ) )
    {
        len = strlen(str);
        if( add_null )
        {
            ++len;
        }
        if( !dw_proto_write_bytestring( ipc, len, str ) )
        {
            return FALSE;
        }
        --count;
    }
    return count == 0;
}

/* ------------------------------------------------------------------------ */
dwc_bool_t dw_proto_read_list( dwc_ipc_t ipc, dwc_combiner_proc_t combiner, dwc_pointer_t arg_3, dwc_pointer_t arg_4 )
{
    dwc_proto_int_t    line_num;
    dwc_length_t       line_len;
    dwc_membuf_t       line = NULL;
    int                i;

    if( !ipc || !combiner )
    {
        printLib( DW_LL_ERROR, "dwlib: proto::read_list bad parameters (%d.%d) were passed", !ipc, !combiner );
        return FALSE;
    }    
    if( !dw_proto_read_int( ipc, &line_num ) )
    {
        return FALSE;
    }    
    printLib( DW_LL_DEBUG, "dwlib: proto::read_list %d strings should be read", line_num );
    if( line_num < 1 )
    {
        return TRUE;
    }
    for( i = 0; i < line_num; ++i )
    {
        if( !dw_proto_read_bytestring( ipc, &line_len, &line, TRUE ) )
        {
            return FALSE;
        }
        if( line && !combiner( line_len, line, arg_3, arg_4 ) )
        {
            printLib( DW_LL_ERROR, "dwlib: proto::read_list combine routine failed on line[size=%d]=%s", line_len, line );
            DWC_Free( line );
            return FALSE;
        }
        DWC_Free( line );
    }
    return TRUE;
}   

/* ------------------------------------------------------------------------ */
dwc_bool_t dw_proto_write_stat( DWC_ipc_t ipc, dwc_scan_stat_t* stat )
{
    if( !stat )
    {
        printLib( DW_LL_ERROR, "dwlib: proto::write_stat bad parameters was passed" );
        return FALSE;
    }
        // asv: rewrite this - send only non-zero values.
    printLib( DW_LL_DEBUG, "dwlib: proto::write_stat %d pairs should be written", 9 );
    if( !dw_proto_write_int( ipc, 9 ) /* num pairs (what:count) */
        ||
        !dw_proto_write_int( ipc, DERR_KNOWN_VIRUS )  /* 1 */
        ||
        !dw_proto_write_int( ipc, stat->dw_infected ) 
        ||
        !dw_proto_write_int( ipc, DERR_VIRUS_MODIFICATION ) /* 2 */
        ||
        !dw_proto_write_int( ipc, stat->dw_modifications ) 
        ||
        !dw_proto_write_int( ipc, DERR_UNKNOWN_VIRUS ) /* 3 */
        ||
        !dw_proto_write_int( ipc, stat->dw_suspicious ) 
        ||
        !dw_proto_write_int( ipc, DERR_SKIP_OBJECT ) /* 4 */
        ||
        !dw_proto_write_int( ipc, stat->dw_skipped ) 
        ||
        !dw_proto_write_int( ipc, DERR_ARCHIVE_RESTRICTION ) /* 5 */
        ||
        !dw_proto_write_int( ipc, stat->dw_archive_restriction ) 
        ||
        !dw_proto_write_int( ipc, DERR_FILTER_REJECT ) /* 6 */
        ||
        !dw_proto_write_int( ipc, stat->dw_filter_alert ) 
        ||
        !dw_proto_write_int( ipc, DERR_DAEMON_ERROR ) /* 7 */
        ||
        !dw_proto_write_int( ipc, stat->dw_error ) 
        ||
        !dw_proto_write_int( ipc, DERR_HAVE_CURED ) /* 8 */
        ||
        !dw_proto_write_int( ipc, stat->dw_cured ) 
        ||
        !dw_proto_write_int( ipc, DERR_EVAL_KEY ) /* 9 */
        ||
        !dw_proto_write_int( ipc, stat->dw_eval_key ) )
    {
        printLib( DW_LL_ERROR, "dwlib: proto::write_stat - protocol error" );
        return FALSE;
    }
    return TRUE;
}

/* ------------------------------------------------------------------------ */
dwc_bool_t dw_proto_read_stat( DWC_ipc_t ipc, dwc_scan_stat_t* stat )
{
    int                 i;
    dwc_proto_int_t     codes_num = 0, what = 0, value = 0;

    EXIT_IF_1PARAM( !stat, "dwlib: proto::read_stat", return FALSE );
    
    if( !dw_proto_read_int( ipc, &codes_num ) )
    {
        return FALSE;
    }
    printLib( DW_LL_DEBUG, "dwlib: proto::read_stat %d pairs should be read", codes_num );
    stat->dw_scanned += 1;
    for( i = 0; i < codes_num; ++i )
    {
        if( !dw_proto_read_int( ipc, &what )
            ||
            !dw_proto_read_int( ipc, &value ) )
        {
            printLib( DW_LL_ERROR, "dwlib: proto::read_stat cannot read pair #%d", i );
            return FALSE;
        }
        printLib( DW_LL_DEBUG, "dwlib: proto::read_stat (%#X:%d)", what, value );
        if( dw_bm_contain( DERR_KNOWN_VIRUS, what ) )
        {
            stat->dw_infected += value;
        }
        else if( dw_bm_contain( DERR_VIRUS_MODIFICATION, what ) )
        {
            stat->dw_modifications += value;
        }
        else if( dw_bm_contain( DERR_UNKNOWN_VIRUS, what ) )
        {
            stat->dw_suspicious += value;
        }
        else if( dw_bm_contain( DERR_SKIP_OBJECT, what ) )
        {
            stat->dw_skipped += value;
        }
        else if( dw_bm_contain( DERR_ARCHIVE_RESTRICTION, what ) )
        {
            stat->dw_archive_restriction += value;
        }
        else if( dw_bm_contain( DERR_FILTER_REJECT, what ) )
        {
            stat->dw_filter_alert += value;            
        }
        else if( dw_bm_contain( DERR_DAEMON_ERROR, what ) )
        {
            stat->dw_error += value;
        }
        else if( dw_bm_contain( DERR_HAVE_CURED, what ) )
        {
            stat->dw_cured += value;
        }
        else if( dw_bm_contain( DERR_HAVE_DELETED, what ) )
        {
            stat->dw_deleted += value;
        }
        else if( dw_bm_contain( DERR_EVAL_KEY, what ) )
        {
            stat->dw_eval_key += value;
        }
        else if( dw_bm_contain( DERR_ADWARE, what ) )
        {
            stat->dw_adware += value;
        }
        else if( dw_bm_contain( DERR_DIALER, what ) )
        {
            stat->dw_dialer += value;
        }
        else if( dw_bm_contain( DERR_JOKE, what ) )
        {
            stat->dw_joke += value;
        }
        else if( dw_bm_contain( DERR_RISKWARE, what ) )
        {
            stat->dw_riskware += value;
        }
        else if( dw_bm_contain( DERR_HACKTOOL, what ) )
        {
            stat->dw_hacktool += value;
        }
    }
    return TRUE;
}

/* ------------------------------------------------------------------------ */
dwc_string_t dw_proto_read_report( dwc_ipc_t ipc, dwc_length_t size, dwc_const_path_t del_path )
{
    dwc_string_t       retstr = NULL;
    dwc_multistring_t  rlist = NULL;
       
    if( !ipc )
    {
        printLib( DW_LL_ERROR, "dwlib: proto::read_report bad parameter was passed", !ipc );
        return NULL;
    }    
    rlist = dw_multistring_create( size );
    if( !rlist )
    {
        printLib( DW_LL_ERROR, "dwlib: proto::read_report cannot allocate report list" );
        return NULL;
    }
    retstr = NULL;
    if( del_path )
    {
        if( dw_proto_read_list( ipc, dw_combiner_add_multistring_and_delete_path, (dwc_pointer_t) rlist, (dwc_pointer_t) del_path ) )
        {
            retstr = dw_multistring_concat( rlist, DW_SD_FORWARD );
        }
    }
    else
    {
        if( dw_proto_read_list( ipc, dw_combiner_add_multistring, (dwc_pointer_t) rlist, NULL ) )
        {
            retstr = dw_multistring_concat( rlist, DW_SD_FORWARD );
        }
    }
    dw_multistring_delete( rlist );
    return retstr;
}


/* ------------------------------------------------------------------------ */
dwc_bool_t dw_combiner_add_multistring_and_delete_path( dwc_length_t line_len, dwc_membuf_t line, dwc_pointer_t arg_list, dwc_pointer_t arg_path )
{
    dwc_multistring_t  list = (dwc_multistring_t) arg_list;
    dwc_path_t         path = (dwc_path_t) arg_path;
    dwc_string_t       slash = NULL;
    dwc_string_t       path_begin = NULL;
    int                slashoff;

    if( (line_len < 0) || !line || !list || !path )
    {
        printLib( DW_LL_ERROR, "dwlib: combiner::add_multistring_and_delete_path bad parameters (%d.%d.%d.%d) were passed", (line_len < 0), !line, !list, !path );
        return FALSE;
    }
    slashoff = 0;
    if( (NULL != (slash = strrchr( path, PATH_SEPARATOR )))
#if defined(DW_HAVE_SEPARATOR_2)
        ||
        (NULL != (slash = strrchr( path, PATH_SEPARATOR_2 )))
#endif /* HAVE_SEPARATOR_2 */        
        )
    {
        slashoff = slash - path + 1;
    }
    slash = line;
    if( (slashoff > 0) && (slashoff < line_len) )
    {
        path_begin = strstr( line, path );
        if( path_begin )
        {
            if( path_begin != line )
            {
                /* need copy line before path */
                path_begin[0] = NULLCHAR;
                dw_multistring_add( list, line );
            }
            if( (slashoff-1) < strlen(path_begin+1) )
            {
                slash = path_begin + slashoff;
            }
            else
            {
                slash = path_begin;
            }
        }
    }        
    dw_multistring_add( list, slash );
    return TRUE;
}

/* ------------------------------------------------------------------------ */
dwc_bool_t dw_combiner_add_multistring( dwc_length_t line_len, dwc_membuf_t line, dwc_pointer_t arg_list, dwc_pointer_t ignore )
{
    return dw_multistring_add( (dwc_multistring_t) arg_list, line );
}

/* ------------------------------------------------------------------------ */
dwc_bool_t dw_combiner_parse_viruses( dwc_length_t line_len, dwc_membuf_t line, dwc_pointer_t arg_struct, dwc_pointer_t ignore )
{
    dwc_viruses_t      *virs = (dwc_viruses_t*) arg_struct;
    dwc_multistring_t  *list = NULL;
    dwc_string_t        sptr = NULL;

    if( (line_len < 0) || !line || !virs )
    {
        printLib( DW_LL_ERROR, "dwlib: combiner::add_multistring_and_delete_path bad parameters (%d.%d.%d) were passed", (line_len < 0), !line, !virs );
        return FALSE;
    }
    sptr = strrchr( line, ',' );
    if( !sptr )
    {
        printLib( DW_LL_ERROR, "dwlib: combiner::parse_viruses line malformed - [%d]%s", line_len, line );
        return FALSE;
    }
    ++sptr;
    switch( *sptr )
    {
        case 'K':
            list = &virs->known;
            break;
        case 'M':
            list = &virs->modification;
            break;
        case 'S':
            list = &virs->suspicious;
            break;
        case 'A':
            list = &virs->adware;
            break;
        case 'D':
            list = &virs->dialer;
            break;
        case 'J':
            list = &virs->joke;
            break;
        case 'R':
            list = &virs->riskware;
            break;
        case 'H':
            list = &virs->hacktool;
            break;
        default:
            printLib( DW_LL_ERROR, "dwlib: combiner::parse_viruses unknown infection type '%c' in line - [%d]%s", *sptr, line_len, line );
            return FALSE;
    }
    if( !(*list) )
    {
        (*list) = dw_multistring_create(0);
        if( !(*list) )
        {
            printLib( DW_LL_ERROR, "dwlib: combiner::parse_viruses no memory" );
            return FALSE;
        }
    }
    --sptr;
    *sptr = NULLCHAR;
    printLib( DW_LL_DEBUG, "dwlib: combiner::parse_viruses to list '%c' add virus %s", sptr[1], line );
    return dw_multistring_add( *list, line );
}

#endif /* _DWC_PROTO_C */
