diff --git a/backup-domain.pl b/backup-domain.pl index 5491ee2af..da4d51946 100755 --- a/backup-domain.pl +++ b/backup-domain.pl @@ -116,8 +116,7 @@ package virtual_server; } elsif ($a eq "--feature") { local $f = shift(@ARGV); - $f eq "virtualmin" || $config{$f} || - &indexof($f, &list_backup_plugins()) >= 0 || + &is_enabled_feature($f, &list_backup_plugins()) || &usage("Feature $f is not enabled on this system"); push(@bfeats, $f); } @@ -521,4 +520,3 @@ sub usage print "Multiple destinations can be given by repeating this flag.\n"; exit(1); } - diff --git a/backup.pl b/backup.pl index 74d0e399f..7ed153a5a 100755 --- a/backup.pl +++ b/backup.pl @@ -387,5 +387,3 @@ sub usage print " [--force-email]\n"; exit(1); } - - diff --git a/config b/config index dbb0838f1..4401144e3 100644 --- a/config +++ b/config @@ -3,7 +3,6 @@ mail=1 web=1 dns=1 mysql=1 -ftp=0 webalizer=0 iface= shell=/dev/null @@ -39,7 +38,6 @@ udisplay_max= postgres=0 avail_change-user=1 apache_config=ServerName ${DOM} ServerAlias www.${DOM} ServerAlias mail.${DOM} DocumentRoot ${HOME}/public_html ErrorLog /var/log/virtualmin/${DOM}_error_log CustomLog /var/log/virtualmin/${DOM}_access_log combined ScriptAlias /cgi-bin/ ${HOME}/cgi-bin/ DirectoryIndex index.php index.htm index.html Options -Indexes +IncludesNOEXEC +SymLinksIfOwnerMatch Require all granted AllowOverride All Require all granted AllowOverride All -proftpd_config=ServerName ${DOM} ServerAlias ftp.${DOM} User ftp Group ftp UserAlias anonymous ftp DenyAll RequireValidShell off ExtendedLog ${HOME}/logs/ftp.log domain_template=none subdomain_template=none user_template=none @@ -49,7 +47,7 @@ reseller_template=none edit_afiles=1 edit_homes=0 alias_types=1,2,5,6,7,8,9,10,11,12,13 -disable=unix,mail,web,dns,mysql,postgres,ftp +disable=unix,mail,web,dns,mysql,postgres webmin_theme=* leave_acl=0 hard_quotas=1 @@ -201,7 +199,6 @@ reseller_unix=0 dovecot_ssl=1 postfix_ssl=1 mysql_ssl=1 -proftpd_ssl=1 cert_type=sha2 gzip_mysql=1 show_preview=2 diff --git a/config-freebsd b/config-freebsd index 6b84f2192..fb376314a 100644 --- a/config-freebsd +++ b/config-freebsd @@ -3,7 +3,6 @@ mail=1 web=1 dns=1 mysql=1 -ftp=0 webalizer=0 iface= shell=/dev/null @@ -39,7 +38,6 @@ udisplay_max= postgres=0 avail_change-user=1 apache_config=ServerName ${DOM} ServerAlias www.${DOM} ServerAlias mail.${DOM} DocumentRoot ${HOME}/public_html ErrorLog /var/log/virtualmin/${DOM}_error_log CustomLog /var/log/virtualmin/${DOM}_access_log combined ScriptAlias /cgi-bin/ ${HOME}/cgi-bin/ DirectoryIndex index.php index.htm index.html Options -Indexes +IncludesNOEXEC +SymLinksIfOwnerMatch Require all granted AllowOverride All Require all granted AllowOverride All -proftpd_config=ServerName ${DOM} ServerAlias ftp.${DOM} User nobody Group nogroup UserAlias anonymous nobody DenyAll RequireValidShell off ExtendedLog ${HOME}/logs/ftp.log domain_template=none subdomain_template=none user_template=none @@ -49,7 +47,7 @@ reseller_template=none edit_afiles=1 edit_homes=0 alias_types=1,2,5,6,7,8,9,10,11,12,13 -disable=unix,mail,web,dns,mysql,postgres,ftp +disable=unix,mail,web,dns,mysql,postgres webmin_theme=* leave_acl=0 hard_quotas=1 @@ -201,7 +199,6 @@ reseller_unix=0 dovecot_ssl=1 postfix_ssl=1 mysql_ssl=1 -proftpd_ssl=1 cert_type=sha2 gzip_mysql=1 show_preview=2 diff --git a/config.info b/config.info index 7ef04c040..e2a7ff8ea 100644 --- a/config.info +++ b/config.info @@ -3,7 +3,7 @@ mail_system=Mail server to configure,4,0-Postfix,1-Sendmail,2-Qmail,3-Detect aut generics=Also update outgoing addresses for mailboxes?,1,1-Yes,0-No bccs=Allow automatic BCCing of outgoing email?,1,1-Yes,0-No quotas=Set quotas for domain and mail users?,1,1-Yes (if enabled),0-No -disable=Features to deactivate when disabling,13,unix-Administration user (lock account),mail-Mail (lock mailboxes),web-Website (replace site with error page),dns-DNS (stop serving domain),mysql-MySQL (disallow login by MySQL user),postgres-PostgreSQL (disallow login by PostgreSQL user),ftp-ProFTPD (deny access) +disable=Features to deactivate when disabling,13,unix-Administration user (lock account),mail-Mail (lock mailboxes),web-Website (replace site with error page),dns-DNS (stop serving domain),mysql-MySQL (disallow login by MySQL user),postgres-PostgreSQL (disallow login by PostgreSQL user) disable_mail=When disabling email,1,1-Just lock mailboxes,0-Lock mailboxes and stop accepting email ldap=Store users and groups,1,1-In LDAP database,0-In local files preload_mode=Preload Virtualmin libraries at startup?,1,2-All libraries,1-Only core,0-No diff --git a/copycert-lib.pl b/copycert-lib.pl index 68f460ba6..1934e30fd 100644 --- a/copycert-lib.pl +++ b/copycert-lib.pl @@ -38,10 +38,10 @@ sub list_service_ssl_cert_types 'virt' => 1, 'short' => 'p' }); } -if ($config{'ftp'} || &foreign_installed("proftpd")) { +if (&has_proftpd_support()) { push(@rv, {'id' => 'proftpd', 'dom' => 0, - 'virt' => 1, + 'virt' => 0, 'short' => 'f' }); } if ($config{'mysql'}) { @@ -254,48 +254,23 @@ sub get_all_service_ssl_certs } } -if ($config{'ftp'} || &foreign_installed("proftpd")) { - # Check ProFTPd per-IP certificate - &foreign_require("proftpd"); - my $conf = &proftpd::get_config(); - if ($perip) { - my ($virt, $vconf) = &get_proftpd_virtual($d); - if ($virt) { - my $cfile = &proftpd::find_directive( - "TLSRSACertificateFile", $vconf); - $cfile ||= &proftpd::find_directive( - "TLSECCertificateFile", $vconf); - my $kfile = &proftpd::find_directive( - "TLSRSACertificateKeyFile", $vconf); - $kfile ||= &proftpd::find_directive( - "TLSECCertificateKeyFile", $vconf); - my $cafile = &proftpd::find_directive( - "TLSCACertificateFile", $vconf); - if ($cfile) { - push(@svcs, { 'id' => 'proftpd', - 'cert' => $cfile, - 'key' => $kfile, - 'ca' => $cafile, - 'prefix' => 'ftp', - 'port' => 990, - 'ip' => $d->{'ip'}, - 'd' => $d, - }); - } - } - } - +my $proftpd_conf; +if (&has_proftpd_support()) { # Check ProFTPd global certificate + &foreign_require("proftpd"); + $proftpd_conf = &proftpd::get_config(); + } +if ($proftpd_conf) { my $cfile = &proftpd::find_directive( - "TLSRSACertificateFile", $conf); + "TLSRSACertificateFile", $proftpd_conf); $cfile ||= &proftpd::find_directive( - "TLSECCertificateFile", $conf); + "TLSECCertificateFile", $proftpd_conf); my $kfile = &proftpd::find_directive( - "TLSRSACertificateKeyFile", $conf); + "TLSRSACertificateKeyFile", $proftpd_conf); $kfile ||= &proftpd::find_directive( - "TLSECCertificateKeyFile", $conf); + "TLSECCertificateKeyFile", $proftpd_conf); my $cafile = &proftpd::find_directive( - "TLSCACertificateFile", $conf); + "TLSCACertificateFile", $proftpd_conf); if ($cfile) { push(@svcs, { 'id' => 'proftpd', 'cert' => $cfile, @@ -395,7 +370,9 @@ sub update_all_domain_service_ssl_certs &push_all_print(); &set_all_null_print(); foreach my $svc (@$before) { - if ($tmpl->{'web_'.$svc->{'id'}.'_ssl'}) { + # Global service certs were explicitly copied, so keep them current. + my $enabled = $svc->{'d'} ? $tmpl->{'web_'.$svc->{'id'}.'_ssl'} : 1; + if ($enabled) { if ($svc->{'d'}) { my $func = "sync_".$svc->{'id'}."_ssl_cert"; &$func($d, 1) if (defined(&$func)); @@ -702,6 +679,7 @@ sub copy_proftpd_ssl_service my ($d) = @_; # Get the ProFTPd config and cert files +&has_proftpd_support() || &error($text{'copycert_eproftpd'}); &foreign_require("proftpd"); &proftpd::lock_proftpd_files(); my $conf = &proftpd::get_config(); diff --git a/create-scheduled-backup.pl b/create-scheduled-backup.pl index 24efc6b7b..c16bac93a 100755 --- a/create-scheduled-backup.pl +++ b/create-scheduled-backup.pl @@ -122,8 +122,7 @@ package virtual_server; } elsif ($a eq "--feature") { local $f = shift(@ARGV); - $f eq "virtualmin" || $config{$f} || - &indexof($f, &list_backup_plugins()) >= 0 || + &is_enabled_feature($f, &list_backup_plugins()) || &usage("Feature $f is not enabled on this system"); push(@bfeats, $f); } @@ -437,4 +436,3 @@ sub usage print "Multiple destinations can be given by repeating this flag.\n"; exit(1); } - diff --git a/edit_newbw.cgi b/edit_newbw.cgi index 676680968..d9493df09 100755 --- a/edit_newbw.cgi +++ b/edit_newbw.cgi @@ -126,7 +126,7 @@ print &ui_table_row(&hlink($text{'newbw_servers'}, "bandwidth_serversmode"), [ &list_visible_domains() ])); # Log files for FTP and mail -$defftplog = $config{'ftp'} ? &get_proftpd_log() : undef; +$defftplog = &get_proftpd_log(); print &ui_table_row(&hlink($text{'newbw_ftplog'}, "bandwidth_ftplog_def"), &ui_opt_textbox("ftplog", $config{'bw_ftplog'}, 40, $defftplog ? &text('newbw_ftplogdef', "$defftplog")."
" @@ -175,4 +175,3 @@ if ($config{'bw_active'} && $virtualmin_pro) { print &ui_buttons_end(); &ui_print_footer("", $text{'index_return'}); - diff --git a/edit_user.cgi b/edit_user.cgi index 3d2b92f67..6fcecaf8a 100755 --- a/edit_user.cgi +++ b/edit_user.cgi @@ -157,7 +157,7 @@ elsif ($user_type eq 'ftp') { $user = &create_initial_user($d, undef, 1); # FTP user in a sub-server .. check if FTP restrictions are active - if ($user->{'webowner'} && $d->{'parent'} && $config{'ftp'}) { + if ($user->{'webowner'} && $d->{'parent'} && &has_ftp_chroot()) { my @chroots = &list_ftp_chroots(); my ($home) = grep { $_->{'dir'} eq '~' } @chroots; if (!$home) { @@ -617,7 +617,7 @@ else { } # FTP user in a sub-server .. check if FTP restrictions are active - if ($user->{'webowner'} && $d->{'parent'} && $config{'ftp'}) { + if ($user->{'webowner'} && $d->{'parent'} && &has_ftp_chroot()) { my @chroots = &list_ftp_chroots(); my ($home) = grep { $_->{'dir'} eq '~' } @chroots; if (!$home) { diff --git a/feature-ftp.pl b/feature-ftp.pl deleted file mode 100755 index c10fcdb69..000000000 --- a/feature-ftp.pl +++ /dev/null @@ -1,765 +0,0 @@ -# Functions for managing a virtual FTP server - -sub require_proftpd -{ -return if ($require_proftpd++); -&foreign_require("proftpd"); -} - -# check_depends_ftp(&domain) -# Ensure that a domain with FTP enabled has a home directory and possibly a -# private IP -sub check_depends_ftp -{ -my ($d) = @_; -if (!$d->{'dir'}) { - return $text{'setup_edepftpdir'}; - } -if (!$d->{'virt'} && !&supports_namebased_ftp()) { - return $text{'setup_edepftp'}; - } -return undef; -} - -# feature_depends_ftp() -# Checks for dependencies for the FTP feature -sub feature_depends_ftp -{ -# Feature has no explicit dependencies -return 1; -} - -# setup_ftp(&domain) -# Setup a virtual FTP server for some domain -sub setup_ftp -{ -my ($d) = @_; -my $tmpl = &get_template($d->{'template'}); -&$first_print($text{'setup_proftpd'}); -&obtain_lock_ftp($d); -&require_proftpd(); - -# Get the template -my @dirs = &proftpd_template($tmpl->{'ftp'}, $d); - -# Add the directives -my $conf = &proftpd::get_config(); -my $l = $conf->[@$conf - 1]; -my $addfile = $proftpd::config{'add_file'} || $l->{'file'}; -my $lref = &read_file_lines($addfile); -my @ips = &get_proftpd_virtualhost_ips($d); -my @lines = ( "" ); -push(@lines, @dirs); -push(@lines, ""); -push(@$lref, @lines); -&flush_file_lines($addfile); - -# Create directory for FTP root -my ($fdir) = ($tmpl->{'ftp_dir'} || 'ftp'); -my $ftp = "$d->{'home'}/$fdir"; -if (!-d $ftp) { - &make_dir($ftp, 0755); - &set_ownership_permissions($d->{'uid'}, $d->{'ugid'}, 0755, $ftp); - } - -&release_lock_ftp($d); -&$second_print($text{'setup_done'}); -®ister_post_action(\&restart_proftpd); -undef(@proftpd::get_config_cache); - -# Add the FTP server user to the domain's group, so that the directory -# can be accessed in anonymous mode -my $ftp_user = &get_proftpd_user($d); -if ($ftp_user) { - &add_user_to_domain_group($d, $ftp_user, 'setup_ftpuser'); - } -return 1; -} - -# delete_ftp(&domain) -# Delete the virtual server from the ProFTPd config -sub delete_ftp -{ -my ($d) = @_; -&require_proftpd(); -&$first_print($text{'delete_proftpd'}); -&obtain_lock_ftp($d); -my ($virt, $vconf, $conf) = &get_proftpd_virtual($d); -if ($virt) { - my $lref = &read_file_lines($virt->{'file'}); - splice(@$lref, $virt->{'line'}, $virt->{'eline'} - $virt->{'line'} + 1); - &flush_file_lines(); - - &$second_print($text{'setup_done'}); - ®ister_post_action(\&restart_proftpd); - undef(@proftpd::get_config_cache); - } -else { - &$second_print($text{'delete_noproftpd'}); - } -&release_lock_ftp($d); -return 1; -} - -# clone_ftp(&domain, &old-domain) -# Copy proftpd directives to a new cloned domain -sub clone_ftp -{ -my ($d, $oldd) = @_; -&$first_print($text{'clone_ftp'}); -&require_proftpd(); -my ($virt, $vconf, $conf, $anon, $aconf) = &get_proftpd_virtual($d); -my ($ovirt, $ovconf) = &get_proftpd_virtual($oldd); -if (!$ovirt) { - &$second_print($text{'clone_ftpold'}); - return 0; - } -if (!$virt) { - &$second_print($text{'clone_ftpnew'}); - return 0; - } -&obtain_lock_ftp($d); - -# Splice across directives, fixing home -my $olref = &read_file_lines($ovirt->{'file'}); -my $lref = &read_file_lines($virt->{'file'}); -my @lines = @$olref[$ovirt->{'line'}+1 .. $ovirt->{'eline'}-1]; -foreach my $l (@lines) { - $l =~ s/\Q$oldd->{'home'}\E/$d->{'home'}/; - } -splice(@$lref, $virt->{'line'}+1, $virt->{'eline'}-$virt->{'line'}-1, @lines); -&flush_file_lines($virt->{'file'}); -($virt, $vconf, $conf, $anon, $aconf) = &get_proftpd_virtual($d); - -# Fix server name -my $sname = &proftpd::find_directive_struct("ServerName", $vconf); -if ($sname) { - &proftpd::save_directive("ServerName", [ $d->{'dom'} ], $vconf, $conf); - &flush_file_lines($virt->{'file'}); - } - -&release_lock_ftp($d); -®ister_post_action(\&restart_proftpd); -&$second_print($text{'setup_done'}); -return 1; -} - -# modify_ftp(&domain, &olddomain) -# If the server has changed IP address, update the ProFTPd virtual server -sub modify_ftp -{ -my ($d, $oldd) = @_; -my $rv = 0; - -if ($d->{'dom'} eq $oldd->{'dom'} && - $d->{'home'} eq $oldd->{'home'} && - $d->{'ip'} eq $oldd->{'ip'}) { - # Nothing important has changed, so exit now - return 1; - } - -&obtain_lock_ftp($d); -&require_proftpd(); -my ($virt, $vconf, $conf, $anon, $aconf) = &get_proftpd_virtual($oldd); -if (!$virt) { - &release_lock_ftp($d); - return 0; - } -if ($d->{'dom'} ne $oldd->{'dom'}) { - # Update domain name in ProFTPd servername or alias - &$first_print($text{'save_proftpd2'}); - my $sname = &proftpd::find_directive_struct("ServerName", $vconf); - if ($sname) { - &proftpd::save_directive( - "ServerName", [ $d->{'dom'} ], $vconf, $conf); - } - my @sa = map { s/\Q$oldd->{'dom'}\E/$d->{'dom'}/g; $_ } - &apache::find_directive("ServerAlias", $vconf); - &proftpd::save_directive("ServerAlias", \@sa, $vconf, $conf); - $rv++; - &$second_print($text{'setup_done'}); - } -if ($d->{'home'} ne $oldd->{'home'} && $anon) { - # Update anonymous FTP directory in ProFTPd virtual server - &$first_print($text{'save_proftpd3'}); - my $lref = &read_file_lines($anon->{'file'}); - $lref->[$anon->{'line'}] =~ s/$oldd->{'home'}/$d->{'home'}/; - &flush_file_lines($anon->{'file'}); - $rv++; - &$second_print($text{'setup_done'}); - } -if ($d->{'ip'} ne $oldd->{'ip'} || $d->{'ip6'} ne $oldd->{'ip6'} || - $d->{'dom'} ne $oldd->{'dom'}) { - # Update IP address in ProFTPd virtualhost - &$first_print($text{'save_proftpd'}); - my $lref = &read_file_lines($virt->{'file'}); - my @ips = &get_proftpd_virtualhost_ips($d); - $lref->[$virt->{'line'}] = ""; - &flush_file_lines(); - $rv++; - &$second_print($text{'setup_done'}); - } -&release_lock_ftp($d); -®ister_post_action(\&restart_proftpd) if ($rv); -return $rv; -} - -# validate_ftp(&domain) -# Returns an error message if a domain's ProFTPd virtual server is not found -sub validate_ftp -{ -my ($d) = @_; -my ($virt, $vconf, $conf, $anon, $aconf) = &get_proftpd_virtual($d); -return &text('validate_eftp', $d->{'ip'}) if (!$virt); -return undef; -} - -# disable_ftp(&domain) -# Disable FTP for this server by adding a deny directive -sub disable_ftp -{ -my ($d) = @_; -&$first_print($text{'disable_proftpd'}); -&obtain_lock_ftp($d); -&require_proftpd(); -my $ok; -my ($virt, $vconf, $conf, $anon, $aconf) = &get_proftpd_virtual($d); -if ($anon) { - local @limit = &proftpd::find_directive_struct("Limit", $aconf); - local ($login) = grep { $_->{'words'}->[0] eq "LOGIN" } @limit; - if (!$login) { - local $lref = &read_file_lines($anon->{'file'}); - splice(@$lref, $anon->{'eline'}, 0, - "", "DenyAll", ""); - &flush_file_lines(); - } - &$second_print($text{'setup_done'}); - ®ister_post_action(\&restart_proftpd); - $ok = 1; - } -else { - &$second_print($text{'delete_noproftpd'}); - $ok = 0; - } -&release_lock_ftp($d); -return $ok; -} - -# enable_ftp(&domain) -# Enable FTP for this server by removing the deny directive -sub enable_ftp -{ -my ($d) = @_; -&$first_print($text{'enable_proftpd'}); -&obtain_lock_ftp($d); -&require_proftpd(); -my ($virt, $vconf, $conf, $anon, $aconf) = &get_proftpd_virtual($d); -my $ok; -if ($virt) { - local @limit = &proftpd::find_directive_struct("Limit", $aconf); - local ($login) = grep { $_->{'words'}->[0] eq "LOGIN" } @limit; - if ($login) { - local $lref = &read_file_lines($anon->{'file'}); - splice(@$lref, $login->{'line'}, - $login->{'eline'} - $login->{'line'} + 1); - &flush_file_lines(); - } - &$second_print($text{'setup_done'}); - ®ister_post_action(\&restart_proftpd); - $ok = 1; - } -else { - &$second_print($text{'delete_noproftpd'}); - $ok = 0; - } -&release_lock_ftp($d); -return $ok; -} - -# proftpd_template(text, &domain) -# Returns a suitably substituted ProFTPd template -sub proftpd_template -{ -my ($dirs, $d) = @_; -$dirs =~ s/\t/\n/g; -$dirs = &substitute_domain_template($dirs, $d); -local @dirs = split(/\n/, $dirs); -return @dirs; -} - -# check_proftpd_template([directives]) -# Returns an error message if the default ProFTPd directives don't look valid -sub check_proftpd_template -{ -my ($d, $gotuser, $gotgroup); -my @dirs = split(/\t+/, defined($_[0]) ? $_[0] : $config{'proftpd_config'}); -foreach $d (@dirs) { - $d =~ s/#.*$//; - if ($d =~ /^\s*User\s+(\S+)$/i) { - defined(getpwnam($1)) || - $1 eq '$USER' || $1 eq '${USER}' || - return &text('fcheck_euserex', "$1"); - $gotuser++; - } - elsif ($d =~ /^\s*Group\s+(\S+)$/i) { - defined(getgrnam($1)) || - $1 eq '$GROUP' || $1 eq '${GROUP}' || - return &text('fcheck_egroupex', "$1"); - $gotgroup++; - } - } -$gotuser || return $text{'fcheck_euser'}; -$gotgroup || return $text{'fcheck_egroup'}; -return undef; -} - -# restart_proftpd() -# Tell ProFTPd to re-read its config file. Does nothing if run from inetd -sub restart_proftpd -{ -&require_proftpd(); -my $conf = &proftpd::get_config(); -my $st = &proftpd::find_directive("ServerType", $conf); -if (lc($st) ne "inetd") { - # Call proftpd restart function - &$first_print($text{'setup_proftpdpid'}); - local $proftpdlock = "$module_config_directory/proftpd-restart"; - &lock_file($proftpdlock); - local $err = &proftpd::apply_configuration(); - &unlock_file($proftpdlock); - &$second_print($err ? &text('setup_proftpdfailed', $err) - : $text{'setup_done'}); - return $err ? 0 : 1; - } -} - -# get_proftpd_virtual(ip|&domain) -# Returns the list of configuration directives and the directive for the -# virtual server itself for some domain -sub get_proftpd_virtual -{ -my ($ip) = @_; -my @match; -if (ref($ip)) { - @match = ( $ip->{'ip'}, $ip->{'dom'} ); - } -else { - @match = ( $ip ); - } -&require_proftpd(); -my $conf = &proftpd::get_config(); -foreach my $v (&proftpd::find_directive_struct("VirtualHost", $conf)) { - if (&indexof($v->{'words'}->[0], @match) >= 0) { - # Found it! Look for the anonymous block - my @rv = ($v, $v->{'members'}, $conf); - my $a = &proftpd::find_directive_struct( - "Anonymous", $v->{'members'}); - if ($a) { - push(@rv, $a, $a->{'members'}); - } - return @rv; - } - } -return (); -} - -# check_ftp_clash(&domain, [field]) -# Returns 1 if a ProFTPd server already exists for some domain -sub check_ftp_clash -{ -local ($d, $field) = @_; -if (!$field || $field eq 'ip') { - local ($cvirt, $cconf) = &get_proftpd_virtual($d); - return $cvirt ? 1 : 0; - } -return 0; -} - -# backup_ftp(&domain, file) -# Save the virtual server's ProFTPd config as a separate file -sub backup_ftp -{ -my ($d, $file) = @_; -&$first_print($text{'backup_proftpdcp'}); -my ($virt, $vconf) = &get_proftpd_virtual($d); -if ($virt) { - my $lref = &read_file_lines($virt->{'file'}); - &open_tempfile_as_domain_user($d, FILE, ">$file"); - foreach my $l (@$lref[$virt->{'line'} .. $virt->{'eline'}]) { - &print_tempfile(FILE, "$l\n"); - } - &close_tempfile_as_domain_user($d, FILE); - &$second_print($text{'setup_done'}); - return 1; - } -else { - &$second_print($text{'delete_noproftpd'}); - return 0; - } -} - -# restore_ftp(&domain, file) -# Update the virtual server's ProFTPd configuration from a file. Does not -# change the actual lines! -sub restore_ftp -{ -my ($d, $file, $opts, $allopts, $homefmt, $oldd) = @_; -&$first_print($text{'restore_proftpdcp'}); -&obtain_lock_ftp($d); -my ($virt, $vconf) = &get_proftpd_virtual($d); -my $rv; -if ($virt) { - my $srclref = &read_file_lines($file); - my $dstlref = &read_file_lines($virt->{'file'}); - splice(@$dstlref, $virt->{'line'}+1, $virt->{'eline'}-$virt->{'line'}-1, - @$srclref[1 .. @$srclref-2]); - if ($oldd && $oldd->{'home'} && $oldd->{'home'} ne $d->{'home'}) { - # Fix up any file-related directives - foreach my $i ($virt->{'line'} .. $virt->{'line'}+scalar(@$srclref)-1) { - $dstlref->[$i] =~ s/$oldd->{'home'}/$d->{'home'}/g; - } - } - &flush_file_lines(); - &$second_print($text{'setup_done'}); - ®ister_post_action(\&restart_proftpd); - $rv = 1; - } -else { - &$second_print($text{'delete_noproftpd'}); - $rv = 0; - } - -&release_lock_ftp($d); -return $rv; -} - -# get_proftpd_log([&domain]) -# Given a virtual server IP, returns the path to its log file. If no IP is -# give, returns the global log file path. -sub get_proftpd_log -{ -my ($d) = @_; -if (!&foreign_check("proftpd")) { - return undef; - } -&require_proftpd(); -if ($d) { - # Find by domain - local ($virt, $vconf) = &get_proftpd_virtual($d); - if ($virt) { - return &proftpd::find_directive("ExtendedLog", $vconf) || - &proftpd::find_directive("TransferLog", $vconf); - } - } -else { - # Just return global log - my $conf = &proftpd::get_config(); - my $global = &proftpd::find_directive_struct("Global", $conf); - return &proftpd::find_directive("TransferLog", $global->{'members'}) || - &proftpd::find_directive("ExtendedLog", $global->{'members'}) || - &proftpd::find_directive("TransferLog", $conf) || - &proftpd::find_directive("ExtendedLog", $conf) || - "/var/log/xferlog"; - } -return undef; -} - -# bandwidth_ftp(&domain, start, &bw-hash) -# Searches through FTP log files for records after some date, and updates the -# day counters in the given hash -sub bandwidth_ftp -{ -my ($d, $start, $bwinfo) = @_; -my $log = &get_proftpd_log($d); -if ($log) { - return &count_ftp_bandwidth($log, $start, $bwinfo, undef, "ftp"); - } -else { - return $start; - } -} - -# sysinfo_ftp() -# Returns the ProFTPd version -sub sysinfo_ftp -{ -&require_proftpd(); -$proftpd::site{'version'} ||= &proftpd::get_proftpd_version(); -return ( [ $text{'sysinfo_proftpd'}, $proftpd::site{'version'} ] ); -} - -sub startstop_ftp -{ -my ($typestatus) = @_; -&require_proftpd(); -my $conf = &proftpd::get_config(); -my $st = &proftpd::find_directive("ServerType", $conf); -if ($st eq 'inetd') { - # Running under inetd - return ( ); - } -my $status; -if (defined($typestatus->{'proftpd'})) { - $status = $typestatus->{'proftpd'} == 1; - } -else { - $status = &proftpd::get_proftpd_pid(); - } -my @links = ( { 'link' => '/proftpd/', - 'desc' => $text{'index_fmanage'}, - 'manage' => 1 } ); -if ($status) { - return ( { 'status' => 1, - 'name' => $text{'index_fname'}, - 'desc' => $text{'index_fstop'}, - 'restartdesc' => $text{'index_frestart'}, - 'longdesc' => $text{'index_fstopdesc'}, - 'links' => \@links } ); - } -else { - return ( { 'status' => 0, - 'name' => $text{'index_fname'}, - 'desc' => $text{'index_fstart'}, - 'longdesc' => $text{'index_fstartdesc'}, - 'links' => \@links } ); - } -} - -# Call proftpd module's stop function -sub stop_service_ftp -{ -&require_proftpd(); -return &proftpd::stop_proftpd(); -} - -sub start_service_ftp -{ -&require_proftpd(); -return &proftpd::start_proftpd(); -} - -# show_template_ftp(&tmpl) -# Outputs HTML for editing ProFTPd related template options -sub show_template_ftp -{ -my ($tmpl) = @_; - -# ProFTPd directives -my @ffields = ( "ftp", "ftp_dir", "ftp_dir_def" ); -my $ndi = &none_def_input("ftp", $tmpl->{'ftp'}, $text{'tmpl_ftpbelow'}, 1, - 0, undef, \@ffields); -print &ui_table_row(&hlink($text{'tmpl_ftp'}, "template_ftp"), - $ndi."\n". - &ui_textarea("ftp", $tmpl->{'ftp'} eq "none" ? "" : - join("\n", split(/\t/, $tmpl->{'ftp'})), - 10, 60)); - -# Directory for anonymous FTP -print &ui_table_row(&hlink($text{'newftp_dir'}, "template_ftp_dir_def"), - &ui_opt_textbox("ftp_dir", $tmpl->{'ftp_dir'}, 20, - "$text{'default'} (ftp)", - $text{'newftp_dir0'})." \n". - &vui_note($text{'newftp_dir0suf'})); -} - -# parse_template_ftp(&tmpl) -# Updates ProFTPd related template options from %in -sub parse_template_ftp -{ -my ($tmpl) = @_; - -# Save FTP directives -$tmpl->{'ftp'} = &parse_none_def("ftp"); -if ($in{"ftp_mode"} == 2) { - my $err = &check_proftpd_template($in{'ftp'}); - &error($err) if ($err); - if ($in{'ftp_dir_def'}) { - delete($tmpl->{'ftp_dir'}); - } - else { - $in{'ftp_dir'} =~ /^\S+$/ && $in{'ftp_dir'} !~ /^\// && - $in{'ftp_dir'} !~ /\.\./ || &error($text{'newftp_edir'}); - $tmpl->{'ftp_dir'} = $in{'ftp_dir'}; - } - } -} - -# Return links to FTP log -sub links_ftp -{ -my ($d) = @_; -my @rv; -my $lf = &get_proftpd_log($d); -if ($lf) { - local $param = &master_admin() ? "file" - : "extra"; - push(@rv, { 'mod' => 'logviewer', - 'desc' => $text{'links_flog'}, - 'page' => "view_log.cgi?view=1&nonavlinks=1". - "&linktitle=".&urlize($text{'links_flog'})."&". - "$param=".&urlize($lf), - 'cat' => 'logs', - }); - } -return @rv; -} - -# get_proftpd_user(&domain) -# Returns the Unix user that anonymous FTP access is done as. This is just -# taken from the User line in the template directives. -sub get_proftpd_user -{ -my ($d) = @_; -my $tmpl = &get_template($d->{'template'}); -my @dirs = &proftpd_template($tmpl->{'ftp'}, $d); -foreach my $l (@dirs) { - if ($l =~ /^\s*User\s+(\S+)/) { - return $1; - } - } -foreach my $u ("ftp", "anonymous") { - return $u if (defined(getpwnam($u))); - } -return undef; -} - -# Returns 1 if we can configure FTP chroot directories. Assume yes if proftpd -# is being used -sub has_ftp_chroot -{ -return $config{'ftp'} || !&feature_depends_ftp(); -} - -# list_ftp_chroots() -# Returns a list of chroot directories. Each is a hash ref with keys : -# group - A group to restrict, or undef for all -# neg - Negative if to apply to everyone except that group -# dir - The chroot directory, or ~ for users' homes -sub list_ftp_chroots -{ -my @rv; -&require_proftpd(); -my $conf = &proftpd::get_config(); -$proftpd::conf = $conf; # get_or_create is broken in Webmin 1.410 -my $gconf = &proftpd::get_or_create_global($conf); -foreach my $dr (&proftpd::find_directive_struct("DefaultRoot", $gconf)) { - local $chroot = { 'dr' => $dr, - 'dir' => $dr->{'words'}->[0] }; - if ($dr->{'words'}->[1] eq '') { - # Applies to all groups - } - elsif ($dr->{'words'}->[1] =~ /,/) { - # Applies to many .. too complex to support - next; - } - elsif ($dr->{'words'}->[1] =~ /^(\!?)(\S+)$/) { - $chroot->{'neg'} = $1; - $chroot->{'group'} = $2; - } - push(@rv, $chroot); - } -return @rv; -} - -# save_ftp_chroots(&chroots) -# Updates the list of chroot'd directories. -sub save_ftp_chroots -{ -my ($chroots) = @_; -&require_proftpd(); -my $conf = &proftpd::get_config(); -$proftpd::conf = $conf; -my $gconf = &proftpd::get_or_create_global($conf); - -# Find old directives that we can't configure yet -my @old = &proftpd::find_directive_struct("DefaultRoot", $gconf); -my @keep = grep { $_->{'words'}->[1] =~ /,/ } @old; -my @newv = map { $_->{'value'} } @keep; - -# Add new ones -foreach my $chroot (@$chroots) { - my @w = ( $chroot->{'dir'} ); - if ($chroot->{'group'}) { - push(@w, ($chroot->{'neg'} ? "!" : "").$chroot->{'group'}); - } - push(@newv, join(" ", @w)); - } -&proftpd::save_directive("DefaultRoot", \@newv, $gconf, $conf); -&flush_file_lines(); - -®ister_post_action(\&restart_proftpd); -} - -# Lock the ProFTPd config file -sub obtain_lock_ftp -{ -return if (!$config{'ftp'}); -&obtain_lock_anything(); -if ($main::got_lock_ftp == 0) { - &require_proftpd(); - &lock_file($proftpd::config{'proftpd_conf'}); - &lock_file($proftpd::config{'add_file'}) - if ($proftpd::config{'add_file'}); - undef(@proftpd::get_config_cache); - } -$main::got_lock_ftp++; -} - -# Unlock the ProFTPd config file -sub release_lock_ftp -{ -return if (!$config{'ftp'}); -if ($main::got_lock_ftp == 1) { - &require_proftpd(); - &unlock_file($proftpd::config{'proftpd_conf'}); - &unlock_file($proftpd::config{'add_file'}) - if ($proftpd::config{'add_file'}); - } -$main::got_lock_ftp-- if ($main::got_lock_ftp); -&release_lock_anything(); -} - -# supports_namebased_ftp() -# Returns 1 if ProFTPd supports name-based FTP sites -sub supports_namebased_ftp -{ -&require_proftpd(); -$proftpd::site{'version'} ||= &proftpd::get_proftpd_version(); -return $proftpd::site{'version'} >= 1.36; -} - -# get_proftpd_virtualhost_ips(&domain) -# Returns the IPs or hostnames for use in a block -sub get_proftpd_virtualhost_ips -{ -my ($d) = @_; -my @ips; -if ($d->{'virt'}) { - # Accept all connections on an IP - @ips = ( $d->{'ip'} ); - if ($d->{'virt6'}) { - push(@ips, $d->{'ip6'}); - } - } -else { - # Match by hostname - @ips = ( $d->{'dom'} ); - } -return @ips; -} - -# feature_depends_ftp() -# Return undef if the requirements for virtual FTP are met, or an error message -sub feature_depends_ftp -{ -my $mclink = "../config.cgi?$module_name"; -&foreign_installed("proftpd", 1) == 2 || - return &text('index_eproftpd', "/proftpd/", $clink); -my $err = &check_proftpd_template(); -return $err ? &text('check_proftpd', $err) : undef; -} - -$done_feature_script{'ftp'} = 1; - -1; - diff --git a/feature-logrotate.pl b/feature-logrotate.pl index a1b911d3e..682cbb236 100755 --- a/feature-logrotate.pl +++ b/feature-logrotate.pl @@ -494,7 +494,7 @@ sub show_template_logrotate print &ui_table_row( &hlink($text{'tmpl_logrotate'}, "template_logrotate"), &none_def_input("logrotate", $tmpl->{'logrotate'}, - $text{'tmpl_ftpbelow'}, 0, 0, + $text{'tmpl_logrotatebelow'}, 0, 0, $text{'tmpl_logrotatenone'}, [ "logrotate" ])."
\n". &ui_textarea("logrotate", @@ -504,10 +504,10 @@ sub show_template_logrotate # Additional files to rotate print &ui_table_row( - &hlink($text{'tmpl_logrotate_files'}, "template_logrotatefiles"), + &hlink($text{'tmpl_logrotate_files'}, "template_logrotatefiles"), &none_def_input("logrotate_files", $tmpl->{'logrotate_files'}, - $text{'tmpl_ftpbelow2'}, 0, 0, - $text{'tmpl_logrotatenone2'}, + $text{'tmpl_logrotatebelow2'}, 0, 0, + $text{'tmpl_logrotatenone2'}, [ "logrotate_files" ])."
\n". &ui_textarea("logrotate_files", $tmpl->{'logrotate_files'} eq 'none' ? '' : @@ -663,9 +663,6 @@ sub get_all_domain_logs my $alog = &get_website_log($d, 0); my $elog = &get_website_log($d, 1); my @logs = ( $alog, $elog ); -if ($d->{'ftp'}) { - push(@logs, &get_proftpd_log($d)); - } push(@logs, $d->{'php_error_log'} || &get_domain_php_error_log($d)); if ($tmpl) { push(@logs, &get_domain_template_logs($d)); @@ -694,4 +691,3 @@ sub get_domain_template_logs $done_feature_script{'logrotate'} = 1; 1; - diff --git a/feature-ssl.pl b/feature-ssl.pl index 3b40c1e63..fc001f9f8 100755 --- a/feature-ssl.pl +++ b/feature-ssl.pl @@ -3032,58 +3032,6 @@ sub update_ssl_postfix_service } } -# sync_proftpd_ssl_cert(&domain, enable) -# Configure ProFTPd to use a domain's SSL cert for connections on its IP -sub sync_proftpd_ssl_cert -{ -my ($d, $enable) = @_; -&foreign_require("proftpd"); -&proftpd::lock_proftpd_files(); -my ($virt, $vconf, $conf) = &get_proftpd_virtual($d); -return 0 if (!$virt); -if ($enable) { - # Make proftpd virtualhost use domain's SSL cert files - my $cfile = &get_website_ssl_file($d, "cert"); - my $kfile = &get_website_ssl_file($d, "key"); - if (&get_ssl_key_type($kfile) eq 'ec') { - &proftpd::save_directive( - "TLSECCertificateFile", [ $cfile ], $vconf, $conf); - &proftpd::save_directive( - "TLSECCertificateKeyFile", [ $kfile ], $vconf, $conf); - } - else { - &proftpd::save_directive( - "TLSRSACertificateFile", [ $cfile ], $vconf, $conf); - &proftpd::save_directive( - "TLSRSACertificateKeyFile", [ $kfile ], $vconf, $conf); - } - my $cafile = &get_website_ssl_file($d, "ca"); - &proftpd::save_directive( - "TLSCACertificateFile", $cafile ? [ $cafile ] : [ ], $vconf, $conf); - &proftpd::save_directive("TLSEngine", [ "on" ], $vconf, $conf); - &proftpd::save_directive("TLSOptions", [ "NoSessionReuseRequired" ], $vconf, $conf); - } -else { - # Remove SSL cert for domain - &proftpd::save_directive( - "TLSRSACertificateFile", [ ], $vconf, $conf); - &proftpd::save_directive( - "TLSRSACertificateKeyFile", [ ], $vconf, $conf); - &proftpd::save_directive( - "TLSECCertificateFile", [ ], $vconf, $conf); - &proftpd::save_directive( - "TLSECCertificateKeyFile", [ ], $vconf, $conf); - &proftpd::save_directive( - "TLSCACertificateFile", [ ], $vconf, $conf); - &proftpd::save_directive("TLSEngine", [ ], $vconf, $conf); - &proftpd::save_directive("TLSOptions", [ ], $vconf, $conf); - } -&flush_file_lines($virt->{'file'}, undef, 1); -&proftpd::unlock_proftpd_files(); -®ister_post_action(\&restart_proftpd); -return 1; -} - # get_postfix_ssl_cert(&domain) # Returns the path to the cert, key and CA cert in the Postfix config for # a domain, if any @@ -4083,7 +4031,7 @@ sub show_template_ssl "template_web_usermin_ssl"), &ui_yesno_radio("web_usermin_ssl", $tmpl->{'web_usermin_ssl'})); -# Setup Dovecot, Postfix, MySQL and ProFTPd SSL certs +# Setup Dovecot, Postfix and MySQL SSL certs print &ui_table_row(&hlink($text{'newweb_dovecot'}, "template_web_dovecot_ssl"), &ui_yesno_radio("web_dovecot_ssl", $tmpl->{'web_dovecot_ssl'})); @@ -4096,9 +4044,6 @@ sub show_template_ssl "template_web_mysql_ssl"), &ui_yesno_radio("web_mysql_ssl", $tmpl->{'web_mysql_ssl'})); -print &ui_table_row(&hlink($text{'newweb_proftpd'}, - "template_web_proftpd_ssl"), - &ui_yesno_radio("web_proftpd_ssl", $tmpl->{'web_proftpd_ssl'})); } # parse_template_ssl(&tmpl) @@ -4169,7 +4114,6 @@ sub parse_template_ssl $tmpl->{'web_postfix_ssl'} = $in{'web_postfix_ssl'}; $tmpl->{'web_dovecot_ssl'} = $in{'web_dovecot_ssl'}; $tmpl->{'web_mysql_ssl'} = $in{'web_mysql_ssl'}; -$tmpl->{'web_proftpd_ssl'} = $in{'web_proftpd_ssl'}; } # chained_ssl(&domain, [&old-domain]) @@ -4269,4 +4213,3 @@ sub get_dovecot_ssl_dir $done_feature_script{'ssl'} = 1; 1; - diff --git a/feature-unix.pl b/feature-unix.pl index a20e18b84..24dfc3496 100755 --- a/feature-unix.pl +++ b/feature-unix.pl @@ -827,7 +827,7 @@ sub bandwidth_unix { my ($d, $start, $bw) = @_; my $log = $config{'bw_ftplog'} ? $config{'bw_ftplog'} : - $config{'ftp'} ? &get_proftpd_log() : undef; + &get_proftpd_log(); if ($log) { my @users; my @ashells = grep { $_->{'mailbox'} } &list_available_shells(); @@ -1396,4 +1396,3 @@ sub create_email_for_unix $done_feature_script{'unix'} = 1; 1; - diff --git a/feature-webmin.pl b/feature-webmin.pl index 01fa2ac87..35045a9a2 100755 --- a/feature-webmin.pl +++ b/feature-webmin.pl @@ -798,14 +798,6 @@ sub set_user_modules $sd->{'dom'})) if ($elog && !$done{$elog}++); } - # Add FTP logs - if ($sd->{'ftp'}) { - my $flog = &get_proftpd_log($sd); - if ($flog && !$done{$flog}++) { - push(@extras, $flog." ".&text('webmin_flog', - $sd->{'dom'})) - } - } # Add PHP log my $phplog = &get_domain_php_error_log($d); if ($phplog && !$done{$phplog}++) { @@ -1479,4 +1471,3 @@ sub release_lock_webmin $done_feature_script{'webmin'} = 1; 1; - diff --git a/functional-test.pl b/functional-test.pl index 196a04245..11fc90c96 100755 --- a/functional-test.pl +++ b/functional-test.pl @@ -12907,78 +12907,6 @@ package virtual_server; $transfer_tests = [ { 'command' => 'echo Missing user or password ; false' } ]; } -$ftp_tests = [ - # Create a domain with SSL, FTP and a private IP - { 'command' => 'create-domain.pl', - 'args' => [ [ 'domain', $test_domain ], - [ 'desc', 'Test FTP domain' ], - [ 'pass', 'smeg' ], - [ 'dir' ], [ 'unix' ], [ $web ], [ 'dns' ], [ $ssl ], - [ 'logrotate' ], [ 'ftp' ], - [ 'allocate-ip' ], - [ 'content' => 'Test FTP home page' ], - @create_args, ], - }, - - # Copy the domain's SSL cert to proftpd - { 'command' => 'install-service-cert.pl', - 'args' => [ [ 'domain', $test_domain ], - [ 'add-domain' ], - [ 'service', 'proftpd' ] ], - }, - - # Check that anonymous FTP to it works - { 'command' => $wget_command.' --inet4 '. - 'ftp://'.$test_domain.'/', - 'antigrep' => 'Login incorrect', - }, - - # Put a file in the anonymous FTP directory - { 'command' => 'echo "bar" >~'. - $test_domain_user.'/ftp/foo.txt', - }, - - # Try to fetch it - { 'command' => $wget_command.' --inet4 '. - 'ftp://'.$test_domain.'/foo.txt', - 'grep' => 'bar', - }, - - # Test that encrypted FTP works and serves the right cert - { 'command' => $curl_command.' --ftp-ssl --insecure -v --ipv4 '. - 'ftp://'.$test_domain.'/foo.txt', - 'grep' => [ 'bar', 'O=Test FTP domain', 'CN=(\\*\\.)?'.$test_domain ], - }, - - # Disable the domain - { 'command' => 'disable-domain.pl', - 'args' => [ [ 'domain' => $test_domain ], - [ 'feature' => 'ftp' ] ], - }, - - # FTP should fail now - { 'command' => $wget_command.' --inet4 '. - 'ftp://'.$test_domain.'/', - 'grep' => 'Login incorrect', - 'fail' => 1, - }, - - # Re-enable the domain - { 'command' => 'enable-domain.pl', - 'args' => [ [ 'domain' => $test_domain ] ], - }, - - # FTP should work again - { 'command' => $wget_command.' --inet4 '. - 'ftp://'.$test_domain.'/', - 'antigrep' => 'Login incorrect', - }, - - # Cleanup the domain - { 'command' => 'delete-domain.pl', - 'args' => [ [ 'domain', $test_domain ] ], - 'cleanup' => 1 }, - ]; $xml_tests = [ # Create a domain to run API commands on @@ -13262,7 +13190,6 @@ package virtual_server; 'allscript' => $allscript_tests, 'parallel_backup' => $parallel_backup_tests, 'transfer' => $transfer_tests, - 'ftp' => $ftp_tests, 'scheduled' => $scheduled_tests, 'xml' => $xml_tests, 'assoc' => $assoc_tests, diff --git a/get-logs.pl b/get-logs.pl index 03cc30fc5..65631424a 100755 --- a/get-logs.pl +++ b/get-logs.pl @@ -6,8 +6,8 @@ =head1 get-logs.pl Given a domain name with the C<--domain> flag, this command outputs some or all of it's Apache access or error log. The log file to display can be -selected with the C<--access-log>, C<--error-log> or C<--ftp-log> flag, -and the number of lines to output can be limited with the C<--tail> flag +selected with the C<--access-log> or C<--error-log> flag, and the number +of lines to output can be limited with the C<--tail> flag followed by a line count. =cut @@ -45,7 +45,7 @@ package virtual_server; $logtype = "elog"; } elsif ($a eq "--ftp-log") { - $logtype = "flog"; + &usage("Per-domain FTP logs are no longer supported"); } elsif ($a eq "--tail") { $lines = int(shift(@ARGV)); @@ -69,10 +69,6 @@ package virtual_server; &usage("Virtual server does not have a website"); $logfile = &get_website_log($d, $logtype eq "elog" ? 1 : 0); } -elsif ($logtype eq "flog") { - $d->{'ftp'} || &usage("Virtual server does not have FTP enabled"); - $logfile = &get_proftpd_log($d); - } $logfile || &usage("Log file not found!"); # Print it out @@ -94,8 +90,7 @@ sub usage print "Output webserver logs for a domain.\n"; print "\n"; print "virtualmin get-logs --domain name\n"; -print " --access-log | --error-log | --ftp-log\n"; +print " --access-log | --error-log\n"; print " [--tail lines]\n"; exit(1); } - diff --git a/help/config_avail_logviewer.html b/help/config_avail_logviewer.html index f55892f00..0c036e5ea 100644 --- a/help/config_avail_logviewer.html +++ b/help/config_avail_logviewer.html @@ -1,9 +1,8 @@
System Logs (view Apache and FTP logs)
When this option is enabled, virtual server administrations will be able -to view the contents of their Apache and virtual anonymous FTP log files +to view the contents of their Apache log files via the web interface. This is done by granting limited access to the System Logs Viewer module.