File Coverage

blib/lib/CPAN/Testers/Common/Client/Config.pm
Criterion Covered Total %
statement 164 294 55.7
branch 50 142 35.2
condition 12 55 21.8
subroutine 33 50 66.0
pod 15 15 100.0
total 274 556 49.2


line stmt bran cond sub pod time code
1             package CPAN::Testers::Common::Client::Config;
2 5     5   692 use strict;
  5         12  
  5         173  
3 5     5   24 use warnings;
  5         9  
  5         107  
4              
5 5     5   20 use Carp ();
  5         8  
  5         87  
6 5     5   46 use File::Glob ();
  5         11  
  5         104  
7 5     5   21 use File::Spec 3.19 ();
  5         163  
  5         126  
8 5     5   1878 use File::HomeDir 0.58 ();
  5         19068  
  5         107  
9 5     5   27 use File::Path qw( mkpath );
  5         8  
  5         204  
10 5     5   2643 use IPC::Cmd;
  5         201552  
  5         18104  
11              
12             sub new {
13 1     1 1 589 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   0   4 my $warn = exists $args{'warn'} ? $args{'warn'} : sub { warn @_ };
  0         0  
22 1 50       2 $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 630 my $self = shift;
40 1 50       3 my $config = $self->_read_config_file or return;
41 1         3 my $options = $self->_get_config_options( $config );
42 1         3 $self->_config_data( $options );
43 1         19 return 1;
44             }
45              
46             #######################
47             ### basic accessors ###
48             #######################
49              
50 1     1 1 440 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 431 sub transport_args { return shift->{_transport_args} }
57              
58             sub get_config_dir {
59 21 100 66 21 1 313 if ( defined $ENV{PERL_CPAN_REPORTER_DIR} &&
60             length $ENV{PERL_CPAN_REPORTER_DIR}
61             ) {
62 2         20 return $ENV{PERL_CPAN_REPORTER_DIR};
63             }
64              
65 19         75 my $conf_dir = File::Spec->catdir(File::HomeDir->my_home, ".cpanreporter");
66              
67 19 50       342 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         98 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 782 sub mywarn { my $r = shift->{_warn}; return $r->(@_) }
  2         5  
88 1     1 1 681 sub myprint { my $r = shift->{_print}; return $r->(@_) }
  1         3  
89 1     1 1 1401 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   27 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   2 my ($self, $prompt) = @_;
332 1 50 33     5 if ($prompt and ref $prompt and ref $prompt eq 'CODE') {
      33        
333 1         2 $self->{_prompt} = $prompt;
334 1         2 return $self;
335             }
336 0         0 return;
337             }
338              
339             sub _set_mywarn {
340 1     1   2 my ($self, $warn) = @_;
341 1 50 33     8 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   2 my ($self, $print) = @_;
350 1 50 33     8 if ($print and ref $print and ref $print eq 'CODE') {
      33        
351 1         2 $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       6 return $self->_config_error("Insufficient permissions to read '$file'") unless -r _;
368              
369 1 50       30 open my $fh, '<', $file
370             or return $self->_config_error("Failed to open file '$file': $!");
371 1         2 my $contents = do { local $/; <$fh> };
  1         4  
  1         25  
372 1         13 close $fh;
373              
374 1         3 my $config = {};
375 1         1 my $counter = 0;
376 1         26 foreach my $line ( split /(?:\015{1,2}\012|\015|\012)/, $contents ) {
377 4         5 $counter++;
378 4 50       13 next if $line =~ /^\s*(?:\#|\;|$)/; # skip comments and empty lines
379 4         7 $line =~ s/\s\;\s.+$//g; # remove inline comments
380              
381             # handle properties
382 4 50       24 if ( $line =~ /^\s*([^=]+?)\s*=\s*(.*?)\s*$/ ) {
383 4         9 $config->{$1} = $2;
384 4         12 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       12 $self->{_config} = $config if $config;
422 2         5 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         3 my %spec = $self->_config_spec;
443              
444 1         3 my %active;
445 1         4 OPTION: foreach my $option (keys %spec) {
446 16 100       31 if (exists $config->{$option} ) {
447 4         5 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         12 $active{$option} = $val;
456             }
457             else {
458             $active{$option} = $spec{$option}{default}
459 12 100       22 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   877 my ($self, $id_file) = @_;
471              
472             # if file path is enclosed in quotes, remove them:
473 3 100       15 if ($id_file =~ s/\A(['"])(.+)\1\z/$2/) {
474 1         2 $id_file =~ s/\\(.)/$1/g;
475             }
476              
477             # Windows does not use ~ to signify a home directory
478 3 50 33     13 if ( $^O eq 'MSWin32' && $id_file =~ m{^~/(.*)} ) {
    100          
479 0         0 $id_file = File::Spec->catdir(File::HomeDir->my_home, $1);
480             }
481             elsif ( $id_file =~ /~/ ) {
482 1         50 $id_file = File::Spec->canonpath(File::Glob::bsd_glob( $id_file ));
483             }
484 3 100       24 unless ( File::Spec->file_name_is_absolute( $id_file ) ) {
485 1         3 $id_file = File::Spec->catfile(
486             $self->get_config_dir, $id_file
487             );
488             }
489 3         9 return $id_file;
490             }
491              
492             sub _generate_profile {
493 0     0   0 my ($id_file, $email) = @_;
494              
495 0         0 my $cmd = IPC::Cmd::can_run('metabase-profile');
496 0 0       0 return unless $cmd;
497              
498             # XXX this is an evil assumption about email addresses, but
499             # might do for simple cases that users might actually provide
500              
501 0         0 my @opts = ("--output" => $id_file);
502              
503 0 0       0 if ($email =~ /\A(.+)\s+<([^>]+)>\z/ ) {
504 0         0 push @opts, "--email" => $2;
505 0         0 my $name = $1;
506 0         0 $name =~ s/\A["'](.*)["']\z/$1/;
507 0 0       0 push ( @opts, "--name" => $1)
508             if length $name;
509             }
510             else {
511 0         0 push @opts, "--email" => $email;
512             }
513              
514             # XXX profile 'secret' is really just a generated API key, so we
515             # can create something fairly random for the user and use that
516 0         0 push @opts, "--secret" => sprintf("%08x", rand(2**31));
517              
518 0         0 return scalar IPC::Cmd::run(
519             command => [ $cmd, @opts ],
520             verbose => 1,
521             );
522             }
523              
524             sub _grade_action_prompt {
525 0     0   0 return << 'HERE';
526              
527             Some of the following configuration options require one or more "grade:action"
528             pairs that determine what grade-specific action to take for that option.
529             These pairs should be space-separated and are processed left-to-right. See
530             CPAN::Testers::Common::Client::Config documentation for more details.
531              
532             GRADE : ACTION ======> EXAMPLES
533             ------- ------- --------
534             pass yes default:no
535             fail no default:yes pass:no
536             unknown ask/no default:ask/no pass:yes fail:no
537             na ask/yes
538             default
539              
540             HERE
541             }
542              
543             sub _is_valid_action {
544 4     4   5 my $action = shift;
545 4         7 my @valid_actions = qw{ yes no ask/yes ask/no ask };
546 4         4 return grep { $action eq $_ } @valid_actions;
  20         32  
547             }
548              
549              
550             sub _is_valid_grade {
551 6     6   8 my $grade = shift;
552 6         9 my @valid_grades = qw{ pass fail unknown na default };
553 6         10 return grep { $grade eq $_ } @valid_grades;
  30         55  
554             }
555              
556             #--------------------------------------------------------------------------#
557             # _validate
558             #
559             # anything is OK if there is no validation subroutine
560             #--------------------------------------------------------------------------#
561              
562             sub _validate {
563 0     0   0 my ($self, $name, $value) = @_;
564 0         0 my $specs = $self->_config_spec;
565 0 0       0 return 1 if ! exists $specs->{$name}{validate};
566 0         0 return $specs->{$name}{validate}->($self, $name, $value);
567             }
568              
569             #--------------------------------------------------------------------------#
570             # _validate_grade_action
571             # returns hash of grade => action
572             # returns undef
573             #--------------------------------------------------------------------------#
574              
575             sub _validate_grade_action_pair {
576 2     2   5 my ($self, $name, $option) = @_;
577 2   50     4 $option ||= 'no';
578              
579 2         2 my %ga_map; # grade => action
580              
581 2         5 PAIR: for my $grade_action ( split q{ }, $option ) {
582 4         7 my ($grade_list,$action);
583 4 50       13 if ( $grade_action =~ m{.:.} ) {
    0          
    0          
    0          
584             # parse pair for later check
585 4         14 ($grade_list, $action) = $grade_action =~ m{\A([^:]+):(.+)\z};
586             }
587             elsif ( _is_valid_action($grade_action) ) {
588             # action by itself
589 0         0 $ga_map{default} = $grade_action;
590 0         0 next PAIR;
591             }
592             elsif ( _is_valid_grade($grade_action) ) {
593             # grade by itself
594 0         0 $ga_map{$grade_action} = 'yes';
595 0         0 next PAIR;
596             }
597             elsif( $grade_action =~ m{./.} ) {
598             # gradelist by itself, so setup for later check
599 0         0 $grade_list = $grade_action;
600 0         0 $action = 'yes';
601             }
602             else {
603             # something weird, so warn and skip
604 0         0 $self->mywarn(
605             "\nignoring invalid grade:action '$grade_action' for '$name'.\n\n"
606             );
607 0         0 next PAIR;
608             }
609              
610             # check gradelist
611 4         9 my %grades = map { ($_,1) } split( "/", $grade_list);
  6         15  
612 4         9 for my $g ( keys %grades ) {
613 6 50       11 if ( ! _is_valid_grade($g) ) {
614 0         0 $self->mywarn(
615             "\nignoring invalid grade '$g' in '$grade_action' for '$name'.\n\n"
616             );
617 0         0 delete $grades{$g};
618             }
619             }
620              
621             # check action
622 4 50       8 if ( ! _is_valid_action($action) ) {
623 0         0 $self->mywarn(
624             "\nignoring invalid action '$action' in '$grade_action' for '$name'.\n\n"
625             );
626 0         0 next PAIR;
627             }
628              
629             # otherwise, it all must be OK
630 4         13 $ga_map{$_} = $action for keys %grades;
631             }
632              
633 2 50       10 return scalar(keys %ga_map) ? \%ga_map : undef;
634             }
635              
636             sub _validate_transport {
637 1     1   3 my ($self, $name, $option, $config) = @_;
638 1 50       3 $config = $self->_config_data unless $config;
639 1         1 my $transport = '';
640 1         2 my $transport_args = '';
641              
642 1 50       5 if ( $option =~ /^(\w+(?:::\w+)*)\s*(\S.*)$/ ) {
643 1         4 ($transport, $transport_args) = ($1, $2);
644 1         2 my $full_class = "Test::Reporter::Transport::$transport";
645 1     1   65 eval "use $full_class ()";
  1         156  
  0            
  0            
646 1 50       4 if ($@) {
647 1         5 $self->mywarn(
648             "\nerror loading $full_class. Please install the missing module or choose a different transport mechanism.\n\n"
649             );
650             }
651             }
652             else {
653 0         0 $self->mywarn(
654             "\nPlease provide a transport mechanism.\n\n"
655             );
656 0         0 return;
657             }
658              
659             # we do extra validation for Metabase and offer to create the profile
660 1 50       241 if ( $transport eq 'Metabase' ) {
661 1 50       6 unless ( $transport_args =~ /\buri\s+\S+/ ) {
662 0         0 $self->mywarn(
663             "\nPlease provide a target uri.\n\n"
664             );
665 0         0 return;
666             }
667              
668 1 50       6 unless ( $transport_args =~ /\bid_file\s+(\S.+?)\s*$/ ) {
669 0         0 $self->mywarn(
670             "\nPlease specify an id_file path.\n\n"
671             );
672 0         0 return;
673             }
674              
675 1         3 my $id_file = $self->_normalize_id_file($1);
676              
677             # Offer to create if it doesn't exist
678 1 50       27 if ( ! -e $id_file ) {
    50          
679 0 0       0 return unless $self->_has_prompt; # skip unless we have a prompt!
680              
681 0         0 my $answer = $self->myprompt(
682             "\nWould you like to run 'metabase-profile' now to create '$id_file'?", "y"
683             );
684 0 0       0 if ( $answer =~ /^y/i ) {
685 0 0       0 return unless _generate_profile( $id_file, $config->{email_from} );
686             }
687             else {
688 0         0 $self->mywarn( <<"END_ID_FILE" );
689             You can create a Metabase profile by typing 'metabase-profile' in your
690             command prompt and moving the resulting file to the location you specified.
691             If you did not specify an absolute path, put it in your .cpanreporter
692             directory. You will need to do this before continuing.
693             END_ID_FILE
694 0         0 return;
695             }
696             }
697             # Warn and fail validation if there but not readable
698             elsif (! -r $id_file) {
699 0         0 $self->mywarn(
700             "'$id_file' was not readable.\n\n"
701             );
702 0         0 return;
703             }
704              
705             # when we store the transport args internally,
706             # we should use the normalized id_file
707             # (always quoted to support spaces).
708             # Since _normalize_id_file removed '\' from the path in order
709             # to test the real file path, we now need to put them back if we
710             # have them, as _parse_transport_args expects '\\' instead of '\':
711 1         3 $id_file =~ s/\\/\\\\/g;
712 1         9 $transport_args =~ s/(\bid_file\s+)(\S.+?)\s*$/$1"$id_file"/;
713             } # end Metabase
714              
715 1         3 $self->{_transport_name} = $transport;
716 1         2 $self->{_transport_args} = _parse_transport_args($transport_args);
717 1         4 return 1;
718             }
719              
720             # converts a string into a list of arguments for the transport module.
721             # arguments are separated by spaces. If an argument has space, enclose it
722             # using ' or ".
723             sub _parse_transport_args {
724 7     7   477 my ($transport_args) = @_;
725 7         11 my @args;
726 7         37 while ($transport_args =~ /\s*((?:[^'"\s]\S*)|(["'])(?:(?>\\?).)*?\2)/g) {
727 29         47 my $arg = $1;
728 29 100       54 if ($2) {
729 12         40 $arg =~ s/\A(['"])(.+)\1\z/$2/;
730 12         23 $arg =~ s/\\(.)/$1/g;
731             }
732 29         99 push @args, $arg;
733             }
734 7         38 return \@args;
735             }
736              
737             sub _validate_seconds {
738 0     0   0 my ($self, $name, $option) = @_;
739 0 0 0     0 return unless defined($option) && length($option)
      0        
      0        
740             && ($option =~ /^\d/) && $option >= 0;
741 0         0 return $option;
742             }
743              
744             sub _validate_skipfile {
745 0     0   0 my ($self, $name, $option) = @_;
746 0 0       0 return unless $option;
747 0 0       0 my $skipfile = File::Spec->file_name_is_absolute( $option )
748             ? $option : File::Spec->catfile( get_config_dir(), $option );
749 0 0       0 return -r $skipfile ? $skipfile : undef;
750             }
751              
752             # not really a validation, just making sure
753             # it's not empty and contains a '@'
754             sub _validate_email {
755 1     1   2 my ($self, $name, $option) = @_;
756 1 50       3 return unless $option;
757 1         3 my @data = split '@', $option;
758 1 50       12 return $option if scalar @data == 2;
759             }
760              
761              
762             1;
763             __END__