| 1 | package Net::FTP::Stepstone; |
|---|
| 2 | |
|---|
| 3 | use strict; |
|---|
| 4 | use strict; |
|---|
| 5 | use base qw(Class::Accessor::Fast); |
|---|
| 6 | use File::stat (); |
|---|
| 7 | use File::Basename; |
|---|
| 8 | use Time::Piece; |
|---|
| 9 | |
|---|
| 10 | our $VERSION = '0.02'; |
|---|
| 11 | __PACKAGE__->mk_accessors(qw( |
|---|
| 12 | cwd |
|---|
| 13 | lcwd |
|---|
| 14 | stepstone_addr |
|---|
| 15 | stepstone_user |
|---|
| 16 | ftp_addr |
|---|
| 17 | ftp_user |
|---|
| 18 | ftp_pass |
|---|
| 19 | ftp_port |
|---|
| 20 | )); |
|---|
| 21 | |
|---|
| 22 | sub new { |
|---|
| 23 | my $class = shift; |
|---|
| 24 | my $self = $class->SUPER::new(@_); |
|---|
| 25 | $self->cwd('.') unless $self->cwd; |
|---|
| 26 | $self->ftp_port(21) unless $self->ftp_port; |
|---|
| 27 | return $self; |
|---|
| 28 | } |
|---|
| 29 | |
|---|
| 30 | sub ls { |
|---|
| 31 | my $self = shift; |
|---|
| 32 | my $arg = shift || ''; |
|---|
| 33 | my @lines = $self->_ftp_command("ls $arg"); |
|---|
| 34 | my @items = (); |
|---|
| 35 | for (@lines) { |
|---|
| 36 | my $name = (split(' ', $_))[8] or next; |
|---|
| 37 | $name =~ m{^\.{1,2}$} and next; |
|---|
| 38 | push @items, $name; |
|---|
| 39 | } |
|---|
| 40 | return @items; |
|---|
| 41 | } |
|---|
| 42 | |
|---|
| 43 | sub stat { |
|---|
| 44 | my $self = shift; |
|---|
| 45 | my $arg = shift or return; |
|---|
| 46 | $arg =~ s{/$}{}; |
|---|
| 47 | my $dir = dirname($arg); |
|---|
| 48 | my $base = basename($arg); |
|---|
| 49 | my @lines = $self->_ftp_command("ls $dir"); |
|---|
| 50 | my $stob; |
|---|
| 51 | for (@lines) { |
|---|
| 52 | (split(' ', $_))[8] eq $base or next; |
|---|
| 53 | $stob = $self->_stat($_); |
|---|
| 54 | } |
|---|
| 55 | return $stob; |
|---|
| 56 | } |
|---|
| 57 | |
|---|
| 58 | sub pwd { |
|---|
| 59 | my $self = shift; |
|---|
| 60 | if ($self->cwd eq '.') { |
|---|
| 61 | $self->_ftp_command(''); |
|---|
| 62 | } |
|---|
| 63 | return $self->cwd; |
|---|
| 64 | } |
|---|
| 65 | |
|---|
| 66 | sub lcd { |
|---|
| 67 | my $self = shift; |
|---|
| 68 | my $arg = shift; |
|---|
| 69 | $self->_ftp_command("lcd $arg"); |
|---|
| 70 | $self->lcwd($arg); |
|---|
| 71 | } |
|---|
| 72 | |
|---|
| 73 | for my $command (qw(cd get mget)) { |
|---|
| 74 | no strict 'refs'; |
|---|
| 75 | *$command = sub { |
|---|
| 76 | my $self = shift; |
|---|
| 77 | my $arg = join ' ', @_; |
|---|
| 78 | my $res = $self->_ftp_command("$command $arg"); |
|---|
| 79 | return $res unless $res =~ /^2/; |
|---|
| 80 | return; |
|---|
| 81 | }; |
|---|
| 82 | } |
|---|
| 83 | |
|---|
| 84 | sub _ftp_command { |
|---|
| 85 | my $self = shift; |
|---|
| 86 | my $command = shift || ''; |
|---|
| 87 | $command = "lcd $self->{lcwd}\n".$command if $self->lcwd; |
|---|
| 88 | my $ftp_command = <<EOF; |
|---|
| 89 | open $self->{ftp_addr} $self->{ftp_port} |
|---|
| 90 | user $self->{ftp_user} $self->{ftp_pass} |
|---|
| 91 | prompt |
|---|
| 92 | type binary |
|---|
| 93 | cd $self->{cwd} |
|---|
| 94 | $command |
|---|
| 95 | pwd |
|---|
| 96 | close |
|---|
| 97 | quit |
|---|
| 98 | EOF |
|---|
| 99 | my @all = ` echo "$ftp_command" | ssh -l $self->{stepstone_user} $self->{stepstone_addr} ftp -n 2>/dev/null`; |
|---|
| 100 | my @lines = grep {$_ !~ m/not understood|Interactive mode off.|Local directory now/} @all; |
|---|
| 101 | if (my $last_line = $lines[-1]) { |
|---|
| 102 | my ($code, $cwd) = split '"', $last_line; |
|---|
| 103 | if ($code =~ /257/) { |
|---|
| 104 | $self->cwd($cwd); |
|---|
| 105 | } else { |
|---|
| 106 | return $last_line; |
|---|
| 107 | } |
|---|
| 108 | } |
|---|
| 109 | if (scalar @lines > 1) { |
|---|
| 110 | return @lines[0..($#lines -1)]; |
|---|
| 111 | } |
|---|
| 112 | return undef; |
|---|
| 113 | } |
|---|
| 114 | |
|---|
| 115 | sub _stat { |
|---|
| 116 | my $self = shift; |
|---|
| 117 | my $line = shift; |
|---|
| 118 | my ($mode, $nlink, $user, $group, $size, $month, $date, $time_or_year, $name) = split (" ", $line); |
|---|
| 119 | my ($time, $year); |
|---|
| 120 | if ($time_or_year =~ /:/) { |
|---|
| 121 | $time = $time_or_year; |
|---|
| 122 | $year = Time::Piece->strptime($month, "%b")->mon > localtime->mon ? |
|---|
| 123 | localtime->year - 1 : localtime->year; |
|---|
| 124 | } else { |
|---|
| 125 | $time = '00:00'; |
|---|
| 126 | $year = $time_or_year; |
|---|
| 127 | } |
|---|
| 128 | my $mtime = Time::Piece->strptime(join(' ', $year, $month, $date, $time),"%Y %b %d %H:%M")->epoch; |
|---|
| 129 | my $uid = $self->_getent_passwd($user); |
|---|
| 130 | my $gid = $self->_getent_group($group); |
|---|
| 131 | my $stob = File::stat::populate( |
|---|
| 132 | 0, # dev |
|---|
| 133 | 0, # ino |
|---|
| 134 | $self->_mode($mode), # mode |
|---|
| 135 | $nlink + 0, # nlink |
|---|
| 136 | $uid + 0, # uid |
|---|
| 137 | $gid + 0, # gid |
|---|
| 138 | 0, # rdev |
|---|
| 139 | $size + 0, # size |
|---|
| 140 | 0, # atime |
|---|
| 141 | $mtime + 0, # mtime |
|---|
| 142 | 0, # ctime |
|---|
| 143 | 1, # blksize |
|---|
| 144 | $size + 0, # blocks |
|---|
| 145 | ); |
|---|
| 146 | return $stob; |
|---|
| 147 | } |
|---|
| 148 | |
|---|
| 149 | for my $db (qw(group passwd)) { |
|---|
| 150 | no strict 'refs'; |
|---|
| 151 | my $command = "_getent_$db"; |
|---|
| 152 | *$command = sub { |
|---|
| 153 | my $self = shift; |
|---|
| 154 | my $name = shift; |
|---|
| 155 | my $val = `getent $db $name | cut -d: -f3`; |
|---|
| 156 | chomp $val; |
|---|
| 157 | $val = 99 unless length $val; |
|---|
| 158 | return $val; |
|---|
| 159 | } |
|---|
| 160 | } |
|---|
| 161 | |
|---|
| 162 | sub _mode { |
|---|
| 163 | my $self = shift; |
|---|
| 164 | my $mode = shift; |
|---|
| 165 | my $val = 0; |
|---|
| 166 | my ($t,$u,$g,$o) = ($mode =~ /^(.)(...)(...)(...)$/); |
|---|
| 167 | |
|---|
| 168 | for ($t){ |
|---|
| 169 | $val |= 0010000 if /p/; |
|---|
| 170 | $val |= 0020000 if /c/; |
|---|
| 171 | $val |= 0040000 if /d/; |
|---|
| 172 | $val |= 0060000 if /b/; |
|---|
| 173 | $val |= 0100000 if /-/; |
|---|
| 174 | $val |= 0120000 if /l/; |
|---|
| 175 | $val |= 0140000 if /s/; |
|---|
| 176 | } |
|---|
| 177 | |
|---|
| 178 | for ($u){ |
|---|
| 179 | $val |= 0400 if /r/; |
|---|
| 180 | $val |= 0200 if /w/; |
|---|
| 181 | $val |= 0100 if /[xs]/; |
|---|
| 182 | $val |= 04000 if /[sS]/; |
|---|
| 183 | } |
|---|
| 184 | |
|---|
| 185 | for ($g){ |
|---|
| 186 | $val |= 0040 if /r/; |
|---|
| 187 | $val |= 0020 if /w/; |
|---|
| 188 | $val |= 0010 if /[xs]/; |
|---|
| 189 | $val |= 02000 if /[sS]/; |
|---|
| 190 | } |
|---|
| 191 | |
|---|
| 192 | for ($o){ |
|---|
| 193 | $val |= 0004 if /r/; |
|---|
| 194 | $val |= 0002 if /w/; |
|---|
| 195 | $val |= 0001 if /[xt]/; |
|---|
| 196 | $val |= 01000 if /[Tt]/; |
|---|
| 197 | } |
|---|
| 198 | |
|---|
| 199 | return $val; |
|---|
| 200 | } |
|---|
| 201 | |
|---|
| 202 | |
|---|
| 203 | 1; |
|---|
| 204 | __END__ |
|---|
| 205 | |
|---|
| 206 | =head1 NAME |
|---|
| 207 | |
|---|
| 208 | Net::FTP::Stepstone - |
|---|
| 209 | |
|---|
| 210 | =head1 SYNOPSIS |
|---|
| 211 | |
|---|
| 212 | use Net::FTP::Stepstone; |
|---|
| 213 | |
|---|
| 214 | =head1 DESCRIPTION |
|---|
| 215 | |
|---|
| 216 | Net::FTP::Stepstone is |
|---|
| 217 | |
|---|
| 218 | =head1 AUTHOR |
|---|
| 219 | |
|---|
| 220 | Author E<lt>author@galaxyE<gt> |
|---|
| 221 | |
|---|
| 222 | This library is free software; you can redistribute it and/or modify |
|---|
| 223 | it under the same terms as Perl itself. |
|---|
| 224 | |
|---|
| 225 | =head1 SEE ALSO |
|---|
| 226 | |
|---|
| 227 | L<> |
|---|
| 228 | |
|---|
| 229 | =cut |
|---|