# -*- perl -*-
#
# $Id: qsync_utils.pm,v 1.21 1999/02/12 19:56:49 ejb Exp $
# $Source: /home/ejb/source/qsync/util/qsutil_modules/RCS/qsync_utils.pm,v $
# $Author: ejb $
#

require 5.001;
use strict;

require OnExit;
require SMBClient;
require Tarfile;
require QsyncConfig;

package main;

my $whoami = $main::progname;

# Need a better way to tell if in win32 such as using CONFIG or $^O.
my $in_win32 = exists($ENV{'COMSPEC'});

#
# General utility functions that are used by more than one tool.
#

sub get_qsdiff_args
{
    my $whoami = $main::progname;
    my $conf_name = &QsyncConfig::conf_name();

    my $config = shift;
    my $sync_dir = $config->sync_dir();

    my $filter_args = "";
    my $umask_args = "";
    my $peer = $config->get_config("peer");
    my $rename_to = $config->get_config("rename_to");

    my $conf_file = $config->conf_file();

    # Explicitly populate inclusion/exclusion list with a few special cases
    $filter_args .= " -exclude */core"
	if $config->bool_config("exclude_cores");
    $filter_args .= " -include $conf_file";
    $filter_args .= " -include " . $config->get_config("filter");
    $filter_args .= " -include $sync_dir";

    if (! $config->live())
    {
	$filter_args .= " -exclude $rename_to"
	    if ($config->get_config("rename_to") ne "");
	
	if ($peer ne "")
	{
	    $filter_args .= " -include $peer/$conf_name";
	    
	    # Specify umasks if needed
	    my $other_umask = $config->other_umask();
	    if ($other_umask ne "")
	    {
		my $umask = $config->get_config("umask");
		$umask_args .= " -old-umask $other_umask -new-umask $umask";
	    }
	}
    }

    $filter_args .= " -filter " . $config->get_config("filter");

    my $owner_flag = ($config->bool_config("ownerships") ? "" :
		      "-no-ownerships");
    my $mtime_flag = ($config->bool_config("nonfile_mtimes") ? "" :
		      "-no-nonfile-mtimes");
    "$owner_flag $mtime_flag $umask_args $filter_args";
}

sub get_qsfiles_args
{
    my $config = shift;

    my $prune_dirs = $config->get_config("prune");
    my $prune_args = "";

    $prune_dirs =~ s/^\s+|\s+$//g;
    my @prune_dirs = split(/\s+/, $prune_dirs);
    if (@prune_dirs)
    {
	$prune_args = "-prune " . join(' -prune ', @prune_dirs);
    }

    $prune_args .= " -prune-from-filter " . $config->get_config("filter");

    my $extra_args = $config->get_config("extra_qsfiles_flags");

    my $junkfiles = $config->get_config("junkfiles");
    my $one_filesys = $config->bool_config("one_filesys");

    my $clean_flag = (($junkfiles eq "cleanup") ? "-cleanup" :
		      ($junkfiles eq "ignore") ? "-ignore" :
		      "");
    my $xdev_flag = ($one_filesys ? "-xdev" : "");

    "$extra_args $prune_args $xdev_flag $clean_flag";
}

sub process_diff
{
    my ($config, $diff, $tarlist, $actions, $olddata) = @_;
    my $sync_dir = $config->sync_dir();

    open(DIFF, "<$diff") ||
	die "$whoami: can't read $sync_dir/diff: $!\n";

    while(<DIFF>)
    {
	chop;
	# Regular expressions are used here rather than split to
	# accomodate filenames with spaces in them.
	next unless m/^(\S+) (.*)$/;
	my ($cmd, $rest) = ($1, $2);
	if (($cmd eq "add") || ($cmd eq "change"))
	{
	    push(@$tarlist, $rest);
	}
	elsif (($cmd eq "rm") ||
	       ($cmd eq "rmdir") ||
	       ($cmd eq "attrib") ||
	       ($cmd eq "chmod") ||
	       ($cmd eq "chown") ||
	       ($cmd eq "chgrp") ||
	       ($cmd eq "typechange") ||
	       ($cmd eq "mkdir") ||
	       ($cmd eq "setmtime"))
	{
	    push(@$actions, $_);
	}
	elsif ($cmd eq "olddata")
	{
	    push(@$olddata, $rest);
	}
	# Otherwise, ignore
    }
    my %sync_files = ();
    my @other_files = ();
    for (@$tarlist)
    {
	if (m,^\./$sync_dir/,)
	{
	    $sync_files{$_} = 1;
	}
	else
	{
	    push(@other_files, $_);
	}
    }

    # Make sure new_sync is in the list even if stat failed (as it may
    # in some environments when the file is open, which it will be)
    # Make sure to use sync_dir rather than data_dir here.  This has
    # to be with respect to the actual tarlist.
    my $new_sync = $config->get_config("new_sync");
    my $data_dir = $config->data_dir();
    $new_sync =~ s/^$data_dir/$sync_dir/;
    $sync_files{"./$new_sync"} = 1 if (-f $new_sync);
    
    # Use sync_dir rather than data_dir for this checking.
    my $pending = &QsyncConfig::pending($sync_dir);

    @$tarlist = ();
    if (exists($sync_files{"./$pending"}))
    {
	push(@$tarlist, "./$pending");
	delete $sync_files{"./$pending"};
    }
    else
    {
	die "$whoami: INTERNAL ERROR: ./$pending is not in tarlist\n";
    }
    for (sort keys %sync_files)
    {
	push(@$tarlist, $_);
    }
    push(@$tarlist, sort @other_files);
}

sub remove_files
{
    my (@files) = @_;
    my @entries = ();
    my $f;
    foreach $f (@files)
    {
	if ((-d $f) && (! -l $f))
	{
	    local(*DIR);
	    if (opendir(DIR, $f) && (@entries = readdir(DIR)))
	    {
		splice(@entries, 0, 2); # skip . and ..
		my $e;
		foreach $e (@entries)
		{
		    &remove_files("$f/$e");
		}
	    }
	    rmdir($f) unless $f eq ".";
	}
	else
	{
	    unlink($f);
	}
    }
}

sub qsystem
{
    if ($in_win32)
    {
	my $cmd = join(' ', @_);
	system("sh -c \"$cmd\"");
    }
    else
    {
	system @_;
    }
}

sub get_smb
{
    my $config = shift;
    my $mkdir = $_[0] || 0;

    my $sync_dir = $config->sync_dir();

    my ($host, $service, $user, $pass, $dir) =
	($config->samba_config()->host(),
	 $config->samba_config()->service(),
	 $config->samba_config()->user(),
	 $config->samba_config()->pass(),
	 $config->samba_config()->dir()
	 );

    my $smb = new SMBClient($host, $service, $user, $pass);
    $smb->chdir($dir) or
	die "$whoami: can't access \\\\$host\\$service$dir: " .
	    $smb->errorcode() . "\n";
    $smb->mkdir($sync_dir) if $mkdir;
    $smb->chdir($sync_dir) or 
	die "$whoami: can't access \\\\$host\\$service$dir:$sync_dir: " .
	    $smb->errorcode() . "\n";

    my $spec = "\\\\$host\\$service$dir:$sync_dir";

    ($smb, $spec);
}

sub copy_to_samba
{
    my $config = shift;
    my ($smb, $spec) = &get_smb($config);
    
    my $data_dir = $config->data_dir();
    opendir(D, $data_dir) or
	die "$whoami: can't opendir $data_dir: $!\n";
    my @entries = readdir(D);
    closedir(D);
    
    my @files = $smb->list("");
    for (@files)
    {
	$smb->delete($_);
    }

    for (@entries)
    {
	next if ($_ eq "." || $_ eq "..");
	
	$smb->put("$data_dir/$_", $_) or
	    die "$whoami: put $_ to $spec failed: " .
		$smb->errorcode() . "\n";
    }
    undef $smb;
}

sub copy_from_samba
{
    my $config = shift;
    my ($smb, $spec) = &get_smb($config);

    my $data_dir = $config->data_dir();
    opendir(D, $data_dir) or
	die "$whoami: can't opendir $data_dir: $!\n";
    my @entries = readdir(D);
    closedir(D);
    for (@entries)
    {
	next if ($_ eq "." || $_ eq "..");
	&remove_files("$data_dir/$_");
    }

    my @files = $smb->list("");
    for (@files)
    {
	$smb->get($_, "$data_dir/$_") or
	    die "$whoami: get $_ from $spec failed " .
		$smb->errorcode() . "\n";
    }
    undef $smb;
}

1;
