File Coverage

blib/lib/IO/Ftp.pm
Criterion Covered Total %
statement 24 144 16.6
branch 0 58 0.0
condition 0 15 0.0
subroutine 8 18 44.4
pod 5 8 62.5
total 37 243 15.2


line stmt bran cond sub pod time code
1             package IO::Ftp;
2             require 5.005_62;
3              
4 1     1   541 use strict;
  1         2  
  1         29  
5 1     1   4 use warnings;
  1         1  
  1         41  
6              
7             require Exporter;
8              
9             our @ISA = qw(Exporter);
10              
11 1     1   4 use vars qw/$VERSION/;
  1         3  
  1         106  
12              
13             $VERSION = 0.05;
14             our %EXPORT_TAGS = ( 'all' => [ qw(
15             new
16             delete
17             rename_to
18             mdtm
19             size
20             filename
21             ) ] );
22              
23             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
24             our @EXPORT = qw();
25              
26              
27 1     1   4 use File::Basename;
  1         1  
  1         95  
28 1     1   851 use URI;
  1         6708  
  1         29  
29 1     1   1000 use Symbol;
  1         910  
  1         65  
30 1     1   927 use Net::FTP;
  1         46239  
  1         65  
31 1     1   11 use Carp;
  1         2  
  1         1325  
32              
33              
34             sub new {
35 0     0 1   my ($src, $mode, $uri_string, %args) = @_;
36 0   0       my $class = ref $src || 'IO::Ftp';
37 0 0 0       if (ref $src and not $src->isa('IO::Ftp')) {
38 0           carp "Can't make an IO::FTP from a ", ref $src;
39 0           return;
40             }
41              
42 0           my $uri;
43 0 0         if (ref $uri_string) {
44 0 0         unless ($uri_string->isa('URI')) {
45 0           carp "can' t make a URI from a ", ref $uri_string;
46 0           return;
47             }
48 0           $uri = $uri_string;
49             } else {
50 0           $uri = URI->new('ftp:' . $uri_string);
51             }
52              
53 0           my $ftp;
54 0 0 0       if (ref $src and not $uri->host) {
55 0 0         if ($src->connected) {
56 0           warn "Can't reuse host with open connection";
57 0           return;
58             }
59 0           $ftp = ${*$src}{'io_ftp_ftp'};
  0            
60             } else {
61 0           $ftp = Net::FTP->new(
62             $uri->host,
63             Port => $uri->port,
64             Debug => $args{DEBUG},
65             Timeout => $args{Timeout},
66             BlockSize => $args{BlockSize},
67             Passive => $args{Passive},
68             );
69             }
70            
71 0 0         unless ($ftp) {
72 0           carp "Can't connect to host ", $uri->host;
73 0           return;
74             }
75            
76 0           my $self = __open($ftp, $mode, $uri, %args);
77 0 0         return unless $self;
78            
79 0           ${*$self}{'io_ftp_ftp'} = $ftp;
  0            
80 0           ${*$self}{'io_ftp_uri'} = $uri;
  0            
81              
82 0           return bless $self, $class;
83             }
84              
85             sub __open {
86 0     0     my ($ftp, $mode, $uri, %args) = @_;
87              
88 0   0       my $id = $uri->user || 'anonymous';
89 0   0       my $pwd = $uri->password || 'anon@anon.org';
90            
91 0 0         unless ($ftp->login($id, $pwd)) {
92 0           warn "Can't login: ", $ftp->message;
93 0           return;
94             }
95            
96 0 0         fileparse_set_fstype($args{OS}) if $args{OS};
97            
98 0           my ($file, $path) = fileparse($uri->path);
99 0 0         warn "File: $file, Path: $path" if $args{DEBUG};
100              
101 0 0         if ($path =~ m{^//(.*)}) { # initial single / is relative path, // is absolute
102 0           $path = $1;
103 0 0         warn "cwd /" if $args{DEBUG};
104 0 0         unless ($ftp->cwd('/')) {
105 0           warn "Can't cwd to /";
106 0           return;
107             }
108             }
109            
110 0           foreach (split '/', $path) {
111 0 0         next unless $_; #ignore embedded back-to-back /. else will cwd with no parm, which will default to 'cwd /'
112 0 0         warn "cwd $_" if $args{DEBUG};
113 0 0         unless ($ftp->cwd($_)) {
114 0           warn "Can't cwd to $_";
115 0           return;
116             }
117             }
118 0 0         if ($args{type}) {
119 0           $args{type} = uc $args{type};
120 0 0         unless ($args{type} =~ /^[AI]$/) {
121 0           carp "Invalid type: $args{type}";
122 0           return;
123             }
124 0 0         unless ($ftp->type($args{type}) ) {
125 0           carp "Can't set type $args{type}: ", $ftp->message;
126             }
127             }
128              
129 0 0         if ($mode eq '<<') {
130 0           $file = __find_file($ftp, $file);
131 0 0         return unless $file;
132             }
133              
134             # cache these in case user wants initial values. Can't get them once the data connection is open.
135 0           my $size = $ftp->size($file);
136 0           my $mdtm = $ftp->mdtm($file);
137            
138            
139 0           my $dataconn;
140 0 0 0       if ($mode eq '<' or $mode eq '<<') {
    0          
    0          
141 0           $dataconn = $ftp->retr($file);
142             } elsif ($mode eq '>') {
143 0           $dataconn = $ftp->stor($file);
144             } elsif ($mode eq '>>') {
145 0           $dataconn = $ftp->appe($file);
146             } else {
147 0           carp "Invalid mode $mode";
148 0           return;
149             }
150              
151 0 0         unless ($dataconn) {
152 0           carp "Can't open $file: ", $ftp->message ;
153 0           return;
154             }
155              
156             # we want to be a subclass of the dataconn, but its class is dynamic.
157 0           push @ISA, ref $dataconn;
158            
159 0           ${*$dataconn}{'io_ftp_file'} = $file;
  0            
160 0           ${*$dataconn}{'io_ftp_path'} = $path;
  0            
161 0           ${*$dataconn}{'io_ftp_size'} = $size;
  0            
162 0           ${*$dataconn}{'io_ftp_mdtm'} = $mdtm;
  0            
163            
164 0           return $dataconn;
165             }
166              
167             sub __find_file {
168 0     0     my ($ftp,$pattern) = @_;
169              
170 0           my @files = $ftp->ls($pattern);
171 0           return $files[0];
172             }
173              
174              
175             sub filename {
176 0     0 0   my $self = shift;
177 0           return ${*$self}{'io_ftp_file'};
  0            
178             }
179              
180             sub path {
181 0     0 0   my $self = shift;
182 0           return ${*$self}{'io_ftp_path'};
  0            
183             }
184              
185             sub uri {
186 0     0 0   my $self = shift;
187 0           return ${*$self}{'io_ftp_uri'};
  0            
188             }
189              
190             ### allow shortcuts to Net::FTP's rename and delete, but only if data connection not open. OTW we'll hang.
191              
192             sub rename_to {
193 0     0 1   my ($self, $new_name) = @_;
194 0 0         return if $self->connected;
195            
196 0           my $ret = ${*$self}{'io_ftp_ftp'}->rename(${*$self}{'io_ftp_file'}, $new_name);
  0            
  0            
197 0           ${*$self}{'io_ftp_file'} = $new_name;
  0            
198 0           return $ret;
199             }
200              
201             sub delete {
202 0     0 1   my ($self) = @_;
203 0 0         return if $self->connected;
204            
205 0           return ${*$self}{'io_ftp_ftp'}->delete(${*$self}{'io_ftp_file'});
  0            
  0            
206             }
207              
208              
209             ### return cached stats if connected, or real ones if connection closed.
210              
211             sub mdtm {
212 0     0 1   my ($self) = @_;
213 0 0         return ${*$self}{'io_ftp_mdtm'} if $self->connected;
  0            
214            
215 0           return ${*$self}{'io_ftp_ftp'}->mdtm(${*$self}{'io_ftp_file'});
  0            
  0            
216             }
217              
218             sub size {
219 0     0 1   my ($self) = @_;
220 0 0         return ${*$self}{'io_ftp_size'} if $self->connected;
  0            
221            
222 0           return ${*$self}{'io_ftp_ftp'}->size(${*$self}{'io_ftp_file'});
  0            
  0            
223             }
224              
225              
226             1;
227              
228              
229             =head1 NAME
230              
231             IO::Ftp - A simple interface to Net::FTP's socket level get/put
232              
233             =head1 SYNOPSIS
234              
235              
236             use IO::Ftp;
237            
238             my $out = IO::Ftp->new('>','//user:pwd@foo.bar.com/foo/bar/fu.bar', TYPE=>'a');
239             my $in = IO::Ftp->new('<','//foo.bar.com/foo/bar/fu.bar', TYPE=>'a'); #anon access example
240            
241             while (<$in>) {
242             s/foo/bar/g;
243             print $out;
244             }
245            
246             close $in;
247             close $out;
248              
249              
250             ### for something along the lines of 'mget':
251            
252             while (my $in = IO::Ftp->new('<<','//foo.bar.com/foo/bar/*.txt', TYPE=>'a') {
253             print "processing ",$in->filename, "\n";
254             #...
255             $in->close;
256             $in->delete;
257             }
258              
259              
260             =head1 DESCRIPTION
261              
262             =head2 EXPORTS
263              
264             None by default.
265              
266             =head2 REQUIRES
267              
268             L
269             L
270             L
271             L
272              
273              
274             =head1 CONSTRUCTOR
275              
276             =over 4
277              
278             =item new ( MODE, URI [,OPTIONS] )
279              
280             C indicates the FTP command to use, and is one of
281              
282             =over 4
283              
284             =item < get
285              
286             =item > put
287              
288             =item >> append
289              
290             =item << get with wildcard match. This allows fetching a file when the name is not known,
291             or is partially known. Wildcarding is as performed by Net::FTP::ls. If more than one file matches,
292             the same one will always be returned. To process a number of files, they must be deleted
293             or renamed to not match the wildcard.
294              
295             =back
296              
297             C is an FTP format URI without the leading 'ftp:'.
298             C are passed in hash format, and can be one or more of
299              
300             =over 4
301              
302             =item TYPE force ASCII (a) or binary (i) mode for the transfer.
303              
304             =item DEBUG Enables debug messages. Also enabled Net::FTP's Debug flag.
305              
306             =item Timeout Passed to Net::FTP::new
307              
308             =item BlockSize Passed to Net::FTP::new
309              
310             =item Passive Passed to Net::FTP::new
311              
312             =back
313              
314             =back
315              
316             =head1 METHODS
317              
318             =over 4
319              
320             =item rename_to (NEW_NAME)
321             Renames the file.
322              
323             =item delete
324             Deletes the file.
325              
326             =item size
327             Returns the size of the file.
328              
329             =item mdtm
330             Returns the modification time of the fiile.
331              
332             =back
333             Note: These methods cannot be performed while the connection is open.
334             rename_to and delete will fail and return undef if used before the socket is closed.
335              
336             size and mdtm cache their values before the socket is opened.
337             After the socket is closed, they call the Net::FTP methods of the same name.
338              
339             =head1 CREDITS
340              
341             Graham Barr for his Net::FTP module, which does all the 'real work'.
342              
343             tye at PerlMonks
344              
345             =head1 COPYRIGHT
346              
347             (c) 2003 Mike Blackwell. All rights reserved.
348             This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
349              
350              
351             =head1 AUTHOR
352              
353             Mike Blackwell
354              
355             =head1 SEE ALSO
356              
357             Net::FTP
358             perl(1).
359              
360             =cut