#ifndef _DRWEB_SAMBA_C
#define _DRWEB_SAMBA_C

/*--------------------------------------------------------------------------
 *  Copyright (c) 2001-2004 Igor Daniloff.
 *	All rights reserved.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * 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.	 See the
 * GNU General Public License for more details in file LICENSE.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *--------------------------------------------------------------------------

   Dr.Web (R) Samba VFS (http://www.samba.org)

--------------------------------------------------------------------------*/

#ifndef lint
static const char rcsid[] = "$Id: drweb_samba.c,v 1.146.2.23 2009/11/10 13:55:57 akorkov Exp $";
#endif

/* Samba include files */
#include "config.h"
/* System include files */
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#include <errno.h>
#include <string.h>
#include <sysexits.h>
/* Samba include files */

#include "version.h"

#include "popt.h"

#include "includes.h"
#include "proto.h"
#include "vfs.h"

/*
 -----+----------------------+-----------------
  VFS | Samba		     | smb_spider.so.N
 -----+----------------------+-----------------
   1  | 2.2.1 and 2.2.2	     |	1
   2  | 2.2.3		     |	2
   3  | 2.2.4 and 2.2.5	     |	3
   4  | 2.2.6, 2.2.7, 2.2.8  |	4
   9  | 3.0.0 and 3.0.1	     |	9
  10  | 3.0.2		     |	10
  10a | 3.0.3 - 3.0.10	     |	11
  11  | 3.0.11		     |	12
  11  | 3.0.12 - 3.0.14	     |	12a
  13  | 3.0.20		     |	13
  14  | 3.0.20a - 3.0.20b    |	14
  15  | 3.0.21 - 3.0.22	     |	15
  16  | 3.0.23		     |	16
  21  | 3.0.25a,	     |	21
  21  | 3.0.26 - 3.0.31	     |	22 /// interface has been changed, the version number stayed
  22  | 3.0.26a-3	     |	22 (see BugID #17869)
*/

#define SMB_V3_VFS	    (9)

#undef DW_HAVE_SEEKDIR
#if SMB_VFS_INTERFACE_VERSION >= 11
#  define DW_HAVE_SEEKDIR 1
#endif /* SMB_VFS_INTERFACE_VERSION >= 11 */

#if SMB_VFS_INTERFACE_VERSION >= SMB_V3_VFS
#  undef TRUE
#  undef FALSE
#  define TRUE	  True
#  define FALSE	  False
#endif /* SMB_VFS_INTERFACE_VERSION >= SMB_V3_VFS */

#include "dwc_lib.h"
#include "dwc_macro.h"
#include "dwc_scan.h"
#include "dwc_err.h"
#include "dwc_queue.h"
#include "dw_options.h"
#include "dw_files.h"
#include "dw_dfork.h"
#include "dw_files.h"

#undef snprintf

#undef pstring
#define pstring fstring

/* Add to errno */
#define EOK		    0

/* Vfs operations opcodes */
#define DWSMB_OP_OPEN	    (1)
#define DWSMB_OP_CLOSE	    (2)

#define MAX_USERNAME	    (40)

static int use_allowed_tdb = 0, use_blocked_tdb = 0;

const char allowed_base_name[] = "allowed_by_drweb.tdb";
const char blocked_base_name[] = "blocked_by_drweb.tdb";

TDB_CONTEXT *drweb_open_tdb(const char *path);

#define HASH_SIZE 16

typedef struct dwsmb_tdb_key {
     SMB_INO_T inode;
     SMB_DEV_T device;
     SMB_OFF_T size;
     unsigned char hash[HASH_SIZE];
} dwsmb_tdb_key;

#ifndef SAMBA_VERSION_OFFICIAL_STRING
#  define SAMBA_VERSION_OFFICIAL_STRING VERSION
#endif

#ifndef SAMBA_VERSION_OFFICIAL_STRING
#  define SAMBA_VERSION_OFFICIAL_STRING VERSION
#endif

/// for each number on two sibols
#define SAMBA_LONG_VERSION_RELEASE  (SAMBA_VERSION_MAJOR*10000+SAMBA_VERSION_MINOR*100+SAMBA_VERSION_RELEASE)

// #if SMB_VFS_INTERFACE_VERSION >= 22
//     #define DW_UNIX_TO_DOS(cmsg, msg)	       push_ascii_fstring( (cmsg), (msg) )
#if SMB_VFS_INTERFACE_VERSION >= SMB_V3_VFS
#  define DW_INIT_OK			    NT_STATUS_OK
#  define DW_INIT_FAIL			    NT_STATUS_UNSUCCESSFUL

#  if SMB_VFS_INTERFACE_VERSION >= 22 && !((SAMBA_LONG_VERSION_RELEASE == 30200) && defined(SAMBA_VERSION_PRE_RELEASE))
#    define INIT_NAME                       init_samba_module
#  else
#    define INIT_NAME			    init_module
#  endif

#  define INIT_ARG			    void
typedef NTSTATUS			    dw_init_vfs_ret_t;
typedef void*				    dw_init_vfs_arg_t;
#  define INIT_VFS_ARG			    (NULL)
#  define THIS_ARG			    vfs_handle_struct *handle,
#  define HANDLE_ARG			    vfs_handle_struct *handle
#  define THIS_VALUE			    handle,
#  define CONST_ARG			    const

#  if SMB_VFS_INTERFACE_VERSION < 21
#    define DW_OPS_CONNECT(conn, serv, user)	SMB_VFS_NEXT_CONNECT(handle, (conn), (serv), (user))
#    define DW_OPS_DISCONNECT(conn)		SMB_VFS_NEXT_DISCONNECT(handle, (conn))
#    define DW_OPS_OPEN(conn,fname,flags,mode)	SMB_VFS_NEXT_OPEN(handle, (conn), (fname), (flags), (mode))
#    define DW_OPS_STAT(conn,fname,st)		SMB_VFS_NEXT_STAT(handle, (conn), (fname), (st))
#    define DW_OPS_UNLINK(conn,fname)		SMB_VFS_NEXT_UNLINK(handle, (conn), (fname))
#    define DW_OPS_RENAME(conn,old,new)		SMB_VFS_NEXT_RENAME(handle, (conn), old, new)
#  else /* !SMB_VFS_INTERFACE_VERSION < 21 */
#    define DW_OPS_CONNECT(conn, serv, user)	SMB_VFS_NEXT_CONNECT(handle, (serv), (user))
#    define DW_OPS_DISCONNECT(conn)		SMB_VFS_NEXT_DISCONNECT(handle)
#    define DW_OPS_OPEN(conn,fname,flags,mode)	SMB_VFS_NEXT_OPEN(handle, (fname), (fsp), (flags), (mode))
#    define DW_OPS_STAT(conn,fname,st)		SMB_VFS_NEXT_STAT(handle, (fname), (st))
#    define DW_OPS_UNLINK(conn,fname)		SMB_VFS_NEXT_UNLINK(handle, (fname))
#    define DW_OPS_RENAME(conn,old,new)		SMB_VFS_NEXT_RENAME(handle, old, new)
#  endif /* SMB_VFS_INTERFACE_VERSION < 21 */

/* for samba 3.2.0pre3 SMB_VFS_NEXT_CLOSE take 3 parameters */
#  if (SMB_VFS_INTERFACE_VERSION > 21) && !((SAMBA_LONG_VERSION_RELEASE == 30200) && defined(SAMBA_VERSION_PRE_RELEASE))
#    define DW_OPS_CLOSE(fsp,fd)		SMB_VFS_NEXT_CLOSE(handle, (fsp))
#  else
#    define DW_OPS_CLOSE(fsp,fd)		SMB_VFS_NEXT_CLOSE(handle, (fsp), (fd))
#  endif

#  define DW_ZERO_IP(ip)			    zero_ip(&(ip))

#  if SMB_VFS_INTERFACE_VERSION >= 22
#    define DW_UNIX_TO_DOS(cmsg, msg)		push_ascii_fstring( (cmsg), (msg) )
#  else
#    define DW_UNIX_TO_DOS(cmsg, msg)		push_ascii_pstring( (cmsg), (msg) )
#  endif

#else /* SMB_VFS_INTERFACE_VERSION < SMB_V3_VFS */

#  define DW_INIT_OK			    &drweb_vfs_ops
#  define DW_INIT_FAIL			    NULL
#  define INIT_RETURN			    struct vfs_ops*
#  if SMB_VFS_INTERFACE_VERSION < 3
#    define INIT_NAME			    vfs_module_init
#    define INIT_ARG			    int* vfs_version
typedef struct vfs_ops*			    dw_init_vfs_ret_t;
typedef void*				    dw_init_vfs_arg_t;
#    define INIT_VFS_ARG		    (NULL)
#    define CONST_ARG
#    define DW_UNIX_TO_DOS(cmsg, msg)	    pstrcpy( (cmsg), unix_to_dos((msg), FALSE) )
#  else /* SMB_VFS_INTERFACE_VERSION >= 3 */
#    define INIT_NAME			    vfs_init
#    define INIT_ARG			    int* vfs_version, struct vfs_ops *def_vfs_ops
typedef struct vfs_ops*			    dw_init_vfs_ret_t;
typedef struct vfs_ops*			    dw_init_vfs_arg_t;
#    define INIT_VFS_ARG		    def_vfs_ops
#    define CONST_ARG			    const
#    define DW_UNIX_TO_DOS(cmsg, msg)	    pstrcpy( (cmsg), unix_to_dos((msg)) )
#  endif /* SMB_VFS_INTERFACE_VERSION < 3 */

#  define THIS_ARG
#  define THIS_VALUE
#  define DW_OPS_CONNECT(conn,serv,user)	    default_vfs_ops.connect((conn), (serv), (user))
#  define DW_OPS_DISCONNECT(conn)		    default_vfs_ops.disconnect((conn))
#  define DW_OPS_OPEN(conn,fname,flags,mode)	    default_vfs_ops.open((conn), (fname), (flags), (mode))
#  define DW_OPS_CLOSE(fsp,fd)			    default_vfs_ops.close((fsp), (fd))
#  define DW_OPS_STAT(conn,fname,st)		    default_vfs_ops.stat((conn), (fname), (st))
#  define DW_OPS_UNLINK(conn,fname)		    default_vfs_ops.unlink((conn), (( char *)fname))
#  define DW_ZERO_IP(ip)			    memset(&(ip), 0, sizeof((ip)))
#endif /* SMB_VFS_INTERFACE_VERSION >= SMB_V3_VFS */

#ifdef FreeBSD_DW
#  define DEFAULT_CONF_PATH "/usr/local/etc/drweb"
#else /* FreeBSD_DW */
#  define DEFAULT_CONF_PATH "/etc/drweb"
#endif

#define DEFAULT_VAR_PATH "/var/drweb"
#define DEFUALT_QUARANTINE_PATH DEFAULT_VAR_PATH"/infected"

#define _QUOTEME(arg) #arg
#define QUOTEME(arg) _QUOTEME(arg)

#undef DW_VERSION
#define DW_VERSION QUOTEME(RELEASE)

/* Prototypes */
#if SMB_VFS_INTERFACE_VERSION < 21
static int dwsmb_connect ( THIS_ARG struct connection_struct *conn, CONST_ARG char *service, CONST_ARG char *user );
static int dwsmb_open ( THIS_ARG struct connection_struct *conn, CONST_ARG char *fname, int flags, mode_t mode );
static void dwsmb_disconnect ( THIS_ARG struct connection_struct *conn );
#else
static int dwsmb_connect ( THIS_ARG CONST_ARG char *service, CONST_ARG char *user );
static int dwsmb_open ( THIS_ARG CONST_ARG char *fname, struct files_struct *fsp, int flags, mode_t mode );
static void dwsmb_disconnect ( HANDLE_ARG );
#endif
static dw_init_vfs_ret_t dwsmb_init_vfs_ops ( dw_init_vfs_arg_t );
static dwc_bool_t dwsmb_notify_message( dwc_string_t cli_addr, dwc_path_t filename, dwc_scan_result_t res, pstring inf_addr );
static int dwsmb_scan ( THIS_ARG struct connection_struct *conn, const char *fname, const char *cpath, int flags, int op );
#if SMB_VFS_INTERFACE_VERSION > 21
static int dwsmb_close ( THIS_ARG struct files_struct *fsp);
#else
static int dwsmb_close ( THIS_ARG struct files_struct *fsp, int fd);
#endif

static int init_connection();

/* Global variables */
static dwc_bool_t	    dwsmb_loaded;
static dw_filter_settings_t dwsmb_conf;
const  char		    dw_smb_pid_path[] = DEFAULT_VAR_PATH"/run"; /* path to pid-files */
int locked_fd;
char service_ext[128]=""; /* name of service */
char config_address[4096]=""; /* address of config */
char close_command[1024]="";
enum { MAX_REPORT_SIZE = 4096 };
char _report[MAX_REPORT_SIZE]="";
char *report = _report;
dwc_bool_t load_configuration();

/* To understand a problem: Samba define some functions for strings
 * with definition "__ERROR__XX__NEVER_USE_STR..."
 * We will undefine them back.
 */
#undef sprintf
#undef strcat
#undef strcpy

#if SMB_VFS_INTERFACE_VERSION < SMB_V3_VFS
/* Default VFS operations switch */
extern struct vfs_ops	    default_vfs_ops;
#endif /* SMB_VFS_INTERFACE_VERSION < SMB_V3_VFS */

char skipped_file_name[1024] = "";
const char version_info[] = "Version: Dr.Web (R) Samba VFS module version: "DW_VERSION;
const char build_info[] = "Build info: VFS version "SMB_VFS_INTERFACE_VERSION_STRING" Samba build version "SAMBA_VERSION_OFFICIAL_STRING;

/*-------------------------------------------------------------------------*/
const char * dw_get_user_name(struct connection_struct *conn)
{
#if SMB_VFS_INTERFACE_VERSION < 21
  return conn->user;
#else
  return get_current_username();
#endif
}
/*-------------------------------------------------------------------------*/
typeof(printLib) * _printLib = &printLib;

#define printLib(level, args...)		\
do {						\
  int smb_level = 0;				\
  _printLib(level, args);			\
  if (level>3) smb_level = 1;			\
  if (level>4) smb_level = 2;			\
  if (level>5) smb_level = 3;			\
  DEBUG(smb_level, ("smb_spider: "));		\
  DEBUG(smb_level, (args));			\
  DEBUG(smb_level, ("\n"));			\
} while (0)
/*-------------------------------------------------------------------------*/
dwc_bool_t skip_special_files(SMB_STRUCT_STAT * st, const char * fname)
{
        const char * type_str = NULL;

        if (S_ISBLK(st->st_mode)) type_str = "block special file";
        else if (S_ISCHR(st->st_mode)) type_str = "character special file";
        else if (S_ISDIR(st->st_mode)) type_str = "directory";
        else if (S_ISFIFO(st->st_mode)) type_str = "pipe or FIFO special file";
        else if (S_ISSOCK(st->st_mode)) type_str = "socket";

        if (type_str == NULL)
                return FALSE;

        printLib(DW_LL_VERBOSE, "'%s' is a %s", fname, type_str);
        return TRUE;
}
/*-------------------------------------------------------------------------*/
static void script_runner( void* ptr )
{
	dwc_multistring_t   args = (dwc_multistring_t) ptr;
	dwc_string_t	    tmp;
	int		    num;
	char**		    arg_ptr = NULL;
	char**		    sargv = NULL;

	if( !args )
	{
		printLib( DW_LL_ERROR, "(script) no args for script" );
		exit(EX_SOFTWARE);
	}
	num = sizeof(char*) * (1 + dw_multistring_count(args));
	sargv = DWC_Malloc( char**, num );
	if( !sargv )
	{
		printLib( DW_LL_ERROR, "(script) no args for script" );
		exit(EX_OSERR);
	}
	arg_ptr = sargv;
	dw_multistring_rewind( args, DW_SD_FORWARD );
	while( NULL != (tmp = dw_multistring_getnext(args)) )
	{
		*arg_ptr++ = tmp;
	}
	*arg_ptr++ = NULL;
	execv( dwsmb_conf.action.script, sargv );
	printLib( DW_LL_ERROR, "(script) cannot run %s - %s",
		  dwsmb_conf.action.script, stdlib_error(errno) );
	exit(EX_OSERR);
}

/*-------------------------------------------------------------------------*/
char * path_concatantion( const char *cpath, const char *fname )
{
	size_t	len;
	int	rc;
	char	*fullpath = NULL;

	len = strlen( cpath );
	len += strlen( fname );
	len += 2 * sizeof( char ); /* asv: for separator and zero */
	if( len > PATH_MAX )
	{
		printLib( DW_LL_ALERT, "(path_concatantion) path too long (length = %d) - cannot be checked", len );
		return NULL;
	}
	fullpath = DWC_Malloc( char*, len );
	if( !fullpath )
	{
		printLib( DW_LL_ERROR, "(path_concatantion) no memory for path" );
		return NULL;
	}
	rc = snprintf( fullpath, len, "%s%c%s", cpath, PATH_SEPARATOR, fname );
	if( rc != (len - 1) )
	{
		printLib( DW_LL_ERROR, "(path_concatantion) cannot format full path (need %d, wrote %d)", len-1, rc );
		DWC_Free( fullpath );
		return NULL;
	}
	return fullpath;
}
/*-------------------------------------------------------------------------*/
#define dr_become_root() do { printLib(DW_LL_DEBUG, "%d: dr_become_root", __LINE__); dr_become_root_(); } while (0)
#define dr_unbecome_root() do { printLib(DW_LL_DEBUG, "%d: dr_unbecome_root", __LINE__); dr_unbecome_root_(); } while (0)

int dr_root = 0;

void dr_become_root_()
{
	if (dr_root != 0) return;
	dr_root = 1;
	become_root();
}

void dr_unbecome_root_()
{
	if (dr_root == 0) return;
	dr_root = 0;
	unbecome_root();
}
/*-------------------------------------------------------------------------*/
static void fork_script( struct connection_struct *conn, const char* name )
{
	char		   *fullpath = NULL;
	dwc_multistring_t   args = NULL;

	if( !DW_HAVE_A_PATH(dwsmb_conf.action.script) )
		return;
	if( (fullpath=path_concatantion(conn->connectpath, name)) == NULL )
	{
		printLib( DW_LL_ERROR, "(script) cannot build path" );
		return;
	}
	args = dw_multistring_create(0);
	if( !args )
	{
		printLib( DW_LL_ERROR, "(script) no memory for args" );
		goto error_exit;
	}

	/* replace \n on \\n for report */
	// if (report)
	{
		char result[MAX_REPORT_SIZE] = "";
		char * forward = report;
		char * backward = result;
		while (*forward && ((backward-result)<MAX_REPORT_SIZE)) {
			if (*forward == '\n') {
				*backward++ = '\\';
				*backward++ = 'n';
				forward++;
			} else {
				*backward++ = *forward++;
			}
		}
		strcpy(report, result);
	}

	if( !dw_multistring_add( args, dwsmb_conf.action.script )
	    ||
	    !dw_multistring_add( args, fullpath )
	    ||
	    !dw_multistring_add( args, dw_get_user_name(conn) )
	    ||
	    !dw_multistring_add( args, conn->client_address )
	    ||
	    !dw_multistring_add( args, (report)) )
	{
		printLib( DW_LL_ERROR, "(script) no memory for args" );
		goto error_exit;
	}
	if( dw_dfork( script_runner, NULL, args ) < 0 )
	{
		printLib( DW_LL_ERROR, "(script) cannot fork for run script" );
		goto error_exit;
	}
error_exit:
	DWC_Free(fullpath);
	dw_multistring_delete(args);
	return;
}
/*-------------------------------------------------------------------------*/
int block_file(SMB_DEV_T dev, SMB_INO_T ino, SMB_OFF_T size, unsigned char * hash)
{
	TDB_DATA keybuffer, databuffer;
        TDB_CONTEXT * tdb;
        int ret;
	dwsmb_tdb_key key={ ino, dev, size, "" };
	strncpy((char *)(key.hash), (char *)hash, HASH_SIZE);
	keybuffer.dptr = (typeof(keybuffer.dptr))&key;
	keybuffer.dsize = sizeof(key);
	databuffer.dptr = (typeof(keybuffer.dptr))"";
	databuffer.dsize = 1;
	printLib(DW_LL_DEBUG, "(block_file) key size = %d", sizeof(key));

        tdb = drweb_open_tdb(lock_path(blocked_base_name));
        if (!tdb) return -1;
	ret = tdb_store(tdb, keybuffer, databuffer, TDB_REPLACE);
	tdb_close(tdb);
	return ret;
}
/*-------------------------------------------------------------------------*/
int allow_file(SMB_DEV_T dev, SMB_INO_T ino, SMB_OFF_T size, unsigned char * hash)
{
	TDB_DATA keybuffer, databuffer;
        TDB_CONTEXT * tdb;
        int ret;
	dwsmb_tdb_key key={ ino, dev, size, "" };
	strncpy((char *)(key.hash), (char *)hash, HASH_SIZE);
	keybuffer.dptr = (typeof(keybuffer.dptr))&key;
	keybuffer.dsize = sizeof(key);
	databuffer.dptr = (typeof(keybuffer.dptr))"";
	databuffer.dsize = 1;
	printLib(DW_LL_DEBUG, "(allow_file) key size = %d", sizeof(key));

	tdb = drweb_open_tdb(lock_path(allowed_base_name));
	if (!tdb) return -1;
	ret = tdb_store(tdb, keybuffer, databuffer, TDB_REPLACE);
	tdb_close(tdb);
	return ret;
}
/*-------------------------------------------------------------------------*/
int find_blocked_file(SMB_DEV_T dev, SMB_INO_T ino, SMB_OFF_T size, unsigned char * hash)
{
	TDB_DATA keybuffer;
        TDB_CONTEXT * tdb;
        int ret;
	dwsmb_tdb_key key = { ino, dev, size, "" };
	strncpy((char *)(key.hash), (char *)hash, HASH_SIZE);
	keybuffer.dptr = (typeof(keybuffer.dptr))&key;
	keybuffer.dsize = sizeof(key);
	printLib(DW_LL_DEBUG, "(find_blocked_file) key size = %d", sizeof(key));

	tdb = drweb_open_tdb(lock_path(blocked_base_name));
	if (!tdb) return 0;
	ret = tdb_exists(tdb, keybuffer);
	tdb_close(tdb);
	return ret;
}
/*-------------------------------------------------------------------------*/
int find_allowed_file(SMB_DEV_T dev, SMB_INO_T ino, SMB_OFF_T size, unsigned char * hash)
{
	TDB_DATA keybuffer;
        TDB_CONTEXT * tdb;
        int ret;
	dwsmb_tdb_key key = { ino, dev, size, "" };
	strncpy((char *)(key.hash), (char *)hash, HASH_SIZE);
	keybuffer.dptr = (typeof(keybuffer.dptr))&key;
	keybuffer.dsize = sizeof(key);
	printLib(DW_LL_DEBUG, "(find_allowed_file) key size = %d", sizeof(key));

	tdb = drweb_open_tdb(lock_path(allowed_base_name));
	if (!tdb) return 0;
	ret = tdb_exists(tdb, keybuffer);
	tdb_close(tdb);
	return ret;
}
/*-------------------------------------------------------------------------*/
char * renamed_name(const char * old_name)
{
	static char new_name[PATH_MAX]="";
        char * point;

	*new_name='\0';

	if (old_name && strlen(old_name))
		strcpy(new_name, old_name);

	point = strrchr(new_name, '.');

	if (point < strrchr(new_name, '/'))
		strcat(new_name, ".#");
	else
		*(point+1) = '#';

	printLib(DW_LL_DEBUG, "return renamed name '%s'", new_name);
	return new_name;
}
/*-------------------------------------------------------------------------*/
char * quarantine_name(const char * old_name)
{
	static char new_name[PATH_MAX]="";
        static char new_other_name[PATH_MAX]="";
	static char quarantine_dir[PATH_MAX] = DEFUALT_QUARANTINE_PATH;
        char *last_symbol, *slash;
        struct stat st;
        int filename_suffix = 0;

        if (dwsmb_conf.action.quarantine.tf_path)
          strcpy(quarantine_dir, dwsmb_conf.action.quarantine.tf_path);

	last_symbol = quarantine_dir+strlen(quarantine_dir)-1;

	if (*last_symbol == '/')
		*last_symbol = '\0';

	strcpy(new_name, quarantine_dir);
	strcat(new_name, "/");

	slash = strrchr(old_name, '/');
	if (strrchr == NULL)
		strcat(new_name, old_name);
	else
		strcat(new_name, slash+1);

        strcpy(new_other_name, new_name);

        dr_become_root();
        while (!stat(new_other_name, &st))
        {
                filename_suffix++;
                snprintf(new_other_name, PATH_MAX, "%s(%d)", new_name, filename_suffix);
        }
        dr_become_root();

        if (filename_suffix)
                strcpy(new_name, new_other_name);

	printLib(DW_LL_DEBUG, "return quarantine name '%s'", new_name);
	return new_name;
}
/*-------------------------------------------------------------------------*/
void create_file_from_buffer(const char *fname,
			     dwc_scan_result_t res)
{
        int fd;
	struct stat st;
	mode_t mode = 0644;

	if (stat(fname, &st) == 0)
		mode = st.st_mode;

	unlink(fname);

        if (!getResultCuredLength(res)) return;

	printLib(DW_LL_VERBOSE, "try write cured file '%s'", fname);

	fd = open(fname, O_RDWR | O_CREAT, mode);

	if (fd <= 0)
	{
		printLib(DW_LL_ALERT, "cannot open() %s to write cured file - '%s'",
			 fname, strerror(errno));
		return;
	}

	if( dw_write_buf( fd, getResultCuredBuffer(res), getResultCuredLength(res)))
	{
		printLib(DW_LL_INFO, "Cured file has been saved as '%s'", fname );
	}
	else
	{
		printLib(DW_LL_ALERT, "Cannot write cured file %s in current directory.", fname );
		unlink(fname);
	}
	return;
}

/*-------------------------------------------------------------------------*/
void file_operation(const char *fullpath,
		    dwc_action_t action,
                    dwc_scan_result_t res)
{
        const char * qname = NULL;
	printLib(DW_LL_DEBUG, "choose operation for '%s'", fullpath);
	if (action == DW_ACT_QUARANTINE)
	{
		dr_become_root();
#if 0
                qname = dw_quarantine_file( &dwsmb_conf.action.quarantine, fullpath, TRUE );
		printLib(DW_LL_INFO, "move file %s to '%s', chmod:%o", fullpath, qname, dwsmb_conf.action.quarantine.tf_access);
#else
		qname = getResultQuarantine(res);
                if (!qname) qname = quarantine_name(fullpath);
		printLib(DW_LL_INFO, "move file %s to '%s', chmod:%o", fullpath, qname, dwsmb_conf.action.quarantine.tf_access);
                /// todo: fix warnings
		dw_move_file((const dwc_path_t)fullpath, (const dwc_path_t)qname, dwsmb_conf.action.quarantine.tf_access);
#endif
		dr_unbecome_root();
	}
	else if (action == DW_ACT_RENAME)
	{
		const char * new_name = renamed_name(fullpath);
		printLib(DW_LL_INFO, "rename file %s to '%s'",
			 fullpath, new_name);
		if (rename(fullpath, new_name) != 0)
			printLib(DW_LL_ALERT, "cannot rename file '%s' - %s",
				 fullpath, strerror(errno));
	}
	else if (action == DW_ACT_DISCARD)
	{
		printLib(DW_LL_INFO, "unlink file '%s'", fullpath);
		if (unlink(fullpath) != 0)
			printLib(DW_LL_ALERT, "cannot unlink file '%s' - %s",
				 fullpath, strerror(errno));
	}

	if (action == DW_ACT_CURE)
		printLib(DW_LL_ALERT, "file operation = cure, is logic error!");
}
/*-------------------------------------------------------------------------*/
void exec_file_action(const char *fullpath,
		      dwc_scan_result_t res,
		      int is_local_scan)
{
	int ret = getResultStatus(res);

	printLib(DW_LL_DEBUG, "choose action for '%s'", fullpath);

	if ((ret & DERR_HAVE_CURED) && !is_local_scan)
		create_file_from_buffer(fullpath, res);
	else if (ret & DERR_INFECTED)
		file_operation(fullpath, dwsmb_conf.action.on_incurable, res);
	else if (ret & DERR_SUSPICIOUS)
		file_operation(fullpath, dwsmb_conf.action.on_suspicious, res);
	else if (ret & DERR_ADWARE)
		file_operation(fullpath, dwsmb_conf.action.on_adware, res);
	else if (ret & DERR_DIALER)
		file_operation(fullpath, dwsmb_conf.action.on_dialer, res);
	else if (ret & DERR_JOKE)
		file_operation(fullpath, dwsmb_conf.action.on_joke, res);
	else if (ret & DERR_RISKWARE)
		file_operation(fullpath, dwsmb_conf.action.on_riskware, res);
	else if (ret & DERR_HACKTOOL)
		file_operation(fullpath, dwsmb_conf.action.on_hacktool, res);
}
/*-------------------------------------------------------------------------*/
#define SET_OPTIONS( act, opt, tag )			\
BEGIN_MACRO						\
switch( (act) )						\
{							\
case DW_ACT_RENAME:					\
	(opt) |= DRWEBD_ ## tag ## _RENAME; break;	\
case DW_ACT_QUARANTINE:					\
	(opt) |= DRWEBD_ ## tag ## _MOVE; break;	\
case DW_ACT_DISCARD:					\
	(opt) |= DRWEBD_ ## tag ## _DELETE; break;	\
default: break;						\
}							\
END_MACRO
/*-------------------------------------------------------------------------*/
#define SET_EXT_OPTIONS( act, opt, tag )		\
BEGIN_MACRO						\
switch( (act) )						\
{							\
case DW_ACT_RENAME:					\
	(opt) |= DRWEBD_EXT_ ## tag ## _RENAME;	break;	\
case DW_ACT_QUARANTINE:					\
	(opt) |= DRWEBD_EXT_ ## tag ## _MOVE; break;	\
case DW_ACT_DISCARD:					\
	(opt) |= DRWEBD_EXT_ ## tag ## _DELETE;	break;	\
case DW_ACT_PASS:					\
	(opt) |= DRWEBD_EXT_ ## tag ## _IGNORE;	break;	\
default: break;						\
}							\
END_MACRO
/*-------------------------------------------------------------------------*/
#define BLOCK_FILE()							\
BEGIN_MACRO								\
rc = -1;								\
/* skip adding files in the blocked					\
 * files database for not scanned files */				\
if ((!scanned) || !(ret & DERR_VIRUS_MASK))				\
	printLib( DW_LL_ALERT,						\
		  "(block) file %s will not be added to the database "	\
		  "blocked files (file not scanned)", fname );		\
else if (!use_blocked_tdb)						\
	printLib( DW_LL_VERBOSE,					\
		  "(block) database for blocked is not initialized");	\
else {									\
	if (dw_file_md5(fullpath, hash ))				\
		printLib( DW_LL_DEBUG,					\
			  "(block) hash=%s", (char *)hash);		\
	else *hash='\0';						\
									\
	if (block_file(st.st_dev, st.st_ino, st.st_size, hash) == 0)	\
		printLib( DW_LL_ALERT,					\
			  "(block) file %s successfully blocked", fname ); \
	else {								\
		printLib( DW_LL_ERROR,					\
			  "(block) file %s cannot be blocked", fname );	\
		rc=-2;							\
	}								\
}									\
goto error_exit;							\
END_MACRO
/*-------------------------------------------------------------------------*/
#define ALLOW_FILE()							\
BEGIN_MACRO								\
/* skip adding files in the allowed files database			\
 * for not enough clean files */					\
 if (!res || !scanned || (ret &                                         \
                       (DERR_READ_ERR                                   \
                        | DERR_WRITE_ERR                                \
                        | DERR_EVAL_KEY					\
                        | DERR_FILTER_REJECT				\
                        | DERR_HAVE_DELETED				\
                        | DERR_LICENSE_ERROR				\
                        | DERR_SPAM					\
                        | DERR_VIRUS_MASK                               \
                        | DERR_SKIP_OBJECT                              \
                        | DERR_ARCHIVE_RESTRICTION                      \
                        | DERR_DAEMON_ERROR)))				\
{									\
	printLib( DW_LL_VERBOSE,					\
		  "(scan) file %s will not be added to the database allowed files (not enough clean file)", \
		  fname );						\
}									\
else if (!use_allowed_tdb)						\
	printLib( DW_LL_VERBOSE,					\
		  "(allow) database for allowed is not initialized");	\
else									\
{									\
	if (dw_file_md5(fullpath, hash ))				\
		printLib( DW_LL_DEBUG, "(allow) hash=%s", (char *)hash); \
	else								\
		*hash='\0';						\
									\
	if (allow_file(st.st_dev, st.st_ino, st.st_size, hash) == 0)	\
		printLib( DW_LL_VERBOSE,					\
			  "(allow) file %s successfully added to the allowed base", fname ); \
	else								\
		printLib( DW_LL_ALERT,					\
			  "(allow) file %s cannot be added to the allowed base", fname ); \
}									\
END_MACRO

/*-------------------------------------------------------------------------*/
#define ACTION_ON( level, what, desc )					\
BEGIN_MACRO								\
if (( ret & DERR_ ## level )						\
    &&									\
    ( dwsmb_conf.action.on_ ## what != DW_ACT_PASS ))			\
{									\
	printLib( DW_LL_ALERT,						\
		  "(scan) file %s (user = %s [%s]) %s",			\
		  fname, dw_get_user_name(conn), conn->client_address, desc ); \
	BLOCK_FILE();							\
}									\
END_MACRO
/*-------------------------------------------------------------------------*/
static int dwsmb_scan ( THIS_ARG struct connection_struct *conn,
			const char *fname, const char *cpath,
			int flags, int op )
{
	int		    rc, ret, options, ext_options, rep_size = 0, sres = 0;
	char		   *fullpath = NULL;
	char		   *tmppath = NULL;
	size_t		    len;
	SMB_STRUCT_STAT	    st;
	dwc_scan_result_t   res = NULL;
	dwc_bool_t	    scanned = FALSE;
	unsigned char hash[HASH_SIZE]="";
	dwc_bool_t	    is_local_scan = dwsmb_conf.scan.local;

        printLib(DW_LL_DEBUG, "is_local_scan = %d", is_local_scan);

	EXIT_IF_3PARAM( !conn, !fname, !cpath, "(scan)", return (dwsmb_conf.action.on_proc_error != DW_ACT_PASS ? -1 : 0) );
	len = strlen( cpath );
	len += strlen( fname );
	len += 2 * sizeof( char ); /* asv: for separator and zero */
	if( len > PATH_MAX )
	{
		printLib( DW_LL_ALERT, "(scan) path too long (length = %d) - cannot be checked", len );
		if( dwsmb_conf.action.on_proc_error != DW_ACT_PASS )
			return -2;
		else
			return 0;
	}
	fullpath = DWC_Malloc( char*, len );
	if( !fullpath )
	{
		printLib( DW_LL_ERROR, "(scan) no memory for path" );
		if( dwsmb_conf.action.on_proc_error != DW_ACT_PASS )
			return -2;
		else
			return 0;
	}
	rc = snprintf( fullpath, len, "%s%c%s", cpath, PATH_SEPARATOR, fname );
	if( rc != (len - 1) )
	{
		printLib( DW_LL_ERROR, "(scan) cannot format full path (need %d, wrote %d)", len-1, rc );
		if( dwsmb_conf.action.on_proc_error != DW_ACT_PASS )
			return -2;
		else
			goto ok_exit;
	}
	/* Race condition available, but we physically cannot avoid
	 * race conditions on Samba-VFS. Example - file can be created (or modified)
	 * locally (not thru smbfs), after daemon checks this
	 */
	if( DW_OPS_STAT( conn, fullpath, &st ) < 0 )
	{
		if( errno == ENOENT )
		{
			if( (op == DWSMB_OP_OPEN) && (flags & O_CREAT) ) {
				printLib( DW_LL_VERBOSE, "(scan) request on create file - scan is skipped" );
				/* save file name for feature events. dwsmb_close don't get filename for some platforms
				   (RHEL3, Samba 3.0.9 */
				strcpy(skipped_file_name, fname);
				goto ok_exit;
			} else {
				printLib( DW_LL_VERBOSE, "(scan) nonexisten file to open - scan skipped, access is blocked" );
				goto error_exit;
			}
		}
		else
		{
			printLib( DW_LL_ERROR, "(scan) cannot stat file - %s", stdlib_error(errno) );
			if( dwsmb_conf.action.on_proc_error != DW_ACT_PASS ) {
				BLOCK_FILE();
			} else {
				goto ok_exit;
			}
		}
	}
	else if (st.st_size == 0) {
		printLib( DW_LL_VERBOSE, "(scan) file size %lu equal zero - scan is skipped", st.st_size );
		goto ok_exit;
	}
	if ((dwsmb_conf.scan.max_size_to_scan > 0)
	    &&
	    (st.st_size >= dwsmb_conf.scan.max_size_to_scan) )
	{
		printLib( DW_LL_ALERT, "(scan) file size %lu greater that limit %lu - scan is skipped",
			  st.st_size, dwsmb_conf.scan.max_size_to_scan );
		goto ok_exit;
	}

        if (skip_special_files(&st, fname))
        {
                printLib(DW_LL_VERBOSE, "'%s' is not regular file, skipped", fname);
                goto ok_exit;
        }

	options = DRWEBD_HAVE_EXTENDED;
	ext_options = 0;
	if( dwsmb_conf.scan.heuristic )
		options |= DRWEBD_HEURISTIC_ON;
	if( dwsmb_conf.action.on_infected == DW_ACT_CURE )
		options |= DRWEBD_INFECTED_CURE;
	if( dwsmb_conf.action.notify_to_user || dwsmb_conf.action.notify_to_admin || DW_HAVE_A_PATH(dwsmb_conf.action.script))
	{
		options |= DRWEBD_RETURN_REPORT;
		rep_size = sizeof(pstring) - 100 - strlen(dw_basename(fullpath));
		if( rep_size < 100 )
			rep_size = 100;
	}

        /// демон выполняет действия только для локального сканирования (#26567)
        if (is_local_scan)
        {
                SET_OPTIONS( dwsmb_conf.action.on_infected, options, INFECTED );
                SET_OPTIONS( dwsmb_conf.action.on_suspicious, options, SUSPECTED );
                SET_OPTIONS( dwsmb_conf.action.on_incurable, options, INCURABLE );
                SET_OPTIONS( dwsmb_conf.action.on_archive, options, ARCHIVE );
                SET_EXT_OPTIONS( dwsmb_conf.action.on_adware, ext_options, ADWARE );
                SET_EXT_OPTIONS( dwsmb_conf.action.on_dialer, ext_options, DIALER );
                SET_EXT_OPTIONS( dwsmb_conf.action.on_joke, ext_options, JOKE );
                SET_EXT_OPTIONS( dwsmb_conf.action.on_riskware, ext_options, RISKWARE );
                SET_EXT_OPTIONS( dwsmb_conf.action.on_hacktool, ext_options, HACKTOOL );
        }

	printLib( DW_LL_DEBUG, "(scan) options for scan %#X:%#X", options, ext_options );
	if( dwsmb_conf.scan.prefix_path )
	{
		tmppath = dw_normalize_path( fullpath, FALSE, dwsmb_conf.scan.prefix_path, dwsmb_conf.scan.strip_path );
		if( !tmppath )
		{
			printLib( DW_LL_ALERT, "(scan) cannot normalize path %s - cannot be checked", fullpath );
			if( dwsmb_conf.action.on_proc_error != DW_ACT_PASS ) {
				rc = -2;
				BLOCK_FILE();
			} else	{
				goto ok_exit;
			}
		}
		DWC_Free( fullpath );
		fullpath = tmppath;
		len = strlen( fullpath);
		len += sizeof( char);
	}

        init_connection();

        printLib(DW_LL_VERBOSE, "(scan) try %s scanning (1)", (is_local_scan ? "local" : "not local"));
	dr_become_root();
	rc = scanFile( fullpath , len - 1, is_local_scan, options, ext_options, &res, rep_size );
	dr_unbecome_root();

	if(rc != DWL_NOERROR)
        {
		printLib( DW_LL_ALERT, "(scan) file %s is blocked, because has not been checked - library error (%x)", fname, rc );

                if (dwsmb_conf.action.on_proc_error != DW_ACT_PASS )
                {
                        rc = -2;
                        goto error_exit;
                }
        }
	else
		scanned = TRUE;

	rc = -2;
	ret = getResultStatus( res );

	/// определяем условия того, что нам необходимо просканировать
	/// файл нелокально.

	/// во-первых если он до этого был просканирован локально
	if (is_local_scan)
	{
		/// если ошибка чтения (у демона скорее всего
		/// нет прав даже прочитать файл
		if ((ret & DERR_READ_ERR)
		    ||
		    /// демон попытался вылечить файл, но не
		    /// смог записать его
		    (dw_bm_contain(ret, DERR_KNOWN_VIRUS | DERR_WRITE_ERR)
		     && (dwsmb_conf.action.on_infected == DW_ACT_CURE)))
			/// тогда запускаем нелокальное сканирование
		{
			is_local_scan = FALSE;
                        printLib(DW_LL_VERBOSE, "(scan) try %s scanning (2)", (is_local_scan ? "local" : "not local"));
			dr_become_root();
			rc = scanFile( fullpath , len - 1, is_local_scan, options, ext_options, &res, rep_size );
			dr_unbecome_root();

			if( (rc != DWL_NOERROR) && (dwsmb_conf.action.on_proc_error == DW_ACT_REJECT) ) {
				rc = -2;
				printLib( DW_LL_ALERT, "(scan) file %s is blocked, because has not been checked - library error (%x)", fname, rc );
				BLOCK_FILE();
			}
			else
				scanned = TRUE;

			rc = -2;
			ret = getResultStatus( res );
		}
	}

	strcpy(report, (res ? getResultReport(res) : ""));
	report[MAX_REPORT_SIZE] = '\0';

	printLib( DW_LL_VERBOSE, "(scan) DrWeb daemon return %#x code for file %s", ret, fname );

	if( dw_bm_contain(ret, DERR_LICENSE_ERROR) )
	{
		printLib( DW_LL_ALERT, "(scan) file %s (user = %s [%s]) has not been checked due to license reasons",
			  fname, dw_get_user_name(conn), conn->client_address );
		if( dwsmb_conf.action.on_license_error == DW_ACT_REJECT )
			BLOCK_FILE();
	}
	if( dw_bm_contain(ret, DERR_VIRUS_MASK)
	    &&
	    !dw_bm_contain(ret, DERR_HAVE_CURED | DERR_HAVE_DELETED) )
	{
		if( ( (ret & DERR_VIRUS_MODIFICATION)
		      ||
		      ( (ret & DERR_KNOWN_VIRUS)
			&&
			(dwsmb_conf.action.on_infected != DW_ACT_CURE) )
		      ||
		      (ret & DERR_EVAL_KEY) )
		    &&
		    (dwsmb_conf.action.on_incurable == DW_ACT_REJECT) )
		{
			printLib( DW_LL_ALERT, "(scan) file %s (user = %s [%s]) infected and incurable - access is blocked",
				  fname, dw_get_user_name(conn), conn->client_address );
			rc=-1;
		}

		ACTION_ON( UNKNOWN_VIRUS, suspicious, "probably infected" );
		ACTION_ON( KNOWN_VIRUS, infected, "infected" );
		ACTION_ON( ADWARE, adware, "contains an advertizing software" );
		ACTION_ON( DIALER, dialer, "contains a dialer" );
		ACTION_ON( JOKE, joke, "contains an joke or hoax" );
		ACTION_ON( RISKWARE, riskware, "contains a potentially dangerous software" );
		ACTION_ON( HACKTOOL, hacktool, "contains an intrusion tool" );

		if( (0 != (ret & DERR_UNKNOWN_VIRUS))
		    &&
		    (0 == (ret & (DERR_VIRUS_MASK & ~DERR_UNKNOWN_VIRUS)))
		    &&
		    (dwsmb_conf.action.on_suspicious == DW_ACT_PASS) )
		{
			printLib( DW_LL_INFO, "(scan) a suspicious code has been found in file %s. Passed due to configuration", fname );
			goto error_exit;
		}
		else
		{
			printLib( DW_LL_ALERT, "(scan) a virus or malware has been found in file %s and has not been cured", fname );
			rc=-1;
			goto error_exit;
		}
	}
	else
	{
		if (( ret & DERR_VIRUS_MASK )&&(ret & DERR_HAVE_DELETED))
		{
			printLib( DW_LL_ALERT, "(scan) virus has been found in file %s and has been deleted(moved)", fname );
			rc=-1;
			goto error_exit;
		}
		else if (( ret & DERR_VIRUS_MASK )&&( ret & DERR_HAVE_CURED))
		{
			printLib( DW_LL_ALERT, "(scan) virus has been found in file %s and has been cured", fname);
			rc=-1;
			goto error_exit;
		}
	}
	if( (ret & DERR_ARCHIVE_RESTRICTION) && (dwsmb_conf.action.on_archive_restriction == DW_ACT_REJECT) )
	{
		printLib( DW_LL_ALERT, "(scan) file %s cannot be checked (archive restriction) - access is blocked", fname );
		BLOCK_FILE();
	}
	if( (ret & DERR_SKIP_OBJECT) && (dwsmb_conf.action.on_skip == DW_ACT_REJECT) )
	{
		printLib( DW_LL_ALERT, "(scan) file %s cannot be checked (skipped) - access is blocked", fname );
		BLOCK_FILE();
	}
	if( (ret & DERR_DAEMON_ERROR) && (dwsmb_conf.action.on_scan_error == DW_ACT_REJECT) )
	{
		printLib( DW_LL_ALERT, "(scan) file %s cannot be checked (error) - access is blocked", fname );
		BLOCK_FILE();
	}
ok_exit:
	rc = 0;
	ALLOW_FILE();
error_exit:
	if( rc != 0 && scanned )
	{
		if (!is_local_scan || ( ret & DERR_WRITE_ERR))
		{
			exec_file_action(fullpath, res, is_local_scan);
			rc = -1;
		}
		else if( (op == DWSMB_OP_CLOSE)
			 &&
			 dw_bm_contain( dwsmb_conf.scan.scan_mode, DW_SM_ON_WRITE ) )
		{
			sres = DW_OPS_STAT( conn, fullpath, &st );
			if( ( (sres < 0)
			      &&
			      (ENOENT != errno) )
			    ||
			    (0 == sres) )
			{
                                rc = -1;
			}
		}
		if( dwsmb_conf.action.notify_to_user )
		{
			printLib( DW_LL_VERBOSE, "(scan) try notify user, addr = %s", conn->client_address );
			dwsmb_notify_message( conn->client_address, dw_basename(fullpath), res, NULL );
		}
		if( dwsmb_conf.action.notify_to_admin )
		{
			printLib( DW_LL_VERBOSE, "(scan) try notify admin, addr = %s", dwsmb_conf.action.admin_addr );
			dwsmb_notify_message( dwsmb_conf.action.admin_addr, dw_basename(fullpath), res, conn->client_address );
		}
	}

	clearResult( res );
	DWC_Free( fullpath );
	return rc;
}
/*-------------------------------------------------------------------------*/
#define SMB_MAX_MSG	    1600
#if SMB_VFS_INTERFACE_VERSION < SMB_V3_VFS
#define MSG_PORT	SMB_PORT
extern fstring		remote_machine;
#else /* SMB_VFS_INTERFACE_VERSION >= SMB_V3_VFS */
#define MSG_PORT	SMB_PORT2
#endif /* SMB_VFS_INTERFACE_VERSION < SMB_V3_VFS */

/*-------------------------------------------------------------------------*/
static dwc_bool_t dwsmb_notify_message( dwc_string_t cli_addr, dwc_path_t filename,
					dwc_scan_result_t res, pstring inf_addr )
{
#if SMB_VFS_INTERFACE_VERSION >=25
#  define SOCKADDR(a) ((struct sockaddr *)(a))
#else
#  define SOCKADDR(a) (a)
#endif

#if SMB_VFS_INTERFACE_VERSION >=22
	struct sockaddr_storage cli_ip;
#else
	struct in_addr	    cli_ip;
#endif
	struct nmb_name	    me_name, cli_name;
	struct cli_state   *cli;
	dwc_string_t	    report;
	dwc_string_t	    whom;
	/* asv: remove pstring from stack */
	pstring		    msg;
	pstring		    cmsg;
	pstring		    spider_name;
	int		    len;
	int		    grp_id;
	int			msg_len;
	fstring		    remote_machine,resolved_name;
	char		    name_type_hex[10];

	EXIT_IF_3PARAM( !cli_addr, !filename, !res, "(notify)", return FALSE );

	if( inf_addr )
		whom = "admin";
	else
		whom = "client";
#if SMB_VFS_INTERFACE_VERSION >=22

	if (!get_pdc_ip(cli_addr, &cli_ip)) {
		printLib( DW_LL_ERROR, "(notify) cannot get %s IP address", whom );
		return false;
	}

	if (is_zero_addr(SOCKADDR(&cli_ip))) {
		printLib( DW_LL_ERROR, "(notify) is zerro address for %s", cli_addr);
		return false;
	}

#else  /* SMB_VFS_INTERFACE_VERSION < 22 */

	DW_ZERO_IP(cli_ip);
#if defined(__WIN32_DW) || defined(Solaris_DW) || defined(EMX_DW)
	cli_ip.s_addr = inet_addr(  cli_addr );
	if ( cli_ip.s_addr == INADDR_NONE )
	{
		printLib( DW_LL_ERROR, "(notify) cannot get %s IP address", whom );
		return FALSE;
	}
#else /* !(__WIN32_DW || Solaris || EMX) */
	if( inet_aton( cli_addr, &cli_ip ) == 0 )
	{
		printLib( DW_LL_ERROR, "(notify) cannot get %s IP address", whom );
		return FALSE;
	}
#endif /* __WIN32_DW || Solaris */

#endif	/* SMB_VFS_INTERFACE_VERSION >=22 */




#if SMB_VFS_INTERFACE_VERSION >= SMB_V3_VFS
	fstrcpy( remote_machine, get_remote_machine_name());
#endif

#if SMB_VFS_INTERFACE_VERSION >=22
	if ( !name_status_find("*", 0, 0, &cli_ip, resolved_name) )
	{
		printLib( DW_LL_ERROR, "(notify) cannot get %s NetBIOS name", whom );
		fstrcpy(resolved_name, remote_machine);
	}
#elif SMB_VFS_INTERFACE_VERSION >= 2
	if ( !name_status_find("*", 0, 0, cli_ip, resolved_name) )
	{
		printLib( DW_LL_ERROR, "(notify) cannot get %s NetBIOS name", whom );
		fstrcpy(resolved_name, remote_machine);
	}
#else  /* SMB_VFS_INTERFACE_VERSION < 2 */
	if ( !name_status_find( 0, cli_ip, resolved_name) )
	{
		printLib( DW_LL_ERROR, "(notify) cannot get %s NetBIOS name", whom );
		fstrcpy(resolved_name, remote_machine);
	}
#endif /* SMB_VFS_INTERFACE_VERSION >=22 */


	make_nmb_name( &me_name, myhostname(), 0x0 );
	make_nmb_name( &cli_name, remote_machine, MSG_PONG );

	snprintf(name_type_hex, sizeof(name_type_hex), "#%X", MSG_PONG);
	fstrcat(resolved_name, name_type_hex);
	printLib(DW_LL_DEBUG, "(notify) name_type_hex = %s", name_type_hex);

#if SMB_VFS_INTERFACE_VERSION < 21
	cli = cli_initialise( NULL );
#else
	cli = cli_initialise();
#endif /* SMB_VFS_INTERFACE_VERSION < 21 */

	if( !cli )
	{
		printLib( DW_LL_ERROR, "(notify) cannot make session with %s %s (init)",
			  whom, cli_addr );
		return FALSE;
	}

#if SMB_VFS_INTERFACE_VERSION >= 25

        cli_set_port( cli, MSG_PORT );

#elif SAMBA_LONG_VERSION_RELEASE >= 30026
	if( MSG_PORT != cli_set_port( cli, MSG_PORT ) )
	{
		printLib( DW_LL_ERROR, "(notify) cannot make session with %s %s (set port)",
			  whom, cli_addr );
		cli_shutdown( cli );
		return FALSE;
	}
#else
	if( 0 == cli_set_port( cli, MSG_PORT ) )
	{
		printLib( DW_LL_ERROR, "(notify) cannot make session with %s %s (set port)",
			  whom, cli_addr );
		cli_shutdown( cli );
		return FALSE;
	}
#endif /* ! SAMBA_LONG_VERSION_RELEASE >= 30026 */


	printLib(DW_LL_DEBUG, "(notify) address: %s, port: %d, send to %s, resoleved name: %s",
		 cli_addr, MSG_PORT, whom, resolved_name);



#if SAMBA_LONG_VERSION_RELEASE >= 30026 // 3.0.26
	if (!NT_STATUS_IS_OK(cli_connect(cli, resolved_name, &cli_ip)))
	{
		printLib( DW_LL_ERROR, "(notify) cannot make session with %s %s (connect)",
			  whom, cli_addr );
		cli_shutdown( cli );
		return FALSE;
	}
#else
	if( !cli_connect( cli, resolved_name, &cli_ip ) )
	{
		printLib( DW_LL_ERROR, "(notify) cannot make session with %s %s (connect)",
			  whom, cli_addr );
		cli_shutdown( cli );
		return FALSE;
	}
#endif /* ! SAMBA_LONG_VERSION_RELEASE >= 30026 */

	if( !cli_session_request( cli, &me_name, &cli_name) )
	{
		printLib( DW_LL_ERROR, "(notify) cannot make session with %s %s (session request)",
			  whom, cli_addr );
		cli_shutdown( cli );
		return FALSE;
	}

	ZERO_ARRAY( spider_name );
	snprintf( spider_name, MAX_USERNAME, "Dr.WEB SpIDer "DW_VERSION" on %s", myhostname() );

	if( !cli_message_start( cli, remote_machine, spider_name, &grp_id ) )
	{
		printLib( DW_LL_ERROR, "(notify) cannot send message to %s %s (start)",
			  whom, cli_addr );
		cli_shutdown( cli );
		return FALSE;
	}

	report = getResultReport( res );
	if( !report )
		report = "See logs for details.\r\n";
	ZERO_ARRAY( msg );

	if( inf_addr )
	{
		snprintf( msg, DWC_Min(SMB_MAX_MSG, sizeof(pstring)-1),
			  "Dear Administrator,\r\n\r\nAccess to %s for %s was denied.\r\nReport:\r\n%s\r\n",
			  filename, inf_addr, report );
	}
	else
	{
		snprintf( msg, DWC_Min(SMB_MAX_MSG, sizeof(pstring)-1),
			  "Report for %s:\r\n%s\r\nAccess denied.\r\nPlease contact your system administrator.",
			  filename, report );
	}

	DW_UNIX_TO_DOS( cmsg, msg );

	len = strlen( cmsg );
	for ( msg_len=0;msg_len < len; msg_len+=127)
	{
		int l=DWC_Min(len - msg_len , 127);
		ZERO_ARRAY( msg );
		snprintf(msg,l+1,&cmsg[msg_len]);
		if( !cli_message_text( cli, msg, l, grp_id ) )
		{
			printLib( DW_LL_ERROR, "(notify) cannot send message (size=%d) to %s %s with error: %s (body)",
				  len, whom, cli_addr, cli_errstr(cli) );
			cli_shutdown( cli );
			return FALSE;
		}
	}
	if( !cli_message_end( cli, grp_id ) )
	{
		printLib( DW_LL_ERROR, "(notify) cannot send message to %s %s (end)",
			  whom, cli_addr );
		cli_shutdown( cli );
		return FALSE;
	}
	cli_shutdown(cli);
	printLib( DW_LL_INFO, "(notify) succesfully sent notify message to %s %s",
		  whom, cli_addr );
	return TRUE;
}

/*-------------------------------------------------------------------------*/
/* Implementation of VFS functions					   */
/*-------------------------------------------------------------------------*/

/*-------------------------------------------------------------------------*/
#if SMB_VFS_INTERFACE_VERSION < 21
static int dwsmb_connect ( THIS_ARG struct connection_struct *conn, CONST_ARG char *service, CONST_ARG char *user )
#else
	static int dwsmb_connect ( THIS_ARG CONST_ARG char *service, CONST_ARG char *user )
#endif
{
	char path[100]="";
	char pid_str[10]="";
	int  fd_pid;
	struct flock flptr;
	char *Address[1];
	int  Timeout;
	dwc_bool_t Cache;

	/* agent address, timeout, cache (getting from smb.conf) */

#if SMB_VFS_INTERFACE_VERSION < 9
	Address[0] = (char *) malloc (sizeof(DEFAULT_CONF_PATH"/smb_spider.conf"));
	strcpy(Address[0], DEFAULT_CONF_PATH"/smb_spider.conf");
	Timeout = 120;
	Cache = False;
#elif SMB_VFS_INTERFACE_VERSION < 21
	strcpy(close_command, lp_parm_const_string(SNUM(conn), "smb_spider", "close_command", ""));
	Address[0] =  (char *)lp_parm_const_string(SNUM(conn), "smb_spider", "config",
						   DEFAULT_CONF_PATH"/smb_spider.conf");
	Timeout = lp_parm_int(SNUM(conn), "smb_spider", "Timeout", 120);
	Cache =	 lp_parm_bool(SNUM(conn), "smb_spider", "Cache", False);
#else
	strcpy(close_command, lp_parm_const_string(SNUM(handle->conn), "smb_spider", "close_command", ""));
	Address[0] =  (char *)lp_parm_const_string(SNUM(handle->conn), "smb_spider", "config",
						   DEFAULT_CONF_PATH"/smb_spider.conf");
	Timeout = lp_parm_int(SNUM(handle->conn), "smb_spider", "Timeout", 120);
	Cache =	 lp_parm_bool(SNUM(handle->conn), "smb_spider", "Cache", False);
#endif

	strcpy(config_address,Address[0]);
	printLib(DW_LL_INFO,"(connect) config address = %s, Timeout = %d, Cache = %d", Address[0], Timeout, Cache);

	/* save service name, needed for signal_handlers */
	strcpy(service_ext,service);

	if (!load_configuration())
        {
                printLib(DW_LL_ERROR, "smb_spider exited, please check your configuration");
                return -1;
        }
        printLib( DW_LL_INFO, "(connect) loading configuration from %s successful",config_address);

	printLib( DW_LL_INFO, "(connect) Dr.Web (R) Samba VFS ver."DW_VERSION" vfs.%d has been builded on Samba %s",
		  SMB_VFS_INTERFACE_VERSION, SAMBA_VERSION_OFFICIAL_STRING );
	printLib( DW_LL_DEBUG, "(connect) send notify to user = %d, send notify to admin(%s) = %d",
		  dwsmb_conf.action.notify_to_user, dwsmb_conf.action.admin_addr, dwsmb_conf.action.notify_to_admin );

	/* write pid-file */
	dr_become_root();
	sprintf(path,"%s/smb_spider.hup.%d",dw_smb_pid_path,getpid());
	fd_pid = open(path,O_WRONLY|O_CREAT, 0640);

	if(fd_pid == -1)
	{
		printLib(DW_LL_ALERT,"(connect) open cannot open %s to read/write",path);
	}
	else
	{
		locked_fd = fd_pid;
		flptr.l_start=0;
		flptr.l_whence=SEEK_SET;
		flptr.l_len=0;
		flptr.l_type=F_WRLCK;
		flptr.l_pid=getpid();

		fcntl(fd_pid, F_GETLK, &flptr);

		if(flptr.l_type==F_UNLCK)
		{
			sprintf(pid_str,"%d",getpid());
			write(fd_pid,pid_str,strlen(pid_str));
			flptr.l_start=0;
			flptr.l_whence=SEEK_SET;
			flptr.l_len=0;
			flptr.l_type=F_WRLCK;
			flptr.l_pid=getpid();
			if (fcntl(fd_pid, F_SETLK, &flptr)!=-1)
			{
				printLib(DW_LL_VERBOSE,"(connect) %s locked",path);
			}
			else
			{
				printLib(DW_LL_ALERT,"(connect) cannot lock %s",path);
			}
		}
	}
	dr_unbecome_root();
	/* end */

#if SMB_VFS_INTERFACE_VERSION < 21
	EXIT_IF_3PARAM( !conn, !service, !user, "(connect)", return EINVAL );
	printLib( DW_LL_VERBOSE, "(connect) user %s [%s] connect to service %s (r/o = %d)",
		  user, conn->client_address, service, conn->read_only );
	return DW_OPS_CONNECT(conn, service, user);
#else
	EXIT_IF_2PARAM( !service, !user, "(connect)", return EINVAL );
	printLib( DW_LL_VERBOSE, "(connect) user %s [%s] connect to service %s (r/o = %d)",
		  user, handle->conn->client_address, service, handle->conn->read_only );
	return DW_OPS_CONNECT(handle->conn, service, user);
#endif
}

/*-------------------------------------------------------------------------*/
#if SMB_VFS_INTERFACE_VERSION < 21
static void dwsmb_disconnect( THIS_ARG struct connection_struct *conn )
{
	char path[PATH_MAX]="";
	EXIT_IF_1PARAM( !conn, "(disconnect)", return );
	printLib( DW_LL_VERBOSE, "user %s [%s] disconnect from %s",
		  conn->user, conn->client_address, conn->connectpath );
	sprintf(path,"%s/smb_spider.hup.%d",dw_smb_pid_path,getpid());
	if (remove(path) != 0)
		printLib(DW_LL_ALERT, "(disconnect) cannot remove %s (%s)", path, strerror(errno));
	DW_OPS_DISCONNECT(conn);
}
#else /* SMB_VFS_INTERFACE_VERSION < 21 */
static void dwsmb_disconnect( HANDLE_ARG )
{
	char path[PATH_MAX]="";
	printLib( DW_LL_VERBOSE, "user %s [%s] disconnect from %s",
		  dw_get_user_name(handle->conn), handle->conn->client_address, handle->conn->connectpath );
	sprintf(path,"%s/smb_spider.hup.%d",dw_smb_pid_path,getpid());
	if (remove(path) != 0)
		printLib(DW_LL_ALERT, "(disconnect) cannot remove %s (%s)", path, strerror(errno));
	DW_OPS_DISCONNECT(handle->conn);
}
#endif /* SMB_VFS_INTERFACE_VERSION < 21 */
/*-------------------------------------------------------------------------*/
#if SMB_VFS_INTERFACE_VERSION < 21
static int dwsmb_open ( THIS_ARG struct connection_struct *conn, CONST_ARG char *fname, int flags, mode_t mode )
#else
	static int dwsmb_open ( THIS_ARG CONST_ARG char *fname, struct files_struct *fsp, int flags, mode_t mode )
#endif
{
	int	fd, rc = -1;
	SMB_STRUCT_STAT	    st;
	char		   *fullpath = NULL;
	unsigned char hash[HASH_SIZE] = "";

#if SMB_VFS_INTERFACE_VERSION >= 21
        struct connection_struct *conn = fsp->conn;
#endif

	if( dw_bm_equal(dwsmb_conf.scan.scan_mode, DW_SM_ON_WRITE) )
	{
		printLib( DW_LL_VERBOSE, "(open) skip check, because ScanMode == onWrite" );
		rc = 0;
	}
	else
	{
		EXIT_IF_2PARAM( !conn, !fname, "(open)", return -1 );    
		printLib( DW_LL_VERBOSE, "(open) request (path=%s/%s, flags=0x%x, mode=%oo)", 
			  conn->connectpath, fname, flags, mode );
		if( (fullpath=path_concatantion(conn->connectpath, fname)) == NULL )
		{
			if( dwsmb_conf.action.on_proc_error != DW_ACT_PASS )
			{
				fork_script( conn, fname );
				return -1;
			}
		}

		if ( DW_OPS_STAT( conn, fullpath, &st ) == 0 )
		{
                        if (skip_special_files(&st, fname))
                                rc = 0;
                        else
                        {
                                if ((use_allowed_tdb || use_blocked_tdb) && dw_file_md5(fullpath, hash ))
                                        printLib( DW_LL_DEBUG, "(open) hash=%s", (char *)hash);
                                else *hash='\0';

                                if (use_blocked_tdb && find_blocked_file(st.st_dev,st.st_ino, st.st_size, hash)) {
                                        printLib( DW_LL_ALERT, "(open) file %s already blocked", fname );
                                        return -1;
                                }
                                if (use_allowed_tdb && find_allowed_file(st.st_dev,st.st_ino, st.st_size, hash)) {
                                        printLib( DW_LL_VERBOSE, "(open) file %s already allowed", fname );
                                        rc = 0;
                                }
                        }
		}
	}
	if (rc != 0)
	{
		rc = dwsmb_scan( THIS_VALUE conn, fname, conn->connectpath, flags, DWSMB_OP_OPEN );
	}
	if( rc == 0 )
	{
                if (flags & O_CREAT)
                        printLib(DW_LL_INFO, "%s [%s] - allow write file %s",
                                 dw_get_user_name(conn), conn->client_address, fname);
                else
                        printLib(DW_LL_INFO, "%s [%s] - allow access for file %s",
                                 dw_get_user_name(conn), conn->client_address, fname);

		fd = DW_OPS_OPEN(conn, fname, flags, mode);

		printLib( DW_LL_DEBUG, "(open) name = %s -> fd = %d", fname, fd );
	}
	else
	{
		/* block access to file */
		fd = -1;

#if SMB_VFS_INTERFACE_VERSION < 21
		if (rc == -1 ) fork_script( conn, fname );
#else
		if (rc == -1 ) fork_script( fsp->conn, fname );
#endif
	}
	return fd;
}
/*-------------------------------------------------------------------------*/
#if SMB_VFS_INTERFACE_VERSION > 21
static int dwsmb_close ( THIS_ARG struct files_struct *fsp)
#else
  static int dwsmb_close ( THIS_ARG struct files_struct *fsp, int fd)
#endif
{
#if SMB_VFS_INTERFACE_VERSION > 21
        int fd = fsp->fh->fd;
#endif
	int	       rc;

	/* for an empty name of the file we use the saved value	 */
	char * fname = fsp->fsp_name ? fsp->fsp_name : skipped_file_name;

	EXIT_IF_2PARAM( !fsp, fd < 0, "(close)", return EINVAL );
	printLib( DW_LL_VERBOSE, "(close) request for fd = %d name = %s/%s ",
		  fd, fsp->conn->connectpath, fname );
	rc = DW_OPS_CLOSE(fsp, fd);

	if( rc == 0 )
	{
                SMB_STRUCT_STAT st;
                if ((DW_OPS_STAT( fsp->conn, fname, &st ) == 0)
                    && skip_special_files(&st, fname))
                        return 0;

		if( fsp->modified )
		{
#if SMB_VFS_INTERFACE_VERSION > 4
/* smb_spider: close_command  - run when file closed,
 * skip checking on viruses (needed for Dr.Web internal using)
 * using mask %N, %H, %P, %S, %g, %u, %p, %f ,
 * see man smb.conf,  %f - filname relativity smb service name
 */
			if (*close_command)
			{
#if SMB_VFS_INTERFACE_VERSION >= 22
				standard_sub_conn((TALLOC_CTX*)NULL, fsp->conn, close_command);
#else
				standard_sub_conn(fsp->conn,close_command, sizeof(close_command));
#endif
				string_sub(close_command,"%f", fname, sizeof(close_command));
				printLib(DW_LL_VERBOSE, "(close) close_command=%s", close_command);
				if (smbrun(close_command,NULL) !=0)
					printLib(DW_LL_ALERT, "(close) cannot run close_command=%s", close_command);
				printLib(DW_LL_VERBOSE, "(close) file %s/%s will not be checked by Dr.Web daemon",
					 fsp->conn->connectpath, fname);
				return 0;
			}
#endif
			printLib( DW_LL_VERBOSE, "(close) file %s(%d) will been scanned now", fname, fd );
			if( (rc = dwsmb_scan( THIS_VALUE fsp->conn, fname,
					      fsp->conn->connectpath, O_RDONLY, DWSMB_OP_CLOSE )) != 0 )
			{
				if (rc == -1 ) fork_script( fsp->conn, fname );
				if (rc == -2 )
				{
					// if operation faild, then remove created file
					printLib(DW_LL_VERBOSE, "unlink file %s", fname);
					DW_OPS_UNLINK( fsp->conn, fname );
				}
#if (30200 <= SAMBA_LONG_VERSION_RELEASE) && (SAMBA_LONG_VERSION_RELEASE <= 30302)
                                /* see bug 26496 */
                                fsp->update_write_time_on_close = FALSE;
#endif

				rc = 0;// return 0, else user will be disconnected
			}
		}
		else
			printLib( DW_LL_VERBOSE, "(close) file %s(%d) should not to be scanned", fname, fd );
	}
	printLib( DW_LL_DEBUG, "(close) return %d", rc);
	return rc;
}

/*-------------------------------------------------------------------------*/
/* VFS initialisation							   */
/*-------------------------------------------------------------------------*/

/* VFS operations structure */
#if SMB_VFS_INTERFACE_VERSION >= SMB_V3_VFS
	vfs_op_tuple*		    drweb_vfs_ops = NULL;
#else /* SMB_VFS_INTERFACE_VERSION < SMB_V3_VFS */
struct vfs_ops		    drweb_vfs_ops = {
	/* ---------------------- */
	/* Disk operations	  */
	/* connect     */	      dwsmb_connect,
	/* disconnect  */	      dwsmb_disconnect,
	/* disk_free   */	      NULL,
	/* ---------------------- */
	/* Directory operations	  */
	/* opendir     */	      NULL,
	/* readdir     */	      NULL,
	/* mkdir       */	      NULL,
	/* rmdir       */	      NULL,
	/* closedir    */	      NULL,
	/* ---------------------- */
	/* File operations	  */
	/* open	       */	      dwsmb_open,
	/* close       */	      dwsmb_close,
	/* read	       */	      NULL,
	/* write       */	      NULL,
	/* lseek       */	      NULL,
#if SMB_VFS_INTERFACE_VERSION >= 4
	/* sendfile    */	      NULL,
#endif /* SMB_VFS_INTERFACE_VERSION >= 4 */
	/* rename      */	      NULL,
	/* fsync       */	      NULL,
	/* stat	       */	      NULL,
	/* fstat       */	      NULL,
	/* lstat       */	      NULL,
	/* unlink      */	      NULL,
	/* chmod       */	      NULL,
	/* fchmod      */	      NULL,
	/* chown       */	      NULL,
	/* fchown      */	      NULL,
	/* chdir       */	      NULL,
	/* getwd       */	      NULL,
	/* utime       */	      NULL,
	/* ftruncate   */	      NULL,
	/* lock	       */	      NULL,
	/* symlink     */	      NULL,
	/* readlink    */	      NULL,
#if SMB_VFS_INTERFACE_VERSION >= 2
	/* link	       */	      NULL,
	/* mknod       */	      NULL,
	/* realpath    */	      NULL,
	/* ---------------------- */
	/* NT File ACL operations */
	/* fget_nt_acl */	      NULL,
	/* get_nt_acl  */	      NULL,
	/* fset_nt_acl */	      NULL,
	/* set_nt_acl  */	      NULL,
	/* -------------------- */
	/* POSIX ACL operations */
	/* chmod_acl   */	      NULL,
	/* fchmod_acl  */	      NULL,
#if SMB_VFS_INTERFACE_VERSION >= 3
	/* sys_acl_get_entry	*/    NULL,
	/* sys_acl_get_tag_type */    NULL,
	/* sys_acl_get_permset	*/    NULL,
	/* sys_acl_get_qualifier*/    NULL,
	/* sys_acl_get_file	*/    NULL,
	/* sys_acl_get_fd	*/    NULL,
	/* sys_acl_clear_perms	*/    NULL,
	/* sys_acl_add_perm	*/    NULL,
	/* sys_acl_to_text	*/    NULL,
	/* sys_acl_init		*/    NULL,
	/* sys_acl_create_entry */    NULL,
	/* sys_acl_set_tag_type */    NULL,
	/* sys_acl_set_qualifier*/    NULL,
	/* sys_acl_set_permset	*/    NULL,
	/* sys_acl_valid	*/    NULL,
	/* sys_acl_set_file	*/    NULL,
	/* sys_acl_set_fd	*/    NULL,
	/* sys_acl_delete_def_file */ NULL,
	/* sys_acl_get_perm	*/    NULL,
	/* sys_acl_free_text	*/    NULL,
	/* sys_acl_free_acl	*/    NULL,
	/* sys_acl_free_qualifier */  NULL
#endif /* SMB_VFS_INTERFACE_VERSION >= 3 */
#endif /* SMB_VFS_INTERFACE_VERSION >= 2 */
	};
#endif /* SMB_VFS_ITERFACE_VERSION >= SMB_V3_VFS */

TDB_CONTEXT *drweb_open_tdb(const char *path)
{
	TDB_CONTEXT *tdb = NULL;

	tdb = tdb_open(path, 0, 0, O_RDWR, 0666);

	if (!tdb)
	{
		printLib( DW_LL_INFO, "(init) failed to open %s (%s)",
			  path, strerror(errno));
	}

	return tdb;
}

int init_tdb_base(char * path, size_t max_size)
{
	TDB_CONTEXT *tdb = NULL;
	int ret = -1;

	printLib(DW_LL_VERBOSE, "try init base %s", path);
	printLib(DW_LL_DEBUG,	"max size = %d, rewrite base = %d",
		 max_size, dwsmb_conf.scan.rewrite_data_base);

	if (max_size != 0)
	{
		/* if the size of the base more then max, then truncate file. */
		dwc_bool_t truncate = (get_file_size(path) > max_size);

		/* this parameter working too */
		truncate = dwsmb_conf.scan.rewrite_data_base || truncate;

		if (truncate) printLib(DW_LL_VERBOSE, "(init) %s will be truncated", path);

		dr_become_root();

		tdb = tdb_open(path, 0, 0, O_RDWR | O_CREAT | (truncate ? O_TRUNC : 0), 0666);

		ret = (tdb ? 0 : -1);

		if (!tdb)
			printLib( DW_LL_INFO, "(init) failed to open %s (%s)",
				  path, strerror(errno));
		else
		{
			tdb_close(tdb);
			if (chmod(path, 0666) != 0)
			{
				printLib( DW_LL_INFO, "(init) failed to chmod 0666 for %s (%s)",
					  path, strerror(errno));
				ret = -1;
			}
		}

		dr_unbecome_root();
	}

	return ret;
}

static int init_connection()
{
	dwc_string_t dm_addr;
        int ret = -1;

        if (dwc_ipc_inited())
                return DWL_NOERROR;

        dw_multistring_rewind( dwsmb_conf.comm.addr, DW_SD_FORWARD );
	dm_addr = dw_multistring_getnext( dwsmb_conf.comm.addr );

        dr_become_root();
	ret = startupLib( CURRENT_LIB_VERSION, dm_addr, dwsmb_conf.comm.timeout, dwsmb_conf.comm.cache );
	dr_unbecome_root();

        return ret;
}

/*-------------------------------------------------------------------------*/
dwc_bool_t load_configuration()
{
	const int	   addr_size = 380; //see dwc_ipc.c   sizeof(DWC_ipc_addr_t)
	int		   rc;
	DWC_ipc_addr_ptr_t addr;
	DWC_ipc_type_t	   ipc_type;
	dwc_bool_t	   from_agent=0;
	char		   *_address[1];

	printLib(DW_LL_DEBUG,"(load_configuration)");

	_address[0] = (char*)malloc(strlen(config_address));
	strcpy(_address[0],config_address);
	if (dwc_ipc_inited()) dinit_ipc_subsystem();
	cleanupLib();
	/* checking method */
	addr = DWC_Malloc( DWC_ipc_addr_ptr_t, addr_size );

	if ((*_address[0] != '/')&&(parse_addr( _address[0], addr , &ipc_type, FALSE)))
		from_agent=1;
	DWC_Free(addr);

	printLib(DW_LL_VERBOSE, "(%s) loading config from %s <%s>",
		 __func__, from_agent ? "agent" : "file", _address[0] );

	if (from_agent)
	{
		dr_become_root();
		/*  using of NEW function for getting settings */
		if( !dw_load_settings_agent( 1,	 _address, &dwsmb_conf ) )
		{
			dr_unbecome_root();
			printLib( DW_LL_ERROR, "(%s) configuration cannot be loaded from agent with address %s",
				  __func__, config_address);
			return FALSE;
		}
	}
	else
	{
		dr_become_root();
		/*  using of OLD function for getting settings */
		if( !dw_load_settings( 1, _address, &dwsmb_conf ) )
		{
			dr_unbecome_root();
			printLib( DW_LL_ERROR, "(%s) configuration cannot be loaded from file with path %s",
				  __func__, config_address);

			return FALSE;
		}
	}

	dr_unbecome_root();

	if (dwc_ipc_inited()) dinit_ipc_subsystem();  /* necessary for re-initializing drweb_library */

        rc = init_connection();

	if( rc != DWL_NOERROR )
	{
		if( rc != DWL_ALREADYEXEC )
			printLib( DW_LL_ERROR, "(load_configuration) cannot initialize drweb_library" );
		printLib( DW_LL_VERBOSE, "(load_configuration) warning: drweb_library already initalized" );
	}

	if (init_tdb_base(lock_path(blocked_base_name), dwsmb_conf.scan.blocked_cache_size) == 0)
		use_blocked_tdb = 1;
	if (init_tdb_base(lock_path(allowed_base_name), dwsmb_conf.scan.allowed_cache_size) == 0)
		use_allowed_tdb = 1;

	if (!use_blocked_tdb) printLib(DW_LL_INFO, "(init) the blocked tdb-base disabled");
	if (!use_allowed_tdb) printLib(DW_LL_INFO, "(init) the allowed tdb-base disabled");

	return TRUE;
}
/*-------------------------------------------------------------------------*/
void dwsmb_signal_handler_exit( int s )
{
	char path[100]="";
#ifdef Solaris_DW
	printLib(DW_LL_ALERT,"(drweb_smb) has recieved signal %s(%d)", _sys_siglist,s);
#else
	printLib(DW_LL_ALERT,"(drweb_smb) has recieved signal %s(%d)", sys_siglist[s],s);
#endif
	sprintf(path,"%s/smb_spider.hup.%d",dw_smb_pid_path,getpid());
	dr_become_root();
	unlink(path);
	dr_unbecome_root();
	signal(s,SIG_DFL);
	raise(s);
	return;
}
/*-------------------------------------------------------------------------*/
void dwsmb_signal_handler_reload( int s)
{
	printLib( DW_LL_ALERT, "(get SIGHUP) Reloading settings after SIGHUP from %s", config_address);
	if (load_configuration())
		printLib( DW_LL_VERBOSE, "Successful reloading from %s after SIGHUP",config_address);
	return;
}
/*-------------------------------------------------------------------------*/
dw_init_vfs_ret_t INIT_NAME( INIT_ARG )
{
	outputLib(NULL, DW_LL_ALERT, LOG_DAEMON, LOG_INFO, "drweb_samba");
	printLib(DW_LL_DEBUG,"(init)");

#if SMB_VFS_INTERFACE_VERSION < SMB_V3_VFS
	EXIT_IF_1PARAM( !vfs_version, "(init)", return NULL );
	*vfs_version = SMB_VFS_INTERFACE_VERSION;
#endif /* SMB_VFS_INTERFACE_VERSION < SMB_V3_VFS */

	if( dwsmb_loaded )
	{
		printLib( DW_LL_INFO, "(init) already had been loaded" );
		return DW_INIT_OK;
	}

	printLib(DW_LL_DEBUG,"(init) using NEW initialization");

	signal(SIGHUP,dwsmb_signal_handler_reload);

	signal(SIGQUIT,dwsmb_signal_handler_exit);
	signal(SIGILL,dwsmb_signal_handler_exit);
	signal(SIGTRAP,dwsmb_signal_handler_exit);
	signal(SIGABRT,dwsmb_signal_handler_exit);
	signal(SIGIOT,dwsmb_signal_handler_exit);
	signal(SIGBUS,dwsmb_signal_handler_exit);
	signal(SIGFPE,dwsmb_signal_handler_exit);
	signal(SIGSEGV,dwsmb_signal_handler_exit);
	signal(SIGCONT,dwsmb_signal_handler_exit);
	signal(SIGSYS,dwsmb_signal_handler_exit);

	return dwsmb_init_vfs_ops(INIT_VFS_ARG);
}

#if SMB_VFS_INTERFACE_VERSION < 3
/*-------------------------------------------------------------------------*/
struct vfs_ops* dwsmb_init_vfs_ops ( void* ignore )
{
	if( !dw_bm_contain( dwsmb_conf.scan.scan_mode, DW_SM_ON_WRITE ) )
		drweb_vfs_ops.close = NULL;
	return &drweb_vfs_ops;
}
#else /* SMB_VFS_INTERFACE_VERSION >= 3 */
#if SMB_VFS_INTERFACE_VERSION < SMB_V3_VFS

struct vfs_ops* dwsmb_init_vfs_ops ( struct vfs_ops* def_vfs_ops )
{
	struct vfs_ops	tmp_ops;

	memcpy( &tmp_ops, def_vfs_ops, sizeof(struct vfs_ops) );
	tmp_ops.connect = dwsmb_connect;
	tmp_ops.disconnect = dwsmb_disconnect;
	tmp_ops.open = dwsmb_open;
	if( dw_bm_contain( dwsmb_conf.scan.scan_mode, DW_SM_ON_WRITE ) )
		tmp_ops.close = dwsmb_close;
	memcpy( &drweb_vfs_ops, &tmp_ops, sizeof(struct vfs_ops) );
	return &drweb_vfs_ops;
}
#else /* SMB_VFS_INTERFACE_VERSION >= SMB_V3_VFS */

NTSTATUS dwsmb_init_vfs_ops ( void* ignore )
{
	vfs_op_tuple	allways_ops[] = {
		/* ---------------------- */
		/* Disk operations	  */
		{ SMB_VFS_OP(dwsmb_connect),	SMB_VFS_OP_CONNECT,    SMB_VFS_LAYER_TRANSPARENT },
		{ SMB_VFS_OP(dwsmb_disconnect), SMB_VFS_OP_DISCONNECT, SMB_VFS_LAYER_TRANSPARENT },
		/* ---------------------- */
		/* File operations	  */
		{ SMB_VFS_OP(dwsmb_open),	SMB_VFS_OP_OPEN,       SMB_VFS_LAYER_TRANSPARENT } };
	vfs_op_tuple	write_ops[] = {
		/* ---------------------- */
		/* File operations	  */
		{ SMB_VFS_OP(dwsmb_close),	SMB_VFS_OP_CLOSE,      SMB_VFS_LAYER_TRANSPARENT } };
	/* Finish VFS operations definition */
	vfs_op_tuple   null_op =  { NULL, SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP };
	int	       allways_ops_num = sizeof(allways_ops) / sizeof(vfs_op_tuple);
	int	       write_ops_num = sizeof(write_ops) / sizeof(vfs_op_tuple);
	int	       null_op_num = sizeof(null_op) / sizeof(vfs_op_tuple);
	int	       num_op = allways_ops_num + null_op_num; /* connect, disconnect, open, NULL */

#if 0
	if( dw_bm_contain( dwsmb_conf.scan.scan_mode, DW_SM_ON_WRITE ) )
	{
		num_op += write_ops_num;
	}
	drweb_vfs_ops = DWC_Malloc( vfs_op_tuple*, num_op * sizeof(vfs_op_tuple) );
	if( drweb_vfs_ops )
	{
		memcpy( drweb_vfs_ops, allways_ops, sizeof(allways_ops) );
		if( dw_bm_contain( dwsmb_conf.scan.scan_mode, DW_SM_ON_WRITE ) )
		{
			memcpy( &drweb_vfs_ops[allways_ops_num], write_ops, sizeof(write_ops) );
		}
		memcpy( &drweb_vfs_ops[num_op - 1], &null_op, sizeof(null_op) );
		return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "smb_spider", drweb_vfs_ops);
	}
	else
	{
		return NT_STATUS_NO_MEMORY;
	}
#else
	num_op += write_ops_num;
	drweb_vfs_ops = DWC_Malloc( vfs_op_tuple*, num_op * sizeof(vfs_op_tuple) );
	if( drweb_vfs_ops )
	{
		memcpy( drweb_vfs_ops, allways_ops, sizeof(allways_ops) );
		memcpy( &drweb_vfs_ops[allways_ops_num], write_ops, sizeof(write_ops) );
		memcpy( &drweb_vfs_ops[num_op - 1], &null_op, sizeof(null_op) );
		return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "smb_spider", drweb_vfs_ops);
	}
	else
		return NT_STATUS_NO_MEMORY;
#endif // 0
}
#endif /* SMB_VFS_INTERFACE_VERSION < SMB_V3_VFS */
#endif /* SMB_VFS_INTERFACE_VERSION < 3 */

#endif /* _DRWEB_SAMBA_C */
