File Coverage

blib/lib/Metabrik/Client/Www.pm
Criterion Covered Total %
statement 9 343 2.6
branch 0 182 0.0
condition 0 57 0.0
subroutine 3 37 8.1
pod 1 31 3.2
total 13 650 2.0


line stmt bran cond sub pod time code
1             #
2             # $Id$
3             #
4             # client::www Brik
5             #
6             package Metabrik::Client::Www;
7 9     9   73 use strict;
  9         18  
  9         252  
8 9     9   42 use warnings;
  9         23  
  9         244  
9              
10 9     9   43 use base qw(Metabrik);
  9         18  
  9         28799  
11              
12             sub brik_properties {
13             return {
14 0     0 1   revision => '$Revision$',
15             tags => [ qw(unstable browser http javascript screenshot) ],
16             author => 'GomoR ',
17             license => 'http://opensource.org/licenses/BSD-3-Clause',
18             attributes => {
19             uri => [ qw(uri) ],
20             username => [ qw(username) ],
21             password => [ qw(password) ],
22             ignore_content => [ qw(0|1) ],
23             user_agent => [ qw(user_agent) ],
24             ssl_verify => [ qw(0|1) ],
25             datadir => [ qw(datadir) ],
26             timeout => [ qw(0|1) ],
27             rtimeout => [ qw(timeout) ],
28             add_headers => [ qw(http_headers_hash) ],
29             do_javascript => [ qw(0|1) ],
30             do_redirects => [ qw(0|1) ],
31             src_ip => [ qw(ip_address) ],
32             max_redirects => [ qw(count) ],
33             client => [ qw(object) ],
34             _last => [ qw(object|INTERNAL) ],
35             _last_code => [ qw(code|INTERNAL) ],
36             },
37             attributes_default => {
38             ssl_verify => 0,
39             ignore_content => 0,
40             timeout => 0,
41             rtimeout => 10,
42             add_headers => {},
43             do_javascript => 0,
44             do_redirects => 1,
45             max_redirects => 10,
46             },
47             commands => {
48             install => [ ], # Inherited
49             create_user_agent => [ ],
50             reset_user_agent => [ ],
51             get => [ qw(uri|OPTIONAL username|OPTIONAL password|OPTIONAL) ],
52             cat => [ qw(uri|OPTIONAL username|OPTIONAL password|OPTIONAL) ],
53             post => [ qw(content_hash uri|OPTIONAL username|OPTIONAL password|OPTIONAL) ],
54             patch => [ qw(content_hash uri|OPTIONAL username|OPTIONAL password|OPTIONAL) ],
55             put => [ qw(content_hash uri|OPTIONAL username|OPTIONAL password|OPTIONAL) ],
56             head => [ qw(uri|OPTIONAL username|OPTIONAL password|OPTIONAL) ],
57             delete => [ qw(uri|OPTIONAL username|OPTIONAL password|OPTIONAL) ],
58             options => [ qw(uri|OPTIONAL username|OPTIONAL password|OPTIONAL) ],
59             code => [ ],
60             content => [ ],
61             get_content => [ qw(uri|OPTIONAL username|OPTIONAL password|OPTIONAL) ],
62             post_content => [ qw(content_hash uri|OPTIONAL username|OPTIONAL password|OPTIONAL) ],
63             save_content => [ qw(output) ],
64             headers => [ ],
65             get_response_headers => [ ],
66             delete_request_header => [ qw(header) ],
67             get_response_header => [ qw(header) ],
68             set_request_header => [ qw(header value|value_list) ],
69             forms => [ ],
70             links => [ ],
71             trace_redirect => [ qw(uri|OPTIONAL) ],
72             screenshot => [ qw(uri output) ],
73             eval_javascript => [ qw(js) ],
74             info => [ qw(uri|OPTIONAL) ],
75             mirror => [ qw(url|$url_list output|OPTIONAL datadir|OPTIONAL) ],
76             parse => [ qw(html) ],
77             get_last => [ ],
78             get_last_code => [ ],
79             },
80             require_modules => {
81             'IO::Socket::SSL' => [ ],
82             'Progress::Any::Output' => [ ],
83             'Progress::Any::Output::TermProgressBarColor' => [ ],
84             'Data::Dumper' => [ ],
85             'HTML::TreeBuilder' => [ ],
86             'LWP::UserAgent' => [ ],
87             'LWP::UserAgent::ProgressAny' => [ ],
88             'HTTP::Request' => [ ],
89             'HTTP::Request::Common' => [ ],
90             'WWW::Mechanize' => [ ],
91             'Mozilla::CA' => [ ],
92             'HTML::Form' => [ ],
93             'Metabrik::File::Write' => [ ],
94             'Metabrik::System::File' => [ ],
95             'Metabrik::Network::Address' => [ ],
96             },
97             need_packages => {
98             freebsd => [ qw(p5-LWP-Protocol-https) ],
99             ubuntu => [ qw(liblwp-protocol-https-perl) ],
100             debian => [ qw(liblwp-protocol-https-perl) ],
101             kali => [ qw(liblwp-protocol-https-perl) ],
102             },
103             optional_modules => {
104             'WWW::Mechanize::PhantomJS' => [ ],
105             },
106             optional_binaries => {
107             phantomjs => [ ],
108             },
109             };
110             }
111              
112             sub create_user_agent {
113 0     0 0   my $self = shift;
114 0           my ($uri, $username, $password) = @_;
115              
116 0           $self->log->debug("create_user_agent: creating agent");
117              
118 0   0       $uri ||= $self->uri;
119              
120             # Use IO::Socket::SSL which supports timeouts among other things.
121 0           $ENV{PERL_NET_HTTPS_SSL_SOCKET_CLASS} = 'IO::Socket::SSL';
122              
123 0 0         my $ssl_verify = $self->ssl_verify
124             ? IO::Socket::SSL::SSL_VERIFY_PEER()
125             : IO::Socket::SSL::SSL_VERIFY_NONE();
126              
127 0           my %args = (
128             stack_depth => 0, # Default is infinite, and will eat-up whole memory.
129             # 0 means completely turn off the feature.
130             autocheck => 0, # Do not throw on error by checking HTTP code. Let us do it.
131             timeout => $self->rtimeout,
132             ssl_opts => {
133             verify_hostname => $self->ssl_verify,
134             SSL_verify_mode => $ssl_verify,
135             SSL_ca_file => Mozilla::CA::SSL_ca_file(),
136             # SNI support - defaults to PeerHost
137             # SSL_hostname => 'hostname',
138             },
139             );
140              
141 0           my $mechanize = 'WWW::Mechanize';
142 0 0         if ($self->do_javascript) {
143 0 0 0       if ($self->brik_has_module('WWW::Mechanize::PhantomJS')
144             && $self->brik_has_binary('phantomjs')) {
145 0           $mechanize = 'WWW::Mechanize::PhantomJS';
146             }
147             else {
148 0           return $self->log->error("create_user_agent: module [WWW::Mechanize::PhantomJS] not found, cannot do_javascript");
149             }
150             }
151 0 0 0       if ((! $self->do_redirects) && $mechanize eq 'WWW::Mechanize::PhantomJS') {
    0          
152 0           $self->log->warning("create_user_agent: module [WWW::Mechanize::PhantomJS] does ".
153             "not support do_redirects, won't use it.");
154             }
155             elsif ($self->do_redirects) {
156 0           $args{max_redirect} = $self->max_redirects;
157             }
158             else { # Follow redirects not wanted
159 0           $args{max_redirect} = 0;
160             }
161              
162 0           my $src_ip = $self->src_ip;
163 0 0         if (defined($src_ip)) {
164 0 0         my $na = Metabrik::Network::Address->new_from_brik_init($self) or return;
165 0 0         if (! $na->is_ip($src_ip)) {
166 0           return $self->log->error("create_user_agent: src_ip [$src_ip] is invalid");
167             }
168 0           $args{local_address} = $src_ip;
169             }
170              
171 0           my $mech = $mechanize->new(%args);
172 0 0         if (! defined($mech)) {
173 0           return $self->log->error("create_user_agent: unable to create WWW::Mechanize object");
174             }
175              
176 0 0         if ($self->user_agent) {
177 0           $mech->agent($self->user_agent);
178             }
179             else {
180             # Some WWW::Mechanize::* modules can't do that
181 0 0         if ($mech->can('agent_alias')) {
182 0           $mech->agent_alias('Linux Mozilla');
183             }
184             }
185              
186 0 0         $username = defined($username) ? $username : $self->username;
187 0 0         $password = defined($password) ? $password : $self->password;
188 0 0 0       if (defined($username) && defined($password)) {
189 0           $self->log->debug("create_user_agent: using Basic authentication");
190 0           $mech->cookie_jar({});
191 0           $mech->credentials($username, $password);
192             }
193              
194 0 0         if ($self->log->level > 2) {
195 0     0     $mech->add_handler("request_send", sub { shift->dump; return });
  0            
  0            
196 0     0     $mech->add_handler("response_done", sub { shift->dump; return });
  0            
  0            
197             }
198              
199 0           return $mech;
200             }
201              
202             sub reset_user_agent {
203 0     0 0   my $self = shift;
204              
205 0           $self->client(undef);
206              
207 0           return 1;
208             }
209              
210             sub _method {
211 0     0     my $self = shift;
212 0           my ($uri, $username, $password, $method, $data) = @_;
213              
214 0   0       $uri ||= $self->uri;
215 0 0         $self->brik_help_run_undef_arg($method, $uri) or return;
216              
217 0           $self->timeout(0);
218              
219 0 0         $username = defined($username) ? $username : $self->username;
220 0 0         $password = defined($password) ? $password : $self->password;
221 0           my $client = $self->client;
222 0 0         if (! defined($self->client)) {
223 0 0         $client = $self->create_user_agent($uri, $username, $password) or return;
224 0           $self->client($client);
225             }
226              
227 0           my $add_headers = $self->add_headers;
228 0 0         if (defined($add_headers)) {
229 0           for my $k (keys %$add_headers) {
230 0           my $v = $add_headers->{$k};
231 0 0         if (ref($v) eq 'ARRAY') {
232 0           my $this = join('; ', @$v);
233 0           $client->add_header($k => $this);
234             }
235             else {
236 0           $client->add_header($k => $v);
237             }
238             }
239             }
240              
241 0           $self->log->verbose("$method: $uri");
242              
243 0           my $response;
244 0           eval {
245 0 0 0       if ($method ne 'get' && ref($client) eq 'WWW::Mechanize::PhantomJS') {
246 0           return $self->log->error("$method: method not supported by WWW::Mechanize::PhantomJS");
247             }
248 0 0 0       if ($method eq 'post' || $method eq 'put') {
    0 0        
    0          
249 0           $response = $client->$method($uri, Content => $data);
250             }
251             elsif ($method eq 'patch') {
252             # https://stackoverflow.com/questions/23910962/how-to-send-a-http-patch-request-with-lwpuseragent
253 0           my $req = HTTP::Request::Common::PATCH($uri, [ %$data ]);
254 0           $response = $client->request($req);
255             }
256             elsif ($method eq 'options' || $method eq 'patch') {
257 0           my $req = HTTP::Request->new($method, $uri, $add_headers);
258 0           $response = $client->request($req);
259             }
260             else {
261 0           $response = $client->$method($uri);
262             }
263             };
264 0 0         if ($@) {
265 0           chomp($@);
266 0 0         if ($@ =~ /read timeout/i) {
267 0           $self->timeout(1);
268             }
269 0           return $self->log->error("$method: unable to use method [$method] to uri [$uri]: $@");
270             }
271              
272 0           $self->_last($response);
273              
274 0           my %r = ();
275 0           $r{code} = $response->code;
276 0 0         if (! $self->ignore_content) {
277 0 0         if ($self->do_javascript) {
278             # decoded_content method is available in WWW::Mechanize::PhantomJS
279             # but is available in HTTP::Request response otherwise.
280 0           $r{content} = $client->decoded_content;
281             }
282             else {
283 0           $r{content} = $response->decoded_content;
284             }
285             }
286              
287             # Error messages seen from IO::Socket::SSL module.
288 0 0         if ($r{content} =~ /^Can't connect to .+Connection timed out at /is) {
    0          
    0          
289 0           $self->timeout(1);
290 0           return $self->log->error("$method: $uri: connection timed out");
291             }
292             elsif ($r{content} =~ /^Can't connect to .+?\n\n(.+?) at /is) {
293 0           return $self->log->error("$method: $uri: ".lcfirst($1));
294             }
295             elsif ($r{content} =~ /^Connect failed: connect: Interrupted system call/i) {
296 0           return $self->log->error("$method: $uri: connection interrupted by syscall");
297             }
298              
299 0           my $headers = $response->headers;
300 0           $r{headers} = { map { $_ => $headers->{$_} } keys %$headers };
  0            
301 0           delete $r{headers}->{'::std_case'};
302              
303 0           return \%r;
304             }
305              
306             sub get {
307 0     0 0   my $self = shift;
308 0           my ($uri, $username, $password) = @_;
309              
310 0           return $self->_method($uri, $username, $password, 'get');
311             }
312              
313             sub cat {
314 0     0 0   my $self = shift;
315 0           my ($uri, $username, $password) = @_;
316              
317 0 0         $self->_method($uri, $username, $password, 'get') or return;
318 0           return $self->content;
319             }
320              
321             sub post {
322 0     0 0   my $self = shift;
323 0           my ($href, $uri, $username, $password) = @_;
324              
325 0 0         $self->brik_help_run_undef_arg('post', $href) or return;
326              
327 0           return $self->_method($uri, $username, $password, 'post', $href);
328             }
329              
330             sub put {
331 0     0 0   my $self = shift;
332 0           my ($href, $uri, $username, $password) = @_;
333              
334 0 0         $self->brik_help_run_undef_arg('put', $href) or return;
335              
336 0           return $self->_method($uri, $username, $password, 'put', $href);
337             }
338              
339             sub patch {
340 0     0 0   my $self = shift;
341 0           my ($href, $uri, $username, $password) = @_;
342              
343 0 0         $self->brik_help_run_undef_arg('patch', $href) or return;
344              
345 0           return $self->_method($uri, $username, $password, 'patch', $href);
346             }
347              
348             sub delete {
349 0     0 0   my $self = shift;
350 0           my ($uri, $username, $password) = @_;
351              
352 0           return $self->_method($uri, $username, $password, 'delete');
353             }
354              
355             sub options {
356 0     0 0   my $self = shift;
357 0           my ($uri, $username, $password) = @_;
358              
359 0           return $self->_method($uri, $username, $password, 'options');
360             }
361              
362             sub head {
363 0     0 0   my $self = shift;
364 0           my ($uri, $username, $password) = @_;
365              
366 0           return $self->_method($uri, $username, $password, 'head');
367             }
368              
369             sub code {
370 0     0 0   my $self = shift;
371              
372 0           my $last = $self->_last;
373 0 0         if (! defined($last)) {
374 0           return $self->log->error("code: you have to execute a request first");
375             }
376              
377 0           return $last->code;
378             }
379              
380             sub content {
381 0     0 0   my $self = shift;
382              
383 0           my $last = $self->_last;
384 0 0         if (! defined($last)) {
385 0           return $self->log->error("content: you have to execute a request first");
386             }
387              
388 0 0         if ($self->do_javascript) {
389             # decoded_content method is available in WWW::Mechanize::PhantomJS
390             # but is available in HTTP::Request response otherwise.
391 0           my $client = $self->client;
392 0           return $client->decoded_content;
393             }
394              
395 0           return $last->decoded_content;
396             }
397              
398             sub get_content {
399 0     0 0   my $self = shift;
400 0           my @args = @_;
401              
402 0 0         $self->get(@args) or return;
403 0           return $self->content;
404             }
405              
406             sub post_content {
407 0     0 0   my $self = shift;
408 0           my @args = @_;
409              
410 0 0         $self->post(@args) or return;
411 0           return $self->content;
412             }
413              
414             sub save_content {
415 0     0 0   my $self = shift;
416 0           my ($output) = @_;
417              
418 0           my $last = $self->_last;
419 0 0         if (! defined($last)) {
420 0           return $self->log->error("save_content: you have to execute a request first");
421             }
422              
423 0           eval {
424 0           $self->client->save_content($output);
425             };
426 0 0         if ($@) {
427 0           chomp($@);
428 0           return $self->log->error("save_content: unable to save content: $@");
429             }
430              
431 0           return 1;
432             }
433              
434             sub headers {
435 0     0 0   my $self = shift;
436              
437 0           my $last = $self->_last;
438 0 0         if (! defined($last)) {
439 0           return $self->log->error("headers: you have to execute a request first");
440             }
441              
442 0           return $last->headers;
443             }
444              
445             #
446             # Alias for headers Command
447             #
448             sub get_response_headers {
449 0     0 0   my $self = shift;
450              
451 0           return $self->headers;
452             }
453              
454             #
455             # Remove one header for next request.
456             #
457             sub delete_request_header {
458 0     0 0   my $self = shift;
459 0           my ($header) = @_;
460              
461 0 0         $self->brik_help_run_undef_arg('delete_header', $header) or return;
462              
463 0           my $headers = $self->add_headers;
464 0   0       my $value = $headers->{$header} || 'undef';
465 0           delete $headers->{$header};
466              
467 0           return $value;
468             }
469              
470             #
471             # Return one header from last response.
472             #
473             sub get_response_header {
474 0     0 0   my $self = shift;
475 0           my ($header) = @_;
476              
477 0 0         $self->brik_help_run_undef_arg('get_header', $header) or return;
478              
479 0 0         my $headers = $self->headers or return;
480 0 0         if (exists($headers->{$header})) {
481 0           return $headers->{$header};
482             }
483              
484 0           $self->log->verbose("get_header: header [$header] not found");
485              
486 0           return 0;
487             }
488              
489             #
490             # Set header for next request.
491             #
492             sub set_request_header {
493 0     0 0   my $self = shift;
494 0           my ($header, $value) = @_;
495              
496 0 0         $self->brik_help_run_undef_arg('set_request_header', $header) or return;
497 0 0         $self->brik_help_run_undef_arg('set_request_header', $value) or return;
498              
499 0           my $headers = $self->add_headers;
500 0           $headers->{$header} = $value;
501              
502 0           return $value;
503             }
504              
505             sub links {
506 0     0 0   my $self = shift;
507              
508 0           my $last = $self->_last;
509 0 0         if (! defined($last)) {
510 0           return $self->log->error("links: you have to execute a request first");
511             }
512              
513 0           my @links = ();
514 0           for my $l ($self->client->links) {
515 0           push @links, $l->url;
516 0           $self->log->verbose("links: found link [".$l->url."]");
517             }
518              
519 0           return \@links;
520             }
521              
522             sub forms {
523 0     0 0   my $self = shift;
524              
525 0           my $last = $self->_last;
526 0 0         if (! defined($last)) {
527 0           return $self->log->error("forms: you have to execute a request first");
528             }
529              
530 0           my $client = $self->client;
531              
532 0 0         if ($self->log->level > 2) {
533 0           print Data::Dumper::Dumper($last->headers)."\n";
534             }
535              
536             # We use our own "manual" way to get access to content:
537             # WWW::Mechanize::PhantomJS is clearly broken, and we have to support
538             # WWW::Mechanize also. At some point, we should write a good WWW::Mechanize::PhantomJS
539             # module.
540             #my @forms = $client->forms;
541 0 0         my $content = $self->content or return;
542 0           my @forms = HTML::Form->parse($content, $client->base);
543              
544 0           my @result = ();
545 0           for my $form (@forms) {
546 0   0       my $name = $form->{attr}{name} || 'undef';
547 0           my $action = $form->{action};
548 0   0       my $method = $form->{method} || 'undef';
549              
550 0           my $h = {
551             action => $action->as_string,
552             method => $method,
553             };
554              
555 0           for my $input (@{$form->{inputs}}) {
  0            
556 0   0       my $type = $input->{type} || '';
557 0   0       my $name = $input->{name} || '';
558 0   0       my $value = $input->{value} || '';
559 0 0         if ($type ne 'submit') {
560 0           $h->{input}{$name} = $value;
561             }
562             }
563              
564 0           push @result, $h;
565             }
566              
567 0           return \@result;
568             }
569              
570             sub trace_redirect {
571 0     0 0   my $self = shift;
572 0           my ($uri, $username, $password) = @_;
573              
574 0   0       $uri ||= $self->uri;
575 0 0         $self->brik_help_run_undef_arg('trace_redirect', $uri) or return;
576              
577 0           my $prev = $self->do_redirects;
578 0           $self->do_redirects(0);
579              
580 0           my @results = ();
581              
582 0           my $location = $uri;
583             # Max 20 redirects
584 0           for (1..20) {
585 0           $self->log->verbose("trace_redirect: $location");
586              
587 0           my $response;
588 0           eval {
589 0           $response = $self->get($location);
590             };
591 0 0         if ($@) {
592 0           chomp($@);
593 0           return $self->log->error("trace_redirect: unable to get uri [$uri]: $@");
594             }
595              
596 0           my $this = {
597             uri => $location,
598             code => $self->code,
599             };
600 0           push @results, $this;
601              
602 0 0 0       if ($this->{code} != 302 && $this->{code} != 301) {
603 0           last;
604             }
605              
606 0           $location = $this->{location} = $self->headers->{location};
607             }
608              
609 0           $self->do_redirects($prev);
610              
611 0           return \@results;
612             }
613              
614             sub screenshot {
615 0     0 0   my $self = shift;
616 0           my ($uri, $output) = @_;
617              
618 0 0         $self->brik_help_run_undef_arg('screenshot', $uri) or return;
619 0 0         $self->brik_help_run_undef_arg('screenshot', $output) or return;
620              
621 0 0 0       if ($self->brik_has_module('WWW::Mechanize::PhantomJS')
622             && $self->brik_has_binary('phantomjs')) {
623 0 0         my $mech = WWW::Mechanize::PhantomJS->new
624             or return $self->log->error("screenshot: PhantomJS failed");
625              
626 0 0         my $get = $mech->get($uri)
627             or return $self->log->error("screenshot: get uri [$uri] failed");
628              
629 0 0         my $data = $mech->content_as_png
630             or return $self->log->error("screenshot: content_as_png failed");
631              
632 0 0         my $write = Metabrik::File::Write->new_from_brik_init($self) or return;
633 0           $write->encoding('ascii');
634 0           $write->overwrite(1);
635 0           $write->append(0);
636              
637 0 0         $write->open($output) or return $self->log->error("screenshot: open failed");
638 0 0         $write->write($data) or return $self->log->error("screenshot: write failed");
639 0           $write->close;
640              
641 0           return $output;
642             }
643              
644 0           return $self->log->error("screenshot: optional module [WWW::Mechanize::PhantomJS] and optional binary [phantomjs] are not available");
645             }
646              
647             sub eval_javascript {
648 0     0 0   my $self = shift;
649 0           my ($js) = @_;
650              
651 0 0         $self->brik_help_run_undef_arg('eval_javascript', $js) or return;
652              
653             # Perl module Wight may also be an option.
654              
655 0 0 0       if ($self->brik_has_module('WWW::Mechanize::PhantomJS')
656             && $self->brik_has_binary('phantomjs')) {
657 0 0         my $mech = WWW::Mechanize::PhantomJS->new(launch_arg => ['ghostdriver/src/main.js'])
658             or return $self->log->error("eval_javascript: PhantomJS failed");
659              
660 0           return $mech->eval_in_page($js);
661             }
662              
663 0           return $self->log->error("eval_javascript: optional module [WWW::Mechanize::PhantomJS] ".
664             "and optional binary [phantomjs] are not available");
665             }
666              
667             sub info {
668 0     0 0   my $self = shift;
669 0           my ($uri) = @_;
670              
671 0   0       $uri ||= $self->uri;
672 0 0         $self->brik_help_run_undef_arg('info', $uri) or return;
673              
674 0 0         my $r = $self->get($uri) or return;
675 0           my $headers = $r->{headers};
676              
677             # Taken from apps.json from Wappalyzer
678 0           my @headers = qw(
679             IBM-Web2-Location
680             X-Drupal-Cache
681             X-Powered-By
682             X-Drectory-Script
683             Set-Cookie
684             X-Powered-CMS
685             X-KoobooCMS-Version
686             X-ATG-Version
687             User-Agent
688             X-Varnish
689             X-Compressed-By
690             X-Firefox-Spdy
691             X-ServedBy
692             MicrosoftSharePointTeamServices
693             Set-Cookie
694             Generator
695             X-CDN
696             Server
697             X-Tumblr-User
698             X-XRDS-Location
699             X-Content-Encoded-By
700             X-Ghost-Cache-Status
701             X-Umbraco-Version
702             X-Rack-Cache
703             Liferay-Portal
704             X-Flow-Powered
705             X-Swiftlet-Cache
706             X-Lift-Version
707             X-Spip-Cache
708             X-Wix-Dispatcher-Cache-Hit
709             COMMERCE-SERVER-SOFTWARE
710             X-AMP-Version
711             X-Powered-By-Plesk
712             X-Akamai-Transformed
713             X-Confluence-Request-Time
714             X-Mod-Pagespeed
715             Composed-By
716             Via
717             );
718              
719 0 0         if ($self->log->level > 2) {
720 0           print Data::Dumper::Dumper($headers)."\n";
721             }
722              
723 0           my %info = ();
724 0           for my $hdr (@headers) {
725 0           my $this = $headers->header(lc($hdr));
726 0 0         $info{$hdr} = $this if defined($this);
727             }
728              
729 0           my $title = $r->{title};
730 0 0         if (defined($title)) {
731 0           print "Title: $title\n";
732             }
733              
734 0           for my $k (sort { $a cmp $b } keys %info) {
  0            
735 0           print "$k: ".$info{$k}."\n";
736             }
737              
738 0           return 1;
739             }
740              
741             sub mirror {
742 0     0 0   my $self = shift;
743 0           my ($url, $output, $datadir) = @_;
744              
745 0   0       $datadir ||= $self->datadir;
746 0 0         $self->brik_help_run_undef_arg('mirror', $url) or return;
747 0 0         my $ref = $self->brik_help_run_invalid_arg('mirror', $url, 'SCALAR', 'ARRAY') or return;
748              
749 0           my @files = ();
750 0 0         if ($ref eq 'ARRAY') {
751 0 0         $self->brik_help_run_empty_array_arg('mirror', $url) or return;
752              
753 0           for my $this (@$url) {
754 0 0         my $file = $self->mirror($this, $output) or next;
755 0           push @files, @$file;
756             }
757             }
758             else {
759 0 0 0       if ($url !~ /^https?:\/\// && $url !~ /^ftp:\/\//) {
760 0           return $self->log->error("mirror: invalid URL [$url]");
761             }
762              
763 0 0         my $sf = Metabrik::System::File->new_from_brik_init($self) or return;
764 0 0         if (! defined($output)) {
765 0 0         my $filename = $sf->basefile($url) or return;
766 0           $output = $datadir.'/'.$filename;
767             }
768             else { # $output is defined
769 0 0         if (! $sf->is_absolute($output)) { # We want default datadir for output file
770 0           $output = $datadir.'/'.$output;
771             }
772             }
773              
774 0           $self->log->debug("mirror: url[$url] output[$output]");
775              
776 0 0         my $mech = $self->create_user_agent or return;
777 0           LWP::UserAgent::ProgressAny::__add_handlers($mech);
778 0           Progress::Any::Output->set("TermProgressBarColor");
779              
780 0           my $rc;
781 0           eval {
782 0           $rc = $mech->mirror($url, $output);
783             };
784 0 0         if ($@) {
785 0           chomp($@);
786 0           return $self->log->error("mirror: mirroring URL [$url] to local file [$output] failed: $@");
787             }
788 0           my $code = $rc->code;
789 0           $self->_last_code($code);
790 0 0         if ($code == 200) {
    0          
791 0           push @files, $output;
792 0           $self->log->verbose("mirror: downloading URL [$url] to local file [$output] done");
793             }
794             elsif ($code == 304) { # Not modified
795 0           $self->log->verbose("mirror: file [$output] not modified since last check");
796             }
797             else {
798 0           return $self->log->error("mirror: error while mirroring URL [$url] with code: [$code]");
799             }
800             }
801              
802 0           return \@files;
803             }
804              
805             sub parse {
806 0     0 0   my $self = shift;
807 0           my ($html) = @_;
808              
809 0 0         $self->brik_help_run_undef_arg('parse', $html) or return;
810              
811 0           return HTML::TreeBuilder->new_from_content($html);
812             }
813              
814             sub get_last {
815 0     0 0   my $self = shift;
816              
817 0           return $self->_last;
818             }
819              
820             sub get_last_code {
821 0     0 0   my $self = shift;
822              
823 0           return $self->_last_code;
824             }
825              
826             1;
827              
828             __END__