#!/usr/bin/perl
# -*- Perl -*-

=begin comment

This code will search for files on shared networked directories
Currently it is windows only, although it could be extended for unix.

Author:  Bruce Winter bruce@misterhouse.net
URL:     http://misterhouse.net/mh/bin/find_win_programs

This free software is licensed under the terms of the GNU public license. 
Copyright 1998-2001 Bruce Winter

=cut

use strict;

my($Pgm_Path, $Pgm_Name, $Version);
BEGIN {
    ($Version) = q$Revision: 398 $ =~ /: (\S+)/; # Note: revision number is auto-updated by cvs
    ($Pgm_Path, $Pgm_Name) = $0 =~ /(.*)[\\\/](.+)\.?/;
    ($Pgm_Name) = $0 =~ /([^.]+)/, $Pgm_Path = '.' unless $Pgm_Name;
}

my %parms;
use Getopt::Long;
if (!&GetOptions(\%parms, 'h', 'help', 'v', 'boxes=s', 'skip=s', 'dirs=s') or
    !@ARGV or $parms{h} or $parms{help}) {
    print<<eof;

$Pgm_Name finds files on shared networked Windows and samba directories

  Version: $Version

  Usage:
   $Pgm_Name [options] search_string

    -h or -help  => This help text
    -v           => Verbose (shows dirs as they are searched)

    -boxes x,y,z => Search for files on boxes x,y, and z.  Defaults to all.
    -dirs  x,y,z => Search only dirs x,y, and z.  Defaults to all.            
    -skip  x,y,z => Ignore files/dirs with strings x,y, and z.  
                    Defaults to ^/win,^/program

  Examples:
    $Pgm_Name mp3
    $Pgm_Name -v -boxes "house,dm,c2" mp3
    $Pgm_Name -dirs "//house/c,//dm/d" mp3

eof
    exit;
}

my (@boxes, @shares, %counts, $search, @skip, @found);
my $time_start = time;

$search = shift;
@skip   = split ',', $parms{skip}  if $parms{skip};
@boxes  = split ',', $parms{boxes} if $parms{boxes};
@shares = split ',', $parms{dirs};
@skip   = qw(^/program ^/win) unless @skip; # Skip program and windows dirs
@boxes  = &find_boxes unless @boxes;
@shares = &find_shares(@boxes) unless @shares;

$| = 1;                         # Turn on command buffering (e.g. flush on every print)
print "\nSearching for $search in @shares\n\n";
for my $share (@shares) {
    print "Traversing share $share\n";
    &read_dir($share);
}

print "\nRead $counts{dir} directories and $counts{file} files.\n  - Found  $counts{found} files that match $search\n";
print "\nFound files:\n  ", join "\n  ", @found if @found;
print "\nRun took ", scalar(time)-$time_start, " seconds\n";


sub find_boxes {
    print "\nFinding networked box names...\n";
    my @boxes;
    for (split "\n", `net view`) {
        push @boxes, $1 if /^\\\\(\S+)/;
    }
    return @boxes;
}

sub find_shares {
    my @boxes = @_;
    my @shares;
    print "Finding shares on @boxes\n";
    for my $box (@boxes) {
        $box = '\\\\' . $box;
        for (split "\n", `net view $box`) {
            my ($share, $type) = split;
            next unless $type eq 'Disk';
            $share = $box . '\\' . $share;
            push @shares, $share;
        }
    }
    return @shares;
}

                                # This way of finding shares requires WMI to be installed
                                #  - See find_win_programs for more info.
                                #  - Probably no advantage to this.  Will not search linux samba shares.
sub find_shares_wmi {
    my @boxes = @_;
    my @shares;
    print "Finding shares on @boxes\n";
    use Win32::OLE;
    for my $box (@boxes) {
        if(my $WMI = Win32::OLE->GetObject("WinMgmts:{impersonationLevel=impersonate}!//$box")) {
            for my $share (Win32::OLE::in($WMI->InstancesOf('Win32_Share'))) {
                next unless $share->{Type} == 0;     # Look at shares only
                next if     $share->{Name} =~ /\$$/; # Ignore hidden shares
                push @shares, "//$box/$share->{Name}";
                printf "%-8s  Name: %s,  Type: %s, Status: %s  Path: %s\n",
                $box, $share->{Name}, $share->{Type}, $share->{Status}, $share->{Path};
            }
        }
    }
    return @shares;
}

sub read_dir {
    my($dir) = @_;
    return if grep $dir =~ /$_/i, @skip;
    print "  - Reading files in $dir\n" if $parms{v};
    $counts{dir}++;
    opendir(DIR, $dir) or do {print "Error in dir open: $!\n"; return};
    my @files = readdir DIR;
    close DIR;

    for my $file (sort @files) {
        next if $file eq '.' or $file eq '..';
        $file = "$dir/$file";
        &read_dir($file), next if -d $file;
        $counts{file}++;
        if ($file =~ /$search/i) {
            $counts{found}++;
            push @found, $file;
        }
    }
}
           

#
# $Log: find_files,v $
# Revision 1.1  2001/05/28 21:22:46  winter
# - 2.52 release
#
#
