File Coverage

blib/lib/XAS/Lib/Curl/FTP.pm
Criterion Covered Total %
statement 4 6 66.6
branch n/a
condition n/a
subroutine 2 2 100.0
pod n/a
total 6 8 75.0


line stmt bran cond sub pod time code
1             package XAS::Lib::Curl::FTP;
2              
3             our $VERSION = '0.01';
4              
5             BEGIN {
6 1     1   1264 no warnings 'redefine';
  1         1  
  1         39  
7              
8 1     1   218 use WWW::Curl::Easy;
  0            
  0            
9              
10             eval {
11              
12             # these constants are not always defined for libcurl on RHEL 5,6,7.
13             # but they are, if you compile libcurl on Windows
14              
15             unless (CURLAUTH_ONLY) {
16              
17             sub CURLAUTH_ONLY { (1 << 31); } # defined in curl.h
18              
19             }
20              
21             };
22              
23             }
24              
25             use DateTime;
26             use Data::Dumper;
27              
28             use XAS::Class
29             version => $VERSION,
30             base => 'XAS::Base',
31             accessors => 'curl transfer_speed transfer_time',
32             mutators => 'retcode',
33             utils => ':validation dotid trim',
34             constants => 'TRUE FALSE',
35             vars => {
36             PARAMS => {
37             -ssl_verify_peer => { optional => 1, default => 1 },
38             -ssl_verify_host => { optional => 1, default => 0 },
39             -fail_on_error => { optional => 1, default => 0 },
40             -keep_alive => { optional => 1, default => 0 },
41             -connect_timeout => { optional => 1, default => 300 },
42             -ssl_cacert => { optional => 1, default => undef },
43             -ssl_keypasswd => { optional => 1, default => undef },
44             -ssl_cert => { optional => 1, default => undef, depends => [ '-ssl_key' ] },
45             -ssl_key => { optional => 1, default => undef, depends => [ '-ssl_cert' ] },
46             -password => { optional => 1, default => undef, depends => [ '-username' ] },
47             -username => { optional => 1, default => undef, depends => [ '-password' ] },
48             }
49             }
50             ;
51              
52             # ----------------------------------------------------------------------
53             # Public Methods
54             # ----------------------------------------------------------------------
55              
56             sub list {
57             my $self = shift;
58             my ($url) = validate_params(\@_, [
59             { isa => 'Badger::URL' },
60             ]);
61              
62             my @buffer;
63              
64             my $write_callback = sub {
65             my $data = shift;
66             my $pointer = shift;
67              
68             push(@{$pointer}, $data);
69              
70             return length($data);
71              
72             };
73              
74             $self->curl->setopt(CURLOPT_URL, $url->text);
75             $self->curl->setopt(CURLOPT_WRITEDATA, \@buffer);
76             $self->curl->setopt(CURLOPT_WRITEFUNCTION, $write_callback);
77              
78             unless (($self->{'retcode'} = $self->curl->perform) == 0) {
79              
80             $self->throw_msg(
81             dotid($self->class) . '.list',
82             'curl',
83             $self->retcode, lc($self->curl->strerror($self->retcode))
84             );
85              
86             }
87              
88             return wantarray ? @buffer : \@buffer;
89              
90             }
91              
92             sub info {
93             my $self = shift;
94             my ($url) = validate_params(\@_, [
95             { isa => 'Badger::URL' },
96             ]);
97              
98             my @buffer;
99             my $dt = undef;
100             my $size = undef;
101              
102             my $write_callback = sub {
103             my $data = shift;
104             my $pointer = shift;
105              
106             push(@{$pointer}, $data);
107              
108             return length($data);
109              
110             };
111              
112             $self->curl->setopt(CURLOPT_URL, $url->text);
113             $self->curl->setopt(CURLOPT_NOBODY, 1);
114             $self->curl->setopt(CURLOPT_FILETIME, 1);
115             $self->curl->setopt(CURLOPT_WRITEDATA, \@buffer);
116             $self->curl->setopt(CURLOPT_WRITEFUNCTION, $write_callback);
117              
118             if (($self->{'retcode'} = $self->curl->perform) == 0) {
119              
120             $size = $self->curl->getinfo(CURLINFO_CONTENT_LENGTH_DOWNLOAD);
121              
122             my $time = $self->curl->getinfo(CURLINFO_FILETIME);
123             if (defined($time)) {
124              
125             $dt = DateTime->from_epoch(epoch => $time);
126              
127             }
128              
129             } else {
130              
131             $self->throw_msg(
132             dotid($self->class) . '.info',
133             'curl',
134             $self->retcode, lc($self->curl->strerror($self->retcode))
135             );
136              
137             }
138              
139             return $size, $dt;
140              
141             }
142              
143             sub get {
144             my $self = shift;
145             my ($url, $file) = validate_params(\@_, [
146             { isa => 'Badger::URL' },
147             { isa => 'Badger::Filesystem::File' },
148             ]);
149              
150             my $stat = FALSE;
151             my $fd = $file->open('w');
152              
153             my $write_callback = sub {
154             my $buffer = shift;
155             my $fd = shift;
156              
157             return $fd->syswrite($buffer);
158              
159             };
160              
161             $self->curl->setopt(CURLOPT_WRITEDATA, $fd);
162             $self->curl->setopt(CURLOPT_URL, $url->text);
163             $self->curl->setopt(CURLOPT_WRITEFUNCTION, $write_callback);
164              
165             if (($self->{'retcode'} = $self->curl->perform) == 0) {
166              
167             $stat = TRUE;
168              
169             $self->{'transfer_time'} = $self->curl->getinfo(CURLINFO_TOTAL_TIME);
170             $self->{'transfer_speed'} = $self->curl->getinfo(CURLINFO_SPEED_DOWNLOAD);
171              
172             $fd->close;
173              
174             } else {
175              
176             $fd->close;
177              
178             $self->throw_msg(
179             dotid($self->class) . '.get',
180             'curl',
181             $self->retcode, lc($self->curl->strerror($self->retcode))
182             );
183              
184             }
185              
186             return $stat;
187              
188             }
189              
190             sub put {
191             my $self = shift;
192             my ($file, $url) = validate_params(\@_, [
193             { isa => 'Badger::Filesystem::File' },
194             { isa => 'Badger::URL' },
195             ]);
196              
197             my $stat = FALSE;
198             my $fd = $file->open('r');
199             my $size = ($file->stat)[7];
200              
201             my $read_callback = sub {
202             my $size = shift;
203             my $fd = shift;
204              
205             my $buffer;
206             my $rc = $fd->sysread($buffer, $size);
207              
208             return ($rc > 0) ? $buffer : '';
209              
210             };
211              
212             $self->curl->setopt(CURLOPT_UPLOAD, 1);
213             $self->curl->setopt(CURLOPT_READDATA, $fd);
214             $self->curl->setopt(CURLOPT_URL, $url->text);
215             $self->curl->setopt(CURLOPT_INFILESIZE_LARGE, $size);
216             $self->curl->setopt(CURLOPT_READFUNCTION, $read_callback);
217              
218             if (($self->{'retcode'} = $self->curl->perform) == 0) {
219              
220             $stat = TRUE;
221              
222             $self->{'transfer_time'} = $self->curl->getinfo(CURLINFO_TOTAL_TIME);
223             $self->{'transfer_speed'} = $self->curl->getinfo(CURLINFO_SPEED_UPLOAD);
224              
225             $fd->close;
226              
227             } else {
228              
229             $fd->close;
230              
231             $self->throw_msg(
232             dotid($self->class) . '.put',
233             'curl',
234             $self->retcode, lc($self->curl->strerror($self->retcode))
235             );
236              
237             }
238              
239             return $stat;
240              
241             }
242              
243             # ----------------------------------------------------------------------
244             # Private Methods
245             # ----------------------------------------------------------------------
246              
247             sub init {
248             my $class = shift;
249              
250             my $self = $class->SUPER::init(@_);
251              
252             my $protocols = (CURLPROTO_FTP & CURLPROTO_FTPS);
253             my $connect_timeout = $self->connect_timeout * 1000;
254              
255             $self->{'curl'} = WWW::Curl::Easy->new();
256              
257             # basic options
258              
259             $self->curl->setopt(CURLOPT_VERBOSE, $self->xdebug);
260             $self->curl->setopt(CURLOPT_PROTOCOLS, $protocols);
261             $self->curl->setopt(CURLOPT_NOPROGRESS, 1);
262             $self->curl->setopt(CURLOPT_FAILONERROR, $self->fail_on_error);
263             $self->curl->setopt(CURLOPT_FORBID_REUSE, !$self->keep_alive);
264             $self->curl->setopt(CURLOPT_CONNECTTIMEOUT_MS, $connect_timeout);
265              
266             # setup authentication
267              
268             if ($self->username) {
269              
270             $self->curl->setopt(CURLOPT_USERNAME, $self->username);
271             $self->curl->setopt(CURLOPT_PASSWORD, $self->password);
272              
273             }
274              
275             # set up the SSL stuff
276              
277             $self->curl->setopt(CURLOPT_SSL_VERIFYPEER, $self->ssl_verify_peer);
278             $self->curl->setopt(CURLOPT_SSL_VERIFYHOST, $self->ssl_verify_host);
279              
280             if ($self->ssl_keypasswd) {
281              
282             $self->curl->setop(CURLOPT_KEYPASSWD, $self->ssl_keypasswd);
283              
284             }
285              
286             if ($self->ssl_cacert) {
287              
288             $self->curl->setopt(CURLOPT_CAINFO, $self->ssl_cacert);
289              
290             }
291              
292             if ($self->ssl_cert) {
293              
294             $self->curl->setopt(CURLOPT_SSLCERT, $self->ssl_cert);
295             $self->curl->setopt(CURLOPT_SSLKEY, $self->ssl_key);
296              
297             }
298              
299             return $self;
300              
301             }
302              
303             1;
304              
305             __END__
306              
307             =head1 NAME
308              
309             XAS::Lib::Curl::FTP - A class to transfer files using FTP
310              
311             =head1 SYNOPSIS
312              
313             use Badger::URL 'URL';
314             use XAS::Lib::Curl::FTP;
315             use Badger::Filesystem 'File';
316              
317             my $ftp = XAS::Lib::Curl::FTP->new(
318             -username = 'kevin',
319             -password = 'password',
320             );
321              
322             my $url = URL('ftp://examples.com/directory/file.txt');
323             my $file = File('file.txt');
324              
325             if ($ftp->get($url, $file)) {
326              
327             printf("fetched %s in %s seconds, at a speed of %s bytes per second\n",
328             $ftp->transfer_time, $ftp->transfer_speed);
329              
330             }
331              
332             =head1 DESCRIPTION
333              
334             This module uses WWW::Curl to transfer files using FTP/FTPS.
335              
336             =head1 METHODS
337              
338             =head2 new
339              
340             This method initializes the module and takes the following parameters:
341              
342             =over 4
343              
344             =item B<-keep_alive>
345              
346             A toggle to tell curl to forbid the reuse of sockets, defaults to true.
347              
348             =item B<-connect_timeout>
349              
350             The timeout for the initial connection, defaults to 300 seconds.
351              
352             =item B<-password>
353              
354             An optional password to use, implies a username. Wither the password is
355             actually used, depends on -auth_method.
356              
357             =item B<-username>
358              
359             An optional username to use, implies a password.
360              
361             =item B<-ssl_cacert>
362              
363             An optional CA cerificate to use.
364              
365             =item B<-ssl_keypasswd>
366              
367             An optional password for a signed cerificate.
368              
369             =item B<-ssl_cert>
370              
371             An optional certificate to use.
372              
373             =item B<-ssl_key>
374              
375             An optional key for a certificate to use.
376              
377             =item B<-ssl_verify_host>
378              
379             Wither to verify the host certifcate, defaults to true.
380              
381             =item B<-ssl_verify_peer>
382              
383             Wither to verify the peer certificate, defaults to true.
384              
385             =back
386              
387             =head2 list($url)
388              
389             This method will return a list of files for the given url. The format of
390             the list is server dependent.
391              
392             =over 4
393              
394             =item B<$url>
395              
396             This is a Badger::URL object of the files url.
397              
398             =back
399              
400             =head2 info($url)
401              
402             This method will return the size and DateTime object of the file residing at
403             the url.
404              
405             =over 4
406              
407             =item B<$url>
408              
409             This is a Badger::URL object of the files url.
410              
411             =back
412              
413             =head2 get($url, $file)
414              
415             This method will get the file at url and place it a files destination. It
416             will return TRUE upon success, or throw an exception on failure.
417              
418             =over 4
419              
420             =item B<$url>
421              
422             This is a Badger::URL object of the files url.
423              
424             =item B<$file>
425              
426             This ia a Badger::Filesystem::File object of the files destination.
427              
428             =back
429              
430             =head2 put($file, $url)
431              
432             This method will put the file at the destination url. It will return TRUE upon
433             success, or throw an exception on failure.
434              
435             =over 4
436              
437             =item B<$url>
438              
439             This is a Badger::URL object of the files url.
440              
441             =item B<$file>
442              
443             This ia a Badger::Filesystem::File object of the files source.
444              
445             =back
446              
447             =head1 SEE ALSO
448              
449             =over 4
450              
451             =item L<XAS::Lib::Curl::HTTP|XAS::Lib::Curl::HTTP>
452              
453             =item L<XAS|XAS>
454              
455             =back
456              
457             =head1 AUTHOR
458              
459             Kevin L. Esteb, E<lt>kevin@kesteb.usE<gt>
460              
461             =head1 COPYRIGHT AND LICENSE
462              
463             Copyright (c) 2012-2017 Kevin L. Esteb
464              
465             This is free software; you can redistribute it and/or modify it under
466             the terms of the Artistic License 2.0. For details, see the full text
467             of the license at http://www.perlfoundation.org/artistic_license_2_0.
468              
469             =cut