#ifndef _DWC_LIB_C
#define _DWC_LIB_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_lib.c,v 1.106.2.2 2009/10/29 18:36:36 akorkov Exp $";
#endif

#include <sys/types.h>
#if defined(__WIN32_DW)
#include <io.h>
#else /* __WIN32_DW */
#include <unistd.h>
#include <sys/stat.h>
#include <sys/param.h> /* for PATH_MAX */
#endif /* __WIN32_DW */
#include <stdio.h>
#include <stddef.h>
#include <stdarg.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <syslog.h>

#include "dwc_lib.h"
#include "dwc_type.h"
#include "dwc_ipc.h"
#include "dwc_macro.h"
#include "dwc_err.h"
#include "dwc_log.h"
#include "dwc_mutex.h"
#include "dwc_proto.h"
#include "dwc_addrlist.h"
#include "dwc_strings.h"

#define MAJOR_LIBVERSION( v )      (((v)>>8) & 0xFF)
#define MINOR_LIBVERSION( v )      ((v) & 0xFF)

struct dw_scan_result_t {
    dwc_proto_int_t     status;          /* see dwc_scan.h */
    dwc_string_t        filter_rule;     /* DRWEBD_RETURN_FILTER_RULE */
    dwc_string_t        report;          /* DRWEBD_RETURN_REPORT */
    dwc_multistring_t   viruslist;       /* DRWEBD_RETURN_VIRUSES */
    dwc_viruses_t       viruses;         /* DRWEBD_RETURN_SHORT_VIRUSES */
    dwc_scan_stat_t     stat;            /* DRWEBD_RETURN_CODES */
    dwc_spam_stat_t	spam;		 /* DRWEBD_ANTISPAM_ON */
    dwc_path_t          quarantine_name; /* quarantine name */
    dwc_membuf_t        cured_file;
    dwc_length_t        cured_file_length;
};

typedef struct {
    dwc_bool_t             inited;
    dwc_addrlist_t         addresses_list;
} DWC_lib_settings_t;

static DWC_msg_entry_t  DaemonMsgTable[] = {
    { DERR_READ_ERR,           "read error" },
    { DERR_WRITE_ERR,          "write error" },
    { DERR_NOMEMORY,           "no memory for operation" },
    { DERR_CRC_ERROR,          "CRC error (archive with wrong CRC)" },
    { DERR_READSOCKET,         "socket error" },
    { DERR_KNOWN_VIRUS,        "known virus is found" },
    { DERR_UNKNOWN_VIRUS,      "possible unknown virus is found" },
    { DERR_VIRUS_MODIFICATION, "virus modification is found" },
    { DERR_HAVE_CURED,         "one (or more) object(s) has been cured" },
    { DERR_TIMEOUT,            "operation timed out" },
    { DERR_SYMLINK,            "symlink is found" },
    { DERR_NO_REGFILE,         "no regular file is found" },
    { DERR_SKIPPED,            "skipped object" },
    { DERR_TOO_BIG,            "too big file after extraction" },
    { DERR_TOO_COMPRESSED,     "too high compression ratio" },
    { DERR_EVAL_KEY,           "evaluation key is used" },
    { DERR_BAD_CALL,           "invalid call" },
    { DERR_FILTER_REJECT,      "rejected by rule filter" },
    { DERR_ARCHIVE_LEVEL,      "too big nesting level in archive" },
    { DERR_HAVE_DELETED,       "one (or more) object(s) has been deleted" },
    { DERR_IS_CLEAN,           "after scanning/curing composite object is clean" },
    { DERR_LICENSE_ERROR,      "object was not checked due to the license key limitations" },
    { DERR_ADWARE,             "an adware is found" },
    { DERR_DIALER,             "a dialer is found" },
    { DERR_JOKE,               "a joke(hoax) is found" },
    { DERR_RISKWARE,           "a riskware is found" },
    { DERR_HACKTOOL,           "a hacktool is found" },
    END_OF_TABLE
};



static const int  DaemonMsgTableSize = (sizeof(DaemonMsgTable) / sizeof(DWC_msg_entry_t)) - 1;

static DWC_lib_settings_t   Lib;

static int                  fillResult( DWC_ipc_t ipc, int ScanOptions, dwc_const_string_t path, dwc_scan_result_t *pResult, int ReportMaxSize, dwc_bool_t isLocalScan );

FILE * logoutput = NULL;

dwc_bool_t dwc_use_full_names_for_report = FALSE;

/* ----------------------------------------------------------------- */
dwc_scan_result_t createResult( void )
{
    dwc_scan_result_t  sr;
    
    sr = DWC_Malloc( struct dw_scan_result_t*, sizeof(struct dw_scan_result_t) );
    if( !sr )
    {
        printLib( DW_LL_ERROR, "dwlib: %s(%d): no memory for results", __FILE__, __LINE__ );
        return NULL;
    }
    sr->status = DERR_NOERROR;
    sr->report = NULL;
    sr->filter_rule = NULL;
    sr->viruslist = NULL;
    sr->cured_file = NULL;
    sr->cured_file_length = 0;
    sr->quarantine_name = NULL;
    sr->viruses.known = NULL;
    sr->viruses.suspicious = NULL;
    sr->viruses.modification = NULL;
    sr->viruses.adware = NULL;
    sr->viruses.dialer = NULL;
    sr->viruses.joke = NULL;
    sr->viruses.riskware = NULL;
    sr->viruses.hacktool = NULL;
    memset( &sr->stat, 0, sizeof(sr->stat) );
    return sr;
}

/* ----------------------------------------------------------------- */
int setResultQuarantine( dwc_scan_result_t Res, dwc_path_t name )
{
    if( Res )
    {
        if( name )
        {
            if( Res->quarantine_name )
            {
                printLib( DW_LL_DEBUG, "dwlib: try to reset quarantine name in scanresult" );
                DWC_Free( Res->quarantine_name );
            }
            Res->quarantine_name = DWC_Strdup( name );
            if( !Res->quarantine_name )
            {
                printLib( DW_LL_ERROR, "dwlib: no memory for filename (quarantine)" );
                return FALSE;
            }
        }
        return TRUE;
    }
    return FALSE;
}

/* ----------------------------------------------------------------- */
int setResultStatus( dwc_scan_result_t Res, int code )
{
    if( Res )
    {
        Res->status = code;
        return TRUE;
    }
    return FALSE;
}

/* ----------------------------------------------------------------- */
int setResultReport( dwc_scan_result_t Res, char* Report )
{
    char* tmp = NULL;
    
    if( Res  )
    {
        if( Report )
        {
            tmp = DWC_Strdup( Report );
            if( !tmp )
            {
                printLib( DW_LL_ERROR, "dwlib: %s(%d): no memory for report", __FILE__, __LINE__ );
                return FALSE;
            }
        }
        DWC_Free( Res->report );
        Res->report = tmp;
        return TRUE;
    }
    return FALSE;
}

/* ----------------------------------------------------------------- */
int getResultStatus( dwc_scan_result_t Res )
{
    if( Res )
    {
        return Res->status;
    }
    else 
    {
        printLib( DW_LL_VERBOSE, "dwlib: try to get status from NULL pointer" );
        return DERR_BAD_CALL;
    }
}

/* ----------------------------------------------------------------- */
char* getResultQuarantine( dwc_scan_result_t Res )
{
    if( Res )
    {
        if( Res->quarantine_name )
        {
            return Res->quarantine_name;
        }
        else
        {
            printLib( DW_LL_VERBOSE, "dwlib: quarantine name was not set" );
            return NULL;
        }
    }
    else
    {
        return NULL;
    }    
}

/* ----------------------------------------------------------------- */
char* getResultCuredBuffer( dwc_scan_result_t Res )
{
    if( Res )
    {
        if( Res->cured_file )
        {
            return Res->cured_file;
        }
        else
        {
            return NULL;
        }
    }
    else
    {
        return NULL;
    }    
}

/* ----------------------------------------------------------------- */
int getResultCuredLength( dwc_scan_result_t Res )
{
    if( Res )
    {
        if( Res->cured_file )
        {
            return Res->cured_file_length;
        }
        else
        {
            return 0;
        }
    }
    else
    {
        return 0;
    }    
}

/* ----------------------------------------------------------------- */
char* getResultReport( dwc_scan_result_t Res )
{
    if( Res )
    {
        if( Res->report )
        {
            return Res->report;
        }
        else
        {
            return "DrWeb Daemon report was not requested in this configuration.";
        }
    }
    else
    {
        return "FATAL ERROR: null pointer passed";
    }    
}

/* ----------------------------------------------------------------- */
char* getResultFilterReport( dwc_scan_result_t Res )
{
    if( Res )
    {
        if( Res->filter_rule )
        {
            return Res->filter_rule;
        }
        else
        {
            return NULL;
        }
    }
    else
    {
        return NULL;
    }    
}

/* ----------------------------------------------------------------- */
dwc_multistring_t getResultViruses( dwc_scan_result_t Res )
{
    if( Res )
    {
        return Res->viruslist;
    }
    else 
    {
        return NULL;
    }
}

/* ----------------------------------------------------------------- */
dwc_viruses_t* getResultTypedViruses( dwc_scan_result_t Res )
{
    if( Res )
    {
        return &Res->viruses;
    }
    else 
    {
        return NULL;
    }
}

/* ----------------------------------------------------------------- */
dwc_scan_stat_t* getResultStat( dwc_scan_result_t Res )
{
    if( Res )
    {
        return &Res->stat;
    }
    else 
    {
        return NULL;
    }
}

/* ----------------------------------------------------------------- */
dwc_spam_stat_t* getResultSpam( dwc_scan_result_t Res )
{
    if( Res )
    {
        return &Res->spam;
    }
    else 
    {
        return NULL;
    }
}

/* ----------------------------------------------------------------- */
#define PRINT_STAT( st, nm )\
BEGIN_MACRO\
    printLib( LogLevel, "dwlib: stat: %s : %d", nm, st );\
END_MACRO    

/* ----------------------------------------------------------------- */
dwc_void_t printScanStat( int LogLevel, dwc_scan_stat_t* pStat )
{
    if( LogLevel < DW_LL_VERBOSE )
        return;
    if( !pStat )
        return;
    printLib( DW_LL_VERBOSE, "dwlib: Statistic:" );
    PRINT_STAT( pStat->dw_scanned, "Scanned objects" );
    PRINT_STAT( pStat->dw_infected, "Known viruses" );
    PRINT_STAT( pStat->dw_modifications, "Modifications" );
    PRINT_STAT( pStat->dw_suspicious, "Suspicious" );
    PRINT_STAT( pStat->dw_adware, "Advertising software" );
    PRINT_STAT( pStat->dw_dialer, "Dialer program" );
    PRINT_STAT( pStat->dw_joke, "Joke(Hoax) program" );
    PRINT_STAT( pStat->dw_riskware, "Potentially dangerous software" );
    PRINT_STAT( pStat->dw_hacktool, "Intrusion tool" );
    PRINT_STAT( pStat->dw_cured, "Cured" );
    PRINT_STAT( pStat->dw_deleted, "Deleted" );
    PRINT_STAT( pStat->dw_filter_alert, "FilterRule Reject" );
    PRINT_STAT( pStat->dw_skipped, "Skipped object" );
    PRINT_STAT( pStat->dw_archive_restriction, "Archive restriction" );
    PRINT_STAT( pStat->dw_error, "Scanning error" );
    return;
}

/* ----------------------------------------------------------------- */
#define APPEND_STAT( st, nm )\
BEGIN_MACRO\
    if( (st) > 0 )\
    {\
        if( -1 == snprintf( buf, sizeof(buf)-1, "%s : %d\n", (nm), (st) ) )\
            goto error_exit;\
        buf[sizeof(buf)-1] = NULLCHAR;\
        if( !dw_multistring_add( tmp, buf ) )\
            goto error_exit;\
    }\
END_MACRO    

/* ----------------------------------------------------------------- */
char* convertStatToString( dwc_scan_stat_t* pStat )
{
    dwc_multistring_t   tmp = NULL;
    dwc_string_t        str;
    char                buf[64];
    
    if( !pStat )
        return NULL;
    tmp = dw_multistring_create( 0 );
    if( !tmp )
        return NULL;
    if( pStat->dw_eval_key > 0 )
    {
        if( !dw_multistring_add( tmp, "Evaluation key used !\n" ) )
            goto error_exit;
    }
    APPEND_STAT( pStat->dw_infected, "Known viruses" );
    APPEND_STAT( pStat->dw_modifications, "Modifications" );
    APPEND_STAT( pStat->dw_suspicious, "Suspicious" );
    APPEND_STAT( pStat->dw_adware, "Advertising software" );
    APPEND_STAT( pStat->dw_dialer, "Dialer program" );
    APPEND_STAT( pStat->dw_joke, "Joke(Hoax) program" );
    APPEND_STAT( pStat->dw_riskware, "Potentially dangerous software" );
    APPEND_STAT( pStat->dw_hacktool, "Intrusion tool" );
    APPEND_STAT( pStat->dw_cured, "Cured" );
    APPEND_STAT( pStat->dw_deleted, "Deleted" );
    APPEND_STAT( pStat->dw_filter_alert, "FilterRule Reject" );
    APPEND_STAT( pStat->dw_skipped, "Skipped object" );
    APPEND_STAT( pStat->dw_archive_restriction, "Archive restriction" );
    APPEND_STAT( pStat->dw_error, "Scanning error" );
    str = dw_multistring_concat( tmp, DW_SD_FORWARD );
    dw_multistring_delete( tmp );
    return str;
    
  error_exit:
    dw_multistring_delete( tmp );
    return NULL;    
}

/* ----------------------------------------------------------------- */
int clearResult( dwc_scan_result_t Res )
{
    if( Res )
    {
        dw_multistring_delete( Res->viruses.hacktool );
        dw_multistring_delete( Res->viruses.riskware );
        dw_multistring_delete( Res->viruses.joke );
        dw_multistring_delete( Res->viruses.dialer );
        dw_multistring_delete( Res->viruses.adware );
        dw_multistring_delete( Res->viruses.modification );
        dw_multistring_delete( Res->viruses.suspicious );
        dw_multistring_delete( Res->viruses.known );
        dw_multistring_delete( Res->viruslist );
        DWC_Free( Res->quarantine_name );
        DWC_Free( Res->cured_file );
        DWC_Free( Res->filter_rule );
        DWC_Free( Res->report );
        DWC_Free( Res );
    }
    return DWL_NOERROR;
}

/* ----------------------------------------------------------------- */
void outputLib( char* Location, int Level, int Facility, int Priority, const char* ident )
{
    if( Level > 0 )
    {
        LogEnabled = TRUE;
        LogLevel = Level;
    }
    else
    {
        LogEnabled = FALSE;    
        LogLevel = 0;
    }
    if( !Location )
    {
#if defined(__UNIX_DW)
        LogSyslog = TRUE;
        closelog();
        openlog( ident, LOG_PID, LogFacility );
#else /* not UNIX */
        LogSyslog = TRUE;
        LogEnabled = FALSE;
#endif /* __UNIX_DW */
    }
    else
    {
        LogSyslog = FALSE;
        if( 0 != DWC_strncasecmp( Location, "console", strlen("console") ) )
	    {
		logoutput = fopen(Location,"a");
		if (!logoutput)
		    {
			LogEnabled = FALSE; 
		    }
		else
		    {
			
		    }
	    }
	else 
	    {
		logoutput = stderr;
	    }
    }
    LogFacility = Facility;
    LogPriority = Priority;
    if( err_mutex == INVALID_MUTEX )
    {
        /* try create it */
        err_mutex = dwc_mutex_create( "DwLib::StdlibError" );
    }
}

/* ----------------------------------------------------------------------- */
#ifndef __UNIX_GUARD__
void printLib( int level, char const* fmt, ... )
{
    va_list args;

    if( !fmt )
    {    
        return;
    }
    if( LogEnabled )
    {
        va_start( args, fmt );
        InternalPrint( level, fmt, args );
        va_end( args );
    }
}
#else
// see drweb-dazuko/src/drweb_log.cpp
#endif

/* ----------------------------------------------------------------- */
DWC_ipc_t session_open( DWC_bool_t is_local, DWC_bool_t use_nodelay )
{
    DWC_ipc_t           ipc = INVALID_IPC;
    DWC_error_code_t    error;
    dwc_addrentry_t     addren;
    
    addren = NULL;       
    while( NULL != (addren = dwc_addrlist_get( Lib.addresses_list, addren )) )
    {
        error = create_ipc( 
            dwc_addrentry_ipc_type(addren), 
            NULL, 
            dwc_addrentry_ipc_addr(addren), 
            dwc_addrentry_timeout(addren), 
            is_local,
            TRUE,
            &ipc );
        if( error == DWC_NOERROR )
        {
            error = connect_ipc( ipc );
            if( error == DWC_NOERROR )
            {
                if( use_nodelay )
                {
                    dwc_ipc_set_nodelay( ipc, use_nodelay );
                }
                return ipc;
            }           
            delete_ipc( &ipc );
        }
        is_local = FALSE;
    }
    printLib( DW_LL_ERROR, "dwlib: cannot create connection with a DrWeb daemon" );
    delete_ipc( &ipc );
    return INVALID_IPC;
}

/* ------------------------------------------------------------------------ */
int session_close( DWC_ipc_t ipc )
{
    if( delete_ipc( &ipc ) != DWC_NOERROR )
    {
        printLib(DW_LL_VERBOSE,"dwlib: ipc-channel closing failed");
        return DWL_IPCSYSTEM;
    }
    return DWL_NOERROR;
}

/* ------------------------------------------------------------------------ */
int startupLib( int Version, const char *Address, int Timeout, int Cache )
{   
    if( MAJOR_LIBVERSION(Version) != MAJOR_LIBVERSION(CURRENT_LIB_VERSION) )
    {
        printLib( DW_LL_ERROR, "dwlib: startup: library major version (%d) and requested (%d) is different", MAJOR_LIBVERSION(CURRENT_LIB_VERSION), MAJOR_LIBVERSION(Version) );
        return DWL_BADPARAMS;
    }
    if( MINOR_LIBVERSION(Version) != MINOR_LIBVERSION(CURRENT_LIB_VERSION) )
    {
        printLib( DW_LL_ALERT, "dwlib: startup: library minor version (%d) and requested (%d) is different", MINOR_LIBVERSION(CURRENT_LIB_VERSION), MINOR_LIBVERSION(Version) );
    }
    if( Lib.inited )
    {
        printLib( DW_LL_ERROR, "dwlib: startup: library already initialized" );
        return DWL_ALREADYEXEC;
    }
    if( !Address )
    {    
        printLib( DW_LL_ERROR, "dwlib: startup: daemon address not specified" );
        return DWL_BADPARAMS;
    }
    memset( &Lib, 0, sizeof(Lib) );
    if( init_ipc_subsystem() != DWC_NOERROR )
    { 
        printLib( DW_LL_VERBOSE, "dwlib: startup: cannot initialize internal IPC system, deleting addrlist" );
        dwc_addrlist_delete( Lib.addresses_list );
        return DWL_IPCSYSTEM;
    }
    Lib.addresses_list = dwc_addrlist_create();
    if( !Lib.addresses_list )
    {
        printLib( DW_LL_VERBOSE, "dwlib: startup: cannot initialize internal IPC system, dinit_ipc_subsystem" );
        dinit_ipc_subsystem();
        return DWL_NORESOURCES;
    }
    if( !dwc_addrlist_add( Lib.addresses_list, Address, Timeout, Cache ) )
    {
        printLib( DW_LL_VERBOSE, "dwlib: startup: cannot create IPC with specification <%s>", Address );
        dwc_addrlist_delete( Lib.addresses_list );
        dinit_ipc_subsystem();
        return DWL_IPCSYSTEM;
    }
    Lib.inited = TRUE;
    return DWL_NOERROR;
}

/* ------------------------------------------------------------------------ */
int addAddressLib( const char *Address, int Timeout, int Cache )
{
    if( !dwc_addrlist_add( Lib.addresses_list, Address, Timeout, Cache ) )
    {
        return DWL_IPCSYSTEM;
    }
    return DWL_NOERROR;
}

/* ------------------------------------------------------------------------ */
int cleanupLib( void )
{
    if( !Lib.inited )
    {
        printLib( DW_LL_INFO, "dwlib: cleanup: library has not been initialized" );
        return DWL_ALREADYEXEC;
    }
    dwc_addrlist_delete( Lib.addresses_list );
    if( dinit_ipc_subsystem() != DWC_NOERROR )
    {
        printLib( DW_LL_VERBOSE, "dwlib: cleanup: deinitialization of internal IPC system failed" );
        Lib.inited = FALSE;
        return DWL_IPCSYSTEM;
    }
    if( err_mutex != INVALID_MUTEX )
    {
        dwc_mutex_destroy( err_mutex );
        err_mutex = INVALID_MUTEX;
    }
    Lib.inited = FALSE;
    return DWL_NOERROR;
}

/* -------------------------------------------------------------------------- */
int get_version( int* major, int* minor )
{
    dwc_proto_int_t     ver = 0;
    DWC_ipc_t           ipc;

    if( !Lib.inited )
    {
        printLib(DW_LL_ERROR,"dwlib: library is not initialized");
        return DWL_NOTINIT;
    }
    if( !major || !minor )
    {
        printLib(DW_LL_ERROR,"dwlib: null major or minor version storage passed");
        return DWL_BADPARAMS;
    }
    ipc = session_open( FALSE, TRUE );
    if( ipc == INVALID_IPC )
    {
        return DWL_NOCONNECTION;
    }
    if( !dw_proto_write_int( ipc, DRWEBD_VERSION_CMD ) 
        ||
        !dw_proto_read_int( ipc, &ver ) )
    {
        goto error_exit;
    }
    *major = ver/100;
    *minor = ver%100;
    return session_close( ipc );

  error_exit:
    session_close( ipc );
    return DWL_IPCSYSTEM;
}

/* ------------------------------------------------------------------------- */
int getDaemonID( char** id_string )
{
    dwc_length_t        id_len = 0;
    DWC_ipc_t           ipc;

    if( !Lib.inited )
    {
        printLib(DW_LL_ERROR,"dwlib: library is not initialized");
        return DWL_NOTINIT;
    }
    if( !id_string )
    {
        printLib(DW_LL_ERROR,"dwlib: null storage for ID string passed");
        return DWL_BADPARAMS;
    }
    *id_string = NULL;
    ipc = session_open( FALSE, TRUE );
    if( ipc == INVALID_IPC )
    {
        return DWL_NOCONNECTION;
    }
    if( !dw_proto_write_int( ipc, DRWEBD_IDSTRING_CMD )
        ||
        !dw_proto_read_bytestring( ipc, &id_len, id_string, TRUE ) )
    {
        goto error_exit;
    }
    return session_close( ipc );

  error_exit:
    DWC_Free( *id_string );
    session_close( ipc );
    return DWL_IPCSYSTEM;
}

/* ------------------------------------------------------------------------- */
int sendUpdateCmd( int* ret )
{
    DWC_ipc_t           ipc;
    dwc_proto_int_t dwc_ret;

    if( !ret )
    {
        printLib(DW_LL_ERROR,"dwlib: null storage for ret passed");
        return DWL_BADPARAMS;
    }
    if( !Lib.inited )
    {
        printLib(DW_LL_ERROR,"dwlib: library is not initialized");
        return DWL_NOTINIT;
    }
    ipc = session_open( FALSE, TRUE );
    if( ipc == INVALID_IPC )
    {
        return DWL_NOCONNECTION;
    }
    if( !dw_proto_write_int( ipc, DRWEBD_UPDATE_CMD )
        ||
        !dw_proto_read_int( ipc, &dwc_ret ) )
    {
        goto error_exit;
    }
    *ret = dwc_ret;
    return session_close( ipc );

error_exit:
    session_close( ipc );
    return DWL_IPCSYSTEM;
}
/* ------------------------------------------------------------------------- */
int getBases( char** list, int* total_records )
{
    int                 i;
    dwc_proto_int_t     bases_num = 0;
    dwc_length_t        name_len = 0;
    dwc_proto_int_t     vir_num = 0;
    char               *basename = NULL;
    dwc_multistring_t   blist = NULL;
    DWC_ipc_t           ipc = INVALID_IPC;

    if( !Lib.inited )
    {
        printLib(DW_LL_ERROR,"dwlib: library is not initialized");
        return DWL_NOTINIT;
    }
    if( !list || !total_records )
    {
        printLib(DW_LL_ERROR,"dwlib: null storage for list of bases passed" );
        return DWL_BADPARAMS;
    }
    *total_records = 0;
    *list = NULL;
    blist = dw_multistring_create( 0 );
    if( !blist )
    {
        return DWL_NOMEMORY;
    }
    ipc = session_open( FALSE, TRUE );
    if( ipc == INVALID_IPC )
    {
        dw_multistring_delete( blist );    
        return DWL_NOCONNECTION;
    }
    if( !dw_proto_write_int( ipc, DRWEBD_BASEINFO_CMD )
        ||
        !dw_proto_read_int( ipc, &bases_num ) )
    {
        goto error_exit;
    }
    for( i = 0; i < bases_num; ++i )
    {
        if( !dw_proto_read_bytestring( ipc, &name_len, &basename, TRUE ) 
            ||
            !dw_proto_read_int( ipc, &vir_num ) )
        {            
            goto error_exit;
        }
        dw_multistring_add( blist, "Base " );
        dw_multistring_add( blist, basename );
        dw_multistring_add( blist, " contains " );
        dw_multistring_add_int( blist, vir_num );
        dw_multistring_add( blist, " records.\n" );
        DWC_Free(basename);
        *total_records += vir_num;
    }
    *list = dw_multistring_concat( blist, DW_SD_FORWARD );
    dw_multistring_delete( blist );    
    return session_close( ipc );

  error_exit:
    DWC_Free( basename );
    DWC_Free( *list );
    dw_multistring_delete( blist );    
    if( ipc != INVALID_IPC )
    {
        session_close( ipc );
    }
    return DWL_IPCSYSTEM;
}

/* ------------------------------------------------------------------------ */
int scanBuffer( void* Buffer, size_t BufferLen, int ScanOptions, int ScanExtOptions, dwc_scan_result_t *pResult, int ReportMaxSize )
{
    DWC_ipc_t ipc;

    if( !pResult )
    {
        printLib(DW_LL_ERROR, "dwlib: scanbuffer: bad parameters (null pointer passed)");
        return DWL_BADPARAMS;
    }
    *pResult = NULL; 
    if( !Buffer )
    {
        printLib(DW_LL_ERROR, "dwlib: scanbuffer: bad parameters (null pointer passed)");
        return DWL_BADPARAMS;
    }
    if( !Lib.inited )
    {
        printLib(DW_LL_ERROR, "dwlib: scanbuffer: library is not initialized");
        return DWL_NOTINIT;
    }
    ipc = session_open( FALSE, dw_bm_contain( ScanOptions, DRWEBD_USE_TCPNODELAY ) );
    if( ipc == INVALID_IPC )
    {
        return DWL_NOCONNECTION;
    }
    if( !dw_proto_write_int( ipc, DRWEBD_SCAN_CMD )
        ||
        !dw_proto_write_int( ipc, ScanOptions )
        ||
        (dw_bm_contain(ScanOptions, DRWEBD_HAVE_EXTENDED) && !dw_proto_write_int( ipc, ScanExtOptions ))
        ||
        !dw_proto_write_int( ipc, 0 ) 
        ||
        !dw_proto_write_bytestring( ipc, BufferLen, Buffer ) )
    {
        goto error_exit;
    }
    if( fillResult( ipc, ScanOptions, "[MemoryBuffer]", pResult, ReportMaxSize, FALSE ) != DWL_NOERROR )
    {
        goto error_exit;
    }
    return session_close( ipc );

  error_exit:
    printLib( DW_LL_DEBUG, "dwlib: scanbuffer: ipc-operation failed" );
    session_close( ipc );
    return DWL_IPCSYSTEM;
}

/* ------------------------------------------------------------------------ */
int scanFile( const char* Path, size_t PathLen, int Local, int ScanOptions, int ScanExtOptions, dwc_scan_result_t *pResult, int ReportMaxSize )
{
    int             fd, retcode, file_size;
    char            buf[ IO_BUF_LEN ];
    dwc_length_t    towrite = sizeof(buf);
    DWC_ipc_t       ipc = INVALID_IPC;
    
    EXIT_IF_1PARAM( !pResult, "dwlib: scanfile", return DWL_BADPARAMS );
    *pResult = NULL;
    retcode = DWL_NOERROR;
    fd = -1;
    ipc = INVALID_IPC;
#if !defined(__UNIX_DW)
    if( Local )
    {
        return DWL_NOTSUPPORTED;
    }
#endif /* UNIX */       
    if( !Lib.inited )
    {
        printLib(DW_LL_ERROR,"dwlib: scanfile: library is not initialized");
        return DWL_NOTINIT;
    }
    if( (Path && (PathLen > PATH_MAX))
        || (Path && (PathLen == 0))
        || (!Path && (PathLen != 0)) )
    {
        printLib(DW_LL_ERROR,"dwlib: scanfile: bad parameters");
        return DWL_BADPARAMS;
    }        
    ipc = session_open( Local, dw_bm_contain( ScanOptions, DRWEBD_USE_TCPNODELAY ) );
    if( ipc == INVALID_IPC )
    {
        printLib(DW_LL_DEBUG,"dwlib: scanfile: cannot open scan session with drwebd");
        retcode = DWL_NOCONNECTION;
        goto error_exit;
    }
    if( Local && is_localscan_allowed_by_ipc(ipc) )    
    {
        file_size = 0;        
    }
    else
    {
        if( Local && !is_localscan_allowed_by_ipc(ipc) )
        {
            printLib(DW_LL_ALERT,"dwlib: scanfile: cannot use <localscan> mode for current type of IPC");
	    printLib(DW_LL_ALERT,"file operations cannot be performed");
            Local = 0;
        }
        fd = open_file( Path, O_RDONLY );
        if( fd < 0 )
        {
            printLib( DW_LL_ERROR,"dwlib: scanfile: cannot open file <%s> : %s", Path, stdlib_error(errno) );
            retcode = DWL_BADFILE;
            goto error_exit;
        }
        file_size = fileSize( fd );
        if( file_size < 0 )
        {
            printLib( DW_LL_ERROR,"dwlib: scanfile: cannot determine file <%s> size", Path );
            retcode = DWL_BADFILE;
            goto error_exit;
        }
    }
    if( !dw_proto_write_int( ipc, DRWEBD_SCAN_CMD )
        ||
        !dw_proto_write_int( ipc, ScanOptions )
        || 
        (dw_bm_contain(ScanOptions, DRWEBD_HAVE_EXTENDED) && !dw_proto_write_int( ipc, ScanExtOptions ))
        ||
        !dw_proto_write_bytestring( ipc, PathLen, Path )
        ||
        !dw_proto_write_int( ipc, file_size ) )
    {
        goto error_exit;
    }
    while( file_size > 0 )
    {
        if( towrite > file_size )
        {
            towrite = file_size;
        }
        if( read_file( fd, buf, towrite ) != towrite )
        {
            retcode = DWL_BADFILE;
            goto error_exit;
        }
        if( !dw_proto_write_buf( ipc, towrite, buf ) )
        {
            retcode = DWL_IPCSYSTEM;
            goto error_exit;
        }
        file_size -= towrite;
    }
    if( fillResult( ipc, ScanOptions, Path, pResult, ReportMaxSize, Local ) != DWL_NOERROR )
    {
        retcode = DWL_IPCSYSTEM;
        goto error_exit;
    }
    if( fd >= 0 )
    {
        close_file( fd );
    }
    return session_close( ipc );

  error_exit:
    printLib(DW_LL_DEBUG,"dwlib: scanfile failed");
    if( fd < 0 )
    {
        close_file( fd );
    }
    if( ipc != INVALID_IPC )
    {
        session_close( ipc );
    }
    return retcode;
}

/* ------------------------------------------------------------------------ */
int scanMail( char* Path, size_t PathLen, int Local, int ScanOptions, int ScanExtOptions, dwc_scan_result_t *pResult, int ReportMaxSize, dwc_string_t sender, dwc_multistring_t rcpts )
{
    int             fd, retcode, file_size;
    char            buf[ IO_BUF_LEN ];
    dwc_length_t    towrite = sizeof(buf);
    DWC_ipc_t       ipc = INVALID_IPC;
    dwc_string_t    to = NULL;
    
    EXIT_IF_1PARAM( !pResult, "dwlib: scanmail", return DWL_BADPARAMS );
    *pResult = NULL;
    retcode = DWL_NOERROR;
    fd = -1;
    ipc = INVALID_IPC;
#if !defined(__UNIX_DW)
    if( Local )
    {
        return DWL_NOTSUPPORTED;
    }
#endif /* UNIX */       
    if( !Lib.inited )
    {
        printLib(DW_LL_ERROR,"dwlib: scanmail: library is not initialized");
        return DWL_NOTINIT;
    }
    ScanOptions |= DRWEBD_HAVE_ENVELOPE;
    if( (Path && (PathLen > PATH_MAX))
        || (Path && (PathLen == 0))
        || (!Path && (PathLen != 0)) )
    {
        printLib(DW_LL_ERROR,"dwlib: scanmail: bad parameters");
        return DWL_BADPARAMS;
    }        
    ipc = session_open( Local, dw_bm_contain( ScanOptions, DRWEBD_USE_TCPNODELAY ) );
    if( ipc == INVALID_IPC )
    {
        printLib(DW_LL_DEBUG,"dwlib: scanmail: cannot open scan session with drwebd");
        retcode = DWL_NOCONNECTION;
        goto error_exit;
    }
    if( Local && is_localscan_allowed_by_ipc(ipc) )    
    {
        file_size = 0;        
    }
    else
    {
        if( Local && !is_localscan_allowed_by_ipc(ipc) )
        {
            printLib(DW_LL_ALERT,"dwlib: scanmail: cannot use <localscan> mode on backup drwebd");
            Local = 0;
        }
        fd = open_file( Path, O_RDONLY );
        if( fd < 0 )
        {
            printLib( DW_LL_ERROR,"dwlib: scanmail: cannot open file <%s> : %s", Path, stdlib_error(errno) );
            retcode = DWL_BADFILE;
            goto error_exit;
        }
        file_size = fileSize( fd );
        if( file_size < 0 )
        {
            printLib( DW_LL_ERROR,"dwlib: scanmail: cannot determine file <%s> size", Path );
            retcode = DWL_BADFILE;
            goto error_exit;
        }
    }
    if( !dw_proto_write_int( ipc, DRWEBD_SCAN_CMD )
        ||
        !dw_proto_write_int( ipc, ScanOptions ) 
        ||
        (dw_bm_contain(ScanOptions, DRWEBD_HAVE_EXTENDED) && !dw_proto_write_int( ipc, ScanExtOptions ))
        ||
        !dw_proto_write_bytestring( ipc, strlen(sender), sender )
        ||
        !dw_proto_write_int( ipc, dw_multistring_count( rcpts ) ) )
    {
        goto error_exit;
    }
    dw_multistring_rewind( rcpts, DW_SD_FORWARD );
    while( NULL != (to = dw_multistring_getnext( rcpts )) )
    {
        if( !dw_proto_write_bytestring( ipc, strlen(to), to ) )
            goto error_exit;
    }
    if( !dw_proto_write_bytestring( ipc, PathLen, Path )
        ||
        !dw_proto_write_int( ipc, file_size ) )
    {
        goto error_exit;
    }
    while( file_size > 0 )
    {
        if( towrite > file_size )
        {
            towrite = file_size;
        }
        if( read_file( fd, buf, towrite ) != towrite )
        {
            retcode = DWL_BADFILE;
            goto error_exit;
        }
        if( !dw_proto_write_buf( ipc, towrite, buf ) )
        {
            retcode = DWL_IPCSYSTEM;
            goto error_exit;
        }
        file_size -= towrite;
    }
    if( fillResult( ipc, ScanOptions, Path, pResult, ReportMaxSize, Local ) != DWL_NOERROR )
    {
        retcode = DWL_IPCSYSTEM;
        goto error_exit;
    }
    if( fd >= 0 )
    {
        close_file( fd );
    }
    return session_close( ipc );

  error_exit:
    printLib(DW_LL_DEBUG,"dwlib: scanfile failed");
    if( fd < 0 )
    {
        close_file( fd );
    }
    if( ipc != INVALID_IPC )
    {
        session_close( ipc );
    }
    return retcode;
}

/* ------------------------------------------------------------------------ */
int scanFilePart( char* Path, size_t PathLen, size_t Offset, size_t Length, int ScanOptions, int ScanExtOptions, dwc_scan_result_t *pResult, int ReportMaxSize )
{
#if !defined(__UNIX_DW)
    return DWL_NOTSUPPORTED;
#else /* __UNIX_DW */
    DWC_ipc_t ipc;

    if( !pResult )
    {
        printLib(DW_LL_ERROR,"dwlib: scanfilepart: bad parameters");
        return DWL_BADPARAMS;
    }
    *pResult = NULL;
    if( !Lib.inited )
    {
        printLib(DW_LL_ERROR,"dwlib: scanfilepart: library is not initialized");
        return DWL_NOTINIT;
    }
    if( (Path && (PathLen > PATH_MAX))
        || (Path && (PathLen == 0))
        || (!Path && (PathLen != 0)) )
    {
        printLib(DW_LL_ERROR,"dwlib: scanfilepart: bad parameters");
        return DWL_BADPARAMS;
    }
    ipc = session_open( TRUE, dw_bm_contain( ScanOptions, DRWEBD_USE_TCPNODELAY ) );
    if( ipc == INVALID_IPC )
    {
        return DWL_NOCONNECTION;
    }
    if( !is_localscan_allowed_by_ipc(ipc) )
    {
        printLib(DW_LL_ERROR,"dwlib: scanfilepart: cannot create session with local drwebd");
        goto error_exit;
    }
    if( !dw_proto_write_int( ipc, DRWEBD_SCANPART_CMD )
        ||
        !dw_proto_write_int( ipc, Offset )
        ||
        !dw_proto_write_int( ipc, Length )
        ||
        !dw_proto_write_int( ipc, ScanOptions ) 
        ||
        (dw_bm_contain(ScanOptions, DRWEBD_HAVE_EXTENDED) && !dw_proto_write_int( ipc, ScanExtOptions ))
        ||
        !dw_proto_write_bytestring( ipc, PathLen, Path ) )
    {
        goto error_exit;
    }
    if( fillResult( ipc, ScanOptions, Path, pResult, ReportMaxSize, TRUE ) != DWL_NOERROR )
    {
        goto error_exit;
    }
    return session_close( ipc );

  error_exit:
    printLib(DW_LL_DEBUG,"dwlib: scanfilepart: ipc-operation failed");
    session_close( ipc );
    return DWL_IPCSYSTEM;
#endif /* __UNIX_DW */
}

/* ------------------------------------------------------------------------ */
int scanCallbackBuffer( dwc_cb_func_t Callback, dwc_cb_data_t CBData, char* BufferName, int BufferNameLen, int BufferLen, int ScanOptions, int ScanExtOptions, dwc_scan_result_t *pResult, int ReportMaxSize )
{
    DWC_ipc_t       ipc;
    dwc_membuf_t    buf = NULL;
    int             wrote_bytes, got_bytes;

    if( !pResult )
    {
        printLib(DW_LL_ERROR, "dwlib: scancallback: bad parameter pResult (null pointer passed)");
        return DWL_BADPARAMS;
    }
    *pResult = NULL;
    if( !Lib.inited )
    {
        printLib(DW_LL_ERROR, "dwlib: scancallback: library is not initialized");
        return DWL_NOTINIT;
    }
    if( !Callback        
        || (BufferName && (BufferNameLen > PATH_MAX))
        || (BufferName && (BufferNameLen == 0))
        || (!BufferName && (BufferNameLen != 0)) )
    {
        printLib(DW_LL_ERROR,"dwlib: scanfilepart: bad parameters");
        return DWL_BADPARAMS;
    }
    buf = DWC_Malloc(dwc_membuf_t, IO_BUF_LEN);
    if( !buf )
    {
        printLib(DW_LL_ERROR, "dwlib: scancallback: no memory");
        return DWL_NOMEMORY;
    }
    ipc = session_open( FALSE, dw_bm_contain( ScanOptions, DRWEBD_USE_TCPNODELAY ) );
    if( ipc == INVALID_IPC )
    {
        DWC_Free(buf);
        return DWL_NOCONNECTION;
    }
    if( !dw_proto_write_int( ipc, DRWEBD_SCAN_CMD )
        ||
        !dw_proto_write_int( ipc, ScanOptions ) 
        ||
        (dw_bm_contain(ScanOptions, DRWEBD_HAVE_EXTENDED) && !dw_proto_write_int( ipc, ScanExtOptions ))
        ||
        !dw_proto_write_bytestring( ipc, BufferNameLen, BufferName ) 
        ||
        !dw_proto_write_int( ipc, BufferLen ) )
    {
        goto error_exit;
    }
    wrote_bytes = 0;
    while( wrote_bytes < BufferLen )
    {
        got_bytes = Callback( buf, IO_BUF_LEN, CBData );
        if( (got_bytes < 0)
            ||
            !dw_proto_write_buf( ipc, got_bytes, buf ) )
        {
            goto error_exit;
        }
        wrote_bytes += got_bytes;
    }
    DWC_Free( buf );
    if( fillResult( ipc, ScanOptions, BufferName, pResult, ReportMaxSize, FALSE ) != DWL_NOERROR )
    {
        goto error_exit;
    }
    return session_close( ipc );

  error_exit:
    printLib( DW_LL_DEBUG, "dwlib: scancallback: ipc-operation failed" );
    DWC_Free( buf );
    session_close( ipc );
    return DWL_IPCSYSTEM;
}

/* ------------------------------------------------------------------------ */
int fileSize( int fd )
{
    off_t fs, cur;

    cur = lseek_file( fd, 0, SEEK_CUR );
    if( cur < 0 )
    {
        return -1;
    }
    fs = lseek_file( fd, 0, SEEK_END );
    if( fs < 0 )
    {
        return -1;
    }
    if( lseek_file( fd, cur, SEEK_SET ) < 0 )
    {
        return -1;
    }
    return (int)fs;
}

/* ------------------------------------------------------------------------ */
char* getDaemonCodeString( int code )
{
   return findWhat( code, DaemonMsgTable, FIND_FULL_MASK );
}

/* ------------------------------------------------------------------------ */
static int fillResult( DWC_ipc_t ipc, int ScanOptions, dwc_const_string_t path, dwc_scan_result_t *pResult, int ReportMaxSize, dwc_bool_t isLocalScan )
{
    dwc_length_t   rule_len = 0;
    dwc_length_t   spam_details_len=0;

    *pResult = createResult();
    if( !*pResult )
    {
        return DWL_NOMEMORY;
    }
    if( !dw_proto_read_int( ipc, &(*pResult)->status ) )
    {
        goto error_exit;
    }
    if( dw_bm_include( ScanOptions, DRWEBD_RETURN_VIRUSES ) )
    {
        (*pResult)->viruslist = dw_multistring_create( ReportMaxSize );
        if( !(*pResult)->viruslist )
        {
            goto error_exit;
        }
        if( !dw_proto_read_list( ipc, dw_combiner_add_multistring, (*pResult)->viruslist, NULL ) )
        {
            goto error_exit;
        }        
    }
    if( dw_bm_include( ScanOptions, DRWEBD_RETURN_SHORT_VIRUSES ) )
    {
        if( !dw_proto_read_list( ipc, dw_combiner_parse_viruses, &(*pResult)->viruses, NULL ) )
        {
            goto error_exit;
        }        
    }
    if( dw_bm_include( ScanOptions,  DRWEBD_RETURN_FILTER_RULE ) )
    {
        if( !dw_proto_read_bytestring( ipc, &rule_len, (dwc_proto_bytestring_t*) &(*pResult)->filter_rule, TRUE ) )
        {
            goto error_exit;
        }        
    }
    if( dw_bm_include( ScanOptions, DRWEBD_RETURN_REPORT ) )
    {       
        (*pResult)->report = dw_proto_read_report( ipc, ReportMaxSize, (dwc_use_full_names_for_report)? NULL : path );
        if( !(*pResult)->report )
        {
            goto error_exit;
        }
    }
    if( dw_bm_include( ScanOptions, DRWEBD_RETURN_CODES ) )
    {
        if( !dw_proto_read_stat( ipc, &((*pResult)->stat) ) )
        {
            goto error_exit;
        }
    }   
    if( dw_bm_include( ScanOptions, DRWEBD_IS_MAIL )
	&&
	dw_bm_include( ScanOptions, DRWEBD_ANTISPAM_ON ) )
    {
        /* If you will have compilation errors replace the second parameter with &(*pResult)->spam.dw_spam_score) */
        dwc_proto_int_t spam_score = 0;
        if( !dw_proto_read_int( ipc, &spam_score) )
        {
	    goto error_exit;
	}
        (*pResult)->spam.dw_spam_score = spam_score;

        if( !dw_proto_read_bytestring( ipc, &spam_details_len, (dwc_proto_bytestring_t*) &(*pResult)->spam.dw_spam_details, TRUE ) )
        {
            goto error_exit;
        }        
    }   
    if( dw_bm_contain( (*pResult)->status, DERR_HAVE_CURED | DERR_HAVE_DELETED ) 
        &&
        ( !isLocalScan 
          || 
          (isLocalScan && dw_bm_include( ScanOptions, DRWEBD_DONT_CHANGEMAIL ) ) ) )
    {
        printLib( DW_LL_DEBUG, "dwlib: scan::daemon_answer going to receive cured file" );
        if( !dw_proto_read_bytestring( ipc, &(*pResult)->cured_file_length, &(*pResult)->cured_file, FALSE ) )
        {
            goto error_exit;
        }        
        printLib( DW_LL_VERBOSE, "dwlib: scan::daemon_answer cured file length %ld", (*pResult)->cured_file_length );
    }
    return DWL_NOERROR;

  error_exit:
    clearResult( *pResult );
    *pResult = NULL;
    return DWL_IPCSYSTEM;
}

const char * dwcStrError( int err_num )
{
    switch (err_num)
    {
    case DWL_NOERROR:                 return "no errors"; break;
    case DWL_COMMONERROR:             return "returned by system"; break;
    case DWL_IPCSYSTEM:               return "in internal transport system"; break;
    case DWL_BADFILE:                 return "on file passed to lib"; break;
    case DWL_BADPARAMS:               return "incorrect values of parameters"; break;
    case DWL_NOTSUPPORTED:            return "this option not supported"; break;
    case DWL_ALREADYEXEC:             return "try make operation that can be performed only once"; break;
    case DWL_RECVERROR:               return "on recv from daemon"; break;
    case DWL_SENDERROR:               return "on send to daemon"; break;
    case DWL_NOMEMORY:                return "no memory for internal use"; break;
    case DWL_NOTINIT:                 return "library was not inited"; break;
    case DWL_TIMEDOUT:                return "operation timed out"; break;
    //case DWL_NORESOURCES:             return "no resources for internal use"; break;
    case DWL_NOCONNECTION:            return "cannot connect to daemon address"; break;
    }
    return "unknown dwc error";
}

#endif /* DWC_LIB_C */
