php IHDR w Q )Ba pHYs sRGB gAMA a IDATxMk\U s&uo,mD )Xw+e?tw.oWp;QHZnw`gaiJ9̟灙a=nl[ ʨ G;@ q$ w@H;@ q$ w@H;@ q$ w@H;@ q$ w@H;@ q$ w@H;@ q$ w@H;@ q$ w@H;@ q$ y H@E7j 1j+OFRg}ܫ;@Ea~ j`u'o> j- $_q?qS XzG'ay
files >> /usr/libexec/webmin/security-updates/ |
files >> //usr/libexec/webmin/security-updates/security-updates-lib.pl |
# Functions for checking for updates to core Virtualmin packages BEGIN { push(@INC, ".."); }; eval "use WebminCore;"; &init_config(); &foreign_require("software", "software-lib.pl"); &foreign_require("cron", "cron-lib.pl"); &foreign_require("webmin", "webmin-lib.pl"); use Data::Dumper; @update_packages = ( "apache", "postfix", "sendmail", "bind", "procmail", "spamassassin", "logrotate", "webalizer", "mysql", "postgresql", "proftpd", "clamav", "php4", "mailman", "subversion", "python", "ruby", "irb", "rdoc", "rubygems", "openssl", "perl", "php5", "webmin", "usermin", "fcgid", "awstats", "dovecot", "postgrey", "bash", "virtualmin-modules", "kvm", "xen", "nginx", ); $available_cache_file = "$module_config_directory/available.cache"; $current_cache_file = "$module_config_directory/current.cache"; $current_all_cache_file = "$module_config_directory/current-all.cache"; $updates_cache_file = "$module_config_directory/updates.cache"; $cron_cmd = "$module_config_directory/update.pl"; $virtualmin_host = $config{'host'} || "software.virtualmin.com"; $virtualmin_port = 80; $server_manager_host = "vm2.virtualmin.com"; $virtualmin_licence = "/etc/virtualmin-license"; $server_manager_licence = "/etc/server-manager-license"; $webmin_version_path = "$config{'suffix'}/wbm/webmin-version"; $free_webmin_version_path = "$config{'suffix'}/gpl/wbm/webmin-version"; $usermin_version_path = "$config{'suffix'}/wbm/usermin-version"; $free_usermin_version_path = "$config{'suffix'}/gpl/wbm/usermin-version"; $webmin_download_path = "$config{'suffix'}/wbm/webmin-current.tar.gz"; $usermin_download_path = "$config{'suffix'}/wbm/usermin-current.tar.gz"; $free_webmin_download_path = "$config{'suffix'}/gpl/wbm/webmin-current.tar.gz"; $free_usermin_download_path = "$config{'suffix'}/gpl/wbm/usermin-current.tar.gz"; $yum_cache_file = "$module_config_directory/yumcache"; $apt_cache_file = "$module_config_directory/aptcache"; $yum_changelog_cache_dir = "$module_config_directory/yumchangelog"; # test_connection() # Returns undef if we can connect OK, or an error message sub test_connection { return undef if (&free_virtualmin_licence() || # GPL version &free_cloudmin_licence()); my ($user, $pass, $host) = &get_user_pass(); return $text{'index_euser'} if (!$user); return undef; } # get_software_packages() # Fills in software::packages with list of installed packages (if missing), # returns count. sub get_software_packages { if (!$get_software_packages_cache) { %software::packages = ( ); $get_software_packages_cache = &software::list_packages(); } return $get_software_packages_cache; } # list_current(nocache) # Returns a list of packages and versions for the core packages managed # by this module. Return keys are : # name - The my package name (ie. CSWapache2) # update - Name used to refer to it by the updates system (ie. apache2) # version - Version number # epoch - Epoch part of the version # desc - Human-readable description # package - Original generic program, like apache sub list_current { my ($nocache) = @_; if ($nocache || &cache_expired($current_cache_file)) { my $n = &get_software_packages(); my @rv; foreach my $p (@update_packages) { my @pkgs = split(/\s+/, &package_resolve($p)); foreach my $pn (@pkgs) { my $updatepn = $pn; $pn = &csw_to_pkgadd($pn); for(my $i=0; $i<$n; $i++) { next if ($software::packages{$i,'name'} !~ /^$pn$/); push(@rv, { 'update' => $updatepn eq $pn ? $software::packages{$i,'name'} : $updatepn, 'name' => $software::packages{$i,'name'}, 'version' => $software::packages{$i,'version'}, 'epoch' => $software::packages{$i,'epoch'}, 'desc' => $software::packages{$i,'desc'}, 'package' => $p, 'system' => $software::update_system, 'software' => 1, }); &fix_pkgadd_version($rv[$#rv]); } } } # Filter out dupes and sort by name @rv = &filter_duplicates(\@rv); my $incwebmin = &include_webmin_modules(); if ($incwebmin) { # Add installed Webmin modules foreach my $minfo (&get_all_module_infos()) { push(@rv, { 'name' => $minfo->{'dir'}, 'update' => $minfo->{'dir'}, 'desc' => &text('index_webmin', $minfo->{'desc'}), 'version' => $minfo->{'version'}, 'system' => 'webmin', 'updateonly' => 1, }); } # Add installed Webmin themes foreach my $tinfo (&webmin::list_themes()) { push(@rv, { 'name' => $tinfo->{'dir'}, 'update' => $tinfo->{'dir'}, 'desc' => &text('index_webmintheme', $tinfo->{'desc'}), 'version' => $tinfo->{'version'}, 'system' => 'webmin', 'updateonly' => 1, }); } # Add an entry for Webmin itself, but only if this was # a tar.gz install if ($incwebmin != 2) { push(@rv, { 'name' => 'webmin', 'update' => 'webmin', 'desc' => 'Webmin Package', 'version' => &get_webmin_version(), 'system' => 'tgz', 'updateonly' => 1, }); } else { # Remove Webmin from the list, as YUM sometimes # includes it in the 'yum list' output even though # it cannot actual do an update! @rv = grep { $_->{'name'} ne 'webmin' } @rv; } # If Usermin is installed from a tgz, add it too if (&include_usermin_modules() == 1) { push(@rv, { 'name' => 'usermin', 'update' => 'usermin', 'desc' => 'Usermin Package', 'version' => &usermin::get_usermin_version(), 'system' => 'tgz', 'updateonly' => 1, }); } else { @rv = grep { $_->{'name'} ne 'usermin' } @rv; } } &write_cache_file($current_cache_file, \@rv); return @rv; } else { return &read_cache_file($current_cache_file); } } # list_all_current(nocache) # Returns a list of all installed packages, in the same format as list_current sub list_all_current { my ($nocache) = @_; my ($nocache) = @_; if ($nocache || &cache_expired($current_all_cache_file)) { my $n = &get_software_packages(); my @rv; my %pkgmap; foreach my $p (@update_packages) { my @pkgs = split(/\s+/, &package_resolve($p)); foreach my $pn (@pkgs) { $pkgmap{$pn} = $p; } } for(my $i=0; $i<$n; $i++) { push(@rv, { 'name' => $software::packages{$i,'name'}, 'update' => $software::packages{$i,'name'}, 'version' => $software::packages{$i,'version'}, 'epoch' => $software::packages{$i,'epoch'}, 'desc' => $software::packages{$i,'desc'}, 'package' => $pkgmap{$software::packages{$i,'name'}}, 'system' => $software::update_system, }); &fix_pkgadd_version($rv[$#rv]); } # Filter out dupes and sort by name @rv = &filter_duplicates(\@rv); &write_cache_file($current_all_cache_file, \@rv); return @rv; } else { return &read_cache_file($current_all_cache_file); } } # list_available(nocache, all) # Returns the names and versions of packages available from the update # system, that we are interested in. sub list_available { my ($nocache, $all) = @_; my $expired = &cache_expired($available_cache_file.int($all)); if ($nocache || $expired == 2 || $expired == 1 && !&check_available_lock()) { # Get from update system my @rv; my @avail = &packages_available(); if (!$all) { # Limit to packages Virtualmin cares about @avail = &filter_virtualmin(\@avail); } foreach my $avail (@avail) { $avail->{'update'} = $avail->{'name'}; $avail->{'name'} = &csw_to_pkgadd($avail->{'name'}); if (!$all && &installation_candiate($avail)) { $avail->{'desc'} ||= &generate_description($avail); } push(@rv, $avail); } &set_pinned_versions(\@rv); # Filter out dupes and sort by name @rv = &filter_duplicates(\@rv); if (!$all && &include_webmin_modules()) { # Get from Webmin updates services. We exclude Webmin and # Usermin for now, as they cannot be updated via YUM. @rv = grep { $_->{'name'} ne 'webmin' && $_->{'name'} ne 'usermin' } @rv; push(@rv, &webmin_modules_available()); } if (!@rv) { # Failed .. fall back to cache @rv = &read_cache_file($available_cache_file.int($all)); } &write_cache_file($available_cache_file.int($all), \@rv); return @rv; } else { return &read_cache_file($available_cache_file.int($all)); } } # filter_virtualmin(&packages) # Given a list of updates to include only those Virtualmin-related sub filter_virtualmin { my ($avail) = @_; my @rv; foreach my $p (@update_packages) { my @pkgs = split(/\s+/, &package_resolve($p)); foreach my $pn (@pkgs) { my @mavail = grep { $_->{'name'} =~ /^$pn$/ } @$avail; foreach my $avail (@mavail) { $avail->{'package'} = $p; push(@rv, $avail); } } } return @rv; } # check_available_lock() # Returns 1 if the package update system is currently locked sub check_available_lock { if ($software::update_system eq "yum") { return &check_pid_file("/var/run/yum.pid"); } return 0; } # filter_duplicates(&packages) # Given a list of package structures, orders them by name and version number, # and removes dupes with the same name sub filter_duplicates { my ($pkgs) = @_; my @rv = sort { $a->{'name'} cmp $b->{'name'} || &compare_versions($b, $a) } @$pkgs; my %done; return grep { !$done{$_->{'name'},$_->{'system'}}++ } @rv; } # cache_expired(file) # Checks if some cache has expired. Returns 0 if OK, 1 if expired, 2 if # totally missing. sub cache_expired { my ($file) = @_; my @st = stat($file); return 2 if (!@st); if (!$config{'cache_time'} || time()-$st[9] > $config{'cache_time'}*60*60) { return 1; } return 0; } sub write_cache_file { my ($file, $arr) = @_; &open_tempfile(FILE, ">$file"); &print_tempfile(FILE, Dumper($arr)); &close_tempfile(FILE); $read_cache_file_cache{$file} = $arr; } # read_cache_file(file) # Returns the contents of some cache file, as an array ref sub read_cache_file { my ($file) = @_; if (defined($read_cache_file_cache{$file})) { return @{$read_cache_file_cache{$file}}; } if (-r $file) { do $file; $read_cache_file_cache{$file} = $VAR1; return @$VAR1; } return ( ); } # compare_versions(&pkg1, &pk2) # Returns -1 if the version of pkg1 is older than pkg2, 1 if newer, 0 if same. sub compare_versions { my ($pkg1, $pkg2) = @_; if ($pkg1->{'system'} eq 'webmin' && $pkg2->{'system'} eq 'webmin') { # Webmin module version compares are always numeric return $pkg1->{'version'} <=> $pkg2->{'version'}; } my $ec = $pkg1->{'epoch'} <=> $pkg2->{'epoch'}; if ($ec && ($pkg1->{'epoch'} eq '' || $pkg2->{'epoch'} eq '') && $pkg1->{'system'} eq 'apt') { # On some Debian systems, we don't have a my epoch $ec = undef; } return $ec || &software::compare_versions($pkg1->{'version'}, $pkg2->{'version'}); } sub find_cron_job { my @jobs = &cron::list_cron_jobs(); my ($job) = grep { $_->{'user'} eq 'root' && $_->{'command'} eq $cron_cmd } @jobs; return $job; } # package_resolve(name) # Given a package code name from @update_packages, returns a string of the # underlying packages that implement it. This may come from the update system # if the OS has one (YUM or APT, or from Virtualmin's built-in list) sub package_resolve { my ($name) = @_; my $realos = $gconfig{'real_os_type'}; $realos =~ s/ /-/g; my $realver = $gconfig{'real_os_version'}; $realver =~ s/ /-/g; if (open(RESOLV, "$module_root_directory/resolve.$realos-$realver") || open(RESOLV, "$module_root_directory/resolve.$realos") || open(RESOLV, "$module_root_directory/resolve.$gconfig{'os_type'}-$gconfig{'os_version'}") || open(RESOLV, "$module_root_directory/resolve.$gconfig{'os_type'}")) { my $rv; while(<RESOLV>) { if (/^(\S+)\s+(.*)/ && $1 eq $name) { $rv = $2; } elsif (/^\*/) { # All other packages have the same name as their code $rv = $name; } } close(RESOLV); return $rv if ($rv); } if (defined(&software::update_system_resolve)) { return &software::update_system_resolve($name); } return $name; } # packages_available() # Returns a list of all available packages, as hash references with name and # version keys. These come from the APT, YUM or CSW update system, if available. # If not, nothing is returned. sub packages_available { if (@packages_available_cache) { return @packages_available_cache; } if (defined(&software::update_system_available)) { # From a decent package system my @rv = software::update_system_available(); my %done; foreach my $p (@rv) { $p->{'system'} = $software::update_system; $p->{'version'} =~ s/,REV=.*//i; # For CSW if ($p->{'system'} eq 'apt' && !$p->{'source'}) { $p->{'source'} = $p->{'file'} =~ /virtualmin/i ? 'virtualmin' : $p->{'file'} =~ /debian/i ? 'debian' : $p->{'file'} =~ /ubuntu/i ? 'ubuntu' : undef; } $done{$p->{'name'}} = $p; } if ($software::update_system eq "yum" && &has_command("up2date")) { # YUM is the package system select, but up2date is installed # too (ie. RHEL). Fetch its packages too.. if (!$done_rhn_lib++) { do "../software/rhn-lib.pl"; } my @rhnrv = &update_system_available(); foreach my $p (@rhnrv) { $p->{'system'} = "rhn"; my $d = $done{$p->{'name'}}; if ($d) { # Seen already .. but is this better? if (&compare_versions($p, $d) > 0) { # Yes .. replace @rv = grep { $_ ne $d } @rv; push(@rv, $p); $done{$p->{'name'}} = $p; } } else { push(@rv, $p); $done{$p->{'name'}} = $p; } } } @packages_available_cache = @rv; return @rv; } return ( ); } # supports_updates_available() # Returns true if the package update system has a function to find just # updates, and we aren't including Webmin modules sub supports_updates_available { return defined(&software::update_system_updates) && !&include_webmin_modules(); } # updates_available(no-cache, all) # Returns an array of hash refs of package updates available, according to # the update system, with caching. sub updates_available { my ($nocache, $all) = @_; if (!scalar(@updates_available_cache)) { if ($nocache || &cache_expired($updates_cache_file)) { # Get from original source @updates_available_cache = &software::update_system_updates(); foreach my $a (@updates_available_cache) { $a->{'update'} = $a->{'name'}; $a->{'system'} = $software::update_system; } &write_cache_file($updates_cache_file, \@updates_available_cache); } else { # Use on-disk cache @updates_available_cache = &read_cache_file($updates_cache_file); } } if ($all) { return @updates_available_cache; } else { return &filter_virtualmin(\@updates_available_cache); } } # package_install(package, [system], [check-all]) # Install some package, either from an update system or from Virtualmin. Returns # a list of updated package names. sub package_install { my ($name, $system, $all) = @_; my @rv; my $pkg; # First get from list of updates ($pkg) = grep { $_->{'update'} eq $name && ($_->{'system'} eq $system || !$system) } sort { &compare_versions($b, $a) } &list_possible_updates(0, $all); if (!$pkg) { # Then try list of all available packages ($pkg) = grep { $_->{'update'} eq $name && ($_->{'system'} eq $system || !$system) } sort { &compare_versions($b, $a) } &list_available(0, $all); } if (!$pkg) { print &text('update_efindpkg', $name),"<p>\n"; return ( ); } if ($pkg->{'system'} eq 'webmin') { # Webmin module, which we can download and install my ($host, $port, $page, $ssh) = &parse_http_url($pkg->{'updatesurl'}); my ($mhost, $mport, $mpage, $mssl) = &parse_http_url($pkg->{'url'}, $host, $port, $page, $ssl); my $mfile; ($mfile = $mpage) =~ s/^(.*)\///; my $mtemp = &transname($mfile); my $error; print &text('update_wdownload', $pkg->{'name'}),"<br>\n"; &http_download($mhost, $mport, $mpage, $mtemp, \$error, undef, $mssl, $webmin::config{'upuser'}, $webmin::config{'uppass'}); if ($error) { print &text('update_ewdownload', $error),"<p>\n"; return ( ); } print $text{'update_wdownloaded'},"<p>\n"; # Install the module print &text('update_winstall', $pkg->{'name'}),"<br>\n"; my $irv = &webmin::install_webmin_module($mtemp, 1, 0); if (!ref($irv)) { print &text('update_ewinstall', $irv),"<p>\n"; } else { print $text{'update_winstalled'},"<p>\n"; @rv = map { /([^\/]+)$/; $1 } @{$irv->[1]}; } } elsif ($pkg->{'system'} eq 'tgz') { # Tar file of Webmin or Usermin, which we have to download and # install into the destination directory my $temp = &transname($pkg->{'name'}."-".$pkg->{'version'}. ".tar.gz"); my $error; print &text('update_tgzdownload', ucfirst($pkg->{'name'})),"<br>\n"; my ($user, $pass) = &get_user_pass(); my $free = free_virtualmin_licence() || free_cloudmin_licence(); my $path = $pkg->{'name'} eq 'webmin' && $free ? $free_webmin_download_path : $pkg->{'name'} eq 'webmin' && !$free ? $webmin_download_path : $pkg->{'name'} eq 'usermin' && $free ? $free_usermin_download_path : $usermin_download_path; &http_download($virtualmin_host, $virtualmin_port, $path, $temp, \$error, undef, 0, $user, $pass); if ($error || !-r $temp) { print &text('update_ewdownload', $error || "Nothing downloaded"),"<p>\n"; return ( ); } else { my @st = stat($temp); print &text('update_tgzdownloaded', &nice_size($st[7])),"<p>\n"; } # Get the current install directory print $text{'update_tgzuntar'},"<br>\n"; my $curdir; if ($pkg->{'name'} eq 'webmin') { $curdir = $root_directory; $pkg_config_dir = $config_directory; } else { my %miniserv; &foreign_require("usermin", "usermin-lib.pl"); &usermin::get_usermin_miniserv_config(\%miniserv); $curdir = $miniserv{'root'}; $pkg_config_dir = $usermin::config{'usermin_dir'}; } if (!$curdir || $curdir eq "/") { print $text{'update_ecurdir'},"<p>\n"; return ( ); } if (!-r "$pkg_config_dir/config") { print $text{'update_econfigdir'},"<p>\n"; return ( ); } my $targetdir = &read_file_contents("$pkg_config_dir/install-dir"); $targetdir =~ s/\r|\n//g; # Un-tar the archive next to it my $pardir = $curdir; $pardir =~ s/\/([^\/]+)$//; my $out = &backquote_command("cd ".quotemeta($pardir)." && ". "gunzip -c $temp | tar xf -"); if ($?) { my @lines = split(/\n/, $out); while(@lines > 10) { shift(@lines); } # Last 10 only print "<pre>",&html_escape(join("\n", @lines)),"</pre>\n"; print $text{'update_etgzuntar'},"<p>\n"; return ( ); } my $xtractdir = $pardir."/".$pkg->{'name'}."-".$pkg->{'version'}; print $text{'update_tgzuntardone'},"<p>\n"; # Save this CGI from being killed by the upgrade $SIG{'TERM'} = 'IGNORE'; # Run setup.sh to upgrade print $text{'update_tgzsetup'},"<br>\n"; print "<pre>"; &clean_environment(); &open_execute_command(SETUP, "cd $xtractdir && config_dir=$pkg_config_dir autothird=1 ./setup.sh $targetdir 2>&1 </dev/null", 1); while(<SETUP>) { print &html_escape($_); } close(SETUP); print "</pre>\n"; &reset_environment(); if ($out =~ /ERROR/) { print $text{'update_etgzsetup'},"<p>\n"; } else { print $text{'update_tgzsetupdone'},"<p>\n"; } if ($targetdir) { # Delete the extract directory, if we copied to elsewhere &execute_command("rm -rf ".quotemeta($xtractdir)); } elsif ($out !~ /ERROR/) { # Delete the old directory &execute_command("rm -rf ".quotemeta($curdir)); } &unlink_file($temp); @rv = ( $pkg->{'name'} ); } elsif (defined(&software::update_system_install)) { # Using some update system, like YUM or APT &clean_environment(); if ($software::update_system eq $pkg->{'system'}) { # Can use the default system if ($name eq "apache2" && $pkg->{'system'} eq 'apt') { # If updating the apache2 package on an apt system # and apache2-mpm-prefork is installed, also update it # so that ubuntu doesn't pull in the apache2-mpm-worker # instead, which breaks PHP :-( local @pinfo = &software::package_info( "apache2-mpm-prefork"); if (@pinfo) { $name .= " apache2-mpm-prefork"; } } @rv = &software::update_system_install($name, undef, 1); } else { # Another update system exists!! Use it.. if (!$done_rhn_lib++) { do "../software/$pkg->{'system'}-lib.pl"; } if (!$done_rhn_text++) { %text = ( %text, %software::text ); } @rv = &update_system_install($name, undef, 1); } &reset_environment(); } else { &error("Don't know how to install package $pkg->{'name'} with type $pkg->{'type'}"); } # Flush installed cache unlink($current_cache_file); unlink($current_all_cache_file); return @rv; } # package_install_multiple(&package-names, system) # Install multiple packages, either from an update system or from Webmin. # Returns a list of updated package names. sub package_install_multiple { my ($names, $system) = @_; my @rv; my $pkg; if ($system eq "webmin" || $system eq "tgz") { # Install one by one foreach my $name (@$names) { push(@rv, &package_install($name, $system)); } } elsif (defined(&software::update_system_install)) { # Using some update system, like YUM or APT &clean_environment(); if ($software::update_system eq $system) { # Can use the default system @rv = &software::update_system_install( join(" ", @$names), undef, 1); } else { # Another update system exists!! Use it.. if (!$done_rhn_lib++) { do "../software/$pkg->{'system'}-lib.pl"; } if (!$done_rhn_text++) { %text = ( %text, %software::text ); } @rv = &update_system_install(join(" ", @$names), undef, 1); } &reset_environment(); } else { &error("Don't know how to install packages"); } # Flush installed cache unlink($current_cache_file); return @rv; } # get_user_pass() # Returns the username and password to use for HTTP requests, and the base site sub get_user_pass { my %licence; if (-r $virtualmin_licence) { &read_env_file($virtualmin_licence, \%licence); return ($licence{'SerialNumber'}, $licence{'LicenseKey'}, $virtualmin_host); } elsif (-r $server_manager_licence) { &read_env_file($server_manager_licence, \%licence); return ($licence{'SerialNumber'}, $licence{'LicenseKey'}, $server_manager_host); } else { return ( ); } } # free_virtualmin_licence() # Returns 1 if this is a GPL/free install of Virtualmin sub free_virtualmin_licence { my %minfo = &get_module_info('virtual-server'); if (%minfo && $minfo{'virtualmin'} eq 'gpl') { return 1; } if (-r $virtualmin_licence) { local %licence; &read_env_file($virtualmin_licence, \%licence); return $licence{'SerialNumber'} eq 'GPL' ? 1 : 0; } return 0; } # free_cloudmin_licence() # Returns 1 if this is a GPL/free install of Cloudmin sub free_cloudmin_licence { my %minfo = &get_module_info('server-manager'); if (%minfo && $minfo{'gpl'}) { return 1; } if (-r $server_manager_licence) { local %licence; &read_env_file($server_manager_licence, \%licence); return $licence{'SerialNumber'} eq 'GPL' ? 1 : 0; } return 0; } # list_possible_updates([nocache], [all]) # Returns a list of updates that are available. Each element in the array # is a hash ref containing a name, version, description and severity flag. # Intended for calling from themes. Nocache 0=cache everything, 1=flush all # caches, 2=flush only current sub list_possible_updates { my ($nocache, $all) = @_; my @rv; my @current = $all ? &list_all_current($nocache) : &list_current($nocache); if (&supports_updates_available()) { # Software module supplies a function that can list just packages # that need updating my %currentmap; foreach my $c (@current) { $currentmap{$c->{'name'},$c->{'system'}} ||= $c; } foreach my $a (&updates_available($nocache == 1, $all)) { my $c = $currentmap{$a->{'name'},$a->{'system'}}; next if (!$c); next if ($a->{'version'} eq $c->{'version'}); push(@rv, { 'name' => $a->{'name'}, 'update' => $a->{'update'}, 'system' => $a->{'system'}, 'version' => $a->{'version'}, 'oldversion' => $c->{'version'}, 'epoch' => $a->{'epoch'}, 'oldepoch' => $c->{'epoch'}, 'security' => $a->{'security'}, 'source' => $a->{'source'}, 'desc' => $c->{'desc'} || $a->{'desc'}, 'url' => $a->{'url'}, 'updatesurl' => $a->{'updatesurl'}, 'severity' => 0 }); } } else { # Compute from current and available list my @avail = &list_available($nocache == 1, $all); my %availmap; foreach my $a (@avail) { my $oa = $availmap{$a->{'name'},$a->{'system'}}; if (!$oa || &compare_versions($a, $oa) > 0) { $availmap{$a->{'name'},$a->{'system'}} = $a; } } foreach my $c (sort { $a->{'name'} cmp $b->{'name'} } @current) { # Work out the status my $a = $availmap{$c->{'name'},$c->{'system'}}; if ($a->{'version'} && &compare_versions($a, $c) > 0) { # A regular update is available push(@rv, { 'name' => $a->{'name'}, 'update' => $a->{'update'}, 'system' => $a->{'system'}, 'version' => $a->{'version'}, 'oldversion' => $c->{'version'}, 'epoch' => $a->{'epoch'}, 'desc' => $c->{'desc'} || $a->{'desc'}, 'url' => $a->{'url'}, 'updatesurl' => $a->{'updatesurl'}, 'severity' => 0 }); } } } return @rv; } # list_possible_installs([nocache]) # Returns a list of packages that could be installed, but are not yet sub list_possible_installs { my ($nocache) = @_; my @rv; my @current = &list_current($nocache); my @avail = &list_available($nocache == 1); my %currentmap; foreach my $c (@current) { $currentmap{$c->{'name'},$c->{'system'}} = $c; } foreach my $a (sort { $a->{'name'} cmp $b->{'name'} } @avail) { my $c = $currentmap{$a->{'name'},$a->{'system'}}; if (!$c && &installation_candiate($a)) { push(@rv, { 'name' => $a->{'name'}, 'update' => $a->{'update'}, 'system' => $a->{'system'}, 'version' => $a->{'version'}, 'epoch' => $a->{'epoch'}, 'desc' => $a->{'desc'}, 'url' => $a->{'url'}, 'updatesurl' => $a->{'updatesurl'}, 'severity' => 0 }); } } return @rv; } # csw_to_pkgadd(package) # On Solaris systems, convert a CSW package name like ap2_modphp5 to a # real package name like CSWap2modphp5 sub csw_to_pkgadd { my ($pn) = @_; if ($gconfig{'os_type'} eq 'solaris') { $pn =~ s/[_\-]//g; $pn = "CSW$pn"; } return $pn; } # fix_pkgadd_version(&package) # If this is Solaris and the package version is missing, we need to make # a separate pkginfo call to get it. sub fix_pkgadd_version { my ($pkg) = @_; if ($gconfig{'os_type'} eq 'solaris') { if (!$pkg->{'version'}) { # Make an extra call to get the version my @pinfo = &software::package_info($pkg->{'name'}); $pinfo[4] =~ s/,REV=.*//i; $pkg->{'version'} = $pinfo[4]; } else { # Trip off the REV= $pkg->{'version'} =~ s/,REV=.*//i; } } $pkg->{'desc'} =~ s/^\Q$pkg->{'update'}\E\s+\-\s+//; } # include_webmin_modules() # Returns 1 if we should include all Webmin modules and the program itself in # the list of updates. Returns 2 if only non-core modules should be included. # The first case is selected when you have a tar.gz install, while the second # corresponds to a rpm or deb install with Virtualmin modules added. sub include_webmin_modules { return 0 if (&webmin::shared_root_directory()); my $type = &read_file_contents("$root_directory/install-type"); chop($type); if (!$type) { # Webmin tar.gz install return 1; } elsif (&foreign_check("virtual-server")) { # How was virtual-server installed? my $vtype = &read_file_contents( &module_root_directory("virtual-server")."/install-type"); chop($vtype); if (!$vtype) { # A tar.gz install ... which we may be able to update return 2; } return 0; } elsif (&foreign_check("server-manager")) { # How was server-manager installed? my $vtype = &read_file_contents( &module_root_directory("server-manager")."/install-type"); chop($vtype); if (!$vtype) { # A tar.gz install ... which we may be able to update return 2; } return 0; } return 0; } # include_usermin_modules() # Returns 1 if Usermin was installed from a tar.gz, 2 if installed from an # RPM but virtualmin-specific modules were from a tar.gz sub include_usermin_modules { if (&foreign_installed("usermin")) { &foreign_require("usermin", "usermin-lib.pl"); my $type = &usermin::get_install_type(); if (!$type) { # Usermin tar.gz install return 1; } else { # How was virtual-server-theme installed? my %miniserv; &usermin::get_usermin_miniserv_config(\%miniserv); my $vtype = &read_file_contents( "$miniserv{'root'}/virtual-server-theme/install-type"); chop($vtype); if (!$vtype) { # A tar.gz install ... which we may be able to update return 2; } return 0; } } return 0; } # webmin_modules_available() # Returns a list of Webmin modules available for update sub webmin_modules_available { my @rv; my @urls = $webmin::config{'upsource'} ? split(/\t+/, $webmin::config{'upsource'}) : ( $webmin::update_url ); my %donewebmin; foreach my $url (@urls) { my ($updates, $host, $port, $page, $ssl) = &webmin::fetch_updates($url, $webmin::config{'upuser'}, $webmin::config{'uppass'}); foreach $u (@$updates) { # Skip modules that are not for this version of Webmin, IF this # is a core module or is not installed my %minfo = &get_module_info($u->[0]); my %tinfo = &get_theme_info($u->[0]); my %info = %minfo ? %minfo : %tinfo; my $noinstall = !%info && $u->[0] !~ /(virtualmin|virtual-server)-/; next if (($u->[1] >= &webmin::get_webmin_base_version() + .01 || $u->[1] < &webmin::get_webmin_base_version()) && ($noinstall || $info{'longdesc'} || !$webmin::config{'upthird'})); # Skip if not supported on this OS my $osinfo = { 'os_support' => $u->[3] }; next if (!&check_os_support($osinfo)); next if ($donewebmin{$u->[0],$u->[1]}++); push(@rv, { 'update' => $u->[0], 'name' => $u->[0], 'system' => 'webmin', 'desc' => $u->[4] || &text('index_webmin', $tinfo{'desc'} || $minfo{'desc'}), 'version' => $u->[1], 'updatesurl' => $url, 'url' => $u->[2], }); } } # Add latest Webmin version available from Virtualmin, but only if was a # tar.gz install my $free = free_virtualmin_licence() || free_cloudmin_licence(); my ($user, $pass) = &get_user_pass(); if (&include_webmin_modules() == 1) { my ($wver, $error); &http_download($virtualmin_host, $virtualmin_port, $free ? $free_webmin_version_path : $webmin_version_path, \$wver, \$error, undef, 0, $user, $pass); $wver =~ s/\r|\n//g; if (!$error) { push(@rv, { 'name' => 'webmin', 'update' => 'webmin', 'system' => 'tgz', 'desc' => 'Webmin Package', 'version' => $wver }); } } # And Usermin if (&include_usermin_modules() == 1) { my ($uver, $error); &http_download($virtualmin_host, $virtualmin_port, $free ? $free_usermin_version_path : $usermin_version_path, \$uver, \$error, undef, 0, $user, $pass); $uver =~ s/\r|\n//g; if (!$error) { push(@rv, { 'name' => 'usermin', 'update' => 'usermin', 'system' => 'tgz', 'desc' => 'Usermin Package', 'version' => $uver }); } } return @rv; } # installation_candiate(&package) # Returns 1 if some package can be installed, even when it currently isn't. # For now, only Virtualmin plugins are considered. sub installation_candiate { my ($p) = @_; if (!defined($webmin_install_type_cache)) { $webmin_install_type_cache = &webmin::get_install_type() || ""; } if (!defined($usermin_install_type_cache)) { &foreign_require("usermin", "usermin-lib.pl"); $usermin_install_type_cache = &usermin::get_install_type() || ""; } return # RPM packages from YUM $p->{'system'} eq 'yum' && $p->{'name'} =~ /^(wbm|usm|wbt|ust)-(virtualmin|virtual-server)/ && $webmin_install_type_cache eq 'rpm' || # Debian packages from APT $p->{'system'} eq 'apt' && $p->{'name'} =~ /^(webmin|usermin)-(virtualmin|virtual-server)/ && $webmin_install_type_cache eq 'deb' || # Webmin modules from .wbms $p->{'system'} eq 'webmin' && $p->{'name'} =~ /^(virtualmin|virtual-server)/ && !$webmin_install_type_cache || # Usermin modules from .wbms $p->{'system'} eq 'usermin' && $p->{'name'} =~ /^(virtualmin|virtual-server)/ && !$usermin_install_type_cache; } # generate_description(package) # Fakes up a description for a Webmin/Usermin module/theme package sub generate_description { my ($p) = @_; my $name = $p->{'name'}; if ($p->{'system'} eq 'yum') { # Use yum info to get the description, and cache it my %yumcache; &read_file_cached($yum_cache_file, \%yumcache); if ($yumcache{$p->{'name'}."-".$p->{'version'}}) { return $yumcache{$p->{'name'}."-".$p->{'version'}}; } my ($desc, $started_desc); open(YUM, "yum info ".quotemeta($name)." |"); while(<YUM>) { s/\r|\n//g; if (/^Description:\s*(.*)$/) { $desc = $1; $started_desc = 1; } elsif (/\S/ && $started_desc) { $desc .= " ".$_; } } close(YUM); $desc =~ s/^\s+//; $yumcache{$p->{'name'}."-".$p->{'version'}} = $desc; &write_file($yum_cache_file, \%yumcache); return $desc if ($desc =~ /\S/); } elsif ($p->{'system'} eq 'apt') { # Use APT to get description my %aptcache; &read_file_cached($apt_cache_file, \%aptcache); if ($aptcache{$p->{'name'}."-".$p->{'version'}}) { return $aptcache{$p->{'name'}."-".$p->{'version'}}; } my ($desc, $started_desc); open(YUM, "apt-cache show ".quotemeta($name)." |"); while(<YUM>) { s/\r|\n//g; if (/^Description:\s*(.*)$/) { $desc = $1; } } close(YUM); $aptcache{$p->{'name'}."-".$p->{'version'}} = $desc; &write_file($apt_cache_file, \%aptcache); return $desc if ($desc =~ /\S/); } return # RPM names $name =~ /^wbm-virtualmin-/ ? "Virtualmin plugin" : $name =~ /^wbm-vm2-/ ? "Cloudmin plugin" : $name =~ /^wbm-/ ? "Webmin module" : $name =~ /^wbt-virtualmin-/ ? "Virtualmin theme" : $name =~ /^wbt-/ ? "Webmin theme" : $name =~ /^usm-/ ? "Usermin module" : $name =~ /^ust-/ ? "Usermin theme" : # Debian names $name =~ /^webmin-virtualmin-/ ? "Virtualmin plugin or theme" : $name =~ /^webmin-vm2-/ ? "Cloudmin plugin" : $name =~ /^webmin-/ ? "Webmin module" : $name =~ /^usermin-virtualmin-/ ? "Usermin theme" : $name =~ /^usermin-/ ? "Usermin module" : undef; } # clear_repository_cache() # Clear any YUM or APT caches sub clear_repository_cache { if ($software::update_system eq "yum") { &execute_command("yum clean all"); } elsif ($software::update_system eq "apt") { &execute_command("apt-get update"); } elsif ($software::update_system eq "ports") { &foreign_require("proc"); foreach my $cmd ("portsnap fetch", "portsnap update || portsnap extract") { my ($fh, $pid) = &proc::pty_process_exec($cmd); while(<$fh>) { } close($fh); } } } # set_pinned_versions(&packages) # If on Debian, set available package versions based on APT pinning sub set_pinned_versions { my ($avail) = @_; my @davail = grep { $_->{'system'} eq 'apt' } @$avail; return 0 if (!@davail); my %nmap = map { $_->{'name'}, $_ } @davail; while(@davail) { # Process 256 at a time, to prevent huge command line my @dwant; while(@davail && @dwant < 256) { push(@dwant, shift(@davail)); } my $cmd = "apt-cache policy ". join(" ", map { quotemeta($_->{'name'}) } @dwant); &open_execute_command(POLICY, "LANG='' LC_ALL='' $cmd 2>/dev/null", 1); my $currpkg; while(<POLICY>) { if (/^(\S+):/) { $currpkg = $nmap{$1}; } elsif (/^\s+Candidate:\s+(\S+)/ && $currpkg) { my $candidate = $1; $candidate = "" if ($candidate eq "(none)"); my $cepoch; if ($candidate =~ s/^(\d+)://) { $cepoch = $1; } if ($currpkg->{'version'} ne $candidate) { $currpkg->{'version'} = $candidate; $currpkg->{'epoch'} = $cepoch; } } } close(POLICY); } } # get_changelog(&pacakge) # If possible, returns information about what has changed in some update sub get_changelog { my ($pkg) = @_; if ($pkg->{'system'} eq 'yum') { # See if yum supports changelog if (!defined($supports_yum_changelog)) { my $out = &backquote_command("yum -h 2>&1 </dev/null"); $supports_yum_changelog = $out =~ /changelog/ ? 1 : 0; } return undef if (!$supports_yum_changelog); # Check if we have this info cached my $cfile = $yum_changelog_cache_dir."/". $pkg->{'name'}."-".$pkg->{'version'}; my $cl = &read_file_contents($cfile); if (!$cl) { # Run it for this package and version my $started = 0; &open_execute_command(YUMCL, "yum changelog all ". quotemeta($pkg->{'name'}), 1, 1); while(<YUMCL>) { s/\r|\n//g; if (/^\Q$pkg->{'name'}-$pkg->{'version'}\E/) { $started = 1; } elsif (/^==========/ || /^changelog stats/) { $started = 0; } elsif ($started) { $cl .= $_."\n"; } } close(YUMCL); # Save the cache if (!-d $yum_changelog_cache_dir) { &make_dir($yum_changelog_cache_dir, 0700); } &open_tempfile(CACHE, ">$cfile"); &print_tempfile(CACHE, $cl); &close_tempfile(CACHE); } return $cl; } return undef; } # Returns 1 if an option should be shown to list all packages. Only true for # YUM and APT at the moment sub show_all_option { return $software::update_system eq 'apt' || $software::update_system eq 'yum'; } sub flush_package_caches { unlink($current_cache_file); unlink($current_all_cache_file); unlink($updates_cache_file); unlink($available_cache_file); unlink($available_cache_file.'0'); unlink($available_cache_file.'1'); @packages_available_cache = ( ); %read_cache_file_cache = ( ); } # list_for_mode(mode, nocache, all) # If not is 'updates' or 'security', return just updates. Othewise, return # all available packages. sub list_for_mode { my ($mode, $nocache, $all) = @_; return $mode eq 'updates' || $mode eq 'security' ? &list_possible_updates($nocache, $all) : &list_available($nocache, $all); } # check_reboot_required(after-flag) # Returns 1 if the package system thinks a reboot is needed sub check_reboot_required { if ($gconfig{'os_type'} eq 'debian-linux') { return -e "/var/run/reboot-required" ? 1 : 0; } return 0; } 1;y~or5J={Eeu磝Qk ᯘG{?+]ן?wM3X^歌>{7پK>on\jy Rg/=fOroNVv~Y+ NGuÝHWyw[eQʨSb> >}Gmx[o[<{Ϯ_qFvM IENDB`