package myftp; # myFTP is simply a frontend to the Net::FTP modules # I've written this to simplify all those FTP-Transfers here, so that I have a nice overview what happened (stats()) and easy error-catching # The idea is to have all warnings etc saved, and not print while things are called, but have a nice formatted statistic when it quits. # Some functions still need to be done like rename() etc, they currently aren't in because I don't need them but I plan to put in even more stuff # But primary this was just something for me to learn a bit about how OO-Perl works...but maybe some other people find it usefull, too... # Errormessages can be accessed via $ftp->{'errormsg'}. # # myFTP usage: # ~~~~~~~~~~~ # use myftp; # my $ftp = new myftp ( host => 'myhost', # Host to connect to # port => '21', # Port to connect to # user => 'myuser', # username for login # pw => 'mypassword', # password for login # timeout => '20', # default is 30 # retries => '3', # How often should an action be retried if it fails? # verbose => '0', # Verbose-Level: 0 = no warnings, no failures # 1 = no warnings, failures printed # 2 = warnings and failures printed # 3 = same as 2 + more details # debug => '0', # Use Net::FTP-Debug mode? (default 0) # stats => '1', # Print stats when object is DESTROYed? # # $ftp->get('remotefile', 'localfile') || die "couldn't get remotefile: $self->{'errormsg'}; # $ftp->put('localfile', 'remotefile'); # $ftp->ls('remotefile'); # $ftp->dir('remotefile'); # $ftp->delete('remotefile'); # $ftp->stats(); # # use strict; use Net::FTP; use Carp; sub new { my $proto = shift; my $class = ref($proto) || $proto; my $self = {}; my %arg = @_; $self->{'host'} = defined($arg{'host'}) ? $arg{'host'} : do { carp "No host specified!"; return undef; }; $self->{'port'} = defined($arg{'port'}) ? $arg{'port'} : 21; $self->{'user'} = defined($arg{'user'}) ? $arg{'user'} : do { carp "No user specified!"; return undef; }; $self->{'pw'} = defined($arg{'pw'}) ? $arg{'pw'} : do { carp "No password(pw) specified!"; return undef; }; $self->{'timeout'} = defined($arg{'timeout'}) ? $arg{'timeout'} : 30; $self->{'retries'} = defined($arg{'retries'}) ? $arg{'retries'} : 0; $self->{'verbose'} = defined($arg{'verbose'}) ? $arg{'verbose'} : 1; $self->{'debug'} = defined($arg{'debug'}) ? $arg{'debug'} : 0; $self->{'stats'} = defined($arg{'stats'}) ? $arg{'stats'} : 1; $self->{'files_ok'} = {}; $self->{'files_failed'} = {}; $self->{'sub_warnings'} = []; $self->{'failures'} = 0; $self->{'warnings'} = 0; $self->{'errormsg'} = ''; $self->{'connected'} = ''; $self->{'start_time'} = scalar(localtime); $self->{'ftp'} = ''; $SIG{__WARN__} = sub { push(@{ $self->{'sub_warnings'} }, shift); } if $self->{'verbose'} == 0; bless ($self, $class); { my $ok = 0; my $counter = -1; my $start_time = time; do { unless ($self->{'ftp'} = Net::FTP->new($self->{'host'}, Port => $self->{'port'}, Timeout => $self->{'timeout'}, Debug => $self->{'debug'})) { carp "Couldn't connect to $self->{'host'}" if $self->{'verbose'} > 1; $self->{'warnings'}++; $counter++; } else { $self->{'time'}{'connect'} = (time - $start_time); print "Connection to $self->{'host'} established.\n" if $self->{'verbose'} > 2; $ok++; } } while ($counter < $self->{'retries'} && $ok == 0); do { carp "Couldn't connect to $self->{'host'}"; return undef; } if $ok == 0; } { my $ok = 0; my $counter = -1; my $start_time = time; do { unless ($self->{'ftp'}->login($self->{'user'}, $self->{'pw'})) { carp "Couldn't login to $self->{'host'}: FTP-Error ".$self->{'ftp'}->code()." : ".$self->{'ftp'}->message() if $self->{'verbose'} > 1; $self->{'warnings'}++; $counter++; } else { $self->{'time'}{'login'} = (time - $start_time); print "Login accepted to $self->{'host'}.\n" if $self->{'verbose'} > 2; $ok++; } } while ($counter < $self->{'retries'} && $ok == 0); do { carp "FTP Failed: Couldn't login to $self->{'host'}"; return undef; } if $ok == 0; } return $self; } sub DESTROY { my $self = shift; $self->{'ftp'}->quit() if defined($self->{'ftp'}); print $self->status() if $self->{'stats'} > 0; } sub status { my $self = shift; my @data = ("##################".("#" x length(scalar(localtime)))."\n", "# myFTP Stats \@ ".scalar(localtime)." #\n", "##################".("#" x length(scalar(localtime)))."\n\n", sprintf("%-23s %s %s", 'Started:', $self->{'start_time'}, "\n"), sprintf("%-23s %s %s", 'Remote host:', $self->{'host'}.":".$self->{'port'},"\n"), sprintf("%-23s %s %s", 'User:', $self->{'user'}, "\n\n"), sprintf("%-23s %s %s", 'Warnings:', $self->{'warnings'}, "\n"), sprintf("%-23s %s %s", 'Failures:', $self->{'failures'}, "\n\n"), sprintf("%-23s %s %s", 'Connect-Time (secs):', $self->{'time'}{'connect'}, "\n"), sprintf("%-23s %s %s", 'Login-Time (secs):', $self->{'time'}{'login'}, "\n\n") ); push(@data, (scalar(keys %{ $self->{'files_ok'} })." files transfered\n", "=================".("=" x length(scalar(keys %{ $self->{'files_ok'} })))."\n")); foreach (keys %{ $self->{'files_ok'} }) { $self->{'files_ok'}{$_} > 59 ? push(@data, "$_ in ".($self->{'files_ok'}{$_} / 60)." minute(s)\n") : push(@data, "$_ in $self->{'files_ok'}{$_} second(s)\n"); } push(@data, ("\n".scalar(keys %{ $self->{'files_failed'} })." files not transfered\n", "=====================".("=" x length(scalar(keys %{ $self->{'files_failed'} })))."\n")); push(@data, "$_ : $self->{'files_failed'}{$_}") foreach (keys %{ $self->{'files_failed'} }); push(@data, ("\n".scalar(keys %{ $self->{'time'}{'commands'} })." other commands done\n", "====================".("=" x length(scalar(keys %{ $self->{'time'}{'commands'} })))."\n")); push(@data, "Time(secs) for: $_: $self->{'time'}{'commands'}{$_}\n") foreach keys %{ $self->{'time'}{'commands'} }; push(@data, ("\n".scalar(@{ $self->{'sub_warnings'} })." unexpected warnings\n", "====================".("=" x length(scalar(@{ $self->{'sub_warnings'} })))."\n")); push(@data, $_) foreach(@{ $self->{'sub_warnings'} }); return @data; } sub error { my $self = shift; my $msg = shift; my $file = shift; $self->{'errormsg'} = $msg; $self->{'failures'}++; $self->{'files_failed'}{$file} = $msg if $file; warn $msg if $self->{'verbose'} > 0; return; } sub dir { my $self = shift; my $dir = shift || do { $self->error("dir() needs dir/file as parameter!"); return undef; }; my @dir = (); { my $ok = 0; my $counter = -1; my $start_time = time; do { unless (@dir = $self->{'ftp'}->dir($dir)) { carp "Couldn't list(dir) $dir: ".$self->{'ftp'}->code().": ".$self->{'ftp'}->message() if $self->{'verbose'} > 1; $self->{'warnings'}++; $counter++; } else { $self->{'time'}{'commands'}{"dir($dir)"} = (time - $start_time); $ok++; } } while ($counter < $self->{'retries'} && $ok == 0); do { $self->error("Couldn't list(dir) $dir: ".$self->{'ftp'}->code().": ".$self->{'ftp'}->message()); return undef; } if $ok == 0; } return @dir; } sub ls { my $self = shift; my $dir = shift || do { $self->{'errormsg'} = "ls() needs dir/file as parameter!"; return undef; }; my @dir = (); { my $ok = 0; my $counter = -1; my $start_time = time; do { unless (@dir = $self->{'ftp'}->ls($dir)) { carp "Couldn't list(ls) $dir: ".$self->{'ftp'}->code().": ".$self->{'ftp'}->message() if $self->{'verbose'} > 1; $self->{'warnings'}++; $counter++; } else { $self->{'time'}{'commands'}{"ls($dir)"} = (time - $start_time); $ok++; } } while ($counter < $self->{'retries'} && $ok == 0); do { $self->error("Couldn't list(ls) $dir: ".$self->{'ftp'}->code().": ".$self->{'ftp'}->message()); return undef; } if $ok == 0; } return @dir; } sub size { my $self = shift; my $file = shift || do { $self->{'errormsg'} = "size() needs file as parameter!"; return undef; }; my $size = ''; { my $ok = 0; my $counter = -1; my $start_time = time; do { unless($size = grep_size($self->{'ftp'}->dir($file))) { carp "Couldn't get size of $file:".$self->{'ftp'}->code().": ".$self->{'ftp'}->message() if $self->{'verbose'} > 1; $self->{'warnings'}++; $counter++; } else { $self->{'time'}{'commands'}{"size($file)"} = (time - $start_time); $ok++; } } while ($counter < $self->{'retries'} && $ok == 0); do { $self->error("Couldn't get size of $file: ".$self->{'ftp'}->code().": ".$self->{'ftp'}->message()); return undef; } if $ok == 0; } sub grep_size { my $size; foreach(@_) { ($size) = $_ =~ /^.*?\s+.*?\s+.*?\s+.*?\s+(.*?)\s.*?/; } return $size if $size =~ /^\d*$/; return undef; } return $size; } sub delete { my $self = shift; my $file = shift || do { $self->{'errormsg'} = "delete() needs file as parameter!"; return undef; }; { my $ok = 0; my $counter = -1; my $start_time = time; do { unless ($self->{'ftp'}->delete($file)) { carp "Couldn't delete $file: ".$self->{'ftp'}->code().": ".$self->{'ftp'}->message() if $self->{'verbose'} > 1; $self->{'warnings'}++; $counter++; } else { $self->{'time'}{'commands'}{"delete($file)"} = (time - $start_time); $ok++; } } while ($counter < $self->{'retries'} && $ok == 0); do { $self->error("Couldn't delete $file: ".$self->{'ftp'}->code().": ".$self->{'ftp'}->message()); return undef; } if $ok == 0; } return 1; } sub put { my $self = shift; my %arg = @_; my $localfile = $arg{'localfile'} || do { $self->{'errormsg'} = "put() needs (localfile,remotefile) as parameters!"; return undef; };; my $remotefile = $arg{'remotefile'} || do { $self->{'errormsg'} = "put() needs (localfile,remotefile) as parameters!"; return undef; };; my $mode = $arg{'mode'} || 'binary'; { my $ok = 0; my $counter = -1; my $start_time = time; do { unless ($self->{'ftp'}->$mode()) { carp "Couldn't set mode to $mode!: ".$self->{'ftp'}->code().": ".$self->{'ftp'}->message() if $self->{'verbose'} > 1; $self->{'warnings'}++; $counter++; } else { $ok++; } } while ($counter < $self->{'retries'} && $ok == 0); do { $self->error("Couldn't upload $localfile as $remotefile: Couldn't change mode to $mode: ".$self->{'ftp'}->code().": ".$self->{'ftp'}->message(), "$localfile => $remotefile"); return undef; } if $ok == 0; } { my $ok = 0; my $counter = -1; my $start_time = time; do { unless ($self->{'ftp'}->put($localfile, $remotefile)) { carp "Couldn't upload $localfile as $remotefile: ".$self->{'ftp'}->code().": ".$self->{'ftp'}->message() if $self->{'verbose'} > 1; $self->{'warnings'}++; $counter++; } else { $self->{'files_ok'}{"$localfile => $remotefile"} = (time - $start_time); $self->{'files'}++; $ok++; } } while ($counter < $self->{'retries'} && $ok == 0); do { $self->error("Couldn't upload $localfile as $remotefile: ".$self->{'ftp'}->code().": ".$self->{'ftp'}->message(), "$localfile => $remotefile"); return undef; } if $ok == 0; } return 1; } sub get { my $self = shift; my %arg = @_; my $remotefile = $arg{'remotefile'} || do { $self->{'errormsg'} = "put() needs (localfile,remotefile) as parameters!"; $self->{'failures'}++; return undef; };; my $localfile = $arg{'localfile'} || do { $self->{'errormsg'} = "put() needs (localfile,remotefile) as parameters!"; $self->{'failures'}++; return undef; };; my $mode = $arg{'mode'} || 'binary'; { my $ok = 0; my $counter = -1; my $start_time = time; do { unless ($self->{'ftp'}->$mode()) { carp "Couldn't set mode to $mode!: ".$self->{'ftp'}->code().": ".$self->{'ftp'}->message() if $self->{'verbose'} > 1; $self->{'warnings'}++; $counter++; } else { $ok++; } } while ($counter < $self->{'retries'} && $ok == 0); do { $self->error("Couldn't download $remotefile as $localfile: Couldn't change mode to $mode: ".$self->{'ftp'}->code().": ".$self->{'ftp'}->message(), "$remotefile => $localfile"); return undef; } if $ok == 0; } { my $ok = 0; my $counter = -1; my $start_time = time; do { unless ($self->{'ftp'}->get($remotefile, $localfile)) { carp "Couldn't download $remotefile as $localfile!" if $self->{'verbose'} > 1; $self->{'warnings'}++; $counter++; } else { $self->{'files_ok'}{"$localfile => $remotefile"} = (time - $start_time); $self->{'files'}++; $ok++; } } while ($counter < $self->{'retries'} && $ok == 0); do { $self->error("Couldn't download $remotefile as $localfile: ".$self->{'ftp'}->code().": ".$self->{'ftp'}->message(), "$remotefile => $localfile"); return undef; } if $ok == 0; } return 1; } 1;