Handle hostnames with upper-case letters
[webmin.git] / webmin / upgrade.cgi
1 #!/usr/local/bin/perl
2 # upgrade.cgi
3 # Upgrade webmin if possible
4
5 require './webmin-lib.pl';
6 &foreign_require("proc", "proc-lib.pl");
7 &foreign_require("acl", "acl-lib.pl");
8 &ReadParseMime();
9
10 $| = 1;
11 $theme_no_table = 1;
12 &ui_print_header(undef, $text{'upgrade_title'}, "");
13
14 # Save this CGI from being killed by the upgrade
15 $SIG{'TERM'} = 'IGNORE';
16
17 if ($in{'source'} == 0) {
18         # from local file
19         &error_setup(&text('upgrade_err1', $in{'file'}));
20         $file = $in{'file'};
21         if (!-r $file) { &inst_error($text{'upgrade_efile'}); }
22         if ($file =~ /webmin-(\d+\.\d+)/) {
23                 $version = $1;
24                 }
25         if (!$in{'force'}) {
26                 if ($version == &get_webmin_version()) {
27                         &inst_error(&text('upgrade_elatest', $version));
28                         }
29                 elsif ($version <= &get_webmin_version()) {
30                         &inst_error(&text('upgrade_eversion', $version));
31                         }
32                 }
33         }
34 elsif ($in{'source'} == 1) {
35         # from uploaded file
36         &error_setup($text{'upgrade_err2'});
37         $file = &transname();
38         $need_unlink = 1;
39         if ($no_upload) {
40                 &inst_error($text{'upgrade_ebrowser'});
41                 }
42         open(MOD, ">$file");
43         print MOD $in{'upload'};
44         close(MOD);
45         if ($in{'upload_filename'} =~ /webmin-(\d+\.\d+)/) {
46                 $version = $1;
47                 }
48         }
49 elsif ($in{'source'} == 2) {
50         # find latest version at www.webmin.com by looking at index page
51         &error_setup($text{'upgrade_err3'});
52         ($ok, $version) = &get_latest_webmin_version();
53         $ok || &inst_error($version);
54         if (!$in{'force'}) {
55                 if ($version == &get_webmin_version()) {
56                         &inst_error(&text('upgrade_elatest', $version));
57                         }
58                 elsif ($version <= &get_webmin_version()) {
59                         &inst_error(&text('upgrade_eversion', $version));
60                         }
61                 }
62         if ($in{'mode'} eq 'rpm') {
63                 # Downloading RPM
64                 $progress_callback_url = &convert_osdn_url(
65                     "http://$osdn_host/webadmin/webmin-$version-1.noarch.rpm");
66                 }
67         elsif ($in{'mode'} eq 'deb') {
68                 # Downloading Debian package
69                 $progress_callback_url = &convert_osdn_url(
70                     "http://$osdn_host/webadmin/webmin_${version}_all.deb");
71                 }
72         elsif ($in{'mode'} eq 'solaris-pkg') {
73                 # Downloading my Solaris package
74                 $progress_callback_url = &convert_osdn_url(
75                     "http://$osdn_host/webadmin/webmin-$version.pkg.gz");
76                 }
77         else {
78                 # Downloading tar.gz file
79                 $progress_callback_url = &convert_osdn_url(
80                         "http://$osdn_host/webadmin/webmin-$version.tar.gz");
81                 }
82         ($host, $port, $page, $ssl) = &parse_http_url($progress_callback_url);
83         $file = &transname();
84         &http_download($host, $port, $page, $file, \$error,
85                        \&progress_callback, $ssl);
86         $error && &inst_error($error);
87         $need_unlink = 1;
88         }
89 elsif ($in{'source'} == 5) {
90         # Download from some URL
91         &error_setup(&text('upgrade_err5', $in{'url'}));
92         $file = &transname();
93         $in{'url'} = &convert_osdn_url($in{'url'});
94         $progress_callback_url = $in{'url'};
95         if ($in{'url'} =~ /^(http|https):\/\/([^\/]+)(\/.*)$/) {
96                 $ssl = $1 eq 'https';
97                 $host = $2; $page = $3; $port = $ssl ? 443 : 80;
98                 if ($host =~ /^(.*):(\d+)$/) { $host = $1; $port = $2; }
99                 &http_download($host, $port, $page, $file, \$error,
100                                \&progress_callback, $ssl);
101                 }
102         elsif ($in{'url'} =~ /^ftp:\/\/([^\/]+)(:21)?\/(.*)$/) {
103                 $host = $1; $ffile = $3;
104                 &ftp_download($host, $ffile, $file,
105                               \$error, \&progress_callback);
106                 }
107         else { &inst_error($text{'upgrade_eurl'}); }
108         $need_unlink = 1;
109         $error && &inst_error($error);
110         if ($in{'url'} =~ /webmin-(\d+\.\d+)/) {
111                 $version = $1;
112                 }
113         }
114 elsif ($in{'source'} == 3) {
115         # Get the latest version from Caldera with cupdate
116         &redirect("/cupdate/");
117         }
118 elsif ($in{'source'} == 4) {
119         # Just run the command  emerge webmin
120         &error_setup(&text('upgrade_err4'));
121         $file = "webmin";
122         $need_unlink = 0;
123         }
124 $qfile = quotemeta($file);
125
126 # Import the signature for RPM
127 if ($in{'mode'} eq 'rpm') {
128         system("rpm --import $module_root_directory/jcameron-key.asc >/dev/null 2>&1");
129         }
130
131 # Check the signature if possible
132 if ($in{'sig'}) {
133         # Check the package signature
134         ($ec, $emsg) = &gnupg_setup();
135         if (!$ec) {
136                 if ($in{'mode'} eq 'rpm') {
137                         # Use rpm's gpg signature verification
138                         my $out = `rpm --checksig $qfile 2>&1`;
139                         if ($?) {
140                                 $ec = 3;
141                                 $emsg = &text('upgrade_echecksig',
142                                               "<pre>$out</pre>");
143                                 }
144                         }
145                 else {
146                         # Do a manual signature check
147                         if ($in{'source'} == 2) {
148                                 # Download the key for this tar.gz
149                                 my ($sigtemp, $sigerror);
150                                 &http_download($update_host, $update_port, "/download/sigs/webmin-$version.tar.gz-sig.asc", \$sigtemp, \$sigerror);
151                                 if ($sigerror) {
152                                         $ec = 4;
153                                         $emsg = &text('upgrade_edownsig',
154                                                       $sigerror);
155                                         }
156                                 else {
157                                         my $data =&read_file_contents($file);
158                                         my ($vc, $vmsg) =
159                                             &verify_data($data, $sigtemp);
160                                         if ($vc > 1) {
161                                                 $ec = 3;
162                                                 $emsg = &text(
163                                                     "upgrade_everify$vc",
164                                                     &html_escape($vmsg));
165                                                 }
166                                         }
167                                 }
168                         else {
169                                 $emsg = $text{'upgrade_nosig'};
170                                 }
171                         }
172                 }
173
174         # Tell the user about any GnuPG error
175         if ($ec) {
176                 &inst_error($emsg);
177                 }
178         elsif ($emsg) {
179                 print "$emsg<p>\n";
180                 }
181         else {
182                 print "$text{'upgrade_sigok'}<p>\n";
183                 }
184         }
185 else {
186         print "$text{'upgrade_nocheck'}<p>\n";
187         }
188
189 if ($in{'mode'} ne 'gentoo') {
190         # gunzip the file if needed
191         open(FILE, $file);
192         read(FILE, $two, 2);
193         close(FILE);
194         if ($two eq "\037\213") {
195                 if (!&has_command("gunzip")) {
196                         &inst_error($text{'upgrade_egunzip'});
197                         }
198                 $newfile = &transname();
199                 $out = `gunzip -c $qfile 2>&1 >$newfile`;
200                 if ($?) {
201                         unlink($newfile);
202                         &inst_error(&text('upgrade_egzip', "<tt>$out</tt>"));
203                         }
204                 unlink($file) if ($need_unlink);
205                 $need_unlink = 1;
206                 $file = $newfile;
207                 }
208         }
209 $qfile = quotemeta($file);
210
211 if ($in{'mode'} eq 'rpm') {
212         # Check if it is an RPM package
213         $rpmname = "webmin";
214         if (open(RPM, "$root_directory/rpm-name")) {
215                 chop($rpmname = <RPM>);
216                 close(RPM);
217                 }
218         $out = `rpm -qp $qfile`;
219         $out =~ /(^|\n)\Q$rpmname\E-(\d+\.\d+)/ ||
220                 &inst_error($text{'upgrade_erpm'});
221         $version = $2;
222         if (!$in{'force'}) {
223                 if ($version == &get_webmin_version()) {
224                         &inst_error(&text('upgrade_elatest', $version));
225                         }
226                 elsif ($version <= &get_webmin_version()) {
227                         &inst_error(&text('upgrade_eversion', $version));
228                         }
229                 }
230
231         # Install the RPM
232         $ENV{'tempdir'} = $gconfig{'tempdir'};
233         print "<p>",$text{'upgrade_setuprpm'},"<p>\n";
234         print "<pre>";
235         if ($in{'force'}) {
236                 &proc::safe_process_exec(
237                         "rpm -U --force $qfile", 0, 0,
238                         STDOUT, undef, 1, 1);
239                 }
240         else {
241                 &proc::safe_process_exec(
242                         "rpm -U --ignoreos --ignorearch $qfile", 0, 0,
243                         STDOUT, undef, 1, 1);
244                 }
245         unlink($file) if ($need_unlink);
246         print "</pre>\n";
247         }
248 elsif ($in{'mode'} eq 'deb') {
249         # Check if it is a Debian package
250         $debname = "webmin";
251         if (open(RPM, "$root_directory/deb-name")) {
252                 chop($debname = <RPM>);
253                 close(RPM);
254                 }
255         $out = `dpkg --info $qfile`;
256         $out =~ /Package:\s+(\S+)/ && $1 eq $debname ||
257                 &inst_error($text{'upgrade_edeb'});
258         $out =~ /Version:\s+(\S+)/ ||
259                 &inst_error($text{'upgrade_edeb'});
260         $version = $1;
261         if (!$in{'force'}) {
262                 if ($version == &get_webmin_version()) {
263                         &inst_error(&text('upgrade_elatest', $version));
264                         }
265                 elsif ($version <= &get_webmin_version()) {
266                         &inst_error(&text('upgrade_eversion', $version));
267                         }
268                 }
269
270         # Install the package
271         $ENV{'tempdir'} = $gconfig{'tempdir'};
272         print "<p>",$text{'upgrade_setupdeb'},"<p>\n";
273         print "<pre>";
274         &proc::safe_process_exec("dpkg --install $qfile", 0, 0,
275                                  STDOUT, undef, 1, 1);
276         unlink($file) if ($need_unlink);
277         print "</pre>\n";
278         }
279 elsif ($in{'mode'} eq 'solaris-pkg' || $in{'mode'} eq 'sun-pkg') {
280         # Check if it is a solaris package
281         &foreign_require("software", "software-lib.pl");
282         &foreign_call("software", "is_package", $file) ||
283                 &inst_error($text{'upgrade_epackage'});
284         my @p = &foreign_call("software", "file_packages", $file);
285         #
286         # The package name will always include "webmin" in lower case,
287         # but may be preceeded by the source package source ("WS" for the
288         # Webmin.com package, "SUNW" for the Sun distributed package).
289         # and it could have trailing characters to define a set of items
290         # that are installed separately ("r" for the Sun "root" package,
291         # "u" for the Sun "usr" package.
292         #
293         # So the problem is how to match the requested package to the
294         # currently installed package.
295         #
296
297         foreach $p (@p) {
298                 # Hardcode till I can get a better thought for doing this
299                 # via a config (or other) file..
300                 ($pkg, $description) = split(/ /, $p);
301                 if ($pkg =~ /^(SUNWwebminu|WSwebmin)$/) {
302                         break;
303                         }
304                 else {
305                         $pkg ='';
306                         }
307                 }
308
309         # Fallthrough error
310         if ($pkg eq '' ) {
311                 &inst_error($text{'upgrade_ewpackage'});
312                 }
313
314         # Install the package
315         print "<p>",$text{'upgrade_setuppackage'},"<p>\n";
316         print "PKG: $pkg<br>";
317         $ENV{'KEEP_ETC_WEBMIN'} = 1;
318
319         # Need to do this inline, otherwise the child process won't install the
320         # package.  It would be interesting, however, if this were embedded in
321         # a remote script that could be nohup'd and it would restart the server.
322         chdir("/");
323         &proc::safe_process_exec_logged(
324                 "$config_directory/stop", 0, 0, STDOUT, undef, 1,1);
325
326         $in{'root'} = '/';
327         $in{'adminfile'} = '$module_root_directory/adminupgrade';
328         $rv = &software::install_package($file, $pkg);
329         &error($rv) if ($rv);
330         unlink($file) if ($need_unlink);
331         $ENV{'config_dir'} = $config_directory;
332         $ENV{'webmin_upgrade'} = 1;
333         $ENV{'autothird'} = 1;
334         $ENV{'tempdir'} = $gconfig{'tempdir'};
335         print "<p>",$text{'upgrade_setup'},"<p>\n";
336         print "<pre>";
337
338         # We now need to figure out the installed directory for
339         # this package.  The best way is to find the basename
340         # for the miniserv.pl file associated with this package
341         # or, in grep context:
342         #   grep "miniserv.pl.*$pkg"
343         # and the first element includes the pathname.
344         #
345         $targ = `grep "miniserv.pl.*$pkg" /var/sadm/install/contents`;
346         if ($targ =~ /^(.*)\/miniserv.pl.*$/) {
347                 $dir = $1;
348                 }
349
350         $setup = $in{'dir'} ? "./setup.sh '$in{'dir'}'" : "./setup.sh";
351         print "Package Directory: $dir<br>";
352         print  "cd $dir && ./setup.sh<br>";
353         &proc::safe_process_exec(
354                 "cd $dir && ./setup.sh", 0, 0, STDOUT, undef, 1, 1);
355         &proc::safe_process_exec_logged(
356                 "$config_directory/start", 0, 0, STDOUT, undef, 1,1);
357         print "</pre>\n";
358         }
359 elsif ($in{'mode'} eq 'caldera') {
360         # Check if it is a Caldera RPM of Webmin
361         $out = `rpm -qp $file`;
362         $out =~ /^webmin-(\d+\.\d+)/ ||
363                 &inst_error($text{'upgrade_erpm'});
364         if ($1 <= &get_webmin_version() && !$in{'force'}) {
365                 &inst_error(&text('upgrade_eversion', "$1"));
366                 }
367         my $wfound = 0;
368         open(OUT, "rpm -qpl $file |");
369         while(<OUT>) {
370                 $wfound++ if (/^\/etc\/webmin/);
371                 }
372         close(OUT);
373         $wfound || &inst_error($text{'upgrade_ecaldera'});
374
375         # Install the RPM
376         print "<p>",$text{'upgrade_setuprpm'},"<p>\n";
377         print "<pre>";
378         &proc::safe_process_exec("rpm -U --ignoreos --ignorearch '$file'", 0, 0,
379                            STDOUT, undef, 1, 1);
380         unlink($file) if ($need_unlink);
381         print "</pre>\n";
382         }
383 elsif ($in{'mode'} eq 'gentoo') {
384         # Check if it is a gentoo .tar.gz or .ebuild file of webmin
385         open(EMERGE, "emerge --pretend '$file' 2>/dev/null |");
386         while(<EMERGE>) {
387                 s/\r|\n//g;
388                 s/\033[^m]+m//g;
389                 if (/\s+[NRU]\s+\]\s+([^\/]+)\/webmin\-(\d\S+)/) {
390                         $version = $2;
391                         }
392                 }
393         close(EMERGE);
394         $version || &inst_error($text{'upgrade_egentoo'});
395         if (!$in{'force'}) {
396                 if ($version == &get_webmin_version()) {
397                         &inst_error(&text('upgrade_elatest', $version));
398                         }
399                 elsif ($version <= &get_webmin_version()) {
400                         &inst_error(&text('upgrade_eversion', $version));
401                         }
402                 }
403
404         # Install the Gentoo package
405         print "<p>",$text{'upgrade_setupgentoo'},"<p>\n";
406         print "<pre>";
407         &proc::safe_process_exec("emerge '$file'", 0, 0, STDOUT, undef, 1, 1);
408         unlink($file) if ($need_unlink);
409         print "</pre>\n";
410         }
411 else {
412         # Check if it is a webmin tarfile
413         open(TAR, "tar tf $file 2>&1 |");
414         while(<TAR>) {
415                 s/\r|\n//g;
416                 if (/^webmin-([0-9\.]+)\//) {
417                         $version = $1;
418                         }
419                 if (/^usermin-([0-9\.]+)\//) {
420                         $usermin_version = $1;
421                         }
422                 if (/^[^\/]+\/(\S+)$/) {
423                         $hasfile{$1}++;
424                         }
425                 if (/^(webmin-([0-9\.]+)\/([^\/]+))$/ && $3 ne ".") {
426                         # Found a top-level file, or *possibly* a directory
427                         # under some versions of tar. Keep it so we know which
428                         # files to extract.
429                         push(@topfiles, $_);
430                         }
431                 elsif (/^(webmin-[0-9\.]+\/([^\/]+))\// && $2 ne ".") {
432                         # Found a sub-directory, like webmin-1.xx/foo/
433                         # Keep this, so that we know which modules to extract.
434                         # Also keep the full directory like webmin-1.xx/foo
435                         # to avoid treating it as a file.
436                         $intar{$2}++;
437                         $tardir{$1}++;
438                         }
439                 }
440         close(TAR);
441         if ($usermin_version) {
442                 &inst_error(&text('upgrade_eusermin', $usermin_version));
443                 }
444         if (!$version) {
445                 if ($hasfile{'module.info'}) {
446                         &inst_error(&text('upgrade_emod', 'edit_mods.cgi'));
447                         }
448                 else {
449                         &inst_error($text{'upgrade_etar'});
450                         }
451                 }
452         if (!$in{'force'}) {
453                 if ($version == &get_webmin_version()) {
454                         &inst_error(&text('upgrade_elatest', $version));
455                         }
456                 elsif ($version <= &get_webmin_version()) {
457                         &inst_error(&text('upgrade_eversion', $version));
458                         }
459                 }
460
461         # Work out where to extract
462         if ($in{'dir'}) {
463                 # Since we are currently installed in a fixed directory,
464                 # just extract to a temporary location
465                 $extract = &transname();
466                 mkdir($extract, 0755);
467                 }
468         else {
469                 # Next to the current directory
470                 $extract = "../..";
471                 }
472
473         # Do the extraction of the tar file, and run setup.sh
474         $| = 1;
475         if ($in{'only'}) {
476                 # Extact top-level files like setup.sh and os_list.txt
477                 $topfiles = join(" ", map { quotemeta($_) }
478                                           grep { !$tardir{$_} } @topfiles);
479                 $out = `cd $extract ; tar xf $file $topfiles 2>&1 >/dev/null`;
480                 if ($?) {
481                         &inst_error(&text('upgrade_euntar', "<tt>$out</tt>"));
482                         }
483
484                 # Add current modules and current non-module directories
485                 # (like themes and lang and images)
486                 @mods = grep { $intar{$_} } map { $_->{'dir'} }
487                              &get_all_module_infos(1);
488                 opendir(DIR, $root_directory);
489                 foreach $d (readdir(DIR)) {
490                         next if ($d =~ /^\./);
491                         my $p = "$root_directory/$d";
492                         if (-d $p && !-r "$p/module.info" && $intar{$d}) {
493                                 push(@mods, $d);
494                                 }
495                         }
496                 closedir(DIR);
497
498                 # Extract current modules and other directories
499                 $mods = join(" ", map { quotemeta("webmin-$version/$_") }
500                                       @mods);
501                 $out = `cd $extract ; tar xf $file $mods 2>&1 >/dev/null`;
502                 if ($?) {
503                         &inst_error(&text('upgrade_euntar', "<tt>$out</tt>"));
504                         }
505                 }
506         else {
507                 # Extract the whole file
508                 $out = `cd $extract ; tar xf $file 2>&1 >/dev/null`;
509                 if ($?) {
510                         &inst_error(&text('upgrade_euntar', "<tt>$out</tt>"));
511                         }
512                 }
513         unlink($file) if ($need_unlink);
514         $ENV{'config_dir'} = $config_directory;
515         $ENV{'webmin_upgrade'} = 1;
516         $ENV{'autothird'} = 1;
517         $ENV{'tempdir'} = $gconfig{'tempdir'};
518         $ENV{'deletedold'} = 1 if ($in{'delete'});
519         print "<p>",$text{'upgrade_setup'},"<p>\n";
520         print "<pre>";
521         $setup = $in{'dir'} ? "./setup.sh '$in{'dir'}'" : "./setup.sh";
522         &proc::safe_process_exec(
523                 "cd $extract/webmin-$version && $setup", 0, 0,
524                 STDOUT, undef, 1, 1);
525         print "</pre>\n";
526         if (!$?) {
527                 if ($in{'delete'}) {
528                         # Can delete the old root directory
529                         system("rm -rf \"$root_directory\"");
530                         }
531                 elsif ($in{'dir'}) {
532                         # Can delete the temporary source directory
533                         system("rm -rf \"$extract\"");
534                         }
535                 }
536         }
537 &webmin_log("upgrade", undef, undef, { 'version' => $version,
538                                        'mode' => $in{'mode'} });
539
540 if ($in{'disc'}) {
541         # Forcibly disconnect all other login sessions
542         &foreign_require("acl", "acl-lib.pl");
543         &get_miniserv_config(\%miniserv);
544         &acl::open_session_db(\%miniserv);
545         foreach $s (keys %acl::sessiondb) {
546                 if ($s ne $main::session_id) {
547                         delete($acl::sessiondb{$s});
548                         }
549                 }
550         dbmclose(%acl::sessiondb);
551         &restart_miniserv(1);
552         }
553
554 # Find out about any updates for this new version.
555 ($updates) = &fetch_updates($update_url);
556 $updates = &filter_updates($updates, $version);
557 if (scalar(@$updates)) {
558         print "<br>",&text('upgrade_updates', scalar(@$updates),
559                 "update.cgi?source=0&show=0&missing=0"),"<p>\n";
560         }
561
562 &ui_print_footer("", $text{'index_return'});
563
564 sub inst_error
565 {
566 unlink($file) if ($need_unlink);
567 unlink($updatestemp);
568 print "<b>$main::whatfailed : $_[0]</b> <p>\n";
569 &ui_print_footer("", $text{'index_return'});
570 exit;
571 }
572