File Coverage

blib/lib/Bio/GMOD/Util/Mirror.pm
Criterion Covered Total %
statement 32 129 24.8
branch 3 86 3.4
condition 5 37 13.5
subroutine 8 18 44.4
pod 2 11 18.1
total 50 281 17.7


line stmt bran cond sub pod time code
1             package Bio::GMOD::Util::Mirror;
2              
3 3     3   65967 use strict;
  3         5  
  3         124  
4 3     3   16 use vars qw/@ISA/;
  3         8  
  3         142  
5 3     3   15 use Carp;
  3         6  
  3         443  
6 3     3   4040 use Net::FTP;
  3         266899  
  3         337  
7 3     3   41 use File::Path;
  3         8  
  3         285  
8 3     3   555 use Bio::GMOD;
  3         6  
  3         545  
9 3     3   18 use Bio::GMOD::Util::Rearrange;
  3         5  
  3         11971  
10              
11             @ISA = qw/Bio::GMOD/;
12              
13             # options:
14             # host -- ftp host
15             # path -- ftp path
16             # localpath -- localpath
17             # verbose -- verbose listing
18             # user -- username
19             # pass -- password
20             # passive -- use passive FTP
21             sub new {
22 1     1 1 206 my ($class,@p) = @_;
23 1         47 my ($host,$path,$localpath,$verbose,$user,$pass,$passive,$hash)
24             = rearrange([qw/HOST PATH LOCALPATH VERBOSE USER PASS PASSIVE HASH/],@p);
25 1 50 33     10 croak "Usage: Mirror->new(\$host:/path)" unless $host && $path;
26 1 50       6 if ($host =~ /(.+):(.+)/) {
27 0         0 ($host,$path) = ($1,$2);
28             }
29 1   50     4 $path ||= '/';
30 1   50     7 $user ||= 'anonymous';
31 1   33     17 $pass ||= "$user\@localhost.localdomain";
32            
33 1         2 my %transfer_opts;
34 1 50       4 $transfer_opts{Passive} = 1 if $passive;
35 1         21 $transfer_opts{Timeout} = 600;
36 1   33     14 my $ftp = Net::FTP->new($host,%transfer_opts) || croak "Can't connect: $@\n";
37 0 0         $ftp->login($user,$pass) || croak "Can't login: ",$ftp->message;
38 0           $ftp->binary;
39 0 0         $ftp->hash(1) if $hash;
40 0           my %opts = (host => $host,
41             path => $path,
42             localpath => $localpath,
43             verbose => $verbose,
44             user => $user,
45             pass => $pass,
46             passive => $passive,
47             ftp => $ftp);
48 0           return bless { %opts },$class;
49             }
50              
51             sub path {
52             # return shift->{path};}
53 0     0 0   my $p = $_[0]->{path};
54 0 0         $_[0]->{path} = $_[1] if defined $_[1];
55 0           $p;
56             }
57              
58             sub ftp {
59             # return shift->{ftp}; }
60 0     0 0   my $p = $_[0]->{ftp};
61 0 0         $_[0]->{ftp} = $_[1] if defined $_[1];
62 0           $p;
63             }
64              
65             sub verbose {
66             # return shift->{verbose}; }
67 0     0 0   my $p = $_[0]->{verbose};
68 0 0         $_[0]->{verbose} = $_[1] if defined $_[1];
69 0           $p;
70             }
71              
72             # top-level entry point for mirroring.
73             sub mirror {
74 0     0 1   my $self = shift;
75 0 0         $self->path(shift) if @_;
76 0           my $path = $self->path;
77            
78 0           my $cd;
79 0 0         if ($self->{localpath}) {
80 0           chomp($cd = `pwd`);
81 0 0         chdir($self->{localpath}) or croak "can't chdir to $self->{localpath}: $!";
82             }
83              
84 0 0         my $type = $self->find_type($self->path) or croak "top level file/directory not found";
85 0           my ($prefix,$leaf) = $path =~ m!^(.*?)([^/]+)/?$!;
86 0 0         $self->ftp->cwd($prefix) if $prefix;
87              
88 0           my $ok;
89 0 0         if ($type eq '-') { # ordinary file
    0          
90 0           $ok = $self->get_file($leaf);
91             } elsif ($type eq 'd') { # directory
92 0           $ok = $self->get_dir($leaf);
93             } else {
94 0           carp "Can't parse file type for $leaf\n";
95 0           return;
96             }
97            
98 0 0         chdir $cd if $cd;
99 0           $ok;
100             }
101              
102             # mirror a file
103             sub get_file {
104 0     0 0   my $self = shift;
105 0           my ($path,$mode) = @_;
106 0           my $ftp = $self->ftp;
107            
108 0           my $rtime = $ftp->mdtm($path);
109 0           my $rsize = $ftp->size($path);
110 0 0         $mode = ($self->parse_listing($ftp->dir($path)))[2] unless defined $mode;
111            
112 0 0         my ($lsize,$ltime) = stat($path) ? (stat(_))[7,9] : (0,0);
113 0 0 0       if ( defined($rtime) and defined($rsize)
      0        
      0        
114             and ($ltime >= $rtime)
115             and ($lsize == $rsize) ) {
116 0 0         $self->warning(-msg => "Getting file $path: not newer than local copy.") if $self->verbose;
117 0           return 1;
118             }
119              
120 0           $self->logit(-msg => "Downloading file $path");
121 0 0 0       $ftp->get($path) or ($self->warning(-msg=>$ftp->message) and return);
122 0 0         chmod $mode,$path if $mode;
123             }
124              
125             # mirror a directory, recursively
126             sub get_dir {
127 0     0 0   my $self = shift;
128 0           my ($path,$mode) = @_;
129            
130 0           my $localpath = $path;
131 0 0 0       -d $localpath or mkpath $localpath or carp "mkpath failed: $!" && return;
      0        
132 0 0 0       chdir $localpath or carp "can't chdir to $localpath: $!" && return;
133 0 0         $mode = 0755 if ($mode == 365); # Kludge-can't mirror non-writable directories
134 0 0         chmod $mode,'.' if $mode;
135            
136 0           my $ftp = $self->ftp;
137            
138 0 0 0       my $cwd = $ftp->pwd or carp("can't pwd: ",$ftp->message) && return;
139 0 0 0       $ftp->cwd($path) or carp("can't cwd: ",$ftp->message) && return;
140            
141 0 0         $self->logit(-msg => "Downloading directory $path") if $self->verbose;
142              
143 0           foreach ($ftp->dir) {
144 0 0         next unless my ($type,$name,$mode) = $self->parse_listing($_);
145 0 0         next if $name =~ /^(\.|\.\.)$/; # skip . and ..
146 0 0         $self->get_dir ($name,$mode) if $type eq 'd';
147 0 0         $self->get_file($name,$mode) if $type eq '-';
148 0 0         $self->make_link($name) if $type eq 'l';
149             }
150            
151 0 0 0       $ftp->cwd($cwd) or carp("can't cwd: ",$ftp->message) && return;
152 0           chdir '..';
153             }
154              
155             # subroutine to determine whether a path is a directory or a file
156             sub find_type {
157 0     0 0   my $self = shift;
158 0           my $path = shift;
159            
160 0           my $ftp = $self->ftp;
161 0           my $pwd = $ftp->pwd;
162 0           my $type = '-'; # assume plain file
163 0 0         if ($ftp->cwd($path)) {
164 0           $ftp->cwd($pwd);
165 0           $type = 'd';
166             }
167 0           return $type;
168             }
169              
170             # Attempt to mirror a link. Only works on relative targets.
171             sub make_link {
172 0     0 0   my $self = shift;
173 0           my $entry = shift;
174            
175 0           my ($link,$target) = split /\s+->\s+/,$entry;
176 0 0         return if $target =~ m!^/!;
177 0 0         $self->logit(-msg => "Symlinking $link -> $target") if $self->verbose;
178 0           return symlink $target,$link;
179             }
180              
181             # parse directory listings
182             # -rw-r--r-- 1 root root 312 Aug 1 1994 welcome.msg
183             sub parse_listing {
184 0     0 0   my $self = shift;
185 0           my $listing = shift;
186 0 0         return unless my ($type,$mode,$name) =
187            
188             $listing =~ /^([a-z-])([a-z-]{9}) # -rw-r--r--
189             \s+\d* # 1
190             (?:\s+\w+){2} # root root
191             \s+\d+ # 312
192             \s+\w+\s+\d+\s+[\d:]+ # Aug 1 1994
193             \s+(.+) # welcome.msg
194             $/x;
195 0           return ($type,$name,$self->filemode($mode));
196             }
197              
198             # turn symbolic modes into octal
199             sub filemode {
200 0     0 0   my $self = shift;
201 0           my $symbolic = shift;
202            
203 0           my (@modes) = $symbolic =~ /(...)(...)(...)$/g;
204 0           my $result;
205 0           my $multiplier = 1;
206            
207 0           while (my $mode = pop @modes) {
208 0           my $m = 0;
209 0 0         $m += 1 if $mode =~ /[xsS]/;
210 0 0         $m += 2 if $mode =~ /w/;
211 0 0         $m += 4 if $mode =~ /r/;
212 0 0         $result += $m * $multiplier if $m > 0;
213 0           $multiplier *= 8;
214             }
215 0           $result;
216             }
217              
218             __END__