line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package urpm::download; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
|
4
|
2
|
|
|
2
|
|
3496
|
use strict; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
47
|
|
5
|
2
|
|
|
2
|
|
319
|
use urpm::msg; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
use urpm::util qw(cat_ basename dirname file_size max member output_safe reduce_pathname); |
7
|
|
|
|
|
|
|
use bytes (); |
8
|
|
|
|
|
|
|
use Cwd; |
9
|
|
|
|
|
|
|
use Exporter; |
10
|
|
|
|
|
|
|
# perl_checker: require urpm |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
# help perl_checker: |
13
|
|
|
|
|
|
|
sub getcwd { goto &Cwd::getcwd } |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
our @ISA = 'Exporter'; |
16
|
|
|
|
|
|
|
our @EXPORT = qw(get_proxy |
17
|
|
|
|
|
|
|
propagate_sync_callback |
18
|
|
|
|
|
|
|
sync_file sync_rsync sync_ssh |
19
|
|
|
|
|
|
|
set_proxy_config dump_proxy_config |
20
|
|
|
|
|
|
|
); |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
#- proxy config file. |
23
|
|
|
|
|
|
|
our $PROXY_CFG = '/etc/urpmi/proxy.cfg'; |
24
|
|
|
|
|
|
|
my $proxy_config; |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
#- Timeout for curl connection and wget operations |
27
|
|
|
|
|
|
|
our $CONNECT_TIMEOUT = 60; #- (in seconds) |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
=head1 NAME |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
urpm::download - download routines for the urpm* tools |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
=head1 SYNOPSIS |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
=head1 DESCRIPTION |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
=over |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
=cut |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
sub ftp_http_downloaders() { qw(curl wget prozilla aria2) } |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
sub available_ftp_http_downloaders() { |
46
|
|
|
|
|
|
|
my %binaries = ( |
47
|
|
|
|
|
|
|
curl => 'curl', |
48
|
|
|
|
|
|
|
wget => 'wget', |
49
|
|
|
|
|
|
|
prozilla => 'proz', |
50
|
|
|
|
|
|
|
aria2 => 'aria2c', |
51
|
|
|
|
|
|
|
); |
52
|
|
|
|
|
|
|
grep { -x "/usr/bin/$binaries{$_}" || -x "/bin/$binaries{$_}" } ftp_http_downloaders(); |
53
|
|
|
|
|
|
|
} |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
sub metalink_downloaders() { qw(aria2) } |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
sub available_metalink_downloaders() { |
58
|
|
|
|
|
|
|
my %binaries = ( |
59
|
|
|
|
|
|
|
aria2 => 'aria2c', |
60
|
|
|
|
|
|
|
); |
61
|
|
|
|
|
|
|
grep { -x "/usr/bin/$binaries{$_}" || -x "/bin/$binaries{$_}" } metalink_downloaders(); |
62
|
|
|
|
|
|
|
} |
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
sub use_metalink { |
65
|
|
|
|
|
|
|
my ($urpm, $medium) = @_; |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
$medium->{allow_metalink} //= do { |
68
|
|
|
|
|
|
|
my $use_metalink = 1; |
69
|
|
|
|
|
|
|
preferred_downloader($urpm, $medium, \$use_metalink); |
70
|
|
|
|
|
|
|
$use_metalink; |
71
|
|
|
|
|
|
|
}; |
72
|
|
|
|
|
|
|
} |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
my %warned; |
75
|
|
|
|
|
|
|
sub preferred_downloader { |
76
|
|
|
|
|
|
|
my ($urpm, $medium, $use_metalink) = @_; |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
my @available = urpm::download::available_ftp_http_downloaders(); |
79
|
|
|
|
|
|
|
my @metalink_downloaders = urpm::download::available_metalink_downloaders(); |
80
|
|
|
|
|
|
|
my $metalink_disabled = !$$use_metalink && $medium->{disable_metalink}; |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
if ($$use_metalink && !$metalink_disabled) { |
83
|
|
|
|
|
|
|
#- If metalink is used, only aria2 is available as other downloaders doesn't support metalink |
84
|
|
|
|
|
|
|
unshift @available, @metalink_downloaders; |
85
|
|
|
|
|
|
|
} |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
#- first downloader of @available is the default one |
88
|
|
|
|
|
|
|
my $preferred = $available[0]; |
89
|
|
|
|
|
|
|
my $requested_downloader = requested_ftp_http_downloader($urpm, $medium); |
90
|
|
|
|
|
|
|
if ($requested_downloader) { |
91
|
|
|
|
|
|
|
if (member($requested_downloader, @available)) { |
92
|
|
|
|
|
|
|
#- use user default downloader if provided and available |
93
|
|
|
|
|
|
|
$preferred = $requested_downloader; |
94
|
|
|
|
|
|
|
} elsif ($warned{webfetch_not_available}++ == 0) { |
95
|
|
|
|
|
|
|
$urpm->{log}(N("%s is not available, falling back on %s", $requested_downloader, $preferred)); |
96
|
|
|
|
|
|
|
} |
97
|
|
|
|
|
|
|
} |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
if ($$use_metalink && !member($preferred, @metalink_downloaders)) { |
100
|
|
|
|
|
|
|
$warned{not_using_metalink}++ or |
101
|
|
|
|
|
|
|
$urpm->{log}($requested_downloader eq $preferred ? |
102
|
|
|
|
|
|
|
"not using metalink since requested downloader does not handle it" : |
103
|
|
|
|
|
|
|
"not using metalink since no downloaders handling metalink are available"); |
104
|
|
|
|
|
|
|
$$use_metalink = 0; |
105
|
|
|
|
|
|
|
} |
106
|
|
|
|
|
|
|
$preferred; |
107
|
|
|
|
|
|
|
} |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
sub parse_http_proxy { |
110
|
|
|
|
|
|
|
$_[0] =~ m!^(?:http://)?([^:/]+(:\d+)?)/*$!; |
111
|
|
|
|
|
|
|
} |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
#- parses proxy.cfg (private) |
114
|
|
|
|
|
|
|
sub load_proxy_config () { |
115
|
|
|
|
|
|
|
return if defined $proxy_config; |
116
|
|
|
|
|
|
|
$proxy_config = {}; |
117
|
|
|
|
|
|
|
foreach (cat_($PROXY_CFG)) { |
118
|
|
|
|
|
|
|
chomp; s/#.*$//; s/^\s*//; s/\s*$//; |
119
|
|
|
|
|
|
|
if (/^(?:(.*):\s*)?(ftp_proxy|http_proxy)\s*=\s*(.*)$/) { |
120
|
|
|
|
|
|
|
$proxy_config->{$1 || ''}{$2} = $3; |
121
|
|
|
|
|
|
|
next; |
122
|
|
|
|
|
|
|
} |
123
|
|
|
|
|
|
|
if (/^(?:(.*):\s*)?proxy_user\s*=\s*([^:]*)(?::(.*))?$/) { |
124
|
|
|
|
|
|
|
$proxy_config->{$1 || ''}{user} = $2; |
125
|
|
|
|
|
|
|
$proxy_config->{$1 || ''}{pwd} = $3 if defined $3; |
126
|
|
|
|
|
|
|
next; |
127
|
|
|
|
|
|
|
} |
128
|
|
|
|
|
|
|
if (/^(?:(.*):\s*)?proxy_user_ask/) { |
129
|
|
|
|
|
|
|
$proxy_config->{$1 || ''}{ask} = 1; |
130
|
|
|
|
|
|
|
next; |
131
|
|
|
|
|
|
|
} |
132
|
|
|
|
|
|
|
} |
133
|
|
|
|
|
|
|
} |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
#- writes proxy.cfg |
136
|
|
|
|
|
|
|
sub dump_proxy_config () { |
137
|
|
|
|
|
|
|
$proxy_config or return 0; #- hasn't been read yet |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
my $has_password; |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
open my $f, '>', $PROXY_CFG or return 0; |
142
|
|
|
|
|
|
|
foreach ('', sort grep { !/^(|cmd_line)$/ } keys %$proxy_config) { |
143
|
|
|
|
|
|
|
my $m = $_ eq '' ? '' : "$_:"; |
144
|
|
|
|
|
|
|
my $p = $proxy_config->{$_}; |
145
|
|
|
|
|
|
|
foreach (qw(http_proxy ftp_proxy)) { |
146
|
|
|
|
|
|
|
if (defined $p->{$_} && $p->{$_} ne '') { |
147
|
|
|
|
|
|
|
print $f "$m$_=$p->{$_}\n"; |
148
|
|
|
|
|
|
|
$has_password ||= hide_password($p->{$_}) ne $p->{$_}; |
149
|
|
|
|
|
|
|
} |
150
|
|
|
|
|
|
|
} |
151
|
|
|
|
|
|
|
if ($p->{ask}) { |
152
|
|
|
|
|
|
|
print $f "${m}proxy_user_ask\n"; |
153
|
|
|
|
|
|
|
} elsif (defined $p->{user} && $p->{user} ne '') { |
154
|
|
|
|
|
|
|
print $f "${m}proxy_user=$p->{user}:$p->{pwd}\n"; |
155
|
|
|
|
|
|
|
$has_password ||= $p->{pwd}; |
156
|
|
|
|
|
|
|
} |
157
|
|
|
|
|
|
|
} |
158
|
|
|
|
|
|
|
close $f; |
159
|
|
|
|
|
|
|
chmod 0600, $PROXY_CFG if $has_password; |
160
|
|
|
|
|
|
|
return 1; |
161
|
|
|
|
|
|
|
} |
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
#- deletes the proxy configuration for the specified media |
164
|
|
|
|
|
|
|
sub remove_proxy_media { |
165
|
|
|
|
|
|
|
defined $proxy_config and delete $proxy_config->{$_[0] || ''}; |
166
|
|
|
|
|
|
|
} |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
sub get_proxy_ { |
169
|
|
|
|
|
|
|
my ($urpm) = @_; |
170
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
-e $PROXY_CFG && !-r $PROXY_CFG and $urpm->{error}(N("can not read proxy settings (not enough rights to read %s)", $PROXY_CFG)); |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
get_proxy($urpm); |
174
|
|
|
|
|
|
|
} |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
=item get_proxy($media) |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
Reads and loads the proxy.cfg file ; |
179
|
|
|
|
|
|
|
Returns the global proxy settings (without arguments) or the |
180
|
|
|
|
|
|
|
proxy settings for the specified media (with a media name as argument) |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
=cut |
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
sub get_proxy (;$) { |
185
|
|
|
|
|
|
|
my ($o_media) = @_; $o_media ||= ''; |
186
|
|
|
|
|
|
|
load_proxy_config(); |
187
|
|
|
|
|
|
|
my $p = $proxy_config->{cmd_line} |
188
|
|
|
|
|
|
|
|| $proxy_config->{$o_media} |
189
|
|
|
|
|
|
|
|| $proxy_config->{''} |
190
|
|
|
|
|
|
|
|| { |
191
|
|
|
|
|
|
|
http_proxy => undef, |
192
|
|
|
|
|
|
|
ftp_proxy => undef, |
193
|
|
|
|
|
|
|
user => undef, |
194
|
|
|
|
|
|
|
pwd => undef, |
195
|
|
|
|
|
|
|
}; |
196
|
|
|
|
|
|
|
if ($p->{ask} && ($p->{http_proxy} || $p->{ftp_proxy}) && !$p->{user}) { |
197
|
|
|
|
|
|
|
our $PROMPT_PROXY; |
198
|
|
|
|
|
|
|
unless (defined $PROMPT_PROXY) { |
199
|
|
|
|
|
|
|
require urpm::prompt; |
200
|
|
|
|
|
|
|
$PROMPT_PROXY = new urpm::prompt( |
201
|
|
|
|
|
|
|
N("Please enter your credentials for accessing proxy\n"), |
202
|
|
|
|
|
|
|
[ N("User name:"), N("Password:") ], |
203
|
|
|
|
|
|
|
undef, |
204
|
|
|
|
|
|
|
[ 0, 1 ], |
205
|
|
|
|
|
|
|
); |
206
|
|
|
|
|
|
|
} |
207
|
|
|
|
|
|
|
($p->{user}, $p->{pwd}) = $PROMPT_PROXY->prompt; |
208
|
|
|
|
|
|
|
} |
209
|
|
|
|
|
|
|
$p; |
210
|
|
|
|
|
|
|
} |
211
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
#- copies the settings for proxies from the command line to media named $media |
213
|
|
|
|
|
|
|
#- and writes the proxy.cfg file (used when adding new media) |
214
|
|
|
|
|
|
|
sub copy_cmd_line_proxy { |
215
|
|
|
|
|
|
|
my ($media) = @_; |
216
|
|
|
|
|
|
|
return unless $media; |
217
|
|
|
|
|
|
|
load_proxy_config(); |
218
|
|
|
|
|
|
|
if (defined $proxy_config->{cmd_line}) { |
219
|
|
|
|
|
|
|
$proxy_config->{$media} = $proxy_config->{cmd_line}; |
220
|
|
|
|
|
|
|
dump_proxy_config(); |
221
|
|
|
|
|
|
|
} else { |
222
|
|
|
|
|
|
|
#- use default if available |
223
|
|
|
|
|
|
|
$proxy_config->{$media} = $proxy_config->{''}; |
224
|
|
|
|
|
|
|
} |
225
|
|
|
|
|
|
|
} |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
=item set_cmdline_proxy(%h) |
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
Overrides the config file proxy settings with values passed via command-line |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
=cut |
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
sub set_cmdline_proxy { |
234
|
|
|
|
|
|
|
my (%h) = @_; |
235
|
|
|
|
|
|
|
load_proxy_config(); |
236
|
|
|
|
|
|
|
$proxy_config->{cmd_line} ||= { |
237
|
|
|
|
|
|
|
http_proxy => undef, |
238
|
|
|
|
|
|
|
ftp_proxy => undef, |
239
|
|
|
|
|
|
|
user => undef, |
240
|
|
|
|
|
|
|
pwd => undef, |
241
|
|
|
|
|
|
|
}; |
242
|
|
|
|
|
|
|
$proxy_config->{cmd_line}{$_} = $h{$_} foreach keys %h; |
243
|
|
|
|
|
|
|
} |
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
=item set_proxy_config($key, $value, $o_media) |
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
Changes permanently the proxy settings |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
=cut |
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
sub set_proxy_config { |
252
|
|
|
|
|
|
|
my ($key, $value, $o_media) = @_; |
253
|
|
|
|
|
|
|
$proxy_config->{$o_media || ''}{$key} = $value; |
254
|
|
|
|
|
|
|
} |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
#- set up the environment for proxy usage for the appropriate tool. |
257
|
|
|
|
|
|
|
#- returns an array of command-line arguments for wget or curl. |
258
|
|
|
|
|
|
|
sub set_proxy { |
259
|
|
|
|
|
|
|
my ($proxy) = @_; |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
my $p = $proxy->{proxy}; |
262
|
|
|
|
|
|
|
defined $p->{http_proxy} || defined $p->{ftp_proxy} or return; |
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
my @res; |
265
|
|
|
|
|
|
|
if ($proxy->{type} =~ /\bwget\b/) { |
266
|
|
|
|
|
|
|
if (defined $p->{http_proxy}) { |
267
|
|
|
|
|
|
|
$ENV{http_proxy} = $p->{http_proxy} =~ /^http:/ |
268
|
|
|
|
|
|
|
? $p->{http_proxy} : "http://$p->{http_proxy}"; |
269
|
|
|
|
|
|
|
} |
270
|
|
|
|
|
|
|
$ENV{ftp_proxy} = $p->{ftp_proxy} if defined $p->{ftp_proxy}; |
271
|
|
|
|
|
|
|
@res = ("--proxy-user=$p->{user}", "--proxy-passwd=$p->{pwd}") |
272
|
|
|
|
|
|
|
if defined $p->{user} && defined $p->{pwd}; |
273
|
|
|
|
|
|
|
} elsif ($proxy->{type} =~ /\bcurl\b/) { |
274
|
|
|
|
|
|
|
push @res, ('-x', $p->{http_proxy}) if defined $p->{http_proxy}; |
275
|
|
|
|
|
|
|
push @res, ('-x', $p->{ftp_proxy}) if defined $p->{ftp_proxy}; |
276
|
|
|
|
|
|
|
push @res, ('-U', "$p->{user}:$p->{pwd}") |
277
|
|
|
|
|
|
|
if defined $p->{user} && defined $p->{pwd}; |
278
|
|
|
|
|
|
|
push @res, '-H', 'Pragma:' if @res; |
279
|
|
|
|
|
|
|
} elsif ($proxy->{type} =~ /\baria2\b/) { |
280
|
|
|
|
|
|
|
if (my ($http_proxy) = $p->{http_proxy} && parse_http_proxy($p->{http_proxy})) { |
281
|
|
|
|
|
|
|
my $allproxy = $p->{user}; |
282
|
|
|
|
|
|
|
$allproxy .= ":" . $p->{pwd} if $p->{pwd}; |
283
|
|
|
|
|
|
|
$allproxy .= "@" if $p->{user}; |
284
|
|
|
|
|
|
|
$allproxy .= $http_proxy; |
285
|
|
|
|
|
|
|
@res = ("--all-proxy=http://$allproxy"); |
286
|
|
|
|
|
|
|
} |
287
|
|
|
|
|
|
|
} else { |
288
|
|
|
|
|
|
|
die N("Unknown webfetch `%s' !!!\n", $proxy->{type}); |
289
|
|
|
|
|
|
|
} |
290
|
|
|
|
|
|
|
@res; |
291
|
|
|
|
|
|
|
} |
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
sub _error_msg { |
294
|
|
|
|
|
|
|
my ($name) = @_; |
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
my $msg = $? & 127 ? N("%s failed: exited with signal %d", $name, $? & 127) : |
297
|
|
|
|
|
|
|
N("%s failed: exited with %d", $name, $? >> 8); |
298
|
|
|
|
|
|
|
"$msg\n"; |
299
|
|
|
|
|
|
|
} |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
sub _error { |
302
|
|
|
|
|
|
|
my ($name) = @_; |
303
|
|
|
|
|
|
|
die _error_msg($name); |
304
|
|
|
|
|
|
|
} |
305
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
sub hide_password { |
307
|
|
|
|
|
|
|
my ($url) = @_; |
308
|
|
|
|
|
|
|
$url =~ s|([^:]*://[^/:\@]*:)[^/:\@]*(\@.*)|$1xxxx$2|; #- if needed... |
309
|
|
|
|
|
|
|
$url; |
310
|
|
|
|
|
|
|
} |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
sub propagate_sync_callback { |
313
|
|
|
|
|
|
|
my $options = shift; |
314
|
|
|
|
|
|
|
if (ref($options) && $options->{callback}) { |
315
|
|
|
|
|
|
|
my $mode = shift; |
316
|
|
|
|
|
|
|
if ($mode =~ /^(?:start|progress|end)$/) { |
317
|
|
|
|
|
|
|
my $file = shift; |
318
|
|
|
|
|
|
|
return $options->{callback}($mode, hide_password($file), @_); |
319
|
|
|
|
|
|
|
} else { |
320
|
|
|
|
|
|
|
return $options->{callback}($mode, @_); |
321
|
|
|
|
|
|
|
} |
322
|
|
|
|
|
|
|
} |
323
|
|
|
|
|
|
|
} |
324
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
sub sync_file { |
326
|
|
|
|
|
|
|
my $options = shift; |
327
|
|
|
|
|
|
|
foreach (@_) { |
328
|
|
|
|
|
|
|
propagate_sync_callback($options, 'start', $_); |
329
|
|
|
|
|
|
|
require urpm::util; |
330
|
|
|
|
|
|
|
urpm::util::copy($_, ref($options) ? $options->{dir} : $options) |
331
|
|
|
|
|
|
|
or die N("copy failed"); |
332
|
|
|
|
|
|
|
propagate_sync_callback($options, 'end', $_); |
333
|
|
|
|
|
|
|
} |
334
|
|
|
|
|
|
|
} |
335
|
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
sub sync_wget { |
337
|
|
|
|
|
|
|
-x "/usr/bin/wget" or die N("wget is missing\n"); |
338
|
|
|
|
|
|
|
my $options = shift; |
339
|
|
|
|
|
|
|
$options = { dir => $options } if !ref $options; |
340
|
|
|
|
|
|
|
#- force download to be done in cachedir to avoid polluting cwd. |
341
|
|
|
|
|
|
|
(my $cwd) = getcwd() =~ /(.*)/; |
342
|
|
|
|
|
|
|
chdir $options->{dir}; |
343
|
|
|
|
|
|
|
my ($buf, $total, $file) = ('', undef, undef); |
344
|
|
|
|
|
|
|
my $wget_command = join(" ", map { "'$_'" } |
345
|
|
|
|
|
|
|
#- construction of the wget command-line |
346
|
|
|
|
|
|
|
"/usr/bin/wget", |
347
|
|
|
|
|
|
|
($options->{'limit-rate'} ? "--limit-rate=$options->{'limit-rate'}" : @{[]}), |
348
|
|
|
|
|
|
|
($options->{resume} ? "--continue" : "--force-clobber"), |
349
|
|
|
|
|
|
|
($options->{proxy} ? set_proxy({ type => "wget", proxy => $options->{proxy} }) : @{[]}), |
350
|
|
|
|
|
|
|
($options->{retry} ? ('-t', $options->{retry}) : @{[]}), |
351
|
|
|
|
|
|
|
($options->{callback} ? ("--progress=bar:force", "-o", "-") : |
352
|
|
|
|
|
|
|
$options->{quiet} ? "-q" : @{[]}), |
353
|
|
|
|
|
|
|
"--retr-symlinks", |
354
|
|
|
|
|
|
|
($options->{"no-certificate-check"} ? "--no-check-certificate" : @{[]}), |
355
|
|
|
|
|
|
|
"--timeout=$CONNECT_TIMEOUT", |
356
|
|
|
|
|
|
|
(defined $options->{'wget-options'} ? split /\s+/, $options->{'wget-options'} : @{[]}), |
357
|
|
|
|
|
|
|
'-P', $options->{dir}, |
358
|
|
|
|
|
|
|
@_ |
359
|
|
|
|
|
|
|
) . " |"; |
360
|
|
|
|
|
|
|
$options->{debug} and $options->{debug}($wget_command); |
361
|
|
|
|
|
|
|
local $ENV{LC_ALL} = 'C'; |
362
|
|
|
|
|
|
|
my $wget_pid = open(my $wget, $wget_command); |
363
|
|
|
|
|
|
|
local $/ = \1; #- read input by only one char, this is slow but very nice (and it works!). |
364
|
|
|
|
|
|
|
local $_; |
365
|
|
|
|
|
|
|
while (<$wget>) { |
366
|
|
|
|
|
|
|
$buf .= $_; |
367
|
|
|
|
|
|
|
if ($_ eq "\r" || $_ eq "\n") { |
368
|
|
|
|
|
|
|
if ($options->{callback}) { |
369
|
|
|
|
|
|
|
if ($buf =~ /^--(\d\d\d\d-\d\d-\d\d )?\d\d:\d\d:\d\d--\s+(\S.*)\n/ms) { |
370
|
|
|
|
|
|
|
my $file_ = $2; |
371
|
|
|
|
|
|
|
if ($file && $file ne $file_) { |
372
|
|
|
|
|
|
|
propagate_sync_callback($options, 'end', $file); |
373
|
|
|
|
|
|
|
undef $file; |
374
|
|
|
|
|
|
|
} |
375
|
|
|
|
|
|
|
! defined $file and propagate_sync_callback($options, 'start', $file = $file_); |
376
|
|
|
|
|
|
|
} elsif (defined $file && ! defined $total && ($buf =~ /==>\s+RETR/ || $buf =~ /200 OK$/)) { |
377
|
|
|
|
|
|
|
$total = ''; |
378
|
|
|
|
|
|
|
} elsif ($buf =~ /^Length:\s*(\d\S*)/) { |
379
|
|
|
|
|
|
|
$total = $1; |
380
|
|
|
|
|
|
|
} elsif (defined $total && $buf =~ m!^\s*(\d+)%.*\s+(\S+/s)\s+((ETA|eta)\s+(.*?)\s*)?[\r\n]$!ms) { |
381
|
|
|
|
|
|
|
my ($percent, $speed, $eta) = ($1, $2, $5); |
382
|
|
|
|
|
|
|
if (propagate_sync_callback($options, 'progress', $file, $percent, $total, $eta, $speed) eq 'canceled') { |
383
|
|
|
|
|
|
|
kill 15, $wget_pid; |
384
|
|
|
|
|
|
|
close $wget; |
385
|
|
|
|
|
|
|
return; |
386
|
|
|
|
|
|
|
} |
387
|
|
|
|
|
|
|
if ($_ eq "\n") { |
388
|
|
|
|
|
|
|
propagate_sync_callback($options, 'end', $file); |
389
|
|
|
|
|
|
|
($total, $file) = (undef, undef); |
390
|
|
|
|
|
|
|
} |
391
|
|
|
|
|
|
|
} |
392
|
|
|
|
|
|
|
} else { |
393
|
|
|
|
|
|
|
$options->{quiet} or print STDERR $buf; |
394
|
|
|
|
|
|
|
} |
395
|
|
|
|
|
|
|
$buf = ''; |
396
|
|
|
|
|
|
|
} |
397
|
|
|
|
|
|
|
} |
398
|
|
|
|
|
|
|
$file and propagate_sync_callback($options, 'end', $file); |
399
|
|
|
|
|
|
|
chdir $cwd; |
400
|
|
|
|
|
|
|
close $wget or _error('wget'); |
401
|
|
|
|
|
|
|
} |
402
|
|
|
|
|
|
|
|
403
|
|
|
|
|
|
|
sub sync_curl { |
404
|
|
|
|
|
|
|
-x "/usr/bin/curl" or die N("curl is missing\n"); |
405
|
|
|
|
|
|
|
my $options = shift; |
406
|
|
|
|
|
|
|
$options = { dir => $options } if !ref $options; |
407
|
|
|
|
|
|
|
if (defined $options->{'limit-rate'} && $options->{'limit-rate'} =~ /\d$/) { |
408
|
|
|
|
|
|
|
#- use bytes by default |
409
|
|
|
|
|
|
|
$options->{'limit-rate'} .= 'B'; |
410
|
|
|
|
|
|
|
} |
411
|
|
|
|
|
|
|
#- force download to be done in cachedir to avoid polluting cwd, |
412
|
|
|
|
|
|
|
#- however for curl, this is mandatory. |
413
|
|
|
|
|
|
|
(my $cwd) = getcwd() =~ /(.*)/; |
414
|
|
|
|
|
|
|
chdir($options->{dir}); |
415
|
|
|
|
|
|
|
my (@ftp_files, @other_files); |
416
|
|
|
|
|
|
|
foreach (@_) { |
417
|
|
|
|
|
|
|
my ($proto, $nick, $rest) = m,^(http|ftp)://([^:/]+):(.*),,; |
418
|
|
|
|
|
|
|
if ($nick) { #- escape @ in user names |
419
|
|
|
|
|
|
|
$nick =~ s/@/%40/; |
420
|
|
|
|
|
|
|
$_ = "$proto://$nick:$rest"; |
421
|
|
|
|
|
|
|
} |
422
|
|
|
|
|
|
|
if (m|^ftp://.*/([^/]*)$| && file_size($1) > 8192) { #- manage time stamp for large file only |
423
|
|
|
|
|
|
|
push @ftp_files, $_; |
424
|
|
|
|
|
|
|
} else { |
425
|
|
|
|
|
|
|
push @other_files, $_; |
426
|
|
|
|
|
|
|
} |
427
|
|
|
|
|
|
|
} |
428
|
|
|
|
|
|
|
if (@ftp_files) { |
429
|
|
|
|
|
|
|
my ($cur_ftp_file, %ftp_files_info); |
430
|
|
|
|
|
|
|
local $_; |
431
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
eval { require Date::Manip }; |
433
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
#- prepare to get back size and time stamp of each file. |
435
|
|
|
|
|
|
|
my $cmd = join(" ", map { "'$_'" } "/usr/bin/curl", |
436
|
|
|
|
|
|
|
"-q", # don't read .curlrc; some toggle options might interfer |
437
|
|
|
|
|
|
|
($options->{'limit-rate'} ? ("--limit-rate", $options->{'limit-rate'}) : @{[]}), |
438
|
|
|
|
|
|
|
($options->{proxy} ? set_proxy({ type => "curl", proxy => $options->{proxy} }) : @{[]}), |
439
|
|
|
|
|
|
|
($options->{retry} ? ('--retry', $options->{retry}) : @{[]}), |
440
|
|
|
|
|
|
|
"--stderr", "-", # redirect everything to stdout |
441
|
|
|
|
|
|
|
"--disable-epsv", |
442
|
|
|
|
|
|
|
"--connect-timeout", $CONNECT_TIMEOUT, |
443
|
|
|
|
|
|
|
"-s", "-I", |
444
|
|
|
|
|
|
|
"--anyauth", |
445
|
|
|
|
|
|
|
(defined $options->{'curl-options'} ? split /\s+/, $options->{'curl-options'} : @{[]}), |
446
|
|
|
|
|
|
|
@ftp_files); |
447
|
|
|
|
|
|
|
$options->{debug} and $options->{debug}($cmd); |
448
|
|
|
|
|
|
|
open my $curl, "$cmd |"; |
449
|
|
|
|
|
|
|
while (<$curl>) { |
450
|
|
|
|
|
|
|
if (/Content-Length:\s*(\d+)/) { |
451
|
|
|
|
|
|
|
!$cur_ftp_file || exists($ftp_files_info{$cur_ftp_file}{size}) |
452
|
|
|
|
|
|
|
and $cur_ftp_file = shift @ftp_files; |
453
|
|
|
|
|
|
|
$ftp_files_info{$cur_ftp_file}{size} = $1; |
454
|
|
|
|
|
|
|
} |
455
|
|
|
|
|
|
|
if (/Last-Modified:\s*(.*)/) { |
456
|
|
|
|
|
|
|
!$cur_ftp_file || exists($ftp_files_info{$cur_ftp_file}{time}) |
457
|
|
|
|
|
|
|
and $cur_ftp_file = shift @ftp_files; |
458
|
|
|
|
|
|
|
eval { |
459
|
|
|
|
|
|
|
$ftp_files_info{$cur_ftp_file}{time} = Date::Manip::ParseDate($1); |
460
|
|
|
|
|
|
|
}; |
461
|
|
|
|
|
|
|
} |
462
|
|
|
|
|
|
|
} |
463
|
|
|
|
|
|
|
close $curl or _error('curl'); |
464
|
|
|
|
|
|
|
|
465
|
|
|
|
|
|
|
#- now analyse size and time stamp according to what already exists here. |
466
|
|
|
|
|
|
|
if (@ftp_files) { |
467
|
|
|
|
|
|
|
#- re-insert back shifted element of ftp_files, because curl output above |
468
|
|
|
|
|
|
|
#- has not been parsed correctly, so in doubt download them all. |
469
|
|
|
|
|
|
|
push @ftp_files, keys %ftp_files_info; |
470
|
|
|
|
|
|
|
} else { |
471
|
|
|
|
|
|
|
#- for that, it should be clear ftp_files is empty... |
472
|
|
|
|
|
|
|
#- elsewhere, the above work was useless. |
473
|
|
|
|
|
|
|
foreach (keys %ftp_files_info) { |
474
|
|
|
|
|
|
|
my ($lfile) = m|/([^/]*)$| or next; #- strange if we can't parse it correctly. |
475
|
|
|
|
|
|
|
my $ltime = eval { Date::Manip::ParseDate(scalar gmtime((stat $1)[9])) }; |
476
|
|
|
|
|
|
|
$ltime && -s $lfile == $ftp_files_info{$_}{size} && $ftp_files_info{$_}{time} eq $ltime |
477
|
|
|
|
|
|
|
or push @ftp_files, $_; |
478
|
|
|
|
|
|
|
} |
479
|
|
|
|
|
|
|
} |
480
|
|
|
|
|
|
|
} |
481
|
|
|
|
|
|
|
# Indicates whether this option is available in our curl |
482
|
|
|
|
|
|
|
our $location_trusted; |
483
|
|
|
|
|
|
|
if (!defined $location_trusted) { |
484
|
|
|
|
|
|
|
$location_trusted = `/usr/bin/curl -h` =~ /location-trusted/ ? 1 : 0; |
485
|
|
|
|
|
|
|
} |
486
|
|
|
|
|
|
|
#- http files (and other files) are correctly managed by curl wrt conditional download. |
487
|
|
|
|
|
|
|
#- options for ftp files, -R (-O )* |
488
|
|
|
|
|
|
|
#- options for http files, -R (-O )* |
489
|
|
|
|
|
|
|
my $result; |
490
|
|
|
|
|
|
|
if (my @all_files = ( |
491
|
|
|
|
|
|
|
(map { ("-O", $_) } @ftp_files), |
492
|
|
|
|
|
|
|
(map { m|/| ? ("-O", $_) : @{[]} } @other_files))) |
493
|
|
|
|
|
|
|
{ |
494
|
|
|
|
|
|
|
my @l = (@ftp_files, @other_files); |
495
|
|
|
|
|
|
|
my $cmd = join(" ", map { "'$_'" } "/usr/bin/curl", |
496
|
|
|
|
|
|
|
"-q", # don't read .curlrc; some toggle options might interfer |
497
|
|
|
|
|
|
|
($options->{'limit-rate'} ? ("--limit-rate", $options->{'limit-rate'}) : @{[]}), |
498
|
|
|
|
|
|
|
($options->{resume} ? ("--continue-at", "-") : @{[]}), |
499
|
|
|
|
|
|
|
($options->{proxy} ? set_proxy({ type => "curl", proxy => $options->{proxy} }) : @{[]}), |
500
|
|
|
|
|
|
|
($options->{retry} ? ('--retry', $options->{retry}) : @{[]}), |
501
|
|
|
|
|
|
|
($options->{quiet} ? "-s" : @{[]}), |
502
|
|
|
|
|
|
|
($options->{"no-certificate-check"} ? "-k" : @{[]}), |
503
|
|
|
|
|
|
|
$location_trusted ? "--location-trusted" : @{[]}, |
504
|
|
|
|
|
|
|
"-R", |
505
|
|
|
|
|
|
|
"-f", |
506
|
|
|
|
|
|
|
"--disable-epsv", |
507
|
|
|
|
|
|
|
"--connect-timeout", $CONNECT_TIMEOUT, |
508
|
|
|
|
|
|
|
"--anyauth", |
509
|
|
|
|
|
|
|
(defined $options->{'curl-options'} ? split /\s+/, $options->{'curl-options'} : @{[]}), |
510
|
|
|
|
|
|
|
"--stderr", "-", # redirect everything to stdout |
511
|
|
|
|
|
|
|
@all_files); |
512
|
|
|
|
|
|
|
$options->{debug} and $options->{debug}($cmd); |
513
|
|
|
|
|
|
|
$result = _curl_action($cmd, $options, @l); |
514
|
|
|
|
|
|
|
} |
515
|
|
|
|
|
|
|
chdir $cwd; |
516
|
|
|
|
|
|
|
$result; |
517
|
|
|
|
|
|
|
} |
518
|
|
|
|
|
|
|
|
519
|
|
|
|
|
|
|
sub _curl_action { |
520
|
|
|
|
|
|
|
my ($cmd, $options, @l) = @_; |
521
|
|
|
|
|
|
|
|
522
|
|
|
|
|
|
|
my ($buf, $file); $buf = ''; |
523
|
|
|
|
|
|
|
my $curl_pid = open(my $curl, "$cmd |"); |
524
|
|
|
|
|
|
|
local $/ = \1; #- read input by only one char, this is slow but very nice (and it works!). |
525
|
|
|
|
|
|
|
local $_; |
526
|
|
|
|
|
|
|
while (<$curl>) { |
527
|
|
|
|
|
|
|
$buf .= $_; |
528
|
|
|
|
|
|
|
if ($_ eq "\r" || $_ eq "\n") { |
529
|
|
|
|
|
|
|
if ($options->{callback}) { |
530
|
|
|
|
|
|
|
unless (defined $file) { |
531
|
|
|
|
|
|
|
$file = shift @l; |
532
|
|
|
|
|
|
|
propagate_sync_callback($options, 'start', $file); |
533
|
|
|
|
|
|
|
} |
534
|
|
|
|
|
|
|
if (my ($percent, $total, $eta, $speed) = $buf =~ /^\s*(\d+)\s+(\S+)[^\r\n]*\s+(\S+)\s+(\S+)\s*[\r\n]$/ms) { |
535
|
|
|
|
|
|
|
$speed =~ s/^-//; |
536
|
|
|
|
|
|
|
if (propagate_sync_callback($options, 'progress', $file, $percent, $total, $eta, $speed) eq 'canceled') { |
537
|
|
|
|
|
|
|
kill 15, $curl_pid; |
538
|
|
|
|
|
|
|
close $curl; |
539
|
|
|
|
|
|
|
die N("curl failed: download canceled\n"); |
540
|
|
|
|
|
|
|
} |
541
|
|
|
|
|
|
|
#- this checks that download has actually started |
542
|
|
|
|
|
|
|
if ($_ eq "\n" |
543
|
|
|
|
|
|
|
&& !($speed == 0 && $percent == 100 && index($eta, '--') >= 0) #- work around bug 13685 |
544
|
|
|
|
|
|
|
) { |
545
|
|
|
|
|
|
|
propagate_sync_callback($options, 'end', $file); |
546
|
|
|
|
|
|
|
$file = undef; |
547
|
|
|
|
|
|
|
} |
548
|
|
|
|
|
|
|
} elsif ($buf =~ /^curl:/) { #- likely to be an error reported by curl |
549
|
|
|
|
|
|
|
local $/ = "\n"; |
550
|
|
|
|
|
|
|
chomp $buf; |
551
|
|
|
|
|
|
|
propagate_sync_callback($options, 'error', $file, $buf); |
552
|
|
|
|
|
|
|
} |
553
|
|
|
|
|
|
|
} else { |
554
|
|
|
|
|
|
|
$options->{quiet} or print STDERR $buf; |
555
|
|
|
|
|
|
|
} |
556
|
|
|
|
|
|
|
$buf = ''; |
557
|
|
|
|
|
|
|
} |
558
|
|
|
|
|
|
|
} |
559
|
|
|
|
|
|
|
close $curl or _error('curl'); |
560
|
|
|
|
|
|
|
} |
561
|
|
|
|
|
|
|
|
562
|
|
|
|
|
|
|
sub _calc_limit_rate { |
563
|
|
|
|
|
|
|
my $limit_rate = $_[0]; |
564
|
|
|
|
|
|
|
for ($limit_rate) { |
565
|
|
|
|
|
|
|
/^(\d+)$/ and $limit_rate = int $1/1024, last; |
566
|
|
|
|
|
|
|
/^(\d+)[kK]$/ and $limit_rate = $1, last; |
567
|
|
|
|
|
|
|
/^(\d+)[mM]$/ and $limit_rate = 1024*$1, last; |
568
|
|
|
|
|
|
|
/^(\d+)[gG]$/ and $limit_rate = 1024*1024*$1, last; |
569
|
|
|
|
|
|
|
} |
570
|
|
|
|
|
|
|
$limit_rate; |
571
|
|
|
|
|
|
|
} |
572
|
|
|
|
|
|
|
|
573
|
|
|
|
|
|
|
sub sync_rsync { |
574
|
|
|
|
|
|
|
-x "/usr/bin/rsync" or die N("rsync is missing\n"); |
575
|
|
|
|
|
|
|
my $options = shift; |
576
|
|
|
|
|
|
|
$options = { dir => $options } if !ref $options; |
577
|
|
|
|
|
|
|
#- force download to be done in cachedir to avoid polluting cwd. |
578
|
|
|
|
|
|
|
(my $cwd) = getcwd() =~ /(.*)/; |
579
|
|
|
|
|
|
|
chdir($options->{dir}); |
580
|
|
|
|
|
|
|
my $limit_rate = _calc_limit_rate($options->{'limit-rate'}); |
581
|
|
|
|
|
|
|
foreach (@_) { |
582
|
|
|
|
|
|
|
my $count = 10; #- retry count on error (if file exists). |
583
|
|
|
|
|
|
|
my $basename = basename($_); |
584
|
|
|
|
|
|
|
my $file = m!^rsync://([^/]*::.*)! ? $1 : $_; |
585
|
|
|
|
|
|
|
propagate_sync_callback($options, 'start', $file); |
586
|
|
|
|
|
|
|
do { |
587
|
|
|
|
|
|
|
local $_; |
588
|
|
|
|
|
|
|
my $buf = ''; |
589
|
|
|
|
|
|
|
my $cmd = join(" ", "/usr/bin/rsync", |
590
|
|
|
|
|
|
|
($limit_rate ? "--bwlimit=$limit_rate" : @{[]}), |
591
|
|
|
|
|
|
|
($options->{quiet} ? qw(-q) : qw(--progress -v --no-human-readable)), |
592
|
|
|
|
|
|
|
($options->{compress} ? qw(-z) : @{[]}), |
593
|
|
|
|
|
|
|
($options->{ssh} ? qq(-e $options->{ssh}) : |
594
|
|
|
|
|
|
|
("--timeout=$CONNECT_TIMEOUT", |
595
|
|
|
|
|
|
|
"--contimeout=$CONNECT_TIMEOUT")), |
596
|
|
|
|
|
|
|
qw(--partial --no-whole-file --no-motd --copy-links), |
597
|
|
|
|
|
|
|
(defined $options->{'rsync-options'} ? split /\s+/, $options->{'rsync-options'} : @{[]}), |
598
|
|
|
|
|
|
|
"'$file' '$options->{dir}' 2>&1"); |
599
|
|
|
|
|
|
|
$options->{debug} and $options->{debug}($cmd); |
600
|
|
|
|
|
|
|
open(my $rsync, "$cmd |"); |
601
|
|
|
|
|
|
|
local $/ = \1; #- read input by only one char, this is slow but very nice (and it works!). |
602
|
|
|
|
|
|
|
local $_; |
603
|
|
|
|
|
|
|
while (<$rsync>) { |
604
|
|
|
|
|
|
|
$buf .= $_; |
605
|
|
|
|
|
|
|
if ($_ eq "\r" || $_ eq "\n") { |
606
|
|
|
|
|
|
|
if ($options->{callback}) { |
607
|
|
|
|
|
|
|
if (my ($percent, $speed) = $buf =~ /^\s*\d+\s+(\d+)%\s+(\S+)\s+/) { |
608
|
|
|
|
|
|
|
propagate_sync_callback($options, 'progress', $file, $percent, undef, undef, $speed); |
609
|
|
|
|
|
|
|
} else { |
610
|
|
|
|
|
|
|
$options->{debug} and $options->{debug}($buf); |
611
|
|
|
|
|
|
|
} |
612
|
|
|
|
|
|
|
} else { |
613
|
|
|
|
|
|
|
$options->{quiet} or print STDERR $buf; |
614
|
|
|
|
|
|
|
$options->{debug} and $options->{debug}($buf); |
615
|
|
|
|
|
|
|
} |
616
|
|
|
|
|
|
|
$buf = ''; |
617
|
|
|
|
|
|
|
} |
618
|
|
|
|
|
|
|
} |
619
|
|
|
|
|
|
|
close $rsync; |
620
|
|
|
|
|
|
|
} while ($? != 0 && --$count > 0 && -e $options->{dir} . "/$basename"); |
621
|
|
|
|
|
|
|
propagate_sync_callback($options, 'end', $file); |
622
|
|
|
|
|
|
|
} |
623
|
|
|
|
|
|
|
chdir $cwd; |
624
|
|
|
|
|
|
|
$? == 0 or _error('rsync'); |
625
|
|
|
|
|
|
|
} |
626
|
|
|
|
|
|
|
|
627
|
|
|
|
|
|
|
our $SSH_PATH; |
628
|
|
|
|
|
|
|
sub _init_ssh_path() { |
629
|
|
|
|
|
|
|
foreach (qw(/usr/bin/ssh /bin/ssh)) { |
630
|
|
|
|
|
|
|
-x $_ and $SSH_PATH = $_; |
631
|
|
|
|
|
|
|
next; |
632
|
|
|
|
|
|
|
} |
633
|
|
|
|
|
|
|
} |
634
|
|
|
|
|
|
|
|
635
|
|
|
|
|
|
|
#- Don't generate a tmp dir name, so when we restart urpmi, the old ssh |
636
|
|
|
|
|
|
|
#- connection can be reused |
637
|
|
|
|
|
|
|
our $SSH_CONTROL_DIR = $ENV{TMP} || $ENV{TMPDIR} || '/tmp'; |
638
|
|
|
|
|
|
|
our $SSH_CONTROL_OPTION; |
639
|
|
|
|
|
|
|
|
640
|
|
|
|
|
|
|
sub sync_ssh { |
641
|
|
|
|
|
|
|
$SSH_PATH or _init_ssh_path(); |
642
|
|
|
|
|
|
|
$SSH_PATH or die N("ssh is missing\n"); |
643
|
|
|
|
|
|
|
my $options = shift; |
644
|
|
|
|
|
|
|
$options = { dir => $options } if !ref $options; |
645
|
|
|
|
|
|
|
unless ($options->{'rsync-options'} =~ /(?:-e|--rsh)\b/) { |
646
|
|
|
|
|
|
|
my ($server, $user) = ('', getpwuid($<)); |
647
|
|
|
|
|
|
|
$_[0] =~ /((?:\w|\.)*):/ and $server = $1; |
648
|
|
|
|
|
|
|
$_[0] =~ /((?:\w|-)*)@/ and $user = $1; |
649
|
|
|
|
|
|
|
$SSH_CONTROL_OPTION = "-o 'ControlPath $SSH_CONTROL_DIR/ssh-urpmi-$$-%h_%p_%r' -o 'ControlMaster auto'"; |
650
|
|
|
|
|
|
|
if (start_ssh_master($server, $user)) { |
651
|
|
|
|
|
|
|
$options->{ssh} = qq("$SSH_PATH $SSH_CONTROL_OPTION"); |
652
|
|
|
|
|
|
|
} else { |
653
|
|
|
|
|
|
|
#- can't start master, use single connection |
654
|
|
|
|
|
|
|
$options->{ssh} = $SSH_PATH; |
655
|
|
|
|
|
|
|
} |
656
|
|
|
|
|
|
|
} |
657
|
|
|
|
|
|
|
sync_rsync($options, @_); |
658
|
|
|
|
|
|
|
} |
659
|
|
|
|
|
|
|
|
660
|
|
|
|
|
|
|
sub sync_prozilla { |
661
|
|
|
|
|
|
|
-x "/usr/bin/proz" or die N("prozilla is missing\n"); |
662
|
|
|
|
|
|
|
my $options = shift; |
663
|
|
|
|
|
|
|
$options = { dir => $options } if !ref $options; |
664
|
|
|
|
|
|
|
#- force download to be done in cachedir to avoid polluting cwd. |
665
|
|
|
|
|
|
|
(my $cwd) = getcwd() =~ /(.*)/; |
666
|
|
|
|
|
|
|
chdir $options->{dir}; |
667
|
|
|
|
|
|
|
my $proz_command = join(" ", map { "'$_'" } |
668
|
|
|
|
|
|
|
"/usr/bin/proz", |
669
|
|
|
|
|
|
|
"--no-curses", |
670
|
|
|
|
|
|
|
(defined $options->{'prozilla-options'} ? split /\s+/, $options->{'prozilla-options'} : @{[]}), |
671
|
|
|
|
|
|
|
@_ |
672
|
|
|
|
|
|
|
); |
673
|
|
|
|
|
|
|
my $ret = system($proz_command); |
674
|
|
|
|
|
|
|
chdir $cwd; |
675
|
|
|
|
|
|
|
if ($ret) { |
676
|
|
|
|
|
|
|
if ($? == -1) { |
677
|
|
|
|
|
|
|
die N("Couldn't execute prozilla\n"); |
678
|
|
|
|
|
|
|
} else { |
679
|
|
|
|
|
|
|
_error('prozilla'); |
680
|
|
|
|
|
|
|
} |
681
|
|
|
|
|
|
|
} |
682
|
|
|
|
|
|
|
} |
683
|
|
|
|
|
|
|
|
684
|
|
|
|
|
|
|
sub sync_aria2 { |
685
|
|
|
|
|
|
|
my ($urpm, $medium, $rel_files, $options) = @_; |
686
|
|
|
|
|
|
|
|
687
|
|
|
|
|
|
|
-x "/usr/bin/aria2c" or die N("aria2 is missing\n"); |
688
|
|
|
|
|
|
|
|
689
|
|
|
|
|
|
|
#- force download to be done in cachedir to avoid polluting cwd. |
690
|
|
|
|
|
|
|
(my $cwd) = getcwd() =~ /(.*)/; |
691
|
|
|
|
|
|
|
chdir $options->{dir}; |
692
|
|
|
|
|
|
|
|
693
|
|
|
|
|
|
|
my $stat_file = ($< ? $ENV{HOME} : '/root') . '/.aria2-adaptive-stats'; |
694
|
|
|
|
|
|
|
|
695
|
|
|
|
|
|
|
my $aria2c_command = join(" ", map { "'$_'" } |
696
|
|
|
|
|
|
|
"/usr/bin/aria2c", $options->{debug} ? ('--log', "$options->{dir}/.aria2.log") : @{[]}, |
697
|
|
|
|
|
|
|
'--auto-file-renaming=false', |
698
|
|
|
|
|
|
|
'--ftp-pasv', |
699
|
|
|
|
|
|
|
'--summary-interval=1', |
700
|
|
|
|
|
|
|
'--follow-metalink=mem', |
701
|
|
|
|
|
|
|
$medium->{mirrorlist} ? ( |
702
|
|
|
|
|
|
|
'--metalink-enable-unique-protocol=true', # do not try to connect to the same server using the same protocol |
703
|
|
|
|
|
|
|
'--metalink-preferred-protocol=http', # try http as first protocol as they're stateless and |
704
|
|
|
|
|
|
|
# will put less strain on ie. the ftp servers which connections |
705
|
|
|
|
|
|
|
# are statefull for, causing unhappy mirror admins complaining |
706
|
|
|
|
|
|
|
# about increase of connections, increasing resource usage. |
707
|
|
|
|
|
|
|
'--max-tries=5', # nb: not using $options->{retry} |
708
|
|
|
|
|
|
|
'--lowest-speed-limit=20K', "--timeout", 3, |
709
|
|
|
|
|
|
|
'--split=3', # maximum number of servers to use for one download |
710
|
|
|
|
|
|
|
'--uri-selector=adaptive', "--server-stat-if=$stat_file", "--server-stat-of=$stat_file", |
711
|
|
|
|
|
|
|
$options->{is_versioned} ? @{[]} : '--max-file-not-found=9', # number of not found errors on different servers before aborting file download |
712
|
|
|
|
|
|
|
'--connect-timeout=6', # $CONNECT_TIMEOUT, |
713
|
|
|
|
|
|
|
) : @{[]}, |
714
|
|
|
|
|
|
|
'-Z', '-j1', |
715
|
|
|
|
|
|
|
($options->{'limit-rate'} ? "--max-download-limit=" . $options->{'limit-rate'} : @{[]}), |
716
|
|
|
|
|
|
|
($options->{resume} ? "--continue" : "--allow-overwrite=true"), |
717
|
|
|
|
|
|
|
($options->{proxy} ? set_proxy({ type => "aria2", proxy => $options->{proxy} }) : @{[]}), |
718
|
|
|
|
|
|
|
($options->{"no-certificate-check"} ? "--check-certificate=false" : @{[]}), |
719
|
|
|
|
|
|
|
(defined $options->{'aria2-options'} ? split /\s+/, $options->{'aria2-options'} : @{[]}), |
720
|
|
|
|
|
|
|
_create_metalink_($urpm, $medium, $rel_files, $options)); |
721
|
|
|
|
|
|
|
|
722
|
|
|
|
|
|
|
$options->{debug} and $options->{debug}($aria2c_command); |
723
|
|
|
|
|
|
|
|
724
|
|
|
|
|
|
|
local $ENV{LC_ALL} = 'C'; |
725
|
|
|
|
|
|
|
my $aria2_pid = open(my $aria2, "$aria2c_command |"); |
726
|
|
|
|
|
|
|
|
727
|
|
|
|
|
|
|
_parse_aria2_output($options, $aria2, $aria2_pid, $medium, $rel_files); |
728
|
|
|
|
|
|
|
|
729
|
|
|
|
|
|
|
chdir $cwd; |
730
|
|
|
|
|
|
|
if (!close $aria2) { |
731
|
|
|
|
|
|
|
my $raw_msg = _error_msg('aria2'); |
732
|
|
|
|
|
|
|
my $want_retry; |
733
|
|
|
|
|
|
|
if (!$options->{is_retry} & $options->{is_versioned}) { |
734
|
|
|
|
|
|
|
$want_retry = 1; |
735
|
|
|
|
|
|
|
} else { |
736
|
|
|
|
|
|
|
my $msg = N("Failed to download %s", $rel_files->[0]); |
737
|
|
|
|
|
|
|
$want_retry = $options->{ask_retry} && $options->{ask_retry}($raw_msg, $msg); |
738
|
|
|
|
|
|
|
} |
739
|
|
|
|
|
|
|
if ($want_retry) { |
740
|
|
|
|
|
|
|
$options->{is_retry}++; |
741
|
|
|
|
|
|
|
$options->{debug} and $options->{debug}("retrying ($options->{is_retry})"); |
742
|
|
|
|
|
|
|
goto &sync_aria2; |
743
|
|
|
|
|
|
|
} |
744
|
|
|
|
|
|
|
die $raw_msg; |
745
|
|
|
|
|
|
|
} |
746
|
|
|
|
|
|
|
} |
747
|
|
|
|
|
|
|
|
748
|
|
|
|
|
|
|
sub _parse_aria2_output { |
749
|
|
|
|
|
|
|
my ($options, $aria2, $aria2_pid, $medium, $rel_files) = @_; |
750
|
|
|
|
|
|
|
|
751
|
|
|
|
|
|
|
my ($buf, $_total, $file) = ('', undef, undef); |
752
|
|
|
|
|
|
|
|
753
|
|
|
|
|
|
|
local $/ = \1; #- read input by only one char, this is slow but very nice (and it works!). |
754
|
|
|
|
|
|
|
local $_; |
755
|
|
|
|
|
|
|
|
756
|
|
|
|
|
|
|
while (<$aria2>) { |
757
|
|
|
|
|
|
|
if ($_ eq "\r" || $_ eq "\n") { |
758
|
|
|
|
|
|
|
$options->{debug}("aria2c: $buf") if $options->{debug}; |
759
|
|
|
|
|
|
|
if ($options->{callback}) { |
760
|
|
|
|
|
|
|
if (!defined($file) && @$rel_files) { |
761
|
|
|
|
|
|
|
$file = $medium->{mirrorlist} ? |
762
|
|
|
|
|
|
|
$medium->{mirrorlist} . ': ' . $medium->{'with-dir'} . "/$rel_files->[0]" : |
763
|
|
|
|
|
|
|
"$medium->{url}/$rel_files->[0]"; |
764
|
|
|
|
|
|
|
propagate_sync_callback($options, 'start', $file) |
765
|
|
|
|
|
|
|
if !$options->{is_retry}; |
766
|
|
|
|
|
|
|
} |
767
|
|
|
|
|
|
|
|
768
|
|
|
|
|
|
|
# aria2c 1.16 and beyond: |
769
|
|
|
|
|
|
|
# parses aria2c: [#2c8dae 496KiB/830KiB(59%) CN:1 DL:84KiB ETA:3s] |
770
|
|
|
|
|
|
|
# |
771
|
|
|
|
|
|
|
# using multiline mode and comments for better readability: |
772
|
|
|
|
|
|
|
# |
773
|
|
|
|
|
|
|
if ($buf =~ m! |
774
|
|
|
|
|
|
|
^\[\#[\dA-Fa-f]+ # match #2c8dae |
775
|
|
|
|
|
|
|
\s+ |
776
|
|
|
|
|
|
|
([\d\.]+\w*) # Match 496KiB |
777
|
|
|
|
|
|
|
/ |
778
|
|
|
|
|
|
|
([\d\.]+\w*) # Match 830KiB |
779
|
|
|
|
|
|
|
\s* \( (\d+) % \) # Match (59%) |
780
|
|
|
|
|
|
|
\s+ |
781
|
|
|
|
|
|
|
CN:(\S+) # Match CN:1 |
782
|
|
|
|
|
|
|
\s+ |
783
|
|
|
|
|
|
|
DL:(\S+) # Match DL:84KiB |
784
|
|
|
|
|
|
|
\s+ |
785
|
|
|
|
|
|
|
ETA:(\w+) |
786
|
|
|
|
|
|
|
\]$ |
787
|
|
|
|
|
|
|
!msx |
788
|
|
|
|
|
|
|
) |
789
|
|
|
|
|
|
|
{ |
790
|
|
|
|
|
|
|
my ($total, $percent, $speed, $eta) = ($2, $3, $5, $6); |
791
|
|
|
|
|
|
|
#- $1 = current downloaded size, $4 = connections |
792
|
|
|
|
|
|
|
if (propagate_sync_callback($options, 'progress', $file, $percent, $total, $eta, $speed) eq 'canceled') { |
793
|
|
|
|
|
|
|
kill 15, $aria2_pid; |
794
|
|
|
|
|
|
|
close $aria2; |
795
|
|
|
|
|
|
|
return; |
796
|
|
|
|
|
|
|
} |
797
|
|
|
|
|
|
|
} |
798
|
|
|
|
|
|
|
if ($buf =~ m!Download\scomplete:\s/!) { |
799
|
|
|
|
|
|
|
propagate_sync_callback($options, 'end', $file); |
800
|
|
|
|
|
|
|
shift @$rel_files; |
801
|
|
|
|
|
|
|
delete $options->{is_retry}; |
802
|
|
|
|
|
|
|
$file = undef; |
803
|
|
|
|
|
|
|
} elsif ($buf =~ /ERR\|(.*)/) { |
804
|
|
|
|
|
|
|
propagate_sync_callback($options, 'error', $file, $1); |
805
|
|
|
|
|
|
|
} |
806
|
|
|
|
|
|
|
} else { |
807
|
|
|
|
|
|
|
$options->{quiet} or print STDERR "$buf\n"; |
808
|
|
|
|
|
|
|
} |
809
|
|
|
|
|
|
|
$buf = ''; |
810
|
|
|
|
|
|
|
} else { |
811
|
|
|
|
|
|
|
$buf .= $_; |
812
|
|
|
|
|
|
|
} |
813
|
|
|
|
|
|
|
} |
814
|
|
|
|
|
|
|
} |
815
|
|
|
|
|
|
|
|
816
|
|
|
|
|
|
|
sub start_ssh_master { |
817
|
|
|
|
|
|
|
my ($server, $user) = @_; |
818
|
|
|
|
|
|
|
$server or return 0; |
819
|
|
|
|
|
|
|
if (!check_ssh_master($server, $user)) { |
820
|
|
|
|
|
|
|
system(qq($SSH_PATH -f -N $SSH_CONTROL_OPTION -M $user\@$server)); |
821
|
|
|
|
|
|
|
return ! $?; |
822
|
|
|
|
|
|
|
} |
823
|
|
|
|
|
|
|
return 1; |
824
|
|
|
|
|
|
|
} |
825
|
|
|
|
|
|
|
|
826
|
|
|
|
|
|
|
sub check_ssh_master { |
827
|
|
|
|
|
|
|
my ($server, $user) = @_; |
828
|
|
|
|
|
|
|
system(qq($SSH_PATH -q -f -N $SSH_CONTROL_OPTION $user\@$server -O check)); |
829
|
|
|
|
|
|
|
return ! $?; |
830
|
|
|
|
|
|
|
} |
831
|
|
|
|
|
|
|
|
832
|
|
|
|
|
|
|
END { |
833
|
|
|
|
|
|
|
#- remove ssh persistent connections |
834
|
|
|
|
|
|
|
foreach my $socket (glob "$SSH_CONTROL_DIR/ssh-urpmi-$$-*") { |
835
|
|
|
|
|
|
|
my ($server, $login) = $socket =~ /ssh-urpmi-\d+-([^_]+)_\d+_(.*)$/ or next; |
836
|
|
|
|
|
|
|
system($SSH_PATH, '-q', '-f', '-N', '-o', "ControlPath $socket", '-O', 'exit', "$login\@$server"); |
837
|
|
|
|
|
|
|
} |
838
|
|
|
|
|
|
|
} |
839
|
|
|
|
|
|
|
|
840
|
|
|
|
|
|
|
#- get the width of the terminal |
841
|
|
|
|
|
|
|
my $wchar = 79; |
842
|
|
|
|
|
|
|
if (-t *STDOUT) { |
843
|
|
|
|
|
|
|
eval { |
844
|
|
|
|
|
|
|
require Term::ReadKey; |
845
|
|
|
|
|
|
|
($wchar) = Term::ReadKey::GetTerminalSize(); |
846
|
|
|
|
|
|
|
--$wchar; |
847
|
|
|
|
|
|
|
}; |
848
|
|
|
|
|
|
|
} |
849
|
|
|
|
|
|
|
|
850
|
|
|
|
|
|
|
sub progress_text { |
851
|
|
|
|
|
|
|
my ($mode, $percent, $total, $eta, $speed) = @_; |
852
|
|
|
|
|
|
|
$mode eq 'progress' ? |
853
|
|
|
|
|
|
|
(defined $total && defined $eta ? |
854
|
|
|
|
|
|
|
N(" %s%% of %s completed, ETA = %s, speed = %s", $percent, $total, $eta, $speed) : |
855
|
|
|
|
|
|
|
N(" %s%% completed, speed = %s", $percent, $speed)) : ''; |
856
|
|
|
|
|
|
|
} |
857
|
|
|
|
|
|
|
|
858
|
|
|
|
|
|
|
=item sync_logger($mode, $file, $percent, $_total, $_eta, $_speed) |
859
|
|
|
|
|
|
|
|
860
|
|
|
|
|
|
|
Default logger (callback) suitable for sync operation on STDERR only. |
861
|
|
|
|
|
|
|
|
862
|
|
|
|
|
|
|
=cut |
863
|
|
|
|
|
|
|
|
864
|
|
|
|
|
|
|
sub sync_logger { |
865
|
|
|
|
|
|
|
my ($mode, $file, $percent, $total, $eta, $speed) = @_; |
866
|
|
|
|
|
|
|
if ($mode eq 'start') { |
867
|
|
|
|
|
|
|
print STDERR " $file\n"; |
868
|
|
|
|
|
|
|
} elsif ($mode eq 'progress') { |
869
|
|
|
|
|
|
|
my $text = progress_text($mode, $percent, $total, $eta, $speed); |
870
|
|
|
|
|
|
|
if (length($text) > $wchar) { $text = substr($text, 0, $wchar) } |
871
|
|
|
|
|
|
|
if (bytes::length($text) < $wchar) { |
872
|
|
|
|
|
|
|
# clearing more than needed in case the terminal is not handling utf8 and we have a utf8 string |
873
|
|
|
|
|
|
|
print STDERR $text, " " x ($wchar - bytes::length($text)), "\r"; |
874
|
|
|
|
|
|
|
} else { |
875
|
|
|
|
|
|
|
# clearing all the line first since we can't really know the "length" of the string |
876
|
|
|
|
|
|
|
print STDERR " " x $wchar, "\r", $text, "\r"; |
877
|
|
|
|
|
|
|
} |
878
|
|
|
|
|
|
|
} elsif ($mode eq 'end') { |
879
|
|
|
|
|
|
|
print STDERR " " x $wchar, "\r"; |
880
|
|
|
|
|
|
|
} elsif ($mode eq 'error') { |
881
|
|
|
|
|
|
|
#- error is 3rd argument, saved in $percent |
882
|
|
|
|
|
|
|
print STDERR N("...retrieving failed: %s", $percent), "\n"; |
883
|
|
|
|
|
|
|
} |
884
|
|
|
|
|
|
|
} |
885
|
|
|
|
|
|
|
|
886
|
|
|
|
|
|
|
=item requested_ftp_http_downloader($urpm, $medium) |
887
|
|
|
|
|
|
|
|
888
|
|
|
|
|
|
|
Return the downloader program to use (whether it pas provided on the |
889
|
|
|
|
|
|
|
command line or in the config file). |
890
|
|
|
|
|
|
|
|
891
|
|
|
|
|
|
|
=cut |
892
|
|
|
|
|
|
|
|
893
|
|
|
|
|
|
|
sub requested_ftp_http_downloader { |
894
|
|
|
|
|
|
|
my ($urpm, $medium) = @_; |
895
|
|
|
|
|
|
|
|
896
|
|
|
|
|
|
|
$urpm->{options}{downloader} || #- cmd-line switch |
897
|
|
|
|
|
|
|
$medium && $medium->{downloader} || |
898
|
|
|
|
|
|
|
$urpm->{global_config}{downloader} || ""; |
899
|
|
|
|
|
|
|
} |
900
|
|
|
|
|
|
|
|
901
|
|
|
|
|
|
|
sub parse_url_with_login { |
902
|
|
|
|
|
|
|
my ($url) = @_; |
903
|
|
|
|
|
|
|
$url =~ m!([^:]*)://([^/:]*)(:([^/:\@]*))?\@([^/]*)(.*)! && $1 ne 'ssh' && |
904
|
|
|
|
|
|
|
{ proto => $1, login => $2, password => $4, machine => $5, dir => $6 }; |
905
|
|
|
|
|
|
|
} |
906
|
|
|
|
|
|
|
sub url_obscuring_password { |
907
|
|
|
|
|
|
|
my ($url) = @_; |
908
|
|
|
|
|
|
|
my $u = parse_url_with_login($url); |
909
|
|
|
|
|
|
|
if ($u && $u->{password}) { |
910
|
|
|
|
|
|
|
sprintf('%s://xxx:xxx@%s%s', $u->{proto}, $u->{machine}, $u->{dir}); |
911
|
|
|
|
|
|
|
} else { |
912
|
|
|
|
|
|
|
$url; |
913
|
|
|
|
|
|
|
} |
914
|
|
|
|
|
|
|
} |
915
|
|
|
|
|
|
|
|
916
|
|
|
|
|
|
|
#- $medium can be undef |
917
|
|
|
|
|
|
|
sub _all_options { |
918
|
|
|
|
|
|
|
my ($urpm, $medium, $options) = @_; |
919
|
|
|
|
|
|
|
|
920
|
|
|
|
|
|
|
my %all_options = ( |
921
|
|
|
|
|
|
|
dir => "$urpm->{cachedir}/partial", |
922
|
|
|
|
|
|
|
proxy => get_proxy_($urpm), |
923
|
|
|
|
|
|
|
metalink => $medium->{mirrorlist}, |
924
|
|
|
|
|
|
|
$medium->{"disable-certificate-check"} ? "no-certificate-check" : @{[]}, |
925
|
|
|
|
|
|
|
$urpm->{debug} ? (debug => $urpm->{debug}) : @{[]}, |
926
|
|
|
|
|
|
|
%$options, |
927
|
|
|
|
|
|
|
); |
928
|
|
|
|
|
|
|
foreach my $cpt (qw(compress limit-rate retry wget-options curl-options rsync-options prozilla-options aria2-options metalink)) { |
929
|
|
|
|
|
|
|
$all_options{$cpt} = $urpm->{options}{$cpt} if defined $urpm->{options}{$cpt}; |
930
|
|
|
|
|
|
|
} |
931
|
|
|
|
|
|
|
\%all_options; |
932
|
|
|
|
|
|
|
} |
933
|
|
|
|
|
|
|
|
934
|
|
|
|
|
|
|
sub sync_rel { |
935
|
|
|
|
|
|
|
my ($urpm, $medium, $rel_files, %options) = @_; |
936
|
|
|
|
|
|
|
|
937
|
|
|
|
|
|
|
my @files = map { reduce_pathname("$medium->{url}/$_") } @$rel_files; |
938
|
|
|
|
|
|
|
|
939
|
|
|
|
|
|
|
my $files_text = join(' ', (use_metalink($urpm, $medium) ? ($medium->{mirrorlist}, $medium->{'with-dir'}) : url_obscuring_password($medium->{url})), @$rel_files); |
940
|
|
|
|
|
|
|
$urpm->{debug} and $urpm->{debug}(N("retrieving %s", $files_text)); |
941
|
|
|
|
|
|
|
|
942
|
|
|
|
|
|
|
my $all_options = _all_options($urpm, $medium, \%options); |
943
|
|
|
|
|
|
|
my @result_files = map { $all_options->{dir} . '/' . basename($_) } @$rel_files; |
944
|
|
|
|
|
|
|
unlink @result_files if $all_options->{preclean}; |
945
|
|
|
|
|
|
|
|
946
|
|
|
|
|
|
|
(my $cwd) = getcwd() =~ /(.*)/; |
947
|
|
|
|
|
|
|
eval { _sync_webfetch_raw($urpm, $medium, $rel_files, \@files, $all_options) }; |
948
|
|
|
|
|
|
|
my $err = $@; |
949
|
|
|
|
|
|
|
chdir $cwd; |
950
|
|
|
|
|
|
|
if (!$err) { |
951
|
|
|
|
|
|
|
$urpm->{log}(N("retrieved %s", $files_text)); |
952
|
|
|
|
|
|
|
\@result_files; |
953
|
|
|
|
|
|
|
} else { |
954
|
|
|
|
|
|
|
$urpm->{log}("error: $err"); |
955
|
|
|
|
|
|
|
# don't leave partial download |
956
|
|
|
|
|
|
|
unlink @result_files; |
957
|
|
|
|
|
|
|
undef; |
958
|
|
|
|
|
|
|
} |
959
|
|
|
|
|
|
|
} |
960
|
|
|
|
|
|
|
|
961
|
|
|
|
|
|
|
sub sync_rel_one { |
962
|
|
|
|
|
|
|
my ($urpm, $medium, $rel_file, %options) = @_; |
963
|
|
|
|
|
|
|
|
964
|
|
|
|
|
|
|
my $files = sync_rel($urpm, $medium, [$rel_file], %options) or return; |
965
|
|
|
|
|
|
|
$files->[0]; |
966
|
|
|
|
|
|
|
} |
967
|
|
|
|
|
|
|
|
968
|
|
|
|
|
|
|
=item sync_url($urpm, $url, %options) |
969
|
|
|
|
|
|
|
|
970
|
|
|
|
|
|
|
Retrieve a file from the network and return the local cached file path. |
971
|
|
|
|
|
|
|
|
972
|
|
|
|
|
|
|
=cut |
973
|
|
|
|
|
|
|
|
974
|
|
|
|
|
|
|
sub sync_url { |
975
|
|
|
|
|
|
|
my ($urpm, $url, %options) = @_; |
976
|
|
|
|
|
|
|
sync_rel_one($urpm, { url => dirname($url), disable_metalink => $options{disable_metalink} }, basename($url), %options); |
977
|
|
|
|
|
|
|
} |
978
|
|
|
|
|
|
|
|
979
|
|
|
|
|
|
|
sub sync_rel_to { |
980
|
|
|
|
|
|
|
my ($urpm, $medium, $rel_file, $dest_file, %options) = @_; |
981
|
|
|
|
|
|
|
|
982
|
|
|
|
|
|
|
my $files = sync_rel($urpm, $medium, [$rel_file], %options) or return undef; |
983
|
|
|
|
|
|
|
my $result_file = $files->[0]; |
984
|
|
|
|
|
|
|
$result_file ne $dest_file or rename($result_file, $dest_file) or return; |
985
|
|
|
|
|
|
|
$result_file; |
986
|
|
|
|
|
|
|
} |
987
|
|
|
|
|
|
|
|
988
|
|
|
|
|
|
|
=item get_content($urpm, $url, %o_options) |
989
|
|
|
|
|
|
|
|
990
|
|
|
|
|
|
|
Retrieve a file and return its content. |
991
|
|
|
|
|
|
|
|
992
|
|
|
|
|
|
|
=cut |
993
|
|
|
|
|
|
|
|
994
|
|
|
|
|
|
|
sub get_content { |
995
|
|
|
|
|
|
|
my ($urpm, $url, %o_options) = @_; |
996
|
|
|
|
|
|
|
|
997
|
|
|
|
|
|
|
my $file = sync_url($urpm, $url, %o_options, quiet => 1, preclean => 1) or return; |
998
|
|
|
|
|
|
|
|
999
|
|
|
|
|
|
|
my @l = cat_($file); |
1000
|
|
|
|
|
|
|
unlink $file; |
1001
|
|
|
|
|
|
|
|
1002
|
|
|
|
|
|
|
wantarray() ? @l : join('', @l); |
1003
|
|
|
|
|
|
|
} |
1004
|
|
|
|
|
|
|
|
1005
|
|
|
|
|
|
|
|
1006
|
|
|
|
|
|
|
#- syncing algorithms. |
1007
|
|
|
|
|
|
|
#- |
1008
|
|
|
|
|
|
|
#- nb: $files is constructed from $rel_files using $medium |
1009
|
|
|
|
|
|
|
sub _sync_webfetch_raw { |
1010
|
|
|
|
|
|
|
my ($urpm, $medium, $rel_files, $files, $options) = @_; |
1011
|
|
|
|
|
|
|
|
1012
|
|
|
|
|
|
|
#- currently ftp and http protocols are managed by curl or wget, |
1013
|
|
|
|
|
|
|
#- ssh and rsync protocols are managed by rsync *AND* ssh. |
1014
|
|
|
|
|
|
|
my $proto = urpm::protocol_from_url($medium->{url}) or die N("unknown protocol defined for %s", $medium->{url}); |
1015
|
|
|
|
|
|
|
|
1016
|
|
|
|
|
|
|
if ($proto eq 'file') { |
1017
|
|
|
|
|
|
|
my @l = map { urpm::file_from_local_url($_) } @$files; |
1018
|
|
|
|
|
|
|
eval { sync_file($options, @l) }; |
1019
|
|
|
|
|
|
|
$urpm->{fatal}(10, $@) if $@; |
1020
|
|
|
|
|
|
|
} elsif ($proto eq 'rsync') { |
1021
|
|
|
|
|
|
|
sync_rsync($options, @$files); |
1022
|
|
|
|
|
|
|
} elsif (member($proto, 'ftp', 'http', 'https') || $options->{metalink}) { |
1023
|
|
|
|
|
|
|
|
1024
|
|
|
|
|
|
|
my $preferred = preferred_downloader($urpm, $medium, \$options->{metalink}); |
1025
|
|
|
|
|
|
|
if ($preferred eq 'aria2') { |
1026
|
|
|
|
|
|
|
sync_aria2($urpm, $medium, $rel_files, $options); |
1027
|
|
|
|
|
|
|
} else { |
1028
|
|
|
|
|
|
|
my $sync = $urpm::download::{"sync_$preferred"} or die N("no webfetch found, supported webfetch are: %s\n", join(", ", urpm::download::ftp_http_downloaders())); |
1029
|
|
|
|
|
|
|
|
1030
|
|
|
|
|
|
|
my @l = @$files; |
1031
|
|
|
|
|
|
|
while (@l) { |
1032
|
|
|
|
|
|
|
my $half_MAX_ARG = 131072 / 2; |
1033
|
|
|
|
|
|
|
# restrict the number of elements so that it fits on cmdline of curl/wget/proz/aria2c |
1034
|
|
|
|
|
|
|
my $n = 0; |
1035
|
|
|
|
|
|
|
for (my $len = 0; $n < @l && $len < $half_MAX_ARG; $len += length($l[$n++])) {} |
1036
|
|
|
|
|
|
|
$sync->($options, splice(@l, 0, $n)); |
1037
|
|
|
|
|
|
|
} |
1038
|
|
|
|
|
|
|
} |
1039
|
|
|
|
|
|
|
} elsif ($proto eq 'ssh') { |
1040
|
|
|
|
|
|
|
my @ssh_files = map { m!^ssh://([^/]*)(.*)! ? "$1:$2" : @{[]} } @$files; |
1041
|
|
|
|
|
|
|
sync_ssh($options, @ssh_files); |
1042
|
|
|
|
|
|
|
} else { |
1043
|
|
|
|
|
|
|
die N("unable to handle protocol: %s", $proto); |
1044
|
|
|
|
|
|
|
} |
1045
|
|
|
|
|
|
|
} |
1046
|
|
|
|
|
|
|
|
1047
|
|
|
|
|
|
|
sub _take_n_elem { |
1048
|
|
|
|
|
|
|
my ($n, @l) = @_; |
1049
|
|
|
|
|
|
|
@l < $n ? @l : @l[0 .. $n-1]; |
1050
|
|
|
|
|
|
|
} |
1051
|
|
|
|
|
|
|
|
1052
|
|
|
|
|
|
|
sub _create_one_metalink_line { |
1053
|
|
|
|
|
|
|
my ($medium, $mirror, $rel_file, $counter) = @_; |
1054
|
|
|
|
|
|
|
|
1055
|
|
|
|
|
|
|
my $type = urpm::protocol_from_url($mirror->{url}); |
1056
|
|
|
|
|
|
|
|
1057
|
|
|
|
|
|
|
# If more than 100 mirrors, give all the remaining mirrors a priority of 0 |
1058
|
|
|
|
|
|
|
my $preference = max(0, 100 - $counter); |
1059
|
|
|
|
|
|
|
|
1060
|
|
|
|
|
|
|
my @options = (qq(type="$type"), qq(preference="$preference")); |
1061
|
|
|
|
|
|
|
# Not supported in metalinks |
1062
|
|
|
|
|
|
|
#if (@$list[$i]->{bw}) { |
1063
|
|
|
|
|
|
|
# push @options, 'bandwidth="' . @$list[$i]->{bw} . '"'; |
1064
|
|
|
|
|
|
|
# } |
1065
|
|
|
|
|
|
|
# Supported in metalinks, but no longer used in mirror list..? |
1066
|
|
|
|
|
|
|
if ($mirror->{connections}) { |
1067
|
|
|
|
|
|
|
push @options, qq(maxconnections="$mirror->{connections}"); |
1068
|
|
|
|
|
|
|
} |
1069
|
|
|
|
|
|
|
push @options, 'location="' . lc($mirror->{zone}) . '"'; |
1070
|
|
|
|
|
|
|
my $base = urpm::mirrors::_add__with_dir($mirror->{url}, $medium->{'with-dir'}); |
1071
|
|
|
|
|
|
|
sprintf('%s/%s', join(' ', @options), $base, $rel_file); |
1072
|
|
|
|
|
|
|
} |
1073
|
|
|
|
|
|
|
|
1074
|
|
|
|
|
|
|
sub _create_metalink_ { |
1075
|
|
|
|
|
|
|
my ($urpm, $medium, $rel_files, $options) = @_; |
1076
|
|
|
|
|
|
|
# Don't create a metalink when downloading mirror list |
1077
|
|
|
|
|
|
|
$medium or return; |
1078
|
|
|
|
|
|
|
|
1079
|
|
|
|
|
|
|
# only use the 8 best mirrors, then we let aria2 choose |
1080
|
|
|
|
|
|
|
require urpm::mirrors; |
1081
|
|
|
|
|
|
|
my @mirrors = $medium->{mirrorlist} ? (map { |
1082
|
|
|
|
|
|
|
# aria2 doesn't handle rsync |
1083
|
|
|
|
|
|
|
my @l = grep { urpm::protocol_from_url($_->{url}) ne 'rsync' } @$_; |
1084
|
|
|
|
|
|
|
_take_n_elem(8, @l); |
1085
|
|
|
|
|
|
|
} urpm::mirrors::list_urls($urpm, $medium, '')) : { url => $medium->{url} }; |
1086
|
|
|
|
|
|
|
|
1087
|
|
|
|
|
|
|
my $metalinkfile = "$urpm->{cachedir}/$options->{media}.metalink"; |
1088
|
|
|
|
|
|
|
# Even if not required by metalink spec, this line is needed at top of |
1089
|
|
|
|
|
|
|
# metalink file, otherwise aria2 won't be able to autodetect it.. |
1090
|
|
|
|
|
|
|
my @metalink = ( |
1091
|
|
|
|
|
|
|
'', |
1092
|
|
|
|
|
|
|
'', |
1093
|
|
|
|
|
|
|
'', |
1094
|
|
|
|
|
|
|
); |
1095
|
|
|
|
|
|
|
|
1096
|
|
|
|
|
|
|
foreach my $rel_file (@$rel_files) { |
1097
|
|
|
|
|
|
|
my $i = 0; |
1098
|
|
|
|
|
|
|
my @lines = map { |
1099
|
|
|
|
|
|
|
$i++; |
1100
|
|
|
|
|
|
|
_create_one_metalink_line($medium, $_, $rel_file, $i); |
1101
|
|
|
|
|
|
|
} @mirrors; |
1102
|
|
|
|
|
|
|
|
1103
|
|
|
|
|
|
|
push @metalink, map { "\t$_" } |
1104
|
|
|
|
|
|
|
sprintf('', basename($rel_file)), |
1105
|
|
|
|
|
|
|
(map { "\t$_" } @lines), |
1106
|
|
|
|
|
|
|
''; |
1107
|
|
|
|
|
|
|
} |
1108
|
|
|
|
|
|
|
push @metalink, '', ''; |
1109
|
|
|
|
|
|
|
|
1110
|
|
|
|
|
|
|
output_safe($metalinkfile, join('', map { "$_\n" } @metalink)); |
1111
|
|
|
|
|
|
|
$metalinkfile; |
1112
|
|
|
|
|
|
|
} |
1113
|
|
|
|
|
|
|
|
1114
|
|
|
|
|
|
|
1; |
1115
|
|
|
|
|
|
|
|
1116
|
|
|
|
|
|
|
|
1117
|
|
|
|
|
|
|
=back |
1118
|
|
|
|
|
|
|
|
1119
|
|
|
|
|
|
|
=head1 COPYRIGHT |
1120
|
|
|
|
|
|
|
|
1121
|
|
|
|
|
|
|
Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005 MandrakeSoft SA |
1122
|
|
|
|
|
|
|
|
1123
|
|
|
|
|
|
|
Copyright (C) 2005-2010 Mandriva SA |
1124
|
|
|
|
|
|
|
|
1125
|
|
|
|
|
|
|
Copyright (C) 2011-2017 Mageia |
1126
|
|
|
|
|
|
|
|
1127
|
|
|
|
|
|
|
=cut |