1975 lines
48 KiB
Prolog
1975 lines
48 KiB
Prolog
#!/usr/bin/perl
|
|
|
|
use Time::HiRes qw(gettimeofday tv_interval);
|
|
|
|
$| = 1;
|
|
#my $log = substr($ENV{'SCRIPT_FILENAME'}, 0, rindex($ENV{'SCRIPT_FILENAME'}, '/public_html/')) . "/log.txt";
|
|
|
|
#open(STDERR, ">>$log");
|
|
use CGI;
|
|
use CGI::Carp qw(fatalsToBrowser);
|
|
use Net::FTP;
|
|
|
|
my $q = new CGI;
|
|
my ($ftp,$local_dir,$start_time);
|
|
my ($ftrans,$fsize,$strans,$ssize,$etrans,$esize) = 0;
|
|
my $server = url_unescape($q->param('server'));
|
|
my $username = url_unescape($q->param('username'));
|
|
my $password = url_unescape($q->param('password'));
|
|
my $remote_dir = url_unescape($q->param('remote_dir'));
|
|
my @ascii = qw(asa asp aspx c cpp cgi css hqx htaccess htm html inc ini js jsp log php php3 pl pm ps py rb shar shtm shtml sql tex txt url uu xml xsl);
|
|
my $local_root = substr($ENV{'SCRIPT_FILENAME'}, 0, rindex($ENV{'SCRIPT_FILENAME'}, '/public_html/'));
|
|
my $local_dir = url_unescape($q->param('local_dir'));
|
|
|
|
if ($local_dir !~ /\/$/) {
|
|
$local_dir = "$local_dir/";
|
|
}
|
|
|
|
print "Content-type: text/html\n\n";
|
|
print qq|
|
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
|
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
|
|
|
<html>
|
|
<head>
|
|
<title>Site Transfer Script</title>
|
|
<style type="text/css">
|
|
a:link {
|
|
text-decoration: none;
|
|
}
|
|
a:visited {
|
|
text-decoration: none;
|
|
}
|
|
a:hover {
|
|
text-decoration: none;
|
|
}
|
|
a:active {
|
|
text-decoration: none;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<form method="get" action="transfer.pl">
|
|
<label for="server">Server</label><input type="text" name="server" size=40 value="| . $server . qq{" />
|
|
<label for="username">Username</label><input type="text" name="username" value="} . $username . qq{" />
|
|
<label for="password">Password</label><input type="text" name="password" value="} . $password . qq{" />
|
|
<input type="submit" name="submit" value="Connect" />
|
|
<input type="submit" name="submit" value="Delete" />
|
|
};
|
|
|
|
if ($q->param('submit') eq 'Delete') {
|
|
my $del = unlink($ENV{'SCRIPT_FILENAME'});
|
|
|
|
print qq{
|
|
<table>
|
|
<tr>
|
|
<td>
|
|
};
|
|
if ($del) {
|
|
print '<p>Transfer script deleted</p>';
|
|
}
|
|
else {
|
|
print '<p>Transfer script was not deleted, please remove manually</p>';
|
|
}
|
|
print qq{
|
|
</td>
|
|
</tr>
|
|
};
|
|
}
|
|
|
|
elsif ($q->param('submit') eq 'Connect' || $q->param('submit') eq 'Transfer Selected') {
|
|
print qq{
|
|
<table border=0 cellpadding=0 cellspacing=0 width="100%">
|
|
<tr>
|
|
<td>
|
|
Connecting to <font color="maroon">$server</font> →
|
|
};
|
|
my $rv = $ftp = Net::FTP->new($server, Debug => 0, Hash => \*STDOUT, Passive => 1, Timeout => 60);
|
|
if ($rv) {
|
|
print "Success";
|
|
}
|
|
else {
|
|
print "Connection failed: $@";
|
|
exit;
|
|
}
|
|
print qq{
|
|
<tr>
|
|
<td>
|
|
Logging in using username: <font color="maroon">$username</font> and password: <font color="maroon">$password</font> →
|
|
};
|
|
$rv = $ftp->login($username, $password);
|
|
if ($rv) {
|
|
print "Success";
|
|
}
|
|
else {
|
|
print "Login failed: " . $ftp->message;
|
|
}
|
|
print qq{
|
|
</table>
|
|
<br />
|
|
<table border=1 cellpadding=0 cellspacing=0 width="100%">
|
|
<tr>
|
|
<td>
|
|
};
|
|
$remote_dir = $ftp->pwd if !$remote_dir;
|
|
|
|
if (defined($remote_dir) && $remote_dir !~ /\/$/) {
|
|
$remote_dir = "$remote_dir/";
|
|
}
|
|
|
|
my $rv = $ftp->cwd($remote_dir);
|
|
if ($rv){
|
|
print "Current remote directory: <font color=\"blue\">$remote_dir</font>";
|
|
}
|
|
else {
|
|
print "<font color=\"red\">Error accessing</font> <font color=\"blue\">$remote_dir</font> <font color=\"red\">" . $ftp->message . "</font><br />";
|
|
exit;
|
|
}
|
|
print "<td>Current local directory (files will be transferred here): <font color=\"blue\">$local_root$local_dir</font>";
|
|
|
|
my @dir = sort_list('display', $ftp->dir);
|
|
print qq{
|
|
<tr>
|
|
<td valign="top">
|
|
<table border=0 cellpadding=0 cellspacing=0 width="100%">
|
|
};
|
|
foreach my $value (@dir) {
|
|
if ($value->{'type'} eq '-') {
|
|
print qq{
|
|
<tr>
|
|
<td><input type="checkbox" name="transfer_file" value="} . url_escape($value->{'name'} . '=|=' . $value->{'size'}) . qq{" };
|
|
print "checked=\"checked\" " if grep {(split(/=|=/, url_unescape($_)))[0] eq $value->{'name'}} $q->param('transfer_file');
|
|
print qq{/>
|
|
<td>} . $value->{'type'} . $value->{'mode'} . qq{
|
|
<td>} . $value->{'nlink'} . qq{
|
|
<td>} . $value->{'uid'} . qq{
|
|
<td>} . $value->{'gid'} . qq{
|
|
<td>} . size_convert($value->{'size'}) . qq{
|
|
<td>} . $value->{'date'} . qq{
|
|
<td><font color="green">} . $value->{'name'} . qq{</font>};
|
|
}
|
|
elsif ($value->{'type'} eq 'd') {
|
|
if ($value->{'name'} eq '.') {
|
|
print qq{
|
|
<tr>
|
|
<td></td>
|
|
<td>} . $value->{'type'} . $value->{'mode'} . qq{
|
|
<td>} . $value->{'nlink'} . qq{
|
|
<td>} . $value->{'uid'} . qq{
|
|
<td>} . $value->{'gid'} . qq{
|
|
<td>} . size_convert($value->{'size'}) . qq{
|
|
<td>} . $value->{'date'} . qq{
|
|
<td><a href="transfer.pl?server=} . url_escape($server) . qq{&username=} . url_escape($username) . qq{&password=} . url_escape($password) . qq{&remote_dir=} . url_escape($remote_dir) . qq{&local_dir=} . url_escape($local_dir) . qq{&submit=Connect">} . $value->{'name'} . qq{</a>};
|
|
#<td><a href="transfer.pl?server=} . url_escape($server) . qq{&username=} . url_escape($username) . qq{&password=} . url_escape($password) . qq{&remote_dir=} . url_escape($remote_dir) . qq{&local_dir=} . url_escape($local_dir) . qq{&submit=Connect&transfer=1">Transfer</a>
|
|
}
|
|
elsif ($value->{'name'} eq '..') {
|
|
my $remote_dir = $remote_dir;
|
|
chop($remote_dir) if $remote_dir ne '/';
|
|
print qq{
|
|
<tr>
|
|
<td></td>
|
|
<td>} . $value->{'type'} . $value->{'mode'} . qq{
|
|
<td>} . $value->{'nlink'} . qq{
|
|
<td>} . $value->{'uid'} . qq{
|
|
<td>} . $value->{'gid'} . qq{
|
|
<td>} . size_convert($value->{'size'}) . qq{
|
|
<td>} . $value->{'date'} . qq{
|
|
<td><a href="transfer.pl?server=} . url_escape($server) . qq{&username=} . url_escape($username) . qq{&password=} . url_escape($password) . qq{&remote_dir=} . url_escape(substr($remote_dir, 0, rindex($remote_dir, '/'))) . qq{&local_dir=} . url_escape($local_dir) . qq{&submit=Connect">} . $value->{'name'} . qq{</a>};
|
|
}
|
|
else {
|
|
print qq{
|
|
<tr>
|
|
<td><input type="checkbox" name="transfer_dir" value="} . $value->{'name'} . qq{" };
|
|
print "checked=\"checked\" " if grep {(split(/=|=/, url_unescape($_)))[0] eq $value->{'name'}} $q->param('transfer_dir');
|
|
print qq{/>
|
|
<td>} . $value->{'type'} . $value->{'mode'} . qq{
|
|
<td>} . $value->{'nlink'} . qq{
|
|
<td>} . $value->{'uid'} . qq{
|
|
<td>} . $value->{'gid'} . qq{
|
|
<td>} . size_convert($value->{'size'}) . qq{
|
|
<td>} . $value->{'date'} . qq{
|
|
<td><a href="transfer.pl?server=} . url_escape($server) . qq{&username=} . url_escape($username) . qq{&password=} . url_escape($password) . qq{&remote_dir=} . url_escape($remote_dir . $value->{'name'}) . qq{&local_dir=} . url_escape($local_dir) . qq{&submit=Connect">} . $value->{'name'} . qq{</a>};
|
|
#<td><a href="transfer.pl?server=} . url_escape($server) . qq{&username=} . url_escape($username) . qq{&password=} . url_escape($password) . qq{&remote_dir=} . url_escape($remote_dir . $value->{'name'}) . qq{&local_dir=} . url_escape($local_dir) . qq{&submit=Connect&transfer=1">Transfer</a>
|
|
}
|
|
}
|
|
elsif ($value->{'type'} eq 'l') {
|
|
print qq{
|
|
<tr>
|
|
<td><input type="checkbox" name="transfer_dir" value="} . $value->{'name'} . qq{" />
|
|
<td>} . $value->{'type'} . $value->{'mode'} . qq{
|
|
<td>} . $value->{'nlink'} . qq{
|
|
<td>} . $value->{'uid'} . qq{
|
|
<td>} . $value->{'gid'} . qq{
|
|
<td>} . size_convert($value->{'size'}) . qq{
|
|
<td>} . $value->{'date'} . qq{
|
|
<td><a href="transfer.pl?server=} . url_escape($server) . qq{&username=} . url_escape($username) . qq{&password=} . url_escape($password) . qq{&remote_dir=} . url_escape($remote_dir . $value->{'name'}) . qq{&local_dir=} . url_escape($local_dir) . qq{&submit=Connect">} . $value->{'name'} . qq{</a> → } . $value->{'symlink'} . qq{};
|
|
#<td><a href="transfer.pl?server=} . url_escape($server) . qq{&username=} . url_escape($username) . qq{&password=} . url_escape($password) . qq{&remote_dir=} . url_escape($remote_dir . $value->{'name'}) . qq{&local_dir=} . url_escape($local_dir) . qq{&submit=Connect&transfer=1">Transfer</a>
|
|
}
|
|
else {
|
|
next;
|
|
}
|
|
}
|
|
print "</table>";
|
|
my @dir = sort_list('display', `ls -la $local_root$local_dir`);
|
|
print qq{
|
|
<td valign="top">
|
|
<table border=0 cellpadding=0 cellspacing=0 width="100%">
|
|
};
|
|
foreach my $value (@dir) {
|
|
if ($value->{'type'} eq '-') {
|
|
print qq{
|
|
<tr>
|
|
<td>} . $value->{'type'} . $value->{'mode'} . qq{
|
|
<td>} . $value->{'nlink'} . qq{
|
|
<td>} . $value->{'uid'} . qq{
|
|
<td>} . $value->{'gid'} . qq{
|
|
<td>} . size_convert($value->{'size'}) . qq{
|
|
<td>} . $value->{'date'} . qq{
|
|
<td><font color="green">} . $value->{'name'} . qq{</font>};
|
|
}
|
|
elsif ($value->{'type'} eq 'd') {
|
|
if ($value->{'name'} eq '.') {
|
|
print qq{
|
|
<tr>
|
|
<td>} . $value->{'type'} . $value->{'mode'} . qq{
|
|
<td>} . $value->{'nlink'} . qq{
|
|
<td>} . $value->{'uid'} . qq{
|
|
<td>} . $value->{'gid'} . qq{
|
|
<td>} . size_convert($value->{'size'}) . qq{
|
|
<td>} . $value->{'date'} . qq{
|
|
<td><a href="transfer.pl?server=} . url_escape($server) . qq{&username=} . url_escape($username) . qq{&password=} . url_escape($password) . qq{&remote_dir=} . url_escape($remote_dir) . qq{&local_dir=} . url_escape($local_dir) . qq{&submit=Connect">} . $value->{'name'} . qq{</a>};
|
|
}
|
|
elsif ($value->{'name'} eq '..') {
|
|
my $local_dir = $local_dir;
|
|
chop($local_dir) if $local_dir ne '/';
|
|
print qq{
|
|
<tr>
|
|
<td>} . $value->{'type'} . $value->{'mode'} . qq{
|
|
<td>} . $value->{'nlink'} . qq{
|
|
<td>} . $value->{'uid'} . qq{
|
|
<td>} . $value->{'gid'} . qq{
|
|
<td>} . size_convert($value->{'size'}) . qq{
|
|
<td>} . $value->{'date'} . qq{
|
|
<td><a href="transfer.pl?server=} . url_escape($server) . qq{&username=} . url_escape($username) . qq{&password=} . url_escape($password) . qq{&remote_dir=} . url_escape($remote_dir) . qq{&local_dir=} . url_escape(substr($local_dir, 0, rindex($local_dir, '/'))) . qq{&submit=Connect">} . $value->{'name'} . qq{</a>};
|
|
}
|
|
else {
|
|
print qq{
|
|
<tr>
|
|
<td>} . $value->{'type'} . $value->{'mode'} . qq{
|
|
<td>} . $value->{'nlink'} . qq{
|
|
<td>} . $value->{'uid'} . qq{
|
|
<td>} . $value->{'gid'} . qq{
|
|
<td>} . size_convert($value->{'size'}) . qq{
|
|
<td>} . $value->{'date'} . qq{
|
|
<td><a href="transfer.pl?server=} . url_escape($server) . qq{&username=} . url_escape($username) . qq{&password=} . url_escape($password) . qq{&remote_dir=} . url_escape($remote_dir) . qq{&local_dir=} . url_escape($local_dir . $value->{'name'}) . qq{&submit=Connect">} . $value->{'name'} . qq{</a>};
|
|
}
|
|
}
|
|
elsif ($value->{'type'} eq 'l') {
|
|
print qq{
|
|
<tr>
|
|
<td>} . $value->{'type'} . $value->{'mode'} . qq{
|
|
<td>} . $value->{'nlink'} . qq{
|
|
<td>} . $value->{'uid'} . qq{
|
|
<td>} . $value->{'gid'} . qq{
|
|
<td>} . size_convert($value->{'size'}) . qq{
|
|
<td>} . $value->{'date'} . qq{
|
|
<td><a href="transfer.pl?server=} . url_escape($server) . qq{&username=} . url_escape($username) . qq{&password=} . url_escape($password) . qq{&remote_dir=} . url_escape($remote_dir) . qq{&local_dir=} . url_escape($local_dir . $value->{'name'}) . qq{&submit=Connect">} . $value->{'name'} . qq{</a> → } . $value->{'symlink'};
|
|
}
|
|
else {
|
|
next;
|
|
}
|
|
}
|
|
print "</table>";
|
|
|
|
if ($q->param('submit') eq 'Transfer Selected') {
|
|
print "</table>";
|
|
print "<input type=\"submit\" name=\"submit\" value=\"Transfer Selected\" />";
|
|
print "<input type=\"hidden\" name=\"remote_dir\" value=\"" . url_escape($remote_dir) . "\" />";
|
|
print "<input type=\"hidden\" name=\"local_dir\" value=\"" . url_escape($local_dir) . "\" />";
|
|
print "</form>";
|
|
print "<table border=1 cellpadding=0 cellspacing=0 width=\"100%\">";
|
|
print " <tr>";
|
|
print " <td>";
|
|
print " RemoteFilename";
|
|
print " </td>";
|
|
print " <td>";
|
|
print " Remote File Type";
|
|
print " </td>";
|
|
print " <td>";
|
|
print " Remote File Size";
|
|
print " </td>";
|
|
print " <td>";
|
|
print " Download Progress";
|
|
print " </td>";
|
|
print " <td>";
|
|
print " Local Permission";
|
|
print " </td>";
|
|
print " <td>";
|
|
print " Local File Size";
|
|
print " </td>";
|
|
print " </tr>";
|
|
$start_time = [gettimeofday];
|
|
|
|
my @files_to_transfer = $q->param('transfer_file');
|
|
my @dirs_to_transfer = $q->param('transfer_dir');
|
|
foreach my $target_file (@files_to_transfer) {
|
|
$target_file = url_unescape($target_file);
|
|
my ($target_name, $target_size) = split(/=\|=/, $target_file);
|
|
transfer_file($remote_dir, $local_root . $local_dir, $target_name, $target_size);
|
|
}
|
|
foreach my $target_dir (@dirs_to_transfer) {
|
|
transfer_dir($remote_dir, $local_root . $local_dir, $target_dir);
|
|
}
|
|
|
|
print "</table><br />";
|
|
print "Transfer completed.<br />";
|
|
print "Susscessfully transferred $ftrans files totalling " . size_convert($fsize) . ".<br />";
|
|
print "Failed transferring $etrans files totalling " . size_convert($esize) . ".<br />";
|
|
print "Skipped transferring $strans files totalling " . size_convert($ssize) . ".<br />";
|
|
print "Transfer request covered " . ($ftrans + $strans + $etrans) . " files totalling " . size_convert($fsize + $ssize + $esize) . ".<br />";
|
|
print "Transfer request took " . time_convert(tv_interval($start_time, [gettimeofday])) . " to complete.";
|
|
}
|
|
else {
|
|
print "</table>";
|
|
print "<input type=\"submit\" name=\"submit\" value=\"Transfer Selected\" />";
|
|
print "<input type=\"hidden\" name=\"remote_dir\" value=\"" . url_escape($remote_dir) . "\" />";
|
|
print "<input type=\"hidden\" name=\"local_dir\" value=\"" . url_escape($local_dir) . "\" />";
|
|
print "</form>";
|
|
}
|
|
|
|
$ftp->quit;
|
|
}
|
|
|
|
else {
|
|
print "</form>";
|
|
}
|
|
|
|
print qq{
|
|
</body>
|
|
</html>
|
|
};
|
|
|
|
#close(STDERR);
|
|
|
|
|
|
sub transfer_file {
|
|
my $remote_dir = shift;
|
|
my $local_dir = shift;
|
|
my $target_file = shift;
|
|
my $target_size = shift;
|
|
my $rv = $ftp->cwd($remote_dir);
|
|
if (! $rv) {
|
|
if ($ftp->message =~ /connection closed/i || $ftp->message =~ /timeout/i) {
|
|
$ftp = Net::FTP->new($server, Debug => 0, Hash => \*STDOUT, Passive => 1, Timeout => 60);
|
|
$ftp->login($username, $password);
|
|
$ftp->cwd($remote_dir);
|
|
}
|
|
else {
|
|
print "<font color=\"red\">Error accessing</font> <font color=\"blue\">$remote_dir</font> <font color=\"red\">" . $ftp->message . "</font><br />";
|
|
return;
|
|
}
|
|
}
|
|
if ($local_dir !~ /\/$/) {
|
|
$local_dir = "$local_dir/";
|
|
}
|
|
if ($remote_dir !~ /\/$/) {
|
|
$remote_dir = "$remote_dir/";
|
|
}
|
|
|
|
my $rv;
|
|
my $type = 'binary';
|
|
foreach my $asc (@ascii) {
|
|
if ($target_file =~ /\.$asc$/) {
|
|
$type = 'ascii';
|
|
last;
|
|
}
|
|
}
|
|
|
|
print " <tr>";
|
|
print " <td>";
|
|
print " <font color=\"green\">$target_file</font>";
|
|
print " </td>";
|
|
print " <td>";
|
|
print " $type file";
|
|
print " </td>";
|
|
print " <td>";
|
|
print " " . size_convert($target_size);
|
|
print " </td>";
|
|
|
|
if (-e $local_dir . $target_file && $target_file ne '.htaccess') {
|
|
$strans++;
|
|
$ssize += $target_size;
|
|
print " <td>";
|
|
print " Exists, skipping";
|
|
print " </td>";
|
|
print " <td>";
|
|
print " <br />";
|
|
print " </td>";
|
|
print " <td>";
|
|
print " <br />";
|
|
print " </td>";
|
|
print " </tr>";
|
|
}
|
|
else {
|
|
if ($type eq 'ascii') {
|
|
$ftp->ascii;
|
|
}
|
|
else {
|
|
$ftp->binary;
|
|
}
|
|
|
|
print " <td>";
|
|
|
|
my $bytes_out = tell(*STDOUT);
|
|
my $start_transfer = [gettimeofday];
|
|
$ftp->hash(\*STDOUT, 2048);
|
|
my $rv = $ftp->get($target_file, $local_dir . $target_file);
|
|
|
|
if ($rv) {
|
|
if ((tell(*STDOUT) - $bytes_out) == 1) {
|
|
print "#";
|
|
}
|
|
my $transfer_time = tv_interval($start_transfer, [gettimeofday]) || 1;
|
|
# print " <br />Complete: $value->{'size'} bytes in " . sprintf("%.2f", $transfer_time) . " seconds<br />(". sprintf("%.2f", $value->{'size'} / $transfer_time) . " bytes per second)";
|
|
print " </td>";
|
|
$ftrans++;
|
|
$fsize += $target_size;
|
|
my $rv;
|
|
if ($target_file =~ /\.pl$/ || $target_file =~ /\.cgi$/) {
|
|
print " <td>";
|
|
$rv = chmod(0755, "$local_dir$target_file");
|
|
if ($rv) {
|
|
print "755";
|
|
}
|
|
else {
|
|
print "<br />";
|
|
}
|
|
print " </td>";
|
|
}
|
|
else {
|
|
print " <td>";
|
|
$rv = chmod(0644, "$local_dir$target_file");
|
|
if ($rv) {
|
|
print "644";
|
|
}
|
|
else {
|
|
print "<br />";
|
|
}
|
|
print " </td>";
|
|
}
|
|
print " <td>";
|
|
print " " . size_convert((stat($local_dir . $target_file))[7]);
|
|
print " </td>";
|
|
print " </tr>";
|
|
}
|
|
else {
|
|
if (!$ftp->message || $ftp->message =~ /connection closed/i || $ftp->message =~ /timeout/i) {
|
|
transfer_file($remote_dir, $local_dir, $target_file, $target_size);
|
|
}
|
|
else {
|
|
$etrans++;
|
|
$esize += $target_size;
|
|
print " <font color=\"red\">" . $ftp->message . "</font>";
|
|
print " </td>";
|
|
print " <td>";
|
|
print " <br />";
|
|
print " </td>";
|
|
print " <td>";
|
|
print " <br />";
|
|
print " </td>";
|
|
print " </tr>";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
sub transfer_dir {
|
|
my $remote_dir = shift;
|
|
my $local_dir = shift;
|
|
my $target_dir = shift;
|
|
|
|
return if $target_dir eq '.' || $target_dir eq '..';
|
|
|
|
if ($local_dir !~ /\/$/) {
|
|
$local_dir = "$local_dir/";
|
|
}
|
|
if ($remote_dir !~ /\/$/) {
|
|
$remote_dir = "$remote_dir/";
|
|
}
|
|
|
|
my $rv = $ftp->cwd($remote_dir . $target_dir);
|
|
if (! $rv) {
|
|
if ($ftp->message =~ /connection closed/i || $ftp->message =~ /timeout/i) {
|
|
$ftp = Net::FTP->new($server, Debug => 0, Hash => \*STDOUT, Passive => 1, Timeout => 60);
|
|
$ftp->login($username, $password);
|
|
$ftp->cwd($remote_dir . $target_dir);
|
|
}
|
|
else {
|
|
print "<font color=\"red\">Error accessing</font> <font color=\"blue\">$remote_dir$target_dir</font> <font color=\"red\">" . $ftp->message . "</font><br />";
|
|
return;
|
|
}
|
|
}
|
|
|
|
print " <tr>";
|
|
print " <td colspan=\"6\">";
|
|
print " <br />";
|
|
print " </td>";
|
|
print " </tr>";
|
|
print " <tr>";
|
|
print " <td>";
|
|
print " <font color=\"blue\">$target_dir</font>";
|
|
print " </td>";
|
|
print " <td>";
|
|
print " directory";
|
|
print " </td>";
|
|
print " <td>";
|
|
print " <br />";
|
|
print " </td>";
|
|
|
|
if (-e $local_dir . $target_dir) {
|
|
print " <td>";
|
|
print " Exists, skipping";
|
|
print " </td>";
|
|
print " <td>";
|
|
print " <br />";
|
|
print " </td>";
|
|
}
|
|
else {
|
|
my $rv = mkdir($local_dir . $target_dir, 0755);
|
|
if ($rv) {
|
|
print " <td>";
|
|
print " Success";
|
|
print " </td>";
|
|
print " <td>";
|
|
print " 755";
|
|
print " </td>";
|
|
}
|
|
else {
|
|
print " <td>";
|
|
print " Failed: $!";
|
|
print " </td>";
|
|
print " <td>";
|
|
print " <br />";
|
|
print " </td>";
|
|
}
|
|
}
|
|
print " <td>";
|
|
print " <br />";
|
|
print " </td>";
|
|
print " </tr>";
|
|
|
|
my @dir = sort_list('transfer', $ftp->dir);
|
|
while (@dir) {
|
|
$value = shift(@dir);
|
|
if ($value->{'type'} eq '-') {
|
|
transfer_file($remote_dir . $target_dir, $local_dir . $target_dir, $value->{'name'}, $value->{'size'});
|
|
}
|
|
elsif ($value->{'type'} eq 'd' && $value->{'name'} !~ /^\.\.?/) {
|
|
transfer_dir($remote_dir . $target_dir, $local_dir . $target_dir, $value->{'name'});
|
|
}
|
|
elsif ($value->{'type'} eq 'l' && $value->{'name'} !~ /^\.\.?/) {
|
|
transfer_dir($remote_dir . $target_dir, $local_dir . $target_dir, $value->{'name'});
|
|
}
|
|
}
|
|
}
|
|
|
|
sub size_convert {
|
|
my $size = shift;
|
|
|
|
if ($size > 1073741824) {
|
|
$size = sprintf("%.2f", $size / 1073741824) . " GB";
|
|
}
|
|
elsif ($size > 1048576) {
|
|
$size = sprintf("%.2f", $size / 1048576) . " MB";
|
|
}
|
|
elsif ($size > 1024) {
|
|
$size = sprintf("%.2f", $size / 1024) . " KB";
|
|
}
|
|
else {
|
|
if ($size) {
|
|
$size .= " bytes";
|
|
}
|
|
else {
|
|
$size = "0 bytes";
|
|
}
|
|
}
|
|
|
|
return $size
|
|
}
|
|
|
|
sub sort_list {
|
|
my $format = shift;
|
|
my @list = @_;
|
|
my (@dir, @sym, @file, @unh, @sort);
|
|
my ($current, $parent) = 0;
|
|
foreach my $value (@list) {
|
|
if ($value =~ /^(\S{1})(\S+)\s+(\d+)\s+(\S+)\s+(\S+)\s+(\d+)\s+(\w+\s+\w+\s+\S+)\s+(.+?)\s*(?:->\s*(.+))?$/) { # Linux servers
|
|
if ($1 eq '-') {
|
|
push (@file, {'type' => $1, 'mode' => $2, 'nlink' => $3, 'uid' => $4, 'gid' => $5, 'size' => $6, 'date' => $7, 'name' => $8, 'symlink' => $9});
|
|
}
|
|
elsif ($1 eq 'd') {
|
|
push (@dir, {'type' => $1, 'mode' => $2, 'nlink' => $3, 'uid' => $4, 'gid' => $5, 'size' => $6, 'date' => $7, 'name' => $8, 'symlink' => $9});
|
|
if ($8 eq '.') {
|
|
$current = 1;
|
|
}
|
|
if ($8 eq '..') {
|
|
$parent = 1;
|
|
}
|
|
}
|
|
elsif ($1 eq 'l') {
|
|
push (@sym, {'type' => $1, 'mode' => $2, 'nlink' => $3, 'uid' => $4, 'gid' => $5, 'size' => $6, 'date' => $7, 'name' => $8, 'symlink' => $9});
|
|
}
|
|
}
|
|
elsif ($value =~ /^(\d{2}-\d{2}-\d{2})\s+(\S+)\s+(\<DIR\>)?\s+?(\d+)?\s+?(\S+)$/) { # Microsoft Servers
|
|
if ($3 eq '<DIR>') {
|
|
push (@dir, {'type' => 'd', 'mode' => '', 'nlink' => '', 'uid' => '', 'gid' => '', 'size' => $4, 'date' => "$1 $2", 'name' => $5, 'symlink' => ''});
|
|
}
|
|
else {
|
|
push (@file, {'type' => '-', 'mode' => '', 'nlink' => '', 'uid' => '', 'gid' => '', 'size' => $4, 'date' => "$1 $2", 'name' => $5, 'symlink' => ''});
|
|
}
|
|
}
|
|
else {
|
|
push (@unh, $value);
|
|
}
|
|
}
|
|
if ((@file || @dir || @sym) && !$current) {
|
|
push(@dir, {'type' => 'd', 'mode' => '?', 'nlink' => '?', 'uid' => '?', 'gid' => '?', 'size' => '?', 'date' => '?', 'name' => '.', 'symlink' => ''});
|
|
}
|
|
if ((@file || @dir || @sym) && !$parent) {
|
|
push(@dir, {'type' => 'd', 'mode' => '?', 'nlink' => '?', 'uid' => '?', 'gid' => '?', 'size' => '?', 'date' => '?', 'name' => '..', 'symlink' => ''});
|
|
}
|
|
@file = sort {$a->{'name'} cmp $b->{'name'}} @file;
|
|
@dir = sort {$a->{'name'} cmp $b->{'name'}} @dir;
|
|
@sym = sort {$a->{'name'} cmp $b->{'name'}} @sym;
|
|
|
|
if ($format eq 'display') {
|
|
@sort = (@dir, @sym, @file);
|
|
}
|
|
elsif ($format eq 'transfer') {
|
|
@sort = (@file, @dir, @sym);
|
|
}
|
|
|
|
return @sort, @unh;
|
|
}
|
|
|
|
sub time_convert {
|
|
my $time = shift;
|
|
my ($hour, $minute, $second);
|
|
my @ret;
|
|
|
|
if ($time >= 3600) {
|
|
if (int($time / 3600) == 1) {
|
|
$hour = int($time / 3600) . ' hour';
|
|
}
|
|
elsif (int($time / 3600)) {
|
|
$hour = int($time / 3600) . ' hours';
|
|
}
|
|
|
|
if ($hour) {
|
|
push (@ret, $hour);
|
|
}
|
|
|
|
$time -= $hour * 3600;
|
|
}
|
|
|
|
if ($time >= 60) {
|
|
if (int($time / 60) == 1) {
|
|
$minute = int($time / 60) . ' minute';
|
|
}
|
|
elsif (int($time / 60)) {
|
|
$minute = int($time / 60) . ' minutes';
|
|
}
|
|
|
|
if ($minute) {
|
|
push (@ret, $minute);
|
|
}
|
|
|
|
$time -= $minute * 60;
|
|
}
|
|
|
|
if ($time < 60) {
|
|
$time = int($time);
|
|
if ($time == 1) {
|
|
$second = $time . ' second';
|
|
}
|
|
elsif ($time) {
|
|
$second = $time . ' seconds';
|
|
}
|
|
else {
|
|
$second = '0 seconds';
|
|
}
|
|
if ($second) {
|
|
push (@ret, $second);
|
|
}
|
|
}
|
|
|
|
return join(', ', @ret);
|
|
}
|
|
|
|
sub url_escape {
|
|
my $url = shift;
|
|
|
|
$url =~ s/([^A-Za-z0-9])/sprintf("%%%02X", ord($1))/eg;
|
|
|
|
return $url;
|
|
}
|
|
|
|
sub url_unescape {
|
|
my $url = shift;
|
|
|
|
$url =~ s/\%([a-fA-F0-9]{2})/pack("C", hex($1))/eg;
|
|
|
|
return $url;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Net::FTP.pm
|
|
#
|
|
# Copyright (c) 1995-2004 Graham Barr <gbarr@pobox.com>. All rights reserved.
|
|
# This program is free software; you can redistribute it and/or
|
|
# modify it under the same terms as Perl itself.
|
|
#
|
|
# Documentation (at end) improved 1996 by Nathan Torkington <gnat@frii.com>.
|
|
|
|
package Net::FTP;
|
|
|
|
require 5.001;
|
|
|
|
use strict;
|
|
use vars qw(@ISA $VERSION);
|
|
use Carp;
|
|
|
|
use Socket 1.3;
|
|
use IO::Socket;
|
|
use Time::Local;
|
|
use Net::Cmd;
|
|
use Net::Config;
|
|
use Fcntl qw(O_WRONLY O_RDONLY O_APPEND O_CREAT O_TRUNC);
|
|
# use AutoLoader qw(AUTOLOAD);
|
|
|
|
$VERSION = "2.75";
|
|
@ISA = qw(Exporter Net::Cmd IO::Socket::INET);
|
|
|
|
# Someday I will "use constant", when I am not bothered to much about
|
|
# compatability with older releases of perl
|
|
|
|
use vars qw($TELNET_IAC $TELNET_IP $TELNET_DM);
|
|
($TELNET_IAC,$TELNET_IP,$TELNET_DM) = (255,244,242);
|
|
|
|
# Name is too long for AutoLoad, it clashes with pasv_xfer
|
|
sub pasv_xfer_unique {
|
|
my($sftp,$sfile,$dftp,$dfile) = @_;
|
|
$sftp->pasv_xfer($sfile,$dftp,$dfile,1);
|
|
}
|
|
|
|
BEGIN {
|
|
# make a constant so code is fast'ish
|
|
my $is_os390 = $^O eq 'os390';
|
|
*trEBCDIC = sub () { $is_os390 }
|
|
}
|
|
|
|
1;
|
|
# Having problems with AutoLoader
|
|
#__END__
|
|
|
|
sub new
|
|
{
|
|
my $pkg = shift;
|
|
my ($peer,%arg);
|
|
if (@_ % 2) {
|
|
$peer = shift ;
|
|
%arg = @_;
|
|
} else {
|
|
%arg = @_;
|
|
$peer=delete $arg{Host};
|
|
}
|
|
|
|
my $host = $peer;
|
|
my $fire = undef;
|
|
my $fire_type = undef;
|
|
|
|
if(exists($arg{Firewall}) || Net::Config->requires_firewall($peer))
|
|
{
|
|
$fire = $arg{Firewall}
|
|
|| $ENV{FTP_FIREWALL}
|
|
|| $NetConfig{ftp_firewall}
|
|
|| undef;
|
|
|
|
if(defined $fire)
|
|
{
|
|
$peer = $fire;
|
|
delete $arg{Port};
|
|
$fire_type = $arg{FirewallType}
|
|
|| $ENV{FTP_FIREWALL_TYPE}
|
|
|| $NetConfig{firewall_type}
|
|
|| undef;
|
|
}
|
|
}
|
|
|
|
my $ftp = $pkg->SUPER::new(PeerAddr => $peer,
|
|
PeerPort => $arg{Port} || 'ftp(21)',
|
|
LocalAddr => $arg{'LocalAddr'},
|
|
Proto => 'tcp',
|
|
Timeout => defined $arg{Timeout}
|
|
? $arg{Timeout}
|
|
: 120
|
|
) or return undef;
|
|
|
|
${*$ftp}{'net_ftp_host'} = $host; # Remote hostname
|
|
${*$ftp}{'net_ftp_type'} = 'A'; # ASCII/binary/etc mode
|
|
${*$ftp}{'net_ftp_blksize'} = abs($arg{'BlockSize'} || 10240);
|
|
|
|
${*$ftp}{'net_ftp_localaddr'} = $arg{'LocalAddr'};
|
|
|
|
${*$ftp}{'net_ftp_firewall'} = $fire
|
|
if(defined $fire);
|
|
${*$ftp}{'net_ftp_firewall_type'} = $fire_type
|
|
if(defined $fire_type);
|
|
|
|
${*$ftp}{'net_ftp_passive'} = int
|
|
exists $arg{Passive}
|
|
? $arg{Passive}
|
|
: exists $ENV{FTP_PASSIVE}
|
|
? $ENV{FTP_PASSIVE}
|
|
: defined $fire
|
|
? $NetConfig{ftp_ext_passive}
|
|
: $NetConfig{ftp_int_passive}; # Whew! :-)
|
|
|
|
$ftp->hash(exists $arg{Hash} ? $arg{Hash} : 0, 10240);
|
|
|
|
$ftp->autoflush(1);
|
|
|
|
$ftp->debug(exists $arg{Debug} ? $arg{Debug} : undef);
|
|
|
|
unless ($ftp->response() == CMD_OK)
|
|
{
|
|
$ftp->close();
|
|
$@ = $ftp->message;
|
|
undef $ftp;
|
|
}
|
|
|
|
$ftp;
|
|
}
|
|
|
|
##
|
|
## User interface methods
|
|
##
|
|
|
|
|
|
sub host {
|
|
my $me = shift;
|
|
${*$me}{'net_ftp_host'};
|
|
}
|
|
|
|
|
|
sub hash {
|
|
my $ftp = shift; # self
|
|
|
|
my($h,$b) = @_;
|
|
unless($h) {
|
|
delete ${*$ftp}{'net_ftp_hash'};
|
|
return [\*STDERR,0];
|
|
}
|
|
($h,$b) = (ref($h)? $h : \*STDOUT, $b || 10240);
|
|
select((select($h), $|=1)[0]);
|
|
$b = 512 if $b < 512;
|
|
${*$ftp}{'net_ftp_hash'} = [$h, $b];
|
|
}
|
|
|
|
sub quit
|
|
{
|
|
my $ftp = shift;
|
|
|
|
$ftp->_QUIT;
|
|
$ftp->close;
|
|
}
|
|
|
|
sub DESTROY {}
|
|
|
|
sub ascii { shift->type('A',@_); }
|
|
sub binary { shift->type('I',@_); }
|
|
|
|
sub ebcdic
|
|
{
|
|
carp "TYPE E is unsupported, shall default to I";
|
|
shift->type('E',@_);
|
|
}
|
|
|
|
sub byte
|
|
{
|
|
carp "TYPE L is unsupported, shall default to I";
|
|
shift->type('L',@_);
|
|
}
|
|
|
|
# Allow the user to send a command directly, BE CAREFUL !!
|
|
|
|
sub quot
|
|
{
|
|
my $ftp = shift;
|
|
my $cmd = shift;
|
|
|
|
$ftp->command( uc $cmd, @_);
|
|
$ftp->response();
|
|
}
|
|
|
|
sub site
|
|
{
|
|
my $ftp = shift;
|
|
|
|
$ftp->command("SITE", @_);
|
|
$ftp->response();
|
|
}
|
|
|
|
sub mdtm
|
|
{
|
|
my $ftp = shift;
|
|
my $file = shift;
|
|
|
|
# Server Y2K bug workaround
|
|
#
|
|
# sigh; some idiotic FTP servers use ("19%d",tm.tm_year) instead of
|
|
# ("%d",tm.tm_year+1900). This results in an extra digit in the
|
|
# string returned. To account for this we allow an optional extra
|
|
# digit in the year. Then if the first two digits are 19 we use the
|
|
# remainder, otherwise we subtract 1900 from the whole year.
|
|
|
|
$ftp->_MDTM($file) && $ftp->message =~ /((\d\d)(\d\d\d?))(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/
|
|
? timegm($8,$7,$6,$5,$4-1,$2 eq '19' ? $3 : ($1-1900))
|
|
: undef;
|
|
}
|
|
|
|
sub size {
|
|
my $ftp = shift;
|
|
my $file = shift;
|
|
my $io;
|
|
if($ftp->supported("SIZE")) {
|
|
return $ftp->_SIZE($file)
|
|
? ($ftp->message =~ /(\d+)\s*(bytes?\s*)?$/)[0]
|
|
: undef;
|
|
}
|
|
elsif($ftp->supported("STAT")) {
|
|
my @msg;
|
|
return undef
|
|
unless $ftp->_STAT($file) && (@msg = $ftp->message) == 3;
|
|
my $line;
|
|
foreach $line (@msg) {
|
|
return (split(/\s+/,$line))[4]
|
|
if $line =~ /^[-rwxSsTt]{10}/
|
|
}
|
|
}
|
|
else {
|
|
my @files = $ftp->dir($file);
|
|
if(@files) {
|
|
return (split(/\s+/,$1))[4]
|
|
if $files[0] =~ /^([-rwxSsTt]{10}.*)$/;
|
|
}
|
|
}
|
|
undef;
|
|
}
|
|
|
|
sub login {
|
|
my($ftp,$user,$pass,$acct) = @_;
|
|
my($ok,$ruser,$fwtype);
|
|
|
|
unless (defined $user) {
|
|
require Net::Netrc;
|
|
|
|
my $rc = Net::Netrc->lookup(${*$ftp}{'net_ftp_host'});
|
|
|
|
($user,$pass,$acct) = $rc->lpa()
|
|
if ($rc);
|
|
}
|
|
|
|
$user ||= "anonymous";
|
|
$ruser = $user;
|
|
|
|
$fwtype = ${*$ftp}{'net_ftp_firewall_type'}
|
|
|| $NetConfig{'ftp_firewall_type'}
|
|
|| 0;
|
|
|
|
if ($fwtype && defined ${*$ftp}{'net_ftp_firewall'}) {
|
|
if ($fwtype == 1 || $fwtype == 7) {
|
|
$user .= '@' . ${*$ftp}{'net_ftp_host'};
|
|
}
|
|
else {
|
|
require Net::Netrc;
|
|
|
|
my $rc = Net::Netrc->lookup(${*$ftp}{'net_ftp_firewall'});
|
|
|
|
my($fwuser,$fwpass,$fwacct) = $rc ? $rc->lpa() : ();
|
|
|
|
if ($fwtype == 5) {
|
|
$user = join('@',$user,$fwuser,${*$ftp}{'net_ftp_host'});
|
|
$pass = $pass . '@' . $fwpass;
|
|
}
|
|
else {
|
|
if ($fwtype == 2) {
|
|
$user .= '@' . ${*$ftp}{'net_ftp_host'};
|
|
}
|
|
elsif ($fwtype == 6) {
|
|
$fwuser .= '@' . ${*$ftp}{'net_ftp_host'};
|
|
}
|
|
|
|
$ok = $ftp->_USER($fwuser);
|
|
|
|
return 0 unless $ok == CMD_OK || $ok == CMD_MORE;
|
|
|
|
$ok = $ftp->_PASS($fwpass || "");
|
|
|
|
return 0 unless $ok == CMD_OK || $ok == CMD_MORE;
|
|
|
|
$ok = $ftp->_ACCT($fwacct)
|
|
if defined($fwacct);
|
|
|
|
if ($fwtype == 3) {
|
|
$ok = $ftp->command("SITE",${*$ftp}{'net_ftp_host'})->response;
|
|
}
|
|
elsif ($fwtype == 4) {
|
|
$ok = $ftp->command("OPEN",${*$ftp}{'net_ftp_host'})->response;
|
|
}
|
|
|
|
return 0 unless $ok == CMD_OK || $ok == CMD_MORE;
|
|
}
|
|
}
|
|
}
|
|
|
|
$ok = $ftp->_USER($user);
|
|
|
|
# Some dumb firewalls don't prefix the connection messages
|
|
$ok = $ftp->response()
|
|
if ($ok == CMD_OK && $ftp->code == 220 && $user =~ /\@/);
|
|
|
|
if ($ok == CMD_MORE) {
|
|
unless(defined $pass) {
|
|
require Net::Netrc;
|
|
|
|
my $rc = Net::Netrc->lookup(${*$ftp}{'net_ftp_host'}, $ruser);
|
|
|
|
($ruser,$pass,$acct) = $rc->lpa()
|
|
if ($rc);
|
|
|
|
$pass = '-anonymous@'
|
|
if (!defined $pass && (!defined($ruser) || $ruser =~ /^anonymous/o));
|
|
}
|
|
|
|
$ok = $ftp->_PASS($pass || "");
|
|
}
|
|
|
|
$ok = $ftp->_ACCT($acct)
|
|
if (defined($acct) && ($ok == CMD_MORE || $ok == CMD_OK));
|
|
|
|
if ($fwtype == 7 && $ok == CMD_OK && defined ${*$ftp}{'net_ftp_firewall'}) {
|
|
my($f,$auth,$resp) = _auth_id($ftp);
|
|
$ftp->authorize($auth,$resp) if defined($resp);
|
|
}
|
|
|
|
$ok == CMD_OK;
|
|
}
|
|
|
|
sub account
|
|
{
|
|
@_ == 2 or croak 'usage: $ftp->account( ACCT )';
|
|
my $ftp = shift;
|
|
my $acct = shift;
|
|
$ftp->_ACCT($acct) == CMD_OK;
|
|
}
|
|
|
|
sub _auth_id {
|
|
my($ftp,$auth,$resp) = @_;
|
|
|
|
unless(defined $resp)
|
|
{
|
|
require Net::Netrc;
|
|
|
|
$auth ||= eval { (getpwuid($>))[0] } || $ENV{NAME};
|
|
|
|
my $rc = Net::Netrc->lookup(${*$ftp}{'net_ftp_firewall'}, $auth)
|
|
|| Net::Netrc->lookup(${*$ftp}{'net_ftp_firewall'});
|
|
|
|
($auth,$resp) = $rc->lpa()
|
|
if ($rc);
|
|
}
|
|
($ftp,$auth,$resp);
|
|
}
|
|
|
|
sub authorize
|
|
{
|
|
@_ >= 1 || @_ <= 3 or croak 'usage: $ftp->authorize( [AUTH [, RESP]])';
|
|
|
|
my($ftp,$auth,$resp) = &_auth_id;
|
|
|
|
my $ok = $ftp->_AUTH($auth || "");
|
|
|
|
$ok = $ftp->_RESP($resp || "")
|
|
if ($ok == CMD_MORE);
|
|
|
|
$ok == CMD_OK;
|
|
}
|
|
|
|
sub rename
|
|
{
|
|
@_ == 3 or croak 'usage: $ftp->rename(FROM, TO)';
|
|
|
|
my($ftp,$from,$to) = @_;
|
|
|
|
$ftp->_RNFR($from)
|
|
&& $ftp->_RNTO($to);
|
|
}
|
|
|
|
sub type
|
|
{
|
|
my $ftp = shift;
|
|
my $type = shift;
|
|
my $oldval = ${*$ftp}{'net_ftp_type'};
|
|
|
|
return $oldval
|
|
unless (defined $type);
|
|
|
|
return undef
|
|
unless ($ftp->_TYPE($type,@_));
|
|
|
|
${*$ftp}{'net_ftp_type'} = join(" ",$type,@_);
|
|
|
|
$oldval;
|
|
}
|
|
|
|
sub alloc
|
|
{
|
|
my $ftp = shift;
|
|
my $size = shift;
|
|
my $oldval = ${*$ftp}{'net_ftp_allo'};
|
|
|
|
return $oldval
|
|
unless (defined $size);
|
|
|
|
return undef
|
|
unless ($ftp->_ALLO($size,@_));
|
|
|
|
${*$ftp}{'net_ftp_allo'} = join(" ",$size,@_);
|
|
|
|
$oldval;
|
|
}
|
|
|
|
sub abort
|
|
{
|
|
my $ftp = shift;
|
|
|
|
send($ftp,pack("CCC", $TELNET_IAC, $TELNET_IP, $TELNET_IAC),MSG_OOB);
|
|
|
|
$ftp->command(pack("C",$TELNET_DM) . "ABOR");
|
|
|
|
${*$ftp}{'net_ftp_dataconn'}->close()
|
|
if defined ${*$ftp}{'net_ftp_dataconn'};
|
|
|
|
$ftp->response();
|
|
|
|
$ftp->status == CMD_OK;
|
|
}
|
|
|
|
sub get
|
|
{
|
|
my($ftp,$remote,$local,$where) = @_;
|
|
|
|
my($loc,$len,$buf,$resp,$data);
|
|
local *FD;
|
|
|
|
my $localfd = ref($local) || ref(\$local) eq "GLOB";
|
|
|
|
($local = $remote) =~ s#^.*/##
|
|
unless(defined $local);
|
|
|
|
croak("Bad remote filename '$remote'\n")
|
|
if $remote =~ /[\r\n]/s;
|
|
|
|
${*$ftp}{'net_ftp_rest'} = $where if defined $where;
|
|
my $rest = ${*$ftp}{'net_ftp_rest'};
|
|
|
|
delete ${*$ftp}{'net_ftp_port'};
|
|
delete ${*$ftp}{'net_ftp_pasv'};
|
|
|
|
$data = $ftp->retr($remote) or
|
|
return undef;
|
|
|
|
if($localfd)
|
|
{
|
|
$loc = $local;
|
|
}
|
|
else
|
|
{
|
|
$loc = \*FD;
|
|
|
|
unless(sysopen($loc, $local, O_CREAT | O_WRONLY | ($rest ? O_APPEND : O_TRUNC)))
|
|
{
|
|
carp "Cannot open Local file $local: $!\n";
|
|
$data->abort;
|
|
return undef;
|
|
}
|
|
}
|
|
|
|
if($ftp->type eq 'I' && !binmode($loc))
|
|
{
|
|
carp "Cannot binmode Local file $local: $!\n";
|
|
$data->abort;
|
|
close($loc) unless $localfd;
|
|
return undef;
|
|
}
|
|
|
|
$buf = '';
|
|
my($count,$hashh,$hashb,$ref) = (0);
|
|
|
|
($hashh,$hashb) = @$ref
|
|
if($ref = ${*$ftp}{'net_ftp_hash'});
|
|
|
|
my $blksize = ${*$ftp}{'net_ftp_blksize'};
|
|
local $\; # Just in case
|
|
|
|
while(1)
|
|
{
|
|
last unless $len = $data->read($buf,$blksize);
|
|
|
|
if (trEBCDIC && $ftp->type ne 'I')
|
|
{
|
|
$buf = $ftp->toebcdic($buf);
|
|
$len = length($buf);
|
|
}
|
|
|
|
if($hashh) {
|
|
$count += $len;
|
|
print $hashh "# " x (int($count / $hashb));
|
|
$count %= $hashb;
|
|
}
|
|
unless(print $loc $buf)
|
|
{
|
|
carp "Cannot write to Local file $local: $!\n";
|
|
$data->abort;
|
|
close($loc)
|
|
unless $localfd;
|
|
return undef;
|
|
}
|
|
}
|
|
|
|
print $hashh "\n" if $hashh;
|
|
|
|
unless ($localfd)
|
|
{
|
|
unless (close($loc))
|
|
{
|
|
carp "Cannot close file $local (perhaps disk space) $!\n";
|
|
return undef;
|
|
}
|
|
}
|
|
|
|
unless ($data->close()) # implied $ftp->response
|
|
{
|
|
carp "Unable to close datastream";
|
|
return undef;
|
|
}
|
|
|
|
return $local;
|
|
}
|
|
|
|
sub cwd
|
|
{
|
|
@_ == 1 || @_ == 2 or croak 'usage: $ftp->cwd( [ DIR ] )';
|
|
|
|
my($ftp,$dir) = @_;
|
|
|
|
$dir = "/" unless defined($dir) && $dir =~ /\S/;
|
|
|
|
$dir eq ".."
|
|
? $ftp->_CDUP()
|
|
: $ftp->_CWD($dir);
|
|
}
|
|
|
|
sub cdup
|
|
{
|
|
@_ == 1 or croak 'usage: $ftp->cdup()';
|
|
$_[0]->_CDUP;
|
|
}
|
|
|
|
sub pwd
|
|
{
|
|
@_ == 1 || croak 'usage: $ftp->pwd()';
|
|
my $ftp = shift;
|
|
|
|
$ftp->_PWD();
|
|
$ftp->_extract_path;
|
|
}
|
|
|
|
# rmdir( $ftp, $dir, [ $recurse ] )
|
|
#
|
|
# Removes $dir on remote host via FTP.
|
|
# $ftp is handle for remote host
|
|
#
|
|
# If $recurse is TRUE, the directory and deleted recursively.
|
|
# This means all of its contents and subdirectories.
|
|
#
|
|
# Initial version contributed by Dinkum Software
|
|
#
|
|
sub rmdir
|
|
{
|
|
@_ == 2 || @_ == 3 or croak('usage: $ftp->rmdir( DIR [, RECURSE ] )');
|
|
|
|
# Pick off the args
|
|
my ($ftp, $dir, $recurse) = @_ ;
|
|
my $ok;
|
|
|
|
return $ok
|
|
if $ok = $ftp->_RMD( $dir ) or !$recurse;
|
|
|
|
# Try to delete the contents
|
|
# Get a list of all the files in the directory
|
|
my @filelist = grep { !/^\.{1,2}$/ } $ftp->ls($dir);
|
|
|
|
return undef
|
|
unless @filelist; # failed, it is probably not a directory
|
|
|
|
# Go thru and delete each file or the directory
|
|
my $file;
|
|
foreach $file (map { m,/, ? $_ : "$dir/$_" } @filelist)
|
|
{
|
|
next # successfully deleted the file
|
|
if $ftp->delete($file);
|
|
|
|
# Failed to delete it, assume its a directory
|
|
# Recurse and ignore errors, the final rmdir() will
|
|
# fail on any errors here
|
|
return $ok
|
|
unless $ok = $ftp->rmdir($file, 1) ;
|
|
}
|
|
|
|
# Directory should be empty
|
|
# Try to remove the directory again
|
|
# Pass results directly to caller
|
|
# If any of the prior deletes failed, this
|
|
# rmdir() will fail because directory is not empty
|
|
return $ftp->_RMD($dir) ;
|
|
}
|
|
|
|
sub restart
|
|
{
|
|
@_ == 2 || croak 'usage: $ftp->restart( BYTE_OFFSET )';
|
|
|
|
my($ftp,$where) = @_;
|
|
|
|
${*$ftp}{'net_ftp_rest'} = $where;
|
|
|
|
return undef;
|
|
}
|
|
|
|
|
|
sub mkdir
|
|
{
|
|
@_ == 2 || @_ == 3 or croak 'usage: $ftp->mkdir( DIR [, RECURSE ] )';
|
|
|
|
my($ftp,$dir,$recurse) = @_;
|
|
|
|
$ftp->_MKD($dir) || $recurse or
|
|
return undef;
|
|
|
|
my $path = $dir;
|
|
|
|
unless($ftp->ok)
|
|
{
|
|
my @path = split(m#(?=/+)#, $dir);
|
|
|
|
$path = "";
|
|
|
|
while(@path)
|
|
{
|
|
$path .= shift @path;
|
|
|
|
$ftp->_MKD($path);
|
|
|
|
$path = $ftp->_extract_path($path);
|
|
}
|
|
|
|
# If the creation of the last element was not successful, see if we
|
|
# can cd to it, if so then return path
|
|
|
|
unless($ftp->ok)
|
|
{
|
|
my($status,$message) = ($ftp->status,$ftp->message);
|
|
my $pwd = $ftp->pwd;
|
|
|
|
if($pwd && $ftp->cwd($dir))
|
|
{
|
|
$path = $dir;
|
|
$ftp->cwd($pwd);
|
|
}
|
|
else
|
|
{
|
|
undef $path;
|
|
}
|
|
$ftp->set_status($status,$message);
|
|
}
|
|
}
|
|
|
|
$path;
|
|
}
|
|
|
|
sub delete
|
|
{
|
|
@_ == 2 || croak 'usage: $ftp->delete( FILENAME )';
|
|
|
|
$_[0]->_DELE($_[1]);
|
|
}
|
|
|
|
sub put { shift->_store_cmd("stor",@_) }
|
|
sub put_unique { shift->_store_cmd("stou",@_) }
|
|
sub append { shift->_store_cmd("appe",@_) }
|
|
|
|
sub nlst { shift->_data_cmd("NLST",@_) }
|
|
sub list { shift->_data_cmd("LIST",@_) }
|
|
sub retr { shift->_data_cmd("RETR",@_) }
|
|
sub stor { shift->_data_cmd("STOR",@_) }
|
|
sub stou { shift->_data_cmd("STOU",@_) }
|
|
sub appe { shift->_data_cmd("APPE",@_) }
|
|
|
|
sub _store_cmd
|
|
{
|
|
my($ftp,$cmd,$local,$remote) = @_;
|
|
my($loc,$sock,$len,$buf);
|
|
local *FD;
|
|
|
|
my $localfd = ref($local) || ref(\$local) eq "GLOB";
|
|
|
|
unless(defined $remote)
|
|
{
|
|
croak 'Must specify remote filename with stream input'
|
|
if $localfd;
|
|
|
|
require File::Basename;
|
|
$remote = File::Basename::basename($local);
|
|
}
|
|
if( defined ${*$ftp}{'net_ftp_allo'} )
|
|
{
|
|
delete ${*$ftp}{'net_ftp_allo'};
|
|
} else
|
|
{
|
|
# if the user hasn't already invoked the alloc method since the last
|
|
# _store_cmd call, figure out if the local file is a regular file(not
|
|
# a pipe, or device) and if so get the file size from stat, and send
|
|
# an ALLO command before sending the STOR, STOU, or APPE command.
|
|
my $size = do { local $^W; -f $local && -s _ }; # no ALLO if sending data from a pipe
|
|
$ftp->_ALLO($size) if $size;
|
|
}
|
|
croak("Bad remote filename '$remote'\n")
|
|
if $remote =~ /[\r\n]/s;
|
|
|
|
if($localfd)
|
|
{
|
|
$loc = $local;
|
|
}
|
|
else
|
|
{
|
|
$loc = \*FD;
|
|
|
|
unless(sysopen($loc, $local, O_RDONLY))
|
|
{
|
|
carp "Cannot open Local file $local: $!\n";
|
|
return undef;
|
|
}
|
|
}
|
|
|
|
if($ftp->type eq 'I' && !binmode($loc))
|
|
{
|
|
carp "Cannot binmode Local file $local: $!\n";
|
|
return undef;
|
|
}
|
|
|
|
delete ${*$ftp}{'net_ftp_port'};
|
|
delete ${*$ftp}{'net_ftp_pasv'};
|
|
|
|
$sock = $ftp->_data_cmd($cmd, $remote) or
|
|
return undef;
|
|
|
|
$remote = ($ftp->message =~ /FILE:\s*(.*)/)[0]
|
|
if 'STOU' eq uc $cmd;
|
|
|
|
my $blksize = ${*$ftp}{'net_ftp_blksize'};
|
|
|
|
my($count,$hashh,$hashb,$ref) = (0);
|
|
|
|
($hashh,$hashb) = @$ref
|
|
if($ref = ${*$ftp}{'net_ftp_hash'});
|
|
|
|
while(1)
|
|
{
|
|
last unless $len = read($loc,$buf="",$blksize);
|
|
|
|
if (trEBCDIC && $ftp->type ne 'I')
|
|
{
|
|
$buf = $ftp->toascii($buf);
|
|
$len = length($buf);
|
|
}
|
|
|
|
if($hashh) {
|
|
$count += $len;
|
|
print $hashh "# " x (int($count / $hashb));
|
|
$count %= $hashb;
|
|
}
|
|
|
|
my $wlen;
|
|
unless(defined($wlen = $sock->write($buf,$len)) && $wlen == $len)
|
|
{
|
|
$sock->abort;
|
|
close($loc)
|
|
unless $localfd;
|
|
print $hashh "\n" if $hashh;
|
|
return undef;
|
|
}
|
|
}
|
|
|
|
print $hashh "\n" if $hashh;
|
|
|
|
close($loc)
|
|
unless $localfd;
|
|
|
|
$sock->close() or
|
|
return undef;
|
|
|
|
if ('STOU' eq uc $cmd and $ftp->message =~ m/unique\s+file\s*name\s*:\s*(.*)\)|"(.*)"/)
|
|
{
|
|
require File::Basename;
|
|
$remote = File::Basename::basename($+)
|
|
}
|
|
|
|
return $remote;
|
|
}
|
|
|
|
sub port
|
|
{
|
|
@_ == 1 || @_ == 2 or croak 'usage: $ftp->port([PORT])';
|
|
|
|
my($ftp,$port) = @_;
|
|
my $ok;
|
|
|
|
delete ${*$ftp}{'net_ftp_intern_port'};
|
|
|
|
unless(defined $port)
|
|
{
|
|
# create a Listen socket at same address as the command socket
|
|
|
|
${*$ftp}{'net_ftp_listen'} ||= IO::Socket::INET->new(Listen => 5,
|
|
Proto => 'tcp',
|
|
Timeout => $ftp->timeout,
|
|
LocalAddr => $ftp->sockhost,
|
|
);
|
|
|
|
my $listen = ${*$ftp}{'net_ftp_listen'};
|
|
|
|
my($myport, @myaddr) = ($listen->sockport, split(/\./,$listen->sockhost));
|
|
|
|
$port = join(',', @myaddr, $myport >> 8, $myport & 0xff);
|
|
|
|
${*$ftp}{'net_ftp_intern_port'} = 1;
|
|
}
|
|
|
|
$ok = $ftp->_PORT($port);
|
|
|
|
${*$ftp}{'net_ftp_port'} = $port;
|
|
|
|
$ok;
|
|
}
|
|
|
|
sub ls { shift->_list_cmd("NLST",@_); }
|
|
sub dir { shift->_list_cmd("LIST",@_); }
|
|
|
|
sub pasv
|
|
{
|
|
@_ == 1 or croak 'usage: $ftp->pasv()';
|
|
|
|
my $ftp = shift;
|
|
|
|
delete ${*$ftp}{'net_ftp_intern_port'};
|
|
|
|
$ftp->_PASV && $ftp->message =~ /(\d+(,\d+)+)/
|
|
? ${*$ftp}{'net_ftp_pasv'} = $1
|
|
: undef;
|
|
}
|
|
|
|
sub unique_name
|
|
{
|
|
my $ftp = shift;
|
|
${*$ftp}{'net_ftp_unique'} || undef;
|
|
}
|
|
|
|
sub supported {
|
|
@_ == 2 or croak 'usage: $ftp->supported( CMD )';
|
|
my $ftp = shift;
|
|
my $cmd = uc shift;
|
|
my $hash = ${*$ftp}{'net_ftp_supported'} ||= {};
|
|
|
|
return $hash->{$cmd}
|
|
if exists $hash->{$cmd};
|
|
|
|
return $hash->{$cmd} = 0
|
|
unless $ftp->_HELP($cmd);
|
|
|
|
my $text = $ftp->message;
|
|
if($text =~ /following\s+commands/i) {
|
|
$text =~ s/^.*\n//;
|
|
while($text =~ /(\*?)(\w+)(\*?)/sg) {
|
|
$hash->{"\U$2"} = !length("$1$3");
|
|
}
|
|
}
|
|
else {
|
|
$hash->{$cmd} = $text !~ /unimplemented/i;
|
|
}
|
|
|
|
$hash->{$cmd} ||= 0;
|
|
}
|
|
|
|
##
|
|
## Deprecated methods
|
|
##
|
|
|
|
sub lsl
|
|
{
|
|
carp "Use of Net::FTP::lsl deprecated, use 'dir'"
|
|
if $^W;
|
|
goto &dir;
|
|
}
|
|
|
|
sub authorise
|
|
{
|
|
carp "Use of Net::FTP::authorise deprecated, use 'authorize'"
|
|
if $^W;
|
|
goto &authorize;
|
|
}
|
|
|
|
|
|
##
|
|
## Private methods
|
|
##
|
|
|
|
sub _extract_path
|
|
{
|
|
my($ftp, $path) = @_;
|
|
|
|
# This tries to work both with and without the quote doubling
|
|
# convention (RFC 959 requires it, but the first 3 servers I checked
|
|
# didn't implement it). It will fail on a server which uses a quote in
|
|
# the message which isn't a part of or surrounding the path.
|
|
$ftp->ok &&
|
|
$ftp->message =~ /(?:^|\s)\"(.*)\"(?:$|\s)/ &&
|
|
($path = $1) =~ s/\"\"/\"/g;
|
|
|
|
$path;
|
|
}
|
|
|
|
##
|
|
## Communication methods
|
|
##
|
|
|
|
sub _dataconn
|
|
{
|
|
my $ftp = shift;
|
|
my $data = undef;
|
|
my $pkg = "Net::FTP::" . $ftp->type;
|
|
|
|
eval "require " . $pkg;
|
|
|
|
$pkg =~ s/ /_/g;
|
|
|
|
delete ${*$ftp}{'net_ftp_dataconn'};
|
|
|
|
if(defined ${*$ftp}{'net_ftp_pasv'})
|
|
{
|
|
my @port = map { 0+$_ } split(/,/,${*$ftp}{'net_ftp_pasv'});
|
|
|
|
$data = $pkg->new(PeerAddr => join(".",@port[0..3]),
|
|
PeerPort => $port[4] * 256 + $port[5],
|
|
LocalAddr => ${*$ftp}{'net_ftp_localaddr'},
|
|
Proto => 'tcp'
|
|
);
|
|
}
|
|
elsif(defined ${*$ftp}{'net_ftp_listen'})
|
|
{
|
|
$data = ${*$ftp}{'net_ftp_listen'}->accept($pkg);
|
|
close(delete ${*$ftp}{'net_ftp_listen'});
|
|
}
|
|
|
|
if($data)
|
|
{
|
|
${*$data} = "";
|
|
$data->timeout($ftp->timeout);
|
|
${*$ftp}{'net_ftp_dataconn'} = $data;
|
|
${*$data}{'net_ftp_cmd'} = $ftp;
|
|
${*$data}{'net_ftp_blksize'} = ${*$ftp}{'net_ftp_blksize'};
|
|
}
|
|
|
|
$data;
|
|
}
|
|
|
|
sub _list_cmd
|
|
{
|
|
my $ftp = shift;
|
|
my $cmd = uc shift;
|
|
|
|
delete ${*$ftp}{'net_ftp_port'};
|
|
delete ${*$ftp}{'net_ftp_pasv'};
|
|
|
|
my $data = $ftp->_data_cmd($cmd,@_);
|
|
|
|
return
|
|
unless(defined $data);
|
|
|
|
require Net::FTP::A;
|
|
bless $data, "Net::FTP::A"; # Force ASCII mode
|
|
|
|
my $databuf = '';
|
|
my $buf = '';
|
|
my $blksize = ${*$ftp}{'net_ftp_blksize'};
|
|
|
|
while($data->read($databuf,$blksize)) {
|
|
$buf .= $databuf;
|
|
}
|
|
|
|
my $list = [ split(/\n/,$buf) ];
|
|
|
|
$data->close();
|
|
|
|
if (trEBCDIC)
|
|
{
|
|
for (@$list) { $_ = $ftp->toebcdic($_) }
|
|
}
|
|
|
|
wantarray ? @{$list}
|
|
: $list;
|
|
}
|
|
|
|
sub _data_cmd
|
|
{
|
|
my $ftp = shift;
|
|
my $cmd = uc shift;
|
|
my $ok = 1;
|
|
my $where = delete ${*$ftp}{'net_ftp_rest'} || 0;
|
|
my $arg;
|
|
|
|
for $arg (@_) {
|
|
croak("Bad argument '$arg'\n")
|
|
if $arg =~ /[\r\n]/s;
|
|
}
|
|
|
|
if(${*$ftp}{'net_ftp_passive'} &&
|
|
!defined ${*$ftp}{'net_ftp_pasv'} &&
|
|
!defined ${*$ftp}{'net_ftp_port'})
|
|
{
|
|
my $data = undef;
|
|
|
|
$ok = defined $ftp->pasv;
|
|
$ok = $ftp->_REST($where)
|
|
if $ok && $where;
|
|
|
|
if($ok)
|
|
{
|
|
$ftp->command($cmd,@_);
|
|
$data = $ftp->_dataconn();
|
|
$ok = CMD_INFO == $ftp->response();
|
|
if($ok)
|
|
{
|
|
$data->reading
|
|
if $data && $cmd =~ /RETR|LIST|NLST/;
|
|
return $data
|
|
}
|
|
$data->_close
|
|
if $data;
|
|
}
|
|
return undef;
|
|
}
|
|
|
|
$ok = $ftp->port
|
|
unless (defined ${*$ftp}{'net_ftp_port'} ||
|
|
defined ${*$ftp}{'net_ftp_pasv'});
|
|
|
|
$ok = $ftp->_REST($where)
|
|
if $ok && $where;
|
|
|
|
return undef
|
|
unless $ok;
|
|
|
|
$ftp->command($cmd,@_);
|
|
|
|
return 1
|
|
if(defined ${*$ftp}{'net_ftp_pasv'});
|
|
|
|
$ok = CMD_INFO == $ftp->response();
|
|
|
|
return $ok
|
|
unless exists ${*$ftp}{'net_ftp_intern_port'};
|
|
|
|
if($ok) {
|
|
my $data = $ftp->_dataconn();
|
|
|
|
$data->reading
|
|
if $data && $cmd =~ /RETR|LIST|NLST/;
|
|
|
|
return $data;
|
|
}
|
|
|
|
|
|
close(delete ${*$ftp}{'net_ftp_listen'});
|
|
|
|
return undef;
|
|
}
|
|
|
|
##
|
|
## Over-ride methods (Net::Cmd)
|
|
##
|
|
|
|
sub debug_text { $_[2] =~ /^(pass|resp|acct)/i ? "$1 ....\n" : $_[2]; }
|
|
|
|
sub command
|
|
{
|
|
my $ftp = shift;
|
|
|
|
delete ${*$ftp}{'net_ftp_port'};
|
|
$ftp->SUPER::command(@_);
|
|
}
|
|
|
|
sub response
|
|
{
|
|
my $ftp = shift;
|
|
my $code = $ftp->SUPER::response();
|
|
|
|
delete ${*$ftp}{'net_ftp_pasv'}
|
|
if ($code != CMD_MORE && $code != CMD_INFO);
|
|
|
|
$code;
|
|
}
|
|
|
|
sub parse_response
|
|
{
|
|
return ($1, $2 eq "-")
|
|
if $_[1] =~ s/^(\d\d\d)(.?)//o;
|
|
|
|
my $ftp = shift;
|
|
|
|
# Darn MS FTP server is a load of CRAP !!!!
|
|
return ()
|
|
unless ${*$ftp}{'net_cmd_code'} + 0;
|
|
|
|
(${*$ftp}{'net_cmd_code'},1);
|
|
}
|
|
|
|
##
|
|
## Allow 2 servers to talk directly
|
|
##
|
|
|
|
sub pasv_xfer {
|
|
my($sftp,$sfile,$dftp,$dfile,$unique) = @_;
|
|
|
|
($dfile = $sfile) =~ s#.*/##
|
|
unless(defined $dfile);
|
|
|
|
my $port = $sftp->pasv or
|
|
return undef;
|
|
|
|
$dftp->port($port) or
|
|
return undef;
|
|
|
|
return undef
|
|
unless($unique ? $dftp->stou($dfile) : $dftp->stor($dfile));
|
|
|
|
unless($sftp->retr($sfile) && $sftp->response == CMD_INFO) {
|
|
$sftp->retr($sfile);
|
|
$dftp->abort;
|
|
$dftp->response();
|
|
return undef;
|
|
}
|
|
|
|
$dftp->pasv_wait($sftp);
|
|
}
|
|
|
|
sub pasv_wait
|
|
{
|
|
@_ == 2 or croak 'usage: $ftp->pasv_wait(NON_PASV_FTP)';
|
|
|
|
my($ftp, $non_pasv) = @_;
|
|
my($file,$rin,$rout);
|
|
|
|
vec($rin='',fileno($ftp),1) = 1;
|
|
select($rout=$rin, undef, undef, undef);
|
|
|
|
$ftp->response();
|
|
$non_pasv->response();
|
|
|
|
return undef
|
|
unless $ftp->ok() && $non_pasv->ok();
|
|
|
|
return $1
|
|
if $ftp->message =~ /unique file name:\s*(\S*)\s*\)/;
|
|
|
|
return $1
|
|
if $non_pasv->message =~ /unique file name:\s*(\S*)\s*\)/;
|
|
|
|
return 1;
|
|
}
|
|
|
|
sub cmd { shift->command(@_)->response() }
|
|
|
|
########################################
|
|
#
|
|
# RFC959 commands
|
|
#
|
|
|
|
sub _ABOR { shift->command("ABOR")->response() == CMD_OK }
|
|
sub _ALLO { shift->command("ALLO",@_)->response() == CMD_OK}
|
|
sub _CDUP { shift->command("CDUP")->response() == CMD_OK }
|
|
sub _NOOP { shift->command("NOOP")->response() == CMD_OK }
|
|
sub _PASV { shift->command("PASV")->response() == CMD_OK }
|
|
sub _QUIT { shift->command("QUIT")->response() == CMD_OK }
|
|
sub _DELE { shift->command("DELE",@_)->response() == CMD_OK }
|
|
sub _CWD { shift->command("CWD", @_)->response() == CMD_OK }
|
|
sub _PORT { shift->command("PORT",@_)->response() == CMD_OK }
|
|
sub _RMD { shift->command("RMD", @_)->response() == CMD_OK }
|
|
sub _MKD { shift->command("MKD", @_)->response() == CMD_OK }
|
|
sub _PWD { shift->command("PWD", @_)->response() == CMD_OK }
|
|
sub _TYPE { shift->command("TYPE",@_)->response() == CMD_OK }
|
|
sub _RNTO { shift->command("RNTO",@_)->response() == CMD_OK }
|
|
sub _RESP { shift->command("RESP",@_)->response() == CMD_OK }
|
|
sub _MDTM { shift->command("MDTM",@_)->response() == CMD_OK }
|
|
sub _SIZE { shift->command("SIZE",@_)->response() == CMD_OK }
|
|
sub _HELP { shift->command("HELP",@_)->response() == CMD_OK }
|
|
sub _STAT { shift->command("STAT",@_)->response() == CMD_OK }
|
|
sub _APPE { shift->command("APPE",@_)->response() == CMD_INFO }
|
|
sub _LIST { shift->command("LIST",@_)->response() == CMD_INFO }
|
|
sub _NLST { shift->command("NLST",@_)->response() == CMD_INFO }
|
|
sub _RETR { shift->command("RETR",@_)->response() == CMD_INFO }
|
|
sub _STOR { shift->command("STOR",@_)->response() == CMD_INFO }
|
|
sub _STOU { shift->command("STOU",@_)->response() == CMD_INFO }
|
|
sub _RNFR { shift->command("RNFR",@_)->response() == CMD_MORE }
|
|
sub _REST { shift->command("REST",@_)->response() == CMD_MORE }
|
|
sub _USER { shift->command("user",@_)->response() } # A certain brain dead firewall :-)
|
|
sub _PASS { shift->command("PASS",@_)->response() }
|
|
sub _ACCT { shift->command("ACCT",@_)->response() }
|
|
sub _AUTH { shift->command("AUTH",@_)->response() }
|
|
|
|
sub _SMNT { shift->unsupported(@_) }
|
|
sub _MODE { shift->unsupported(@_) }
|
|
sub _SYST { shift->unsupported(@_) }
|
|
sub _STRU { shift->unsupported(@_) }
|
|
sub _REIN { shift->unsupported(@_) }
|
|
|
|
1;
|