File Coverage

blib/lib/App/Fetchware.pm
Criterion Covered Total %
statement 492 982 50.1
branch 202 468 43.1
condition 17 78 21.7
subroutine 94 121 77.6
pod 76 76 100.0
total 881 1725 51.0


line stmt bran cond sub pod time code
1             package App::Fetchware;
2             our $VERSION = '1.016'; # VERSION: generated by DZP::OurPkgVersion
3             # ABSTRACT: App::Fetchware is Fetchware's API used to make extensions.
4             ###BUGALERT### Uses die instead of croak. croak is the preferred way of throwing
5             #exceptions in modules. croak says that the caller was the one who caused the
6             #error not the specific code that actually threw the error.
7 50     50   82551 use strict;
  50         73  
  50         1516  
8 50     50   180 use warnings;
  50         62  
  50         1536  
9              
10             # CPAN modules making Fetchwarefile better.
11 50     50   942 use File::Spec::Functions qw(catfile splitpath splitdir file_name_is_absolute);
  50         1149  
  50         2871  
12 50     50   919 use Path::Class;
  50         57792  
  50         2016  
13 50     50   1188 use Data::Dumper;
  50         9177  
  50         1775  
14 50     50   1003 use File::Copy 'cp';
  50         3102  
  50         1630  
15 50     50   31450 use HTML::TreeBuilder;
  50         1150126  
  50         442  
16 50     50   1905 use Scalar::Util qw(blessed looks_like_number);
  50         77  
  50         3303  
17 50     50   27292 use Digest::SHA;
  50         101551  
  50         2019  
18 50     50   286 use Digest::MD5;
  50         76  
  50         1324  
19             #use Crypt::OpenPGP::KeyRing;
20             #use Crypt::OpenPGP;
21 50     50   1427 use Archive::Tar;
  50         116754  
  50         2463  
22 50     50   27596 use Archive::Zip qw(:ERROR_CODES :CONSTANTS);
  50         1624600  
  50         6443  
23 50     50   349 use Cwd 'cwd';
  50         185  
  50         1744  
24 50     50   1017 use Sub::Mage;
  50         10110  
  50         551  
25 50     50   8858 use URI::Split qw(uri_split uri_join);
  50         3439  
  50         3505  
26 50     50   1026 use Text::ParseWords 'quotewords';
  50         1772  
  50         1820  
27 50     50   188 use File::Temp 'tempfile';
  50         54  
  50         1629  
28 50     50   22201 use Term::ReadLine;
  50         95047  
  50         1286  
29 50     50   20603 use Term::UI;
  50         837719  
  50         1873  
30              
31 50     50   2148 use App::Fetchware::Util ':UTIL';
  50         77  
  50         11970  
32 50     50   303 use App::Fetchware::Config ':CONFIG';
  50         73  
  50         5547  
33              
34             # Enable Perl 6 knockoffs, and use 5.10.1, because smartmatching and other
35             # things in 5.10 were changed in 5.10.1+.
36 50     50   1402 use 5.010001;
  50         154  
37              
38             # Set up Exporter to bring App::Fetchware's API to everyone who use's it
39             # including fetchware's ability to let you rip into its guts, and customize it
40             # as you need.
41 50     50   233 use Exporter qw( import );
  50         53  
  50         509596  
42             # By default fetchware exports its configuration file like subroutines.
43             #
44             # These days popular dogma considers it bad to import stuff without being asked
45             # to do so, but App::Fetchware is meant to be a configuration file that is both
46             # human readable, and most importantly flexible enough to allow customization.
47             # This is done by making the configuration file a perl source code file called a
48             # Fetchwarefile that fetchware simply executes with eval.
49             our @EXPORT = qw(
50             program
51             filter
52             temp_dir
53             fetchware_db_path
54             user
55             prefix
56             configure_options
57             make_options
58             build_commands
59             install_commands
60             uninstall_commands
61             lookup_url
62             lookup_method
63             gpg_keys_url
64             gpg_sig_url
65             sha1_url
66             md5_url
67             user_agent
68             verify_method
69             no_install
70             verify_failure_ok
71             user_keyring
72             stay_root
73             mirror
74             config
75              
76             new
77             new_install
78             check_syntax
79             start
80             lookup
81             download
82             verify
83             unarchive
84             build
85             install
86             end
87             uninstall
88             upgrade
89              
90             hook
91             );
92              
93             # These tags allow you to replace some or all of fetchware's default behavior to
94             # install unusual software.
95             our %EXPORT_TAGS = (
96             # No OVERRIDE_START OVERRIDE_END because start() does *not* use any helper
97             # subs that could be beneficial to override()rs.
98             OVERRIDE_NEW => [qw(
99             extension_name
100             fetchwarefile_name
101             opening_message
102             get_lookup_url
103             download_lookup_url
104             get_mirrors
105             get_verification
106             get_filter_option
107             append_to_fetchwarefile
108             prompt_for_other_options
109             append_options_to_fetchwarefile
110             edit_manually
111             )],
112             OVERRIDE_NEW_INSTALL => [qw(
113             ask_to_install_now_to_test_fetchwarefile
114             )],
115             OVERRIDE_CHECK_SYNTAX => [qw(
116             check_config_options
117             )],
118             OVERRIDE_LOOKUP => [qw(
119             get_directory_listing
120             parse_directory_listing
121             determine_download_path
122             ftp_parse_filelist
123             http_parse_filelist
124             file_parse_filelist
125             lookup_by_timestamp
126             lookup_by_versionstring
127             lookup_determine_downloadpath
128             )],
129             OVERRIDE_DOWNLOAD => [qw(
130             determine_package_path
131             )],
132             OVERRIDE_VERIFY => [qw(
133             gpg_verify
134             sha1_verify
135             md5_verify
136             digest_verify
137             )],
138             OVERRIDE_UNARCHIVE => [qw(
139             check_archive_files
140             list_files
141             list_files_tar
142             list_files_zip
143             unarchive_package
144             unarchive_tar
145             unarchive_zip
146             )],
147             OVERRIDE_BUILD => [qw(
148             run_star_commands
149             run_configure
150             )],
151             OVERRIDE_INSTALL => [qw(
152             chdir_unless_already_at_path
153             )],
154             OVERRIDE_UNINSTALL => [qw()],
155             OVERRIDE_UPGRADE => [qw()],
156             );
157             # OVERRIDE_ALL is simply all other tags combined.
158             @{$EXPORT_TAGS{OVERRIDE_ALL}} = map {@{$_}} values %EXPORT_TAGS;
159             # *All* entries in @EXPORT_TAGS must also be in @EXPORT_OK.
160             our @EXPORT_OK = @{$EXPORT_TAGS{OVERRIDE_ALL}};
161              
162              
163              
164              
165             ###BUGALERT### Add strict argument checking to App::Fetchware's API subroutines
166             #to check for not being called correctly to aid extension debugging.
167              
168              
169              
170             ###BUGALERT### Recommend installing http://gpg4win.org if you use fetchware on
171             # Windows so you have gpg support.
172              
173              
174              
175              
176              
177              
178             # _make_config_sub() is an internal subroutine that only App::Fetchware and
179             # App::Fetchware::CreateConfigOptions should use. Use
180             # App::Fetchware::CreateConfigOptions to create any configuration option
181             # subroutines that you want your fetchware extensions to have.
182             #=head2 _make_config_sub()
183             #
184             # _make_config_sub($name, $one_or_many_values)
185             #
186             #A function factory that builds many functions that are the exact same, but have
187             #different names. It supports three types of functions determined by
188             #_make_config_sub()'s second parameter. It's first parameter is the name of that
189             #function. This is the subroutine that builds all of Fetchwarefile's
190             #configuration subroutines such as lookupurl, mirror, fetchware, etc....
191             #
192             #=over
193             #=item LIMITATION
194             #
195             #_make_config_sub() creates subroutines that have prototypes, but in order for
196             #perl to honor those prototypes perl B know about them at compile-time;
197             #therefore, that is why _make_config_sub() must be called inside a C block.
198             #
199             #=back
200             #
201             #=over
202             #=item NOTE
203             #_make_config_sub() uses caller to determine the package that _make_config_sub()
204             #was called from. This package is then prepended to the string that is eval'd to
205             #create the designated subroutine in the caller's package. This is needed so that
206             #App::Fetchware "subclasses" can import this function, and enjoy its simple
207             #interface to create custom configuration subroutines.
208             #
209             #=back
210             #
211             #=over
212             #
213             #=item $one_or_many_values Supported Values
214             #
215             #=over
216             #
217             #=item * 'ONE'
218             #
219             #Generates a function with the name of _make_config_sub()'s first parameter that
220             #can B be called one time per Fetchwarefile. If called more than one time
221             #will die with an error message.
222             #
223             #Function created with C<$CONFIG{$name} = $value;> inside the generated function that
224             #is named $name.
225             #
226             #=item * 'ONEARRREF'
227             #
228             #Generates a function with the name of _make_config_sub()'s first parameter that
229             #can B be called one time per Fetchwarefile. And just like C<'ONE'> above
230             #if called more than once it will throw an exception. However, C<'ONEARRREF'> can
231             #be called with a list of values just like C<'MANY'> can, but it can still only
232             #be called once like C<'ONE'>.
233             #
234             #=item * 'MANY'
235             #
236             #Generates a function with the name of _make_config_sub()'s first parameter that
237             #can be called more than just once. This option is only used by fetchware's
238             #C API call.
239             #
240             #Function created with C inside the generated function that
241             #is named $name.
242             #
243             #=item * 'BOOLEAN'
244             #
245             #Generates a function with the name of _make_config_sub()'s first parameter that
246             #can be called only once just like 'ONE' can be, but it also only support true or
247             #false values. What is true and false is the same as in perl, with the exception
248             #that /false/i and /off/i are also false.
249             #
250             #Function created the same way as 'ONE''s are, but with /false/i and /off/i
251             #mutated into a Perl accepted false value (they're turned into zeros.).
252             #
253             #=back
254             #
255             #=back
256             #
257             #All API subroutines fetchware provides to Fetchwarefile's are generated by
258             #_make_config_sub() except for fetchware() and override().
259             #
260             #=cut
261              
262             my @api_functions = (
263             [ program => 'ONE' ],
264             [ filter => 'ONE' ],
265             [ temp_dir => 'ONE' ],
266             [ fetchware_db_path => 'ONE' ],
267             [ user => 'ONE' ],
268             [ prefix => 'ONE' ],
269             [ configure_options=> 'ONEARRREF' ],
270             [ make_options => 'ONEARRREF' ],
271             [ build_commands => 'ONEARRREF' ],
272             [ install_commands => 'ONEARRREF' ],
273             [ uninstall_commands => 'ONEARRREF' ],
274             [ lookup_url => 'ONE' ],
275             [ lookup_method => 'ONE' ],
276             [ gpg_keys_url => 'ONE' ],
277             [ gpg_sig_url => 'ONE' ],
278             [ sha1_url => 'ONE' ],
279             [ md5_url => 'ONE' ],
280             [ user_agent => 'ONE' ],
281             [ verify_method => 'ONE' ],
282             [ mirror => 'MANY' ],
283             [ no_install => 'BOOLEAN' ],
284             [ verify_failure_ok => 'BOOLEAN' ],
285             [ stay_root => 'BOOLEAN' ],
286             [ user_keyring => 'BOOLEAN' ],
287             );
288              
289              
290             # Loop over the list of options needed by _make_config_sub() to generated the
291             # needed API functions for Fetchwarefile.
292             for my $api_function (@api_functions) {
293             _make_config_sub(@{$api_function});
294             }
295              
296              
297             sub _make_config_sub {
298 1276     1276   1265 my ($name, $one_or_many_values, $callers_package) = @_;
299              
300             # Obtain caller's package name, so that the new configuration subroutine
301             # can be created in the caller's package instead of our own. Use the
302             # specifed $callers_package if the caller specified one. This allows
303             # create_config_options() to reuse _make_config_sub() by passing in its
304             # caller to _make_config_sub().
305 1276   66     3966 my $package = $callers_package // caller;
306              
307 1276 50       1743 die <
308             App-Fetchware: internal syntax error: _make_config_sub() was called without a
309             name. It must receive a name parameter as its first paramter. See perldoc
310             App::Fetchware.
311             EOD
312 1276 50 100     4161 unless ($one_or_many_values eq 'ONE'
      100        
      66        
313             or $one_or_many_values eq 'ONEARRREF',
314             or $one_or_many_values eq 'MANY'
315             or $one_or_many_values eq 'BOOLEAN') {
316 0         0 die <
317             App-Fetchware: internal syntax error: _make_config_sub() was called without a
318             one_or_many_values parameter as its second parameter. Or the parameter it was
319             called with was invalid. Only 'ONE', 'MANY', and 'BOOLEAN' are acceptable
320             values. See perldoc App::Fetchware.
321             EOD
322             }
323              
324 1276 100       2422 if ($one_or_many_values eq 'ONE') {
    100          
    100          
    50          
325 724         669 my $eval = <<'EOE';
326             package $package;
327              
328             sub $name (@) {
329             my $value = shift;
330            
331             die <
332             App-Fetchware: internal syntax error: $name was called more than once in this
333             Fetchwarefile. Currently only mirror supports being used more than once in a
334             Fetchwarefile, but you have used $name more than once. Please remove all calls
335             to $name but one. See perldoc App::Fetchware.
336             EOD
337             unless (@_) {
338             config('$name', $value);
339             } else {
340             die <
341             App-Fetchware: internal syntax error. $name was called with more than one
342             option. $name only supports just one option such as '$name 'option';'. It does
343             not support more than one option such as '$name 'option', 'another option';'.
344             Please chose one option not both, or combine both into one option. See perldoc
345             App::Fetchware.
346             EOD
347             }
348             }
349             1; # return true from eval
350             EOE
351 724         5140 $eval =~ s/\$name/$name/g;
352 724         1820 $eval =~ s/\$package/$package/g;
353 724 50   2 1 68211 eval $eval or die <
  2 50   171 1 39  
  2 100   2 1 4  
  2 50   2 1 4  
  1 100   2 1 3  
  1 50   171 1 4  
  171 100   2 1 370  
  171 50   2 1 444  
  171 100   171 1 457  
  170 50   2 1 431  
  1 100   5 1 5  
  2 50   2 1 36  
  2 100   0 1 5  
  2 50   171 1 5  
  1 100   0 1 2  
  1 50   0 1 4  
  2 100   0   35  
  2 50   0   5  
  2 100   0   4  
  1 50   0   2  
  1 100   0   4  
  2 50   0   36  
  2 100   0   5  
  2 50   1   4  
  1 100   1   2  
  1 0   1   4  
  171 0   1   343  
  171 50   1   374  
  171 100   1   462  
  170 0   1   367  
  1 0   1   4  
  2 0   1   35  
  2 0       5  
  2 0       5  
  1 0       2  
  1 0       6  
  2 0       36  
  2 0       4  
  2 0       5  
  1 0       2  
  1 0       4  
  171 0       950  
  171 0       896  
  171 0       473  
  170 0       398  
  1 0       6  
  2 0       36  
  2 50       4  
  2 50       5  
  1 50       3  
  1 50       4  
  5 50       694  
  5 50       13  
  5 50       10  
  4 50       66  
  1 50       4  
  2 50       37  
  2 50       4  
  2 50       5  
  1 50       3  
  1 50       4  
  0 50       0  
  0 50       0  
  0 50       0  
  0 50       0  
  0         0  
  171         343  
  171         398  
  171         425  
  170         365  
  1         48  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  1         793  
  1         5  
  1         4  
  1         3  
  0         0  
  1         756  
  1         3  
  1         3  
  1         3  
  0         0  
  1         517  
  1         5  
  1         4  
  1         3  
  0         0  
  1         260  
  1         3  
  1         3  
  1         3  
  0         0  
  1         221  
  1         4  
  1         3  
  1         9  
  0         0  
  1         226  
  1         3  
  1         3  
  1         3  
  0         0  
  1         223  
  1         4  
  1         4  
  1         2  
  0         0  
  1         217  
  1         3  
  1         3  
  1         3  
  0         0  
  1         224  
  1         4  
  1         3  
  1         3  
  0            
354             1App-Fetchware: internal operational error: _make_config_sub()'s internal eval()
355             call failed with the exception [$@]. See perldoc App::Fetchware.
356             EOD
357             } elsif ($one_or_many_values eq 'ONEARRREF') {
358 265         204 my $eval = <<'EOE';
359             package $package;
360              
361             sub $name (@) {
362             my $value = shift;
363            
364             die <
365             App-Fetchware: internal syntax error: $name was called more than once in this
366             Fetchwarefile. Currently only mirror supports being used more than once in a
367             Fetchwarefile, but you have used $name more than once. Please remove all calls
368             to $name but one. See perldoc App::Fetchware.
369             EOD
370             unless (@_) {
371             config('$name', $value);
372             } else {
373             config('$name', $value, @_);
374             }
375             }
376             1; # return true from eval
377             EOE
378 265         1497 $eval =~ s/\$name/$name/g;
379 265         706 $eval =~ s/\$package/$package/g;
380 265 50   2 1 22657 eval $eval or die <
  2 50   2 1 34  
  2 100   2 1 4  
  2 50   2 1 4  
  1 100   2 1 2  
  1 50       2  
  2 100       211  
  2 50       6  
  2 100       5  
  1 50       2  
  1 100       3  
  2         33  
  2         5  
  2         4  
  1         2  
  1         3  
  2         35  
  2         5  
  2         5  
  1         2  
  1         2  
  2         240  
  2         6  
  2         6  
  1         4  
  1         3  
381             2App-Fetchware: internal operational error: _make_config_sub()'s internal eval()
382             call failed with the exception [$@]. See perldoc App::Fetchware.
383             EOD
384             } elsif ($one_or_many_values eq 'MANY') {
385 68         127 my $eval = <<'EOE';
386             package $package;
387              
388             sub $name (@) {
389             my $value = shift;
390              
391             # Support multiple arguments specified on the same line. like:
392             # mirror 'http://djfjf.com/a', 'ftp://kdjfjkl.net/b';
393             unless (@_) {
394             config('$name', $value);
395             } else {
396             config('$name', $value, @_);
397             }
398             }
399             1; # return true from eval
400             EOE
401 68         290 $eval =~ s/\$name/$name/g;
402 68         178 $eval =~ s/\$package/$package/g;
403 68 50   176 1 4738 eval $eval or die <
  176 100       1089  
  176         391  
  175         424  
  2         7  
404             3App-Fetchware: internal operational error: _make_config_sub()'s internal eval()
405             call failed with the exception [\$@]. See perldoc App::Fetchware.
406             EOD
407             } elsif ($one_or_many_values eq 'BOOLEAN') {
408 219         218 my $eval = <<'EOE';
409             package $package;
410              
411             sub $name (@) {
412             my $value = shift;
413              
414             die <
415             App-Fetchware: internal syntax error: $name was called more than once in this
416             Fetchwarefile. Currently only mirror supports being used more than once in a
417             Fetchwarefile, but you have used $name more than once. Please remove all calls
418             to $name but one. See perldoc App::Fetchware.
419             EOD
420             # Make extra false values false (0). Not needed for true values, because
421             # everything but 0, '', and undef are true values.
422             if ($value =~ /false/i) {
423             $value = 0;
424             } elsif ($value =~ /off/i) {
425             $value = 0;
426             }
427              
428             unless (@_) {
429             config('$name', $value);
430             } else {
431             die <
432             App-Fetchware: internal syntax error. $name was called with more than one
433             option. $name only supports just one option such as '$name 'option';'. It does
434             not support more than one option such as '$name 'option', 'another option';'.
435             Please chose one option not both, or combine both into one option. See perldoc
436             App::Fetchware.
437             EOD
438             }
439             }
440             1; # return true from eval
441             EOE
442 219         1667 $eval =~ s/\$name/$name/g;
443 219         644 $eval =~ s/\$package/$package/g;
444 219 50   7 1 26242 eval $eval or die <
  6 100   4 1 935  
  7 50   4 1 233  
  7 100   4 1 25  
  1 100       4  
  1 100       3  
  6 50       14  
  6 100       228  
  2 100       7  
  4 100       254  
  4 100       10  
  3 100       12  
  1 100       216  
  1 100       3  
  4 100       10  
  3 100       7  
  1 100       4  
  4         469  
  4         10  
  4         16  
  1         3  
  0         0  
  3         5  
  2         5  
  1         4  
  3         256  
  3         7  
  3         11  
  0         0  
  0         0  
  3         6  
  2         4  
  1         4  
445             4App-Fetchware: internal operational error: _make_config_sub()'s internal eval()
446             call failed with the exception [\$@]. See perldoc App::Fetchware.
447             EOD
448             }
449             }
450              
451              
452              
453              
454              
455              
456             sub new {
457 2     1 1 437 my ($term, $program_name) = @_;
458              
459             # Instantiate a new Fetchwarefile object for managing and generating a
460             # Fetchwarefile, which we'll write to a file for the user or use to
461             # build a associated Fetchware package.
462 2         7 my $now = localtime;
463 2         9 my $fetchwarefile = App::Fetchware::Fetchwarefile->new(
464             header => <
465             use App::Fetchware;
466             # Auto generated $now by fetchware's new command.
467             # However, feel free to edit this file if fetchware's new command's
468             # autoconfiguration is not enough.
469             #
470             # Please look up fetchware's documentation of its configuration file syntax at
471             # perldoc App::Fetchware, and only if its configuration file syntax is not
472             # malleable enough for your application should you resort to customizing
473             # fetchware's behavior. For extra flexible customization see perldoc
474             # App::Fetchware.
475             EOF
476             descriptions => {
477              
478             program => <
479             program simply names the program the Fetchwarefile is responsible for
480             downloading, building, and installing.
481             EOA
482             filter => <
483             filter specifies a program name and/or version number that tells fetchware
484             which program and or which version of a program you want fetchware to install.
485             This is *only* needed in cases where there are multiple programs and or
486             multiple versions of the same program in the directory lookup_url specifies.
487             EOA
488             temp_dir => <
489             temp_dir specifies what temporary directory fetchware will use to download and
490             build this program.
491             EOA
492             user => <
493             user specifes a user that fetchware will drop priviledges to when fetchware
494             downloads and builds your software. It will then switch back to root privs, if
495             run as root, and install your software system wide. This does not work on
496             Windows.
497             EOA
498             fetchware_database_path => <
499             fetchware_database_path specifies an alternate path for fetchware to use to
500             store the fetchware package that 'fetchware install' creates, and that
501             'fetchware upgrade' uses to upgrade this fetchware package.
502             EOA
503             prefix => <
504             prefix specifies what base path your software will be installed under. This
505             only works for software that uses GNU AutoTools to configure itself, it uses
506             ./configure.
507             EOA
508             configure_options => <
509             configure_options specifes what options fetchware should pass to ./configure
510             when it configures your software. This option only works for software that
511             uses GNU AutoTools.
512             EOA
513             make_options => <
514             make_options specifes what options fetchware should pass to make when make is
515             run to build and install your software.
516             EOA
517             build_commands => <
518             build_commands specifies what commands fetchware should execute to build your
519             software.
520             EOA
521             install_commands => <
522             install_commands specifies what commands fetchware should execute to install
523             your software.
524             EOA
525             uninstall_commands => <
526             uninstall_commands specifies what commands fetchware should execute to uninstall
527             your software.
528             EOA
529             lookup_url => <
530             lookup_url specifes the url that fetchware uses to determine what what
531             versions of your program are available. It should point to a directory listing
532             instead of a specific file.
533             EOA
534             lookup_method => <
535             lookup_method specifies how fetchware determines what version of your program
536             to install. The default is the 'timestamp' algorithm, and then to try the
537             'versionstring' algorithm if 'timestamp' fails. lookup_method specifies which
538             one you would like to use. Only the strings 'timestamp' and 'versionstring'
539             are allowed options.
540             EOA
541             gpg_keys_url => <
542             gpg_keys_url specifies the url that fetchware will use to download the author's
543             KEYS file that it uses for gpg verification.
544             EOA
545             gpg_sig_url => <
546             gpg_sig_url specifies the url that fetchware uses to download digital
547             signatures of this program. They're files that usually end .asc.
548             EOA
549             sha1_url => <
550             sha1_url specfies the url that fetchware uses to download sha1sum files of
551             this program. This url should be the program's main download site instead of a
552             mirror, because a hacked mirror could alter the sha1sum on that mirror.
553             EOA
554             md5_url => <
555             md5_url specfies the url that fetchware uses to download md5sum files of
556             this program. This url should be the program's main download site instead of a
557             mirror, because a hacked mirror could alter the md5sum on that mirror.
558             EOA
559             verify_method => <
560             verify_method specifes a specific method that fetchware should use to verify
561             your program. This method can be 'gpg', 'sha1', or 'md5'.
562             EOA
563             no_install => <
564             no_install specifies that this software should not be installed. Instead, the
565             install step is skipped, and fetchware prints to STDOUT where it downloaded,
566             verified, and built your program. no_install must be a true or false value.
567             EOA
568             verify_failure_ok => <
569             verify_failure_ok specifies that fetchware should not stop installing your
570             software and terminate with an error message if fetchware fails to verify your
571             software. You should never set this to true. Doing so could cause fetchware to
572             install software that may have been compromised, or had malware inserted into
573             it. Never use this option unless the author or maintainer of this program does
574             not gpg sign or checksum his software.
575             EOA
576             user_keyring => <
577             users_keyring if enabled causes fetchware to use the user's own gpg keyring
578             instead of fetchware's own keyring.
579             EOA
580             mirror => <
581             The mirror configuration option provides fetchware with alternate servers to
582             try to download this program from. This option is used when the server
583             specified in the url options in this file is unavailable or times out.
584             EOA
585             }
586             );
587             ###INSANEFEATUREENHANCEMENT### Prompt for name of program, and do a fuzzy
588             #search on CPAN for that program under
589             #App::Fetchware::FetchwarefileX::UpCasedProgName. Consider using the meta
590             #CPAN API. And if it exists ask user if they wanna use that one instead of
591             #autogening one.
592             #
593             #Perhaps create a 'fetchwarefile' command to download and look at
594             #fetchwarefiles from CPAN, and then install them, and/or perhaps upload
595             #them pausing to ask for the user's PAUSE credentials!!!!!!!!!
596              
597              
598 0         0 extension_name(__PACKAGE__);
599              
600              
601 1         216 my $opening_message = <
602             Fetchware's new command is reasonably sophisticated, and is smart enough to
603             determine based on the lookup_url you provide if it can autogenerate a
604             Fetchwarefile for you. If Fetchware cannot, then it will ask you more
605             questions regarding the information it requires to be able to build a
606             installable fetchware package for you. After that, fetchware will ask you if
607             you would like to edit the Fetchwarefile, fetchware has created for you in an
608             editor. If you say yes, fetchware will open a editor for you, but if you say
609             no, fetchware will skip the custom editing. Next, fetchware will create a test
610             Fetchwarefile for you, and ask you if you would like to test it by trying to
611             install it now. If you say yes, fetchware will install it, and if you say no,
612             then fetchware will print the location of the Fetchwarefile it created for
613             you to later use to install your application.
614             EOM
615              
616 2         6 opening_message($opening_message);
617              
618             # Ask user for name of program unless the user provided one at command
619             # line such as fetchware new .
620 2         6 $program_name = fetchwarefile_name(program => $program_name);
621 0         0 vmsg "Determined name of your program to be [$program_name]";
622              
623 2         434 $fetchwarefile->config_options(program => $program_name);
624 2         7 vmsg "Appended program [$program_name] configuration option to Fetchwarefile";
625              
626 2         8 my $lookup_url = get_lookup_url($term);
627 0         0 vmsg "Asked user for lookup_url [$lookup_url] from user.";
628              
629 1         242 $fetchwarefile->config_options(lookup_url => $lookup_url);
630 2         6 vmsg "Appended lookup_url [$lookup_url] configuration option to Fetchwarefile";
631              
632 2         6 vmsg "Downloaded lookup_url [$lookup_url]";
633 0         0 my $filename_listing = download_lookup_url($term, $lookup_url);
634 2         461 vmsg "Downloaded lookup_url's directory listing";
635 2         8 vmsg Dumper($filename_listing);
636              
637 2         8 my $mirrors_hashref = get_mirrors($term, $filename_listing);
638 0         0 vmsg "Added mirrors to your Fetchwarefile.";
639 1         217 vmsg Dumper($mirrors_hashref);
640              
641 2         7 my $verify_hashref = get_verification($term, $filename_listing, $lookup_url);
642 2         5 vmsg "Added verification settings to Fetchwarefile.";
643 0         0 vmsg Dumper($verify_hashref);
644              
645 2         427 my $filter_hashref = get_filter_option($term, $filename_listing);
646 2         8 vmsg "Added [$filter_hashref->{filter}] filter setting to Fetchwarefile.";
647              
648 2         8 $fetchwarefile->config_options(
649             %$mirrors_hashref,
650             %$verify_hashref,
651             %$filter_hashref
652             );
653              
654             ###BUGALERT### Ask to parrallelize make with make_options???
655             ###BUGALERT### Verify prefix is writable by current user, who will
656             #presumably be the user who will install the package now and later.
657             ###BUGALERT### Ask user for a prefix if their running nonroot???
658 0         0 vmsg 'Prompting for other options that may be needed.';
659 1         216 my $other_options_hashref = prompt_for_other_options($term,
660             temp_dir => {
661             prompt => <
662             What temp_dir configuration option would you like?
663             EOP
664             print_me => <
665             temp_dir is the directory where fetchware creates a temporary directory that
666             stores all of the temporary files it creates while it is building your software.
667             The default directory is /tmp on Unix systems and C:\\temp on Windows systems.
668             EOP
669             },
670             user => {
671             prompt => <
672             What user configuration option would you like?
673             EOP
674             print_me => <
675             user specifies what user fetchware will drop priveleges to on Unix systems
676             capable of doing so. This allows fetchware to download files from the internet
677             with user priveleges, and not do anything as the administrative root user until
678             after the downloaded software package has been verified as exactly the same as
679             the author of the package intended it to be. If you use this option, the only
680             thing that is run as root is 'make install' or whatever this package's
681             install_commands configuratio option is.
682             EOP
683             },
684             prefix => {
685             prompt => <
686             What prefix configuration option would you like?
687             EOP
688             print_me => <
689             prefix specifies the base path that will be used to install this software. The
690             default is /usr/local, which is acceptable for most unix users. Please note that
691             this difective only works for software packages that use GNU AutoTools, software
692             that uses ./configure --prefix= to change the prefix.
693             EOP
694             },
695             configure_options => {
696             prompt => <
697             What configure_options configuration option would you like?
698             EOP
699             print_me => <
700             configure_options specifies what options fetchware should add when it configures
701             this software package for you. A list of possible options can be obtained by
702             running unarchiving the software package that corresponds to this Fetchwarefile,
703             and running the command './configure --help'. These options vary from software
704             package to software package. Please note that this option only works for GNU
705             AutoTools based software distributions, ones that use ./configure to configure
706             the software.
707             EOP
708             },
709             make_options => {
710             prompt => <
711             What make_options configuration option would you like?
712             EOP
713             print_me => <
714             make_options specifies what options fetchware will pass to make when make is run
715             to compile, perhaps test, and install your software package. They are simpley
716             added after make is called. An example is '-j 4', which will cause make to
717             execute 4 jobs simultaneously. A reasonable rule of thumb is to set make's -j
718             argument to two times as many cpu cores your computer has as compiling programs
719             is sometimes IO bound instead of CPU bound, so you can get away with running
720             more jobs then you have cores.
721             EOP
722             },
723             ###BUGALERT### Create a config sub called build_system that takes args like
724             #AutoTools, cmake, MakeMaker, Module::Build, and so on that will use the default
725             #build commands of whatever system this option specifies.
726             build_commands => {
727             prompt => <
728             What build_commands configuration option would you like?
729             EOP
730             print_me => <
731             build_commands specifies what commands fetchware will run to compile your
732             software package. Fetchware's default is simply 'make', which is good for most
733             programs. If you're software package uses something other than fetchware's
734             default of GNU AutoTools, then you may need to change this configuration option
735             to specify what you would like instead. Specify multiple build commands in
736             single quotes with a comma between them:
737             './configure', 'make'
738             EOP
739             },
740             install_commands => {
741             prompt => <
742             What install_commands configuration option would you like?
743             EOP
744             print_me => <
745             install_commands specifies what commands fetchware will run to install your
746             software package. Fetchware's default is simply 'make install', which is good
747             for most programs. If you're software package uses something other than
748             fetchware's default of GNU AutoTools, then you may need to change this
749             configuration option to specify what you would like instead. Specify multiple
750             build commands in single quotes with a comma between them:
751             'make test', 'make install'
752             EOP
753             },
754             uninstall_commands => {
755             prompt => <
756             What uninstall_commands configuration option would you like?
757             EOP
758             print_me => <
759             uninstall_commands specifes what commands fetchware will run to uninstall your
760             software pacakge. The default is 'make uninstall,' which works for some GNU
761             AutoTools packages, but not all. If your software package does not have a 'make
762             uninstall' make target, but it has some other command that can uninstall it,
763             then please specify it using uninstall_commands so fetchware can uninstall it.
764             EOP
765              
766             },
767             lookup_method => {
768             prompt => <
769             What lookup_method configuration option would you like?
770             EOP
771             print_me => <
772             lookup_method specifies what how fetchware determines if a new version of your
773             software package is available. The available algorithms are 'timstamp' and
774             'versionstring'. 'timestamp' uses the timestamp listed in the FTP or HTTP
775             listing, and uses the software package that is the newest by filesystem
776             timestamp. The 'versionstring' algorithm uses the filename of the files in the
777             FTP or HTTP listing. It parses out the version information, sorts it highest to
778             lowest, and then picks the highest version of your software package. The default
779             is try 'timestamp' and if that doesn't work, then try 'versionstring'.
780             EOP
781             },
782             gpg_keys_url => {
783             prompt => <
784             What gpg_keys_url configuration option would you like?
785             EOP
786             print_me => <
787             gpg_keys_url specifies a url similar to lookup_url in that it should specify a
788             directory instead a specific file. It is used to download KEYS files, which
789             contain your program author's gpg keys to import into gpg.
790             EOP
791             },
792             gpg_sig_url => {
793             prompt => <
794             What gpg_sig_url configuration option would you like?
795             EOP
796             print_me => <
797             gpg_sig_url specifies a url similar to lookup_url in that it should specify a
798             directory instead a specific file. It is used to download gpg signatures to
799             verify your software package.
800             EOP
801             },
802             sha1_url => {
803             prompt => <
804             What sha1_url configuration option would you like?
805             EOP
806             print_me => <
807             sha1_url specifies a url similar to lookup_url in that it should specify a
808             directory instead of a specific file. It is separate from lookup_url, because
809             you should download software from mirrors, but checksums from the original
810             vendor's server, because checksums are easily replaced on a mirror by a hacker
811             if the mirror gets hacked.
812             EOP
813             },
814             md5_url => {
815             prompt => <
816             What md5_url configuration option would you like?
817             EOP
818             print_me => <
819             md5_url specifies a url similar to lookup_url in that it should specify a
820             directory instead of a specific file. It is separate from lookup_url, because
821             you should download software from mirrors, but checksums from the original
822             vendor's server, because checksums are easily replaced on a mirror by a hacker
823             if the mirror gets hacked.
824             EOP
825             },
826             verify_method => {
827             prompt => <
828             What verify_method configuration option would you like?
829             EOP
830             print_me => <
831             verify_method specifies what method of verification fetchware should use to
832             ensure the software you have downloaded has not been tampered with. The default
833             is to try gpg verification, then sha1, and then finally md5, and if they all
834             fail an error message is printed and fetchware exits, because if your software
835             package cannot be verified, then it should not be installed. This configuration
836             option allows you to remove the warnings by specifying a specific way of
837             verifying your software has not been tampered with. To disable verification set
838             the 'verify_failure_ok' configuration option to true.
839             EOP
840             },
841             ###BUGALERT### replace no_install config su with a command line option that
842             #would be the opposite of --force???
843             # Nah! Leave it! Just create a command line option for it too!
844             no_install => {
845             prompt => <
846             Would you like to enable the no_install configuration option?
847             EOP
848             ###BUGALERT### no_install is not currently implemented properly!!!
849             print_me => <
850             no_install is a true or false option, whoose acceptable values include 1
851             or 0, true or falue, On or Off. It's default value is false, but if you enable
852             it, then fetchware will not install your software package, and instead it will
853             simply download, verify, and build it. And then it will print out the full path
854             of the directory it built your software package in.
855             EOP
856             ###BUGALERT### Add support for a check regex, so that I can ensure
857             #that what the user enters will be either true or false!!!
858             },
859             verify_failure_ok => {
860             prompt => <
861             Would you like to enable the verify_failure_ok configuration option?
862             EOP
863             print_me => <
864             verify_failure_ok is a true or false option, whoose acceptable values include 1
865             or 0, true or falue, On or Off. It's default value is false, but if you enable
866             it, then fetchware will not print an error message and exit if verification
867             fails for your software package. Please note that you should never use this
868             option, because it makes it possible for fetchware to install source code that
869             may have been tampered with.
870             EOP
871             },
872             users_keyring => {
873             prompt => <
874             Would you like to enable users_keyring configuration option?
875             EOP
876             print_me => <
877             users_keyring when enabled causes fetchware to use the user who calls
878             fetchware's gpg keyring instead of fetchware's own gpg keyring. Useful for
879             source code distributions that do not provide an easily accessible KEYS file.
880             Just remember to import the author's keys into your gpg keyring with gpg
881             --import.
882             EOP
883             },
884             );
885 2         8 vmsg 'User entered the following options.';
886 2         4 vmsg Dumper($other_options_hashref);
887              
888             # Append all other options to the Fetchwarefile.
889 0         0 $fetchwarefile->config_options(%$other_options_hashref);
890 2         432 vmsg 'Appended all other options listed above to Fetchwarefile.';
891              
892 2         6 my $edited_fetchwarefile = edit_manually($term, $fetchwarefile);
893 2         10 vmsg <
894             Asked user if they would like to edit their generated Fetchwarefile manually.
895             EOM
896             # Generate Fetchwarefile.
897 0 100 0     0 if (blessed($edited_fetchwarefile)
898             and
899             $edited_fetchwarefile->isa('App::Fetchware::Fetchwarefile')) {
900             # If edit_manually() did not modify the Fetchwarefile, then generate
901             # it.
902 0         0 $fetchwarefile = $fetchwarefile->generate();
903             } else {
904             # If edit_manually() modified the Fetchwarefile, then do not
905             # generate it, and replace the Fetchwarefile object with the new
906             # string that represents the user's edited Fetchwarefile.
907 1         3 $fetchwarefile = $edited_fetchwarefile;
908             }
909              
910             # Whatever variables the new() API subroutine returns are written via a pipe
911             # back to the parent, and then the parent reads the variables back, and
912             # makes then available to new_install(), back in the parent, as arguments.
913 1         3 return $program_name, $fetchwarefile;
914             }
915              
916              
917              
918              
919              
920              
921             sub extension_name {
922             # Use a state variable to keep $extension_name's value between calls.
923 4     5 1 467 state $extension_name;
924              
925             # If $extension_name has never been touch and is still undef, then allow it
926             # to be set.
927 4 100 66     23 if (not defined $extension_name) {
    100          
928 1         2 $extension_name = shift;
929             # If $extension_name *is* set, and extension_name() was called with an
930             # argument, which is what defined shift does (shift shifts the first value
931             # off of @_ (the subroutine argument array), while defined checks to see if
932             # one was actually defined and provided by the caller.)
933             } elsif (defined $extension_name and defined shift) {
934 1         6 die <
935             App-Fetchware: extension_name() was called more than once. It is a singleton,
936             and therefore can only be called once. Please only call it once to set its
937             value, and then call it repeatedly wherever you need that value. see perldoc
938             App::Fetchware for more details.
939             EOD
940             }
941              
942             # Return the singleton $extension_name.
943 3         9 return $extension_name;
944             }
945              
946              
947              
948             sub opening_message {
949 0     1 1 0 my $opening_message = shift;
950              
951             # Just print the opening message.
952 0         0 print $opening_message;
953             }
954              
955              
956              
957             sub fetchwarefile_name {
958 0     1 1 0 my ($term, $fetchwarefile_name, $fetchwarefile_name_value) = @_;
959 0         0 my $what_a_fetchwarefile_name_is = <
960             Fetchware uses the $fetchwarefile_name configuration option to name this
961             specific Fetchwarefile that Fetchware's new command is helping you create.
962             Since, you did not provide a $fetchwarefile_name on the command line, please
963             provide one below
964             EOM
965 0 50       0 die <
966             App-Fetchware: in your call to fetchwarefile_name() you failed to call it with a
967             defined \$fetchwarefile_name option. The \$fetchwarefile_name option you
968             specified is [$fetchwarefile_name]. Please specify this option, and try again.
969             EOD
970              
971 0 50       0 if (not defined $fetchwarefile_name_value) {
972             $fetchwarefile_name_value = $term->get_reply(
973             prompt => q{What would you like to name this specific Fetchwarefile? },
974             print_me => $what_a_fetchwarefile_name_is,
975             # This option requires a name, so just pressing return, which would
976             # yield undef is not acceptable. We need an actual value, so check
977             # the value to ensure that it is defined.
978 0 50   2   0 allow => sub { defined shift @_ ? return 1 : return 0 }
979 0         0 );
980             }
981              
982 0         0 return $fetchwarefile_name, $fetchwarefile_name_value;
983             }
984              
985              
986              
987             sub get_lookup_url {
988 0     1 1 0 my $term = shift;
989              
990              
991             # prompt for lookup_url.
992 0         0 my $lookup_url = $term->get_reply(
993             print_me => <
994             Fetchware's heart and soul is its lookup_url. This is the configuration option
995             that tells fetchware where to check what the latest version of your program is.
996             This version number is then parsed out of the HTTP/FTP/local directory listing,
997             and compared against the latest installed version to determine when a new
998             version of your program has been released.
999              
1000             How to determine your application's lookup_url:
1001             1. Go to your application's Web site.
1002             2. Determine the download link for the latest version and copy it with
1003             CTRL-C or right-click it and select "copy".
1004             3. Paste the download link into your browser's URL Location Bar.
1005             4. Delete the filename from the location by starting at the end and deleting
1006             everything to the left until you reach a slash '/'.
1007             * ftp://a.url/downloads/program.tar.gz -> ftp://a.url/downloads/
1008             5. Press enter to access the directory listing on your Application's mirror
1009             site.
1010             6. If the directory listing in either FTP or HTTP format is displayed in
1011             your browser, then Fetchware's default, built-in lookup fuctionality will
1012             probably work properly. Copy and paste this URL into the prompt below, and
1013             Fetchware will download and analyze your lookup_url to see if it will work
1014             properly. If you do not end up with a browser directory listing, then
1015             please see Fetchware's documentation using perldoc App::Fetchware.
1016             EOP
1017             prompt => q{What is your application's lookup_url? },
1018             allow => qr!(ftp|http|file)://!);
1019              
1020 0         0 return $lookup_url;
1021             }
1022              
1023              
1024              
1025             sub download_lookup_url {
1026 0     1 1 0 my $term = shift;
1027 0         0 my $lookup_url = shift;
1028              
1029 0         0 my $filename_listing;
1030             eval {
1031             # Use no_mirror_download_dirlist(), because the regular one uses
1032             # config(qw(lookup_url mirror)), which is not known yet.
1033 0         0 my $directory_listing = no_mirror_download_dirlist($lookup_url);
1034              
1035             # Create a fake lookup_url, because parse_directory_listing() uses it to
1036             # determine the type of *_filename_listing() subroutine to call.
1037 0         0 config(lookup_url => $lookup_url);
1038              
1039 0         0 $filename_listing = parse_directory_listing($directory_listing);
1040              
1041 0         0 __clear_CONFIG();
1042              
1043             # Fix the most annoying bug that ever existed in perl.
1044             # http://blog.twoshortplanks.com/2011/06/06/unexceptional-exceptions-in-perl-5-14/
1045 0         0 1;
1046 0 50       0 } or do {
1047 0         0 my $lookup_url_failed_try_again = <
1048             fetchware: the lookup_url you provided failed because of :
1049             [$@]
1050             Please try again. Try the steps outlined above to determine what your program's
1051             lookup_url should be. If you cannot figure out what it should be please see
1052 0         0 perldoc @{[extension_name()]} for additional hints on how to choose a lookup_url.
1053             EOF
1054 0         0 $lookup_url = get_lookup_url($term, $lookup_url_failed_try_again);
1055              
1056             eval {
1057             # Use no_mirror_download_dirlist(), because the regular one uses
1058             # config(qw(lookup_url mirror)), which is not known yet.
1059 0         0 my $dir_list = no_mirror_download_dirlist($lookup_url);
1060              
1061             # Create a fake lookup_url, because parse_directory_listing() uses
1062             # it to determine the type of *_filename_listing() subroutine to
1063             # call.
1064 0         0 config(lookup_url => $lookup_url);
1065              
1066 0         0 $filename_listing = parse_directory_listing($dir_list);
1067              
1068 0         0 __clear_CONFIG();
1069             # Fix the most annoying bug that ever existed in perl.
1070             # http://blog.twoshortplanks.com/2011/06/06/unexceptional-exceptions-in-perl-5-14/
1071 0         0 1;
1072 0 50       0 } or do {
1073 0         0 die <
1074             fetchware: run-time error. The lookup_url you provided [$lookup_url] is not a
1075             usable lookup_url because of the error below:
1076             [$@]
1077 0         0 Please see perldoc @{[extension_name()]} for troubleshooting tips and rerun
1078             fetchware new.
1079             EOD
1080             };
1081             };
1082              
1083 0         0 return $filename_listing;
1084             }
1085              
1086              
1087              
1088             ###BUGALERT### Use the $filename_listing argument to search for a MIRRORS file
1089             #that specifies this open source distribution's official listing of mirrors,
1090             #parse it, and add them to the returned hash or mirrors. But, it'll probably
1091             #need configuration. Use GeoIP? No options are avalable. Parse the list, and
1092             #present it to the user, and ask him to pick some:)
1093             sub get_mirrors {
1094 0     1 1 0 my ($term, $filename_listing) = @_;
1095              
1096 0         0 my @mirrors;
1097              
1098 0         0 my $mirror = $term->get_reply(
1099             print_me => <
1100             Fetchware requires you to please provide a mirror. This mirror is required,
1101             because most software authors prefer users download their software packages from
1102             a mirror instead of from the authors main download site, which your lookup_url
1103             should point to.
1104              
1105             The mirror should be a URL in standard browser format such as [ftp://a.mirror/].
1106             FTP, HTTP, and local file:// mirrors are supported. All other formats are not
1107             supported.
1108             EOP
1109             prompt => 'Please enter the URL of your mirror: ',
1110             allow => qr!^(ftp|http|file)://!,
1111             );
1112              
1113             # Append mirror to $fetchwarefile.
1114 0         0 push @mirrors, $mirror;
1115              
1116 0 50       0 if (
1117             $term->ask_yn(
1118             print_me => <
1119             In addition to the one required mirror that you must define in order for
1120             fetchware to function properly, you may specify additonal mirros that fetchware
1121             will use if the mirror you've already specified is unreachable or download
1122             attempts using that mirror fail.
1123             EOP
1124             prompt => 'Would you like to add any additional mirrors? ',
1125             default => 'n',
1126             )
1127             ) {
1128             # Prompt for first mirror outside loop, because if you just hit enter or
1129             # type done, then the above text will be appended to your fetchwarefile,
1130             # but you'll be able to skip actually adding a mirror.
1131 0         0 my $first_mirror = $term->get_reply(
1132             prompt => 'Type in URL of mirror or done to continue: ',
1133             allow => qr!^(ftp|http|file)://!,
1134             );
1135             # Append $first_mirror to $fetchwarefile.
1136 0         0 push @mirrors, $first_mirror;
1137              
1138 0         0 while (1) {
1139 0         0 my $mirror_or_done = $term->get_reply(
1140             prompt => 'Type in URL of mirror or done to continue: ',
1141             default => 'done',
1142             allow => qr!(^(ftp|http|file)://)|done!,
1143             );
1144 0 50       0 if ($mirror_or_done eq 'done') {
1145 0         0 last;
1146             } else {
1147             # Append $mirror_or_done to $fetchwarefile.
1148 0         0 push @mirrors, $mirror_or_done;
1149             }
1150             }
1151             }
1152              
1153 0         0 return {mirror => \@mirrors};
1154             }
1155              
1156              
1157              
1158             sub get_verification {
1159 0     1 1 0 my ($term, $filename_listing, $lookup_url) = @_;
1160              
1161 0         0 my %options;
1162              
1163             my %available_verify_methods;
1164             # Determine what types of verification are available.
1165 0         0 for my $file_and_timestamp (@$filename_listing) {
1166 0 50       0 if ($file_and_timestamp->[0] =~ /\.(asc|sig|sign)$/) {
    50          
    50          
1167 0         0 $available_verify_methods{gpg}++;
1168             } elsif ($file_and_timestamp->[0] =~ /\.sha1?$/) {
1169 0         0 $available_verify_methods{sha1}++;
1170             } elsif ($file_and_timestamp->[0] =~ /\.md5$/) {
1171 0         0 $available_verify_methods{md5}++;
1172             }
1173             }
1174              
1175 0         0 my $verify_configed_flag = 0;
1176             #If gpg is available prefer it over the others.
1177 0 50 0     0 if (exists $available_verify_methods{gpg}
      0        
1178             and defined $available_verify_methods{gpg}
1179             and $available_verify_methods{gpg} > 0
1180             ) {
1181 0         0 msg <
1182             gpg digital signatures found. Using gpg verification.
1183             EOM
1184 0         0 $options{verify_method} = 'gpg';
1185              
1186             # Search for a KEYS file to use to import the author's keys.
1187 0 50       0 if (grep {$_->[0] eq 'KEYS'} @$filename_listing) {
  0         0  
1188 0         0 msg <
1189             KEYS file found using lookup_url. Adding gpg_keys_url to your Fetchwarefile.
1190             EOM
1191             # Add 'KEYS' or '/KEYS' to $lookup_url's path.
1192 0         0 my ($scheme, $auth, $path, $query, $fragment) =
1193             uri_split($lookup_url);
1194 0         0 $path = catfile($path, 'KEYS');
1195 0         0 $lookup_url = uri_join($scheme, $auth, $path, $query, $fragment);
1196              
1197 0         0 $options{gpg_keys_url} = $lookup_url;
1198 0         0 $verify_configed_flag++;
1199             } else {
1200 0         0 msg <
1201             KEYS file *not* found!
1202             EOM
1203             # Since autoconfiguration of KEYS failed, try asking the user if
1204             # they would like to import the author's key themselves into their
1205             # own keyring and have fetchware use that.
1206 0 50       0 if (
1207             $term->ask_yn(prompt =>
1208             q{Would you like to import the author's key yourself after fetchware completes? },
1209             default => 'n',
1210             print_me => <
1211             Automatic KEYS file discovery failed. Fetchware needs the author's keys to
1212             download and import into its own keyring, or you may specify the option
1213             user_keyring, which if true will cause fetchware to use the user who runs
1214             fetchware's keyring instead of fetchware's own keyring. But you, the user, needs
1215             to import the author's keys into your own gpg keyring. You can do this now in a
1216             separate shell, or after you finish configuring this Fetchwarefile. Just run the
1217             command [gpg --import ].
1218             EOP
1219             )
1220             ) {
1221 0         0 $options{user_keyring} = 'On';
1222              
1223 0         0 $verify_configed_flag++;
1224             }
1225              
1226             # And if the user does not want to, then fallback to sha1 and/or md5
1227             # if they're defined, which is done below.
1228             }
1229             }
1230            
1231            
1232             # Only try sha1 and md5 if gpg failed.
1233 0 50       0 unless ($verify_configed_flag == 1) {
1234 0 50 0     0 if (exists $available_verify_methods{sha1}
    50 0        
      0        
      0        
1235             and defined $available_verify_methods{sha1}
1236             and $available_verify_methods{sha1} > 0
1237             ) {
1238 0         0 msg <
1239             SHA1 checksums found. Using SHA1 verification.
1240             EOM
1241 0         0 $options{verify_method} = 'sha1';
1242             } elsif (exists $available_verify_methods{md5}
1243             and defined $available_verify_methods{md5}
1244             and $available_verify_methods{md5} > 0
1245             ) {
1246 0         0 msg <
1247             MD5 checksums found. Using MD5 verification.
1248             EOM
1249 0         0 $options{verify_method} = 'md5';
1250             } else {
1251             # Print a huge long nasty warning even include links to news stories
1252             # of mirrors actually getting hacked and serving malware, which
1253             # would be detected and prevented with proper verification enabled.
1254              
1255             # Ask user if they would like to continue installing fetchware even if
1256             # verification fails, and then enable the verify_failure_ok option.
1257 0 50       0 if (
1258             $term->ask_yn(prompt => <
1259             Would you like fetchware to ignore the fact that it is unable to verify the
1260             authenticity of any downloads it makes? Are you ok with possibly downloading
1261             viruses, worms, rootkits, or any other malware, and installing it possibly even
1262             as root?
1263             EOP
1264             default => 'n',
1265             print_me => <
1266             Automatic verification of your fetchware package has failed! Fetchware is
1267             capable of ignoring the error, and installing software packages anyway using its
1268             verify_failure_ok configuration option. However, installing software packages
1269             without verifying that they have not been tampered with could allow hackers to
1270             potentially install malware onto your computer. Don't think this is *not*
1271             possible or do you think its extremely unlikely? Well, it's actually
1272             surprisingly common:
1273             1. http://arstechnica.com/security/2012/09/questions-abound-as-malicious-phpmyadmin-backdoor-found-on-sourceforge-site/
1274             Discusses how a mirror for sourceforge was hacked, and the phpMyAdmin
1275             software package on that mirror was modified to spread malware.
1276             2. http://www.geek.com/news/major-open-source-code-repository-hacked-for-months-says-fsf-551344/
1277             Discusses how FSF's gnu.org ftp download site was hacked.
1278             3. http://arstechnica.com/security/2012/11/malicious-code-added-to-open-source-piwik-following-website-compromise/
1279             Discusses how Piwiki's wordpress software was hacked, and downloads of
1280             Piwiki had malicious code inserted into them.
1281             4. http://www.theregister.co.uk/2011/03/21/php_server_hacked/
1282             Discusses how php's wiki.php.org server was hacked yielding credentials to
1283             php's source code repository.
1284             Download mirrors *do* get hacked. Do not make the mistake, and think that it is
1285             not possible. It is possible, and it does happen, so please properly configure
1286             your Fetchwarefile to enable fetchware to verify that the downloaded software is
1287             the same what the author uploaded.
1288             EOP
1289             )
1290             ) {
1291             # If the user is ok with not properly verifying downloads, then
1292             # ignore the failure, and install anyway.
1293 0         0 $options{verify_failure_ok} = 'On';
1294             } else {
1295             # Otherwise, throw an exception.
1296 0         0 die <
1297             fetchware: Fetchware *must* be able to verify any software packages that it
1298             downloads. The Fetchwarefile that you were creating could not do this, because
1299             you failed to specify how fetchware can verify its downloads. Please rerun
1300             fetchware new again, and this time be sure to specify a gpg_keys_url, specify
1301             user_keyring to use your own gpg keyring, or answer yes to the question
1302             regarding adding verify_failure_ok to your Fetchwarefile to make failing
1303             verificaton acceptable to fetchware.
1304             EOD
1305             }
1306             }
1307             }
1308              
1309 0         0 return \%options;
1310             }
1311              
1312              
1313              
1314             sub get_filter_option {
1315 0     1 1 0 my $term = shift;
1316             # $filename_listing is an array of [$filename, $timestamp] arrays.
1317 0         0 my $filename_listing = shift;
1318 0         0 msg <
1319             Analyzing the lookup_url you provided to determine if fetchware can use it to
1320             successfully determine when new versions of your software are released.
1321             EOS
1322              
1323 0         0 my $filter;
1324 0 0       0 if (grep {$_->[0] =~ /^(CURRENT|LATEST)[_-]IS[_-].+/} @$filename_listing) {
  0         0  
1325             # There is only one version in the lookup_url directory listing, so
1326             # I do not need a filter option.
1327 0         0 msg <
1328             * The lookup_url you gave fetchware includes a CURRENT_IS or a LATEST_IS file
1329             that tells fetchware and regular users what the latest version is. Because of
1330             this we can be reasonable sure that a filter option is not needed, so I'll skip
1331             asking for one. You can provide one later if you need to provide one, when
1332             fetchware prompts you for any custom options you may want to use.
1333             EOS
1334             } else {
1335             # There is a CURRENT_IS_ or LATEST_IS_ file that tells
1336             # you what the latest version is.
1337             ###BUGALERT### Why is this line in both sections of the if statement??? Inside
1338             #this else block means that a CURRENT_IS or LATEST-IS was *not* found??? Fix
1339             #this!!!!!!
1340 0         0 msg <
1341             * The directory listing of your lookup_url has a CURRENT_IS_ or
1342             LATEST_IS_ file that specifies the latest version, which means that
1343             your program's corresponding Fetchwarefile does not need a filter option. If you
1344             still would like to provide one, you can do so later on, when fetchware allows
1345             you to define any additional configuration options.
1346             EOS
1347 0         0 my $what_a_filter_is = <
1348             Fetchware needs you to provide a filter option, which is a pattern that fetchware
1349             compares each file in the directory listing of your lookup_url to to determine
1350             which version of your program to install.
1351              
1352             Directories will have other junk files in them or even completely different
1353             programs that could confuse fetchware, and even potentially cause it to install
1354             a different program. Therefore, you should also add the program name to the
1355             begining of your filter. For example if you program is apache, then your filter
1356             should include the name of apache on mirror sites, which is actually:
1357             httpd
1358              
1359             For example, Apache's lookup_url has three versions in the same lookup_url
1360             directory listing. These are 2.4, 2.2, and 2.0. Without the filter option
1361             fetchware would choose the highest, which would be 2.4, which is the latest
1362             version. However, you may want to stick with the older and perhaps more stable
1363             2.2 version of apache. Therefore, you'll need to tell fetchware this by using
1364             by adding the version number to your filter:
1365             httpd-2.2
1366             will result in fetchware filtering the results of its lookup check through your
1367             filter of httpd-2.2 causing fetchware to choose the latest version from the 2.2
1368             stable branch instead of the higher version numbered 2.4 or 2.0 legacy releases.
1369             Note the use of the dash, which is used in the filename to separate the 'httpd'
1370             name part from the '2.2' version part.
1371              
1372             Note: fetchware accepts any valid perl regular expresion as an acceptable
1373             filter option, but that should only be needed for advanced users. See perldoc
1374             fetchware.
1375             EOA
1376             # Prompt for the needed filter option.
1377 0         0 $filter = $term->get_reply(
1378             prompt => <
1379             [Just press enter or return to skip adding a filter option]
1380             What does fetchware need your filter option to be?
1381             EOP
1382             print_me => $what_a_filter_is,
1383             );
1384             ###BUGALERT### Consider Adding a loop around checking the filter option
1385             #that runs determine_lookup_url() using the provided filter option, and
1386             #then asking the user if that is indeed the correct filter option, and
1387             #if not ask again and try it again unit it succeeds or user presses
1388             #ctrl-c|z.
1389             }
1390              
1391 0         0 return {filter => $filter};
1392             }
1393              
1394              
1395              
1396             sub prompt_for_other_options {
1397 0     1 1 0 my $term = shift;
1398              
1399 0         0 my %option_description = @_;
1400              
1401 0         0 my %answered_option;
1402              
1403 0 0       0 if (
1404             $term->ask_yn(prompt =>
1405             q{Would you like to add extra configuration options to your fetchwarefile?},
1406             default => 'n',
1407             print_me => <
1408             Fetchware has many different configuration options that allow you to control its
1409             behavior, and even change its behavior if needed to customize fetchware for any
1410             possible source code distribution.
1411              
1412             If you think you need to add configuration options please check out perldoc
1413             fetchware for more details on fetchware and its Fetchwarefile configuration
1414             options.
1415              
1416             If this is your first package your creating with Fetchware or you're creating a
1417             package for a new program for the first time, you should skip messing with
1418             fetchware's more flexible options, and just give the defaults a chance.
1419             EOP
1420             )
1421             ) {
1422 0         0 my @options = keys %option_description;
1423 0         0 my @config_file_options_to_provide = $term->get_reply(
1424             print_me => <
1425             Below is a listing of Fetchware's available configuration options.
1426             EOP
1427             prompt => <
1428             Please answer with a space seperated list of the number before the configuration
1429             file options that you would like to add to your configuration file?
1430             EOP
1431             choices => \@options,
1432             multi => 1,
1433             );
1434              
1435              
1436 0         0 for my $config_file_option (@config_file_options_to_provide) {
1437             $answered_option{$config_file_option} = $term->get_reply(
1438             print_me => $option_description{$config_file_option}->{print_me},
1439             prompt => $option_description{$config_file_option}->{prompt},
1440 0         0 );
1441             }
1442             }
1443 0         0 return \%answered_option;
1444             }
1445              
1446              
1447              
1448             sub edit_manually {
1449 0     1 1 0 my ($term, $fetchwarefile) = @_;
1450              
1451 0 0       0 if (
1452             $term->ask_yn(
1453             print_me => <
1454             Fetchware has now asked you all of the needed questions to determine what it
1455             thinks your new program's Fetchwarefile should look like. But it's not perfect,
1456             and perhaps you would like to tweak it manually. If you would like to edit it
1457             manually in your favorite editor, answer 'yes', and if you want to skip this just
1458             answer 'no', or just press .
1459              
1460             If you would like to cancel any edits you have made, and use the automagically
1461             generated Fetchwarefile, just delete the entire contents of the file, and save
1462             an empty file.
1463             EOP
1464             prompt => q{Would you like to edit your automagically generated Fetchwarefile manually? },
1465             default => 'n',
1466             )
1467             ) {
1468 0         0 my ($fh, $fetchwarefile_filename) =
1469             tempfile('Fetchwarefile-XXXXXXXXX', TMPDIR => 1);
1470 0         0 print $fh $fetchwarefile->generate();
1471              
1472 0         0 close $fh;
1473              
1474             # Ask what editor to use if EDITOR environment variable is not set.
1475 0   0     0 my $editor = $ENV{EDITOR} || do {
1476             $term->get_reply(prompt => <
1477             What text editor would you like to use?
1478             EOP
1479             print_me => <
1480             The Environment variable EDITOR is not set. This is used by fetchware and other
1481             programs to determine what program fetchware should use to edit your
1482             Fetchwarefile. Please enter what text editor you would like to use. Examples
1483             include: vim, emacs, nano, pico, or notepad.exe (on Windows).
1484             EOP
1485             );
1486             };
1487              
1488 0         0 run_prog($editor, $fetchwarefile_filename);
1489             # NOTE: fetchware will "block" during the above call to run_prog(), and
1490             # wait for the user to close the editor program.
1491              
1492             # If the edited Fetchwarefile does not have a file size of zero.
1493 0 0       0 if (not -z $fetchwarefile_filename) {
1494 0         0 my $fh = safe_open($fetchwarefile_filename, <
1495             fetchware: run-time error. fetchware can't open the fetchwarefile you edited
1496             with your editor after you edited it. This just shouldn't happen. Possible race
1497             condition or weird bug. See perldoc fetchware.
1498             EOD
1499             # Since the generated Fetchwarefile has been edited, because its
1500             # size is nonzero, then replace the App::Fetchware::Fetchwarefile
1501             # object with whatever text can be slurped from the file the user
1502             # edited. Since it is now a scalar instead of an object, that is how
1503             # Fetchware will tell if the user changed it or not.
1504 0         0 $fetchwarefile = do { local $/; <$fh> }; # slurp fetchwarefile
  0         0  
  0         0  
1505             } else {
1506 0         0 msg <
1507             You canceled any custom editing of your fetchwarefile by writing an empty file
1508             to disk.
1509             EOM
1510             }
1511             }
1512 0         0 return $fetchwarefile;
1513             }
1514              
1515              
1516              
1517              
1518              
1519             sub new_install {
1520 0     1 1 0 my ($term, $program_name, $fetchwarefile) = @_;
1521              
1522 0         0 my $fetchware_package_path =
1523             ask_to_install_now_to_test_fetchwarefile($term, \$fetchwarefile,
1524             $program_name);
1525              
1526 0         0 return $fetchware_package_path;
1527              
1528             }
1529              
1530              
1531              
1532              
1533              
1534              
1535             sub ask_to_install_now_to_test_fetchwarefile {
1536 0     0 1 0 my ($term, $fetchwarefile, $program_name) = @_;
1537              
1538              
1539 0         0 vmsg <
1540             Determining if user wants to install now or just save their Fetchwarefile.
1541             EOM
1542              
1543             # If the user wants to install their new Fetchwarefile.
1544 0 0       0 if (
1545             $term->ask_yn(
1546             print_me => <
1547             It is recommended that fetchware go ahead and install the package based on the
1548             Fetchwarefile that fetchware has created for you. If you don't want to install
1549             it now, then enter 'no', but if you want to test your Fetchwarefile now, and
1550             install it, then please enter 'yes' or just press .
1551             EOP
1552             prompt => q{Would you like to install the package you just created a Fetchwarefile for? },
1553             default => 'y',
1554             )
1555             ) {
1556              
1557             # Create a temp Fetchwarefile to store the autogenerated configuration.
1558 0         0 my ($fh, $fetchwarefile_filename)
1559             =
1560             tempfile("fetchware-$$-XXXXXXXXXXXXXX", TMPDIR => 1, UNLINK => 1);
1561 0         0 print $fh $$fetchwarefile;
1562             # Close the temp file to ensure everything that was written to it gets
1563             # flushed from caches and actually makes it to disk.
1564 0         0 close $fh;
1565              
1566 0         0 vmsg <
1567             Saved Fetchwarefile temporarily to [$fetchwarefile_filename].
1568             EOM
1569              
1570             # Reach up bin/fetchware's skirt, and call cmd_install directly, because
1571             # if I use system() and call fetchware again in a separate process using
1572             # the install command, it will return a useless number indicating
1573             # success instead of the $fetchware_package_path I want. I could parse
1574             # the output, but that's a head ache I want to avoid. Instead, I'll just
1575             # be a little frisky.
1576 0         0 my $fetchware_package_path = fetchware::cmd_install($fetchwarefile_filename);
1577             ###BUGALERT### Call cmd_install() inside an eval that will catch any
1578             #problems that come up, and suggest how to fix them???
1579             #Is that really doable???
1580 0         0 vmsg <
1581             Copied Fetchwarefile package to fetchware database [$fetchware_package_path].
1582             EOM
1583 0         0 msg 'Installed Fetchware package to fetchware database.';
1584 0         0 return $fetchware_package_path;
1585             # Else the user just wants to save the Fetchwarefile somewhere.
1586             } else {
1587 0         0 my $fetchwarefile_filename = $program_name . '.Fetchwarefile';
1588              
1589             # Get a name for the Fetchwarefile that does not already exist.
1590 0 0       0 if (-e $fetchwarefile_filename) {
1591 0         0 while (1) {
1592 0         0 $fetchwarefile_filename = $term->get_reply(
1593             prompt => <
1594             What would you like your new Fetchwarefile's filename to be?
1595             EOP
1596             print_me => <
1597             Fetchware by default uses the program name you specified at the beginning of
1598             running fetchware new plus a '.Fetchwarefile' extension to name your
1599             Fetchwarefile. But his file already exists, so you'll have to pick a new
1600             filename that does not currently exist.
1601             EOP
1602             );
1603 0 0       0 last unless -e $fetchwarefile_filename;
1604             }
1605             }
1606 0         0 vmsg <
1607             Determine Fetchwarefile name to be [$fetchwarefile_filename].
1608             EOM
1609              
1610             ###BUGALERT### Replace >, create or delete whole file and replace it with
1611             #what I write now, with >> for append to file if it already exists????
1612             ###BUGALERT### Should safe_open() be moved into the loop above, and instead
1613             #of checking for existence, open the file using safeopen as needed, but
1614             #don't write to it just yet, and then test the open file handle if it's
1615             #empty, and therefore presumable a new file, or an old file that no one
1616             #cares about anymore, because it's empty?
1617 0         0 my $fh = safe_open($fetchwarefile_filename, < '>');
1618             fetchware: failed to open your new fetchwarefile because of os error
1619             [$!]. This really shouldn't happen in this case. Probably a bug, or a weird race
1620             condition.
1621             EOD
1622 0         0 print $fh $$fetchwarefile;
1623              
1624 0         0 close $fh;
1625              
1626 0         0 msg "Saved Fetchwarefile to [$fetchwarefile_filename].";
1627 0         0 return $fetchwarefile_filename;
1628             }
1629             }
1630              
1631              
1632              
1633              
1634             sub start {
1635 173     173 1 1708 my %opts = @_;
1636              
1637             # Add temp_dir config sub to create_tempdir()'s arguments.
1638 173 50       669 if (config('temp_dir')) {
1639 0         0 $opts{TempDir} = config('temp_dir');
1640 0         0 vmsg "Using user specified temporary directory [$opts{TempDir}]";
1641             }
1642              
1643             # Add KeepTempDir option if no_install is set. That way user can still
1644             # access the build directory to do the install themselves.
1645 173 100       413 if (config('no_install')) {
1646 1         3 $opts{KeepTempDir} = 1;
1647 1         4 vmsg "no_install option enabled not deleting temporary directory.";
1648             }
1649              
1650             # Forward opts to create_tempdir(), which does the heavy lifting.
1651 173         735 my $temp_dir = create_tempdir(%opts);
1652 173         1221 msg "Created fetchware temporary directory [$temp_dir]";
1653              
1654 173         1433 return $temp_dir;
1655             }
1656              
1657              
1658              
1659              
1660             sub lookup {
1661 0     0 1 0 msg "Looking up download url using lookup_url [@{[config('lookup_url')]}]";
  0         0  
1662              
1663             # obtain directory listing for file, ftp, or http. (a sub for each.)
1664 0         0 vmsg 'Downloading a directory listing using your lookup_url';
1665 0         0 my $directory_listing = get_directory_listing();
1666 0         0 vmsg 'Obtained the following directory listing:';
1667 0         0 vmsg Dumper($directory_listing);
1668             # parse the directory listing's format based on ftp or http.
1669 0         0 vmsg 'Parse directory listing into internal format.';
1670 0         0 my $filename_listing = parse_directory_listing($directory_listing);
1671 0         0 vmsg 'Directory listing parsed as:';
1672 0         0 vmsg Dumper($filename_listing);
1673             # Run those listings through lookup_by_timestamp() and/or
1674             # lookup_by_versionstring() based on lookup_method, or first by timestamp,
1675             # and then by versionstring if timestamp can't figure out the latest
1676             # version (normally because everything in the directory listing has the
1677             # same timestamp.
1678             # return $download_url, which is lookup_url .
1679 0         0 vmsg 'Using parsed directory listing to determine download url.';
1680 0         0 my $download_path = determine_download_path($filename_listing);
1681              
1682 0         0 vmsg "Download path determined to be [$download_path]";
1683              
1684 0         0 return $download_path;
1685             }
1686              
1687              
1688              
1689              
1690              
1691              
1692             sub get_directory_listing {
1693              
1694 0     0 1 0 return download_dirlist(config('lookup_url'));
1695             }
1696              
1697              
1698              
1699             sub parse_directory_listing {
1700 0     0 1 0 my ($directory_listing) = @_;
1701              
1702 0 0       0 if (config('lookup_url') =~ m!^ftp://!) {
    0          
    0          
1703             ###BUGALERT### *_parse_filelist may not properly skip directories, so a
1704             #directory could exist that could wind up being the "latest version"
1705 0         0 return ftp_parse_filelist($directory_listing);
1706             } elsif (config('lookup_url') =~ m!^http://!) {
1707 0         0 return http_parse_filelist($directory_listing);
1708             } elsif (config('lookup_url') =~ m!^file://!) {
1709 0         0 return file_parse_filelist($directory_listing);
1710             }
1711             }
1712              
1713              
1714              
1715             sub determine_download_path {
1716 0     0 1 0 my $filename_listing = shift;
1717              
1718             # Base lookup algorithm on lookup_method configuration sub if it was
1719             # specified.
1720 0         0 my $sorted_filename_listing;
1721 0 0 0     0 if (defined config('lookup_method')
    0 0        
1722             and config('lookup_method') eq 'timestamp'
1723             ) {
1724 0         0 $sorted_filename_listing = lookup_by_timestamp($filename_listing);
1725             } elsif (defined config('lookup_method')
1726             and config('lookup_method') eq 'versionstring'
1727             ) {
1728 0         0 $sorted_filename_listing = lookup_by_versionstring($filename_listing);
1729             # Default is to just use timestamp although timestamp will call
1730             # versionstring if it can't figure it out, because all of the timestamps
1731             # are the same.
1732             } else {
1733 0         0 $sorted_filename_listing = lookup_by_timestamp($filename_listing);
1734             }
1735              
1736             # Manage duplicate timestamps apropriately including .md5, .asc, .txt files.
1737             # And support some hacks to make lookup() more robust.
1738 0         0 return lookup_determine_downloadpath($sorted_filename_listing);
1739             }
1740              
1741              
1742              
1743              
1744             { # Bare block for holding %month {ftp,http}_parse_filelist() need.
1745             my %month = (
1746             Jan => '01',
1747             Feb => '02',
1748             Mar => '03',
1749             Apr => '04',
1750             May => '05',
1751             Jun => '06',
1752             Jul => '07',
1753             Aug => '08',
1754             Sep => '09',
1755             Oct => '10',
1756             Nov => '11',
1757             Dec => '12',
1758             );
1759              
1760             my %num_month = (
1761             1 => '01',
1762             2 => '02',
1763             3 => '03',
1764             4 => '04',
1765             5 => '05',
1766             6 => '06',
1767             7 => '07',
1768             8 => '08',
1769             9 => '09',
1770             10 => '10',
1771             11 => '11',
1772             12 => '12',
1773             );
1774              
1775              
1776             sub ftp_parse_filelist {
1777 0     0 1 0 my $ftp_listing = shift;
1778              
1779 0         0 my ($filename, $timestamp, @filename_listing);
1780              
1781 0         0 for my $listing (@$ftp_listing) {
1782             # Example Net::FTP->dir() output.
1783             #drwxrwsr-x 49 200 200 4096 Oct 05 14:27 patches
1784             # 0 1 2 3 4 5 6 7 8
1785 0         0 my @fields = split /\s+/, $listing;
1786             # Test & try it??? Probaby won't work.
1787             #my ($month, $day, $year_or_time, $filename) = ( split /\s+/, $listing )[-4--1];
1788 0         0 $filename = $fields[-1];
1789             #month #day #year
1790             #"$fields[6] $fields[7] $fields[8]";
1791 0         0 my $month = $fields[5];
1792 0         0 my $day = $fields[6];
1793 0         0 my $year_or_time = $fields[7];
1794              
1795             # Normalize timestamp format.
1796             # It's a time.
1797 0 0       0 if ($year_or_time =~ /\d\d:\d\d/) {
    0          
1798             # the $month{} hash access replaces text months with numerical
1799             # ones.
1800 0         0 $year_or_time =~ s/://; # Make 12:00 1200 for numerical sort.
1801 0         0 $timestamp = "9999$month{$month}$day$year_or_time";
1802             # It's a year.
1803             } elsif ($year_or_time =~ /\d\d\d\d/) {
1804             # the $month{} hash access replaces text months with numerical
1805             # ones.
1806 0         0 $timestamp = "$year_or_time$month{$month}${day}0000";
1807             }
1808 0         0 push @filename_listing, [$filename, $timestamp];
1809             }
1810              
1811 0         0 return \@filename_listing;
1812             }
1813              
1814              
1815              
1816             sub http_parse_filelist {
1817 0     0 1 0 my $http_listing = shift;
1818              
1819             # Use HTML::TreeBuilder to parse the scalar of html into a tree of tags.
1820 0         0 my $tree = HTML::TreeBuilder->new_from_content($http_listing);
1821              
1822 0         0 my @filename_listing;
1823             my @matching_links = $tree->look_down(
1824             _tag => 'a',
1825             sub {
1826 0     0   0 my $h = shift;
1827              
1828             #parse out archive name.
1829 0         0 my $link = $h->as_text();
1830             # NOTE: The weird alternations adding .asc, .md5, and .sha.?,
1831             # and also a KEYS file are to allow fetchware new to also use
1832             # this subroutine to parse http file listings to analyze the
1833             # contents of the user's lookup_url. It does not make any sense
1834             # to copy and paste this function or even add a callback argument
1835             # allowing you to change the regex.
1836 0 0       0 if ($link =~
1837             /(\.(tar\.(gz|bz2|xz)|(tgz|tbz2|txz))|(asc|md5|sha.?))|KEYS$/) {
1838             # Should I strip out dirs just to be safe?
1839 0         0 my $filename = $link;
1840             # Obtain the tag to the right of the archive link to find the
1841             # timestamp.
1842 0 0       0 if (my $rh = $h->right()) {
1843 0         0 my $listing_line;
1844 0 0       0 if (blessed($rh)) {
1845 0         0 $listing_line = $rh->as_text();
1846             } else {
1847 0         0 $listing_line = $rh;
1848             }
1849 0         0 my @fields = split ' ', $listing_line;
1850             ###BUGALERT### Internationalization probably breaks this
1851             #datetime parsing? Can a library do it?
1852             # day-month-year time
1853             # $fields[0] $fields[1]
1854             # Normalize format for lookup algorithms .
1855 0         0 my ($day, $month, $year) = split /-/, $fields[0];
1856             # Ditch the ':' in the time.
1857 0         0 $fields[1] =~ s/://;
1858             # Some dirlistings use string months Aug, Jun, etc...
1859 0 0       0 if (looks_like_number($month)) {
1860             # Strip leading 0 if it exists by converting the
1861             # string with the useless leading 0 into an integer.
1862             # The %num_month hash lookup will add back a leading
1863             # 0 if there was one. This stupid roundabout code is
1864             # to ensure that there always is a leading 0 if the
1865             # number is less than 10 to ensure that all of the
1866             # numbers this hacky datetime parser outputs all
1867             # have the same length so that the numbers can
1868             # easily be compared with each other.
1869 0         0 $month = sprintf("%u", $month);
1870 0         0 push @filename_listing, [$filename,
1871             "$year$num_month{$month}$day$fields[1]"];
1872             # ...and some use numbers 8, 6, etc....
1873             } else {
1874 0         0 push @filename_listing, [$filename,
1875             "$year$month{$month}$day$fields[1]"];
1876             }
1877             } else {
1878             ###BUGALERT### Add support for other http servers such as lighttpd, nginx,
1879             #cherokee, starman?, AND use the Server: header to determine which algorithm to
1880             #use.
1881 0         0 die <
1882             App-Fetchware: run-time error. A hardcoded algorithm to parse HTML directory
1883             listings has failed! Fetchware currently only supports parseing Apache HTML
1884             directory listings. This is a huge limitation, but surprisingly pretty much
1885             everyone who runs a mirror uses apache for http support. This is a bug so
1886             please report it. Also, if you want to try a possible workaround, just use a ftp
1887             mirror instead of a http one, because ftp directory listings are a easy to
1888             parse. See perldoc App::Fetchware.
1889             EOD
1890             }
1891             }
1892             }
1893 0         0 );
1894              
1895              
1896             # Delete the $tree, so perl can garbage collect it.
1897 0         0 $tree = $tree->delete;
1898              
1899 0         0 return \@filename_listing;
1900             }
1901              
1902              
1903             } # end bare block for %month.
1904              
1905              
1906              
1907              
1908              
1909             sub file_parse_filelist {
1910 1     1 1 1 my $file_listing = shift;
1911              
1912 1         2 for my $file (@$file_listing) {
1913 45 50       2555 my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size, $atime,$mtime,$ctime,
1914             $blksize,$blocks)
1915             = stat($file) or die <
1916             App-Fetchware: Fetchware failed to stat() the file [$file] while trying to parse
1917             your local [file://] lookup_url. The OS error was [$!]. This should not happen,
1918             and is either a bug in fetchware or some sort of race condition.
1919             EOD
1920              
1921             # Replace scalar filename with a arrayref of the filename with its
1922             # assocated timestamp for later processing for lookup().
1923             #
1924             # Use Path::Class's file() constructor & basename() method to strip out
1925             # all unneeded directory information leaving just the file's name.
1926             # Add all of the timestamp numbers together, so that only one numberical
1927             # sort is needed instead of a descending list of numerical sorts.
1928 45         88 $file = [file($file)->basename(), $mtime ];
1929             }
1930              
1931 1         47 return $file_listing;
1932             }
1933              
1934              
1935              
1936             sub lookup_by_timestamp {
1937 1     1 1 77 my $file_listing = shift;
1938            
1939             # Sort the timstamps to determine the latest one. The one with the higher
1940             # numbers, and put $b before $a to put the "bigger", later versions before
1941             # the "lower" older versions.
1942             # Sort based on timestamp, which is $file_listing->[0..*][1][0..6].
1943             # Note: the crazy || ors are to make perl sort each timestamp array first by
1944             # year, then month, then day of the month, and so on.
1945 1         5 my @sorted_listing = sort { $b->[1] <=> $a->[1] } @$file_listing;
  153         135  
1946              
1947 1         6 return \@sorted_listing;
1948             }
1949              
1950              
1951              
1952             sub lookup_by_versionstring {
1953 9     9 1 38458 my $file_listing = shift;
1954              
1955             # Implement versionstring algorithm.
1956 9         7 my @versionstrings;
1957 9         12 for (my $i = 0; $i <= $#{$file_listing}; $i++) {
  85         119  
1958             # Split the filename on "Not a numbers", so remove all "not
1959             # numbers", but keep a list of things that actually are numbers.
1960 76         174 my @iversionstring = split(/\D+/, $file_listing->[$i][0]);
1961              
1962             # Use grep to strip leading empty strings (eg: '').
1963 76         61 @iversionstring = grep {$_ ne ''} @iversionstring;
  248         275  
1964              
1965 76 100       103 if (@iversionstring == 0) {
1966             # Let the usr know we're skipping this filename, but only if they
1967             # really want to know (They turned on verbose output.).
1968 10         24 vmsg <
1969             File [$file_listing->[$i][0]] has no version number in it. Ignoring.
1970             EOM
1971             # And also skip adding this @iversionstring to @versionstrings,
1972             # because this @iversionstring is empty, and how do I sort an empty
1973             # array? Return undef--nope causes "value undef in sort fatal errors
1974             # and warnings." Return 0--nope causes a file with no version number
1975             # at beginning of listing to stay at listing, and cause fetchware to
1976             # fail picking the right version. Return -1--nope, because that's
1977             # hackish and lame. Instead, just not include them in the lookup
1978             # listing, and if that means that the lookup listing is empty throw
1979             # an exception.
1980 10         9 next;
1981             }
1982             # Add $i's version string to @versionstrings.
1983 66         115 push @versionstrings, [$i, @iversionstring];
1984              
1985             # And the sort below sorts them into highest number first order.
1986             }
1987              
1988 9 50       17 die <
1989 0         0 App-Fetchware: The lookup_url your provided [@{[config('lookup_url')]}] does not
1990             have any filenames with detectable version numbers in them. Fetchware's
1991             'versionstring' lookup algorithm depends on files having version numbers in them
1992             such as [httpd-2.2.22.tar.gz] notice the [2.2.22] version number. Fetchware
1993             failed to find any of those in the lookup_url you provided. Consider a different
1994             lookup_url or try switching to the default 'timestamp' lookup algorithm adding
1995             the "lookup_method" configuration option to your Fetchwarefile.
1996             EOD
1997              
1998             # LIMITATION: The sort block below can not have any undef values in its
1999             # input. If there are any, then perl will give a warning about a value being
2000             # undef in a sort, if you are not lucky, then it will actually trigger a
2001             # fatal error. There are CPAN Testers reports with this problem, so it really
2002             # can happen. But you do not have to worry about this, because the for loop
2003             # above that creates @versionstrings
2004             @versionstrings = sort {
2005             # Figure out whoose ($b or $a) is larger and set $last_index to it.
2006 9         25 my $last_index;
  160         102  
2007 160 100       87 if ($#{$b} > $#{$a}) {
  160         109  
  160         169  
2008 56         33 $last_index = $#{$b};
  56         48  
2009             } else {
2010 104         65 $last_index = $#{$a};
  104         73  
2011             }
2012              
2013             # Loop over the indexes of both $b and $a at the same time comparing
2014             # them one by one with <=>...
2015             # ...and be sure to start at index 1, because index 0 is the index of
2016             # $file_listing that this entry in @versionstrings belongs to...
2017 160         166 for my $x (1..$last_index) {
2018             # If one of $b or $a has more numbers in it ($#{$a_or_b} is smaller than
2019             # $x), then if it's $b we should return -1, because $b is smaller
2020             # than $a, and if it's $a, we should return 1, because $b is bigger
2021             # than $a.
2022 395 100       233 return -1 if $x > $#{$b};
  395         467  
2023 386 100       213 return 1 if $x > $#{$a};
  386         453  
2024              
2025 352         283 my $spaceship_result = $b->[$x] <=> $a->[$x];
2026              
2027             # ...and as soon as they no longer equal each other return whatever
2028             # result (-1 or 1) <=> gives.
2029 352 100       427 return $spaceship_result if $spaceship_result != 0;
2030             }
2031              
2032             # Return 0 for equal, because if the two versions were not equal, then
2033             # the for loop above would have caught it, and returned the appropriate
2034             # -1 or 1.
2035 30         24 return 0;
2036             } @versionstrings;
2037              
2038             # Now, "sort" $file_listing into the order @versionstrings was sorted into
2039             # using the copy @sorted_file_listing.
2040 9         8 my @sorted_file_listing;
2041 9         11 for my $versionstring_arrayref (@versionstrings) {
2042 66         61 push @sorted_file_listing,
2043             # The $versionstring_arrayref->[0] part refers to the index that was
2044             # saved first when @versionstrings was created.
2045             $file_listing->[$versionstring_arrayref->[0]];
2046             }
2047              
2048             # Return the sorted $file_listing, @sorted_filename_listing.
2049 9         32 return \@sorted_file_listing;
2050             }
2051              
2052              
2053              
2054              
2055             sub lookup_determine_downloadpath {
2056 0     0 1 0 my $file_listing = shift;
2057              
2058             # First grep @$file_listing for $CONFIG{filter} if $CONFIG{filter} is defined.
2059             # This is done, because some distributions have multiple versions of the
2060             # same program in one directory, so sorting by version numbers or
2061             # timestamps, and then by filetype like below is not enough to determine,
2062             # which file to download, so filter was invented to fix this problem by
2063             # letting Fetchwarefile's specify which version of the software to download.
2064 0 0       0 if (defined config('filter')) {
2065 0         0 @$file_listing = grep { $_->[0] =~ /@{[config('filter')]}/ } @$file_listing;
  0         0  
  0         0  
2066             }
2067              
2068             # Skip any filenames with win32 in them on non-Windows systems.
2069             # Windows systems who may need to download the win32 version can just use
2070             # filter 'win32' for that or maybe 'win32|http-2.2' if they need the other
2071             # functionality of filter.
2072 0 0       0 if ($^O ne 'MSWin32') { # $^O is what os I'm on, MSWin32, Linux, darwin, etc
2073 0         0 @$file_listing = grep { $_->[0] !~ m/win32/i } @$file_listing;
  0         0  
2074             }
2075              
2076             # Support 'LATEST{_,-}IS' and 'CURRENT{_,-}IS', which indicate what the
2077             # latest version is. These files come from each software distributions
2078             # mirror scripts, so they should be more accurate than either of my lookup
2079             # algorithms. Both Apache and the Linux kernel maintain these files.
2080 0         0 $_->[0] =~ /^(?:latest|current)[_-]is[_-](.*)$/i for @$file_listing;
2081 0         0 my $latest_version = $1;
2082 0 0       0 @$file_listing = grep { $_->[0] =~ /$latest_version/ } @$file_listing
  0         0  
2083             if defined $latest_version;
2084              
2085             # Determine the $download_url based on the sorted @$file_listing by
2086             # finding a downloadable file (a tarball or zip archive).
2087             # Furthermore, choose them based on best compression to worst to save some
2088             # bandwidth.
2089 0         0 for my $fl (@$file_listing) {
2090 0 0       0 if ($fl->[0] =~ /\.tar\.xz$/) {
    0          
    0          
    0          
    0          
    0          
    0          
    0          
2091 0         0 my $path = ( uri_split(config('lookup_url')) )[2];
2092 0         0 return "$path/$fl->[0]";
2093             } elsif ($fl->[0] =~ /\.txz$/) {
2094 0         0 my $path = ( uri_split(config('lookup_url')) )[2];
2095 0         0 return "$path/$fl->[0]";
2096             } elsif ($fl->[0] =~ /\.tar\.bz2$/) {
2097 0         0 my $path = ( uri_split(config('lookup_url')) )[2];
2098 0         0 return "$path/$fl->[0]";
2099             } elsif ($fl->[0] =~ /\.tbz$/) {
2100 0         0 my $path = ( uri_split(config('lookup_url')) )[2];
2101 0         0 return "$path/$fl->[0]";
2102             } elsif ($fl->[0] =~ /\.tar\.gz$/) {
2103 0         0 my $path = ( uri_split(config('lookup_url')) )[2];
2104 0         0 return "$path/$fl->[0]";
2105             } elsif ($fl->[0] =~ /\.tgz$/) {
2106 0         0 my $path = ( uri_split(config('lookup_url')) )[2];
2107 0         0 return "$path/$fl->[0]";
2108             } elsif ($fl->[0] =~ /\.zip$/) {
2109 0         0 my $path = ( uri_split(config('lookup_url')) )[2];
2110 0         0 return "$path/$fl->[0]";
2111             } elsif ($fl->[0] =~ /\.fpkg$/) {
2112 0         0 my $path = ( uri_split(config('lookup_url')) )[2];
2113 0         0 return "$path/$fl->[0]";
2114             }
2115             ##DELME## if (config('lookup_url') =~ m!^file://!) {
2116             ##DELME## # Must prepend scheme, so that download() knows how to retrieve this
2117             ##DELME## # file with download_file(), which requires a URL that must begin
2118             ##DELME## # with a scheme, and file:// is the scheme for local files.
2119             ##DELME## $fl->[0] =~ s/"file://$fl->[0]";
2120             }
2121 0         0 die <
2122             App-Fetchware: run-time error. Fetchware failed to determine what URL it should
2123             use to download your software. This URL is based on the lookup_url you
2124             specified. See perldoc App::Fetchware.
2125             EOD
2126             }
2127              
2128              
2129              
2130              
2131             sub download {
2132 1     1 1 8 my ($temp_dir, $download_path) = @_;
2133              
2134             # Ensure we're passed just a path, and *not* a full URL.
2135 1 50       5 die <
2136             App-Fetchware: download() has been passed a full URL *not* only a path.
2137             download() should only be called with a path never a full URL. The URL you
2138             specified was [$download_path]
2139             EOD
2140              
2141 1         7 vmsg <
2142             Using [$download_path] as basis for determined our download_url using the user
2143             supplied mirrors.
2144             EOM
2145              
2146 1         4 msg "Downloading from url [$download_path] to temp dir [$temp_dir]";
2147              
2148 1         6 my $downloaded_file_path = download_file(PATH => $download_path);
2149 1         6 vmsg "Downloaded file to [$downloaded_file_path]";
2150              
2151 1         5 my $package_path = determine_package_path($temp_dir, $downloaded_file_path);
2152 1         17 msg "Determined package path to be [$package_path]";
2153              
2154 1         18 return $package_path;
2155             }
2156              
2157              
2158              
2159              
2160              
2161              
2162             sub determine_package_path {
2163 2     2 1 4244 my ($tempdir, $filename) = @_;
2164              
2165             # return $package_path, which stores the full path of where the file
2166             # HTTP::Tiny downloaded.
2167             ###BUGALERT### $tempdir is no longer used, so remove it from
2168             #determine_package_path() and probably download() too.
2169 2         5755 return catfile(cwd(), $filename)
2170             }
2171              
2172              
2173              
2174              
2175             sub verify {
2176 0     0 1 0 my ($download_path, $package_path) = @_;
2177              
2178 0         0 msg "Verifying the downloaded package [$package_path]";
2179              
2180 0         0 my $retval;
2181 0 0       0 unless (defined(config('verify_method'))) {
    0          
    0          
    0          
2182             # if gpg fails try
2183             # sha and if it fails try
2184             # md5 and if it fails die
2185 0         0 msg 'Trying to use gpg to cyptographically verify downloaded package.';
2186 0         0 my ($gpg_err, $sha_err, $md5_err);
2187 0         0 eval {$retval = gpg_verify($download_path)};
  0         0  
2188 0         0 $gpg_err = $@;
2189 0 0       0 if ($gpg_err) {
2190 0         0 msg <
2191             Cyptographic verification using gpg failed!
2192             GPG verification error [
2193             $@
2194             ]
2195             EOM
2196 0         0 warn $gpg_err;
2197             }
2198 0 0 0     0 if (! $retval or $gpg_err) {
2199 0         0 msg <
2200             Trying SHA1 verification of downloaded package.
2201             EOM
2202 0         0 eval {$retval = sha1_verify($download_path, $package_path)};
  0         0  
2203 0         0 $sha_err = $@;
2204 0 0       0 if ($sha_err) {
2205 0         0 msg <
2206             SHA1 verification failed!
2207             SHA1 verificaton error [
2208             $@
2209             ]
2210             EOM
2211 0         0 warn $sha_err;
2212             }
2213 0 0 0     0 if (! $retval or $sha_err) {
2214 0         0 msg <
2215             Trying MD5 verification of downloaded package.
2216             EOM
2217 0         0 eval {$retval = md5_verify($download_path, $package_path)};
  0         0  
2218 0         0 $md5_err = $@;
2219 0 0       0 if ($md5_err) {
2220 0         0 msg <
2221             MD5 verification failed!
2222             MD5 verificaton error [
2223             $@
2224             ]
2225             EOM
2226 0         0 warn $md5_err;
2227             }
2228             }
2229 0 0 0     0 if (! $retval or $md5_err) {
2230 0 0       0 die <
2231             App-Fetchware: run-time error. Fetchware failed to verify your downloaded
2232             software package. You can rerun fetchware with the --force option or add
2233             [verify_failure_ok 'True';] to your Fetchwarefile. See the section VERIFICATION
2234             FAILED in perldoc fetchware.
2235             EOD
2236             }
2237 0 0       0 if (config('verify_failure_ok')) {
2238 0         0 warn <
2239             App-Fetchware: run-time warning. Fetchware failed to verify the integrity of you
2240             downloaded file [$package_path]. This is ok, because you asked Fetchware to
2241             ignore its errors when it tries to verify the integrity of your downloaded file.
2242             You can also ignore the errors Fetchware printed out abover where it tried to
2243             verify your downloaded file. See perldoc App::Fetchware.
2244             EOW
2245 0         0 vmsg <
2246             Verification Failed! But you asked to ignore verification failures, so this
2247             failure is not fatal.
2248             EOM
2249 0         0 return 'warned due to verify_failure_ok'
2250             }
2251             }
2252             } elsif (config('verify_method') =~ /gpg/i) {
2253 0         0 vmsg <
2254             You selected gpg cryptographic verification. Verifying now.
2255             EOM
2256             ###BUGALERT### Should trap the exception {gpg,sha1,md5}_verify()
2257             #throws, and then add that error to the one here, otherwise the
2258             #error message here is never seen.
2259 0 0 0     0 gpg_verify($download_path)
2260             or die <
2261             App-Fetchware: run-time error. You asked fetchware to only try to verify your
2262             package with gpg or openpgp, but they both failed. See the warning above for
2263             their error message. See perldoc App::Fetchware.
2264             EOD
2265             } elsif (config('verify_method') =~ /sha1?/i) {
2266 0         0 vmsg <
2267             You selected SHA1 checksum verification. Verifying now.
2268             EOM
2269 0 0 0     0 sha1_verify($download_path, $package_path)
2270             or die <
2271             App-Fetchware: run-time error. You asked fetchware to only try to verify your
2272             package with sha, but it failed. See the warning above for their error message.
2273             See perldoc App::Fetchware.
2274             EOD
2275             } elsif (config('verify_method') =~ /md5/i) {
2276 0         0 vmsg <
2277             You selected MD5 checksum verification. Verifying now.
2278             EOM
2279 0 0 0     0 md5_verify($download_path, $package_path)
2280             or die <
2281             App-Fetchware: run-time error. You asked fetchware to only try to verify your
2282             package with md5, but it failed. See the warning above for their error message.
2283             See perldoc App::Fetchware.
2284             EOD
2285             } else {
2286 0         0 die <
2287             App-Fetchware: run-time error. Your fetchware file specified a wrong
2288             verify_method option. The only supported types are 'gpg', 'sha', 'md5', but you
2289 0         0 specified [@{[config('verify_method')]}]. See perldoc App::Fetchware.
2290             EOD
2291             }
2292 0         0 msg 'Verification succeeded.';
2293             }
2294              
2295              
2296              
2297              
2298              
2299              
2300             sub gpg_verify {
2301 0     0 1 0 my $download_path = shift;
2302              
2303 0         0 my $keys_file;
2304             # Attempt to download KEYS file in lookup_url's containing directory.
2305             # If that fails, try gpg_keys_url if defined.
2306             # Import downloaded KEYS file into a local gpg keyring using gpg command.
2307             # Determine what URL to use to download the signature file *only* from
2308             # lookup_url's host, so that we only download the signature from the
2309             # project's main mirror.
2310             # Download it.
2311             # gpg verify the sig using the downloaded and imported keys in our local
2312             # keyring.
2313              
2314             # Skip downloading and importing keys if we're called from inside a
2315             # fetchware package, which should already have a copy of our package's
2316             # KEYS file.
2317 0 0 0     0 unless (config('user_keyring')
      0        
2318             or (-e './pubring.gpg' and -e './secring.gpg')) {
2319             # Obtain a KEYS file listing everyone's key that signs this distribution.
2320 0 0       0 if (defined config('gpg_keys_url')) {
2321 0         0 $keys_file = no_mirror_download_file(config('gpg_keys_url'));
2322             } else {
2323 0         0 eval {
2324 0         0 $keys_file = no_mirror_download_file(config('lookup_url'). '/KEYS');
2325             };
2326 0 0       0 die <
2327             App-Fetchware: Fetchware was unable to download the gpg_key_url you specified or
2328 0         0 that fetchware tried appending asc, sig, or sign to [@{[config('lookup_url')]}].
2329             It needs to download this file to properly verify you software package. This is
2330             a fatal error, because failing to verify packages is a perferable default over
2331             potentially installing compromised ones. If failing to verify your software
2332             package is ok to you, then you may disable verification by adding
2333             verify_failure_ok 'On'; to your Fetchwarefile. See perldoc App::Fetchware.
2334             EOD
2335             }
2336              
2337             # Import downloaded KEYS file into a local gpg keyring using gpg
2338             # command.
2339 0 0       0 eval {
2340             # Add --homedir option if needed.
2341 0 0       0 if (config('user_keyring')) {
2342 0         0 run_prog('gpg', '--import', $keys_file);
2343             } else {
2344 0         0 run_prog('gpg', '--homedir', '.', '--import', $keys_file);
2345             }
2346 0         0 1;
2347             } or msg <
2348             App-Fetchware: Warning: gpg exits nonzero when importing large KEY files such as
2349             Apache's. However, despite exiting nonzero gpg still manages to import most of
2350             the keys into its keyring. It only exits nonzero, because some of the keys in
2351             the KEYS file had errors, and these key's errors were enough to cause gpg to
2352             exit nonzero, but not enough to cause it to completely fail importing the keys.
2353             EOM
2354             }
2355              
2356             # Download Signature using lookup_url.
2357 0         0 my $sig_file;
2358 0         0 my (undef, undef, $path, undef, undef) = uri_split($download_path);
2359 0         0 my ($scheme, $auth, undef, undef, undef) = uri_split(config('lookup_url'));
2360 0         0 my $sig_url;
2361 0         0 for my $ext (qw(asc sig sign)) {
2362 0         0 eval {
2363 0         0 $sig_url = uri_join($scheme, $auth, "$path.$ext", undef, undef);
2364 0         0 $sig_file = no_mirror_download_file($sig_url);
2365              
2366             };
2367             # If the file was downloaded stop trying other extensions.
2368 0 0       0 last if defined $sig_file;
2369             }
2370 0 0       0 die <
2371             App-Fetchware: Fetchware was unable to download the gpg_sig_url you specified or
2372             that fetchware tried appending asc, sig, or sign to [$sig_url]. It needs
2373             to download this file to properly verify you software package. This is a fatal
2374             error, because failing to verify packages is a perferable default over
2375             potentially installing compromised ones. If failing to verify your software
2376             package is ok to you, then you may disable verification by adding
2377             verify_failure_ok 'On'; to your Fetchwarefile. See perldoc App::Fetchware.
2378             EOD
2379              
2380              
2381             ###BUGALERT### # Use Crypt::OpenPGP if its installed.
2382             ###BUGALERT### if (eval {use Crypt::OpenPGP}) {
2383             ##DOESNTWORK?? # Build a pubring needed for verify.
2384             ##DOESNTWORK?? my $pubring = Crypt::OpenPGP::KeyRing->new();
2385             ##DOESNTWORK?? my $secring = Crypt::OpenPGP::KeyRing->new();
2386             ##DOESNTWORK??
2387             ##DOESNTWORK?? # Turn on gpg compatibility just in case its needed.
2388             ##DOESNTWORK?? my $pgp = Crypt::OpenPGP->new(
2389             ##DOESNTWORK?? Compat => 'GnuPG',
2390             ##DOESNTWORK?? PubRing => $pubring,
2391             ##DOESNTWORK?? SecRing => $secring,
2392             ##DOESNTWORK?? # Automatically download public keys as needed.
2393             ##DOESNTWORK?? AutoKeyRetrieve => 1,
2394             ##DOESNTWORK?? # Use this keyserver to download them from.
2395             ##DOESNTWORK?? KeyServer => 'pool.sks-keyservers.net',
2396             ##DOESNTWORK?? );
2397             ##DOESNTWORK??
2398             ##DOESNTWORK?? # Verify the downloaded file.
2399             ##DOESNTWORK?? my $retval = $pgp->verify(SigFile => $sig_file, Files => $CONFIG{PackagePath});
2400             ##DOESNTWORK?? if ($retval == 0) {
2401             ##DOESNTWORK?? warn "Crypt::OpenPGP failed due to invalid signature.";
2402             ##DOESNTWORK?? # return failure, because Fetchware failed to verify the downloaded
2403             ##DOESNTWORK?? # file.
2404             ##DOESNTWORK?? return undef;
2405             ##DOESNTWORK?? } elsif ($retval) {
2406             ##DOESNTWORK?? return 'Package verified';
2407             ##DOESNTWORK?? } else {
2408             ##DOESNTWORK?? # print warning about $pgp errstr message.
2409             ##DOESNTWORK?? my $errstr = $pgp->errstr();
2410             ##DOESNTWORK?? warn "Crypt::OpenPGP failed with message: [$errstr]";
2411             ##DOESNTWORK?? # return failure, because Fetchware failed to verify the downloaded
2412             ##DOESNTWORK?? # file.
2413             ##DOESNTWORK?? return undef;
2414             ##DOESNTWORK?? }
2415             ###BUGALERT### } else {
2416             ###BUGALERT### ###BUGALERT### eval the run_prog()'s below & add better error reporting in
2417             ###BUGALERT### ###BUGALERT### if Crypt::OpenPGP works ok remove gpg support & this if &
2418             ###BUGALERT### }
2419             #IPC::System::Simple dependency.
2420             #my standard format.
2421             # Use automatic key retrieval & a cool pool of keyservers
2422             ###BUGALERT## Give Crypt::OpenPGP another try with
2423             #pool.sks-keyservers.net
2424             ###BUGALERT### Should I cache the files gpg puts in its "homedir"? They
2425             #are the public keys that verify this fetchware package. Or should they
2426             #always be downloaded on demand as they are now??? But if verify() can
2427             #have keys cached inside the fetchware package does that mean that I
2428             #should open up this as an API for fetchware extensions????? I don't
2429             #know. I'll have to think more about this issue.
2430             #run_prog('gpg', '--keyserver', 'pool.sks-keyservers.net',
2431             # '--keyserver-options', 'auto-key-retrieve=1',
2432             # '--homedir', '.', "$sig_file");
2433              
2434             # Verify sig.
2435             # Add --homedir option if needed.
2436 0 0       0 if (config('user_keyring')) {
2437 0         0 run_prog('gpg', '--verify', $sig_file);
2438             } else {
2439 0         0 run_prog('gpg', '--homedir', '.', '--verify', $sig_file);
2440             }
2441              
2442             # Return true indicating the package was verified.
2443 0         0 return 'Package Verified';
2444             }
2445              
2446              
2447              
2448             sub sha1_verify {
2449 0     0 1 0 my ($download_path, $package_path) = @_;
2450              
2451 0         0 return digest_verify('SHA-1', $download_path, $package_path);
2452             }
2453              
2454              
2455              
2456             sub md5_verify {
2457 0     0 1 0 my ($download_path, $package_path) = @_;
2458              
2459 0         0 return digest_verify('MD5', $download_path, $package_path);
2460             }
2461              
2462              
2463              
2464             sub digest_verify {
2465 0     0 1 0 my ($digest_type, $download_path, $package_path) = @_;
2466              
2467             # Turn SHA-1 into sha1 & MD5 into md5.
2468 0         0 my $digest_ext = $digest_type;
2469 0         0 $digest_ext = lc $digest_type;
2470 0         0 $digest_ext =~ s/-//g;
2471             ##subify get_sha_sum()
2472 0         0 my $digest_file;
2473             # Obtain a sha sum file.
2474 0 0       0 if (defined config("${digest_ext}_url")) {
2475 0         0 my (undef, undef, $path, undef, undef) = uri_split($download_path);
2476 0         0 my ($scheme, $auth, undef, undef, undef) =
2477             uri_split(config("${digest_ext}_url"));
2478 0         0 my $digest_url = uri_join($scheme, $auth, $path, undef, undef);
2479 0         0 msg "Downloading $digest_ext digest using [$digest_url.$digest_ext]";
2480 0         0 $digest_file = no_mirror_download_file("$digest_url.$digest_ext");
2481             } else {
2482 0         0 eval {
2483 0         0 my (undef, undef, $path, undef, undef) = uri_split($download_path);
2484 0         0 my ($scheme, $auth, undef, undef, undef) =
2485             uri_split(config('lookup_url'));
2486 0         0 my $digest_url = uri_join($scheme, $auth, $path, undef, undef);
2487 0         0 msg "Downloading $digest_ext digest using [$digest_url.$digest_ext]";
2488 0         0 $digest_file = no_mirror_download_file("$digest_url.$digest_ext");
2489             };
2490 0 0       0 if ($@) {
2491 0         0 die <
2492             App-Fetchware: Fetchware was unable to download the $digest_type sum it needs to
2493             download to properly verify you software package. This is a fatal error, because
2494             failing to verify packages is a perferable default over potentially installin
2495             compromised ones. If failing to verify your software package is ok to you, then
2496             you may disable verification by adding verify_failure_ok 'On'; to your
2497             Fetchwarefile. See perldoc App::Fetchware.
2498             EOD
2499             }
2500             }
2501            
2502             ###BUGALERT###subify calc_sum()
2503             # Open the downloaded software archive for reading.
2504 0         0 my $package_fh = safe_open($package_path, <
2505             App-Fetchware: run-time error. Fetchware failed to open the file it downloaded
2506             while trying to read it in order to check its MD5 sum. The file was
2507             [$package_path]. See perldoc App::Fetchware.
2508             EOD
2509              
2510             # Do Digest type checking myself, because until Digest.pm 1.17,
2511             # Digest->new() could run any Perl code you specify or a user does causing
2512             # the security hole. Instead of use Digest 1.17, just avoid it altogether.
2513 0         0 my $digest;
2514 0 0       0 if ($digest_type eq 'MD5') {
    0          
2515 0         0 $digest = Digest::MD5->new();
2516             } elsif ($digest_type eq 'SHA-1') {
2517 0         0 $digest = Digest::SHA->new();
2518             } else {
2519 0         0 die <
2520             EOD
2521             }
2522              
2523             # Digest requires the filehandle to have binmode set.
2524 0         0 binmode $package_fh;
2525              
2526 0         0 my $calculated_digest;
2527 0         0 eval {
2528             # Add the file for digesting.
2529 0         0 $digest->addfile($package_fh);
2530             # Actually digest it.
2531 0         0 $calculated_digest = $digest->hexdigest();
2532             };
2533 0 0       0 if ($@) {
2534 0         0 die <
2535             App-Fetchware: run-time error. Digest::$digest_type croak()ed an error [$@].
2536             See perldoc App::Fetchware.
2537             EOD
2538             }
2539              
2540 0 0       0 close $package_fh or die <
2541             App-Fetchware: run-time error Fetchware failed to close the file
2542             [$package_path] after opening it for reading. See perldoc App::Fetchware.
2543             EOD
2544              
2545             ###subify compare_sums();
2546             # Open the downloaded software archive for reading.
2547 0         0 my $digest_fh = safe_open($digest_file, <
2548             App-Fetchware: run-time error. Fetchware failed to open the $digest_type file it
2549             downloaded while trying to read it in order to check its $digest_type sum. The file was
2550             [$digest_file]. See perldoc App::Fetchware.
2551             EOD
2552             # Will only check the first checksum it finds.
2553 0         0 while (<$digest_fh>) {
2554 0 0       0 next if /^\s+$/; # skip whitespace only lines just in case.
2555 0         0 my @fields = split ' '; # Defaults to $_, which is filled in by <>
2556              
2557             # Search the @fields for a regex that is either 32 hex for md5 or 40 hex
2558             # for sha1.
2559 0         0 my ($checksum) = grep /^[0-9a-f]{32}(?:[0-9a-f]{8})?$/i, @fields;
2560              
2561             # Skip trying to verify the $checksum if we failed to find it in this
2562             # line, and instead skip to the next line in the checksum file to try to
2563             # find a $checksum.
2564 0 0       0 next unless defined $checksum;
2565              
2566 0 0       0 if ($checksum eq $calculated_digest) {
    0          
2567 0         0 return 'Package verified';
2568             # Sometimes a = is appended to make it 32bits.
2569             } elsif ("$checksum=" eq $calculated_digest) {
2570 0         0 return 'Package verified';
2571             }
2572             }
2573 0         0 close $digest_fh;
2574              
2575             # Return failure, because fetchware failed to verify by checksum
2576 0         0 return undef;
2577             }
2578              
2579              
2580              
2581              
2582             sub unarchive {
2583 49     49 1 124 my $package_path = shift;
2584              
2585 49         286 msg "Unarchiving the downloaded package [$package_path]";
2586              
2587              
2588 49         271 my ($format, $files) = list_files($package_path);
2589            
2590             { # Encloseing block for $", which prints a \n between each array element.
2591 49         70 local $" = "\n";
  49         156  
2592 49         320 vmsg <
2593             Files are:
2594             [
2595             @$files
2596             ]
2597             EOM
2598             } # Enclosing block for $"
2599              
2600             # Ensure no files starting with an absolute path get extracted
2601             # And determine $build_path.
2602 49         325 my $build_path = check_archive_files($files);
2603              
2604 49         235 vmsg "Unarchiving $format archive [$package_path].";
2605 49         272 unarchive_package($format, $package_path);
2606            
2607 49         447 msg "Determined build path to be [$build_path]";
2608 49         485 return $build_path;
2609             }
2610              
2611              
2612              
2613              
2614              
2615              
2616             sub list_files {
2617 49     49 1 90 my $package_path = shift;
2618              
2619             # List files based on archive format.
2620 49         132 my $files;
2621             my $format;
2622 49 50       850 if ($package_path =~ /\.(t(gz|bz|xz|Z))|(tar\.(gz|bz2|xz|Z))|.fpkg$/) {
    0          
2623 49         101 $format = 'tar';
2624 49         252 vmsg <
2625             Listing files in your tar format archive [$package_path].
2626             EOM
2627 49         295 $files = list_files_tar($package_path);
2628             } elsif ($package_path =~ /\.zip$/) {
2629 0         0 $format = 'zip';
2630 0         0 vmsg <
2631             Listing files in your zip format archive [$package_path].
2632             EOM
2633 0         0 $files = list_files_zip($package_path);
2634             } else {
2635 0         0 die <
2636             App-Fetchware: Fetchware failed to determine what type of archive your
2637             downloaded package is [$package_path]. Fetchware only supports zip and tar
2638             format archives.
2639             EOD
2640             }
2641              
2642             # unarchive_package() needs $format, so return that too.
2643 49         149 return $format, $files;
2644             }
2645              
2646              
2647              
2648             sub list_files_tar {
2649 49     49 1 72 my $path_to_tar_archive = shift;
2650              
2651 49         489 my $tar_iter = Archive::Tar->iter($path_to_tar_archive, 1, );
2652 49 50       66557 die <
2653             App-Fetchware: fetchware failed to create a new Archive::Tar iterator. The
2654 0         0 Archive::Tar error message was [@{[Archive::Tar->error()]}].
2655             EOD
2656              
2657             # Iterate over the the archive one file at a time to save memory on big
2658             # archives suchs a say MariaDB or the Linux kernel.
2659 49         95 my @files;
2660 49         169 while (my $file = $tar_iter->() ) {
2661 196         80924 push @files, $file->full_path();
2662             }
2663              
2664 49         29511 return \@files;
2665             }
2666              
2667              
2668             { # Begin %zip_error_codes hash.
2669             my %zip_error_codes = (
2670             AZ_OK => 'Everything is fine.',
2671             AZ_STREAM_END =>
2672             'The read stream (or central directory) ended normally.',
2673             AZ_ERROR => 'There was some generic kind of error.',
2674             AZ_FORMAT_ERROR => 'There is a format error in a ZIP file being read.',
2675             AZ_IO_ERROR => 'There was an IO error'
2676             );
2677              
2678              
2679              
2680             sub list_files_zip {
2681 0     0 1 0 my $path_to_zip_archive = shift;
2682              
2683 0         0 my $zip = Archive::Zip->new();
2684              
2685 0         0 my $zip_error;
2686 0 0       0 if(($zip_error = $zip->read($path_to_zip_archive)) ne AZ_OK) {
2687 0         0 die <
2688             App-Fetchware: Fetchware failed to read in the zip file [$path_to_zip_archive].
2689             The zip error message was [$zip_error_codes{$zip_error}].
2690             EOD
2691             }
2692              
2693             # List the zip files "members," which are annoying classes not just a list
2694             # of file names. I could use the memberNames() method, but that method
2695             # returns their "internal" names, but I want their external names, what
2696             # their names will be on your file system.
2697 0         0 my @members = $zip->members();
2698              
2699 0         0 my @external_filenames;
2700 0         0 for my $member (@members) {
2701 0         0 push @external_filenames, $member->fileName();
2702             }
2703              
2704             # Return list of "external" filenames.
2705 0         0 return \@external_filenames;
2706             }
2707              
2708              
2709              
2710             sub unarchive_package {
2711 49     49 1 79 my ($format, $package_path) = @_;
2712              
2713 49 50       300 unarchive_tar($package_path) if $format eq 'tar';
2714 49 50       279 unarchive_zip($package_path) if $format eq 'zip';
2715             }
2716              
2717              
2718              
2719             sub unarchive_tar {
2720 49     49 1 85 my $path_to_tar_archive = shift;
2721              
2722 49         252 my @extracted_files = Archive::Tar->extract_archive($path_to_tar_archive);
2723             # extract_archive() returns false if the extraction failed, which will
2724             # create an array with one false element, so I have test if tha one element
2725             # is false not something like if (@extracted_files), because if
2726             # extract_archive() returns undef on failure not empty list.
2727 49 50       731483 unless ($extracted_files[0]) {
2728 0         0 die <
2729             App-Fetchware: Fetchware failed to extract your archive [$path_to_tar_archive].
2730 0         0 The error message from Archive::Tar was [@{[Archive::Tar->error()]}].
2731             EOD
2732             } else {
2733 49         237 return @extracted_files;
2734             }
2735             }
2736              
2737              
2738              
2739             sub unarchive_zip {
2740 0     0 1 0 my $path_to_zip_archive = shift;
2741              
2742 0         0 my $zip = Archive::Zip->new();
2743              
2744 0         0 my $zip_error;
2745 0 0       0 if(($zip_error = $zip->read($path_to_zip_archive)) ne AZ_OK) {
2746 0         0 die <
2747             App-Fetchware: Fetchware failed to read in the zip file [$path_to_zip_archive].
2748             The zip error message was [$zip_error_codes{$zip_error}].
2749             EOD
2750             }
2751              
2752 0 0       0 if (($zip_error = $zip->extractTree()) ne AZ_OK) {
2753 0         0 die <
2754             App-Fetchware: Fetchware failed to extract the zip file [$path_to_zip_archive].
2755             The zip error message was [$zip_error_codes{$zip_error}].
2756             EOD
2757             } else {
2758 0         0 return 'Extraced files successfully.';
2759             }
2760             }
2761              
2762             } # End %zip_error_codes
2763              
2764              
2765              
2766              
2767             sub check_archive_files {
2768 52     52 1 584 my $files = shift;
2769              
2770              
2771             # Determine if *all* files are in the same directory.
2772 52         59 my %dir;
2773 52         162 for my $path (@$files) {
2774             # Skip Fetchwarefiles.
2775 210 100       425 next if $path eq './Fetchwarefile';
2776 161 100       419 if (file_name_is_absolute($path)) {
2777 1         9 my $error = <
2778             App-Fetchware: run-time error. The archive you asked fetchware to download has
2779             one or more files with an absolute path. Absolute paths in archives is
2780             dangerous, because the files could potentially overwrite files anywhere in the
2781             filesystem including important system files. That is why this is a fatal error
2782             that cannot be ignored. See perldoc App::Fetchware.
2783             Absolute path [$path].
2784             EOE
2785 1         2 $error .= "[\n";
2786 1         4 $error .= join("\n", @$files);
2787 1         2 $error .= "\n]\n";
2788            
2789 1         5 die $error;
2790             }
2791              
2792 160         1042 my ($volume,$directories,$file) = splitpath($path);
2793 160         1394 my @dirs = splitdir($directories);
2794             # Skip empty directories.
2795 160 100       674 next unless @dirs;
2796              
2797 111         650 $dir{$dirs[0]}++;
2798             }
2799              
2800              
2801 51         117 my $i = 0;
2802 51         157 for my $dir (keys %dir) {
2803 51         68 $i++;
2804 51 50       92 warn < 1;
2805             App-Fetchware: run-time warning. The archive you asked Fetchware to download
2806             does *not* have *all* of its files in one and only one containing directory.
2807             This is not a problem for fetchware, because it does all of its downloading,
2808             unarchive, and building in a temporary directory that makes it easy to
2809             automatically delete all of the files when fetchware is done with them. See
2810             perldoc App::Fetchware.
2811             EOD
2812            
2813             # Return $build_path
2814 51         74 my $build_path = $dir;
2815 51         142 return $build_path;
2816             }
2817             }
2818              
2819              
2820              
2821              
2822             sub build {
2823 0     0 1 0 my $build_path = shift;
2824              
2825 0         0 msg "Building your package in [$build_path]";
2826              
2827 0         0 vmsg "changing Directory to build path [$build_path]";
2828 0 0       0 chdir $build_path or die <
2829             App-Fetchware: run-time error. Failed to chdir to the directory fetchware
2830             unarchived [$build_path]. OS error [$!].
2831             EOD
2832              
2833              
2834             # If build_commands is set, then all other build config options are ignored.
2835 0 0 0     0 if (defined config('build_commands')) {
    0 0        
2836 0         0 vmsg 'Building your package using user specified build_commands.';
2837 0         0 run_star_commands(config('build_commands'));
2838             # Otherwise handle the other options properly.
2839             } elsif (
2840             defined config('configure_options')
2841             or defined config('prefix')
2842             or defined config('make_options')
2843             ) {
2844              
2845             # Set up configure_options and prefix, and then run ./configure.
2846 0 0       0 vmsg "Running configure with options [@{[config('configure_options')]}]"
  0         0  
2847             if defined config('configure_options');
2848 0         0 run_configure();
2849              
2850             # Next, make.
2851 0 0       0 if (defined config('make_options')) {
2852 0         0 vmsg 'Executing make to build your package';
2853 0         0 run_prog('make', config('make_options'))
2854             } else {
2855 0         0 vmsg 'Executing make to build your package';
2856 0         0 run_prog('make');
2857             }
2858              
2859             # Execute the default commands.
2860             } else {
2861 0         0 vmsg 'Running default build commands [./configure] and [make]';
2862 0         0 run_prog($_) for qw(./configure make);
2863             }
2864            
2865             # Return success.
2866 0         0 msg 'The build was successful.';
2867 0         0 return 'build succeeded';
2868             }
2869              
2870             ###BUGALERT### Add a *() API REFERENCE section for each fetchware API
2871             #subroutine, and subify the API subs that aren't yet.
2872              
2873              
2874              
2875              
2876             sub run_star_commands {
2877 4     4 1 1690 my @star_commands = @_;
2878              
2879             # Support multiple options like star_command './configure', 'make';
2880             # Should be called like run_star_commands(config'*_commands')), and
2881             # config('star_commands') returns a list of *all* star_commands.
2882 4         10 for my $star_command (@star_commands) {
2883             # If a /,\s+/ is present in a $star_command
2884             # To support: star_commands './configure, make';
2885 6 100       38 if ($star_command =~ /,\s*/) {
2886             # split on it, and run each resulting command.
2887 3         19 my @star_commands = split /,\s*/, $star_command;
2888 3         8 for my $split_star_command (@star_commands) {
2889 6         39 run_prog($split_star_command);
2890             }
2891             # Or just run the one command.
2892             } else {
2893 3         14 run_prog($star_command);
2894             }
2895             }
2896             }
2897              
2898              
2899              
2900             ###BUGALERT### Add an uninstall() option to instead edit the AutoTools paths
2901             #into relative ones.
2902              
2903             sub run_configure {
2904 49     49 1 150 my $configure = './configure';
2905 49 50       221 if (config('configure_options')) {
2906             # Support multiple options like configure_options '--prefix', '.';
2907 0         0 for my $configure_option (config('configure_options')) {
2908 0         0 $configure .= " $configure_option";
2909             }
2910             }
2911            
2912 49 50       170 if (config('prefix')) {
2913 0 0       0 if ($configure =~ /--prefix/) {
2914 0         0 die <
2915             App-Fetchware: run-time error. You specified both the --prefix option twice.
2916             Once in 'prefix' and once in 'configure_options'. You may only specify prefix
2917             once in either configure option. See perldoc App::Fetchware.
2918             EOD
2919             } else {
2920             ###BUGALERT## At least under AutoTools, --prefix needs to be a full
2921             #path. Should I check for this here? Ignore this possible error, and
2922             #just let ./configure check its own arguments. Or add syntax
2923             #checking to configuration subroutines???
2924 0         0 $configure .= " --prefix=@{[config('prefix')]}";
  0         0  
2925             }
2926             }
2927            
2928             # Finally run ./configure.
2929 49         270 run_prog($configure);
2930              
2931             # Return success.
2932 49         316 return 'Configure successful';
2933             }
2934              
2935              
2936              
2937             sub install {
2938 88     88 1 277 my $build_path = shift;
2939              
2940             # Skip installation if the user requests it.
2941 88 50       378 if (config('no_install')) {
2942 0         0 msg <
2943             Installation skipped, because no_install is specified in your Fetchwarefile.
2944             EOM
2945 0         0 return 'installation skipped!' ;
2946             }
2947              
2948 88         698 msg 'Installing your software package.';
2949              
2950 88         544 chdir_unless_already_at_path($build_path);
2951              
2952 88 50       1036 if (defined config('install_commands')) {
2953 0         0 vmsg 'Installing your package using user specified commands.';
2954 0         0 run_star_commands(config('install_commands'));
2955             } else {
2956 88 50       343 if (defined config('make_options')) {
2957 0         0 vmsg <
2958             Installing package using default command [make] with user specified make options.
2959             EOM
2960 0         0 run_prog('make', config('make_options'), 'install', );
2961             } else {
2962 88         335 vmsg <
2963             Installing package using default command [make].
2964             EOM
2965 88         618 run_prog('make', 'install');
2966             }
2967             }
2968              
2969 88         920 msg 'Installation succeeded';
2970             # Return success.
2971 88         583 return 'install succeeded';
2972             }
2973              
2974              
2975              
2976              
2977              
2978             sub chdir_unless_already_at_path {
2979 139     139 1 10618 my $path = shift;
2980              
2981             # chdir() to $path unless its already our cwd.
2982             # This is needed, because we'll inherit the "child's" chdir if stay_root is
2983             # turned on, because stay_root does *not* fork and drop privs, which
2984             # typicially causes the child's chdir to be "inherited" by the parent,
2985             # because there is no parent and there is no child due to *not* forking.
2986 139 50       337228 unless ( dir(cwd())->dir_list(-1, 1) eq $path ) {
2987 139 50       18875 chdir($path) or die <
2988             fetchware: fetchware failed to chdir to the build directory [$path]. It
2989             needs to chdir() to this directory, so that it can finish your fetchware
2990             command.
2991             EOD
2992 139         1303 vmsg "chdir()'d to the necessary path [$path].";
2993             }
2994             }
2995              
2996              
2997              
2998              
2999              
3000              
3001             ###BUGALERT### Is uninstall() calling API subs a bug??? Should it just use the
3002             #lower level library functions of these tools. Have it do this after I subify
3003             #the rest of the API subs like I've done to lookup and download.
3004             ###BUGALERT### NOT TESTED!!! There is no t/App-Fetchware-uninstall.t test
3005             #file!!! cmd_uninstall(), which uses uninstall(), is tested, but not uninstall()
3006             #directly!!!
3007             sub uninstall {
3008 49     49 1 100 my $build_path = shift;
3009              
3010 49         282 msg "Uninstalling package unarchived at path [$build_path]";
3011              
3012 49         239 chdir_unless_already_at_path($build_path);
3013              
3014 49 50       614 if (defined config('uninstall_commands')) {
3015 0         0 vmsg 'Uninstalling using user specified uninstall commands.';
3016 0         0 run_star_commands(config('uninstall_commands'));
3017             } else {
3018             # Set up configure_options and prefix, and then run ./configure, because
3019             # Autotools uses full paths that ./configure sets up, and these paths
3020             # change from install time to uninstall time.
3021 49         191 vmsg q{Uninstalling using AutoTool's default of make uninstall};
3022              
3023 49         266 vmsg q{Running AutoTool's default ./configure};
3024 49         297 run_configure();
3025 49 50       437 if (defined config('make_options')) {
3026 0         0 vmsg <
3027             Running AutoTool's default make uninstall with user specified make options.
3028             EOM
3029 0         0 run_prog('make', config('make_options'), 'uninstall');
3030             } else {
3031 49         228 vmsg <
3032             Running AutoTool's default make uninstall.
3033             EOM
3034 49         310 run_prog('make', 'uninstall');
3035             }
3036             }
3037              
3038              
3039 49         609 msg <
3040             Package uninstalled from system, but still installed in Fetchware's database.
3041             EOM
3042             # Return success.
3043 49         402 return 'uninstall succeeded';
3044             }
3045              
3046              
3047              
3048              
3049             sub upgrade {
3050 9     9 1 2874 my ($download_path, $fetchware_package_path) = @_;
3051              
3052             # I only need the basename.
3053 9         21 my $download_path_basename = file($download_path)->basename();
3054 9         521 my $upgrade_name_basename =
3055             file( $fetchware_package_path)->basename();
3056 9         320 vmsg <
3057             Shortened the new download url [$download_path_basename] and the installed
3058             package's [$upgrade_name_basename] into just their basenames.
3059             EOM
3060              
3061             # Strip trailing garbage to normalize their names, so that they can be
3062             # compared to each other.
3063             ###BUGALERT### This comparision is quite fragile. Figure out a better way to
3064             #do this!!!
3065 9         20 $upgrade_name_basename =~ s/\.fpkg$//;
3066 9         33 $download_path_basename
3067             =~ s/(\.(?:zip|tgz|tbz|txz|fpkg)|(?:\.tar\.(gz|bz2|xz|Z)?))$//;
3068 9         21 vmsg <
3069             Striped the new download url [$download_path_basename] and the installed
3070             package's [$upgrade_name_basename] of their file extensions.
3071             EOM
3072              
3073             # Check if $upgrade_name_basename and $download_path_basename are eq, and if
3074             # they are return false indicating that this program should not be upgraded,
3075             # because the version available for upgrading is the same as the currently
3076             # installed version.
3077 9 100       26 return 0 if $upgrade_name_basename eq $download_path_basename;
3078              
3079             # Transform both competing filenames into a string of version numbers.
3080              
3081             # Use lookup_by_versionstring() to determine which version of the same
3082             # program is "newer."
3083 6         16 my $sorted_file_names = lookup_by_versionstring(
3084             [
3085             [$upgrade_name_basename, 'placeholder'],
3086             [$download_path_basename, 'placeholder'],
3087             ]
3088             );
3089              
3090 6 100 66     21 if ($sorted_file_names->[0][0] eq $download_path_basename
3091             # Make sure cmd_upgrade() does not upgrade when the latest version is
3092             # the same as the currently installed version ($upgrade_name_basename).
3093             and $sorted_file_names->[0][0] ne $upgrade_name_basename) {
3094             # The latest version we can download ($download_path_basename) is newer
3095             # than the currently installed version ($upgrade_name_basename), so we
3096             # should upgrade.
3097 3         16 return 1;
3098             } else {
3099             # Currenlty installed version ($upgrade_name_basename) is equal to the
3100             # latest version available for download ($download_path_basename), so
3101             # return false indicating that we sould not upgrade.
3102 3         18 return 0;
3103             }
3104             }
3105              
3106              
3107              
3108              
3109             sub check_syntax {
3110              
3111             # Use check_config_options() to run config() a bunch of times to check the
3112             # already parsed Fetchwarefile.
3113 170     170 1 3194 return check_config_options(
3114             BothAreDefined => [ [qw(build_commands)],
3115             [qw(prefix configure_options make_options)] ],
3116             Mandatory => [ 'program', <
3117             App-Fetchware: Your Fetchwarefile must specify a program configuration
3118             option. Please add one, and try again.
3119             EOM
3120             Mandatory => [ 'mirror', <
3121             App-Fetchware: Your Fetchwarefile must specify a mirror configuration
3122             option. Please add one, and try again.
3123             EOM
3124             Mandatory => [ 'lookup_url', <
3125             App-Fetchware: Your Fetchwarefile must specify a lookup_url configuration
3126             option. Please add one, and try again.
3127             EOM
3128             ConfigOptionEnum => ['lookup_method', [qw(timestamp versionstring)] ],
3129             ConfigOptionEnum => ['verify_method', [qw(gpg sha1 md5)] ],
3130             );
3131             }
3132              
3133              
3134              
3135              
3136              
3137             sub check_config_options {
3138 179     179 1 502 my @args = @_;
3139              
3140 179         263 my @both_are_defined;
3141             my @mandatory;
3142 0         0 my @config_option_enum;
3143              
3144             # Process arguments, and check that they were specified correctly.
3145             # Loop over @args 2 at a time hence the $i += 2 instead of $i++.
3146 179         636 for( my $i = 0; $i < @args; $i += 2 ) {
3147 1034         1367 my( $type, $AnB ) = @args[ $i, $i+1 ];
3148 1034 100       1429 die <
3149             App-Fetchware: check_config_options()'s even arguments must be an array
3150             reference. Please correct your arguments, and try again.
3151             EOD
3152 1033 100       1273 die <
3153             App-Fetchware: check_config_options()'s even arguments must be an array
3154             reference with exactly two elements in it. Please correct and try again.
3155             EOD
3156              
3157 1032 100       1698 if ($type eq 'BothAreDefined') {
    100          
    50          
3158 173         545 push @both_are_defined, $AnB;
3159             } elsif ($type eq 'Mandatory') {
3160 515         829 push @mandatory, $AnB;
3161             } elsif ($type eq 'ConfigOptionEnum') {
3162 344         643 push @config_option_enum, $AnB;
3163             } else {
3164 0         0 die <
3165             App-Fetchware: check_config_options() only supports types 'BothAreDefined',
3166             'Mandatory', and 'ConfigOptionEnum.' Please specify one of these, and try again.
3167             EOD
3168             }
3169             }
3170              
3171             # Process @both_are_defined by checking if both of the elements in the
3172             # provided arrayrefs are "both defined", and if they are "both defined"
3173             # throw an exception.
3174 177         352 for my $AnB (@both_are_defined) {
3175 173         261 my ($A, $B) = @$AnB;
3176              
3177 173         229 my @A_defined;
3178             my @B_defined;
3179              
3180             # Check which ones are defined in both $A and $B
3181             {
3182             # the config() call will call the specified strings of which many
3183             # are expected to be uninitialized. Because we expect them to be
3184             # uninitialized, we use that behavior to determine if they have been
3185             # specified in the users Fetchwarefile, and if an option was not
3186             # specified, then undef is returned by config(). Since, we expect
3187             # lots of undef warnings, we'll disable them.
3188 50     50   583 no warnings 'uninitialized';
  50         103  
  50         8720  
  173         178  
3189 173         314 @A_defined = grep {config($_)} @$A;
  173         410  
3190 173         332 @B_defined = grep {config($_)} @$B;
  519         713  
3191             }
3192              
3193 173 100 100     728 if (@A_defined > 0 and @B_defined > 0) {
3194 1         34 die <
3195             App-Fetchware: Your Fetchwarefile has incompatible configuration options.
3196             You specified configuration options [@$A] and [@$B], but these options are not
3197             compatible with each other. Please specifiy either [@$A] or [@$B] not both.
3198             EOD
3199             }
3200             }
3201              
3202              
3203             # Process @mandatory options by checking if they're defined, and if not
3204             # throwing the specified exception.
3205 176         354 for my $AnB (@mandatory) {
3206 515         720 my ($option, $error_message) = @$AnB;
3207              
3208 515 100       822 die $error_message if not defined config($option);
3209             }
3210              
3211              
3212             # Process @config_option_enum.
3213 175         323 for my $AnB (@config_option_enum) {
3214 344         464 my ($option, $enumerations) = @$AnB;
3215              
3216             # Ditch uninitialized warnings, because I'm using undef to mean
3217             # unspecified, so undef is not something unexpected to bother warning
3218             # about, but something that will happen all the time.
3219             {
3220 50     50   262 no warnings 'uninitialized';
  50         76  
  50         10369  
  344         386  
3221            
3222             # Only test the @enumerations if $option was specified in the
3223             # Fetchwarefile.
3224 344 100       506 if (config($option)) {
3225              
3226             # Only one @enumerations should equal $option not more than one, hence
3227             # the == 1 part.
3228 171 100       190 die <
  513         755  
3229             App-Fetchware: You specified the option [$option], but failed to specify only
3230             one of its acceptable values [@$enumerations]. Please change the value you
3231 1         2 specified [@{[config($option)]}] to one of the acceptable ones listed above, and try again.
3232             EOD
3233             }
3234              
3235             }
3236             }
3237            
3238 174         455 return 'Syntax Ok';
3239             }
3240              
3241              
3242              
3243              
3244             sub end {
3245             # Use cleanup_tempdir() to cleanup your tempdir for us.
3246 168     168 1 2222 cleanup_tempdir();
3247             }
3248              
3249              
3250              
3251             1;
3252              
3253              
3254              
3255              
3256              
3257              
3258              
3259              
3260              
3261              
3262              
3263              
3264              
3265              
3266              
3267              
3268              
3269             sub hook ($$) {
3270 2     2 1 785 my ($sub_to_hook, $callback) = @_;
3271              
3272 2 100       24 die <can($sub_to_hook);
3273             App-Fetchware: The subroutine [$sub_to_hook] you attempted to override does
3274             not exist in this package. Perhaps you misspelled it, or it does not exist in
3275             the current package.
3276             EOD
3277              
3278 1         4 override $sub_to_hook => $callback;
3279              
3280             # Overriding the subroutine is not enough, because it is overriding it
3281             # inside App::Fetchware, so I need to also override the subroutine inside
3282             # hook()'s caller as done below.
3283             {
3284 50     50   276 no warnings 'redefine';
  50         79  
  50         5393  
  1         51  
3285 1         5 clone($sub_to_hook => (from => 'App::Fetchware', to => caller()));
3286             }
3287             }
3288              
3289              
3290             ###BUGALERT### Add an section of use cases. You know explaing why you'd use
3291             #no_install, or why'd you'd use look, or why And so on.....
3292              
3293              
3294              
3295              
3296             ###BUGALERT### Create a fetchware command to do this for users perhaps even
3297             #plugin it into Module::Starter???? If possible.
3298             ####BUGALERT## Even have so that you can specify which API subs you want to
3299             #override or avoid overriding, and then it will create the skelton with stubs
3300             #for those API sub already having some empty POD crap and the correct
3301             #prototypes.
3302              
3303              
3304              
3305              
3306              
3307              
3308              
3309              
3310             ###BUGALERT### Actually implement croak or more likely confess() support!!!
3311              
3312             __END__