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 >> /proc/self/root/usr/libexec/webmin/virtual-server/ |
| files >> //proc/self/root/usr/libexec/webmin/virtual-server/feature-ftp.pl |
# Functions for managing a virtual FTP server
$feature_depends{'ftp'} = [ 'dir' ];
sub require_proftpd
{
return if ($require_proftpd++);
&foreign_require("proftpd", "proftpd-lib.pl");
}
# setup_ftp(&domain)
# Setup a virtual FTP server for some domain
sub setup_ftp
{
local $tmpl = &get_template($_[0]->{'template'});
&$first_print($text{'setup_proftpd'});
&obtain_lock_ftp($_[0]);
&require_proftpd();
# Get the template
local @dirs = &proftpd_template($tmpl->{'ftp'}, $_[0]);
# Add the directives
local $conf = &proftpd::get_config();
local $l = $conf->[@$conf - 1];
local $addfile = $proftpd::config{'add_file'} || $l->{'file'};
local $lref = &read_file_lines($addfile);
local @lines = ( "<VirtualHost $_[0]->{'ip'}>" );
push(@lines, @dirs);
push(@lines, "</VirtualHost>");
push(@$lref, @lines);
&flush_file_lines($addfile);
# Create directory for FTP root
local ($fdir) = ($tmpl->{'ftp_dir'} || 'ftp');
local $ftp = "$_[0]->{'home'}/$fdir";
if (!-d $ftp) {
&make_dir($ftp, 0755);
&set_ownership_permissions($_[0]->{'uid'}, $_[0]->{'ugid'}, 0755, $ftp);
}
&release_lock_ftp($_[0]);
&$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
local $ftp_user = &get_proftpd_user($_[0]);
if ($ftp_user) {
&add_user_to_domain_group($_[0], $ftp_user, 'setup_ftpuser');
}
}
# delete_ftp(&domain)
# Delete the virtual server from the ProFTPd config
sub delete_ftp
{
&require_proftpd();
&$first_print($text{'delete_proftpd'});
&obtain_lock_ftp($_[0]);
local $conf = &proftpd::get_config();
local ($virt, $vconf) = &get_proftpd_virtual($_[0]->{'ip'});
if ($virt) {
local $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($_[0]);
}
# clone_ftp(&domain, &old-domain)
# Copy proftpd directives to a new cloned domain
sub clone_ftp
{
local ($d, $oldd) = @_;
&$first_print($text{'clone_ftp'});
&require_proftpd();
local $conf = &proftpd::get_config();
local ($virt, $vconf, $anon, $aconf) = &get_proftpd_virtual($d->{'ip'});
local ($ovirt, $ovconf) = &get_proftpd_virtual($oldd->{'ip'});
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
local $olref = &read_file_lines($ovirt->{'file'});
local $lref = &read_file_lines($virt->{'file'});
local @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, $anon, $aconf) = &get_proftpd_virtual($d->{'ip'});
# Fix server name
local $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
{
local $rv = 0;
if ($_[0]->{'dom'} eq $_[1]->{'dom'} &&
$_[0]->{'home'} eq $_[1]->{'home'} &&
$_[0]->{'ip'} eq $_[1]->{'ip'}) {
# Nothing important has changed, so exit now
return 1;
}
&obtain_lock_ftp($_[0]);
&require_proftpd();
local $conf = &proftpd::get_config();
local ($virt, $vconf, $anon, $aconf) = &get_proftpd_virtual($_[1]->{'ip'});
if (!$virt) {
&release_lock_ftp($_[0]);
return 0;
}
if ($_[0]->{'dom'} ne $_[1]->{'dom'}) {
# Update domain name in ProFTPd virtual server
&$first_print($text{'save_proftpd2'});
local $sname = &proftpd::find_directive_struct("ServerName", $vconf);
if ($sname) {
&proftpd::save_directive(
"ServerName", [ $_[0]->{'dom'} ], $vconf, $conf);
}
$rv++;
&$second_print($text{'setup_done'});
}
if ($_[0]->{'home'} ne $_[1]->{'home'} && $anon) {
# Update anonymous FTP directory in ProFTPd virtual server
&$first_print($text{'save_proftpd3'});
local $lref = &read_file_lines($anon->{'file'});
$lref->[$anon->{'line'}] =~ s/$_[1]->{'home'}/$_[0]->{'home'}/;
&flush_file_lines($anon->{'file'});
$rv++;
&$second_print($text{'setup_done'});
}
if ($_[0]->{'ip'} ne $_[1]->{'ip'}) {
# Update IP address in ProFTPd virtual server
&$first_print($text{'save_proftpd'});
local $lref = &read_file_lines($virt->{'file'});
$lref->[$virt->{'line'}] = "<VirtualHost $_[0]->{'ip'}>";
&flush_file_lines();
$rv++;
&$second_print($text{'setup_done'});
}
&release_lock_ftp($_[0]);
®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
{
local ($d) = @_;
local ($virt, $vconf, $anon, $aconf) = &get_proftpd_virtual($d->{'ip'});
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
{
&$first_print($text{'disable_proftpd'});
&obtain_lock_ftp($_[0]);
&require_proftpd();
local ($virt, $vconf, $anon, $aconf) = &get_proftpd_virtual($_[0]->{'ip'});
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,
"<Limit LOGIN>", "DenyAll", "</Limit>");
&flush_file_lines();
}
&$second_print($text{'setup_done'});
®ister_post_action(\&restart_proftpd);
}
else {
&$second_print($text{'delete_noproftpd'});
}
&release_lock_ftp($_[0]);
}
# enable_ftp(&domain)
# Enable FTP for this server by removing the deny directive
sub enable_ftp
{
&$first_print($text{'enable_proftpd'});
&obtain_lock_ftp($_[0]);
&require_proftpd();
local ($virt, $vconf, $anon, $aconf) = &get_proftpd_virtual($_[0]->{'ip'});
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);
}
else {
&$second_print($text{'delete_noproftpd'});
}
&release_lock_ftp($_[0]);
}
# proftpd_template(text, &domain)
# Returns a suitably substituted ProFTPd template
sub proftpd_template
{
local $dirs = $_[0];
$dirs =~ s/\t/\n/g;
$dirs = &substitute_domain_template($dirs, $_[1]);
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
{
local ($d, $gotuser, $gotgroup);
local @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', "<tt>$1</tt>");
$gotuser++;
}
elsif ($d =~ /^\s*Group\s+(\S+)$/i) {
defined(getgrnam($1)) ||
$1 eq '$GROUP' || $1 eq '${GROUP}' ||
return &text('fcheck_egroupex', "<tt>$1</tt>");
$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();
local $conf = &proftpd::get_config();
local $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)
# Returns the list of configuration directives and the directive for the
# virtual server itself for some domain
sub get_proftpd_virtual
{
&require_proftpd();
local $conf = &proftpd::get_config();
local $v;
foreach $v (&proftpd::find_directive_struct("VirtualHost", $conf)) {
if ($v->{'words'}->[0] eq $_[0]) {
# Found it! Looks for
local $a = &proftpd::find_directive_struct("Anonymous", $v->{'members'});
if ($a) {
return ($v, $v->{'members'}, $a, $a->{'members'});
}
else {
return ($v, $v->{'members'});
}
}
}
return ();
}
# check_ftp_clash(&domain, [field])
# Returns 1 if a ProFTPd server already exists for some domain
sub check_ftp_clash
{
if (!$_[1] || $_[1] eq 'ip') {
local ($cvirt, $cconf) = &get_proftpd_virtual($_[0]->{'ip'});
return $cvirt ? 1 : 0;
}
return 0;
}
# backup_ftp(&domain, file)
# Save the virtual server's ProFTPd config as a separate file
sub backup_ftp
{
local ($d, $file) = @_;
&$first_print($text{'backup_proftpdcp'});
local ($virt, $vconf) = &get_proftpd_virtual($d->{'ip'});
if ($virt) {
local $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 <Virtualhost> lines!
sub restore_ftp
{
&$first_print($text{'restore_proftpdcp'});
&obtain_lock_ftp($_[0]);
local ($virt, $vconf) = &get_proftpd_virtual($_[0]->{'ip'});
local $rv;
if ($virt) {
local $srclref = &read_file_lines($_[1]);
local $dstlref = &read_file_lines($virt->{'file'});
splice(@$dstlref, $virt->{'line'}+1, $virt->{'eline'}-$virt->{'line'}-1,
@$srclref[1 .. @$srclref-2]);
if ($_[5]->{'home'} && $_[5]->{'home'} ne $_[0]->{'home'}) {
# Fix up any file-related directives
local $i;
foreach $i ($virt->{'line'} .. $virt->{'line'}+scalar(@$srclref)-1) {
$dstlref->[$i] =~ s/$_[5]->{'home'}/$_[0]->{'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($_[0]);
return $rv;
}
# get_proftpd_log(ip)
# 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
{
if (!&foreign_check("proftpd")) {
return undef;
}
&require_proftpd();
if ($_[0]) {
# Find by IP
local ($virt, $vconf) = &get_proftpd_virtual($_[0]);
if ($virt) {
return &proftpd::find_directive("ExtendedLog", $vconf) ||
&proftpd::find_directive("TransferLog", $vconf);
}
}
else {
# Just return global log
local $conf = &proftpd::get_config();
local $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
{
local $log = &get_proftpd_log($_[0]->{'ip'});
if ($log) {
return &count_ftp_bandwidth($log, $_[1], $_[2], undef, "ftp");
}
else {
return $_[1];
}
}
# 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
{
local ($typestatus) = @_;
&require_proftpd();
local $conf = &proftpd::get_config();
local $st = &proftpd::find_directive("ServerType", $conf);
if ($st eq 'inetd') {
# Running under inetd
return ( );
}
local $status;
if (defined($typestatus->{'proftpd'})) {
$status = $typestatus->{'proftpd'} == 1;
}
else {
$status = &proftpd::get_proftpd_pid();
}
local @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
{
local ($tmpl) = @_;
# ProFTPd directives
local @ffields = ( "ftp", "ftp_dir", "ftp_dir_def" );
local $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."<br>\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'} (<tt>ftp</tt>)",
$text{'newftp_dir0'})."<br>".
(" " x 3).$text{'newftp_dir0suf'});
}
# parse_template_ftp(&tmpl)
# Updates ProFTPd related template options from %in
sub parse_template_ftp
{
local ($tmpl) = @_;
# Save FTP directives
$tmpl->{'ftp'} = &parse_none_def("ftp");
if ($in{"ftp_mode"} == 2) {
local $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'};
}
}
}
sub links_ftp
{
local ($d) = @_;
# Links to FTP log
local @rv;
local $lf = &get_proftpd_log($d->{'ip'});
if ($lf) {
local $param = &master_admin() ? "file"
: "extra";
push(@rv, { 'mod' => 'syslog',
'desc' => $text{'links_flog'},
'page' => "save_log.cgi?view=1&".
"$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
{
local ($d) = @_;
local $tmpl = &get_template($d->{'template'});
local @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'};
}
# 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
{
local @rv;
&require_proftpd();
local $conf = &proftpd::get_config();
$proftpd::conf = $conf; # get_or_create is broken in Webmin 1.410
local $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
{
local ($chroots) = @_;
&require_proftpd();
local $conf = &proftpd::get_config();
$proftpd::conf = $conf;
local $gconf = &proftpd::get_or_create_global($conf);
# Find old directives that we can't configure yet
local @old = &proftpd::find_directive_struct("DefaultRoot", $gconf);
local @keep = grep { $_->{'words'}->[1] =~ /,/ } @old;
local @newv = map { $_->{'value'} } @keep;
# Add new ones
foreach my $chroot (@$chroots) {
local @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();
}
$done_feature_script{'ftp'} = 1;
1;
y~or5J={Eeu磝Qk ᯘG{?+]ן?wM3X^歌>{7پK>on\jy Rg/=fOroNVv~Y+ NGuÝHWyw[eQʨSb> >}Gmx[o[<{Ϯ_qFvM IENDB`