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