line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package App::Fetchware::Util; |
2
|
|
|
|
|
|
|
our $VERSION = '1.016'; # VERSION: generated by DZP::OurPkgVersion |
3
|
|
|
|
|
|
|
# ABSTRACT: Miscelaneous functions for App::Fetchware. |
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
|
53
|
|
|
53
|
|
22672
|
use strict; |
|
53
|
|
|
|
|
58
|
|
|
53
|
|
|
|
|
1202
|
|
8
|
53
|
|
|
53
|
|
176
|
use warnings; |
|
53
|
|
|
|
|
77
|
|
|
53
|
|
|
|
|
1222
|
|
9
|
|
|
|
|
|
|
|
10
|
53
|
|
|
|
|
3058
|
use File::Spec::Functions qw(catfile catdir splitpath splitdir rel2abs |
11
|
53
|
|
|
53
|
|
1369
|
file_name_is_absolute rootdir tmpdir); |
|
53
|
|
|
|
|
1846
|
|
12
|
53
|
|
|
53
|
|
1647
|
use Path::Class; |
|
53
|
|
|
|
|
109269
|
|
|
53
|
|
|
|
|
2024
|
|
13
|
53
|
|
|
53
|
|
28491
|
use Net::FTP; |
|
53
|
|
|
|
|
1817158
|
|
|
53
|
|
|
|
|
3101
|
|
14
|
53
|
|
|
53
|
|
38552
|
use HTTP::Tiny; |
|
53
|
|
|
|
|
408655
|
|
|
53
|
|
|
|
|
1848
|
|
15
|
53
|
|
|
53
|
|
2789
|
use Perl::OSType 'is_os_type'; |
|
53
|
|
|
|
|
1669
|
|
|
53
|
|
|
|
|
2413
|
|
16
|
53
|
|
|
53
|
|
215
|
use Cwd; |
|
53
|
|
|
|
|
77
|
|
|
53
|
|
|
|
|
2513
|
|
17
|
53
|
|
|
53
|
|
6821
|
use App::Fetchware::Config ':CONFIG'; |
|
53
|
|
|
|
|
90
|
|
|
53
|
|
|
|
|
7467
|
|
18
|
53
|
|
|
53
|
|
24147
|
use File::Copy 'cp'; |
|
53
|
|
|
|
|
78406
|
|
|
53
|
|
|
|
|
2922
|
|
19
|
53
|
|
|
53
|
|
241
|
use File::Temp 'tempdir'; |
|
53
|
|
|
|
|
62
|
|
|
53
|
|
|
|
|
1891
|
|
20
|
53
|
|
|
53
|
|
196
|
use File::stat; |
|
53
|
|
|
|
|
58
|
|
|
53
|
|
|
|
|
347
|
|
21
|
53
|
|
|
53
|
|
2205
|
use Fcntl qw(S_ISDIR :flock S_IMODE); |
|
53
|
|
|
|
|
58
|
|
|
53
|
|
|
|
|
6271
|
|
22
|
|
|
|
|
|
|
# Privileges::Drop only works on Unix, so only load it on Unix. |
23
|
53
|
|
|
53
|
|
26472
|
use if is_os_type('Unix'), 'Privileges::Drop'; |
|
53
|
|
|
|
|
378
|
|
|
53
|
|
|
|
|
230
|
|
24
|
53
|
|
|
53
|
|
162021
|
use POSIX '_exit'; |
|
53
|
|
|
|
|
63
|
|
|
53
|
|
|
|
|
383
|
|
25
|
53
|
|
|
53
|
|
26810
|
use Sub::Mage; |
|
53
|
|
|
|
|
278778
|
|
|
53
|
|
|
|
|
332
|
|
26
|
53
|
|
|
53
|
|
25118
|
use URI::Split qw(uri_split uri_join); |
|
53
|
|
|
|
|
72722
|
|
|
53
|
|
|
|
|
3083
|
|
27
|
53
|
|
|
53
|
|
21307
|
use Text::ParseWords 'quotewords'; |
|
53
|
|
|
|
|
46833
|
|
|
53
|
|
|
|
|
2439
|
|
28
|
53
|
|
|
53
|
|
229
|
use Data::Dumper; |
|
53
|
|
|
|
|
59
|
|
|
53
|
|
|
|
|
1957
|
|
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
# Enable Perl 6 knockoffs, and use 5.10.1, because smartmatching and other |
31
|
|
|
|
|
|
|
# things in 5.10 were changed in 5.10.1+. |
32
|
53
|
|
|
53
|
|
711
|
use 5.010001; |
|
53
|
|
|
|
|
131
|
|
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
# Set up Exporter to bring App::Fetchware::Util's API to everyone who use's it. |
35
|
53
|
|
|
53
|
|
170
|
use Exporter qw( import ); |
|
53
|
|
|
|
|
56
|
|
|
53
|
|
|
|
|
224852
|
|
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
our %EXPORT_TAGS = ( |
38
|
|
|
|
|
|
|
UTIL => [qw( |
39
|
|
|
|
|
|
|
msg |
40
|
|
|
|
|
|
|
vmsg |
41
|
|
|
|
|
|
|
run_prog |
42
|
|
|
|
|
|
|
no_mirror_download_dirlist |
43
|
|
|
|
|
|
|
download_dirlist |
44
|
|
|
|
|
|
|
ftp_download_dirlist |
45
|
|
|
|
|
|
|
http_download_dirlist |
46
|
|
|
|
|
|
|
file_download_dirlist |
47
|
|
|
|
|
|
|
no_mirror_download_file |
48
|
|
|
|
|
|
|
download_file |
49
|
|
|
|
|
|
|
download_ftp_url |
50
|
|
|
|
|
|
|
download_http_url |
51
|
|
|
|
|
|
|
download_file_url |
52
|
|
|
|
|
|
|
do_nothing |
53
|
|
|
|
|
|
|
safe_open |
54
|
|
|
|
|
|
|
drop_privs |
55
|
|
|
|
|
|
|
write_dropprivs_pipe |
56
|
|
|
|
|
|
|
read_dropprivs_pipe |
57
|
|
|
|
|
|
|
create_tempdir |
58
|
|
|
|
|
|
|
original_cwd |
59
|
|
|
|
|
|
|
cleanup_tempdir |
60
|
|
|
|
|
|
|
)], |
61
|
|
|
|
|
|
|
); |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
# create_config_options |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
# *All* entries in @EXPORT_TAGS must also be in @EXPORT_OK. |
66
|
|
|
|
|
|
|
our @EXPORT_OK = map {@{$_}} values %EXPORT_TAGS; |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
###BUGALERT### Add Test::Wrap support to msg() and vmsg() so that they will |
76
|
|
|
|
|
|
|
#inteligently rewrap any text they receive so newly filled in variables won't |
77
|
|
|
|
|
|
|
#screw up the wrapping. |
78
|
|
|
|
|
|
|
sub msg (@) { |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
# If fetchware was not run in quiet mode, -q. |
81
|
2468
|
100
|
100
|
2468
|
1
|
14172
|
unless (defined $fetchware::quiet and $fetchware::quiet > 0) { |
82
|
|
|
|
|
|
|
# print are arguments. Use say if the last one doesn't end with a |
83
|
|
|
|
|
|
|
# newline. $#_ is the last subscript of the @_ variable. |
84
|
2466
|
100
|
|
|
|
10783
|
if ($_[$#_] =~ /\w*\n\w*\z/) { |
85
|
103
|
|
|
|
|
11159
|
print @_; |
86
|
|
|
|
|
|
|
} else { |
87
|
2363
|
|
|
|
|
186228
|
say @_; |
88
|
|
|
|
|
|
|
} |
89
|
|
|
|
|
|
|
# Quiet mode is turned on. |
90
|
|
|
|
|
|
|
} else { |
91
|
|
|
|
|
|
|
# Don't print anything. |
92
|
2
|
|
|
|
|
196
|
return; |
93
|
|
|
|
|
|
|
} |
94
|
|
|
|
|
|
|
} |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
sub vmsg (@) { |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
# If fetchware was not run in quiet mode, -q. |
101
|
|
|
|
|
|
|
###BUGALERT### Can I do something like: |
102
|
|
|
|
|
|
|
#eval "use constant quiet => 0;" so that the iffs below can be resolved at |
103
|
|
|
|
|
|
|
#run-time to make vmsg() and msg() faster??? |
104
|
5233
|
100
|
100
|
5233
|
1
|
26659
|
unless (defined $fetchware::quiet and $fetchware::quiet > 0) { |
105
|
|
|
|
|
|
|
# If verbose is also turned on. |
106
|
5231
|
100
|
100
|
|
|
17666
|
if (defined $fetchware::verbose and $fetchware::verbose > 0) { |
107
|
|
|
|
|
|
|
# print our arguments. Use say if the last one doesn't end with a |
108
|
|
|
|
|
|
|
# newline. $#_ is the last subscript of the @_ variable. |
109
|
4400
|
100
|
|
|
|
42421
|
if ($_[$#_] =~ /\w*\n\w*\z/) { |
110
|
1362
|
|
|
|
|
201876
|
print @_; |
111
|
|
|
|
|
|
|
} else { |
112
|
3038
|
|
|
|
|
346533
|
say @_; |
113
|
|
|
|
|
|
|
} |
114
|
|
|
|
|
|
|
} |
115
|
|
|
|
|
|
|
# Quiet mode is turned on. |
116
|
|
|
|
|
|
|
} else { |
117
|
|
|
|
|
|
|
# Don't print anything. |
118
|
2
|
|
|
|
|
170
|
return; |
119
|
|
|
|
|
|
|
} |
120
|
|
|
|
|
|
|
} |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
###BUGALERT### Add support for dry-run functionality!!!! |
128
|
|
|
|
|
|
|
sub run_prog { |
129
|
199
|
|
|
199
|
1
|
3631
|
my (@args) = @_; |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
# Kill weird "Insecure dependency in system while running with -T switch." |
132
|
|
|
|
|
|
|
# fatal exceptions by clearing the taint flag with a regex. I'm not actually |
133
|
|
|
|
|
|
|
# running in taint mode, but it bizarrely thinks I am. |
134
|
199
|
|
|
|
|
582
|
for my $arg (@args) { |
135
|
340
|
50
|
|
|
|
1646
|
if ($arg =~ /(.*)/) { |
136
|
340
|
|
|
|
|
1389
|
$arg = $1; |
137
|
|
|
|
|
|
|
} else { |
138
|
0
|
|
|
|
|
0
|
die <
|
139
|
|
|
|
|
|
|
php.Fetchwarefile: Match anything pattern match failed! Huh! This shouldn't |
140
|
|
|
|
|
|
|
happen, and is probably a bug. |
141
|
|
|
|
|
|
|
EOD |
142
|
|
|
|
|
|
|
} |
143
|
|
|
|
|
|
|
} |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
# Use Text::ParseWords quotewords() subroutine to deal with spliting the |
146
|
|
|
|
|
|
|
# arguments on whitespace, and to properly quote and keep single and double |
147
|
|
|
|
|
|
|
# quotes. |
148
|
199
|
|
|
|
|
368
|
my $program; |
149
|
199
|
|
|
|
|
423
|
($program, @args) = map {quotewords('\s+', 1, $_)} @args; |
|
340
|
|
|
|
|
9813
|
|
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
# If fetchware is run without -q. |
152
|
199
|
100
|
100
|
|
|
10176
|
unless (defined $fetchware::quiet and $fetchware::quiet > 0) { |
153
|
197
|
|
|
|
|
638
|
local $" = ']['; |
154
|
197
|
|
|
|
|
861
|
vmsg <
|
155
|
|
|
|
|
|
|
Running command [$program] with options [@args]. |
156
|
|
|
|
|
|
|
EOM |
157
|
197
|
50
|
|
|
|
724836
|
system($program, @args) == 0 or die <
|
158
|
|
|
|
|
|
|
fetchware: run-time error. Fetchware failed to execute the specified program |
159
|
|
|
|
|
|
|
[$program] with the arguments [@args]. The OS error was [$!], and the return |
160
|
0
|
|
|
|
|
0
|
value was [@{[$? >> 8]}]. Please see perldoc App::Fetchware::Diagnostics. |
161
|
|
|
|
|
|
|
EOD |
162
|
|
|
|
|
|
|
# If fetchware is run with -q. |
163
|
|
|
|
|
|
|
} else { |
164
|
|
|
|
|
|
|
# Use a piped open() to capture STDOUT, so that STDOUT is not printed to |
165
|
|
|
|
|
|
|
# the terminal like it usually is therby "quiet"ing it. |
166
|
|
|
|
|
|
|
# If not on Windows use safer open call that doesn't work on Windows. |
167
|
2
|
50
|
|
|
|
24
|
unless (is_os_type('Windows', $^O)) { |
168
|
2
|
50
|
|
|
|
5538
|
open(my $fh, '-|', "$program", @args) or die <
|
169
|
|
|
|
|
|
|
fetchware: run-time error. Fetchware failed to execute the specified program |
170
|
|
|
|
|
|
|
while capturing its input to prevent it from being copied to the screen, because |
171
|
|
|
|
|
|
|
you ran fetchware with it's --quite or -q option. The program was [$program], |
172
|
|
|
|
|
|
|
and its arguments were [@args]. OS error [$!], and exit value [$?]. Please see |
173
|
|
|
|
|
|
|
perldoc App::Fetchware::Diagnostics. |
174
|
|
|
|
|
|
|
EOD |
175
|
|
|
|
|
|
|
# Close $fh, to cause perl to wait for the command to do its |
176
|
|
|
|
|
|
|
# outputing to STDOUT. |
177
|
2
|
|
|
|
|
2342
|
close $fh; |
178
|
|
|
|
|
|
|
# We're on Windows. |
179
|
|
|
|
|
|
|
} else { |
180
|
0
|
0
|
|
|
|
0
|
open(my $fh, '-|', "$program @args") or die <
|
181
|
|
|
|
|
|
|
fetchware: run-time error. Fetchware failed to execute the specified program |
182
|
|
|
|
|
|
|
while capturing its input to prevent it from being copied to the screen, because |
183
|
|
|
|
|
|
|
you ran fetchware with it's --quite or -q option. The program was [$program], |
184
|
|
|
|
|
|
|
and its arguments were [@args]. OS error [$!], and exit value [$?]. Please see |
185
|
|
|
|
|
|
|
perldoc App::Fetchware::Diagnostics. |
186
|
|
|
|
|
|
|
EOD |
187
|
|
|
|
|
|
|
# Close $fh, to cause perl to wait for the command to do its |
188
|
|
|
|
|
|
|
# outputing to STDOUT. |
189
|
0
|
|
|
|
|
0
|
close $fh; |
190
|
|
|
|
|
|
|
} |
191
|
|
|
|
|
|
|
} |
192
|
|
|
|
|
|
|
} |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
###BUGALERT### All download routines should be modified to use HTTP::Tiny's |
200
|
|
|
|
|
|
|
#iterative download interface so I can write the downloaded files straight to |
201
|
|
|
|
|
|
|
#disk to avoid wasting 20, 30 or 120gigs or so or whatever the file size is in |
202
|
|
|
|
|
|
|
#memory for each downloaded file. |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
sub download_dirlist { |
206
|
3
|
|
|
3
|
1
|
9265
|
my %opts; |
207
|
|
|
|
|
|
|
my $url; |
208
|
|
|
|
|
|
|
# One arg means its a $url. |
209
|
3
|
50
|
|
|
|
52
|
if (@_ == 1) { |
|
|
0
|
|
|
|
|
|
210
|
3
|
|
|
|
|
24
|
$url = shift; |
211
|
|
|
|
|
|
|
# More than one means it's a PATH, and if it's not a path... |
212
|
|
|
|
|
|
|
} elsif (@_ == 2) { |
213
|
0
|
|
|
|
|
0
|
%opts = @_; |
214
|
|
|
|
|
|
|
# Or your param wasn't PATH |
215
|
0
|
0
|
0
|
|
|
0
|
if (not exists $opts{PATH} and not defined $opts{PATH}) { |
216
|
|
|
|
|
|
|
# Use goto for cool old-school C-style error handling to avoid copy |
217
|
|
|
|
|
|
|
# and pasting or insane nested ifs. |
218
|
0
|
|
|
|
|
0
|
goto PATHERROR; |
219
|
|
|
|
|
|
|
} |
220
|
|
|
|
|
|
|
# ...then it's an error. |
221
|
|
|
|
|
|
|
} else { |
222
|
0
|
|
|
|
|
0
|
PATHERROR: die <
|
223
|
|
|
|
|
|
|
App-Fetchware-Util: You can only specify either PATH or URL never both. Only |
224
|
|
|
|
|
|
|
specify one or the other when you call download_dirlist(). |
225
|
|
|
|
|
|
|
EOD |
226
|
|
|
|
|
|
|
} |
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
# Ensure the user has specified a mirror, because otherwise download_file() |
229
|
|
|
|
|
|
|
# will try to just download a path, and that's not going to work. |
230
|
3
|
50
|
33
|
|
|
36
|
die <
|
231
|
|
|
|
|
|
|
App-Fetchware-Util: You only called download_dirlist() with just a PATH |
232
|
|
|
|
|
|
|
parameter, but also failed to specify any mirrors in your configuration. Without |
233
|
|
|
|
|
|
|
any defined mirrors download_dirlist() cannot determine from what host to |
234
|
|
|
|
|
|
|
download your file. Please specify a mirror and try again. |
235
|
|
|
|
|
|
|
EOD |
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
# Set up our list of urls that we'll try to download the specified PATH or |
238
|
|
|
|
|
|
|
# URL from. |
239
|
3
|
50
|
|
|
|
23
|
my @urls = config('mirror') if defined config('mirror'); |
240
|
3
|
50
|
33
|
|
|
61
|
if (exists $opts{PATH} |
|
|
50
|
33
|
|
|
|
|
|
|
|
33
|
|
|
|
|
241
|
|
|
|
|
|
|
and defined $opts{PATH} |
242
|
|
|
|
|
|
|
and $opts{PATH}) { |
243
|
|
|
|
|
|
|
# The PATH option means that $url is not a full blown URL, but just a |
244
|
|
|
|
|
|
|
# path without a hostname or scheme portion. |
245
|
|
|
|
|
|
|
# Therefore, we append $url, because the PATH option means it's actually |
246
|
|
|
|
|
|
|
# just a path, so we append it to each @url. |
247
|
0
|
|
|
|
|
0
|
for my $mirror_url (@urls) { |
248
|
|
|
|
|
|
|
# Use URI to replace the current path with the one the caller |
249
|
|
|
|
|
|
|
# specified in the $url parameter. |
250
|
0
|
|
|
|
|
0
|
my ($scheme, $auth, undef, undef, undef) = uri_split($mirror_url); |
251
|
0
|
|
|
|
|
0
|
$mirror_url = uri_join($scheme, $auth, $opts{PATH}, undef, undef); |
252
|
|
|
|
|
|
|
} |
253
|
|
|
|
|
|
|
} elsif (defined $url |
254
|
|
|
|
|
|
|
and $url) { |
255
|
|
|
|
|
|
|
# Add $url to @urls since it too has a hostname. And use unshift |
256
|
|
|
|
|
|
|
# to put it in the first position instead of last if you were to use |
257
|
|
|
|
|
|
|
# push. |
258
|
3
|
|
|
|
|
12
|
unshift @urls, $url; |
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
# I must parse out the path portion of the specified URL, because this |
261
|
|
|
|
|
|
|
# path portion will be appended to the mirrors you have specified. |
262
|
3
|
|
|
|
|
28
|
my $url_path = ( uri_split($url) )[2]; |
263
|
3
|
|
|
|
|
50
|
for my $mirror_url (@urls) { |
264
|
|
|
|
|
|
|
# If the $mirror_url has no path... |
265
|
3
|
|
|
|
|
10
|
my ($scheme, $auth, $path, $query, $frag) = |
266
|
|
|
|
|
|
|
uri_split($mirror_url); |
267
|
|
|
|
|
|
|
###BUGALERT### Should check below also check for $query and $frag, |
268
|
|
|
|
|
|
|
#and if the mirror has specified those to include those too??? |
269
|
3
|
50
|
|
|
|
32
|
if ($path eq '') { |
270
|
|
|
|
|
|
|
#...then append $url's path. |
271
|
|
|
|
|
|
|
###BUGALERT## As shown before I was using URI's much nicer |
272
|
|
|
|
|
|
|
#interface, but it was deleting the path instead of replacing |
273
|
|
|
|
|
|
|
#the path! I tried reproducing this with a small test file, but |
274
|
|
|
|
|
|
|
#it worked just fine in the small test file. So, it must be some |
275
|
|
|
|
|
|
|
#really weird bug to fail here, but work in a smaller test file. |
276
|
|
|
|
|
|
|
#I don't know try replacing all of the URI::Split calls with the |
277
|
|
|
|
|
|
|
#equivelent URI->path() calls, and you'll get the weird bug. |
278
|
|
|
|
|
|
|
#$mirror_url->path($url_path); |
279
|
0
|
|
|
|
|
0
|
$mirror_url = |
280
|
|
|
|
|
|
|
uri_join($scheme, $auth, $url_path, $query, $frag); |
281
|
|
|
|
|
|
|
# But if the $mirror_url does have a path... |
282
|
|
|
|
|
|
|
} else { |
283
|
|
|
|
|
|
|
#...Then keep the mirrors path intact. |
284
|
|
|
|
|
|
|
# |
285
|
|
|
|
|
|
|
# Because if you specify a path when you define that mirror |
286
|
|
|
|
|
|
|
# chances are you did it, because that mirror stores it in a |
287
|
|
|
|
|
|
|
# different directory. For example Apache is /apache on some |
288
|
|
|
|
|
|
|
# mirrors, but apache.hostname on other mirrors. |
289
|
|
|
|
|
|
|
} |
290
|
|
|
|
|
|
|
} |
291
|
|
|
|
|
|
|
} |
292
|
|
|
|
|
|
|
|
293
|
3
|
|
|
|
|
8
|
my $dirlist; |
294
|
|
|
|
|
|
|
|
295
|
3
|
|
|
|
|
10
|
for my $mirror_url (@urls) { |
296
|
3
|
|
|
|
|
7
|
eval { |
297
|
3
|
|
|
|
|
25
|
msg "Attempting to download [$mirror_url]."; |
298
|
|
|
|
|
|
|
# Try the mirror_url directly without trying any mirrors. |
299
|
3
|
|
|
|
|
26
|
$dirlist = no_mirror_download_dirlist($mirror_url); |
300
|
|
|
|
|
|
|
}; |
301
|
3
|
50
|
|
|
|
13
|
if ($@) { |
302
|
0
|
|
|
|
|
0
|
msg "Directory download attempt failed! Error was["; |
303
|
0
|
|
|
|
|
0
|
print $@; |
304
|
0
|
|
|
|
|
0
|
msg "]."; |
305
|
|
|
|
|
|
|
} |
306
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
# Skip the rest of the @urls after we successfully download the $url. |
308
|
3
|
50
|
|
|
|
13
|
if (defined $dirlist) { |
309
|
3
|
|
|
|
|
16
|
msg "Successfully downloaded the directory listing."; |
310
|
3
|
|
|
|
|
10
|
last; |
311
|
|
|
|
|
|
|
} |
312
|
|
|
|
|
|
|
} |
313
|
|
|
|
|
|
|
|
314
|
3
|
50
|
|
|
|
20
|
die <
|
315
|
|
|
|
|
|
|
App-Fetchware-Util: Failed to download the specifed URL [$url] or path |
316
|
|
|
|
|
|
|
[$opts{PATH}] using the included hostname in the url you specifed or any |
317
|
0
|
|
|
|
|
0
|
mirrors. The mirrors are [@{[config('mirror')]}]. And the urls |
318
|
|
|
|
|
|
|
that fetchware tried to download were [@urls]. |
319
|
|
|
|
|
|
|
EOD |
320
|
|
|
|
|
|
|
|
321
|
3
|
|
|
|
|
34
|
return $dirlist; |
322
|
|
|
|
|
|
|
} |
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
sub no_mirror_download_dirlist { |
327
|
3
|
|
|
3
|
1
|
10
|
my $url = shift; |
328
|
|
|
|
|
|
|
|
329
|
3
|
|
|
|
|
6
|
my $dirlist; |
330
|
3
|
50
|
|
|
|
59
|
if ($url =~ m!^ftp://.*$!) { |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
331
|
0
|
|
|
|
|
0
|
$dirlist = ftp_download_dirlist($url); |
332
|
|
|
|
|
|
|
} elsif ($url =~ m!^http://.*$!) { |
333
|
0
|
|
|
|
|
0
|
$dirlist = http_download_dirlist($url); |
334
|
|
|
|
|
|
|
} elsif ($url =~ m!^file://.*$!) { |
335
|
3
|
|
|
|
|
17
|
$dirlist = file_download_dirlist($url); |
336
|
|
|
|
|
|
|
} else { |
337
|
0
|
|
|
|
|
0
|
die <
|
338
|
|
|
|
|
|
|
App-Fetchware: run-time syntax error: the url parameter your provided in |
339
|
|
|
|
|
|
|
your call to download_dirlist() [$url] does not have a supported URL scheme (the |
340
|
|
|
|
|
|
|
http:// or ftp:// part). The only supported download types, schemes, are FTP and |
341
|
|
|
|
|
|
|
HTTP. See perldoc App::Fetchware. |
342
|
|
|
|
|
|
|
EOD |
343
|
|
|
|
|
|
|
} |
344
|
|
|
|
|
|
|
|
345
|
3
|
|
|
|
|
14
|
return $dirlist; |
346
|
|
|
|
|
|
|
} |
347
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
sub ftp_download_dirlist { |
351
|
0
|
|
|
0
|
1
|
0
|
my $ftp_url = shift; |
352
|
0
|
|
|
|
|
0
|
$ftp_url =~ m!^ftp://([-a-z,A-Z,0-9,\.]+)(/.*)?!; |
353
|
0
|
|
|
|
|
0
|
my $site = $1; |
354
|
0
|
|
|
|
|
0
|
my $path = $2; |
355
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
# Add debugging later based on fetchware commandline args. |
357
|
|
|
|
|
|
|
# for debugging: $ftp = Net::FTP->new('$site','Debug' => 10); |
358
|
|
|
|
|
|
|
# open a connection and log in! |
359
|
0
|
|
|
|
|
0
|
my $ftp; |
360
|
0
|
0
|
|
|
|
0
|
$ftp = Net::FTP->new($site) |
361
|
|
|
|
|
|
|
or die <
|
362
|
|
|
|
|
|
|
App-Fetchware: run-time error. fetchware failed to connect to the ftp server at |
363
|
|
|
|
|
|
|
domain [$site]. The system error was [$@]. |
364
|
|
|
|
|
|
|
See man App::Fetchware. |
365
|
|
|
|
|
|
|
EOD |
366
|
|
|
|
|
|
|
|
367
|
0
|
0
|
|
|
|
0
|
$ftp->login("anonymous",'-anonymous@') |
368
|
|
|
|
|
|
|
or die <
|
369
|
|
|
|
|
|
|
App-Fetchware: run-time error. fetchware failed to log in to the ftp server at |
370
|
0
|
|
|
|
|
0
|
domain [$site]. The ftp error was [@{[$ftp->message]}]. See man App::Fetchware. |
371
|
|
|
|
|
|
|
EOD |
372
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
|
374
|
0
|
0
|
|
|
|
0
|
my @dir_listing = $ftp->dir($path) |
375
|
|
|
|
|
|
|
or die <
|
376
|
|
|
|
|
|
|
App-Fetchware: run-time error. fetchware failed to get a long directory listing |
377
|
0
|
|
|
|
|
0
|
of [$path] on server [$site]. The ftp error was [@{[$ftp->message]}]. See man App::Fetchware. |
378
|
|
|
|
|
|
|
EOD |
379
|
|
|
|
|
|
|
|
380
|
0
|
|
|
|
|
0
|
$ftp->quit(); |
381
|
|
|
|
|
|
|
|
382
|
0
|
|
|
|
|
0
|
return \@dir_listing; |
383
|
|
|
|
|
|
|
} |
384
|
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
sub http_download_dirlist { |
388
|
0
|
|
|
0
|
1
|
0
|
my $http_url = shift; |
389
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
# Forward any other options over to HTTP::Tiny. This is used mostly to |
391
|
|
|
|
|
|
|
# support changing user agent strings, but why not support them all. |
392
|
0
|
0
|
|
|
|
0
|
my %opts = @_ if @_ % 2 == 0; |
393
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
# Append user_agent if specified. |
395
|
0
|
0
|
|
|
|
0
|
$opts{agent} = config('user_agent') if config('user_agent'); |
396
|
|
|
|
|
|
|
|
397
|
0
|
|
|
|
|
0
|
my $http = HTTP::Tiny->new(%opts); |
398
|
|
|
|
|
|
|
###BUGALERT### Should use request() instead of get, because request can |
399
|
|
|
|
|
|
|
#directly write the chunks of the file to disk as they are downloaded. get() |
400
|
|
|
|
|
|
|
#just uses RAM, so a 50Meg file takes up 50 megs of ram, and so on. |
401
|
|
|
|
|
|
|
###BUGALERT### Also, if you use request instead, and get chunks of bytes |
402
|
|
|
|
|
|
|
#instead of just writing them to disk, you could also use a |
403
|
|
|
|
|
|
|
#Term::ProgressBar to print a cool progress bar during the download! |
404
|
|
|
|
|
|
|
#This could also be added to the ftp downloaders too, but probably not the |
405
|
|
|
|
|
|
|
#local file:// downloaders though. |
406
|
0
|
|
|
|
|
0
|
my $response = $http->get($http_url); |
407
|
|
|
|
|
|
|
|
408
|
0
|
0
|
|
|
|
0
|
die <{success}; |
409
|
|
|
|
|
|
|
App-Fetchware: run-time error. HTTP::Tiny failed to download a directory listing |
410
|
|
|
|
|
|
|
of your provided lookup_url. HTTP status code [$response->{status} $response->{reason}] |
411
|
0
|
|
|
|
|
0
|
HTTP headers [@{[Data::Dumper::Dumper($response)]}]. |
412
|
|
|
|
|
|
|
See man App::Fetchware. |
413
|
|
|
|
|
|
|
EOD |
414
|
|
|
|
|
|
|
|
415
|
|
|
|
|
|
|
|
416
|
0
|
|
|
|
|
0
|
while (my ($k, $v) = each %{$response->{headers}}) { |
|
0
|
|
|
|
|
0
|
|
417
|
0
|
0
|
|
|
|
0
|
for (ref $v eq 'ARRAY' ? @$v : $v) { |
418
|
|
|
|
|
|
|
} |
419
|
|
|
|
|
|
|
} |
420
|
|
|
|
|
|
|
|
421
|
0
|
0
|
|
|
|
0
|
die <{content}; |
422
|
|
|
|
|
|
|
App-Fetchware: run-time error. The lookup_url you provided downloaded nothing. |
423
|
|
|
|
|
|
|
HTTP status code [$response->{status} $response->{reason}] |
424
|
0
|
|
|
|
|
0
|
HTTP headers [@{[Data::Dumper::Dumper($response)]}]. |
425
|
|
|
|
|
|
|
See man App::Fetchware. |
426
|
|
|
|
|
|
|
EOD |
427
|
0
|
|
|
|
|
0
|
return $response->{content}; |
428
|
|
|
|
|
|
|
} |
429
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
|
431
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
sub file_download_dirlist { |
433
|
9
|
|
|
9
|
1
|
623
|
my $local_lookup_url = shift; |
434
|
|
|
|
|
|
|
|
435
|
9
|
|
|
|
|
35
|
$local_lookup_url =~ s!^file://!!; # Strip scheme garbage. |
436
|
|
|
|
|
|
|
|
437
|
|
|
|
|
|
|
# Prepend original_cwd() if $local_lookup_url is a relative path. |
438
|
9
|
50
|
|
|
|
36
|
unless (file_name_is_absolute($local_lookup_url)) { |
439
|
0
|
|
|
|
|
0
|
$local_lookup_url = catdir(original_cwd(), $local_lookup_url); |
440
|
|
|
|
|
|
|
} |
441
|
|
|
|
|
|
|
|
442
|
|
|
|
|
|
|
# Throw an exception if called with a directory that does not exist. |
443
|
9
|
100
|
|
|
|
481
|
die <
|
444
|
|
|
|
|
|
|
App-Fetchware-Util: The directory that fetchware is trying to use to determine |
445
|
|
|
|
|
|
|
if a new version of the software is available does not exist. This directory is |
446
|
|
|
|
|
|
|
[$local_lookup_url], and the OS error is [$!]. |
447
|
|
|
|
|
|
|
EOD |
448
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
|
450
|
7
|
|
|
|
|
14
|
my @file_listing; |
451
|
7
|
50
|
|
|
|
271
|
opendir my $dh, $local_lookup_url or die <
|
452
|
|
|
|
|
|
|
App-Fetchware-Util: The directory that fetchware is trying to use to determine |
453
|
|
|
|
|
|
|
if a new version of the software is availabe cannot be opened. This directory is |
454
|
|
|
|
|
|
|
[$local_lookup_url], and the OS error is [$!]. |
455
|
|
|
|
|
|
|
EOD |
456
|
7
|
|
|
|
|
305
|
while (my $filename = readdir($dh)) { |
457
|
|
|
|
|
|
|
# Trim the useless '.' and '..' Unix convention fake files from the listing. |
458
|
239
|
100
|
100
|
|
|
595
|
unless ($filename eq '.' or $filename eq '..') { |
459
|
|
|
|
|
|
|
# Turn the relative filename into a full pathname. |
460
|
|
|
|
|
|
|
# |
461
|
|
|
|
|
|
|
# Full pathnames are required, because lookup()'s |
462
|
|
|
|
|
|
|
# file_parse_filelist() stat()s each file using just their filename, |
463
|
|
|
|
|
|
|
# and if it's relative instead of absolute these stat() checks will |
464
|
|
|
|
|
|
|
# fail. |
465
|
225
|
|
|
|
|
406
|
my $full_path = catfile($local_lookup_url, $filename); |
466
|
225
|
|
|
|
|
456
|
push @file_listing, $full_path; |
467
|
|
|
|
|
|
|
} |
468
|
|
|
|
|
|
|
} |
469
|
|
|
|
|
|
|
|
470
|
7
|
|
|
|
|
67
|
closedir $dh; |
471
|
|
|
|
|
|
|
|
472
|
|
|
|
|
|
|
# Throw another exception if the directory contains nothing. |
473
|
|
|
|
|
|
|
# Awesome, clever, and simple Path::Class based "is dir empty" test courtesy |
474
|
|
|
|
|
|
|
# of tobyinc on PerlMonks (http://www.perlmonks.org/?node_id=934482). |
475
|
7
|
|
|
|
|
39
|
my $pc_local_lookup_url = dir($local_lookup_url); |
476
|
7
|
100
|
66
|
|
|
612
|
die <stat() && !$pc_local_lookup_url->children(); |
477
|
|
|
|
|
|
|
App-Fetchware-Util: The directory that fetchware is trying to use to determine |
478
|
|
|
|
|
|
|
if a new version of the software is available is empty. This directory is |
479
|
|
|
|
|
|
|
[$local_lookup_url]. |
480
|
|
|
|
|
|
|
EOD |
481
|
|
|
|
|
|
|
|
482
|
5
|
|
|
|
|
19846
|
return \@file_listing; |
483
|
|
|
|
|
|
|
} |
484
|
|
|
|
|
|
|
|
485
|
|
|
|
|
|
|
|
486
|
|
|
|
|
|
|
|
487
|
|
|
|
|
|
|
|
488
|
|
|
|
|
|
|
###BUGALERT###I'm a 190 line disaster! Please refactor me. Oh, and |
489
|
|
|
|
|
|
|
#download_dirlist() too please, because I'm just a copy and paste of that |
490
|
|
|
|
|
|
|
#subroutine! |
491
|
|
|
|
|
|
|
sub download_file { |
492
|
5
|
|
|
5
|
1
|
144
|
my %opts; |
493
|
|
|
|
|
|
|
my $url; |
494
|
|
|
|
|
|
|
# One arg means its a $url. |
495
|
5
|
100
|
|
|
|
27
|
if (@_ == 1) { |
|
|
50
|
|
|
|
|
|
496
|
2
|
|
|
|
|
6
|
$url = shift; |
497
|
|
|
|
|
|
|
# More than one means it's a PATH, and if it's not a path... |
498
|
|
|
|
|
|
|
} elsif (@_ == 2) { |
499
|
3
|
|
|
|
|
16
|
%opts = @_; |
500
|
|
|
|
|
|
|
# Or your param wasn't PATH |
501
|
3
|
0
|
33
|
|
|
20
|
if (not exists $opts{PATH} and not defined $opts{PATH}) { |
502
|
|
|
|
|
|
|
# Use goto for cool old-school C-style error handling to avoid copy |
503
|
|
|
|
|
|
|
# and pasting or insane nested ifs. |
504
|
0
|
|
|
|
|
0
|
goto PATHERROR; |
505
|
|
|
|
|
|
|
} |
506
|
|
|
|
|
|
|
# ...then it's an error. |
507
|
|
|
|
|
|
|
} else { |
508
|
0
|
|
|
|
|
0
|
PATHERROR: die <
|
509
|
|
|
|
|
|
|
App-Fetchware-Util: You can only specify either PATH or URL never both. Only |
510
|
|
|
|
|
|
|
specify one or the other when you call download_file(). |
511
|
|
|
|
|
|
|
EOD |
512
|
|
|
|
|
|
|
} |
513
|
|
|
|
|
|
|
# Ensure the user has specified a mirror, because otherwise download_file() |
514
|
|
|
|
|
|
|
# will try to just download a path, and that's not going to work. |
515
|
5
|
0
|
66
|
|
|
29
|
if (not config('mirror') and exists $opts{PATH} |
|
|
0
|
33
|
|
|
|
|
|
|
0
|
|
|
|
|
|
516
|
|
|
|
|
|
|
and |
517
|
|
|
|
|
|
|
# True if lookup_url is a file and if lookup_url is undef. |
518
|
|
|
|
|
|
|
defined config('lookup_url') ? |
519
|
|
|
|
|
|
|
config('lookup_url') =~ m!^file://! ? 1 : 0 |
520
|
|
|
|
|
|
|
: 1 |
521
|
|
|
|
|
|
|
) { |
522
|
0
|
|
|
|
|
0
|
die <
|
523
|
|
|
|
|
|
|
App-Fetchware-Util: You only called download_file() with just a PATH parameter, |
524
|
|
|
|
|
|
|
but also failed to specify any mirrors in your configuration. Without any |
525
|
|
|
|
|
|
|
defined mirrors download_file() cannot determine from what host to download your |
526
|
|
|
|
|
|
|
file. Please specify a mirror and try again. |
527
|
|
|
|
|
|
|
EOD |
528
|
|
|
|
|
|
|
} |
529
|
|
|
|
|
|
|
|
530
|
|
|
|
|
|
|
# Set up our list of urls that we'll try to download the specified PATH or |
531
|
|
|
|
|
|
|
# URL from. |
532
|
5
|
100
|
|
|
|
21
|
my @urls = config('mirror') if defined config('mirror'); |
533
|
|
|
|
|
|
|
# If we're called with a PATH option and the lookup_url is for a local file, |
534
|
|
|
|
|
|
|
# then we should just convert from a PATH into a $url. |
535
|
5
|
50
|
66
|
|
|
37
|
if (exists $opts{PATH} |
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
536
|
|
|
|
|
|
|
and |
537
|
|
|
|
|
|
|
# Is lookup_url not a file://, true for undef and any other scheme. |
538
|
|
|
|
|
|
|
defined config('lookup_url') ? |
539
|
|
|
|
|
|
|
config('lookup_url') =~ m!^file://! ? 1 : 0 |
540
|
|
|
|
|
|
|
: 0 |
541
|
|
|
|
|
|
|
) { |
542
|
3
|
|
|
|
|
11
|
$url = "file://$opts{PATH}"; |
543
|
3
|
|
|
|
|
6
|
delete $opts{PATH}; |
544
|
|
|
|
|
|
|
# Otherwise, we should add lookup_url's hostname to the list of mirrors, but |
545
|
|
|
|
|
|
|
# be sure to push it onto @urls so that it is used last. |
546
|
|
|
|
|
|
|
# |
547
|
|
|
|
|
|
|
# But only if lookup_url is defined. |
548
|
|
|
|
|
|
|
} elsif (defined config('lookup_url')) { |
549
|
0
|
|
|
|
|
0
|
my ($scheme, $auth, undef, undef, undef) = |
550
|
|
|
|
|
|
|
uri_split(config('lookup_url')); |
551
|
0
|
|
|
|
|
0
|
push @urls, uri_join($scheme, $auth, undef, undef, undef); |
552
|
|
|
|
|
|
|
} |
553
|
|
|
|
|
|
|
|
554
|
5
|
50
|
33
|
|
|
67
|
if (exists $opts{PATH} |
|
|
50
|
33
|
|
|
|
|
|
|
|
33
|
|
|
|
|
555
|
|
|
|
|
|
|
and defined $opts{PATH} |
556
|
|
|
|
|
|
|
and $opts{PATH}) { |
557
|
|
|
|
|
|
|
# The PATH option means that $url is not a full blown URL, but just a |
558
|
|
|
|
|
|
|
# path without a hostname or scheme portion. |
559
|
|
|
|
|
|
|
# Therefore, we append $url, because the PATH option means it's actually |
560
|
|
|
|
|
|
|
# just a path, so we append it to each @url. |
561
|
0
|
|
|
|
|
0
|
for my $mirror_url (@urls) { |
562
|
|
|
|
|
|
|
# If the $mirror_url has no path... |
563
|
0
|
|
|
|
|
0
|
my ($scheme, $auth, $path, $query, $frag) = |
564
|
|
|
|
|
|
|
uri_split($mirror_url); |
565
|
|
|
|
|
|
|
# Skip messing with the path if $path eq $opts{PATH}, which means the |
566
|
|
|
|
|
|
|
# current $mirror_url is $url, so we shouldn't add its own path to |
567
|
|
|
|
|
|
|
# itself--we should skip it instead. |
568
|
0
|
0
|
|
|
|
0
|
next if $path eq $opts{PATH}; |
569
|
0
|
0
|
|
|
|
0
|
if ($path eq '') { |
570
|
|
|
|
|
|
|
#...then append $url's path. |
571
|
|
|
|
|
|
|
###BUGALERT## As shown before I was using URI's much nicer |
572
|
|
|
|
|
|
|
#interface, but it was deleting the path instead of replacing |
573
|
|
|
|
|
|
|
#the path! I tried reproducing this with a small test file, but |
574
|
|
|
|
|
|
|
#it worked just fine in the small test file. So, it must be some |
575
|
|
|
|
|
|
|
#really weird bug to fail here, but work in a smaller test file. |
576
|
|
|
|
|
|
|
#I don't know try replacing all of the URI::Split calls with the |
577
|
|
|
|
|
|
|
#equivelent URI->path() calls, and you'll get the weird bug. |
578
|
|
|
|
|
|
|
#$mirror_url->path($opts{PATH}); |
579
|
|
|
|
|
|
|
###Add an unless ($opts{PATH} eq '') |
580
|
|
|
|
|
|
|
$mirror_url = |
581
|
0
|
|
|
|
|
0
|
uri_join($scheme, $auth, $opts{PATH}, $query, $frag); |
582
|
|
|
|
|
|
|
# But if the $mirror_url does have a path... |
583
|
|
|
|
|
|
|
} else { |
584
|
|
|
|
|
|
|
#...Then keep the mirrors path intact. |
585
|
|
|
|
|
|
|
# |
586
|
|
|
|
|
|
|
# Because if you specify a path when you define that mirror |
587
|
|
|
|
|
|
|
# chances are you did it, because that mirror stores it in a |
588
|
|
|
|
|
|
|
# different directory. For example Apache is /apache on some |
589
|
|
|
|
|
|
|
# mirrors, but apache.hostname on other mirrors. |
590
|
|
|
|
|
|
|
# |
591
|
|
|
|
|
|
|
#Except add $path's basename, because otherwise we'll ask |
592
|
|
|
|
|
|
|
#for a dirlisting or try to download a directory as a file. |
593
|
0
|
0
|
|
|
|
0
|
unless ($path =~ m!/$!) { |
594
|
|
|
|
|
|
|
$mirror_url = |
595
|
|
|
|
|
|
|
uri_join($scheme, $auth, $path . '/' |
596
|
0
|
|
|
|
|
0
|
. file($opts{PATH})->basename(), $query, $frag); |
597
|
|
|
|
|
|
|
# Skip adding a '/' if ones already there at the end. |
598
|
|
|
|
|
|
|
} else { |
599
|
|
|
|
|
|
|
$mirror_url = |
600
|
|
|
|
|
|
|
uri_join($scheme, $auth, $path |
601
|
0
|
|
|
|
|
0
|
. file($opts{PATH})->basename(), $query, $frag); |
602
|
|
|
|
|
|
|
} |
603
|
|
|
|
|
|
|
} |
604
|
|
|
|
|
|
|
} |
605
|
|
|
|
|
|
|
} elsif (defined $url |
606
|
|
|
|
|
|
|
and $url) { |
607
|
|
|
|
|
|
|
# Add $url to @urls since it too has a hostname. And use unshift |
608
|
|
|
|
|
|
|
# to put it in the first position instead of last if you were to use |
609
|
|
|
|
|
|
|
# push. |
610
|
5
|
|
|
|
|
15
|
unshift @urls, $url; |
611
|
|
|
|
|
|
|
|
612
|
|
|
|
|
|
|
# I must parse out the path portion of the specified URL, because this |
613
|
|
|
|
|
|
|
# path portion will be appended to the mirrors you have specified. |
614
|
5
|
|
|
|
|
37
|
my $url_path = ( uri_split($url) )[2]; |
615
|
5
|
|
|
|
|
70
|
for my $mirror_url (@urls) { |
616
|
|
|
|
|
|
|
# If the $mirror_url has no path... |
617
|
8
|
|
|
|
|
22
|
my ($scheme, $auth, $path, $query, $frag) = |
618
|
|
|
|
|
|
|
uri_split($mirror_url); |
619
|
|
|
|
|
|
|
# Skip messing with the path if $path eq $url_path, which means the |
620
|
|
|
|
|
|
|
# current $mirror_url is $url, so we shouldn't add its own path to |
621
|
|
|
|
|
|
|
# itself--we should skip it instead. |
622
|
8
|
100
|
|
|
|
117
|
next if $path eq $url_path; |
623
|
1
|
50
|
|
|
|
3
|
if ($path eq '') { |
624
|
|
|
|
|
|
|
#...then append $url's path. |
625
|
|
|
|
|
|
|
###BUGALERT## As shown before I was using URI's much nicer |
626
|
|
|
|
|
|
|
#interface, but it was deleting the path instead of replacing |
627
|
|
|
|
|
|
|
#the path! I tried reproducing this with a small test file, but |
628
|
|
|
|
|
|
|
#it worked just fine in the small test file. So, it must be some |
629
|
|
|
|
|
|
|
#really weird bug to fail here, but work in a smaller test file. |
630
|
|
|
|
|
|
|
#I don't know try replacing all of the URI::Split calls with the |
631
|
|
|
|
|
|
|
#equivelent URI->path() calls, and you'll get the weird bug. |
632
|
|
|
|
|
|
|
#$mirror_url->path($url_path); |
633
|
|
|
|
|
|
|
###Add an unless ($url_path eq '') |
634
|
1
|
|
|
|
|
3
|
$mirror_url = |
635
|
|
|
|
|
|
|
uri_join($scheme, $auth, $url_path, $query, $frag); |
636
|
|
|
|
|
|
|
# But if the $mirror_url does have a path... |
637
|
|
|
|
|
|
|
} else { |
638
|
|
|
|
|
|
|
#...Then keep the mirrors path intact. |
639
|
|
|
|
|
|
|
# |
640
|
|
|
|
|
|
|
# Because if you specify a path when you define that mirror |
641
|
|
|
|
|
|
|
# chances are you did it, because that mirror stores it in a |
642
|
|
|
|
|
|
|
# different directory. For example Apache is /apache on some |
643
|
|
|
|
|
|
|
# mirrors, but apache.hostname on other mirrors. |
644
|
|
|
|
|
|
|
# |
645
|
|
|
|
|
|
|
#Except add $path's basename, because otherwise we'll ask |
646
|
|
|
|
|
|
|
#for a dirlisting or try to download a directory as a file. |
647
|
0
|
0
|
|
|
|
0
|
unless ($path =~ m!/$!) { |
648
|
0
|
|
|
|
|
0
|
$mirror_url = |
649
|
|
|
|
|
|
|
uri_join($scheme, $auth, $path . '/' |
650
|
|
|
|
|
|
|
. file($url_path)->basename(), $query, $frag); |
651
|
|
|
|
|
|
|
# Skip adding a '/' if ones already there at the end. |
652
|
|
|
|
|
|
|
} else { |
653
|
0
|
|
|
|
|
0
|
$mirror_url = |
654
|
|
|
|
|
|
|
uri_join($scheme, $auth, $path |
655
|
|
|
|
|
|
|
. file($url_path)->basename(), $query, $frag); |
656
|
|
|
|
|
|
|
} |
657
|
|
|
|
|
|
|
} |
658
|
|
|
|
|
|
|
} |
659
|
|
|
|
|
|
|
} |
660
|
|
|
|
|
|
|
|
661
|
5
|
|
|
|
|
25
|
my $filename; |
662
|
|
|
|
|
|
|
|
663
|
5
|
|
|
|
|
11
|
for my $mirror_url (@urls) { |
664
|
5
|
|
|
|
|
241
|
eval { |
665
|
5
|
|
|
|
|
29
|
msg "Attempting to download [$mirror_url]."; |
666
|
|
|
|
|
|
|
# Try the mirror_url directly without trying any mirrors. |
667
|
5
|
|
|
|
|
25
|
$filename = no_mirror_download_file($mirror_url); |
668
|
|
|
|
|
|
|
}; |
669
|
5
|
50
|
|
|
|
22
|
if ($@) { |
670
|
0
|
|
|
|
|
0
|
msg "File download attempt failed! Error was["; |
671
|
0
|
|
|
|
|
0
|
print $@; |
672
|
0
|
|
|
|
|
0
|
msg "]."; |
673
|
|
|
|
|
|
|
} |
674
|
|
|
|
|
|
|
|
675
|
|
|
|
|
|
|
# Skip the rest of the @urls after we successfully download the $url. |
676
|
5
|
50
|
|
|
|
18
|
if (defined $filename) { |
677
|
5
|
|
|
|
|
37
|
msg "Successfully downloaded the file [$mirror_url]."; |
678
|
5
|
|
|
|
|
31
|
last; |
679
|
|
|
|
|
|
|
} |
680
|
|
|
|
|
|
|
} |
681
|
|
|
|
|
|
|
|
682
|
5
|
50
|
|
|
|
24
|
die <
|
683
|
|
|
|
|
|
|
App-Fetchware-Util: Failed to download the specifed URL [$url] or path |
684
|
|
|
|
|
|
|
[$opts{PATH}] using the included hostname in the url you specifed or any |
685
|
0
|
|
|
|
|
0
|
mirrors. The mirrors are [@{[config('mirror')]}]. And the urls |
686
|
0
|
|
|
|
|
0
|
that fetchware tried to download were [@{[@urls]}]. |
687
|
|
|
|
|
|
|
EOD |
688
|
|
|
|
|
|
|
|
689
|
5
|
|
|
|
|
77
|
return $filename; |
690
|
|
|
|
|
|
|
} |
691
|
|
|
|
|
|
|
|
692
|
|
|
|
|
|
|
|
693
|
|
|
|
|
|
|
|
694
|
|
|
|
|
|
|
sub no_mirror_download_file { |
695
|
5
|
|
|
5
|
1
|
12
|
my $url = shift; |
696
|
|
|
|
|
|
|
|
697
|
5
|
|
|
|
|
5
|
my $filename; |
698
|
5
|
50
|
|
|
|
54
|
if ($url =~ m!^ftp://!) { |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
699
|
0
|
|
|
|
|
0
|
$filename = download_ftp_url($url); |
700
|
|
|
|
|
|
|
} elsif ($url =~ m!^http://!) { |
701
|
0
|
|
|
|
|
0
|
$filename = download_http_url($url); |
702
|
|
|
|
|
|
|
} elsif ($url =~ m!^file://!) { |
703
|
5
|
|
|
|
|
22
|
$filename = download_file_url($url); |
704
|
|
|
|
|
|
|
} else { |
705
|
0
|
|
|
|
|
0
|
die <
|
706
|
|
|
|
|
|
|
App-Fetchware: run-time syntax error: the url parameter your provided in |
707
|
|
|
|
|
|
|
your call to download_file() [$url] does not have a supported URL scheme (the |
708
|
|
|
|
|
|
|
http:// or ftp:// part). The only supported download types, schemes, are FTP and |
709
|
|
|
|
|
|
|
HTTP. See perldoc App::Fetchware. |
710
|
|
|
|
|
|
|
EOD |
711
|
|
|
|
|
|
|
} |
712
|
|
|
|
|
|
|
|
713
|
5
|
|
|
|
|
888
|
return $filename; |
714
|
|
|
|
|
|
|
} |
715
|
|
|
|
|
|
|
|
716
|
|
|
|
|
|
|
|
717
|
|
|
|
|
|
|
|
718
|
|
|
|
|
|
|
sub download_ftp_url { |
719
|
0
|
|
|
0
|
1
|
0
|
my $ftp_url = shift; |
720
|
|
|
|
|
|
|
|
721
|
|
|
|
|
|
|
###BUGALERT### Replace custom regex with URI::Split's regex. |
722
|
0
|
|
|
|
|
0
|
$ftp_url =~ m!^ftp://([-a-z,A-Z,0-9,\.]+)(/.*)?!; |
723
|
0
|
|
|
|
|
0
|
my $site = $1; |
724
|
0
|
|
|
|
|
0
|
my $path = $2; |
725
|
0
|
|
|
|
|
0
|
my ($volume, $directories, $file) = splitpath($path); |
726
|
|
|
|
|
|
|
|
727
|
|
|
|
|
|
|
# for debugging: $ftp = Net::FTP->new('site','Debug',10); |
728
|
|
|
|
|
|
|
# open a connection and log in! |
729
|
|
|
|
|
|
|
|
730
|
0
|
0
|
|
|
|
0
|
my $ftp = Net::FTP->new($site) |
731
|
|
|
|
|
|
|
or die <
|
732
|
|
|
|
|
|
|
App-Fetchware: run-time error. fetchware failed to connect to the ftp server at |
733
|
|
|
|
|
|
|
domain [$site]. The system error was [$@]. |
734
|
|
|
|
|
|
|
See man App::Fetchware. |
735
|
|
|
|
|
|
|
EOD |
736
|
|
|
|
|
|
|
|
737
|
0
|
0
|
|
|
|
0
|
$ftp->login("anonymous",'-anonymous@') |
738
|
|
|
|
|
|
|
or die <
|
739
|
|
|
|
|
|
|
App-Fetchware: run-time error. fetchware failed to log in to the ftp server at |
740
|
0
|
|
|
|
|
0
|
domain [$site]. The ftp error was [@{[$ftp->message]}]. See man App::Fetchware. |
741
|
|
|
|
|
|
|
EOD |
742
|
|
|
|
|
|
|
|
743
|
|
|
|
|
|
|
# set transfer mode to binary |
744
|
0
|
0
|
|
|
|
0
|
$ftp->binary() |
745
|
|
|
|
|
|
|
or die <
|
746
|
|
|
|
|
|
|
App-Fetchware: run-time error. fetchware failed to swtich to binary mode while |
747
|
|
|
|
|
|
|
trying to download a the file [$path] from site [$site]. The ftp error was |
748
|
0
|
|
|
|
|
0
|
[@{[$ftp->message]}]. See perldoc App::Fetchware. |
749
|
|
|
|
|
|
|
EOD |
750
|
|
|
|
|
|
|
|
751
|
|
|
|
|
|
|
# change the directory on the ftp site |
752
|
0
|
0
|
|
|
|
0
|
$ftp->cwd($directories) |
753
|
|
|
|
|
|
|
or die <
|
754
|
|
|
|
|
|
|
App-Fetchware: run-time error. fetchware failed to cwd() to [$path] on site |
755
|
0
|
|
|
|
|
0
|
[$site]. The ftp error was [@{[$ftp->message]}]. See perldoc App::Fetchware. |
756
|
|
|
|
|
|
|
EOD |
757
|
|
|
|
|
|
|
|
758
|
|
|
|
|
|
|
|
759
|
|
|
|
|
|
|
# Download the file to the current directory. The start() subroutine should |
760
|
|
|
|
|
|
|
# have cd()d to a tempdir for fetchware to use. |
761
|
0
|
0
|
|
|
|
0
|
$ftp->get($file) |
762
|
|
|
|
|
|
|
or die <
|
763
|
|
|
|
|
|
|
App-Fetchware: run-time error. fetchware failed to download the file [$file] |
764
|
|
|
|
|
|
|
from path [$path] on server [$site]. The ftp error message was |
765
|
0
|
|
|
|
|
0
|
[@{[$ftp->message]}]. See perldoc App::Fetchware. |
766
|
|
|
|
|
|
|
EOD |
767
|
|
|
|
|
|
|
|
768
|
|
|
|
|
|
|
# ftp done! |
769
|
0
|
|
|
|
|
0
|
$ftp->quit; |
770
|
|
|
|
|
|
|
|
771
|
|
|
|
|
|
|
# The caller needs the $filename to determine the $package_path later. |
772
|
0
|
|
|
|
|
0
|
return $file; |
773
|
|
|
|
|
|
|
} |
774
|
|
|
|
|
|
|
|
775
|
|
|
|
|
|
|
|
776
|
|
|
|
|
|
|
|
777
|
|
|
|
|
|
|
sub download_http_url { |
778
|
0
|
|
|
0
|
1
|
0
|
my $http_url = shift; |
779
|
|
|
|
|
|
|
|
780
|
|
|
|
|
|
|
# Forward any other options over to HTTP::Tiny. This is used mostly to |
781
|
|
|
|
|
|
|
# support changing user agent strings, but why not support them all. |
782
|
0
|
0
|
|
|
|
0
|
my %opts = @_ if @_ % 2 == 0; |
783
|
|
|
|
|
|
|
|
784
|
|
|
|
|
|
|
# Append user_agent if specified. |
785
|
0
|
0
|
|
|
|
0
|
$opts{agent} = config('user_agent') if config('user_agent'); |
786
|
|
|
|
|
|
|
|
787
|
0
|
|
|
|
|
0
|
my $http = HTTP::Tiny->new(%opts); |
788
|
|
|
|
|
|
|
###BUGALERT### Should use request() instead of get, because request can |
789
|
|
|
|
|
|
|
#directly write the chunks of the file to disk as they are downloaded. get() |
790
|
|
|
|
|
|
|
#just uses RAM, so a 50Meg file takes up 50 megs of ram, and so on. |
791
|
0
|
|
|
|
|
0
|
my $response = $http->get($http_url); |
792
|
|
|
|
|
|
|
|
793
|
|
|
|
|
|
|
#use Test::More; |
794
|
|
|
|
|
|
|
#diag("RESPONSE OBJECT["); |
795
|
|
|
|
|
|
|
#diag explain $response->{status}; |
796
|
|
|
|
|
|
|
#diag explain $response->{headers}; |
797
|
|
|
|
|
|
|
#diag explain $response->{url}; |
798
|
|
|
|
|
|
|
#diag explain $response->{reason}; |
799
|
|
|
|
|
|
|
#diag explain $response->{success}; |
800
|
|
|
|
|
|
|
## Should be commented out to avoid borking the terminal, but is needed when |
801
|
|
|
|
|
|
|
## HTTP::Tiny has internal 599 errors, because the error message is in the |
802
|
|
|
|
|
|
|
## content. |
803
|
|
|
|
|
|
|
##diag explain $response->{content}; |
804
|
|
|
|
|
|
|
#diag("]"); |
805
|
|
|
|
|
|
|
|
806
|
0
|
0
|
|
|
|
0
|
die <{success}; |
807
|
|
|
|
|
|
|
App-Fetchware: run-time error. HTTP::Tiny failed to download a file or directory |
808
|
|
|
|
|
|
|
listingfrom your provided url [$http_url]. HTTP status code |
809
|
|
|
|
|
|
|
[$response->{status} $response->{reason}] HTTP headers |
810
|
0
|
|
|
|
|
0
|
[@{[Data::Dumper::Dumper($response->{headers})]}]. |
811
|
|
|
|
|
|
|
See man App::Fetchware. |
812
|
|
|
|
|
|
|
EOD |
813
|
|
|
|
|
|
|
|
814
|
|
|
|
|
|
|
# In this case the content is binary, so it will mess up your terminal. |
815
|
|
|
|
|
|
|
#diag($response->{content}) if length $response->{content}; |
816
|
0
|
0
|
|
|
|
0
|
die <{content}; |
817
|
|
|
|
|
|
|
App-Fetchware: run-time error. The url [$http_url] you provided downloaded |
818
|
|
|
|
|
|
|
nothing. HTTP status code [$response->{status} $response->{reason}] |
819
|
0
|
|
|
|
|
0
|
HTTP headers [@{[Data::Dumper::Dumper($response)]}]. |
820
|
|
|
|
|
|
|
See man App::Fetchware. |
821
|
|
|
|
|
|
|
EOD |
822
|
|
|
|
|
|
|
|
823
|
|
|
|
|
|
|
# Must convert the worthless $response->{content} variable into a real file |
824
|
|
|
|
|
|
|
# on the filesystem. Note: start() should have cd()d us into a suitable |
825
|
|
|
|
|
|
|
# tempdir. |
826
|
0
|
|
|
|
|
0
|
my $path = $http_url; |
827
|
0
|
|
|
|
|
0
|
$path =~ s!^http://!!; |
828
|
|
|
|
|
|
|
# Determine filename from the $path. |
829
|
0
|
|
|
|
|
0
|
my ($volume, $directories, $filename) = splitpath($path); |
830
|
|
|
|
|
|
|
# If $filename is empty string, then its probably a index directory listing. |
831
|
0
|
|
0
|
|
|
0
|
$filename ||= 'index.html'; |
832
|
|
|
|
|
|
|
###BUGALERT### Need binmode() on Windows??? |
833
|
|
|
|
|
|
|
###BUGALERT### Switch to safe_open()???? |
834
|
0
|
0
|
|
|
|
0
|
open(my $fh, '>', $filename) or die <
|
835
|
|
|
|
|
|
|
App-Fetchware: run-time error. Fetchware failed to open a file necessary for |
836
|
|
|
|
|
|
|
fetchware to store HTTP::Tiny's output. Os error [$!]. See perldoc |
837
|
|
|
|
|
|
|
App::Fetchware. |
838
|
|
|
|
|
|
|
EOD |
839
|
|
|
|
|
|
|
# Write HTTP::Tiny's downloaded file to a real file on the filesystem. |
840
|
0
|
|
|
|
|
0
|
print $fh $response->{content}; |
841
|
0
|
0
|
|
|
|
0
|
close $fh |
842
|
|
|
|
|
|
|
or die <
|
843
|
|
|
|
|
|
|
App-Fetchware: run-time error. Fetchware failed to close the file it created to |
844
|
|
|
|
|
|
|
save the content it downloaded from HTTP::Tiny. This file was [$filename]. OS |
845
|
|
|
|
|
|
|
error [$!]. See perldoc App::Fetchware. |
846
|
|
|
|
|
|
|
EOS |
847
|
|
|
|
|
|
|
|
848
|
|
|
|
|
|
|
# The caller needs the $filename to determine the $package_path later. |
849
|
0
|
|
|
|
|
0
|
return $filename; |
850
|
|
|
|
|
|
|
} |
851
|
|
|
|
|
|
|
|
852
|
|
|
|
|
|
|
|
853
|
|
|
|
|
|
|
|
854
|
|
|
|
|
|
|
|
855
|
|
|
|
|
|
|
sub download_file_url { |
856
|
5
|
|
|
5
|
1
|
8
|
my $url = shift; |
857
|
|
|
|
|
|
|
|
858
|
5
|
|
|
|
|
28
|
$url =~ s!^file://!!; # Strip useless URL scheme. |
859
|
|
|
|
|
|
|
|
860
|
|
|
|
|
|
|
# Prepend original_cwd() only if the $url is *not* absolute, which will mess |
861
|
|
|
|
|
|
|
# it up. |
862
|
5
|
50
|
|
|
|
28
|
$url = catdir(original_cwd(), $url) unless file_name_is_absolute($url); |
863
|
|
|
|
|
|
|
|
864
|
|
|
|
|
|
|
# Download the file:// URL to the current directory, which should already be |
865
|
|
|
|
|
|
|
# in $temp_dir, because of start()'s chdir(). |
866
|
|
|
|
|
|
|
# |
867
|
|
|
|
|
|
|
# Don't forget to clear taint. Fetchware does *not* run in taint mode, but |
868
|
|
|
|
|
|
|
# for some reason, bug?, File::Copy checks if data is tainted, and then |
869
|
|
|
|
|
|
|
# retaints it if it is already tainted, but for some reason I get "Insecure |
870
|
|
|
|
|
|
|
# dependency" taint failure exceptions when drop priving. The fix is to |
871
|
|
|
|
|
|
|
# always untaint my data as done below. |
872
|
|
|
|
|
|
|
###BUGALERT### Investigate this as a possible taint bug in perl or just |
873
|
|
|
|
|
|
|
#File::Copy. Perhaps the cause is using File::Copy::cp(copy) after drop |
874
|
|
|
|
|
|
|
#priving with data from root? |
875
|
5
|
|
|
|
|
77
|
$url =~ /(.*)/; |
876
|
5
|
|
|
|
|
14
|
my $untainted_url = $1; |
877
|
5
|
|
|
|
|
14894
|
my $cwd = cwd(); |
878
|
5
|
|
|
|
|
64
|
$cwd =~ /(.*)/; |
879
|
5
|
|
|
|
|
35
|
my $untainted_cwd = $1; |
880
|
5
|
50
|
|
|
|
71
|
cp($untainted_url, $untainted_cwd) or die <
|
881
|
|
|
|
|
|
|
App::Fetchware: run-time error. Fetchware failed to copy the download URL |
882
|
|
|
|
|
|
|
[$untainted_url] to the working directory [$untainted_cwd]. Os error [$!]. |
883
|
|
|
|
|
|
|
EOD |
884
|
|
|
|
|
|
|
|
885
|
|
|
|
|
|
|
# Return just file filename of the downloaded file. |
886
|
5
|
|
|
|
|
2686
|
return file($url)->basename(); |
887
|
|
|
|
|
|
|
} |
888
|
|
|
|
|
|
|
|
889
|
|
|
|
|
|
|
|
890
|
|
|
|
|
|
|
|
891
|
|
|
|
|
|
|
|
892
|
|
|
|
|
|
|
|
893
|
|
|
|
|
|
|
|
894
|
|
|
|
|
|
|
|
895
|
|
|
|
|
|
|
###BUGALERT### safe_open() does not check extended file perms such as ext*'s |
896
|
|
|
|
|
|
|
#crazy attributes, linux's (And other Unixs' too) MAC stuff or Windows NT's |
897
|
|
|
|
|
|
|
#crazy file permissions. Could use Win32::Perms for just Windows, but its not |
898
|
|
|
|
|
|
|
#on CPAN. And what about the other OSes. |
899
|
|
|
|
|
|
|
###BUGALERT### Consier moving this to CPAN??? File::SafeOpen???? |
900
|
|
|
|
|
|
|
sub safe_open { |
901
|
181
|
|
|
181
|
1
|
2438476
|
my $file_to_check = shift; |
902
|
181
|
|
100
|
|
|
876
|
my $open_fail_message = shift // <
|
903
|
|
|
|
|
|
|
Failed to open file [$file_to_check]. OS error [$!]. |
904
|
|
|
|
|
|
|
EOE |
905
|
|
|
|
|
|
|
|
906
|
181
|
|
|
|
|
423
|
my %opts = @_; |
907
|
|
|
|
|
|
|
|
908
|
181
|
|
|
|
|
210
|
my $fh; |
909
|
|
|
|
|
|
|
|
910
|
|
|
|
|
|
|
|
911
|
|
|
|
|
|
|
# Open the file first. |
912
|
181
|
100
|
66
|
|
|
749
|
unless (exists $opts{MODE} and defined $opts{MODE}) { |
913
|
180
|
50
|
|
|
|
7261
|
open $fh, '<', $file_to_check or die $open_fail_message; |
914
|
|
|
|
|
|
|
} else { |
915
|
1
|
50
|
|
|
|
48
|
open $fh, $opts{MODE}, $file_to_check or die $open_fail_message; |
916
|
|
|
|
|
|
|
} |
917
|
|
|
|
|
|
|
|
918
|
181
|
|
|
|
|
1503
|
my $info = stat($fh);# or goto STAT_ERROR; |
919
|
|
|
|
|
|
|
|
920
|
|
|
|
|
|
|
# Owner must be either me (whoever runs fetchware) or superuser. No one else |
921
|
|
|
|
|
|
|
# can be trusted. |
922
|
181
|
50
|
33
|
|
|
29962
|
if(($info->uid() != 0) && ($info->uid() != $<)) { |
923
|
0
|
|
|
|
|
0
|
die <
|
924
|
|
|
|
|
|
|
App-Fetchware-Util: The file fetchware attempted to open is not owned by root or |
925
|
|
|
|
|
|
|
the person who ran fetchware. This means the file could have been dangerously |
926
|
|
|
|
|
|
|
altered, or it's a simple permissions problem. Do not simly change the |
927
|
|
|
|
|
|
|
ownership, and rerun fetchware. Please check that the file [$file_to_check] has |
928
|
|
|
|
|
|
|
not been tampered with, correct the ownership problems and try again. |
929
|
|
|
|
|
|
|
EOD |
930
|
|
|
|
|
|
|
} |
931
|
|
|
|
|
|
|
|
932
|
|
|
|
|
|
|
# Check if group and other can write $fh. |
933
|
|
|
|
|
|
|
# Use 066 to detect read or write perms. |
934
|
|
|
|
|
|
|
###BUGALERT### What does this actually test????? |
935
|
181
|
100
|
|
|
|
3525
|
if ($info->mode() & 022) { # Someone else can write this $fh. |
936
|
2
|
|
|
|
|
32
|
die <
|
937
|
|
|
|
|
|
|
App-Fetchware-Util: The file fetchware attempted to open [$file_to_check] is |
938
|
|
|
|
|
|
|
writable by someone other than just the owner. Fetchwarefiles and fetchware |
939
|
|
|
|
|
|
|
packages must only be writable by the owner. Do not only change permissions to |
940
|
|
|
|
|
|
|
fix this error. This error may have allowed someone to alter the contents of |
941
|
|
|
|
|
|
|
your Fetchwarefile or fetchware packages. Ensure the file was not altered, then |
942
|
|
|
|
|
|
|
change permissions to 644. |
943
|
|
|
|
|
|
|
EOD |
944
|
|
|
|
|
|
|
} |
945
|
|
|
|
|
|
|
|
946
|
|
|
|
|
|
|
# Then check the directories its contained in. |
947
|
|
|
|
|
|
|
|
948
|
|
|
|
|
|
|
# Make the file an absolute path if its not already. |
949
|
179
|
|
|
|
|
2041
|
$file_to_check = rel2abs($file_to_check); |
950
|
|
|
|
|
|
|
|
951
|
|
|
|
|
|
|
# Create array of current directory and all parent directories and even root |
952
|
|
|
|
|
|
|
# directory to check all of their permissions below. |
953
|
179
|
|
|
|
|
3862
|
my $dir = dir($file_to_check); |
954
|
179
|
|
|
|
|
9197
|
my @directories = do { |
955
|
179
|
|
|
|
|
244
|
my @dirs; |
956
|
179
|
|
|
|
|
1271
|
until ($dir eq rootdir()) { |
957
|
|
|
|
|
|
|
# Add this dir to the array of dirs to keep. |
958
|
539
|
|
|
|
|
25906
|
push @dirs, $dir; |
959
|
|
|
|
|
|
|
|
960
|
|
|
|
|
|
|
# This loops version of $i++ the itcremeter. |
961
|
539
|
|
|
|
|
1722
|
$dir = $dir->parent(); |
962
|
|
|
|
|
|
|
} |
963
|
179
|
|
|
|
|
9152
|
push @dirs, $dir->parent(); # $dir->parent() should be the root dir. |
964
|
|
|
|
|
|
|
|
965
|
|
|
|
|
|
|
# Return, by being the last statement, the list of parent dirs for |
966
|
|
|
|
|
|
|
# $file_to_check. |
967
|
179
|
|
|
|
|
6945
|
@dirs; |
968
|
|
|
|
|
|
|
}; |
969
|
|
|
|
|
|
|
# Who cares if _PC_CHOWN_RESTRICTED is set, check all parent dirs anyway, |
970
|
|
|
|
|
|
|
# because if say /home was 777, then anyone (other) can change any child |
971
|
|
|
|
|
|
|
# file in any directory above /home now anyway even if _PC_CHOWN_RESTRICTED |
972
|
|
|
|
|
|
|
# is set. |
973
|
179
|
|
|
|
|
676
|
for my $dir (@directories) { |
974
|
|
|
|
|
|
|
|
975
|
716
|
|
|
|
|
8245
|
my $info = stat($dir);# or goto STAT_ERROR; |
976
|
|
|
|
|
|
|
|
977
|
|
|
|
|
|
|
# Owner must be either me (whoever runs fetchware) or superuser. No one |
978
|
|
|
|
|
|
|
# else can be trusted. |
979
|
716
|
50
|
33
|
|
|
71711
|
if(($info->uid() != 0) && ($info->uid() != $<)) { |
980
|
0
|
|
|
|
|
0
|
die <
|
981
|
|
|
|
|
|
|
App-Fetchware-Util: The file fetchware attempted to open is not owned by root or |
982
|
|
|
|
|
|
|
the person who ran fetchware. This means the file could have been dangerously |
983
|
|
|
|
|
|
|
altered, or it's a simple permissions problem. Do not simly change the |
984
|
|
|
|
|
|
|
ownership, and rerun fetchware. Please check that the file [$file_to_check] has |
985
|
|
|
|
|
|
|
not been tampered with, correct the ownership problems and try again. |
986
|
|
|
|
|
|
|
EOD |
987
|
|
|
|
|
|
|
} |
988
|
|
|
|
|
|
|
|
989
|
|
|
|
|
|
|
# Check if group and other can write $fh. |
990
|
|
|
|
|
|
|
# Use 066 to detect read or write perms. |
991
|
|
|
|
|
|
|
###BUGALERT### What does this actually test????? |
992
|
716
|
100
|
|
|
|
11237
|
if ($info->mode() & 022) { # Someone else can write this $fh... |
993
|
|
|
|
|
|
|
# ...except if this file has the sticky bit set and its a directory. |
994
|
179
|
100
|
66
|
|
|
3297
|
die <mode & 01000 and S_ISDIR($info->mode); |
995
|
|
|
|
|
|
|
App-Fetchware-Util: The file fetchware attempted to open [$file_to_check] is |
996
|
|
|
|
|
|
|
writable by someone other than just the owner. Fetchwarefiles and fetchware |
997
|
|
|
|
|
|
|
packages must only be writable by the owner. Do not only change permissions to |
998
|
|
|
|
|
|
|
fix this error. This error may have allowed someone to alter the contents of |
999
|
|
|
|
|
|
|
your Fetchwarefile or fetchware packages. Ensure the file was not altered, then |
1000
|
|
|
|
|
|
|
change permissions to 644. Permissions on failed directory were: |
1001
|
1
|
|
|
|
|
22
|
@{[Dumper($info)]} |
1002
|
1
|
|
|
|
|
171
|
Umask [@{[umask]}]. |
1003
|
|
|
|
|
|
|
EOD |
1004
|
|
|
|
|
|
|
} |
1005
|
|
|
|
|
|
|
|
1006
|
|
|
|
|
|
|
} |
1007
|
|
|
|
|
|
|
# Return the proven above "safe" file handle. |
1008
|
178
|
|
|
|
|
1942
|
return $fh; |
1009
|
|
|
|
|
|
|
|
1010
|
|
|
|
|
|
|
# Use cool C style goto error handling. It beats copy and paste, and the |
1011
|
|
|
|
|
|
|
# horrible contortions needed for "structured programming." |
1012
|
|
|
|
|
|
|
STAT_ERROR: { |
1013
|
0
|
|
|
|
|
0
|
die <
|
|
0
|
|
|
|
|
0
|
|
1014
|
|
|
|
|
|
|
App-Fetchware-Util: stat($fh) filename [$file_to_check] failed! This just |
1015
|
|
|
|
|
|
|
shouldn't happen unless of course the file you specified does not exist. Please |
1016
|
|
|
|
|
|
|
ensure files you specify when you run fetchware actually exist. |
1017
|
|
|
|
|
|
|
EOD |
1018
|
|
|
|
|
|
|
} |
1019
|
|
|
|
|
|
|
} |
1020
|
|
|
|
|
|
|
|
1021
|
|
|
|
|
|
|
|
1022
|
|
|
|
|
|
|
|
1023
|
|
|
|
|
|
|
sub drop_privs { |
1024
|
128
|
|
|
128
|
1
|
27792
|
my $child_code = shift; |
1025
|
128
|
|
100
|
|
|
1040
|
my $regular_user = shift // 'nobody'; |
1026
|
128
|
|
|
|
|
287
|
my %opts = @_; |
1027
|
|
|
|
|
|
|
|
1028
|
|
|
|
|
|
|
# Need to do this in 2 places. |
1029
|
|
|
|
|
|
|
my $dont_drop_privs = sub { |
1030
|
2
|
|
|
2
|
|
8
|
my $child_code = shift; |
1031
|
|
|
|
|
|
|
|
1032
|
2
|
|
|
|
|
4
|
my $output; |
1033
|
2
|
50
|
|
|
|
68
|
open my $output_fh, '>', \$output or die <
|
1034
|
|
|
|
|
|
|
App-Fetchware-Util: fetchware failed to open an internal scalar reference as a |
1035
|
|
|
|
|
|
|
file handle. OS error [$!]. |
1036
|
|
|
|
|
|
|
EOD |
1037
|
2
|
|
|
|
|
12
|
$child_code->($output_fh); |
1038
|
|
|
|
|
|
|
|
1039
|
2
|
50
|
|
|
|
38
|
close $output_fh or die <
|
1040
|
|
|
|
|
|
|
App-Fetchware-Util: fetchware failed to close an internal scalar reference that |
1041
|
|
|
|
|
|
|
was open as a file handle. OS error [$!]. |
1042
|
|
|
|
|
|
|
EOD |
1043
|
2
|
|
|
|
|
14
|
return \$output; |
1044
|
128
|
|
|
|
|
1206
|
}; |
1045
|
|
|
|
|
|
|
|
1046
|
|
|
|
|
|
|
# Execute $child_code without dropping privs if the user's configuration |
1047
|
|
|
|
|
|
|
# file is configured to force fetchware to "stay_root." |
1048
|
128
|
100
|
|
|
|
422
|
if (config('stay_root')) { |
1049
|
2
|
|
|
|
|
22
|
msg <
|
1050
|
|
|
|
|
|
|
stay_root is set to true. NOT dropping privileges! |
1051
|
|
|
|
|
|
|
EOM |
1052
|
2
|
|
|
|
|
12
|
return $dont_drop_privs->($child_code); |
1053
|
|
|
|
|
|
|
} |
1054
|
|
|
|
|
|
|
|
1055
|
126
|
50
|
33
|
|
|
1004
|
if (is_os_type('Unix') and ($< == 0 or $> == 0)) { |
|
|
|
33
|
|
|
|
|
1056
|
|
|
|
|
|
|
# cmd_new() needs to skip the creation of this useless directory that it |
1057
|
|
|
|
|
|
|
# does not use. Furthemore, the creation of this extra tempdir is not |
1058
|
|
|
|
|
|
|
# needed by cmd_new(), and this tempdir presumes start() was called |
1059
|
|
|
|
|
|
|
# before drop_privs(), which is always the case except for cmd_new(). |
1060
|
|
|
|
|
|
|
# |
1061
|
|
|
|
|
|
|
# But another case where this temp dir's creations should be skipped is |
1062
|
|
|
|
|
|
|
# if start() is overridden with hook() to make start() do something |
1063
|
|
|
|
|
|
|
# other than create a temp dir, because in some cases such as using VCS |
1064
|
|
|
|
|
|
|
# instead of Web sites and mirrors, you do not need to bother with |
1065
|
|
|
|
|
|
|
# creating a tempdir, because the working dir of the repo can be used |
1066
|
|
|
|
|
|
|
# instead. Therefore, if the parent directory is not /^fetchware-$$/, |
1067
|
|
|
|
|
|
|
# then we'll also skip creating the tempd dir, because it most likely |
1068
|
|
|
|
|
|
|
# means that a tempdir is not needed. |
1069
|
126
|
100
|
|
|
|
279405
|
$opts{SkipTempDirCreation} = 1 |
1070
|
|
|
|
|
|
|
unless file(cwd())->basename() =~ /^fetchware-$$/; |
1071
|
126
|
50
|
66
|
|
|
21956
|
unless (exists $opts{SkipTempDirCreation} |
|
|
|
66
|
|
|
|
|
1072
|
|
|
|
|
|
|
and defined $opts{SkipTempDirCreation} |
1073
|
|
|
|
|
|
|
and $opts{SkipTempDirCreation}) { |
1074
|
|
|
|
|
|
|
# Ensure that $user_temp_dir can be accessed by my drop priv'd child. |
1075
|
|
|
|
|
|
|
# And only try to change perms to 0755 only if perms are not 0755 |
1076
|
|
|
|
|
|
|
# already. |
1077
|
120
|
|
|
|
|
245034
|
my $st = stat(cwd()); |
1078
|
120
|
50
|
|
|
|
22631
|
unless ((S_IMODE($st->mode) & 0755) >= 0755) { |
1079
|
120
|
50
|
|
|
|
265938
|
chmod 0755, cwd() or die <
|
1080
|
|
|
|
|
|
|
App-Fetchware-Util: Fetchware failed to change the permissions of the current |
1081
|
0
|
|
|
|
|
0
|
temporary directory [@{[cwd()]} to 0755. The OS error was [$!]. |
1082
|
|
|
|
|
|
|
EOD |
1083
|
|
|
|
|
|
|
} |
1084
|
|
|
|
|
|
|
# Create a new tempdir for the droped prive user to use, and be sure |
1085
|
|
|
|
|
|
|
# to chown it so they can actually write to it as well. |
1086
|
|
|
|
|
|
|
# $new_temp_dir does not have a semaphore file, but its parent |
1087
|
|
|
|
|
|
|
# directory does, which will still keep fetchware clean from |
1088
|
|
|
|
|
|
|
# deleting this directory out from underneath us. |
1089
|
|
|
|
|
|
|
# |
1090
|
|
|
|
|
|
|
# Also note, that cwd() is "blindly" coded here, which makes it a |
1091
|
|
|
|
|
|
|
# "dependency," but drop_privs() is meant to be called after start() |
1092
|
|
|
|
|
|
|
# by fetchware::cmd_*(). It's not meant to be a generic subroutine |
1093
|
|
|
|
|
|
|
# to drop privs, and it's also not really meant to be used by |
1094
|
|
|
|
|
|
|
# fetchware extensions mostly just fetchware itself. Perhaps I |
1095
|
|
|
|
|
|
|
# should move it back to bin/fetchware??? |
1096
|
|
|
|
|
|
|
# |
1097
|
|
|
|
|
|
|
# Also also note, that CLEANUP option is *not* specified, because |
1098
|
|
|
|
|
|
|
# that can cause this directory in cases of errors, and you can't |
1099
|
|
|
|
|
|
|
# track down an error in a build script if the directory everything |
1100
|
|
|
|
|
|
|
# is in has been deleted. |
1101
|
120
|
|
|
|
|
242766
|
my $new_temp_dir = tempdir("fetchware-$$-XXXXXXXXXX", |
1102
|
|
|
|
|
|
|
DIR => cwd()); |
1103
|
|
|
|
|
|
|
# Determine /etc/passwd entry for the "effective" uid of the |
1104
|
|
|
|
|
|
|
# current fetchware process. I should use the "effective" uid |
1105
|
|
|
|
|
|
|
# instead of the "real" uid, because effective uid is used to |
1106
|
|
|
|
|
|
|
# determine what each uid can do, and the real uid is only |
1107
|
|
|
|
|
|
|
# really used to track who the original user was in a setuid |
1108
|
|
|
|
|
|
|
# program. |
1109
|
120
|
|
50
|
|
|
46719
|
my ($name, $useless, $uid, $gid, $quota, $comment, $gcos, $dir, |
1110
|
|
|
|
|
|
|
$shell, $expire) |
1111
|
|
|
|
|
|
|
= getpwnam(config('user') // 'nobody'); |
1112
|
120
|
50
|
|
|
|
2536
|
chown($uid, $gid, $new_temp_dir) or die <
|
1113
|
|
|
|
|
|
|
App-Fetchware-Util: Fetchware failed to chown [$new_temp_dir] to the user it is |
1114
|
|
|
|
|
|
|
dropping privileges to. This just shouldn't happen, and might be a bug, or |
1115
|
|
|
|
|
|
|
perhaps your system temporary directory is full. The OS error was [$!]. |
1116
|
|
|
|
|
|
|
EOD |
1117
|
120
|
50
|
|
|
|
1530
|
chmod(0700, $new_temp_dir) or die <
|
1118
|
|
|
|
|
|
|
App-Fetchware-Util: Fetchware failed to change the permissions of its new |
1119
|
|
|
|
|
|
|
temporary directory [$new_temp_dir] to 0700 that it created, because its |
1120
|
|
|
|
|
|
|
dropping privileges. This just shouldn't happen, and is bug, or perhaps your |
1121
|
|
|
|
|
|
|
system temporary directory is full. The OS error is [$!]. |
1122
|
|
|
|
|
|
|
EOD |
1123
|
|
|
|
|
|
|
# And of course chdir() to $new_temp_dir, because everything assumes |
1124
|
|
|
|
|
|
|
# that the cwd() is where everything should be saved and done. |
1125
|
120
|
50
|
|
|
|
1511
|
chdir($new_temp_dir) or die <
|
1126
|
|
|
|
|
|
|
App-Fetchware-Util: Fetchware failed to chdir() to its new temporary directory |
1127
|
|
|
|
|
|
|
[$new_temp_dir]. This shouldn't happen, and is most likely a bug, or perhaps |
1128
|
|
|
|
|
|
|
your system temporary directory is full. The OS error was [$!]. |
1129
|
|
|
|
|
|
|
EOD |
1130
|
|
|
|
|
|
|
} |
1131
|
|
|
|
|
|
|
|
1132
|
|
|
|
|
|
|
# Open a pipe to allow the child to talk back to the parent. |
1133
|
126
|
50
|
|
|
|
1732
|
pipe(READONLY, WRITEONLY) or die <
|
1134
|
|
|
|
|
|
|
App-Fetchware-Util: Fetchware failed to create a pipe to allow the forked |
1135
|
|
|
|
|
|
|
process to communication back to the parent process. OS error [$!]. |
1136
|
|
|
|
|
|
|
EOD |
1137
|
|
|
|
|
|
|
# Turn them into proper lexical file handles. |
1138
|
126
|
|
|
|
|
1266
|
my ($readonly, $writeonly) = (*READONLY, *WRITEONLY); |
1139
|
|
|
|
|
|
|
|
1140
|
|
|
|
|
|
|
# Set up a SIGPIPE handler in case the writer closes the pipe before the |
1141
|
|
|
|
|
|
|
# reader closes their pipe. |
1142
|
|
|
|
|
|
|
$SIG{'PIPE'} = sub { |
1143
|
0
|
|
|
0
|
|
0
|
die <
|
1144
|
|
|
|
|
|
|
App-Fetchware-Util: Fetchware received a PIPE signal from the OS indicating the |
1145
|
|
|
|
|
|
|
pipe is dead. This should not happen, and is because the child was killed out |
1146
|
|
|
|
|
|
|
from under the parent, or there is a bug. This is a fatal error, because it's |
1147
|
|
|
|
|
|
|
possible the parent needs whatever information the child was going to use the |
1148
|
|
|
|
|
|
|
pipe to send to the parent, and now it is unclear if the proper expected output |
1149
|
|
|
|
|
|
|
has been received or not; therefore, we're just playing it safe and die()ing. |
1150
|
|
|
|
|
|
|
EOD |
1151
|
126
|
|
|
|
|
1851
|
}; |
1152
|
|
|
|
|
|
|
|
1153
|
|
|
|
|
|
|
# Code below based on a cool forking idiom by Aristotle. |
1154
|
|
|
|
|
|
|
# (http://blogs.perl.org/users/aristotle/2012/10/concise-fork-idiom.html) |
1155
|
126
|
|
|
|
|
83717
|
for ( scalar fork ) { |
1156
|
|
|
|
|
|
|
# Fork failed. |
1157
|
|
|
|
|
|
|
# defined() operates on default variable, $_. |
1158
|
126
|
50
|
|
|
|
2452
|
if (not defined $_) { |
1159
|
0
|
|
|
|
|
0
|
die <
|
1160
|
|
|
|
|
|
|
App-Fetchware-Util: Fork failed! This shouldn't happen!?! Os error [$!]. |
1161
|
|
|
|
|
|
|
EOD |
1162
|
|
|
|
|
|
|
} |
1163
|
|
|
|
|
|
|
|
1164
|
|
|
|
|
|
|
# Fork succeeded, Parent code goes here. |
1165
|
126
|
|
|
|
|
877
|
my $kidpid = $_; |
1166
|
126
|
50
|
|
|
|
936
|
if ( $kidpid ) { |
1167
|
126
|
50
|
|
|
|
5443
|
close $writeonly or die <
|
1168
|
|
|
|
|
|
|
App-Fetchware-Util: Failed to close $writeonly pipe in parent. Os error [$!]. |
1169
|
|
|
|
|
|
|
EOD |
1170
|
126
|
|
|
|
|
706
|
my $output; |
1171
|
|
|
|
|
|
|
|
1172
|
|
|
|
|
|
|
# Read the child's output until child closes pipe sending EOF. |
1173
|
126
|
|
|
|
|
4410708
|
$output .= $_ while (<$readonly>); |
1174
|
|
|
|
|
|
|
|
1175
|
|
|
|
|
|
|
# Close $readonly pipe, because we have received the output from |
1176
|
|
|
|
|
|
|
# the user. |
1177
|
126
|
50
|
|
|
|
3307
|
close $readonly or die <
|
1178
|
|
|
|
|
|
|
App-Fetchware-Util: Failed to close $readonly pipe in parent. Os error [$!]. |
1179
|
|
|
|
|
|
|
EOD |
1180
|
|
|
|
|
|
|
|
1181
|
|
|
|
|
|
|
# Just block waiting for the child to finish. |
1182
|
126
|
|
|
|
|
161408
|
waitpid($kidpid, 0); |
1183
|
|
|
|
|
|
|
# If the child failed ($? >> 8 != 0), then the parent should |
1184
|
|
|
|
|
|
|
# fail as well, because the child only exists to drop privs with |
1185
|
|
|
|
|
|
|
# the ability to still at a later time execute something as root |
1186
|
|
|
|
|
|
|
# again, so the fork is needed, because once you drop privs |
1187
|
|
|
|
|
|
|
# you can't get them back, and you don't want to be able to for |
1188
|
|
|
|
|
|
|
# security reasons. |
1189
|
126
|
50
|
|
|
|
1413
|
if (($? >> 8) != 0) { |
1190
|
|
|
|
|
|
|
# Note this message is only vmsg()'d instead of die()'d, |
1191
|
|
|
|
|
|
|
# because if its printed always, it could confuse users. |
1192
|
|
|
|
|
|
|
# Because priv_drop()ing is the default, this error would be |
1193
|
|
|
|
|
|
|
# seen all the time making getting confused by it likely. |
1194
|
0
|
|
|
|
|
0
|
vmsg <
|
1195
|
|
|
|
|
|
|
App-Fetchware-Util: An error occured forcing fetchware to exit while fetchware |
1196
|
|
|
|
|
|
|
has forked to drop its root priviledges to avoid downloading files and building |
1197
|
|
|
|
|
|
|
programs as root. Root priviledges are only maintained to install the software |
1198
|
|
|
|
|
|
|
in a system directory requiring root access. The error that caused the child to |
1199
|
|
|
|
|
|
|
fail will have already been printed above by the child. |
1200
|
|
|
|
|
|
|
EOM |
1201
|
0
|
|
|
|
|
0
|
msg <
|
1202
|
|
|
|
|
|
|
For help troublehsooting fetchware failed inside directory: |
1203
|
0
|
|
|
|
|
0
|
@{[cwd()]} |
1204
|
|
|
|
|
|
|
EOM |
1205
|
|
|
|
|
|
|
# Keep all of fetchware's temporary files and directories |
1206
|
|
|
|
|
|
|
# around so the user has access to them, so they can be |
1207
|
|
|
|
|
|
|
# troubleshooted to see what caused the failure. |
1208
|
0
|
|
|
|
|
0
|
$File::Temp::KEEP_ALL = 1; |
1209
|
|
|
|
|
|
|
# Exit non-zero indicating failure, because whatever the |
1210
|
|
|
|
|
|
|
# child did failed, and the child's main eval {} in |
1211
|
|
|
|
|
|
|
# bin/fetchware caught that failure, printed it to the |
1212
|
|
|
|
|
|
|
# screen, and exit()ed non-zero for failure. And since the |
1213
|
|
|
|
|
|
|
# child failed ($? >> 8 != 0), the parent should fail too. |
1214
|
0
|
|
|
|
|
0
|
exit 1; |
1215
|
|
|
|
|
|
|
# If successful, return to the child a ref of @output to caller. |
1216
|
|
|
|
|
|
|
} else { |
1217
|
126
|
|
|
|
|
5173
|
return \$output; |
1218
|
|
|
|
|
|
|
} |
1219
|
|
|
|
|
|
|
# Fork succeeded, child code goes here. |
1220
|
|
|
|
|
|
|
} else { |
1221
|
0
|
0
|
|
|
|
0
|
close $readonly or die <
|
1222
|
|
|
|
|
|
|
App-Fetchware-Util: Failed to close $readonly pipe in child. Os error [$!]. |
1223
|
|
|
|
|
|
|
EOD |
1224
|
|
|
|
|
|
|
# Drop privs. |
1225
|
|
|
|
|
|
|
# drop_privileges() dies on an error just let drop_privs() caller |
1226
|
|
|
|
|
|
|
# catch it. |
1227
|
0
|
|
|
|
|
0
|
my ($uid, $gid) = drop_privileges($regular_user); |
1228
|
|
|
|
|
|
|
|
1229
|
|
|
|
|
|
|
|
1230
|
|
|
|
|
|
|
# Execute the coderef that is supposed to be done as non-root. |
1231
|
0
|
|
|
|
|
0
|
$child_code->($writeonly); |
1232
|
|
|
|
|
|
|
|
1233
|
|
|
|
|
|
|
# Now close the pipe, to avoid creating a dead pipe causing a |
1234
|
|
|
|
|
|
|
# SIGPIPE to be sent to the parent. |
1235
|
0
|
0
|
|
|
|
0
|
close $writeonly or die <
|
1236
|
|
|
|
|
|
|
App-Fetchware-Util: Failed to close $writeonly pipe in child. Os error [$!]. |
1237
|
|
|
|
|
|
|
EOD |
1238
|
|
|
|
|
|
|
|
1239
|
|
|
|
|
|
|
# Exit success, because failure is only indicated by a thrown |
1240
|
|
|
|
|
|
|
# exception that bin/fetchware's main eval {} will catch, print, |
1241
|
|
|
|
|
|
|
# and exit non-zero indicating failure. |
1242
|
|
|
|
|
|
|
# Use POSIX's _exit() to avoid calling END{} blocks. This *must* |
1243
|
|
|
|
|
|
|
# be done to prevent File::Temp's END{} block from attempting to |
1244
|
|
|
|
|
|
|
# delete the temp directory that the parent still needs to |
1245
|
|
|
|
|
|
|
# finish installing or uninstalling. The parent's END{} block's |
1246
|
|
|
|
|
|
|
# will still be called, so this just turns off the child |
1247
|
|
|
|
|
|
|
# deleting the temp dir not the parent. |
1248
|
0
|
|
|
|
|
0
|
_exit 0; |
1249
|
|
|
|
|
|
|
} |
1250
|
|
|
|
|
|
|
} |
1251
|
|
|
|
|
|
|
# Non-Unix OSes just execute the $child_code. |
1252
|
|
|
|
|
|
|
} else { |
1253
|
0
|
|
|
|
|
0
|
return $dont_drop_privs->($child_code); |
1254
|
|
|
|
|
|
|
} |
1255
|
|
|
|
|
|
|
} |
1256
|
|
|
|
|
|
|
|
1257
|
|
|
|
|
|
|
|
1258
|
|
|
|
|
|
|
|
1259
|
|
|
|
|
|
|
|
1260
|
|
|
|
|
|
|
###BUGALERT### Add quotemeta() support to pipe parsers to help prevent attacks. |
1261
|
|
|
|
|
|
|
|
1262
|
|
|
|
|
|
|
|
1263
|
|
|
|
|
|
|
|
1264
|
|
|
|
|
|
|
{ # Bareblock just for the $MAGIC_NUMBER. |
1265
|
|
|
|
|
|
|
# Determine $front_magic |
1266
|
|
|
|
|
|
|
my $front_magic; |
1267
|
|
|
|
|
|
|
$front_magic = int(rand(8128389023)); |
1268
|
|
|
|
|
|
|
# For no particular reason convert the random integer into hex, because I |
1269
|
|
|
|
|
|
|
# never store something in decimal and then exact same thing in hex. |
1270
|
|
|
|
|
|
|
$front_magic = $front_magic . sprintf("%x", $front_magic); |
1271
|
|
|
|
|
|
|
# Run srand() again to change random number generator between rand() calls. |
1272
|
|
|
|
|
|
|
# Not really necessary, but should make it harder to guess correct magic |
1273
|
|
|
|
|
|
|
# numbers. |
1274
|
|
|
|
|
|
|
srand(time()); |
1275
|
|
|
|
|
|
|
# Same a $front_magic. |
1276
|
|
|
|
|
|
|
my $back_magic = int(rand(986487516)); |
1277
|
|
|
|
|
|
|
# Octal this time :) for no real reason. |
1278
|
|
|
|
|
|
|
$back_magic = $back_magic . sprintf("%o", $back_magic); |
1279
|
|
|
|
|
|
|
my $MAGIC_NUMBER = $front_magic |
1280
|
|
|
|
|
|
|
. 'MAGIC_NUMBER_REPLACING_NEWLINE' |
1281
|
|
|
|
|
|
|
. $back_magic; |
1282
|
|
|
|
|
|
|
|
1283
|
|
|
|
|
|
|
sub write_dropprivs_pipe { |
1284
|
1
|
|
|
1
|
1
|
1250
|
my $write_pipe = shift; |
1285
|
|
|
|
|
|
|
|
1286
|
1
|
|
|
|
|
13
|
for my $a_var (@_) { |
1287
|
4
|
50
|
|
|
|
94
|
die <
|
1288
|
|
|
|
|
|
|
fetchware: Huh? [$a_var] has fetchware's MAGIC_NUMBER in it? This shouldn't |
1289
|
|
|
|
|
|
|
happen, and messes up fetchware's simple IPC. You should never see this error, |
1290
|
|
|
|
|
|
|
because it's not a particuarly magic number if anybody actually uses it. This is |
1291
|
|
|
|
|
|
|
most likely a bug, so please report it. |
1292
|
|
|
|
|
|
|
EOD |
1293
|
|
|
|
|
|
|
|
1294
|
|
|
|
|
|
|
# Write to the $write_pipe, but use the $MAGIC_NUMBER instead of just |
1295
|
|
|
|
|
|
|
# newline. |
1296
|
4
|
|
|
|
|
26
|
print $write_pipe $a_var . $MAGIC_NUMBER; |
1297
|
|
|
|
|
|
|
} |
1298
|
|
|
|
|
|
|
} |
1299
|
|
|
|
|
|
|
|
1300
|
|
|
|
|
|
|
|
1301
|
|
|
|
|
|
|
|
1302
|
|
|
|
|
|
|
sub read_dropprivs_pipe { |
1303
|
121
|
|
|
121
|
1
|
1708
|
my $output = shift; |
1304
|
|
|
|
|
|
|
|
1305
|
121
|
50
|
|
|
|
560
|
die <
|
1306
|
|
|
|
|
|
|
App-Fetchware-Util: pipe_read_newling() was called with an output variable |
1307
|
|
|
|
|
|
|
[$output] that was not a scalar reference. It must be a scalar reference. |
1308
|
|
|
|
|
|
|
EOD |
1309
|
|
|
|
|
|
|
|
1310
|
121
|
|
|
|
|
218
|
my @variables; |
1311
|
121
|
|
|
|
|
1535
|
for my $variable (split(/$MAGIC_NUMBER/, $$output)) { |
1312
|
|
|
|
|
|
|
# And some error handling just in case. |
1313
|
232
|
50
|
|
|
|
573
|
die <
|
1314
|
|
|
|
|
|
|
fetchware: Huh? The child failed to write the proper variable back to the |
1315
|
|
|
|
|
|
|
parent! The variable is [$variable]. This should be defined but it is |
1316
|
|
|
|
|
|
|
not! |
1317
|
|
|
|
|
|
|
EOD |
1318
|
|
|
|
|
|
|
# Clear possibly tainted variables. It's a weird bug that makes no |
1319
|
|
|
|
|
|
|
# sense. I don't turn -t or -T on, so what gives??? If you're curious |
1320
|
|
|
|
|
|
|
# try commenting out the taint clearing code below, and running the |
1321
|
|
|
|
|
|
|
# t/bin-fetchware-install.t test file (Or any other ones that call |
1322
|
|
|
|
|
|
|
# drop_privs().). |
1323
|
232
|
|
|
|
|
226
|
my $untainted; |
1324
|
|
|
|
|
|
|
# Need the m//ms options to match strings with newlines in them. |
1325
|
232
|
50
|
|
|
|
1512
|
if ($variable =~ /(.*)/ms) { |
1326
|
232
|
|
|
|
|
756
|
$untainted = $1; |
1327
|
|
|
|
|
|
|
} else { |
1328
|
0
|
|
|
|
|
0
|
die <
|
1329
|
|
|
|
|
|
|
App::Fetchware::Util: Untaint failed! Huh! This just shouldn't happen! It's |
1330
|
|
|
|
|
|
|
probably a bug. |
1331
|
|
|
|
|
|
|
EOD |
1332
|
|
|
|
|
|
|
} |
1333
|
|
|
|
|
|
|
|
1334
|
|
|
|
|
|
|
# Push $untainted instead of just $variable, because I want to return |
1335
|
|
|
|
|
|
|
# untatined data instead of potentially tainted data. |
1336
|
232
|
|
|
|
|
629
|
push @variables, $untainted; |
1337
|
|
|
|
|
|
|
} |
1338
|
|
|
|
|
|
|
|
1339
|
121
|
|
|
|
|
551
|
return @variables; |
1340
|
|
|
|
|
|
|
} |
1341
|
|
|
|
|
|
|
###BUGALERT### Add some pipe parsers that use Storable too. |
1342
|
|
|
|
|
|
|
|
1343
|
|
|
|
|
|
|
} # End $MAGIC_NUMBER bare block. |
1344
|
|
|
|
|
|
|
|
1345
|
|
|
|
|
|
|
|
1346
|
|
|
|
|
|
|
|
1347
|
|
|
|
|
|
|
|
1348
|
|
|
|
|
|
|
|
1349
|
|
|
|
|
|
|
|
1350
|
|
|
|
|
|
|
|
1351
|
|
|
|
|
|
|
sub do_nothing { |
1352
|
3
|
|
|
3
|
1
|
11
|
return; |
1353
|
|
|
|
|
|
|
} |
1354
|
|
|
|
|
|
|
|
1355
|
|
|
|
|
|
|
|
1356
|
|
|
|
|
|
|
|
1357
|
|
|
|
|
|
|
|
1358
|
|
|
|
|
|
|
|
1359
|
|
|
|
|
|
|
|
1360
|
|
|
|
|
|
|
{ # Begin scope block for $original_cwd. |
1361
|
|
|
|
|
|
|
|
1362
|
|
|
|
|
|
|
# $original_cwd is a scalar variable that stores fetchware's original |
1363
|
|
|
|
|
|
|
# working directory for later use if its needed. It is access with |
1364
|
|
|
|
|
|
|
# original_cwd() below. |
1365
|
|
|
|
|
|
|
my $original_cwd; |
1366
|
|
|
|
|
|
|
# $fh_sem is a semaphore lock file that create_tempdir() creates, and |
1367
|
|
|
|
|
|
|
# cleanup_tempdir() closes clearing the lock. This is used to support |
1368
|
|
|
|
|
|
|
# fetchware clean. The filehandle needs to be declared outside |
1369
|
|
|
|
|
|
|
# create_tempdir()'s scope, because when this filehandle goes out of scope |
1370
|
|
|
|
|
|
|
# the file is closed, and the lock is released, but fetchware needs to keep |
1371
|
|
|
|
|
|
|
# hold of this lock for the life of fetchware to ensure that any fetchware |
1372
|
|
|
|
|
|
|
# clean won't delete this fetchware temporary directory. |
1373
|
|
|
|
|
|
|
my $fh_sem; |
1374
|
|
|
|
|
|
|
|
1375
|
|
|
|
|
|
|
|
1376
|
|
|
|
|
|
|
###BUGALERT### Add support for the -f/--force option to force deleting fetchware |
1377
|
|
|
|
|
|
|
#temp dirs even if locked. |
1378
|
|
|
|
|
|
|
sub create_tempdir { |
1379
|
389
|
|
|
389
|
1
|
14015
|
my %opts = @_; |
1380
|
|
|
|
|
|
|
|
1381
|
389
|
|
|
|
|
973
|
msg 'Creating temp dir to use to install your package.'; |
1382
|
|
|
|
|
|
|
|
1383
|
|
|
|
|
|
|
# Ask for better security. |
1384
|
389
|
|
|
|
|
3151
|
File::Temp->safe_level( File::Temp::HIGH ); |
1385
|
|
|
|
|
|
|
|
1386
|
|
|
|
|
|
|
# Create the temp dir in the portable locations as returned by |
1387
|
|
|
|
|
|
|
# File::Spec->tempdir() using the specified template (the weird $$ is this |
1388
|
|
|
|
|
|
|
# processes process id), and cleaning up at program exit. |
1389
|
389
|
|
|
|
|
8956
|
my $exception = ''; |
1390
|
389
|
|
|
|
|
553
|
my $temp_dir; |
1391
|
389
|
100
|
|
|
|
579
|
eval { |
1392
|
389
|
|
|
|
|
522
|
local $@; |
1393
|
|
|
|
|
|
|
|
1394
|
|
|
|
|
|
|
# Determine tempdir()'s arguments. |
1395
|
389
|
|
|
|
|
1773
|
my @args = ("fetchware-$$-XXXXXXXXXX");#, TMPDIR => 1); |
1396
|
|
|
|
|
|
|
|
1397
|
|
|
|
|
|
|
# Specify the caller's TempDir (DIR) if they specify it. |
1398
|
389
|
100
|
|
|
|
1039
|
push @args, DIR => $opts{TempDir} if defined $opts{TempDir}; |
1399
|
|
|
|
|
|
|
|
1400
|
|
|
|
|
|
|
# Specify either system temp directory or user specified directory. |
1401
|
|
|
|
|
|
|
push @args, |
1402
|
389
|
100
|
|
|
|
1460
|
(defined $opts{TempDir} ? (DIR => $opts{TempDir}) : (TMPDIR => 1)); |
1403
|
|
|
|
|
|
|
|
1404
|
|
|
|
|
|
|
# Don't CLEANUP if KeepTempDir is set. |
1405
|
389
|
100
|
|
|
|
1128
|
push @args, CLEANUP => 1 if not defined $opts{KeepTempDir}; |
1406
|
|
|
|
|
|
|
|
1407
|
|
|
|
|
|
|
# Call tempdir() with the @args I've built. |
1408
|
389
|
|
|
|
|
1676
|
$temp_dir = tempdir(@args); |
1409
|
|
|
|
|
|
|
|
1410
|
|
|
|
|
|
|
# Only when we do *not* drop privs... |
1411
|
387
|
50
|
33
|
|
|
140422
|
if (config('stay_root') |
|
|
|
33
|
|
|
|
|
1412
|
|
|
|
|
|
|
or ($< != 0 or $> != 0) |
1413
|
|
|
|
|
|
|
) { |
1414
|
|
|
|
|
|
|
# ...Must chmod 700 so gpg's localized keyfiles are good. |
1415
|
0
|
0
|
|
|
|
0
|
chmod(0700, $temp_dir) or die <
|
1416
|
|
|
|
|
|
|
App-Fetchware-Util: Fetchware failed to change the permissions of its temporary |
1417
|
|
|
|
|
|
|
directory [$temp_dir] to 0700. This should not happen, and is a bug, or perhaps |
1418
|
|
|
|
|
|
|
your system's temporary directory is full. The OS error was [$!]. |
1419
|
|
|
|
|
|
|
EOD |
1420
|
|
|
|
|
|
|
} |
1421
|
|
|
|
|
|
|
|
1422
|
387
|
|
|
|
|
603
|
$exception = $@; |
1423
|
387
|
|
|
|
|
1135
|
1; # return true unless an exception is thrown. |
1424
|
|
|
|
|
|
|
} or die <
|
1425
|
|
|
|
|
|
|
App-Fetchware: run-time error. Fetchware tried to use File::Temp's tempdir() |
1426
|
|
|
|
|
|
|
subroutine to create a temporary file, but tempdir() threw an exception. That |
1427
|
|
|
|
|
|
|
exception was [$exception]. See perldoc App::Fetchware. |
1428
|
|
|
|
|
|
|
EOD |
1429
|
|
|
|
|
|
|
|
1430
|
387
|
|
|
|
|
1077776
|
$original_cwd = cwd(); |
1431
|
387
|
|
|
|
|
4529
|
vmsg "Saving original working directory as [$original_cwd]"; |
1432
|
|
|
|
|
|
|
|
1433
|
|
|
|
|
|
|
# Change directory to $CONFIG{TempDir} to make unarchiving and building happen |
1434
|
|
|
|
|
|
|
# in a temporary directory, and to allow for multiple concurrent fetchware |
1435
|
|
|
|
|
|
|
# runs at the same time. |
1436
|
387
|
50
|
|
|
|
5542
|
chdir $temp_dir or die <
|
1437
|
|
|
|
|
|
|
App-Fetchware: run-time error. Fetchware failed to change its directory to the |
1438
|
|
|
|
|
|
|
temporary directory that it successfully created. This just shouldn't happen, |
1439
|
|
|
|
|
|
|
and is weird, and may be a bug. See perldoc App::Fetchware. |
1440
|
|
|
|
|
|
|
EOD |
1441
|
387
|
|
|
|
|
1875
|
vmsg "Successfully changed working directory to [$temp_dir]."; |
1442
|
|
|
|
|
|
|
|
1443
|
|
|
|
|
|
|
# Create 'fetcwhare.sem' - the fetchware semaphore lock file. |
1444
|
387
|
50
|
|
|
|
26554
|
open $fh_sem, '>', 'fetchware.sem' or die <
|
1445
|
|
|
|
|
|
|
App-Fetchware-Util: Failed to create [fetchware.sem] semaphore lock file! This |
1446
|
|
|
|
|
|
|
should not happen, because fetchware is creating this file in a brand new |
1447
|
|
|
|
|
|
|
directory that only fetchware should be accessing. You simply shouldn't see this |
1448
|
|
|
|
|
|
|
error unless some one is messing with fetchware, or perphaps there actually is a |
1449
|
|
|
|
|
|
|
bug? I don't know, but this just shouldn't happen. It's so hard to trigger it to |
1450
|
|
|
|
|
|
|
happen, it can't easily be tested in fetchware's test suite. OS error [$!]. |
1451
|
|
|
|
|
|
|
EOD |
1452
|
387
|
|
|
|
|
1021
|
vmsg "Successfully created [fetchware.sem] semaphore lock file."; |
1453
|
|
|
|
|
|
|
# Now flock 'fetchware.sem.' This should |
1454
|
|
|
|
|
|
|
# Use LOCK_NB so flock won't stupidly wait forever and ever until the lock |
1455
|
|
|
|
|
|
|
# becomes available. |
1456
|
387
|
50
|
|
|
|
3177
|
flock $fh_sem, LOCK_EX | LOCK_NB or die <
|
1457
|
|
|
|
|
|
|
App-Fetchware-Util: Failed to flock [fetchware.sem] semaphore lock file! This |
1458
|
|
|
|
|
|
|
should not happen, because this is being done in a brand new temporary directory |
1459
|
|
|
|
|
|
|
that only this instance of fetchware cares about. This just shouldn't happen. OS |
1460
|
|
|
|
|
|
|
error [$!]. |
1461
|
|
|
|
|
|
|
EOD |
1462
|
387
|
|
|
|
|
903
|
vmsg "Successfully locked [fetchware.sem] semaphore lock file using flock."; |
1463
|
|
|
|
|
|
|
|
1464
|
387
|
|
|
|
|
2256
|
msg "Temporary directory created [$temp_dir]"; |
1465
|
|
|
|
|
|
|
|
1466
|
387
|
|
|
|
|
4533
|
return $temp_dir; |
1467
|
|
|
|
|
|
|
} |
1468
|
|
|
|
|
|
|
|
1469
|
|
|
|
|
|
|
|
1470
|
|
|
|
|
|
|
|
1471
|
|
|
|
|
|
|
sub original_cwd { |
1472
|
1131
|
|
|
1131
|
1
|
17143
|
return $original_cwd; |
1473
|
|
|
|
|
|
|
} |
1474
|
|
|
|
|
|
|
|
1475
|
|
|
|
|
|
|
|
1476
|
|
|
|
|
|
|
|
1477
|
|
|
|
|
|
|
sub cleanup_tempdir { |
1478
|
373
|
|
|
373
|
1
|
2516
|
msg 'Cleaning up temporary directory temporary directory.'; |
1479
|
|
|
|
|
|
|
|
1480
|
|
|
|
|
|
|
# Close and unlock the fetchware semaphore lock file, 'fetchware.sem.' |
1481
|
373
|
50
|
|
|
|
1373
|
if (defined $fh_sem) { |
1482
|
373
|
50
|
|
|
|
4323
|
close $fh_sem or die <
|
1483
|
|
|
|
|
|
|
App-Fetchware-Util: Huh? close() failed! Fetchware failed to close(\$fh_sem). |
1484
|
|
|
|
|
|
|
Perhaps some one or something deleted it under us? Maybe a fetchware clean was |
1485
|
|
|
|
|
|
|
run with the force flag (--force) while this other fetchware was running? |
1486
|
|
|
|
|
|
|
OS error [$!]. |
1487
|
|
|
|
|
|
|
EOD |
1488
|
373
|
|
|
|
|
961
|
vmsg <
|
1489
|
|
|
|
|
|
|
Closed [fetchware.sem] filehandle to unlock this fetchware temporary directory from any |
1490
|
|
|
|
|
|
|
fetchware clean runs. |
1491
|
|
|
|
|
|
|
EOM |
1492
|
|
|
|
|
|
|
} |
1493
|
|
|
|
|
|
|
|
1494
|
|
|
|
|
|
|
# chdir to original_cwd() directory, so File::Temp can delete the tempdir. |
1495
|
|
|
|
|
|
|
# This is necessary, because operating systems do not allow you to delete a |
1496
|
|
|
|
|
|
|
# directory that a running program has as its cwd. |
1497
|
373
|
50
|
|
|
|
1332
|
if (defined(original_cwd())) { |
1498
|
373
|
|
|
|
|
646
|
vmsg "Changing directory to [@{[original_cwd()]}]."; |
|
373
|
|
|
|
|
727
|
|
1499
|
373
|
50
|
|
|
|
1430
|
chdir(original_cwd()) or die <
|
1500
|
|
|
|
|
|
|
App-Fetchware: run-time error. Fetchware failed to chdir() to |
1501
|
0
|
|
|
|
|
0
|
[@{[original_cwd()]}]. See perldoc App::Fetchware. |
1502
|
|
|
|
|
|
|
EOD |
1503
|
|
|
|
|
|
|
} |
1504
|
|
|
|
|
|
|
|
1505
|
|
|
|
|
|
|
# cleanup_tempdir() used to actually delete the temporary directory by using |
1506
|
|
|
|
|
|
|
# File::Temp's cleanup() subroutine, but that subroutine deletes *all* |
1507
|
|
|
|
|
|
|
# temporary directories that File::Temp has created and marked for deletion, |
1508
|
|
|
|
|
|
|
# which might include directories created before this call to |
1509
|
|
|
|
|
|
|
# cleanup_tempdir(), but are needed after. Therefore, cleanup_tempdir() no |
1510
|
|
|
|
|
|
|
# longer actually deletes anything; instead, File::Temp can do it in its END |
1511
|
|
|
|
|
|
|
# handler. |
1512
|
|
|
|
|
|
|
# |
1513
|
|
|
|
|
|
|
# The code below is left here on purpose, to remind everyone *not* to call |
1514
|
|
|
|
|
|
|
# File::Temp's cleanup() here!! Do not do it! |
1515
|
|
|
|
|
|
|
###DONOTDO#### Call File::Temp's cleanup subrouttine to delete fetchware's temp |
1516
|
|
|
|
|
|
|
###DONOTDO#### directory. |
1517
|
|
|
|
|
|
|
###DONOTDO###vmsg 'Cleaning up temporary directory.'; |
1518
|
|
|
|
|
|
|
###DONOTDO###File::Temp::cleanup(); |
1519
|
|
|
|
|
|
|
|
1520
|
373
|
|
|
|
|
735
|
vmsg "Leaving tempdir alone. File::Temp's END handler will delete it."; |
1521
|
|
|
|
|
|
|
|
1522
|
373
|
|
|
|
|
1072
|
vmsg 'Clearing internal %CONFIG variable that hold your parsed Fetchwarefile.'; |
1523
|
373
|
|
|
|
|
2590
|
__clear_CONFIG(); |
1524
|
|
|
|
|
|
|
|
1525
|
373
|
|
|
|
|
800
|
msg 'Cleaned up temporary directory.'; |
1526
|
|
|
|
|
|
|
|
1527
|
|
|
|
|
|
|
# Return true. |
1528
|
373
|
|
|
|
|
1081
|
return 'Cleaned up tempdir'; |
1529
|
|
|
|
|
|
|
} |
1530
|
|
|
|
|
|
|
|
1531
|
|
|
|
|
|
|
|
1532
|
|
|
|
|
|
|
} # End scope block for $original_cwd and $fh_sem. |
1533
|
|
|
|
|
|
|
|
1534
|
|
|
|
|
|
|
1; |
1535
|
|
|
|
|
|
|
|
1536
|
|
|
|
|
|
|
=pod |
1537
|
|
|
|
|
|
|
|
1538
|
|
|
|
|
|
|
=head1 NAME |
1539
|
|
|
|
|
|
|
|
1540
|
|
|
|
|
|
|
App::Fetchware::Util - Miscelaneous functions for App::Fetchware. |
1541
|
|
|
|
|
|
|
|
1542
|
|
|
|
|
|
|
=head1 VERSION |
1543
|
|
|
|
|
|
|
|
1544
|
|
|
|
|
|
|
version 1.016 |
1545
|
|
|
|
|
|
|
|
1546
|
|
|
|
|
|
|
=head1 SYNOPSIS |
1547
|
|
|
|
|
|
|
|
1548
|
|
|
|
|
|
|
use App::Fetchware::Util ':UTIL'; |
1549
|
|
|
|
|
|
|
|
1550
|
|
|
|
|
|
|
|
1551
|
|
|
|
|
|
|
# Logging subroutines. |
1552
|
|
|
|
|
|
|
msg 'message to print to STDOUT'; |
1553
|
|
|
|
|
|
|
|
1554
|
|
|
|
|
|
|
vmsg 'message to print to STDOUT'; |
1555
|
|
|
|
|
|
|
|
1556
|
|
|
|
|
|
|
|
1557
|
|
|
|
|
|
|
# Run external command subroutine. |
1558
|
|
|
|
|
|
|
run_prog($program, @args); |
1559
|
|
|
|
|
|
|
|
1560
|
|
|
|
|
|
|
|
1561
|
|
|
|
|
|
|
# Download subroutines. |
1562
|
|
|
|
|
|
|
my $dir_list = download_dirlist($ftp_or_http_url) |
1563
|
|
|
|
|
|
|
|
1564
|
|
|
|
|
|
|
my $dir_list = ftp_download_dirlist($ftp_url); |
1565
|
|
|
|
|
|
|
|
1566
|
|
|
|
|
|
|
my $dir_list = http_download_dirlist($http_url); |
1567
|
|
|
|
|
|
|
|
1568
|
|
|
|
|
|
|
|
1569
|
|
|
|
|
|
|
my $filename = download_file($url) |
1570
|
|
|
|
|
|
|
|
1571
|
|
|
|
|
|
|
my $filename = download_ftp_url($url); |
1572
|
|
|
|
|
|
|
|
1573
|
|
|
|
|
|
|
my $filename = download_http_url($url); |
1574
|
|
|
|
|
|
|
|
1575
|
|
|
|
|
|
|
my $filename = download_file_url($url); |
1576
|
|
|
|
|
|
|
|
1577
|
|
|
|
|
|
|
|
1578
|
|
|
|
|
|
|
# Miscelaneous subroutines. |
1579
|
|
|
|
|
|
|
just_filename() |
1580
|
|
|
|
|
|
|
|
1581
|
|
|
|
|
|
|
do_nothing(); |
1582
|
|
|
|
|
|
|
|
1583
|
|
|
|
|
|
|
|
1584
|
|
|
|
|
|
|
# Temporary directory subroutines. |
1585
|
|
|
|
|
|
|
my $temp_dir = create_tempdir(); |
1586
|
|
|
|
|
|
|
|
1587
|
|
|
|
|
|
|
my $original_cwd = original_cwd(); |
1588
|
|
|
|
|
|
|
|
1589
|
|
|
|
|
|
|
cleanup_tempdir(); |
1590
|
|
|
|
|
|
|
|
1591
|
|
|
|
|
|
|
=head1 DESCRIPTION |
1592
|
|
|
|
|
|
|
|
1593
|
|
|
|
|
|
|
App::Fetchware::Util holds miscelaneous utilities that fetchware needs for |
1594
|
|
|
|
|
|
|
various purposes such as logging and controling executed processes based on -q |
1595
|
|
|
|
|
|
|
or -v switches (msg(), vmsg(), run_prog()), subroutines for downloading |
1596
|
|
|
|
|
|
|
directory listings (*_dirlist()) or files (download_*()) using ftp, http, or |
1597
|
|
|
|
|
|
|
local files (file://), do_nothing() for extensions to fetchware, and subroutines |
1598
|
|
|
|
|
|
|
for managing a temporary directory. |
1599
|
|
|
|
|
|
|
|
1600
|
|
|
|
|
|
|
=head1 LOGGING SUBROUTINES |
1601
|
|
|
|
|
|
|
|
1602
|
|
|
|
|
|
|
These subroutines' log messages generated by fetchware by printing them to |
1603
|
|
|
|
|
|
|
C. They do not currently support logging to a file directly, but you |
1604
|
|
|
|
|
|
|
could redirect fetchware's standard output to a file using your shell if you |
1605
|
|
|
|
|
|
|
want to: |
1606
|
|
|
|
|
|
|
|
1607
|
|
|
|
|
|
|
fetchware any arguments > fetchware.log |
1608
|
|
|
|
|
|
|
fetchware upgrade-all > fetchware.log |
1609
|
|
|
|
|
|
|
|
1610
|
|
|
|
|
|
|
=head2 Standards for using msg() and vmsg() |
1611
|
|
|
|
|
|
|
|
1612
|
|
|
|
|
|
|
msg() should be used to describe the main events that happen, while vmsg() |
1613
|
|
|
|
|
|
|
should be used to describe what all of the main subroutine calls do. |
1614
|
|
|
|
|
|
|
|
1615
|
|
|
|
|
|
|
For example, cmd_uninstall() has a msg() at the beginning and at the end, and so |
1616
|
|
|
|
|
|
|
do the main App::Fetchware subroutines that it uses such as start(), download(), |
1617
|
|
|
|
|
|
|
unarchive(), end() and so on. They both use vmsg() to add more detailed messages |
1618
|
|
|
|
|
|
|
about the particular even "internal" things they do. |
1619
|
|
|
|
|
|
|
|
1620
|
|
|
|
|
|
|
msg() and vmsg() are also used without parens due to their appropriate |
1621
|
|
|
|
|
|
|
prototypes. This makes them stand out from regular old subroutine calls more. |
1622
|
|
|
|
|
|
|
|
1623
|
|
|
|
|
|
|
=head2 msg() |
1624
|
|
|
|
|
|
|
|
1625
|
|
|
|
|
|
|
msg 'message to print to STDOUT' ; |
1626
|
|
|
|
|
|
|
msg('message to print to STDOUT'); |
1627
|
|
|
|
|
|
|
|
1628
|
|
|
|
|
|
|
msg() simply takes a list of scalars, and it prints them to STDOUT according to |
1629
|
|
|
|
|
|
|
any verbose (-v), or quiet (-q) options that the user may have provided to |
1630
|
|
|
|
|
|
|
fetchware. |
1631
|
|
|
|
|
|
|
|
1632
|
|
|
|
|
|
|
msg() will still print its arguments if the user provided a -v (verbose) |
1633
|
|
|
|
|
|
|
argument, but it will B print its argument if the user provided a -q (quiet) |
1634
|
|
|
|
|
|
|
command line option. |
1635
|
|
|
|
|
|
|
|
1636
|
|
|
|
|
|
|
=over |
1637
|
|
|
|
|
|
|
=item This subroutine makes use of prototypes, so that you can avoid using parentheses around its args to make it stand out more in code. |
1638
|
|
|
|
|
|
|
|
1639
|
|
|
|
|
|
|
=back |
1640
|
|
|
|
|
|
|
|
1641
|
|
|
|
|
|
|
=head2 vmsg() |
1642
|
|
|
|
|
|
|
|
1643
|
|
|
|
|
|
|
vmsg 'message to print to STDOUT' ; |
1644
|
|
|
|
|
|
|
vmsg('message to print to STDOUT'); |
1645
|
|
|
|
|
|
|
|
1646
|
|
|
|
|
|
|
vmsg() simply takes a list of scalars, and it prints them to STDOUT according to |
1647
|
|
|
|
|
|
|
any verbose (-v), or quiet (-q) options that the user may have provided to |
1648
|
|
|
|
|
|
|
fetchware. |
1649
|
|
|
|
|
|
|
|
1650
|
|
|
|
|
|
|
vmsg() will B print its arguments if the user provided a -v (verbose) |
1651
|
|
|
|
|
|
|
argument, but it will B print its argument if the user provided a -q (quiet) |
1652
|
|
|
|
|
|
|
command line option. |
1653
|
|
|
|
|
|
|
|
1654
|
|
|
|
|
|
|
=over |
1655
|
|
|
|
|
|
|
=item This subroutine makes use of prototypes, so that you can avoid using parentheses around its args to make it stand out more in code. |
1656
|
|
|
|
|
|
|
|
1657
|
|
|
|
|
|
|
=back |
1658
|
|
|
|
|
|
|
|
1659
|
|
|
|
|
|
|
=head1 EXTERNAL COMMAND SUBROUTINES |
1660
|
|
|
|
|
|
|
|
1661
|
|
|
|
|
|
|
run_prog() should be the B function you use to execute external commands |
1662
|
|
|
|
|
|
|
when you L, or L, |
1663
|
|
|
|
|
|
|
because run_prog() properly checks if the user specified the quiet switch |
1664
|
|
|
|
|
|
|
(C<-q>), and disables external commands from printing to C if it has |
1665
|
|
|
|
|
|
|
been enabled. |
1666
|
|
|
|
|
|
|
|
1667
|
|
|
|
|
|
|
=head2 run_prog() |
1668
|
|
|
|
|
|
|
|
1669
|
|
|
|
|
|
|
run_prog($program, @args); |
1670
|
|
|
|
|
|
|
|
1671
|
|
|
|
|
|
|
# Or let run_prog() deal with splitting the $command into multiple pieces. |
1672
|
|
|
|
|
|
|
run_prig($command); |
1673
|
|
|
|
|
|
|
|
1674
|
|
|
|
|
|
|
run_prog() uses L to execute the program for you. Only the secure way of |
1675
|
|
|
|
|
|
|
avoiding the shell is used, so you can not use any shell redirection or any |
1676
|
|
|
|
|
|
|
shell builtins. |
1677
|
|
|
|
|
|
|
|
1678
|
|
|
|
|
|
|
If the user ran fetchware with -v (verbose) then run_prog() changes none of its |
1679
|
|
|
|
|
|
|
behavior it still just executes the program. However, if the user runs the |
1680
|
|
|
|
|
|
|
program with -q (quiet) specified, then the the command is run using a piped |
1681
|
|
|
|
|
|
|
open to capture the output of the program. This captured output is then ignored, |
1682
|
|
|
|
|
|
|
because the user asked to never be bothered with the output. This piped open |
1683
|
|
|
|
|
|
|
uses the safer shell avoiding syntax on systems with L, and systems |
1684
|
|
|
|
|
|
|
without L, Windows, the older less safe syntax is used. Backticks are |
1685
|
|
|
|
|
|
|
avoided, because they always use the shell. |
1686
|
|
|
|
|
|
|
|
1687
|
|
|
|
|
|
|
run_prog() when called with only one argument will split that one argument into |
1688
|
|
|
|
|
|
|
multiple pieces using L quotewords() subroutine, which |
1689
|
|
|
|
|
|
|
properly deals with quotes just like the shell does. quotewords() is always used |
1690
|
|
|
|
|
|
|
even if you provide an already split up list of arguments to run_prog(). |
1691
|
|
|
|
|
|
|
|
1692
|
|
|
|
|
|
|
=head2 Executing external commands without using run_prog() |
1693
|
|
|
|
|
|
|
|
1694
|
|
|
|
|
|
|
msg(), vmsg(), and run_prog() determine if -v and if -q were specified by |
1695
|
|
|
|
|
|
|
checking the values of the global variables listed below: |
1696
|
|
|
|
|
|
|
|
1697
|
|
|
|
|
|
|
=over |
1698
|
|
|
|
|
|
|
|
1699
|
|
|
|
|
|
|
=item * $fetchware::quiet - is C<0> if -q was B specified. |
1700
|
|
|
|
|
|
|
|
1701
|
|
|
|
|
|
|
=item * $fetchware::verbose - is C<0> if -v was B specified. |
1702
|
|
|
|
|
|
|
|
1703
|
|
|
|
|
|
|
=back |
1704
|
|
|
|
|
|
|
|
1705
|
|
|
|
|
|
|
Both of these variables work the same way. If they are 0, then -q or -v was |
1706
|
|
|
|
|
|
|
B specified. And if they are defined and greather than (>) 0, then -q or -v |
1707
|
|
|
|
|
|
|
were specified on the command line. You should test for greater than 0 B |
1708
|
|
|
|
|
|
|
B<== 1>, because Fetchware takes advantage of a cool feature in GetOpt::Long |
1709
|
|
|
|
|
|
|
allowing the user to specify -v and -q more than once. This triggers either |
1710
|
|
|
|
|
|
|
$fetchware::quiet or $fetchware::verbose to be greater than one, which would |
1711
|
|
|
|
|
|
|
cause a direct C<== 1> test to fail even though the user is no asking for |
1712
|
|
|
|
|
|
|
I verbose messages. Internally Fetchware only supports one verbositly |
1713
|
|
|
|
|
|
|
level. |
1714
|
|
|
|
|
|
|
|
1715
|
|
|
|
|
|
|
=head1 DOWNLOAD SUBROUTINES |
1716
|
|
|
|
|
|
|
|
1717
|
|
|
|
|
|
|
App::Fetchware::Util's download_*() and *_dirlist() subroutines allow you to |
1718
|
|
|
|
|
|
|
download FTP, HTTP, or local file (file://) directory listings or files |
1719
|
|
|
|
|
|
|
respectively. |
1720
|
|
|
|
|
|
|
|
1721
|
|
|
|
|
|
|
=over |
1722
|
|
|
|
|
|
|
=item NOTICE |
1723
|
|
|
|
|
|
|
Each *_dirlist() subroutine returns its own format that is different from the |
1724
|
|
|
|
|
|
|
others. Fetchware uses the *_parse_filelist() subroutines to parse this |
1725
|
|
|
|
|
|
|
differing directory listings into a specifc format of an array of arrays of |
1726
|
|
|
|
|
|
|
filenames and timestamps. You could load these subroutines from the |
1727
|
|
|
|
|
|
|
C App::Fetchware export tag to use in your Fetchwarefile or |
1728
|
|
|
|
|
|
|
your fetchware extension. |
1729
|
|
|
|
|
|
|
|
1730
|
|
|
|
|
|
|
=back |
1731
|
|
|
|
|
|
|
|
1732
|
|
|
|
|
|
|
=head2 download_dirlist() |
1733
|
|
|
|
|
|
|
|
1734
|
|
|
|
|
|
|
my $dir_list = download_dirlist($url) |
1735
|
|
|
|
|
|
|
|
1736
|
|
|
|
|
|
|
my $dir_list = download_dirlist(PATH => $path) |
1737
|
|
|
|
|
|
|
|
1738
|
|
|
|
|
|
|
Can be called with either a $url or a PATH parameter. When called with a $url |
1739
|
|
|
|
|
|
|
parameter, the specified $url is downloaded using no_mirror_download_dirlist(), |
1740
|
|
|
|
|
|
|
and returned if successful. If it fails then each C the user specified |
1741
|
|
|
|
|
|
|
is also tried unitl there are no more mirrors, and then an exception is thrown. |
1742
|
|
|
|
|
|
|
|
1743
|
|
|
|
|
|
|
If you specify a PATH parameter instead of a $url parameter, then that path is |
1744
|
|
|
|
|
|
|
appended to each C, and the resultant url is downloaded using |
1745
|
|
|
|
|
|
|
no_mirror_download_dirlist(). |
1746
|
|
|
|
|
|
|
|
1747
|
|
|
|
|
|
|
=head2 no_mirror_download_dirlist() |
1748
|
|
|
|
|
|
|
|
1749
|
|
|
|
|
|
|
my $dir_list = no_mirror_download_dirlist($ftp_or_http_url) |
1750
|
|
|
|
|
|
|
|
1751
|
|
|
|
|
|
|
Downloads a ftp or http url and assumes that it will be downloading a directory |
1752
|
|
|
|
|
|
|
listing instead of an actual file. To download an actual file use |
1753
|
|
|
|
|
|
|
L. download_dirlist returns the directory listing that it |
1754
|
|
|
|
|
|
|
obtained from the ftp or http server. ftp server will be an arrayref of C |
1755
|
|
|
|
|
|
|
like output, while the http output will be a scalar of the HTML dirlisting |
1756
|
|
|
|
|
|
|
provided by the http server. |
1757
|
|
|
|
|
|
|
|
1758
|
|
|
|
|
|
|
=head2 ftp_download_dirlist() |
1759
|
|
|
|
|
|
|
|
1760
|
|
|
|
|
|
|
my $dir_list = ftp_download_dirlist($ftp_url); |
1761
|
|
|
|
|
|
|
|
1762
|
|
|
|
|
|
|
Uses Net::Ftp's dir() method to obtain a I directory listing. lookup() |
1763
|
|
|
|
|
|
|
needs it in I format, so that the timestamp algorithm has access to each |
1764
|
|
|
|
|
|
|
file's timestamp. |
1765
|
|
|
|
|
|
|
|
1766
|
|
|
|
|
|
|
Returns an array ref of the directory listing. |
1767
|
|
|
|
|
|
|
|
1768
|
|
|
|
|
|
|
=head2 http_download_dirlist() |
1769
|
|
|
|
|
|
|
|
1770
|
|
|
|
|
|
|
my $dir_list = http_download_dirlist($http_url); |
1771
|
|
|
|
|
|
|
|
1772
|
|
|
|
|
|
|
Uses HTTP::Tiny to download a HTML directory listing from a HTTP Web server. |
1773
|
|
|
|
|
|
|
|
1774
|
|
|
|
|
|
|
Returns an scalar of the HTML ladden directory listing. |
1775
|
|
|
|
|
|
|
|
1776
|
|
|
|
|
|
|
If an even number of other options are specified (a faux hash), then those |
1777
|
|
|
|
|
|
|
options are forwarded on to L's new() method. See L for |
1778
|
|
|
|
|
|
|
details about what these options are. For example, you couse use this to add a |
1779
|
|
|
|
|
|
|
C header to your request if a download site annoying checks referrers. |
1780
|
|
|
|
|
|
|
|
1781
|
|
|
|
|
|
|
=head2 file_download_dirlist() |
1782
|
|
|
|
|
|
|
|
1783
|
|
|
|
|
|
|
my $file_listing = file_download_dirlist($local_lookup_url) |
1784
|
|
|
|
|
|
|
|
1785
|
|
|
|
|
|
|
Glob's provided $local_lookup_url, and builds a directory listing of all files |
1786
|
|
|
|
|
|
|
in the provided directory. |
1787
|
|
|
|
|
|
|
|
1788
|
|
|
|
|
|
|
=head2 download_file() |
1789
|
|
|
|
|
|
|
|
1790
|
|
|
|
|
|
|
my $filename = download_file($url) |
1791
|
|
|
|
|
|
|
|
1792
|
|
|
|
|
|
|
my $filename = download_file(PATH => $path) |
1793
|
|
|
|
|
|
|
|
1794
|
|
|
|
|
|
|
Can be called with either a $url or a PATH parameter. When called with a $url |
1795
|
|
|
|
|
|
|
parameter, the specified $url is downloaded using no_mirror_download_file(), |
1796
|
|
|
|
|
|
|
and returned if successful. If it fails then each C the user specified |
1797
|
|
|
|
|
|
|
is also tried unitl there are no more mirrors, and then an exception is thrown. |
1798
|
|
|
|
|
|
|
|
1799
|
|
|
|
|
|
|
If you specify a PATH parameter instead of a $url parameter, then that path is |
1800
|
|
|
|
|
|
|
appended to each C, and the resultant url is downloaded using |
1801
|
|
|
|
|
|
|
no_mirror_download_file(). |
1802
|
|
|
|
|
|
|
|
1803
|
|
|
|
|
|
|
=head2 no_mirror_download_file() |
1804
|
|
|
|
|
|
|
|
1805
|
|
|
|
|
|
|
my $filename = no_mirror_download_file($url) |
1806
|
|
|
|
|
|
|
|
1807
|
|
|
|
|
|
|
Downloads one $url and assumes it is a file that will be downloaded instead of a |
1808
|
|
|
|
|
|
|
file listing that will be returned. no_mirror_download_file() returns the file |
1809
|
|
|
|
|
|
|
name of the file it downloads. |
1810
|
|
|
|
|
|
|
|
1811
|
|
|
|
|
|
|
Like its name says it does not try any configured mirrors at all. This |
1812
|
|
|
|
|
|
|
subroutine should not be used; instead download_file() should be used, because |
1813
|
|
|
|
|
|
|
you should respect your user's desired mirrors. |
1814
|
|
|
|
|
|
|
|
1815
|
|
|
|
|
|
|
=head2 download_ftp_url() |
1816
|
|
|
|
|
|
|
|
1817
|
|
|
|
|
|
|
my $filename = download_ftp_url($url); |
1818
|
|
|
|
|
|
|
|
1819
|
|
|
|
|
|
|
Uses Net::FTP to download the specified FTP URL using binary mode. |
1820
|
|
|
|
|
|
|
|
1821
|
|
|
|
|
|
|
=head2 download_http_url() |
1822
|
|
|
|
|
|
|
|
1823
|
|
|
|
|
|
|
my $filename = download_http_url($url); |
1824
|
|
|
|
|
|
|
|
1825
|
|
|
|
|
|
|
Uses HTTP::Tiny to download the specified HTTP URL. |
1826
|
|
|
|
|
|
|
|
1827
|
|
|
|
|
|
|
Supports adding extra arguments to HTTP::Tiny's new() constructor. These |
1828
|
|
|
|
|
|
|
arguments are B checked for correctness; instead, they are simply forwarded |
1829
|
|
|
|
|
|
|
to HTTP::Tiny, which does not check them for correctness either. HTTP::Tiny |
1830
|
|
|
|
|
|
|
simply loops over its internal listing of what is arguments should be, and then |
1831
|
|
|
|
|
|
|
accesses the arguments if they exist. |
1832
|
|
|
|
|
|
|
|
1833
|
|
|
|
|
|
|
This was really only implemented to allow App::FetchwareX::HTMLPageSync to change |
1834
|
|
|
|
|
|
|
its user agent string to avoid being blocked or freaking out Web developers that |
1835
|
|
|
|
|
|
|
they're being screen scraped by some obnoxious bot as HTMLPageSync is wimpy and |
1836
|
|
|
|
|
|
|
harmless, and only downloads one page. |
1837
|
|
|
|
|
|
|
|
1838
|
|
|
|
|
|
|
You would add an argument like this: |
1839
|
|
|
|
|
|
|
download_http_url($http_url, agent => 'Firefox'); |
1840
|
|
|
|
|
|
|
|
1841
|
|
|
|
|
|
|
See HTTP::Tiny's documentation for what these options are. |
1842
|
|
|
|
|
|
|
|
1843
|
|
|
|
|
|
|
=head2 download_file_url() |
1844
|
|
|
|
|
|
|
|
1845
|
|
|
|
|
|
|
my $filename = download_file_url($url); |
1846
|
|
|
|
|
|
|
|
1847
|
|
|
|
|
|
|
Uses File::Copy to copy ("download") the local file to the current working |
1848
|
|
|
|
|
|
|
directory. |
1849
|
|
|
|
|
|
|
|
1850
|
|
|
|
|
|
|
=head1 TEMPDIR SUBROUTINES |
1851
|
|
|
|
|
|
|
|
1852
|
|
|
|
|
|
|
These subroutines manage the creation of a temporary directory for you. They |
1853
|
|
|
|
|
|
|
also implement the original_cwd() getter subroutine that returns the current |
1854
|
|
|
|
|
|
|
working directory fetchware was at before create_tempdir() chdir()'d to the |
1855
|
|
|
|
|
|
|
temporary directory you specify. File::Temp's tempdir() is used, and |
1856
|
|
|
|
|
|
|
cleanup_tempdir() manages the C fetchware semaphore file. |
1857
|
|
|
|
|
|
|
|
1858
|
|
|
|
|
|
|
=over |
1859
|
|
|
|
|
|
|
=item NOTICE |
1860
|
|
|
|
|
|
|
App::Fetchware::Util's temporary directory creation utilities, create_tempdir(), |
1861
|
|
|
|
|
|
|
original_cwd(), and cleanup_tempdir(), only keep track of one tempdir at a time. If |
1862
|
|
|
|
|
|
|
you create another tempdir with create_tempdir() it will override the value of |
1863
|
|
|
|
|
|
|
original_cwd(), which may mess up other functions that call create_tempdir(), |
1864
|
|
|
|
|
|
|
original_cwd(), and cleanup_tempdir(). Therefore, becareful when you call these |
1865
|
|
|
|
|
|
|
functions, and do B use them inside a fetchware extension if you reuse |
1866
|
|
|
|
|
|
|
App::Fetchware's start() and end(), because App::Fetchware's start() and end() |
1867
|
|
|
|
|
|
|
use these functions, so your use of them will conflict. If you still need to |
1868
|
|
|
|
|
|
|
create a tempdir just call File::Temp's tempdir() directly. |
1869
|
|
|
|
|
|
|
|
1870
|
|
|
|
|
|
|
=back |
1871
|
|
|
|
|
|
|
|
1872
|
|
|
|
|
|
|
=head2 create_tempdir() |
1873
|
|
|
|
|
|
|
|
1874
|
|
|
|
|
|
|
my $temp_dir = create_tempdir(); |
1875
|
|
|
|
|
|
|
|
1876
|
|
|
|
|
|
|
Creates a temporary directory, chmod 700's it, and chdir()'s into it. |
1877
|
|
|
|
|
|
|
|
1878
|
|
|
|
|
|
|
Accepts the fake hash argument C 1>, which tells create_tempdir() |
1879
|
|
|
|
|
|
|
to B delete the temporary directory when the program exits. |
1880
|
|
|
|
|
|
|
|
1881
|
|
|
|
|
|
|
Also, accepts C '/tmp'> to specify what temporary directory to |
1882
|
|
|
|
|
|
|
use. The default with out this argument is to use tempdir()'s default, which is |
1883
|
|
|
|
|
|
|
whatever File::Spec's tmpdir() says to use. |
1884
|
|
|
|
|
|
|
|
1885
|
|
|
|
|
|
|
The C 1> option causes create_tempdir() to B chown to |
1886
|
|
|
|
|
|
|
config('user'). |
1887
|
|
|
|
|
|
|
|
1888
|
|
|
|
|
|
|
=head3 Locking Fetchware's temp directories with a semaphore file. |
1889
|
|
|
|
|
|
|
|
1890
|
|
|
|
|
|
|
In order to support C, create_tempdir() creates a semaphore |
1891
|
|
|
|
|
|
|
file. The file is used by C (via bin/fetchware's cmd_clean()) |
1892
|
|
|
|
|
|
|
to determine if another fetchware process out there is currently using this |
1893
|
|
|
|
|
|
|
temporary directory, and if it is not, the file is not currently locked with |
1894
|
|
|
|
|
|
|
flock, then the entire directory is deleted using File::Path's remove_path() |
1895
|
|
|
|
|
|
|
function. If the file is there and locked, then the directory is skipped by |
1896
|
|
|
|
|
|
|
cmd_clean(). |
1897
|
|
|
|
|
|
|
|
1898
|
|
|
|
|
|
|
cleanup_tempdir() is responsible for unlocking the semaphore file that |
1899
|
|
|
|
|
|
|
create_tempdir() creates. However, the coolest part of using flock is that if |
1900
|
|
|
|
|
|
|
fetchware is killed in any manner whether its C block or File::Temp's |
1901
|
|
|
|
|
|
|
Cblock run, the OS will still unlock the file, so no edge cases need |
1902
|
|
|
|
|
|
|
handling, because the OS will do them for us! |
1903
|
|
|
|
|
|
|
|
1904
|
|
|
|
|
|
|
=head2 original_cwd() |
1905
|
|
|
|
|
|
|
|
1906
|
|
|
|
|
|
|
my $original_cwd = original_cwd(); |
1907
|
|
|
|
|
|
|
|
1908
|
|
|
|
|
|
|
original_cwd() simply returns the value of fetchware's $original_cwd that is |
1909
|
|
|
|
|
|
|
saved inside each create_tempdir() call. A new call to create_tempdir() will |
1910
|
|
|
|
|
|
|
reset this value. Note: App::Fetchware's start() also calls create_tempdir(), so |
1911
|
|
|
|
|
|
|
another call to start() will also reset original_cwd(). |
1912
|
|
|
|
|
|
|
|
1913
|
|
|
|
|
|
|
=head2 cleanup_tempdir() |
1914
|
|
|
|
|
|
|
|
1915
|
|
|
|
|
|
|
cleanup_tempdir(); |
1916
|
|
|
|
|
|
|
|
1917
|
|
|
|
|
|
|
Cleans up B temporary files or directories that anything in this process used |
1918
|
|
|
|
|
|
|
File::Temp to create. You cannot only clean up one directory or another; |
1919
|
|
|
|
|
|
|
instead, you must just use this sparingly or in an END block although file::Temp |
1920
|
|
|
|
|
|
|
takes care of that for you unless you asked it not to. |
1921
|
|
|
|
|
|
|
|
1922
|
|
|
|
|
|
|
It also closes $fh_sem, which is the filehandle of the 'fetchware.sem' file |
1923
|
|
|
|
|
|
|
create_tempdir() opens and I. By closing it in cleanup_tempdir(), we're |
1924
|
|
|
|
|
|
|
unlocking it. According to MJD's "File Locking Tips and Traps," it's better to |
1925
|
|
|
|
|
|
|
just close the file, then use flock to unlock it. |
1926
|
|
|
|
|
|
|
|
1927
|
|
|
|
|
|
|
=head1 SECURITY SUBROUTINES |
1928
|
|
|
|
|
|
|
|
1929
|
|
|
|
|
|
|
This section describes Utilty subroutines that can be used for checking security |
1930
|
|
|
|
|
|
|
of files on the file system to see if fetchware should open and use them. |
1931
|
|
|
|
|
|
|
|
1932
|
|
|
|
|
|
|
=head2 safe_open() |
1933
|
|
|
|
|
|
|
|
1934
|
|
|
|
|
|
|
my $fh = safe_open($file_to_check, <
|
1935
|
|
|
|
|
|
|
App-Fetchware-Extension???: Failed to open file [$file_to_check]! Because of |
1936
|
|
|
|
|
|
|
OS error [$!]. |
1937
|
|
|
|
|
|
|
EOE |
1938
|
|
|
|
|
|
|
|
1939
|
|
|
|
|
|
|
# To open for writing instead of reading |
1940
|
|
|
|
|
|
|
my $fh = safe_open($file_to_check, < '>'); |
1941
|
|
|
|
|
|
|
App-Fetchware-Extension???: Failed to open file [$file_to_check]! Because of |
1942
|
|
|
|
|
|
|
OS error [$!]. |
1943
|
|
|
|
|
|
|
EOE |
1944
|
|
|
|
|
|
|
|
1945
|
|
|
|
|
|
|
safe_open() takes $file_to_check and does a bunch of file checks on that |
1946
|
|
|
|
|
|
|
file to determine if it's safe to open and use the contents of that file in |
1947
|
|
|
|
|
|
|
your program. Instead of returning true or false, it returns a file handle of |
1948
|
|
|
|
|
|
|
the file you want to check that has already been open for you. This is done to |
1949
|
|
|
|
|
|
|
prevent race conditions between the time safe_open() checks the file's safety |
1950
|
|
|
|
|
|
|
and the time the caller actually opens the file. |
1951
|
|
|
|
|
|
|
|
1952
|
|
|
|
|
|
|
safe_open() also takes an optional second argument that specifies a caller |
1953
|
|
|
|
|
|
|
specific error message that replaces the generic default one. |
1954
|
|
|
|
|
|
|
|
1955
|
|
|
|
|
|
|
Fetchware occasionally needs to write files especially in fetchware's new() |
1956
|
|
|
|
|
|
|
command; therefore safe_open() also takes the fake hash argument |
1957
|
|
|
|
|
|
|
C 'E'>, which opens the file in a mode specified by the caller. |
1958
|
|
|
|
|
|
|
C<'E'> is for writing for example. See C for a list of |
1959
|
|
|
|
|
|
|
possible modes. |
1960
|
|
|
|
|
|
|
|
1961
|
|
|
|
|
|
|
In fetchware, this subroutine is used to check if every file fetchware |
1962
|
|
|
|
|
|
|
opens is safe to do so. It is based on is_safe() and is_very_safe() from the |
1963
|
|
|
|
|
|
|
Perl Cookbook by Tom Christiansen and Nathan Torkington. |
1964
|
|
|
|
|
|
|
|
1965
|
|
|
|
|
|
|
What this subroutine checks: |
1966
|
|
|
|
|
|
|
|
1967
|
|
|
|
|
|
|
=over |
1968
|
|
|
|
|
|
|
|
1969
|
|
|
|
|
|
|
=item * |
1970
|
|
|
|
|
|
|
|
1971
|
|
|
|
|
|
|
It opens the file you give to it as an argument, and all subsequent operations |
1972
|
|
|
|
|
|
|
are done on the opened filehandle to prevent race conditions. |
1973
|
|
|
|
|
|
|
|
1974
|
|
|
|
|
|
|
=item * |
1975
|
|
|
|
|
|
|
|
1976
|
|
|
|
|
|
|
Then it checks that the owner of the specified file must be either the superuser |
1977
|
|
|
|
|
|
|
or the user who ran fetchware. |
1978
|
|
|
|
|
|
|
|
1979
|
|
|
|
|
|
|
=item * |
1980
|
|
|
|
|
|
|
|
1981
|
|
|
|
|
|
|
It checks that the mode, as returned by File::stat's overridden stat, is not |
1982
|
|
|
|
|
|
|
writable by group or other. Fancy MAC permissions such as Linux's extfs's |
1983
|
|
|
|
|
|
|
extensions and fancy Windows permissions are B currently checked. |
1984
|
|
|
|
|
|
|
|
1985
|
|
|
|
|
|
|
=item * |
1986
|
|
|
|
|
|
|
|
1987
|
|
|
|
|
|
|
Then safe_open() stat's each and every parent directory that is in this file's |
1988
|
|
|
|
|
|
|
full path, and runs the same checks that are run above on each parent directory. |
1989
|
|
|
|
|
|
|
|
1990
|
|
|
|
|
|
|
=item * |
1991
|
|
|
|
|
|
|
|
1992
|
|
|
|
|
|
|
_PC_CHOWN_RESTRICTED is not tested; instead what is_very_safe() does is simply |
1993
|
|
|
|
|
|
|
always done. Because even with A _PC_CHOWN_RESTRICTED test, /home, for example, |
1994
|
|
|
|
|
|
|
could be 777. This is Unix after all, and root can do anything including screw |
1995
|
|
|
|
|
|
|
up permissions on system directories. |
1996
|
|
|
|
|
|
|
|
1997
|
|
|
|
|
|
|
=back |
1998
|
|
|
|
|
|
|
|
1999
|
|
|
|
|
|
|
If you actually are some sort of security expert, please feel free to |
2000
|
|
|
|
|
|
|
double-check if the list of stuff to check for is complete, and perhaps even the |
2001
|
|
|
|
|
|
|
Perl implementation to see if the subroutine really does check if |
2002
|
|
|
|
|
|
|
safe_open($file_to_check) is actually safe. |
2003
|
|
|
|
|
|
|
|
2004
|
|
|
|
|
|
|
=over |
2005
|
|
|
|
|
|
|
|
2006
|
|
|
|
|
|
|
=item WARNING |
2007
|
|
|
|
|
|
|
|
2008
|
|
|
|
|
|
|
According to L's chmod() documentation, on Win32 perl's Unixish file |
2009
|
|
|
|
|
|
|
permissions arn't supported only "owner" is: |
2010
|
|
|
|
|
|
|
|
2011
|
|
|
|
|
|
|
"Only good for changing "owner" read-write access, "group", and "other" bits are |
2012
|
|
|
|
|
|
|
meaningless. (Win32)" |
2013
|
|
|
|
|
|
|
|
2014
|
|
|
|
|
|
|
I'm not completely sure this means that under Win32 only owner perms mean |
2015
|
|
|
|
|
|
|
something, or if just chmod()ing group or ther bits don't do anything, but |
2016
|
|
|
|
|
|
|
testing if group and other are rwx does work. This needs testing. |
2017
|
|
|
|
|
|
|
|
2018
|
|
|
|
|
|
|
And remember this only applies to Win32, and fetchware has not yet been properly |
2019
|
|
|
|
|
|
|
ported or tested under Win32 yet. |
2020
|
|
|
|
|
|
|
|
2021
|
|
|
|
|
|
|
=back |
2022
|
|
|
|
|
|
|
|
2023
|
|
|
|
|
|
|
=head2 drop_privs() |
2024
|
|
|
|
|
|
|
|
2025
|
|
|
|
|
|
|
my $output = drop_privs(sub { |
2026
|
|
|
|
|
|
|
my $write_pipe = shift; |
2027
|
|
|
|
|
|
|
# Do stuff as $regular_user |
2028
|
|
|
|
|
|
|
... |
2029
|
|
|
|
|
|
|
# Use write_dropprivs_pipe to share variables back to parent. |
2030
|
|
|
|
|
|
|
write_dropprivs_pipe($write_pipe, $var1, $var2, ...); |
2031
|
|
|
|
|
|
|
|
2032
|
|
|
|
|
|
|
}, $regular_user |
2033
|
|
|
|
|
|
|
); |
2034
|
|
|
|
|
|
|
|
2035
|
|
|
|
|
|
|
# Back in the parent, use read_dropprivs_pipe() to read in whatever |
2036
|
|
|
|
|
|
|
# variables the child shared with us. |
2037
|
|
|
|
|
|
|
my ($var1, $var2, ...) = read_dropprivs_pipe($output); |
2038
|
|
|
|
|
|
|
|
2039
|
|
|
|
|
|
|
Forks and drops privs to $regular_user, and then executes whatever is in the |
2040
|
|
|
|
|
|
|
first argument, which should be a code reference. Throws an exception on any |
2041
|
|
|
|
|
|
|
problems with the fork. |
2042
|
|
|
|
|
|
|
|
2043
|
|
|
|
|
|
|
It only allows you to specify what the lower priveledged user does. The parent |
2044
|
|
|
|
|
|
|
process's behavior can not be changed. All the parent does: |
2045
|
|
|
|
|
|
|
|
2046
|
|
|
|
|
|
|
=over |
2047
|
|
|
|
|
|
|
|
2048
|
|
|
|
|
|
|
=item * |
2049
|
|
|
|
|
|
|
|
2050
|
|
|
|
|
|
|
Create a pipe to allow the child to communicate any information back to the |
2051
|
|
|
|
|
|
|
parent. |
2052
|
|
|
|
|
|
|
|
2053
|
|
|
|
|
|
|
=item * |
2054
|
|
|
|
|
|
|
|
2055
|
|
|
|
|
|
|
Read any data the child may write to that pipe. |
2056
|
|
|
|
|
|
|
|
2057
|
|
|
|
|
|
|
=item * |
2058
|
|
|
|
|
|
|
|
2059
|
|
|
|
|
|
|
After the child has died, collect the child's exit status. |
2060
|
|
|
|
|
|
|
|
2061
|
|
|
|
|
|
|
=item * |
2062
|
|
|
|
|
|
|
|
2063
|
|
|
|
|
|
|
And return the output the child wrote on the pipe as a scalar reference. |
2064
|
|
|
|
|
|
|
|
2065
|
|
|
|
|
|
|
=back |
2066
|
|
|
|
|
|
|
|
2067
|
|
|
|
|
|
|
Whatever the child writes is returned. drop_privs() does not use Storable or |
2068
|
|
|
|
|
|
|
JSON or XML or anything. It is up to you to specify how the data is to be |
2069
|
|
|
|
|
|
|
represented and used. However, L and |
2070
|
|
|
|
|
|
|
L are provided. They provide a simple way to store |
2071
|
|
|
|
|
|
|
multiple variables that can have any character in them including newline. See |
2072
|
|
|
|
|
|
|
their documentation for details. |
2073
|
|
|
|
|
|
|
|
2074
|
|
|
|
|
|
|
=over |
2075
|
|
|
|
|
|
|
|
2076
|
|
|
|
|
|
|
=item SECURITY NOTICE |
2077
|
|
|
|
|
|
|
|
2078
|
|
|
|
|
|
|
The output returned by drop_privs() is whatever the child wants it to be. If |
2079
|
|
|
|
|
|
|
somehow the child got hacked, the $output could be something that could cause |
2080
|
|
|
|
|
|
|
the parent (which has root perms!) to execute some code, or otherwise do |
2081
|
|
|
|
|
|
|
something that could cause the child to gain root access. So be sure to check |
2082
|
|
|
|
|
|
|
how you use drop_privs() return value, and definitley don't just string eval it. |
2083
|
|
|
|
|
|
|
Structure it so the return value can only be used as data for variables, and |
2084
|
|
|
|
|
|
|
that those variables are never executed by root. |
2085
|
|
|
|
|
|
|
|
2086
|
|
|
|
|
|
|
=back |
2087
|
|
|
|
|
|
|
|
2088
|
|
|
|
|
|
|
drop_privs() handles being on nonunix for you. On a platform that is not Unix |
2089
|
|
|
|
|
|
|
that does not have Unix's fork() and exec() security model, drop_privs() simply |
2090
|
|
|
|
|
|
|
executes the provided code reference I dropping priveledges. |
2091
|
|
|
|
|
|
|
|
2092
|
|
|
|
|
|
|
=over |
2093
|
|
|
|
|
|
|
|
2094
|
|
|
|
|
|
|
=item USABILITY NOTICE |
2095
|
|
|
|
|
|
|
|
2096
|
|
|
|
|
|
|
drop_privs()'s implementation depends on start() creating a tempdir and |
2097
|
|
|
|
|
|
|
chdir()ing to it. Furthermore, drop_privs() sometimes creates a tempdir of its |
2098
|
|
|
|
|
|
|
own, and it does not do a chdir back to another directory, so drop_privs() |
2099
|
|
|
|
|
|
|
depends on end() to chdir back to original_cwd(). Therefore, do not use |
2100
|
|
|
|
|
|
|
drop_privs() without also using start() and end() to manage a temporary |
2101
|
|
|
|
|
|
|
directory for drop_privs(). |
2102
|
|
|
|
|
|
|
|
2103
|
|
|
|
|
|
|
=back |
2104
|
|
|
|
|
|
|
|
2105
|
|
|
|
|
|
|
drop_privs() also supports a C 1> option that turns |
2106
|
|
|
|
|
|
|
off drop_privs() creating a temporary diretory to give the child a writable |
2107
|
|
|
|
|
|
|
temporary directory. This option is only used by cmd_new(), and probably only |
2108
|
|
|
|
|
|
|
really needs to be used there. Also, note that you must provide this option |
2109
|
|
|
|
|
|
|
after the $child_code coderef, and the $regular user options. Like so, |
2110
|
|
|
|
|
|
|
C 1>. |
2111
|
|
|
|
|
|
|
|
2112
|
|
|
|
|
|
|
=head2 drop_privs() PIPE PARSING UTILITIES |
2113
|
|
|
|
|
|
|
|
2114
|
|
|
|
|
|
|
drop_privs() uses a pipe for IPC between the child and the parent. This section |
2115
|
|
|
|
|
|
|
contains utilties that help users of drop_privs() parse the input and output |
2116
|
|
|
|
|
|
|
they send from the child back to the parent. |
2117
|
|
|
|
|
|
|
|
2118
|
|
|
|
|
|
|
Use write_dropprivs_pipe() to send data back to the parent, that later you'll read |
2119
|
|
|
|
|
|
|
with read_dropprivs_pipe() back in the parent. |
2120
|
|
|
|
|
|
|
|
2121
|
|
|
|
|
|
|
=head3 write_dropprivs_pipe() |
2122
|
|
|
|
|
|
|
|
2123
|
|
|
|
|
|
|
write_dropprivs_pipe($write_pipe, $variable1, $variable2, $variable3); |
2124
|
|
|
|
|
|
|
|
2125
|
|
|
|
|
|
|
Simply uses the caller provided $write_pipe file handle to write the rest of its |
2126
|
|
|
|
|
|
|
args to that file handle separated by a I. |
2127
|
|
|
|
|
|
|
|
2128
|
|
|
|
|
|
|
This magic number is just generated uniquely each time App::Fetchware::Util is |
2129
|
|
|
|
|
|
|
compiled. This number replaces using newline to separate each of the variables |
2130
|
|
|
|
|
|
|
that write_dropprivs_pipe() writes. This way you can include newline, and in |
2131
|
|
|
|
|
|
|
fact anything that does not contain the magic number, which is obviously |
2132
|
|
|
|
|
|
|
suitably unlikely. |
2133
|
|
|
|
|
|
|
|
2134
|
|
|
|
|
|
|
=over |
2135
|
|
|
|
|
|
|
|
2136
|
|
|
|
|
|
|
=item UNDEF AND EMPTY STRING WARNING |
2137
|
|
|
|
|
|
|
|
2138
|
|
|
|
|
|
|
write_dropprivs_pipe() and read_dropprivs_pipe() both bizarely, accidentily |
2139
|
|
|
|
|
|
|
I undef. It's really a function of Perl's C operators |
2140
|
|
|
|
|
|
|
side-effect of returning undef when there is no data to actually return, but the |
2141
|
|
|
|
|
|
|
seperator actually does exist. However, do B depend on this so called |
2142
|
|
|
|
|
|
|
"preservation", because C<''>, empty string, is converted into undef by |
2143
|
|
|
|
|
|
|
read_dropprivs_pipe() preventing you from distinguishing between the two values. |
2144
|
|
|
|
|
|
|
|
2145
|
|
|
|
|
|
|
=back |
2146
|
|
|
|
|
|
|
|
2147
|
|
|
|
|
|
|
=head3 read_dropprivs_pipe() |
2148
|
|
|
|
|
|
|
|
2149
|
|
|
|
|
|
|
my ($variable1, $variable2, $variable3) = pipe_read_newling($output); |
2150
|
|
|
|
|
|
|
|
2151
|
|
|
|
|
|
|
read_dropprivs_pipe() opens the scalar $output, and returns a list of $outputs |
2152
|
|
|
|
|
|
|
parsed out variables split on the $MAGIC_NUMBER, which is randomly generated |
2153
|
|
|
|
|
|
|
during each time you run Fetchware to avoid you every actually using it. |
2154
|
|
|
|
|
|
|
|
2155
|
|
|
|
|
|
|
=over |
2156
|
|
|
|
|
|
|
|
2157
|
|
|
|
|
|
|
=item UNDEF AND EMPTY STRING WARNING |
2158
|
|
|
|
|
|
|
|
2159
|
|
|
|
|
|
|
write_dropprivs_pipe() and read_dropprivs_pipe() both bizarely, accidentily |
2160
|
|
|
|
|
|
|
I undef. It's really a function of Perl's C operators |
2161
|
|
|
|
|
|
|
side-effect of returning undef when there is no data to actually return, but the |
2162
|
|
|
|
|
|
|
seperator actually does exist. However, do B depend on this so called |
2163
|
|
|
|
|
|
|
"preservation", because C<''>, empty string, is converted into undef by |
2164
|
|
|
|
|
|
|
read_dropprivs_pipe() preventing you from distinguishing between the two values. |
2165
|
|
|
|
|
|
|
|
2166
|
|
|
|
|
|
|
=back |
2167
|
|
|
|
|
|
|
|
2168
|
|
|
|
|
|
|
=head1 MISCELANEOUS UTILTY SUBROUTINES |
2169
|
|
|
|
|
|
|
|
2170
|
|
|
|
|
|
|
This is just a catch all category for everything else in App::Fetchware::Utility. |
2171
|
|
|
|
|
|
|
|
2172
|
|
|
|
|
|
|
=head2 do_nothing() |
2173
|
|
|
|
|
|
|
|
2174
|
|
|
|
|
|
|
do_nothing(); |
2175
|
|
|
|
|
|
|
|
2176
|
|
|
|
|
|
|
do_nothing() does nothing but return. It simply returns doing nothing. It is |
2177
|
|
|
|
|
|
|
meant to be used by App::Fetchware "subclasses" that "override" App::Fetchware's |
2178
|
|
|
|
|
|
|
API subroutines to make those API subroutines do nothing. |
2179
|
|
|
|
|
|
|
|
2180
|
|
|
|
|
|
|
=head1 ERRORS |
2181
|
|
|
|
|
|
|
|
2182
|
|
|
|
|
|
|
As with the rest of App::Fetchware, App::Fetchware::Util does not return any |
2183
|
|
|
|
|
|
|
error codes; instead, all errors are die()'d if it's App::Fetchware::Util's |
2184
|
|
|
|
|
|
|
error, or croak()'d if its the caller's fault. These exceptions are simple |
2185
|
|
|
|
|
|
|
strings, and are listed in the L section below. |
2186
|
|
|
|
|
|
|
|
2187
|
|
|
|
|
|
|
=head1 BUGS |
2188
|
|
|
|
|
|
|
|
2189
|
|
|
|
|
|
|
App::Fetchware::Util's temporary directory creation utilities, create_tempdir(), |
2190
|
|
|
|
|
|
|
original_cwd(), and cleanup_tempdir(), only keep track of one tempdir at a time. If |
2191
|
|
|
|
|
|
|
you create another tempdir with create_tempdir() it will override the value of |
2192
|
|
|
|
|
|
|
original_cwd(), which may mess up other functions that call create_tempdir(), |
2193
|
|
|
|
|
|
|
original_cwd(), and cleanup_tempdir(). Therefore, be careful when you call these |
2194
|
|
|
|
|
|
|
functions, and do B use them inside a fetchware extension if you reuse |
2195
|
|
|
|
|
|
|
App::Fetchware's start() and end(), because App::Fetchware's start() and end() |
2196
|
|
|
|
|
|
|
use these functions, so your use of them will conflict. If you still need to |
2197
|
|
|
|
|
|
|
create a tempdir just call File::Temp's tempdir() directly. |
2198
|
|
|
|
|
|
|
|
2199
|
|
|
|
|
|
|
=head1 AUTHOR |
2200
|
|
|
|
|
|
|
|
2201
|
|
|
|
|
|
|
David Yingling |
2202
|
|
|
|
|
|
|
|
2203
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
2204
|
|
|
|
|
|
|
|
2205
|
|
|
|
|
|
|
This software is copyright (c) 2016 by David Yingling. |
2206
|
|
|
|
|
|
|
|
2207
|
|
|
|
|
|
|
This is free software; you can redistribute it and/or modify it under |
2208
|
|
|
|
|
|
|
the same terms as the Perl 5 programming language system itself. |
2209
|
|
|
|
|
|
|
|
2210
|
|
|
|
|
|
|
=cut |
2211
|
|
|
|
|
|
|
|
2212
|
|
|
|
|
|
|
__END__ |