#!/usr/bin/perl

########## Confixx(R) 3.0 Professional ############
####### Copyright SWsoft, Inc. 2004-2005 ##########
#### http://www.sw-soft.com - info@sw-soft.com ####

use vars qw($mail_server $mail_quota $dns_server $user_quota $majordomo_quota 
						$user_homeDir $mailSpool $pop_homeDir $majordomo_ldir $bin_quota 
						$bin_quotaon $bin_quotaoff $bin_quotacheck $bin_df %bin);

use File::Basename;
use strict;
no strict "refs";

my($quotaVersion,%quotaDevState,%quotedFileSystem,%qbin);
my $hasQuota = eval("require Quota");

sub quotaSupportCheck {
  my $configure=shift;

  my ($qfile,$name,$version,$out,$path,$binName,$nosilent,$hasUncertain);
  %quotedFileSystem=();

  %qbin=();

  if ( !$configure ){ 
# install
    print &ltext('install_header_quota')."\n";
    &RestoreBin(\%qbin,'quota');
    undef $nosilent;
  }elsif($configure =~ /admin/i){
# admin
    &RestoreBin(\%qbin,'quota');
    $nosilent='nosilent';
  }else{
# configure
    $nosilent='nosilent';
  }

  my @quotabin = ('quota','quotacheck','quotaon','quotaoff','setquota','df');

#
# fill %qbin
#
  foreach $name (@quotabin) {
		unless (exists($qbin{$name}) && (-x $qbin{$name})){
			if (exists($bin{$name}) && (-x $bin{$name})){
				$qbin{$name} =$bin{$name};
			}else{
				$binName = "bin_$name";
				$path=$$binName;
				unless(-x $path){
					$path= &getBin($name);
					$hasUncertain=1;
				}
				$qbin{$name} = $path;
			}
		}
	}

	if ( $nosilent && $hasUncertain){
		foreach $name (@quotabin) {
			unless (-x $qbin{$name}){
				$path=	&BinQuestion(&ltext('install_bin_path', $name),"/sbin/$name");
				$qbin{$name} = $path;
			}
		}
	} 

#
# get quoted file systems
#
  my $ptrFs=&quotaGetFS(\%qbin,$configure);
  %quotedFileSystem = %{$ptrFs};

  if($mail_server == 2 || $dns_server == 2 ){
    $user_quota='0';
  }else{
    if ($user_quota && $user_quota ne '0'){
      unless (&quotaSupportPath($user_homeDir,$nosilent,\%qbin)){
				print &ltext('quota_path_nosupport',$user_homeDir)."\n";
				$user_quota='0';
      }
    }
  }
  if ($mail_server){
    if ($mail_quota && $mail_quota ne '0'){
      my $mailDir = (($mailSpool eq "") && (-d $pop_homeDir))
		? $pop_homeDir
		: $mailSpool;

      unless (&quotaSupportPath($mailDir,$nosilent,\%qbin)){
				print &ltext('quota_path_nosupport',$mailDir)."\n";
				$mail_quota='0';
      }
    }

    if ($majordomo_quota && $majordomo_quota ne '0' && $majordomo_ldir){

      unless (&quotaSupportPath($majordomo_ldir,$nosilent,\%qbin)){
				print &ltext('quota_path_nosupport',$majordomo_ldir)."\n";
				$majordomo_quota='0';
      }
    }
  }else{
    $mail_quota='0';
    $majordomo_quota='0';
  }

  if ($configure =~ /conf/i){
    while (($name,$path) = each(%qbin)){
      $bin{$name}=$path;
    }
  }else{
    &StoreBin(\%qbin,'quota');
  }
}

#
# Check or answer for binary
#
sub quotaCheckBin {
  my ($ptrPath, $ptrBin, $name, $defPath, $silent)=@_;
  my $path = $$ptrPath;
  unless ( -x $path){
    $path=$ptrBin->{$name};
    unless ( -x $path || $silent){
      $path = &BinQuestion(&ltext('install_bin_path', $name),$defPath);
      $ptrBin->{$name}=$path;
    }
    $$ptrPath=$path;
  }
  return $path;
}

#
# check if quota is on for the path
#
sub quotaGetDev {

	my ($path,$ptrStatFS)=@_;
  
	unless (-d $path){
		return undef;
	}

  my $dev = Quota::getqcarg($path);
	if ($dev){
		if (ref($ptrStatFS) eq 'HASH'){
			unless($ptrStatFS->{$dev}){
				undef $dev;
			}
		}
		if ($dev){
			if(Quota::sync($dev) && ($! != 1)){
				undef $dev;
			}
		}
	}
	return $dev;
}

#
# get hash of XFS devises. 1 - has usrquota, 0 - else
#
sub getXFSdev {
	my ($mpoint,@line,$ret);
	$ret = {};
	my @mount = `mount`;
	foreach $mpoint (@mount){
		chomp $mpoint;
		@line=split(' ',$mpoint,6);
		if($line[4]=~/xfs/i){
			$ret->{$line[0]}=($line[5]=~/usrquota/)?1:0;
		}
	}
	if(wantarray()){
		return %{$ret};
	}else{
		return $ret;
	}
}

#
# get devices there is quota on
#
sub quotaStatFS {
  my $ptrBin=shift;
  unless (ref($ptrBin) eq 'HASH'){
    $ptrBin = \%bin;
  }
  my %quotaStatFS=();
  my ($dev,$ok,$opts);
# check quota
  unless (defined $bin_quotaon){
    $bin_quotaon='';
  }
  &quotaCheckBin(\$bin_quotaon,$ptrBin,'quotaon','/sbin/quotaon','silent');

  $quotaVersion=&quotaCheckVersion($bin_quota);

  if ((-x $bin_quotaon) && ($quotaVersion >= 3.04)){

		my %xfs = &getXFSdevs(); ## get XFS file systems

    if (open(OUT,"$bin_quotaon -p -a -u 2>&1|")){
      $ok=1;
      my ($dev,$on);
      while (<OUT>){
				if (/\(\s*(\/\S+)\s*\)[\s\w]*\s+(\w+)$/){
					$dev=$1;
					$on=$2;
					if ($on=~/on/i || $xfs{$dev}){
						$quotaStatFS{$dev}=1;
					}elsif($on=~/off/i){
						$quotaStatFS{$dev}=0;
					}
				}
			}
			close (OUT);
		}
	}else{
		my (@mount,@line,$mpoint);
		@mount = `mount`;
		$ok=(@mount>0)?1:0;
		foreach $mpoint (@mount){
			chomp $mpoint;
			@line = split(' ',$mpoint,6);
			if($line[5]=~/usrquota/){
				$dev=$line[0];
				if($line[4]=~/xfs/i){ ## XFS file system
					$dev="(XFS)$dev";
				}
				$quotaStatFS{$dev}=1;
			}
		}
		close (OUT);
	}
  
	if ($ok){
		return \%quotaStatFS;
	}else{
		return undef;
	}
}


#
# get file systen there has user quote support
#
sub quotaGetFS {

  my $ptrBin=shift;
  my ($config,$out,$err,$fquser);
  unless (ref($ptrBin) eq 'HASH'){
    $config=$ptrBin;
    $ptrBin = \%bin;
  }else{
    $config=shift;
  }

# clean hash
  %quotedFileSystem=();

# check quota
  unless (defined $bin_quota){
    $bin_quota='';
  }
  &quotaCheckBin(\$bin_quota,$ptrBin,'quota','/usr/bin/quota');

  if (-x $bin_quota){

# run quota for check version
    $out = `$bin_quota -V 2>&1`;
    unless ($?){
      if ($out =~ /\s(\d+\.\d+)/){
				$quotaVersion=$1;
      }
    }
  }

# name of user quota file
  $fquser=($quotaVersion>=2)?'aquota.user':'quota.user';
  my($dev,$path,$type, $opts,$qfile);

  if($hasQuota){
    if (Quota::setmntent()==0){
      while ( ($dev,$path,$type, $opts) = Quota::getmntent() ){
				if ($opts =~ /usrquota/){
					if($type=~/xfs/i){
						$dev="(XFS)$dev";
						$qfile='';
					}else{
						if ($opts =~ /usrquota=([^,\b]+)/){
							$qfile=$1;
						}else{
							$qfile=($path=~/\/$/)?"$path$fquser":"$path/$fquser";
						}
					}
					$quotedFileSystem{$dev}=$qfile;
				}
      }
      Quota::endmntent();
    }else{
      $err=Qouta::strerr();
      print "$err\n";
    }
  }else{
    if (open(MOUNT,"mount |")){
      while(<MOUNT>){
				if(/(\/\S+)\s*on\s*(\/\S*)\s*type\s*(\S+)\s*\((\S*)\)/){
					$dev=$1;
					$path=$2;
					$type=$3;
					$opts=$4;
					if ( $opts =~ /usrquota/ ){

						if ( $type=~/xfs/i ) {

							$dev="(XFS)$dev";
							$qfile='';

						} else {

							if ($opts =~ /usrquota=([^,\b]+)/){
								$qfile=$1;
							}else{
								$qfile="$path/$fquser";
							}

						}
						$quotedFileSystem{$dev}=$qfile;
					} ## if usrquota
				}
			}
      close (MOUNT);
    }
  }
  return \%quotedFileSystem;
}

sub findExistsPath {
  my $path=shift;
#
# find the exist path
#
  while(! -d $path){
    if ($path =~ /(\S+)\/[^\/]+$/){
      $path = $1;
    }else{
			$path = '/';
      last;
    }
  }
  return $path;
}

#
# check quota support for path
#
sub quotaSupportPath {
  my ($path ,$nosilent,$ptrBin)=@_;
  if (ref($nosilent) eq 'HASH'){
    $ptrBin = $nosilent;
    undef $nosilent;
  }
  unless (ref($ptrBin) eq 'HASH'){
    $ptrBin=\%bin;
  }

  my ($error,$dev,$lastword,$out);

  $path=&findExistsPath($path);

  if ($hasQuota){
		$dev = Quota::getqcarg($path);
  }else{
		&quotaCheckBin(\$bin_df,$ptrBin,'df','/bin/df');
		$bin_df = 'df' unless (-x $bin_df);
		my $cmd ="$bin_df -P $path";
		if (open (DF,"$cmd |")){
			while (<DF>){
				if (/^(\/\S*)\s+/){
					$dev = $1;
					last;
				}
			}
			close (DF);
			my (@mount,$mpoint,@line);
			@mount = `mount`;
			foreach $mpoint (@mount){
				chomp $mpoint;
				@line = split(' ',$mpoint);
				next unless $line[0] eq $dev;
				if($line[4]=~/xfs/i){
					$dev="(XFS)$dev";
					last;
				}
			}
		}else{
			die "Can not run command: $cmd\n$!\n";
		}  
  }
  unless ($dev){
    print &ltext("quota_dev_get","#1092",$path,$!)."\n";
    return 0;
  }

	if($dev=~/^\(XFS\)/i){  ## XFS file system
		return 1;
	}

#
# check mount options
#
  unless ( exists $quotedFileSystem{$dev} ){
    print &ltext("quota_dev_nosupport",$dev)."\n";
    return 0;
  }
  unless (defined $bin_quotaon){
    $bin_quotaon='';
  }
  &quotaCheckBin(\$bin_quotaon,$ptrBin,'quotaon','/sbin/quotaon');

  if (-x $bin_quota){

# run quota for check version
    $out = `$bin_quota -V 2>&1`;
    unless ($?){
      if ($out =~ /\s(\d+\.\d+)/){
				$quotaVersion=$1;
      }
    }
  }
  $quotaVersion=&quotaCheckVersion($bin_quota);

	my $noCheck = 0;
  if ((-x $bin_quotaon) && ($quotaVersion >= 3.04)){
#
# check if user quota is on
#
    $out = `$bin_quotaon -p -u $dev 2>&1`;
		$out =~ s/^quotaon:.*?$//g; ## remove error messages

    if ($out =~ /\s(\w+)$/){
      $lastword=$1;
      if ($lastword eq 'on'){
				$noCheck = 1;
			}else{
				if ($lastword eq 'off'){
					$out=`$bin_quotaon -u $dev 2>&1`;
					if($? && $out=~/\/quota\.user\s/){
						$quotedFileSystem{$dev}=$dev.'/quota.user';
					}
				}else{
					print &ltext("quota_dev_nosupport",$dev)."\n";
					return 0;
				}
			}
		}
	}else{
		unless ($quotaDevState{$dev}){
			$quotaDevState{$dev}=  &quotaOn($dev,$nosilent,$ptrBin);
		}
  }

  unless ( $noCheck || -r $quotedFileSystem{$dev} ){
#
# try make aquota.user
#
    unless (&quotaCheck($dev,$nosilent,$ptrBin)){
      return 0;
    }
  }

#
# O.K.
#
  return 1;

}

sub quotaCheckVersion {
	my $quota = shift;
	if ($quotaVersion=~/\d+\.\d+/){
		return $quotaVersion;
	}
	$quota = $bin_quota unless (-x $quota);
	if (-x $quota){
# run quota for check version
		my $out = `$quota -V 2>&1`;
		unless ($?){
			if ($out =~ /\s(\d+\.\d+)/){
				$quotaVersion=$1;
			}
		}
	}
	return $quotaVersion;	
}

sub quotaOn {
  my ($dev ,$nosilent,$ptrBin)=@_;
  if (ref($nosilent) eq 'HASH'){
    $ptrBin = $nosilent;
    undef $nosilent;
  }
  unless (ref($ptrBin) eq 'HASH'){
    $ptrBin=\%bin;
  }

  my ($ret,$out);

  unless (defined $bin_quotaon){
    $bin_quotaon='';
  }
  &quotaCheckBin(\$bin_quotaon,$ptrBin,'quotaon','/sbin/quotaon');

	if (-x $bin_quotaon){
		my $go=1;
		my $xfs=0;
		if($dev=~/^\(XFS\)(.*)/){
			$dev=$1;
			$xfs=1;
		}else{
			if ($nosilent){
				$go=&YesNoQuestion( &ltext('quota_dev_seton', $dev) , "j");
			}
		}
		if ($go){
#
# run quotaon for device
#
			$out=`$bin_quotaon -u $dev 2>&1`;
			if($?){	
				$ret=0;
				if ($out =~ /Device or resource busy|already on/i) {
						# already on..
					$ret = 1;
				}	elsif ($out =~ /Package not installed/i) {
					# No quota support!
				}elsif($out=~/^\S*$/) {
					$ret=1;
				}else{
					if ($nosilent){
						print STDERR "Quotaon error: $out\n";
					}
				}
			}else{
				$ret = 1;
			}
		}
	}else{
		$ret=0;
	}
	return $ret;
}

sub quotaCheck {
	my ($dev ,$nosilent,$ptrBin)=@_;
	if (ref($nosilent) eq 'HASH'){
		$ptrBin = $nosilent;
		undef $nosilent;
	}
	unless (ref($ptrBin) eq 'HASH'){
		$ptrBin=\%bin;
	}

	my($out,$keys);
	my $ret=1;

	if($dev=~/^\(XFS\)/){ ## XFS file system 
		return 1;
	}

	$bin_quotacheck||='';
	$bin_quotaon||='';
	$bin_quotaoff||='';

  &quotaCheckBin(\$bin_quotacheck,$ptrBin,'quotacheck','/sbin/quotacheck');
  &quotaCheckBin(\$bin_quotaon,$ptrBin,'quotaon','/sbin/quotaon');
  &quotaCheckBin(\$bin_quotaoff,$ptrBin,'quotaoff','/sbin/quotaoff');

	if (-x $bin_quotacheck){
		my $go=1;
		if ($nosilent){
			$go=&YesNoQuestion( &ltext('quota_dev_check', $dev) , "j");
		}
		if ($go){

			system("$bin_quotaoff $dev");
			foreach $keys ('-u -f','-u -f -m -c'){
				$out=`$bin_quotacheck $keys $dev 2>&1`;
				if ($? || $out =~ /cannot remount|please stop/i){
					print $out if $nosilent;
				}else{
					last;
				}
			}
			$out = `$bin_quotaon $dev 2>&1`;
			if($? && $out=~/\/quota\.user\s/){
#
# need quota.user instead aquota.user
#
				$quotedFileSystem{$dev}=$dev.'/quota.user';

				foreach $keys ('-u -f -F vfsold','-u -f -m -c -F vfsold'){
					$out=`$bin_quotacheck $keys $dev 2>&1`;
					if ($? || $out =~ /cannot remount|please stop/i){
						print $out if $nosilent;
					}else{
						last;
					}
				}
				$out = `$bin_quotaon $dev 2>&1`;
				if($?){
					print $out if $nosilent;
				}else{
					$ret =1;
				}
			}else{
				$ret=1;
			}
		}
	}
	return $ret;
}


return $hasQuota;
