package Modules::FileCopy;

########## Confixx(R) 3.1 Professional ############
####### Copyright SWsoft, Inc. 2004-2005 ##########
#### http://www.swsoft.com - info@swsoft.com ####

use File::Copy;
use File::Find;
use File::Basename;
use Cwd;
use Digest::MD5;
use Exporter;

our(@ISA,@EXPORT,@EXPORT_OK);
@ISA = qw/Exporter/;
@EXPORT = qw/safe_copy copy_tree move_tree/;

use strict;

sub safe_copy{
	my( $srcDir, $dstDir, $file, $mode )= @_;
	unless( -d $srcDir ){
		die "Error: safe_copy: the source dir is not set\n";
	}
	unless( -d $dstDir ){
		die "Error: safe_copy: the destination dir is not set\n";
	}
	my $srcFile = "$srcDir/$file";
	unless( -f $srcFile ){
		die "Error: safe_copy: the source file '$srcFile' is not found\n";
	}

	my $dstFile = "$dstDir/$file";
	my $ret = 1;

	copy( $srcFile, $dstFile ) or
		die "Error: safe_copy: can't copy '$srcFile' to '$dstFile': $!\n";

	if( $mode ){
		chmod( $mode, $dstFile ) or
			die "Error: safe_copy: can't change mode of '$dstFile': $!\n";
	}

	if( open( FILE, '<', $srcFile ) ){
		my $md5 = new Digest::MD5;
		my $srcMD5 = $md5->addfile( *FILE )->digest;
		close( FILE );

		if( open( FILE, '<', $dstFile ) ){
			$md5->reset();
			my $dstMD5 = $md5->addfile( *FILE )->digest;
			close( FILE );

			unless( $srcMD5 == $dstMD5 ){
				die "Error: safe_copy: '$dstFile' has the bad MD5 sum\n";
			}
		} else {
			warn "Error: safe_copy: can't open the destination file '$dstFile': $!\n";
			$ret = 0;
		}
	} else {
		warn "Error: safe_copy: can't open the source file '$srcFile': $!\n";
		$ret = 0;
	}

	return $ret;
}

sub move_tree{
	my( $srcDir, $dstDir ) = @_;
	&copy_tree( $srcDir, $dstDir, 1 );
}

sub copy_tree{
	my( $srcDir, $dstDir, $move ) = @_;
	my( $mode,$uid,$gid,$subPath,$dstPath,$chmod,$srcLink,$fileName,
			 %dirTree, $cwd, $realDstDir, $path );

	unless( -d $srcDir ){
		print STDERR "Modules::FileCopy::copy_tree : source is not a dir ($srcDir)\n";
		return 0;
	}

	unless( -d $dstDir){
		if( -e $dstDir ){
			unlink $dstDir;
		}
		if( mkdir( $dstDir ) ){
			( $mode, $uid, $gid ) = ( stat( $srcDir ) )[2,4,5];
			chmod $mode, $dstDir;
			chown $uid, $gid, $dstDir;
		}
	}


	my $re = qr/^\Q$srcDir\E/;
	my %realDir = ( $dstDir => [ unpack( 'C*', $dstDir ) ] );

	find
		{	'wanted' => 
				sub {
					$fileName = $_;
			
					return 0 if $fileName =~ /^\.\.?$/;

					( $mode, $uid, $gid ) = ( stat( $fileName ) )[2,4,5];
					$chmod = 0;
				
					($dstPath = $File::Find::dir) =~ s/$re/$dstDir/;

					if( exists( $realDir{$dstPath} ) ){
						$realDstDir = pack( 'C*', @{$realDir{$dstPath}} );
					}else{
						$realDstDir = $dstPath;
						$realDir{$dstPath} = [ unpack( 'C*', $realDstDir ) ];
					}					
					unless( -d $realDstDir ){
						print STDERR "Modules::FileCopy::copy_tree : can't get destination dir ($realDstDir)\n";
						return 0;
					}
					$cwd = getcwd();

					if( -d $fileName ){
#
# dir
#
						chdir $realDstDir;
						if( -e $fileName && ! -d $fileName ){
							unlink $fileName;
						}
						unless( -d $fileName ){
							if( mkdir( $fileName ) ){
								$chmod  = 1;
							}else{
								print STDERR "Modules::FileCopy::copy_tree : can't create dir: $dstPath: $!\n";
							}
						}
						
						( $path = $File::Find::name ) =~ s/$re/$dstDir/;

						$realDir{$path} = [ @{$realDir{$dstPath}},
																unpack( 'C*', '/' ),
																unpack( 'C*', $fileName ) ];

#
# end dir
#						
					}elsif( -l $fileName ){
#
# symlink
#
						chdir $realDstDir;
						if( -e $fileName ){
							unlink $fileName;
							chdir $cwd;
						}

						$srcLink = readlink( $fileName );
						if( $srcLink ){
							unless( $srcLink =~ /^\// ){
								chdir $realDstDir;
								if( symlink( $srcLink, $fileName ) ){
									chdir $cwd;
									if( $move ){
										unlink( $fileName );
									}
								}else{
									print STDERR "Modules::FileCopy::copy_tree : can't create symlink $dstPath/$fileName : $!\n";
								}
							}
						}else{
							print STDERR "Modules::FileCopy::copy_tree : can't read symlink $Find::File::name: $!\n";
						}
#
# end symlink
#					
					}elsif( -f $fileName ){
#
# file
#
						chdir $realDstDir;
						if( -e $fileName ){
							unlink $fileName;
						}
						if( open( DST, '>', $fileName ) ){
							chdir $cwd;
							if( copy( $fileName, \*DST ) ){
								if( $move ){
									unlink( $fileName );
								}
								$chmod  = 1;
							}else{
								print STDERR "Modules::FileCopy::copy_tree : can't copy $Find::File::name: $!\n";
							}
							close( DST );
						}else{
							print STDERR "Modules::FileCopy::copy_tree : can't open desctination file '$fileName': $!\n";
						}
#
# end file
#
					}else{
						if( $::DEBUG ){
							print STDERR "Modules::FileCopy::copy_tree : can't get stat of $File::Find::name\n";
						}
					}

					if( $chmod ){
						chdir $realDstDir;
						chmod( $mode, $fileName ) or
							print STDERR "Modules::FileCopy::copy_tree : can't chmod $dstPath/$fileName : $!\n";
						
						chown( $uid, $gid, $fileName ) or 
							print STDERR "Modules::FileCopy::copy_tree : can't chown $dstPath/$fileName : $!\n";
					}
					chdir $cwd;

				},

			'postprocess' =>
				sub {
					if( $move  ){
						$path = getcwd();
						if( -d $path ){
							rmdir( $path );
						}
					}
					( $path = $File::Find::dir ) =~ s/$re/$dstDir/;
					delete $realDir{$path};
				}
			},
				$srcDir;
	
}

1;
