dpkg (1.18.25) stretch; urgency=medium
[dpkg] / dselect / methods / Dselect / Ftp.pm
CommitLineData
1479465f
GJ
1# This program is free software; you can redistribute it and/or modify
2# it under the terms of the GNU General Public License as published by
3# the Free Software Foundation; version 2 of the License.
4#
5# This program is distributed in the hope that it will be useful,
6# but WITHOUT ANY WARRANTY; without even the implied warranty of
7# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
8# GNU General Public License for more details.
9#
10# You should have received a copy of the GNU General Public License
11# along with this program. If not, see <https://www.gnu.org/licenses/>.
12
13package Dselect::Ftp;
14
15use strict;
16use warnings;
17
18our $VERSION = '0.02';
19our @EXPORT = qw(
20 %CONFIG
21 yesno
22 nb
23 do_connect
24 do_mdtm
25 view_mirrors
26 add_site
27 edit_site
28 edit_config
29 read_config
30 store_config
31);
32
33use Exporter qw(import);
34use Carp;
35use Net::FTP;
36use Data::Dumper;
37
38my %CONFIG;
39
40sub nb {
41 my $nb = shift;
42 if ($nb > 1024**2) {
43 return sprintf('%.2fM', $nb / 1024**2);
44 } elsif ($nb > 1024) {
45 return sprintf('%.2fk', $nb / 1024);
46 } else {
47 return sprintf('%.2fb', $nb);
48 }
49
50}
51
52sub read_config {
53 my $vars = shift;
54 my ($code, $conf);
55
56 local($/);
57 open(my $vars_fh, '<', $vars)
58 or die "couldn't open '$vars': $!\n" .
59 "Try to relaunch the 'Access' step in dselect, thanks.\n";
60 $code = <$vars_fh>;
61 close $vars_fh;
62
63 my $VAR1; ## no critic (Variables::ProhibitUnusedVariables)
64 $conf = eval $code;
65 die "couldn't eval $vars content: $@\n" if ($@);
66 if (ref($conf) =~ /HASH/) {
67 foreach (keys %{$conf}) {
68 $CONFIG{$_} = $conf->{$_};
69 }
70 } else {
71 print "Bad $vars file : removing it.\n";
72 print "Please relaunch the 'Access' step in dselect. Thanks.\n";
73 unlink $vars;
74 exit 0;
75 }
76}
77
78sub store_config {
79 my $vars = shift;
80
81 # Check that config is completed
82 return if not $CONFIG{done};
83
84 open(my $vars_fh, '>', $vars)
85 or die "couldn't open $vars in write mode: $!\n";
86 print { $vars_fh } Dumper(\%CONFIG);
87 close $vars_fh;
88}
89
90sub view_mirrors {
91 print <<'MIRRORS';
92Please see <http://ftp.debian.org/debian/README.mirrors.txt> for a current
93list of Debian mirror sites.
94MIRRORS
95}
96
97sub edit_config {
98 my $methdir = shift;
99 my $i;
100
101 #Get a config for ftp sites
102 while(1) {
103 $i = 1;
104 print "\n\nList of selected ftp sites :\n";
105 foreach (@{$CONFIG{site}}) {
106 print "$i. ftp://$_->[0]$_->[1] @{$_->[2]}\n";
107 $i++;
108 }
109 print "\nEnter a command (a=add e=edit d=delete q=quit m=mirror list) \n";
110 print 'eventually followed by a site number : ';
111 chomp($_ = <STDIN>);
112 /q/i && last;
113 /a/i && add_site();
114 /d\s*(\d+)/i &&
115 do {
116 splice(@{$CONFIG{site}}, $1 - 1, 1) if ($1 <= @{$CONFIG{site}});
117 next;};
118 /e\s*(\d+)/i &&
119 do {
120 edit_site($CONFIG{site}[$1 - 1]) if ($1 <= @{$CONFIG{site}});
121 next; };
122 /m/i && view_mirrors();
123 }
124
125 print "\n";
126 $CONFIG{use_auth_proxy} = yesno($CONFIG{use_auth_proxy} ? 'y' : 'n',
127 'Go through an authenticated proxy');
128
129 if ($CONFIG{use_auth_proxy}) {
130 print "\nEnter proxy hostname [$CONFIG{proxyhost}] : ";
131 chomp($_ = <STDIN>);
132 $CONFIG{proxyhost} = $_ || $CONFIG{proxyhost};
133
134 print "\nEnter proxy log name [$CONFIG{proxylogname}] : ";
135 chomp($_ = <STDIN>);
136 $CONFIG{proxylogname} = $_ || $CONFIG{proxylogname};
137
138 print "\nEnter proxy password [$CONFIG{proxypassword}] : ";
139 chomp ($_ = <STDIN>);
140 $CONFIG{proxypassword} = $_ || $CONFIG{proxypassword};
141 }
142
143 print "\nEnter directory to download binary package files to\n";
144 print "(relative to $methdir)\n";
145 while(1) {
146 print "[$CONFIG{dldir}] : ";
147 chomp($_ = <STDIN>);
148 s{/$}{};
149 $CONFIG{dldir} = $_ if ($_);
150 last if -d "$methdir/$CONFIG{dldir}";
151 print "$methdir/$CONFIG{dldir} is not a directory !\n";
152 }
153}
154
155sub add_site {
156 my $pas = 1;
157 my $user = 'anonymous';
158 my $email = qx(whoami);
159 chomp $email;
160 $email .= '@' . qx(cat /etc/mailname || dnsdomainname);
161 chomp $email;
162 my $dir = '/debian';
163
164 push (@{$CONFIG{site}}, [ '', $dir, [ 'dists/stable/main',
165 'dists/stable/contrib',
166 'dists/stable/non-free' ],
167 $pas, $user, $email ]);
168 edit_site($CONFIG{site}[@{$CONFIG{site}} - 1]);
169}
170
171sub edit_site {
172 my $site = shift;
173
174 local($_);
175
176 print "\nEnter ftp site [$site->[0]] : ";
177 chomp($_ = <STDIN>);
178 $site->[0] = $_ || $site->[0];
179
180 print "\nUse passive mode [" . ($site->[3] ? 'y' : 'n') . '] : ';
181 chomp($_ = <STDIN>);
182 $site->[3] = (/y/i ? 1 : 0) if ($_);
183
184 print "\nEnter username [$site->[4]] : ";
185 chomp($_ = <STDIN>);
186 $site->[4] = $_ || $site->[4];
187
188 print <<'EOF';
189
190If you're using anonymous ftp to retrieve files, enter your email
191address for use as a password. Otherwise enter your password,
192or "?" if you want dselect-ftp to prompt you each time.
193
194EOF
195
196 print "Enter password [$site->[5]] : ";
197 chomp($_ = <STDIN>);
198 $site->[5] = $_ || $site->[5];
199
200 print "\nEnter debian directory [$site->[1]] : ";
201 chomp($_ = <STDIN>);
202 $site->[1] = $_ || $site->[1];
203
204 print "\nEnter space separated list of distributions to get\n";
205 print "[@{$site->[2]}] : ";
206 chomp($_ = <STDIN>);
207 $site->[2] = [ split(/\s+/) ] if $_;
208}
209
210sub yesno($$) {
211 my ($d, $msg) = @_;
212
213 my ($res, $r);
214 $r = -1;
215 $r = 0 if $d eq 'n';
216 $r = 1 if $d eq 'y';
217 croak 'incorrect usage of yesno, stopped' if $r == -1;
218 while (1) {
219 print $msg, " [$d]: ";
220 $res = <STDIN>;
221 $res =~ /^[Yy]/ and return 1;
222 $res =~ /^[Nn]/ and return 0;
223 $res =~ /^[ \t]*$/ and return $r;
224 print "Please enter one of the letters 'y' or 'n'\n";
225 }
226}
227
228##############################
229
230sub do_connect {
231 my($ftpsite,$username,$pass,$ftpdir,$passive,
232 $useproxy,$proxyhost,$proxylogname,$proxypassword) = @_;
233
234 my($rpass,$remotehost,$remoteuser,$ftp);
235
236 TRY_CONNECT:
237 while(1) {
238 my $exit = 0;
239
240 if ($useproxy) {
241 $remotehost = $proxyhost;
242 $remoteuser = $username . '@' . $ftpsite;
243 } else {
244 $remotehost = $ftpsite;
245 $remoteuser = $username;
246 }
247 print "Connecting to $ftpsite...\n";
248 $ftp = Net::FTP->new($remotehost, Passive => $passive);
249 if(!$ftp || !$ftp->ok) {
250 print "Failed to connect\n";
251 $exit=1;
252 }
253 if (!$exit) {
254# $ftp->debug(1);
255 if ($useproxy) {
256 print "Login on $proxyhost...\n";
257 $ftp->_USER($proxylogname);
258 $ftp->_PASS($proxypassword);
259 }
260 print "Login as $username...\n";
261 if ($pass eq '?') {
262 print 'Enter password for ftp: ';
263 system('stty', '-echo');
264 $rpass = <STDIN>;
265 chomp $rpass;
266 print "\n";
267 system('stty', 'echo');
268 } else {
269 $rpass = $pass;
270 }
271 if(!$ftp->login($remoteuser, $rpass))
272 { print $ftp->message() . "\n"; $exit=1; }
273 }
274 if (!$exit) {
275 print "Setting transfer mode to binary...\n";
276 if(!$ftp->binary()) { print $ftp->message . "\n"; $exit=1; }
277 }
278 if (!$exit) {
279 print "Cd to '$ftpdir'...\n";
280 if(!$ftp->cwd($ftpdir)) { print $ftp->message . "\n"; $exit=1; }
281 }
282
283 if ($exit) {
284 if (yesno ('y', 'Retry connection at once')) {
285 next TRY_CONNECT;
286 } else {
287 die 'error';
288 }
289 }
290
291 last TRY_CONNECT;
292 }
293
294# if(!$ftp->pasv()) { print $ftp->message . "\n"; die 'error'; }
295
296 return $ftp;
297}
298
299##############################
300
301# assume server supports MDTM - will be adjusted if needed
302my $has_mdtm = 1;
303
304my %months = ('Jan', 0,
305 'Feb', 1,
306 'Mar', 2,
307 'Apr', 3,
308 'May', 4,
309 'Jun', 5,
310 'Jul', 6,
311 'Aug', 7,
312 'Sep', 8,
313 'Oct', 9,
314 'Nov', 10,
315 'Dec', 11);
316
317my $ls_l_re = qr<
318 ([^ ]+\ *){5} # Perms, Links, User, Group, Size
319 [^ ]+ # Blanks
320 \ ([A-Z][a-z]{2}) # Month name (abbreviated)
321 \ ([0-9 ][0-9]) # Day of month
322 \ ([0-9 ][0-9][:0-9][0-9]{2}) # Filename
323>x;
324
325sub do_mdtm {
326 my ($ftp, $file) = @_;
327 my ($time);
328
329 #if ($has_mdtm) {
330 $time = $ftp->mdtm($file);
331# my $code = $ftp->code();
332# my $message = $ftp->message();
333# print " [ $code: $message ] ";
334 if ($ftp->code() == 502 || # MDTM not implemented
335 $ftp->code() == 500) { # command not understood (SUN firewall)
336 $has_mdtm = 0;
337 } elsif (!$ftp->ok()) {
338 return;
339 }
340 #}
341
342 if (! $has_mdtm) {
343 require Time::Local;
344
345 my @files = $ftp->dir($file);
346 if (($#files == -1) ||
347 ($ftp->code == 550)) { # No such file or directory
348 return;
349 }
350
351# my $code = $ftp->code();
352# my $message = $ftp->message();
353# print " [ $code: $message ] ";
354
355# print "[$#files]";
356
357 # get the date components from the output of 'ls -l'
358 if ($files[0] =~ $ls_l_re) {
359
360 my($month_name, $day, $year_or_time, $month, $hours, $minutes,
361 $year);
362
363 # what we can read
364 $month_name = $2;
365 $day = 0 + $3;
366 $year_or_time = $4;
367
368 # translate the month name into number
369 $month = $months{$month_name};
370
371 # recognize time or year, and compute missing one
372 if ($year_or_time =~ /([0-9]{2}):([0-9]{2})/) {
373 $hours = 0 + $1; $minutes = 0 + $2;
374 my @this_date = gmtime(time());
375 my $this_month = $this_date[4];
376 my $this_year = $this_date[5];
377 if ($month > $this_month) {
378 $year = $this_year - 1;
379 } else {
380 $year = $this_year;
381 }
382 } elsif ($year_or_time =~ / [0-9]{4}/) {
383 $hours = 0; $minutes = 0;
384 $year = $year_or_time - 1900;
385 } else {
386 die 'cannot parse year-or-time';
387 }
388
389 # build a system time
390 $time = Time::Local::timegm(0, $minutes, $hours, $day, $month, $year);
391 } else {
392 die 'regex match failed on LIST output';
393 }
394 }
395
396 return $time;
397}
398
3991;
400
401__END__