#ifndef _DW_FILES_C
#define _DW_FILES_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_files.c,v 1.68.2.2 2009/04/03 15:04:05 akorkov Exp $";
#endif

#if defined(__UNIX_DW)
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
#include <limits.h>
#endif /* UNIX */
#include <sys/stat.h>
#include <fcntl.h>
#if defined(EMX_DW)
#include <io.h>
#endif /* EMX */
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "dwc_err.h"
#if defined(__UNIX_DW)
#include "dwc_lib.h"
#endif /* UNIX */
#if defined(__WIN32_DW)
#include "dw_winlib.h"
#endif /* WIN32 */
#include "dwc_macro.h"
#include "dw_files.h"

#if !defined(DW_SAMBA)                                          

#if defined(DW_SENDMAIL)
static dwc_bool_t   dw_test_spoolfile( const dwc_tmpfiles_t *tmpl, const dwc_bool_t local, dwc_path_t prefix, dwc_count_t strips, dwc_path_t *name )
;
#endif /* SENDMAIL */
#if defined(__WIN32_DW)
static dwc_fd_t     mkstemp( const dwc_path_t tmpl );
#endif /* WIN32 */
#endif /* not SAMBA */

/* ---------------------------------------------------------------------- */
dwc_path_t dw_basename( dwc_path_t path )
{
    dwc_path_t res = NULL;

    if( path )
    {
        if( NULL != (res = strrchr( path, PATH_SEPARATOR )) )
            res += sizeof(char);
#if defined(DW_HAVE_SEPARATOR_2)
        else if( NULL != (res = strrchr( path, PATH_SEPARATOR_2 )) )
            res += sizeof(char);
#endif /* HAVE_SEPARATOR_2 */        
        else
            res = path;
    }
    return res;
}

/* ---------------------------------------------------------------------- */
/* strips - works like -pnum in patch utility (i.e. src = /one/of/my/paths)
 * 0      - name prefixed from unmodified  (i.e. dst = /one/of/my/paths)
 * 1      - (i.e. dst = one/of/my/paths)
 * 2      - (i.e. dst = of/my/paths)
 * In this case, actions for strips 0 and 1 is same
 */
dwc_path_t dw_normalize_path( dwc_const_path_t fname, dwc_bool_t need_expand, dwc_path_t prefix, dwc_count_t strips )
{
    dwc_path_t  tmp = NULL;
    dwc_path_t  etmp = NULL;
    dwc_const_path_t  exp;
    dwc_count_t len, num;
    const char*       ptr;
    char*       nptr;

    len = strlen( fname );
    if( !fname || (len < 1) )
    {
        printLib( DW_LL_ERROR, "files: no path for normalizing" );
        return NULL;
    }    
#if defined(__UNIX_DW)    
    if( need_expand )
    {
        tmp = DWC_Malloc( dwc_path_t, PATH_MAX + 1 );
        if( !tmp )
        {
            printLib( DW_LL_ERROR, "files: no memory for normalizing" );
            return NULL;
        }
        if( realpath( fname, tmp ) == NULL )
        {
            printLib( DW_LL_ERROR, "files: cannot expand filename %s - %s", fname, stdlib_error(errno) );
            DWC_Free( tmp );
            return NULL;
        }
        printLib( DW_LL_VERBOSE, "files: filename %s expanded to %s", fname, tmp );
        len = strlen( tmp );
    }
#endif /* UNIX */   
    if( (prefix && (prefix[0] != NULLCHAR)) || (strips > 0) )
    {
        if( tmp )
        {
            exp = tmp;
        }
        else
        {
            exp = fname;
        } 
        ptr = exp;
        num = strips;
        for( num = strips; num > 0; --num )
        {
            nptr = strchr( ptr, PATH_SEPARATOR );            
#if defined(DW_HAVE_SEPARATOR_2)
            if( !nptr )
                nptr = strrchr( ptr, PATH_SEPARATOR_2 );
#endif /* HAVE_SEPARATOR_2 */        
            if( !nptr )
            {
                printLib( DW_LL_ERROR, "files: strip %d prefixes in %s is impossible", strips, exp );
                DWC_Free( tmp );
                return NULL;
            }            
            len -= nptr - ptr;
            for( ; (nptr[0] == PATH_SEPARATOR) && (len > 0); --len )
            {
                /* del sequence of slashes */
                ++nptr;
            }
            if( len == 0 )
            {
                printLib( DW_LL_ERROR, "files: strip %d prefixes in %s is impossible", strips, exp );
                DWC_Free( tmp );
                return NULL;
            }
            ptr = nptr;
        }
        if( prefix && (prefix[0] != NULLCHAR) ) 
        {
#if !defined(DW_HAVE_SEPARATOR_2)
            for( ; (ptr[0] == PATH_SEPARATOR) && (len > 0); --len )
#else /* HAVE_SEPARATOR_2 */                
            for( ; ((ptr[0] == PATH_SEPARATOR) || (ptr[0] == PATH_SEPARATOR_2)) && (len > 0); --len )
#endif /* HAVE_SEPARATOR_2 */                
            {
                /* shift first slash */
                ++ptr;
            }
            len = strlen( prefix );
            len += strlen( ptr );
            len += 2 * sizeof( char ); /* asv: for separator and zero */
            etmp = DWC_Malloc( dwc_path_t, len );
            if( !etmp )
            {
                printLib( DW_LL_ERROR, "files: no memory for normalizing" );
                DWC_Free( tmp );
                return NULL;
            }
            snprintf( etmp, len, "%s%c%s", prefix, PATH_SEPARATOR, ptr );
            etmp[len - 1] = NULLCHAR;
        }
        else
        {
            /* just strip path */
            etmp = DWC_Strdup( ptr );
        }
        printLib( DW_LL_VERBOSE, "files: filename %s converted to %s", exp, etmp );
        DWC_Free( tmp );
        tmp = etmp;
    }
    if( !tmp )
    {
        tmp = DWC_Strdup( fname );
        if( !tmp )
        {
            printLib( DW_LL_ERROR, "files: no memory for normalizing" );
            return NULL;
        }
    }
    return tmp;
}

//#if !defined(DW_SAMBA)
/* ---------------------------------------------------------------------- */
dwc_fd_t dw_open_tmpfile ( const dwc_tmpfiles_t *tmpl, dwc_path_t *pname )
{
    dwc_length_t    fmtlen;
    dwc_path_t      fname = NULL;
    dwc_fd_t        fd = -1;
#if defined(__UNIX_DW)
    struct timeval  tv;
#if !defined(EMX_DW) 
    int             i;
#endif /* not EMX */  
#endif /* UNIX */    

    EXIT_IF_4PARAM( !pname, !tmpl, !tmpl->tf_path, !tmpl->tf_prefix, "files: open tmpfile", return -1 );
    *pname = NULL;
    switch( tmpl->tf_naming )
    {
        case DW_NAME_STD:   
            fmtlen = 6;                 
            break;
#if !defined(__WIN32_DW)
#if !defined(EMX_DW)            
        case DW_NAME_RAND48:
            fmtlen = 8;
            break;
#endif /* not EMX */            
        case DW_NAME_TAI:
            fmtlen = (10 + sizeof(char)) * 2 + 6; /* asv: 2*(%ul + .) + XXXXXX */
            break;
#endif /* not WIN32 */            
        default:
            printLib( DW_LL_ERROR, "files: cannot open tmpfile - wrong filenaming mode" );
            return -1;
    }
    fmtlen += strlen( tmpl->tf_path );
    fmtlen += strlen( tmpl->tf_prefix );
    fmtlen += 2 * sizeof(char); /* asv: for separator and zero */
    fname = DWC_Malloc( dwc_path_t, fmtlen );
    if( !fname )
    {
        goto error_exit;
    }
    switch( tmpl->tf_naming )
    {
        case DW_NAME_STD:                    
            snprintf( fname, fmtlen, "%s%c%sXXXXXX", tmpl->tf_path, PATH_SEPARATOR, tmpl->tf_prefix );
            fd = mkstemp( fname );
            break;
#if !defined(__WIN32_DW)            
#if !defined(EMX_DW)
        case DW_NAME_RAND48:
            if( gettimeofday( &tv, NULL ) == 0 )
            {
                srand48(tv.tv_usec);
            }
            for( i = 0; i < NUM_OPEN_ERROR; ++i )  
            {
                snprintf( fname, fmtlen, "%s%c%s%08lX", tmpl->tf_path, PATH_SEPARATOR, tmpl->tf_prefix, lrand48() );
                fd = xopen_file( fname, O_RDWR | O_CREAT | O_EXCL, tmpl->tf_access );
                if( fd < 0 )
                {
                    if( errno == EEXIST )
                    {
                        continue;
                    }
                    printLib( DW_LL_ERROR, "files: cannot open tmpfile %s - (%d) %s", fname, errno, stdlib_error(errno) );
                    goto error_exit;
                }
                else
                {
                    break;
                }
            }
            break;  
#endif /* not EMX */      
        case DW_NAME_TAI:
            if( gettimeofday( &tv, NULL ) != 0 ) 
            {   
                tv.tv_sec = 0; 
                tv.tv_usec = 0;
            }
            snprintf( fname, fmtlen, "%s%c%010lu.%010lu.%sXXXXXX", tmpl->tf_path, PATH_SEPARATOR, tv.tv_sec, tv.tv_usec,tmpl->tf_prefix );
            fd = mkstemp( fname );
            break;
#endif /* not WIN32 */            
        default:
            printLib( DW_LL_ERROR, "files: cannot open tmpfile - wrong filenaming mode" );
            return -1;
    }
    if( fd < 0 )
    {
        printLib( DW_LL_ERROR, "files: cannot open tmpfile %s - (%d) %s", fname, errno, stdlib_error(errno));
        goto error_exit;
    }
#if !defined(EMX_DW)        
    if( (tmpl->tf_naming != DW_NAME_RAND48)
        &&
        (tmpl->tf_access != 0600)
        &&
        (fchmod( fd, tmpl->tf_access ) != 0) )
    {
        printLib( DW_LL_ERROR, "files: cannot set permissions of tmpfile %s - (%d) %s", fname, errno, stdlib_error(errno) );
        goto error_exit;
    }
#else /* EMX */
    setmode( fd, O_BINARY );
#endif /* EMX */    
    printLib( DW_LL_VERBOSE, "files: open tmpfile (%d) %s %#o", fd, fname, tmpl->tf_access );
    *pname = fname;
    return fd;

  error_exit:
    if( fd >= 0 )
    {
        close_file( fd );
    }
    if( fname )
    {       
        unlink( fname );
        DWC_Free( fname );
    }
    return -1;
}

#if defined(DW_QMAIL) || defined(DW_PIPE)
/* ---------------------------------------------------------------------- */
dwc_path_t dw_stdin_to_tmpfile ( const dwc_tmpfiles_t *tmpl )
{
    char *fname = NULL;
    int   nfd = -1;

    nfd = dw_open_tmpfile( tmpl, &fname );
    if( nfd < 0 )
    {
        printLib( DW_LL_ERROR, "cannot create temporary file - (%d) %s", errno, stdlib_error(errno) );
        goto error_exit;
    }
    if( !dw_write_fd_to_fd( STDIN_FILENO, nfd, 0 ) )
    {
        printLib( DW_LL_ERROR, "cannot save message from stdin to temporary file" );
        goto error_exit;
    }
    if( fsync( nfd ) < 0 )
    {
        printLib( DW_LL_ERROR, "cannot fsync temporary file - (%d) %s", errno, stdlib_error(errno) );
        goto error_exit;
    }
    if( close_file( nfd ) < 0 )
    {
        printLib( DW_LL_ERROR, "cannot close temporary file - (%d) %s", errno, stdlib_error(errno) );
        goto error_exit;
    }
    return fname;

  error_exit:
    if( nfd >= 0 )
    {
        close_file( nfd );
    }
    if( fname )
    {
        unlink( fname );
        DWC_Free( fname );
    }
    return NULL;
}
#endif /* QMAIL or PIPE */

/* ---------------------------------------------------------------------- */
dwc_syserr_t dw_check_read( const dwc_path_t fname )
{
    int    fd = -1, buf_len;
    char   buf[10];
        
    if( !fname )
    {
        printLib( DW_LL_ERROR, "check_file: no files specify to check" );
        return -1;
    }
    fd = open_file( fname, O_RDONLY );
    if( fd < 0 )
    {
        printLib( DW_LL_ERROR, "check_file: cannot open %s - (%d) %s", fname, errno, stdlib_error(errno) );
        return -1;
    }
    buf_len = DWC_Min( sizeof(buf), fileSize(fd) );
    if( buf_len < 0 )
    {
        printLib( DW_LL_ERROR, "check_file: incorrect file %s size", fname );
        close( fd );
        return -1;
    }
    else if( buf == 0 )
    {
        printLib( DW_LL_INFO, "check_file: WARNING: %s - empty file specified", fname );
    }
    else if( !dw_read_buf( fd, buf, sizeof(buf) ) )
    {
        printLib( DW_LL_INFO, "check_file: error reading %s - (%d) %s", fname, errno, stdlib_error(errno) );
        close( fd );
        return -1;
    }
    return close( fd );
}

#if defined(DW_HAVE_SPOOL)
/* ---------------------------------------------------------------------- */
static dwc_bool_t dw_test_spoolfile( const dwc_tmpfiles_t *tmpl, const dwc_bool_t local, dwc_path_t prefix, dwc_count_t strips, dwc_path_t *name )
{
    int                 fd = -1, ret, status, options, ext_options;    
    dwc_scan_result_t   scan = NULL;
    dwc_path_t          scan_path = NULL;
    char                test_buf1[] = "Subject: Standard antivirus test file\r\nMime-Version: 1.0\r\nContent-Type: multipart/mixed; boundary=\"test\"\r\nContent-Disposition: inline\r\n\r\n--test\r\nContent-Type: text/plain; charset=us-ascii\r\nContent-Disposition: inline\r\n\r\nAttachment to this message contains standard antivirus test file.\r\n--test\r\nContent-Type: application/x-msdos-program\r\nContent-Disposition: attachment; filename=\"eicar.com\"\r\n\r\nX5O!P%@";
    char                test_address[] = "test_daemon_address@drweb.com";
    char               *status_str = NULL;
    dwc_multistring_t   test_rcpt;
    char                test_buf2[] = "AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*\r\n--test--\r\n";
    
    fd = dw_open_tmpfile( tmpl, name );
    if( fd < 0 )
    {
        printLib( DW_LL_ERROR, "check_scan: cannot create spool file %s - (%d) %s", name, errno, strerror(errno) );
        return FALSE;        
    }
    if( write( fd, test_buf1, sizeof( test_buf1 ) - 1 ) < 0 )
    {
        printLib( DW_LL_ERROR, "check_scan: cannot write to spool file - (%d) %s", errno, strerror(errno) );
        goto error_exit;
    }
    if( write( fd, test_buf2, sizeof( test_buf2 ) ) < 0 )
    {
        printLib( DW_LL_ERROR, "check_scan: cannot write to spool file - (%d) %s", errno, strerror(errno) );
        goto error_exit;
    }
    if( close( fd ) < 0 )
    {
        printLib( DW_LL_ERROR, "check_scan: cannot close spool file - (%d) %s", errno, strerror(errno) );
        fd = -1;
        goto error_exit;
    }    
    scan_path = dw_normalize_path( *name, local, prefix, strips );
    if( !scan_path )
    {
        printLib( DW_LL_ERROR, "check_scan: cannot normalize path %s with expand=%d, prefix=%s, shift=%d", name, local, prefix, strips );
        goto error_exit;
    }
    test_rcpt = dw_multistring_create(0);
    if( !test_rcpt || !dw_multistring_add(test_rcpt, test_address) )
    {        
        printLib( DW_LL_ERROR, "check_scan: cannot create test envelope - no memory" );
        dw_multistring_delete(test_rcpt);
        goto error_exit;
    }
    
    dw_bs_empty( options );
    dw_bs_empty( ext_options );
    dw_bs_add( options, DRWEBD_IS_MAIL );
    dw_bs_add( options, DRWEBD_HAVE_ENVELOPE );
    dw_bs_add( options, DRWEBD_HAVE_EXTENDED );

    ret = scanMail( scan_path, strlen(scan_path), local, options, ext_options, &scan, 1024, test_address, test_rcpt );
    DWC_Free( scan_path );
    dw_multistring_delete(test_rcpt);
    if( ret != DWL_NOERROR )
    {
        printLib( DW_LL_ERROR, "check_scan: cannot scan spool file (%d)", ret );
        goto error_exit;
    }
    status = getResultStatus( scan );
    status_str = getDaemonCodeString(status);
    printLib( DW_LL_INFO, "check_scan: daemon return %#x code (%s)", status, (status_str ? status_str : "null") );
    if( !dw_bm_include( status, DERR_KNOWN_VIRUS ) )
    {
        printLib( DW_LL_ERROR, "check_scan: daemon cannot detect standard test file (EICAR) - (0x%x) %s", status, (status_str ? status_str : "null") );
        DWC_Free(status_str);
        goto error_exit;
    }
    DWC_Free(status_str);
    clearResult( scan );
    return TRUE;
    
 error_exit:
    if( fd >= 0 )
    {
        close( fd );
    }
    if( scan )
    {
        clearResult( scan );
    }
    unlink( *name );
    return FALSE;
}

/* ---------------------------------------------------------------------- */
dwc_syserr_t dw_check_spool( const dwc_tmpfiles_t *tmpl, const dwc_bool_t local, dwc_path_t prefix, dwc_count_t strips )
{
    dwc_bool_t    error;
    dwc_path_t    tmp_name;

    if( !dw_test_spoolfile( tmpl, local, prefix, strips, &tmp_name ) )
    {
        printLib( DW_LL_ERROR, "check_spool: cannot test spool file" );
        error = -1;
        goto error_exit;
    }
    if( unlink( tmp_name ) < 0 )
    {
        printLib( DW_LL_ERROR, "check_spool: cannot unlink spool file" );
        error = -1;
        goto error_exit;
    }
    error = 0;
  error_exit:
    DWC_Free( tmp_name );
    return error;
}

/* ---------------------------------------------------------------------- */
dwc_syserr_t dw_check_quarantine( const dwc_tmpfiles_t *srctmpl, const dwc_tmpfiles_t *dsttmpl, const dwc_bool_t local, dwc_path_t prefix, dwc_count_t strips )
{
    dwc_syserr_t    error;
    dwc_fd_t        fd = -1;
    dwc_path_t      tmp_name = NULL;
    dwc_path_t      arc_name = NULL;
    dwc_bool_t      f_tmp, f_arc;
    
    f_tmp = f_arc = FALSE;
    if( !dw_test_spoolfile( srctmpl, local, prefix, strips, &tmp_name ) )
    {
        printLib( DW_LL_ERROR, "check_move: cannot test spool-file" );
        error = -1;
        goto error_exit;
    }
    f_tmp = TRUE;
    fd = dw_open_tmpfile( dsttmpl, &arc_name );
    if( fd < 0 )
    {
        printLib( DW_LL_ERROR, "check_move: cannot create archive file %s", (arc_name?arc_name:"null") );
        error = -1;
        goto error_exit;
    }
    f_arc = TRUE;
    if( close( fd ) < 0 )
    {
        printLib( DW_LL_ERROR, "check_move: cannot close archive file %s - (%d) %s", arc_name, errno, strerror(errno) );
        error = -1;
        goto error_exit;
    }
    if( dw_move_file( tmp_name, arc_name, dsttmpl->tf_access ) < 0 )
    {
        printLib( DW_LL_ERROR, "check_move: cannot rename temporary file to archive file - (%d) %s.\n", errno, strerror(errno) );
        error = -1;
        goto error_exit;
    }
    error = 0;
  error_exit:
    if( tmp_name )
    {
        if( f_tmp )
        {
            unlink( tmp_name );
        }
        DWC_Free( tmp_name );
    }
    if( arc_name )
    {
        if( f_arc )
        {
            unlink( arc_name );
        }
        DWC_Free( arc_name );
    }
    return error;
}
#endif /* HAVE_SPOOL */

/* ---------------------------------------------------------------------- */
dwc_syserr_t dw_move_file( dwc_const_path_t oldpath, dwc_const_path_t newpath, const dwc_mode_t nmode )
{
    int         error, save_errno;

    save_errno = 0;
    if( rename( oldpath, newpath ) == 0 )
    {
        if( chmod( newpath, nmode ) < 0 )
        {
            save_errno = errno;
            printLib( DW_LL_ERROR, "move_file: cannot change mode for quarantine file - (%d) %s", errno, stdlib_error(errno) );
            error = -1;
            goto error_exit;
        }
        return 0;
    }
    printLib( DW_LL_VERBOSE, "move_file: try to use by-block copy, because std rename %s to %s failed - %s", oldpath, newpath, stdlib_error(errno) );
    if( dw_copy_file( oldpath, newpath, nmode ) < 0 )
    {
        save_errno = errno;
        printLib( DW_LL_ERROR, "move_file: cannot copy spool file to quarantine file - (%d) %s", errno, stdlib_error(errno) );
        error = -1;
        goto error_exit;
    }
    error = 0;
    
  error_exit:
    if( error != 0 )
    {
        unlink( newpath );
    }
    if( (error == 0) && (unlink( oldpath ) < 0) )
    {        
        save_errno = errno;
        printLib( DW_LL_ERROR, "move_file: cannot unlink temporary file %s - %s", oldpath, stdlib_error(errno) );
        if( unlink( newpath ) < 0 )
        {
            printLib( DW_LL_ERROR, "move_file: cannot unlink archive file %s - %s", newpath, stdlib_error(errno) );            
        } 
    }
    errno = save_errno;
    return error;
}

/* ---------------------------------------------------------------------- */
dwc_syserr_t dw_copy_file( dwc_const_path_t oldpath,  dwc_const_path_t newpath, const dwc_mode_t nmode )
{
    char*       buf = NULL;
    int         len, towrite, written, error, save_errno, src_fd, dst_fd, wrb;
    struct stat st;

    save_errno = 0;
    dst_fd = src_fd = -1;
    buf = DWC_Malloc( char*, IO_BUF_LEN );
    if( !buf )
    {
        save_errno = ENOMEM;
        printLib( DW_LL_ERROR, "copy_file: cannot allocate buffer for moving" );
        error = -1;
        goto error_exit;
    }
    if( stat( oldpath, &st ) < 0 )
    {
        save_errno = errno;
        printLib( DW_LL_ERROR, "copy_file: error stat() temporary file %s - %s", oldpath, stdlib_error(errno) );
        error = -1;
        goto error_exit;
    }
    len = st.st_size;
    dst_fd = xopen_file( newpath, O_CREAT | O_WRONLY, st.st_mode );
    if( dst_fd < 0 )
    {
        save_errno = errno;
        printLib( DW_LL_ERROR, "copy_file: cannot open archive file %s - %s", newpath, stdlib_error(errno) );
        error = -1;
        goto error_exit;
    }
#if !defined(EMX_DW)    
    if( fchmod( dst_fd, nmode ) < 0 )
    {
        printLib( DW_LL_ERROR, "copy_file: cannot change mode for quarantine file %s - %s", newpath, stdlib_error(errno) );
        error = -1;
        goto error_exit;
    }
#endif /* EMX */    
    src_fd = open_file( oldpath, O_RDONLY );
    if( src_fd < 0 )
    {
        save_errno = errno;
        printLib( DW_LL_ERROR, "copy_file: cannot open temporary file %s - %s", oldpath, stdlib_error(errno) );
        error = -1;
        goto error_exit;
    }
    if( lseek( src_fd, 0, SEEK_SET ) < 0 )
    {
        save_errno = errno;
        printLib( DW_LL_ERROR, "copy_file: cannot seek to begin of temporary file %s - %s", oldpath, stdlib_error(errno) );
        error = -1;
        goto error_exit;
    }
    if( lseek( dst_fd, 0, SEEK_SET ) < 0 )
    {
        save_errno = errno;
        printLib( DW_LL_ERROR, "copy_file: cannot seek to begin of archive file %s - %s", newpath, stdlib_error(errno) );
        error = -1;
        goto error_exit;
    }
    while( len )
    {
        towrite = read( src_fd, buf, IO_BUF_LEN );
        if( towrite < 0 )
        {
            save_errno = errno;
            printLib( DW_LL_ERROR, "copy_file: cannot read temporary file %s - %s", oldpath, stdlib_error(errno) );
            error = -1;
            goto error_exit;
        }
        if( towrite == 0 )
        {
            /* end-of-file */
            break;
        }
        written = 0;
        do
        {
            wrb = write( dst_fd, buf + written, towrite - written );
            if( wrb < 0 )
            {
                save_errno = errno;
                printLib( DW_LL_ERROR, "copy_file: quarantine file writing error (%d:%d) - %s.\n", towrite, written, strerror(errno) );
                error = -1;
                goto error_exit;
            }
            written += wrb;
        } while( written < towrite );
        len -= towrite;
    }
    /* Set return code to OK */
    fsync( dst_fd );
    error = 0;
    
  error_exit:
    if( src_fd >= 0 )
    {
        if( close( src_fd ) < 0 )
        {
            save_errno = errno;
            printLib( DW_LL_ERROR, "copy_file: cannot close temporary file %s - %s", oldpath, stdlib_error(errno) );
            error = -1;
        }
    }
    if( dst_fd >= 0 )
    {
        if( close( dst_fd ) < 0 )
        {
            save_errno = errno;
            printLib( DW_LL_ERROR, "copy_file: cannot close quarantine file %s - %s", newpath, stdlib_error(errno) );
            error = -1;
        }
    }
    DWC_Free( buf );
    errno = save_errno;
    return error;
}

/* ---------------------------------------------------------------------- */
dwc_path_t dw_quarantine_file( const dwc_tmpfiles_t *tmpl, dwc_const_path_t fname, const dwc_bool_t moving )
{
    dwc_path_t   name;
    dwc_fd_t     fd;
    
    fd = dw_open_tmpfile( tmpl, &name );
    if( fd < 0 )
    {
        printLib( DW_LL_ERROR, "quarantine: cannot create file %s", name );
        goto error_exit;
    }
    if( close( fd ) < 0 )
    {
        printLib( DW_LL_ERROR, "quarantine: cannot close file - %s", stdlib_error(errno) );
        goto error_exit;
    }    
    if( moving && dw_move_file( fname, name, tmpl->tf_access ) < 0 )
    {
        printLib( DW_LL_ERROR, "quarantine: cannot move file - %s", stdlib_error(errno) );        
        goto error_exit;
    }
    else if( !moving && dw_copy_file( fname, name, tmpl->tf_access ) < 0 )
    {
        printLib( DW_LL_ERROR, "quarantine: cannot copy file - %s", stdlib_error(errno) );        
        goto error_exit;
    }
    return name;
    
  error_exit:
    DWC_Free( name );            
    return NULL;
}            

/* ---------------------------------------------------------------------- */
dwc_bool_t dw_write_file_to_fd( const dwc_path_t from_name, const dwc_off_t start, const dwc_off_t length, dwc_fd_t to_fd )
{
    int         src_fd = -1;    
    dwc_bool_t  retcode = FALSE;
    
    src_fd = open_file( from_name, O_RDONLY );
    if( src_fd < 0 )
    {
        printLib( DW_LL_ERROR, "files: cannot open file %s with message - %s", from_name, stdlib_error(errno) );
        goto error_exit;
    }
    if( (dwc_off_t) -1 == lseek( src_fd, start, SEEK_SET ) )
    {
        printLib( DW_LL_ERROR, "files: cannot set pos in file %s with message - %s", from_name, stdlib_error(errno) );
        goto error_exit;        
    }
    if( !dw_write_fd_to_fd( src_fd, to_fd, length ) )
    {
        printLib( DW_LL_ERROR, "files: cannot write file %s to %d", from_name, to_fd );
        goto error_exit;        
    }
    retcode = TRUE;
  error_exit:
    if( src_fd >= 0 )
        close( src_fd );
    return retcode;
}

/* ---------------------------------------------------------------------- */
dwc_bool_t dw_write_file_to_buf( const dwc_path_t from_name, const dwc_off_t start, const dwc_membuf_t buf, dwc_length_t buf_len )
{
    int         src_fd = -1;    
    dwc_bool_t  retcode = FALSE;
    
    src_fd = open_file( from_name, O_RDONLY );
    if( src_fd < 0 )
    {
        printLib( DW_LL_ERROR, "files: cannot open file %s with message - %s", from_name, stdlib_error(errno) );
        goto error_exit;
    }
    if( (dwc_off_t) -1 == lseek( src_fd, start, SEEK_SET ) )
    {
        printLib( DW_LL_ERROR, "files: cannot set pos in file %s with message - %s", from_name, stdlib_error(errno) );
        goto error_exit;        
    }
    if( !dw_read_buf( src_fd, buf, buf_len ) )
    {
        printLib( DW_LL_ERROR, "files: cannot read from file %s with message", from_name );
        goto error_exit;        
    }
    retcode = TRUE;
  error_exit:
    if( src_fd >= 0 )
        close( src_fd );
    return retcode;
}

/* ---------------------------------------------------------------------- */
dwc_bool_t dw_write_buf( dwc_fd_t to_fd, const dwc_membuf_t buf, dwc_length_t buf_len )
{
    int sendbytes, written;

    sendbytes = 0;    
    while( buf_len > 0 )
    {
        written = write( to_fd, (const char*) buf + sendbytes, buf_len );
        if( written < 0  )
        {
            printLib( DW_LL_ERROR, "files: write buf %p[%d] to fd (%d) error - (%d) %s", buf, buf_len, to_fd, errno, stdlib_error(errno) );
            return FALSE;
        }
        sendbytes += written;
        buf_len -= written;
    }
    return TRUE;
}

/* ---------------------------------------------------------------------- */
dwc_bool_t dw_write_str( dwc_fd_t to_fd, const dwc_string_t str )
{
    if( !str )
        return TRUE;
    else
        return dw_write_buf( to_fd, str, strlen(str) );
}

/* ---------------------------------------------------------------------- */
dwc_bool_t dw_read_buf( dwc_fd_t from_fd, dwc_membuf_t buf, dwc_length_t buf_len )
{
    int readed = 0;
    
    while( buf_len > 0 )
    {
        readed = read( from_fd, (void*)((const char*) buf + readed), buf_len );
        if( readed <= 0  )
        {
            return FALSE;
        }
        buf_len -= readed;
    }
    return TRUE;
}    

/* ---------------------------------------------------------------------- */
dwc_bool_t dw_write_fd_to_fd( dwc_fd_t from_fd, dwc_fd_t to_fd, dwc_length_t length )
{
    dwc_length_t overall;
    int          readed;    
    dwc_bool_t   now_end;
    char         buf[IO_BUF_LEN];

    if( (from_fd < 0) || (to_fd < 0) )
        return FALSE;
    overall = 0;
    now_end = FALSE;
    while( !now_end )
    {
        readed = read( from_fd, buf, sizeof(buf) );
        if( readed < 0 )
        {
            printLib( DW_LL_ERROR, "files: cannot read file with message" );
            return FALSE;
        }
        if( readed == 0 )
        {
            /* end-of-file */
            return TRUE;
        }
        if( (length > 0) && (readed + overall > length) )
        {
            /* truncate readed buffer 
               write and exit */
            readed = length - overall;
            now_end = TRUE;
        }
        if( !dw_write_buf( to_fd, buf, readed ) )
        {
            printLib( DW_LL_ERROR, "files: cannot write chuck from %d to %d - (%d) %s", from_fd, to_fd, errno, stdlib_error(errno) );
            return FALSE;
        }
        overall += readed;
    }
    return TRUE;
}

#if defined(__WIN32_DW)
/* ---------------------------------------------------------------------- */
dwc_fd_t mkstemp( const dwc_path_t tmpl )
{
    int          i, tid, timecomp, rndcomp;
    char         uniq[6];
    dwc_fd_t     fd = -1;
    dwc_string_t xxxtmpl = NULL;
    
    xxxtmpl = strstr( tmpl, "XXXXXX" );
    if( !xxxtmpl )
    {
        printLib( DW_LL_ERROR,"winfiles: cannot create temp-file - wrong template %s", tmpl );
        goto error_exit;
    }
    tid = 0xFF & GetCurrentThreadId();
    for( i = 0; i < NUM_OPEN_ERROR; ++i )
    {
        uniq[0] = NULLCHAR;
        timecomp = 0xFF & GetTickCount();
        rndcomp = rand() % 255;
        snprintf( uniq, sizeof(uniq), "%x%x%x", tid, timecomp, rndcomp );
        memcpy( xxxtmpl, uniq, sizeof(uniq) );
        fd = xopen_file( tmpl, O_CREAT | O_EXCL | O_RDWR, _S_IREAD | _S_IWRITE );
        if( fd >= 0 )
            break;
        if( errno == EEXIST )
            continue;
        printLib( DW_LL_ERROR, "winfiles: cannot create temp-file - %s", stdlib_error( errno ) );
        goto error_exit;
    }
    if( fd >= 0 )
    {
        printLib( DW_LL_VERBOSE, "winfiles: temp-file = %s", tmpl );
    }
    else
    {
        printLib( DW_LL_ERROR, "winfiles: cannot create temp-file - probe limit exhaused" );
    }
    return fd;

 error_exit:
    if( fd >= 0 )
        close_file( fd );
    return -1;
}
#endif /* WIN32 */

//#endif /* not SAMBA */

#endif /* _DWC_FILES_H */
