### Copyright notice

# SMB - A perl module for accessing SMB resources.
# Copyright (C) 1998 Remco van Mook

# 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.

# 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., 675 Mass Ave, Cambridge, MA 02139, USA.

# The author can be contacted by e-mail: mook@cs.utwente.nl

package SMB;
use Exporter ();

use strict;
use vars qw( @ISA @EXPORT );
@ISA = qw(Exporter);
@EXPORT = qw( GetSMBTar GetSMBFile GetSMBDir GetSMBShr
              GetSMBHosts GetSMBGroups
              PutSMBFile PutSMBTar
);

## Configuration

my %cfg = ();
$cfg{bindir}="/usr/local/bin";
$cfg{masterbrowser}="biereco";

#sort an array of hashes by $_->{name} (for GetSMBDir et al)

sub byname {
  (lc $a->{name}) cmp (lc $b->{name})
}

# Gets the file //$host/$share/$file, using $user and $pass, to $target.
# And return the error code. If $target is unspecified, 
# STDOUT is used (-).
# Syntax: $error = GetSMBFile ($host,$share,$file,$user,$pass,$target)

sub GetSMBFile {
  my ($host, $share, $file, $user, $pass, $target) = @_;
  if ( $target eq "") { $target = "-" };
  $file =~ s/^(.*)\/([^\/]*)$/$1$2/ ;
  if ( $user ne "" ) { $user="-U $user"; }
  if ( $pass ne "") { 
    if ( $user eq "" ) {
      $user = "-U guest";
    }
  } else { 
    $pass = "-N";
  }
  my @args = ("$cfg{bindir}/smbclient", "//$host/$share", "$pass", "$user",  "-d0", "-c", "get \"$file\" $target");
  return system (@args);
}

# Makes a TAR of //$host/$share/$dir, using $user and $pass, to $target.
# And return the error code. If $target is unspecified, 
# STDOUT is used (-).
# Syntax: $error = GetSMBTar ($host,$share,$dir,$user,$pass,$target)

sub GetSMBTar {
  my ($host, $share, $dir, $user, $pass, $target) = @_;
  if ( $target eq "") { $target = "-" };
  if ( $user ne "" ) { $user="-U $user"; }
  if ( $pass ne "") { 
    if ( $user eq "" ) {
      $user = "-U guest";
    }
  } else { 
    $pass = "-N";
  }
  my @args = ("$cfg{bindir}/smbclient", "//$host/$share", "$pass", "$user", "-d0", "-D", "$dir", "-Tc", "$target");
  return system (@args); 
}

# Return an array with sorted dir and filelisting
# Syntax: @output = GetSMBDir (host,share,dir,user,pass)
# array contains hashes; keys: name, attr, size, date

sub GetSMBDir {
  my ($host, $share, $dir, $user, $pass ) = @_; 
  my @dir = (); my @files = ();
  if (! $user eq "") { $user = "-U ".$user }
  if ( $pass eq "") { $pass = "-N" } 

  my $lookup = "$cfg{bindir}/smbclient \"//$_[0]/$_[1]\" \"$pass\" \"$user\" -d0 -c ls -D \"$_[2]\"";

  my @out = `$lookup`;
  my $line;
  foreach $line ( @out ) {
    if ($line =~ /^  ([\S ]*\S|[\.]+) {5,}([HDRSA]+) +([0-9]+)  (\S[\S ]+\S)$/g) {
      my $rec = {};
      $rec->{name} = $1;
      $rec->{attr} = $2;
      $rec->{size} = $3;
      $rec->{date} = $4;
      if ($rec->{attr} =~ /D/) {
        push @dir, $rec;
      } else {
        push @files, $rec;
      }
    } elsif ($line =~ /^  ([\S ]*\S|[\.]+) {6,}([0-9]+)  (\S[\S ]+\S)$/) {
      my $rec = {};
      $rec->{name} = $1;
      $rec->{attr} = "";
      $rec->{size} = $2;
      $rec->{date} = $3;
      push @files, $rec; # No attributes at all, so it must be a file
    }
  } 
  my @ret = sort byname @dir;
  @files = sort byname @files;
  foreach $line ( @files ) {
    push @ret, $line;
  }
  return @ret; 
}

# Return an array with sorted share listing 
# Syntax: @output = GetSMBShr (host)
# array contains hashes; keys: name, type, comment

sub GetSMBShr {
  my $share = $_[0];
  my @ret = ();
  my $lookup = "$cfg{bindir}/smbclient -L \'$share\' -d0";
  my @out = `$lookup`;
  my $line = shift @out;
  while ( (not $line =~ /^\s+Sharename/) and ($#out >= 0) ) {
    $line = shift @out;
  }
  if ($#out >= 0) {
    $line = shift @out;
    $line = shift @out; 
    while ( (not $line =~ /^$/) and ($#out >= 0) ) {
      if ( $line =~ /^\s+([\S ]*\S)\s+(Disk)\s+([\S ]*)/ ) {
        my $rec = {};
        $rec->{name} = $1;
        $rec->{type} = $2;
        $rec->{comment} = $3;
        push @ret, $rec;
      } 
      $line = shift @out;
    }
  }
  return sort byname @ret;
}

# Return an array with sorted host listing 
# Syntax: @output = GetSMBHosts (host,group)
# array contains hashes; keys: name, comment 

sub GetSMBHosts {
  my ($host,$workgroup) = @_;
  my @ret = ();
  my $lookup = "$cfg{bindir}/smbclient -L \"$host\" -W \"$workgroup\" -d0";
  my @out = `$lookup`;
  my $line = shift @out;

  while ((not $line =~ /^This machine has a browse list/) and ($#out >= 0) ) {
    $line = shift @out;
  }
  if ($#out >= 0) {
    $line = shift @out;
    $line = shift @out;
    $line = shift @out;
    $line = shift @out;
    while ((not $line =~ /^$/) and ($#out >= 0)) {
      if ( $line =~ /^\t([\S ]*\S) {5,}(\S[\S ]*|\S|)$/ ) {
        my $rec = {};
        $rec->{name} = $1;
        $rec->{comment} = $2;
        push @ret, $rec;
      }
      $line = shift @out;
    }
  }
  return sort byname @ret; 
}

# Return an array with sorted groups listing 
# Syntax: @output = GetSMBGroups ()
# array contains hashes; keys: name, master

sub GetSMBGroups {
  my @ret = ();
  my $lookup = "$cfg{bindir}/smbclient -L \"$cfg{masterbrowser}\" -d0";
  my @out = `$lookup`;
  my $line = shift @out;

  while ((not $line =~ /^This machine has a workgroup list/) and ($#out >= 0) ) {
    $line = shift @out;
  }
  if ($#out >= 0) {
    $line = shift @out;
    $line = shift @out;
    $line = shift @out;
    $line = shift @out;
    while ((not $line =~ /^$/) and ($#out >= 0) ) {
      if ( $line =~ /^\t([\S ]*\S) {2,}(\S[\S ]*)$/ ) {
        my $rec = {};
        $rec->{name} = $1;
        $rec->{master} = $2;
        push @ret, $rec;
      }
      $line = shift @out;
    }
  }
  return sort byname @ret;
}


# Puts the file $orig to //$host/$share/$file, using $user and $pass.
# And return the error code. If $orig is unspecified, 
# STDIN is used (-).
# Syntax: $error = PutSMBFile ($host,$share,$file,$user,$pass,$orig)

sub PutSMBFile {
  my ($host, $share, $file, $user, $pass, $orig) = @_;
  if ( $orig eq "") { $orig = "-" };
  $file =~ s/^(.*)\/([^\/]*)$/$1$2/ ;
  if ( $user ne "" ) { $user="-U $user"; }
  if ( $pass ne "") { 
    if ( $user eq "" ) {
      $user = "-U guest";
    }
  } else { 
    $pass = "-N";
  }
  my @args = ("$cfg{bindir}/smbclient", "//$host/$share", "$pass", "$user",  "-d0", "-c", "put \"$orig\" \"$file\"");
  return system (@args);
}

# Puts a TAR file $orig to //$host/$share/$dir, using $user and $pass.
# And return the error code. If $orig is unspecified, 
# STDIN is used (-).
# Syntax: $error = PutSMBTar ($host,$share,$dir,$user,$pass,$orig)

sub PutSMBTar {
  my ($host, $share, $dir, $user, $pass, $orig) = @_;
  if ( $orig eq "") { $orig = "-" };
  if ( $user ne "" ) { $user="-U $user"; }
  if ( $pass ne "") { 
    if ( $user eq "" ) {
      $user = "-U guest";
    }
  } else { 
    $pass = "-N";
  }
  my @args = ("$cfg{bindir}/smbclient", "//$host/$share", "$pass", "$user", "-d0", "-D", "$dir", "-Tx", "$orig");
  return system (@args); 
}


