File Coverage

blib/lib/CPAN/Testers/Common/Client/Config.pm
Criterion Covered Total %
statement 162 291 55.6
branch 48 140 34.2
condition 12 55 21.8
subroutine 33 49 67.3
pod 15 15 100.0
total 270 550 49.0


line stmt bran cond sub pod time code
1             package CPAN::Testers::Common::Client::Config;
2 5     5   580 use strict;
  5         5  
  5         115  
3 5     5   15 use warnings;
  5         7  
  5         91  
4              
5 5     5   16 use Carp ();
  5         6  
  5         52  
6 5     5   16 use File::Glob ();
  5         3  
  5         72  
7 5     5   16 use File::Spec 3.19 ();
  5         108  
  5         86  
8 5     5   1837 use File::HomeDir 0.58 ();
  5         16086  
  5         107  
9 5     5   26 use File::Path qw( mkpath );
  5         7  
  5         242  
10 5     5   3035 use IPC::Cmd;
  5         194496  
  5         15445  
11              
12             sub new {
13 1     1 1 597 my ($class, %args) = @_;
14 1         4 my $self = bless {
15             _prompt => undef,
16             _warn => undef,
17             _print => undef,
18             _config => {},
19             }, $class;
20              
21 1 50       3 my $warn = exists $args{'warn'} ? $args{'warn'} : \&CORE::warn;
22 1 50       3 $self->_set_mywarn( $warn )
23             or Carp::croak q(the 'warn' parameter must be a coderef);
24              
25 1 50   0   3 my $print = exists $args{'print'} ? $args{'print'} : sub { print @_ };
  0         0  
26 1 50       2 $self->_set_myprint( $print )
27             or Carp::croak q(the 'print' parameter must be a coderef);
28              
29             # prompt is optional
30 1 50       2 if (exists $args{'prompt'}) {
31 1 50       2 $self->_set_myprompt( $args{'prompt'} )
32             or Carp::croak q(the 'prompt' parameter must be a coderef);
33             }
34              
35 1         3 return $self;
36             }
37              
38             sub read {
39 1     1 1 674 my $self = shift;
40 1 50       3 my $config = $self->_read_config_file or return;
41 1         4 my $options = $self->_get_config_options( $config );
42 1         3 $self->_config_data( $options );
43 1         3 return 1;
44             }
45              
46             #######################
47             ### basic accessors ###
48             #######################
49              
50 1     1 1 399 sub email_from { return shift->{_config}{email_from} }
51 0     0 1 0 sub edit_report { return shift->_config_data_for('edit_report', @_) }
52 0     0 1 0 sub send_report { return shift->_config_data_for('send_report', @_) }
53 0     0 1 0 sub send_duplicates { return shift->_config_data_for('send_duplicates', @_) }
54 0     0 1 0 sub transport { return shift->{_config}{transport} }
55 1     1 1 4 sub transport_name { return shift->{_transport_name} }
56 2     2 1 491 sub transport_args { return shift->{_transport_args} }
57              
58             sub get_config_dir {
59 21 100 66 21 1 397 if ( defined $ENV{PERL_CPAN_REPORTER_DIR} &&
60             length $ENV{PERL_CPAN_REPORTER_DIR}
61             ) {
62 2         16 return $ENV{PERL_CPAN_REPORTER_DIR};
63             }
64              
65 19         83 my $conf_dir = File::Spec->catdir(File::HomeDir->my_home, ".cpanreporter");
66              
67 19 50       326 if ($^O eq 'MSWin32') {
68 0         0 my $alt_dir = File::Spec->catdir(File::HomeDir->my_documents, ".cpanreporter");
69 0 0 0     0 $conf_dir = $alt_dir if -d $alt_dir && ! -d $conf_dir;
70             }
71              
72 19         89 return $conf_dir;
73             }
74              
75             sub get_config_filename {
76 1 50 33 1 1 5 if ( defined $ENV{PERL_CPAN_REPORTER_CONFIG} &&
77             length $ENV{PERL_CPAN_REPORTER_CONFIG}
78             ) {
79 0         0 return $ENV{PERL_CPAN_REPORTER_CONFIG};
80             }
81             else {
82 1         2 return File::Spec->catdir( get_config_dir, 'config.ini' );
83             }
84             }
85              
86             # the provided subrefs do not know about $self.
87 2     2 1 691 sub mywarn { my $r = shift->{_warn}; return $r->(@_) }
  2         7  
88 1     1 1 620 sub myprint { my $r = shift->{_print}; return $r->(@_) }
  1         3  
89 1     1 1 1154 sub myprompt { my $r = shift->{_prompt}; return $r->(@_) }
  1         3  
90 0     0   0 sub _has_prompt { return exists $_[0]->{_prompt} }
91              
92             sub setup {
93 0     0 1 0 my $self = shift;
94              
95 0 0       0 Carp::croak q{please provide a 'prompt' coderef to new()}
96             unless $self->_has_prompt;
97              
98 0         0 my $config_dir = $self->get_config_dir;
99 0 0       0 mkpath $config_dir unless -d $config_dir;
100              
101 0 0       0 unless ( -d $config_dir ) {
102 0         0 $self->myprint(
103             "\nCPAN Testers: couldn't create configuration directory '$config_dir': $!"
104             );
105 0         0 return;
106             }
107              
108 0         0 my $config_file = $self->get_config_filename;
109              
110             # explain grade:action pairs to the user
111 0         0 $self->myprint( _grade_action_prompt() );
112              
113 0         0 my ($config, $existing_options) = ( {}, {} );
114              
115             # read or create the config file
116 0 0       0 if ( -f $config_file ) {
117 0         0 $self->myprint("\nCPAN Testers: found your config file at:\n$config_file\n");
118              
119             # bail out if we can't read it
120 0         0 $existing_options = $self->_read_config_file;
121 0 0       0 if ( !$existing_options ) {
122 0         0 $self->mywarn("\nCPAN Testers: configuration will not be changed\n");
123 0         0 return;
124             }
125              
126 0         0 $self->myprint("\nCPAN Testers: Updating your configuration settings:\n");
127             }
128             else {
129 0         0 $self->myprint("\nCPAN Testers: no config file found; creating a new one.\n");
130             }
131              
132 0         0 my %spec = $self->_config_spec;
133              
134 0         0 foreach my $k ( $self->_config_order ) {
135 0         0 my $option_data = $spec{$k};
136 0         0 $self->myprint("\n$option_data->{info}\n");
137              
138             # options with defaults are mandatory
139 0 0       0 if (defined $option_data->{default}) {
140              
141             # as a side-effect, people may use '' without
142             # an actual default value to mark the option
143             # as mandatory. So we only show de default value
144             # if there is one.
145 0 0       0 if (length $option_data->{default}) {
146 0         0 $self->myprint("(Recommended: '$option_data->{default}')\n\n");
147             }
148             # repeat until validated
149             PROMPT:
150 0   0     0 while ( defined (
151             my $answer = $self->myprompt(
152             "$k?",
153             $existing_options->{$k} || $option_data->{default}
154             )
155             )) {
156             # TODO: I don't think _validate() is being used
157             # because of this. Should we remove it?
158 0 0 0     0 if ( ! $option_data->{validate} ||
159             $option_data->{validate}->($self, $k, $answer, $config)
160             ) {
161 0         0 $config->{$k} = $answer;
162 0         0 last PROMPT;
163             }
164             }
165             }
166             else {
167             # only initialize options without defaults if the answer
168             # matches non white space and validates properly.
169             # Otherwise, just ignore it.
170 0   0     0 my $answer = $self->myprompt("$k?", $existing_options->{$k} || q{});
171 0 0       0 if ( $answer =~ /\S/ ) {
172 0         0 $config->{$k} = $answer;
173             }
174             }
175             # delete existing keys as we proceed so we know what's left
176 0         0 delete $existing_options->{$k};
177             }
178              
179             # initialize remaining options
180             $self->myprint(
181 0 0       0 "\nYour CPAN Testers config file also contains these advanced options\n\n"
182             ) if keys %$existing_options;
183              
184 0         0 foreach my $k ( keys %$existing_options ) {
185 0         0 $config->{$k} = $self->myprompt("$k?", $existing_options->{$k});
186             }
187              
188 0         0 $self->myprint("\nCPAN Testers: writing config file to '$config_file'.\n");
189 0 0       0 if ( $self->_write_config_file( $config ) ) {
190 0         0 $self->_config_data( $config );
191 0         0 return $config;
192             }
193             else {
194 0         0 return;
195             }
196             }
197              
198             #--------------------------------------------------------------------------#
199             # _config_spec -- returns configuration options information
200             #
201             # Keys include
202             # default -- recommended value, used in prompts and as a fallback
203             # if an options is not set; mandatory if defined
204             # prompt -- short prompt for EU::MM prompting
205             # info -- long description shown before prompting
206             # validate -- CODE ref; return normalized option or undef if invalid
207             #--------------------------------------------------------------------------#
208             sub _config_spec {
209             return (
210 1     1   34 email_from => {
211             default => '',
212             prompt => 'What email address will be used to reference your reports?',
213             validate => \&_validate_email,
214             info => <<'HERE',
215             CPAN Testers requires a valid email address to identify senders
216             in the body of a test report. Please use a standard email format
217             like: "John Doe"
218             HERE
219             },
220             smtp_server => {
221             default => undef, # (deprecated)
222             prompt => "[DEPRECATED] It's safe to remove this from your config file.",
223             },
224             edit_report => {
225             default => 'default:ask/no pass/na:no',
226             prompt => 'Do you want to review or edit the test report?',
227             validate => \&_validate_grade_action_pair,
228             info => <<'HERE',
229             Before test reports are sent, you may want to review or edit the test
230             report and add additional comments about the result or about your system
231             or Perl configuration. By default, we will ask after each report is
232             generated whether or not you would like to edit the report. This option
233             takes "grade:action" pairs.
234             HERE
235             },
236             send_report => {
237             default => 'default:ask/yes pass/na:yes',
238             prompt => 'Do you want to send the report?',
239             validate => \&_validate_grade_action_pair,
240             info => <<'HERE',
241             By default, we will prompt you for confirmation that the test report
242             should be sent before actually doing it. This gives the opportunity to
243             skip sending particular reports if you need to (e.g. if you caused the
244             failure). This option takes "grade:action" pairs.
245             HERE
246             },
247             transport => {
248             default => 'Metabase uri https://metabase.cpantesters.org/api/v1/ id_file metabase_id.json',
249             prompt => 'Which transport system will be used to transmit the reports?',
250             validate => \&_validate_transport,
251             info => <<'HERE',
252             CPAN Testers gets your reports over HTTPS using Metabase. This option lets
253             you set a different uri, transport mechanism and metabase profile path. If you
254             are receiving HTTPS errors, you may change the uri to use plain HTTP, though
255             this is not recommended. Unless you know what you're doing, just accept
256             the default value.
257             HERE
258             },
259             send_duplicates => {
260             default => 'default:no',
261             prompt => 'This report is identical to a previous one. Send it anyway?',
262             validate => \&_validate_grade_action_pair,
263             info => <<'HERE',
264             CPAN Testers records tests grades for each distribution, version and
265             platform. By default, duplicates of previous results will not be sent at
266             all, regardless of the value of the "send_report" option. This option takes
267             "grade:action" pairs.
268             HERE
269             },
270             send_PL_report => {
271             prompt => 'Do you want to send the PL report?',
272             default => undef,
273             validate => \&_validate_grade_action_pair,
274             },
275             send_make_report => {
276             prompt => 'Do you want to send the make/Build report?',
277             default => undef,
278             validate => \&_validate_grade_action_pair,
279             },
280             send_test_report => {
281             prompt => 'Do you want to send the test report?',
282             default => undef,
283             validate => \&_validate_grade_action_pair,
284             },
285             send_skipfile => {
286             prompt => "What file has patterns for things that shouldn't be reported?",
287             default => undef,
288             validate => \&_validate_skipfile,
289             },
290             cc_skipfile => {
291             prompt => "What file has patterns for things that shouldn't CC to authors?",
292             default => undef,
293             validate => \&_validate_skipfile,
294             },
295             command_timeout => {
296             prompt => 'If no timeout is set by CPAN, halt system commands after how many seconds?',
297             default => undef,
298             validate => \&_validate_seconds,
299             },
300             email_to => {
301             default => undef,
302             validate => \&_validate_email,
303             },
304             editor => {
305             default => undef,
306             },
307             debug => {
308             default => undef,
309             },
310             retry_submission => {
311             default => undef,
312             },
313             );
314             }
315              
316             #--------------------------------------------------------------------------#
317             # _config_order -- determines order of interactive config. Only items
318             # in interactive config will be written to a starter config file
319             #--------------------------------------------------------------------------#
320             sub _config_order {
321 0     0   0 return qw(
322             email_from
323             edit_report
324             send_report
325             transport
326             );
327             }
328              
329              
330             sub _set_myprompt {
331 1     1   1 my ($self, $prompt) = @_;
332 1 50 33     7 if ($prompt and ref $prompt and ref $prompt eq 'CODE') {
      33        
333 1         13 $self->{_prompt} = $prompt;
334 1         2 return $self;
335             }
336 0         0 return;
337             }
338              
339             sub _set_mywarn {
340 1     1   1 my ($self, $warn) = @_;
341 1 50 33     13 if ($warn and ref $warn and ref $warn eq 'CODE') {
      33        
342 1         4 $self->{_warn} = $warn;
343 1         3 return $self;
344             }
345 0         0 return;
346             }
347              
348             sub _set_myprint {
349 1     1   1 my ($self, $print) = @_;
350 1 50 33     10 if ($print and ref $print and ref $print eq 'CODE') {
      33        
351 1         1 $self->{_print} = $print;
352 1         3 return $self;
353             }
354 0         0 return;
355             }
356              
357             # _read_config_file() is a trimmed down version of
358             # Adam Kennedy's great Config::Tiny to fit our needs
359             # (while also avoiding the extra toolchain dep).
360             sub _read_config_file {
361 1     1   1 my $self = shift;
362 1         2 my $file = $self->get_config_filename;
363              
364             # check the file
365 1 50       18 return $self->_config_error("File '$file' does not exist") unless -e $file;
366 1 50       3 return $self->_config_error("'$file' is a directory, not a file") unless -f _;
367 1 50       3 return $self->_config_error("Insufficient permissions to read '$file'") unless -r _;
368              
369 1 50       22 open my $fh, '<', $file
370             or return $self->_config_error("Failed to open file '$file': $!");
371 1         1 my $contents = do { local $/; <$fh> };
  1         3  
  1         17  
372 1         6 close $fh;
373              
374 1         1 my $config = {};
375 1         2 my $counter = 0;
376 1         28 foreach my $line ( split /(?:\015{1,2}\012|\015|\012)/, $contents ) {
377 4         4 $counter++;
378 4 50       8 next if $line =~ /^\s*(?:\#|\;|$)/; # skip comments and empty lines
379 4         4 $line =~ s/\s\;\s.+$//g; # remove inline comments
380              
381             # handle properties
382 4 50       23 if ( $line =~ /^\s*([^=]+?)\s*=\s*(.*?)\s*$/ ) {
383 4         7 $config->{$1} = $2;
384 4         5 next;
385             }
386              
387 0         0 return $self->_config_error(
388             "Syntax error in config file '$file' at line $counter: '$_'"
389             );
390             }
391 1         5 return $config;
392             }
393              
394             sub _write_config_file {
395 0     0   0 my ($self, $config) = @_;
396              
397 0         0 my $contents = '';
398 0         0 foreach my $item ( sort keys %$config ) {
399 0 0       0 if ( $config->{$item} =~ /(?:\012|\015)/s ) {
400 0         0 return $self->_config_error("Illegal newlines in option '$item'");
401             }
402 0         0 $contents .= "$item=$config->{$item}\n";
403             }
404              
405 0         0 my $file = $self->get_config_filename;
406 0 0       0 open my $fh, '>', $file
407             or return $self->_config_error("Error writing config file '$file': $!");
408              
409 0         0 print $fh $contents;
410 0         0 close $fh;
411             }
412              
413             sub _config_error {
414 0     0   0 my ($self, $msg) = @_;
415 0         0 $self->mywarn( "\nCPAN Testers: $msg\n" );
416 0         0 return;
417             }
418              
419             sub _config_data {
420 2     2   2 my ($self, $config) = @_;
421 2 100       4 $self->{_config} = $config if $config;
422 2         2 return $self->{_config};
423             }
424              
425             sub _config_data_for {
426 0     0   0 my ($self, $type, $grade) = @_;
427 0         0 my %spec = $self->_config_spec;
428 0 0       0 my $data = exists $self->{_config}{$type} ? $self->{_config}{$type} : q();
429              
430 0         0 my $dispatch = $spec{$type}{validate}->(
431             $self,
432             $type,
433             join( q{ }, 'default:no', $data )
434             );
435 0   0     0 return lc( $dispatch->{$grade} || $dispatch->{default} );
436             }
437              
438             # extract and return valid options,
439             # with fallback to defaults
440             sub _get_config_options {
441 1     1   2 my ($self, $config) = @_;
442 1         2 my %spec = $self->_config_spec;
443              
444 1         3 my %active;
445 1         3 OPTION: foreach my $option (keys %spec) {
446 16 100       17 if (exists $config->{$option} ) {
447 4         4 my $val = $config->{$option};
448 4 50 33     11 if ( $spec{$option}{validate}
449             && !$spec{$option}{validate}->($self, $option, $val)
450             ) {
451 0         0 $self->mywarn( "\nCPAN Testers: invalid option '$val' in '$option'. Using default value instead.\n\n" );
452 0         0 $active{$option} = $spec{$option}{default};
453 0         0 next OPTION;
454             }
455 4         8 $active{$option} = $val;
456             }
457             else {
458             $active{$option} = $spec{$option}{default}
459 12 100       19 if defined $spec{$option}{default};
460             }
461             }
462 1         7 return \%active;
463             }
464              
465             #--------------------------------------------------------------------------#
466             # _normalize_id_file
467             #--------------------------------------------------------------------------#
468              
469             sub _normalize_id_file {
470 3     3   770 my ($self, $id_file) = @_;
471              
472             # if file path is enclosed in quotes, remove them:
473 3         11 $id_file =~ s/\A(['"])(.+)\1\z/$2/;
474              
475             # Windows does not use ~ to signify a home directory
476 3 50 33     15 if ( $^O eq 'MSWin32' && $id_file =~ m{^~/(.*)} ) {
    100          
477 0         0 $id_file = File::Spec->catdir(File::HomeDir->my_home, $1);
478             }
479             elsif ( $id_file =~ /~/ ) {
480 1         46 $id_file = File::Spec->canonpath(File::Glob::bsd_glob( $id_file ));
481             }
482 3 100       24 unless ( File::Spec->file_name_is_absolute( $id_file ) ) {
483 1         2 $id_file = File::Spec->catfile(
484             $self->get_config_dir, $id_file
485             );
486             }
487 3         5 return $id_file;
488             }
489              
490             sub _generate_profile {
491 0     0   0 my ($id_file, $email) = @_;
492              
493 0         0 my $cmd = IPC::Cmd::can_run('metabase-profile');
494 0 0       0 return unless $cmd;
495              
496             # XXX this is an evil assumption about email addresses, but
497             # might do for simple cases that users might actually provide
498              
499 0         0 my @opts = ("--output" => $id_file);
500              
501 0 0       0 if ($email =~ /\A(.+)\s+<([^>]+)>\z/ ) {
502 0         0 push @opts, "--email" => $2;
503 0         0 my $name = $1;
504 0         0 $name =~ s/\A["'](.*)["']\z/$1/;
505 0 0       0 push ( @opts, "--name" => $1)
506             if length $name;
507             }
508             else {
509 0         0 push @opts, "--email" => $email;
510             }
511              
512             # XXX profile 'secret' is really just a generated API key, so we
513             # can create something fairly random for the user and use that
514 0         0 push @opts, "--secret" => sprintf("%08x", rand(2**31));
515              
516 0         0 return scalar IPC::Cmd::run(
517             command => [ $cmd, @opts ],
518             verbose => 1,
519             );
520             }
521              
522             sub _grade_action_prompt {
523 0     0   0 return << 'HERE';
524              
525             Some of the following configuration options require one or more "grade:action"
526             pairs that determine what grade-specific action to take for that option.
527             These pairs should be space-separated and are processed left-to-right. See
528             CPAN::Testers::Common::Client::Config documentation for more details.
529              
530             GRADE : ACTION ======> EXAMPLES
531             ------- ------- --------
532             pass yes default:no
533             fail no default:yes pass:no
534             unknown ask/no default:ask/no pass:yes fail:no
535             na ask/yes
536             default
537              
538             HERE
539             }
540              
541             sub _is_valid_action {
542 4     4   4 my $action = shift;
543 4         5 my @valid_actions = qw{ yes no ask/yes ask/no ask };
544 4         4 return grep { $action eq $_ } @valid_actions;
  20         20  
545             }
546              
547              
548             sub _is_valid_grade {
549 6     6   5 my $grade = shift;
550 6         8 my @valid_grades = qw{ pass fail unknown na default };
551 6         4 return grep { $grade eq $_ } @valid_grades;
  30         37  
552             }
553              
554             #--------------------------------------------------------------------------#
555             # _validate
556             #
557             # anything is OK if there is no validation subroutine
558             #--------------------------------------------------------------------------#
559              
560             sub _validate {
561 0     0   0 my ($self, $name, $value) = @_;
562 0         0 my $specs = $self->_config_spec;
563 0 0       0 return 1 if ! exists $specs->{$name}{validate};
564 0         0 return $specs->{$name}{validate}->($self, $name, $value);
565             }
566              
567             #--------------------------------------------------------------------------#
568             # _validate_grade_action
569             # returns hash of grade => action
570             # returns undef
571             #--------------------------------------------------------------------------#
572              
573             sub _validate_grade_action_pair {
574 2     2   3 my ($self, $name, $option) = @_;
575 2   50     4 $option ||= 'no';
576              
577 2         2 my %ga_map; # grade => action
578              
579 2         4 PAIR: for my $grade_action ( split q{ }, $option ) {
580 4         3 my ($grade_list,$action);
581 4 50       10 if ( $grade_action =~ m{.:.} ) {
    0          
    0          
    0          
582             # parse pair for later check
583 4         10 ($grade_list, $action) = $grade_action =~ m{\A([^:]+):(.+)\z};
584             }
585             elsif ( _is_valid_action($grade_action) ) {
586             # action by itself
587 0         0 $ga_map{default} = $grade_action;
588 0         0 next PAIR;
589             }
590             elsif ( _is_valid_grade($grade_action) ) {
591             # grade by itself
592 0         0 $ga_map{$grade_action} = 'yes';
593 0         0 next PAIR;
594             }
595             elsif( $grade_action =~ m{./.} ) {
596             # gradelist by itself, so setup for later check
597 0         0 $grade_list = $grade_action;
598 0         0 $action = 'yes';
599             }
600             else {
601             # something weird, so warn and skip
602 0         0 $self->mywarn(
603             "\nignoring invalid grade:action '$grade_action' for '$name'.\n\n"
604             );
605 0         0 next PAIR;
606             }
607              
608             # check gradelist
609 4         7 my %grades = map { ($_,1) } split( "/", $grade_list);
  6         11  
610 4         6 for my $g ( keys %grades ) {
611 6 50       7 if ( ! _is_valid_grade($g) ) {
612 0         0 $self->mywarn(
613             "\nignoring invalid grade '$g' in '$grade_action' for '$name'.\n\n"
614             );
615 0         0 delete $grades{$g};
616             }
617             }
618              
619             # check action
620 4 50       5 if ( ! _is_valid_action($action) ) {
621 0         0 $self->mywarn(
622             "\nignoring invalid action '$action' in '$grade_action' for '$name'.\n\n"
623             );
624 0         0 next PAIR;
625             }
626              
627             # otherwise, it all must be OK
628 4         11 $ga_map{$_} = $action for keys %grades;
629             }
630              
631 2 50       13 return scalar(keys %ga_map) ? \%ga_map : undef;
632             }
633              
634             sub _validate_transport {
635 1     1   2 my ($self, $name, $option, $config) = @_;
636 1 50       4 $config = $self->_config_data unless $config;
637 1         5 my $transport = '';
638 1         2 my $transport_args = '';
639              
640 1 50       5 if ( $option =~ /^(\w+(?:::\w+)*)\s*(\S.*)$/ ) {
641 1         3 ($transport, $transport_args) = ($1, $2);
642 1         2 my $full_class = "Test::Reporter::Transport::$transport";
643 1     1   82 eval "use $full_class ()";
  1         213  
  0            
  0            
644 1 50       5 if ($@) {
645 1         4 $self->mywarn(
646             "\nerror loading $full_class. Please install the missing module or choose a different transport mechanism.\n\n"
647             );
648             }
649             }
650             else {
651 0         0 $self->mywarn(
652             "\nPlease provide a transport mechanism.\n\n"
653             );
654 0         0 return;
655             }
656              
657             # we do extra validation for Metabase and offer to create the profile
658 1 50       293 if ( $transport eq 'Metabase' ) {
659 1 50       7 unless ( $transport_args =~ /\buri\s+\S+/ ) {
660 0         0 $self->mywarn(
661             "\nPlease provide a target uri.\n\n"
662             );
663 0         0 return;
664             }
665              
666 1 50       6 unless ( $transport_args =~ /\bid_file\s+(\S.+?)\s*$/ ) {
667 0         0 $self->mywarn(
668             "\nPlease specify an id_file path.\n\n"
669             );
670 0         0 return;
671             }
672              
673 1         3 my $id_file = $self->_normalize_id_file($1);
674              
675             # Offer to create if it doesn't exist
676 1 50       20 if ( ! -e $id_file ) {
    50          
677 0 0       0 return unless $self->_has_prompt; # skip unless we have a prompt!
678              
679 0         0 my $answer = $self->myprompt(
680             "\nWould you like to run 'metabase-profile' now to create '$id_file'?", "y"
681             );
682 0 0       0 if ( $answer =~ /^y/i ) {
683 0 0       0 return unless _generate_profile( $id_file, $config->{email_from} );
684             }
685             else {
686 0         0 $self->mywarn( <<"END_ID_FILE" );
687             You can create a Metabase profile by typing 'metabase-profile' in your
688             command prompt and moving the resulting file to the location you specified.
689             If you did not specify an absolute path, put it in your .cpanreporter
690             directory. You will need to do this before continuing.
691             END_ID_FILE
692 0         0 return;
693             }
694             }
695             # Warn and fail validation if there but not readable
696             elsif (! -r $id_file) {
697 0         0 $self->mywarn(
698             "'$id_file' was not readable.\n\n"
699             );
700 0         0 return;
701             }
702              
703             # when we store the transport args internally,
704             # we should use the normalized id_file
705             # (always quoted to support spaces).
706 1         9 $transport_args =~ s/(\bid_file\s+)(\S.+?)\s*$/$1"$id_file"/;
707             } # end Metabase
708              
709 1         2 $self->{_transport_name} = $transport;
710 1         3 $self->{_transport_args} = _parse_transport_args($transport_args);
711 1         4 return 1;
712             }
713              
714             # converts a string into a list of arguments for the transport module.
715             # arguments are separated by spaces. If an argument has space, enclose it
716             # using ' or ".
717             sub _parse_transport_args {
718 7     7   599 my ($transport_args) = @_;
719 7         7 my @args;
720 7         40 while ($transport_args =~ /\s*((?:[^'"\s]\S*)|(["'])(?:\\?+.)*?\2)/g) {
721 29         35 my $arg = $1;
722 29 100       39 if ($2) {
723 12         32 $arg =~ s/\A(['"])(.+)\1\z/$2/;
724 12         16 $arg =~ s/\\(.)/$1/g;
725             }
726 29         83 push @args, $arg;
727             }
728 7         30 return \@args;
729             }
730              
731             sub _validate_seconds {
732 0     0   0 my ($self, $name, $option) = @_;
733 0 0 0     0 return unless defined($option) && length($option)
      0        
      0        
734             && ($option =~ /^\d/) && $option >= 0;
735 0         0 return $option;
736             }
737              
738             sub _validate_skipfile {
739 0     0   0 my ($self, $name, $option) = @_;
740 0 0       0 return unless $option;
741 0 0       0 my $skipfile = File::Spec->file_name_is_absolute( $option )
742             ? $option : File::Spec->catfile( get_config_dir(), $option );
743 0 0       0 return -r $skipfile ? $skipfile : undef;
744             }
745              
746             # not really a validation, just making sure
747             # it's not empty and contains a '@'
748             sub _validate_email {
749 1     1   1 my ($self, $name, $option) = @_;
750 1 50       2 return unless $option;
751 1         3 my @data = split '@', $option;
752 1 50       5 return $option if scalar @data == 2;
753             }
754              
755              
756             1;
757             __END__