#ifndef _DW_UTIL_C
#define _DW_UTIL_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: dw_util.c,v 1.18 2008/03/28 13:56:24 akorkov Exp $";
#endif

#if defined(__UNIX_DW)
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/param.h>
#endif /* UNIX */
#include <sys/stat.h>
#if defined(FreeBSD_DW)
#include <sys/syslimits.h>
#endif /* FreeBSD */
#if defined(Solaris_DW)
#include <limits.h>
#endif /* Solaris */
#include <ctype.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#if defined(__UNIX_DW)
#include <signal.h>
#include <syslog.h>
#include <pwd.h>
#if !defined(EMX_DW)
#include <sysexits.h>
#else /* EMX */
#include <io.h>
#include <netdb.h>
#endif /* not EMX */
#endif /* UNIX */
#if defined(__UNIX_DW)
#include "dwc_lib.h"
#include "dwc_proto.h"
#include "dwc_ipc.h"
#endif /* UNIX */
#if defined(__WIN32_DW)
#include "dw_winlib.h"
#endif /* WIN32 */
#include "dw_util.h"

#if defined(__UNIX_DW)
dwc_sls_t          SyslogFacilities[] = {
        { "Daemon", LOG_DAEMON },
        { "Mail",   LOG_MAIL },
        { "Local0", LOG_LOCAL0 },
        { "Local1", LOG_LOCAL1 },
        { "Local2", LOG_LOCAL2 },
        { "Local3", LOG_LOCAL3 },
        { "Local4", LOG_LOCAL4 },
        { "Local5", LOG_LOCAL5 },
        { "Local6", LOG_LOCAL6 },
        { "Local7", LOG_LOCAL7 },
        END_SLS_LINE };

dwc_sls_t          SyslogPriorities[] = {
        { "Alert",  LOG_ALERT },
        { "Info",   LOG_INFO },
        { "Notice", LOG_NOTICE },
        { "Debug",  LOG_DEBUG },        
        END_SLS_LINE };
#endif /* UNIX */

dwc_sls_t          LogLevels[] = {
        { "Quiet",  DW_LL_QUIET },
        { "Errors", DW_LL_ERROR },
        { "Alerts", DW_LL_ALERT },
        { "Info",   DW_LL_INFO },
        { "Verbose",DW_LL_VERBOSE },
        { "Debug",  DW_LL_DEBUG },
        END_SLS_LINE };

/*-------------------------------------------------------------------------*/
dwc_bool_t dw_set_slproperty( char* key, int *pvalue, dwc_sls_t val_arr[] )
{        
    int i;
    
    for( i = 0; val_arr[i].key; ++i )
    {
        if( 0 == DWC_strncasecmp( key, val_arr[i].key, strlen(key) ) )
        {
            *pvalue = val_arr[i].val;
            return TRUE;
        }
    }    
    return FALSE;
}            

/* ---------------------------------------------------------------------- */
char* dw_skip_space( char *buf )
{
    char *bp;
    
    for( bp = buf; (*bp != NULLCHAR) && isspace((int)(*bp)); ++bp )
        ;
    if( (*bp == NULLCHAR) )
    {
        return NULL;
    }
    return bp;
}

#if !defined(__WIN32_DW)
/*-------------------------------------------------------------------------*/
dwc_void_t dw_sleep( const int msec )
{
    struct timeval tv;

    tv.tv_sec = msec / 1000;
    tv.tv_usec = (msec * 1000) % 1000000; 
    select( 0, NULL, NULL, NULL, &tv );
    return;
}

#if !defined(DW_AGENT)
/* ---------------------------------------------------------------------- */
dwc_bool_t dw_init_lib( dwcf_comm_opt_t* comm )
{
    int             libcode = DWL_NOERROR;
    dwc_string_t    dm_addr;

    if( !comm )
    {
        printLib( DW_LL_ERROR, "init_lib: cannot add address to library (error: %d)", libcode );
        return FALSE;
    }
    dw_multistring_rewind( comm->addr, DW_SD_FORWARD );
    dm_addr = dw_multistring_getnext( comm->addr );
    printLib( DW_LL_VERBOSE, "init_lib: set primary daemon address = <%s>", dm_addr );
    libcode = startupLib( CURRENT_LIB_VERSION, dm_addr, comm->timeout, comm->cache );
    if( libcode != DWL_NOERROR )
    {
        printLib( DW_LL_VERBOSE, "init_lib: cannot initialize dwclient library (error: %d)", libcode );
        return FALSE;
    }
    while( NULL != (dm_addr = dw_multistring_getnext(comm->addr)) )
    {
        printLib( DW_LL_VERBOSE, "init_lib: set backup daemon address = <%s>", dm_addr );
        libcode = addAddressLib( dm_addr, comm->timeout, comm->cache );
        if( libcode != DWL_NOERROR )
        {
            printLib( DW_LL_VERBOSE, "init_lib: cannot add address to library (error: %d)", libcode );
            return FALSE;
        }
    }
    return TRUE;
}

/* ---------------------------------------------------------------------- */
dwc_bool_t dw_check_version( void )
{
    int                 libcode;
    int                 major = 0, minor = 0;

    libcode = get_version( &major, &minor );
    if( libcode != DWL_NOERROR )
    {
        printLib( DW_LL_ERROR, "cannot retrieve daemon version (error: %d)", libcode );
        return FALSE;
    }    
    if( (major < 4) || ((major == 4) && (minor < 33)) )
    {
        printLib( DW_LL_ERROR, "THIS VERSION IS NOT COMPATIBLE WITH Dr.WEB (R) DAEMON VERSIONS LESS THAT 4.33 !!!" );
        return FALSE;
    }
    return TRUE;
}    
#endif /* not AGENT */

#if !defined(EMX_DW)
/* ----------------------------------------------------------------------- */
pid_t dw_daemonize( dwc_string_t ch_dir, dwc_bool_t close_fd )
{
    pid_t          pid;
    int            fd;
    int            maxfd = 3;
    struct rlimit  filelimit;

    signal( SIGTTOU, SIG_IGN );
    signal( SIGTTIN, SIG_IGN );
    signal( SIGTSTP, SIG_IGN );

    pid = fork();
    if( pid == 0 )
    {
        /* create new session without controling terminal */
        if( setsid() < 0 )
        {
            printLib( DW_LL_ERROR, "conf: cannot create new session" );
            return -1;
        }
        /* change current dir to allow umount it */        
        if( ch_dir && (chdir(ch_dir) < 0) )
        {
            printLib( DW_LL_ERROR, "conf: cannot change directory to %s", ch_dir );
            return -1;
        }
        closelog();
        if( (freopen( "/dev/null", "r", stdin ) == NULL)
            ||
            (freopen( "/dev/null", "w", stdout ) == NULL)
            ||
            (freopen( "/dev/null", "w", stderr ) == NULL) )
        {
            printLib( DW_LL_ERROR, "conf: cannot redirect std-streams to /dev/null" );
            return -1;
        }
        if( getrlimit( RLIMIT_NOFILE, &filelimit ) < 0 )
        {
            printLib( DW_LL_ERROR, "conf: cannot get system limits" );
            return -1;
        }
        if( close_fd )
        {
            maxfd = filelimit.rlim_max;
            for( fd = 3; fd < maxfd; ++fd )
            {
                close( fd );
            }        
        }
    }
    return pid;
}
#endif /* not EMX */

#if !defined(DW_SAMBA)            
#if !defined(DW_AGENT)
#if defined(DW_SENDMAIL) || defined(DW_CGP) || defined(DW_ZMAILER) || defined(DW_COURIER) || defined(DW_MIO)
/* ---------------------------------------------------------------------- */
dwc_bool_t dw_init_agent( dwcf_agent_opt_t* popt )
{
    dwc_proto_int_t rc;
    
    EXIT_IF_1PARAM( !popt, "dwlib:init_stat", return FALSE );
    if( !popt->addr )
    {
        printLib( DW_LL_DEBUG, "init_stat: agent address was not defined - assume no agent" );
        return TRUE;
    }    

    popt->ipc_locker = dwc_mutex_create( "DrWebAgent::Connection" );
    if( !popt->ipc_locker )
    {
        printLib( DW_LL_ERROR, "init_stat: cannot create locker for connection to agent" );
        goto error;
    }
    popt->ipc = dwc_ipc_create_client( popt->addr, popt->timeout, FALSE );
    if( !popt->ipc )
    {
        printLib( DW_LL_ERROR, "init_stat: cannot connect to agent" );
        goto error;
    }
    if( !dw_proto_write_int( popt->ipc, DW_AGENT_PROTOCOL_VERSION ) /* proto version */
        ||       
        !dw_proto_write_int( popt->ipc, DW_AGENT_REGISTER_CONNECTION )
        ||
        !dw_proto_read_int( popt->ipc, &rc )
        ||
        (DW_AGENT_OK != rc) )
    {
        printLib( DW_LL_ERROR, "init_stat: cannot register agent" );
        goto error;
    }
    printLib( DW_LL_INFO, "init_stat: connection(%s) with agent has been established", popt->addr );
    return TRUE;
    
  error:    
    dwc_ipc_delete( popt->ipc );
    dwc_mutex_destroy( popt->ipc_locker );
    return FALSE;
}

/* ---------------------------------------------------------------------- */
dwc_void_t dw_fini_agent( dwcf_agent_opt_t* popt )
{
    dwc_proto_int_t rc;
    
    EXIT_IF_1PARAM( !popt, "dwlib:fini_stat", return );
    if( !popt->addr )
    {
        printLib( DW_LL_DEBUG, "fini_stat: agent address was not defined - assume no agent" );
        return;
    }    
    EXIT_IF_1PARAM( !popt->ipc, "dwlib:fini_stat", return );
    
    if( !dw_proto_write_int( popt->ipc, DW_AGENT_DEREGISTER_CONNECTION )
        ||
        !dw_proto_read_int( popt->ipc, &rc )
        ||
        (DW_AGENT_OK != rc) )
    {
        printLib( DW_LL_ERROR, "fini_stat: cannot deregister agent" );
    }
    dwc_ipc_delete( popt->ipc );
    dwc_mutex_destroy( popt->ipc_locker );
    printLib( DW_LL_INFO, "init_stat: connection(%s) with agent has been closed", popt->addr );
    return;
}

/* ---------------------------------------------------------------------- */
dwc_bool_t dw_drop_stat( dwcf_agent_opt_t *popt, dwc_scan_result_t res )
{
    dwc_scan_stat_t    *pstat = getResultStat( res );
    dwc_viruses_t      *pvirs = getResultTypedViruses( res );
    dwc_bool_t          ret = TRUE;
    dwc_proto_int_t     rc = 0;

    EXIT_IF_2PARAM( !popt, !res, "dwlib:drop_stat:", return FALSE );
    if( !popt->addr )
    {
        printLib( DW_LL_DEBUG, "drop_stat: agent address was not defined - assume no agent" );
        return TRUE;
    }
    EXIT_IF_4PARAM( !popt->ipc, !popt->ipc_locker, !pstat, !pvirs, "dwlib:drop_stat:", return FALSE );
    
    printScanStat( DW_LL_VERBOSE, pstat );
    if( !dwc_mutex_lock( popt->ipc_locker ) )
    {
        printLib( DW_LL_ERROR, "drop_stat: cannot lock ipc with agent - skip statistic" );
        return FALSE;
    }
    dwc_ipc_set_timeout( popt->ipc, popt->timeout );
    if( !dw_proto_write_int( popt->ipc, DW_AGENT_POST_STAT ) /* command */
        ||
        !dw_proto_write_stat( popt->ipc, pstat ) 
        ||
        !dw_proto_write_list( popt->ipc, pvirs->known, TRUE )
        ||
        !dw_proto_write_list( popt->ipc, pvirs->modification, TRUE )
        ||
        !dw_proto_read_int( popt->ipc, &rc )
        ||
        (DW_AGENT_OK != rc) )
    {
        printLib( DW_LL_ERROR, "drop_stat: errors during talking with agent" );
        ret = FALSE;
    }
    if( !dwc_mutex_unlock( popt->ipc_locker ) )
    {
        printLib( DW_LL_ERROR, "drop_stat: cannot unlock ipc with agent - FATAL, no more statistic" );
        return FALSE;
    }
    return ret;
}
#else /* POSTFIX or EXIM or QMAIL */
/* ---------------------------------------------------------------------- */
dwc_bool_t dw_drop_stat( dwcf_agent_opt_t *popt, dwc_scan_result_t res )
{
    dwc_ipc_t           ipc = NULL;
    dwc_scan_stat_t    *pstat = getResultStat( res );
    dwc_viruses_t      *pvirs = getResultTypedViruses( res );
    dwc_bool_t          ret = TRUE;
    dwc_proto_int_t     rc = 0;

    EXIT_IF_2PARAM( !popt, !res, "drop_stat:", return FALSE );
    if( !popt->addr )
    {
        printLib( DW_LL_DEBUG, "drop_stat: agent address was not defined - assume no agent" );
        return TRUE;
    }
    EXIT_IF_2PARAM( !pstat, !pvirs, "drop_stat:", return FALSE );
    
    printScanStat( DW_LL_VERBOSE, pstat );
    ipc = dwc_ipc_create_client( popt->addr, popt->timeout, FALSE );
    if( !ipc )
    {
        printLib( DW_LL_ERROR, "drop_stat: cannot connect to agent" );
        return FALSE;
    }
    if( !dw_proto_write_int( ipc, DW_AGENT_PROTOCOL_VERSION ) /* proto version */
        ||
        !dw_proto_write_int( ipc, DW_AGENT_POST_STAT ) /* command */
        ||
        !dw_proto_write_stat( ipc, pstat ) 
        ||
        !dw_proto_write_list( ipc, pvirs->known, TRUE )
        ||
        !dw_proto_write_list( ipc, pvirs->modification, TRUE )
        ||
        !dw_proto_read_int( ipc, &rc )
        ||
        (DW_AGENT_OK != rc) )
    {
        printLib( DW_LL_ERROR, "drop_stat: errors during talking with agent" );
        ret = FALSE;
    }
    dwc_ipc_delete(ipc);
    if( rc != 0 )
        ret = FALSE;
    return ret;
}
#endif /* SENDMAIL or CGP or ZMAILER or COURIER or MIO */
#endif /* not AGENT */

/* ----------------------------------------------------------------------- */
dwc_bool_t dw_set_user ( const dwc_string_t user )
{
#if defined(__UNIX_DW)
    struct passwd *account;

    if( user )
    {
        account = getpwnam( user );
        if( !account )
        {
            printLib( DW_LL_ERROR, "conf: non-existing user account %s specified", user );
            return FALSE;
        }
        if( (setgid(account->pw_gid) < 0)
            ||
            (setuid(account->pw_uid) < 0) )
        {
            printLib( DW_LL_ERROR, "conf: cannot set uid or gid - %s", stdlib_error(errno) );
            return FALSE;
        }
        printLib( DW_LL_VERBOSE, "conf: filter would use %s account (uid=%d, gid=%d)", user, getuid(), getgid() );
    }
    else
    {
        printLib( DW_LL_VERBOSE, "conf: no user specified - filter would use current account (uid=%d, gid=%d)", getuid(), getgid() );
    }
#endif /* UNIX */    
    return TRUE;    
}
#endif /* not SAMBA */
#endif /* not WIN32 */

#endif /* _DW_UTIL_C */
