#!/usr/bin/perl

# KMbeuPS Driver Installation Routine
# Version 2.0
# August 12th 2015
# Jan Heinecke
# Based on Version 1.3 from Markus Timpner [March 14th 2006]
#
# installation procedure version 2.0 contains about 80% new code
# changes:
# - new routine for detecting PPD and Filter directory
# 		- check cupsd.conf and cups-files.conf (since Cups 1.6) for DataDir and ServerBin
# 		- if necessary, check DefaultDataDir and DefaultServerBin locations
# - if detection fails, installation folders can be input manually
# - all PPDs from installation folder will be installed (PPD files do not have to be added to XML file)
#
# changes with 1.5
# - bugfix in file and directory checking functions
# 
# changes with 1.5
# - bugfix in DefaultDataDir and DefaultServerBin check
#
# changes with 1.4
# -added DefaultDataDir and DefaultServerBin
# - modified regex for extracting filter file name (remove white space)
# - added chmod for filter file (0755)

use strict;
use Cwd;

print "\nInstallation procedure for Konica Minolta drivers is starting.\n";
print "**************************************************************\n\n";

# default cups configuration files folder
my @DefaultCupsConfigFolder=();
$DefaultCupsConfigFolder[0]="/etc/cups"; 

# DataDir and ServerBin are defined in cupsd.conf and since CUPS 1.6 they are defined 
# in the cups-files.conf
# 
# here some default path information, in case DataDir and ServerBin directives cannot be 
# found in cupsd.conf or cups-files.conf

# default DataDir, if not defined in cupsd.conf
my @DefaultDataDir=();
$DefaultDataDir[0]="/usr/share/cups"; 

# default ServerBin, if not defined in cupsd.conf
my @DefaultServerBin=();
$DefaultServerBin[0]="/usr/lib/cups"; 
$DefaultServerBin[1]="/usr/libexec/cups";
$DefaultServerBin[2]="/usr/lib32/cups";

# ini file containing the filter filename for installation
my $inifile="kmdriverini.txt";


my $CupsConfigFolder;
my $CupsFilesConf;
my $CupsDConf;
my $DataDirSearch='DataDir\s+';
my $ServerBinSearch='ServerBin\s+';
my $DataDir;
my $ServerBin;
my $PPDDir;
my $FilterDir;

my $user_input;
my $FolderDetectionEnd=0;
my $i;

# get ServerRoot, DataDir and ServerBin from environment (cups-config command)
my $ServerRootEnv=`cups-config --serverroot`;
my $DataDirEnv=`cups-config --datadir`;
my $ServerBinEnv=`cups-config --serverbin`;

$ServerRootEnv =~ s/[ \t\r\n\v\f]+//;
$DataDirEnv =~ s/[ \t\r\n\v\f]+//;
$ServerBinEnv =~ s/[ \t\r\n\v\f]+//;
#print "ServerRoot $ServerRootEnv\n";
#print "DataDir $DataDirEnv\n";
#print "ServerBin $ServerBinEnv\n";

if(-d "$ServerRootEnv"){
	$CupsConfigFolder = $ServerRootEnv;
}
if(-d "$DataDirEnv"){
	$DataDir = $DataDirEnv;
	if(-d "$ServerBinEnv"){
		$ServerBin = $ServerBinEnv;
		$FolderDetectionEnd=1;
	}
}



# check if if cups configuration files folder is accessible
# only executed if ServerRoot cannot be found using cups-config command
if(!(defined($CupsConfigFolder))){
print "Search for Cups Configuration files.\n";
  for ($i=0; $i<@DefaultCupsConfigFolder;$i++)
  {
    if(-d "$DefaultCupsConfigFolder[$i]"){
      $CupsConfigFolder = $DefaultCupsConfigFolder[$i];
      last;
    }
  }
}

# search for cups-files.conf and cupsd.conf
# try to get DataDir and ServerBin information from cups-files.conf (first priority) 
# or from cupsd.conf 
# only executed if DataDir or ServerBin cannot be found using cups-config command
while ($FolderDetectionEnd==0) {
	if(!(defined($CupsConfigFolder))){
		print "Cannot determine the Cups configuration folder [default: /etc/cups], containing ";
		print "[cupsd.conf / cups-files.conf] ! \n";
	}
	if(defined($CupsConfigFolder)){
		$CupsFilesConf="$CupsConfigFolder\/cups-files.conf";
		if ( (-e $CupsFilesConf) && (-r $CupsFilesConf)){
			print "Search in $CupsFilesConf for DataDir and ServerBin directives.\n";
			if (open (CUPSCONF, $CupsFilesConf)) {
				while (<CUPSCONF>)	{
					if (m/$DataDirSearch/i)		{
						chomp $_;
						my($tmp1 , $tmp2) = split /\s+/,$_,2;
						$DataDir = $tmp2;
						#$PPDDir="$DataDir\/model";
					}
					if(m/$ServerBinSearch/i){
						chomp $_;
						my($tmp1 , $tmp2) = split /\s+/,$_,2;
						$ServerBin = $tmp2;
						#$FilterDir="$ServerBin\/filter";
					}				
				}
			}
			else{	
				print "Can't open file: $CupsFilesConf\n";
			}
		}
		else
		{
			print "Can't find file: $CupsFilesConf\n";
			print "$CupsFilesConf has been introduced with Cups 1.6\n";
		}
		if (!(defined($DataDir) && defined($ServerBin))){
			$CupsDConf="$CupsConfigFolder\/cupsd.conf";
			if ( (-e $CupsDConf) && (-r $CupsDConf)){
				print "Search in $CupsDConf for DataDir and ServerBin directives.\n";
				if (open (CUPSCONF, $CupsDConf)) {
					while (<CUPSCONF>)	{
						if (m/$DataDirSearch/i)		{
							chomp $_;
							my($tmp1 , $tmp2) = split /\s+/,$_,2;
							$DataDir = $tmp2;
							#$PPDDir="$DataDir\/model";
						}
						if(m/$ServerBinSearch/i){
							chomp $_;
							my($tmp1 , $tmp2) = split /\s+/,$_,2;
							$ServerBin = $tmp2;
							#$FilterDir="$ServerBin\/filter";
						}				
					}
				}
				else{	
					print "Can't open file: $CupsDConf\n";
				}
			}
			else
			{
				print "Can't find file: $CupsDConf\n";
			}		
		}
		
	}
	if (!(defined($DataDir) && defined($ServerBin))){
		print "Can't find DataDir and ServerBin directives.\n";
		print "Please input location where to find the cups configuration files \n.";
		print "(cups-files.conf/cupsd.conf). Default location: /etc/cups\n";
		print "To skip the folder detection, type: skip\n";
		print "(You may be asked later where to install PPD files and filter.)\n>";
		chomp ($user_input=<STDIN>);
###		print "input: $user_input\n";
		if ($user_input eq "skip"){
			$FolderDetectionEnd=1;
		}
		else{
			$CupsConfigFolder=$user_input;
		}
	}

	if ((defined($DataDir) && defined($ServerBin))){
		$FolderDetectionEnd=1;
		print "DataDir: $DataDir\n";
		print "ServerBin: $ServerBin\n";

	}
}


# check for PDD and Filter destination

#if DataDir and ServerBin not known, search default locations for PPD and filter
if (!(defined($DataDir) && defined($ServerBin))){
	print "Search known default locations for installing PPD and filter files.\n";

	#search default PPD location
	if(!(defined($DataDir))){
		for ($i=0; $i<@DefaultDataDir;$i++){
			print "search $DefaultDataDir[$i]\/model\n";
			if(-d "$DefaultDataDir[$i]\/model"){
				if(-w "$DefaultDataDir[$i]\/model"){
					$PPDDir = "$DefaultDataDir[$i]\/model";
					print ("PPD folder $PPDDir found\n");
					last;
				}
				else{
					print ("No write access to PPD folder: $DefaultDataDir[$i]\/model\n");
				}
			}
		}
	}

	#search default filter location
	if(!(defined($ServerBin))){
		for ($i=0; $i<@DefaultServerBin;$i++){
			print "search $DefaultServerBin[$i]\/filter\n";
			if(-d "$DefaultServerBin[$i]\/filter"){
				if(-w "$DefaultServerBin[$i]\/filter"){
					$FilterDir = "$DefaultServerBin[$i]\/filter";
					print ("Filter location $FilterDir found\n");
					last;
				}
				else{
					print ("No write access to filter location: $DefaultServerBin[$i]\/filter\n");						
				}
			}
		}
	}			
}

#if DataDir and ServerBin are known, check locations for PPD and filter 	
#search model folder based on DataDir location
if ((defined($DataDir))){
	print ("check PPD folder: $DataDir\/model\n");
	if(-d "$DataDir\/model"){
		if(-w "$DataDir\/model"){
			$PPDDir = "$DataDir\/model";
			print ("PPD folder $PPDDir found\n");
		}
		else{
			print ("No write access to PPD folder: $DataDir\/model\n");
		}
	}
	else{
		print "Can't find folder for PPD files: $DataDir\/model\n";
		#create model folder
		print "To create folder $DataDir\/model now enter: y\n>";
		chomp ($user_input=<STDIN>);
		if ($user_input eq "y"){
			if (mkdir "$DataDir\/model"){
				print "$DataDir\/model folder created\n";
				$PPDDir = "$DataDir\/model";				
			}
			else{
				print "Could not create folder: $DataDir\/model\n";
			}
		}
	}
}
#search filter folder based on ServerBin location
if (defined($ServerBin)){
	print ("check filter folder: $ServerBin\/filter\n");
	if(-d "$ServerBin\/filter"){
		if(-w "$ServerBin\/filter"){
			$FilterDir = "$ServerBin\/filter";
			print ("filter folder $FilterDir found\n");
		}
		else{
			print ("No write access to filter folder: $ServerBin\/filter\n");
		}
	}
	else{
		print "Can't find folder for filter files: $ServerBin\/filter\n";
		#create filter folder
		print "To create folder $ServerBin\/filter now enter: y\n>";
		chomp ($user_input=<STDIN>);
		if ($user_input eq "y"){
			if (mkdir "$ServerBin\/filter"){
				print "$ServerBin\/filter folder created\n";
				$FilterDir = "$ServerBin\/filter";				
			}
			else{
				print "Could not create folder: $DataDir\/model\n";
			}
		}
	}
}

#If PPDDir not known, ask for it
if (!(defined($PPDDir))){
	my $PPDDirFound=0;
	while ($PPDDirFound==0){
		print "\nPlease input where the PPD files should be installed.\n";
		print "To abort installation process enter: exit\n>";
		chomp ($user_input=<STDIN>);
		if ($user_input eq "exit"){
			print "Installation process aborted.\n";
			exit;
		}
		else{
			if ($user_input ne ""){
				$PPDDir=$user_input;
				if(-d "$PPDDir"){
					if(-w "$PPDDir"){
						$PPDDirFound=1;
					}
					else{
					print ("No write access to PPD folder: $PPDDir\n");
					}
				}
				else{
					print ("Can't find folder for PPD files: $PPDDir\n");
					#create PPD folder
					print "To create folder $PPDDir now enter: y\n>";
					chomp ($user_input=<STDIN>);
					if ($user_input eq "y"){
						if (mkdir "$PPDDir"){
							print "$PPDDir folder created\n";
							$PPDDirFound=1;
						}
						else{
							print "Could not create folder: $PPDDir\n";
						}
					}
				}
			}	
		}
	}
}

#If FilterDir not known, ask for it
if (!(defined($FilterDir))){
	my $FilterDirFound=0;
	while ($FilterDirFound==0){
		print "\nPlease input where the filter file should be installed.\n";
		print "To abort installation process enter: exit\n>";
		chomp ($user_input=<STDIN>);
		if ($user_input eq "exit"){
			print "Installation process aborted.\n";
			exit;
		}
		else{
			if ($user_input ne ""){		
				$FilterDir=$user_input;
				if(-d "$FilterDir"){
					if(-w "$FilterDir"){
						$FilterDirFound=1;
					}
					else{
					print ("No write access to filter folder: $FilterDir\n");
					}
				}
				else{
					print ("Can't find folder for filter files: $FilterDir\n");
					#create filter folder
					print "To create folder $FilterDir now enter: y\n>";
					chomp ($user_input=<STDIN>);
					if ($user_input eq "y"){
						if (mkdir "$FilterDir"){
							print "$FilterDir folder created\n";
							$FilterDirFound=1;
						}
						else{
							print "Could not create folder: $FilterDir\n";
						}
					}
				}
			}	
		}
	}
}
	

print "\nFiles will be installed in following folders:\n";
print "PPD folder: $PPDDir\n";
print "Filter folder: $FilterDir\n";
print "To continue with the installation routine enter: y\n>";
chomp ($user_input=<STDIN>);
if (!($user_input eq "y")){
	print "Installation process aborted.\n";
    exit;	
}
else
{
# installation of PPD and Filter files
	installFilter($FilterDir,getFilterFiles($inifile));
	installPPD($PPDDir);

	print "\nInstallation procedure finished!\n"; 

	print "\nTo install printers you can use the CUPS webinterface for example.\n";
	print "Please restart the CUPS server now\n\n";
	exit;
}

# routines for filter installation

sub installFilter{
	my $FilterDir=shift @_;
	my @filters=@_;
	print "\nInstalling filter file(s) to $FilterDir\n";
	my $filter;
	foreach $filter (@filters){
		my $VersionCheck=0;
		my $newversion=0;
		my $oldversion=0; 
        $filter =~ s/^\"+//;
        $filter =~ s/^\s+//;
        $filter =~ s/\s+$//;
		print "\n\t".$filter."\n";
		$newversion=getFilterVersion("$filter");

		if(-e "$FilterDir/$filter"){
			print "\tFilter file with same name already exists in destination folder!\n";
			$oldversion=getFilterVersion("$FilterDir/$filter");
			if($newversion==0 || $oldversion==0){
				if($oldversion==0){
					print "\told version: cannot be obtained!\n";
				}
				else{
					print "\told version: ".$oldversion."\n";
				}
				if($newversion==0){
					print "\tnew version: cannot be obtained!\n";
				}
				else{
					print "\tnew version: ".$newversion."\n";
				}
				$VersionCheck=1;
			}
			if($newversion lt $oldversion){
				print "\told version: ".$oldversion."\n";
				print "\tnew version: ".$newversion."\n";
				print "The existing filter is newer than the filter selected for installation.\n";
				$VersionCheck=2;				
			}
			if($newversion eq $oldversion){
				print "\told version: ".$oldversion."\n";
				print "\tnew version: ".$newversion."\n";
				print "The existing filter is the same version as the filter selected for installation.\n";						
				$VersionCheck=3;
			}
			if($newversion gt $oldversion){
				print "\told version: ".$oldversion."\n";
				print "\tnew version: ".$newversion."\n";
				print "The existing filter is the older as the filter selected for installation.\n";						
				$VersionCheck=4;
			}
		}	

		if($VersionCheck==0){
			print "\tInstalling ".$filter." (version ".$newversion.")"; 	
			copyFilter($filter,$FilterDir);
		}
		else{
			print "Do you like to overwrite the existing filter file [y]? ";
			my $overwrite_decision="n";
			$overwrite_decision=<STDIN>;
			chomp $overwrite_decision;
			if(($overwrite_decision eq ('y' or 'Y'))or(length($overwrite_decision)==0)){
				print "\tOverwriting filter !\n";
				print "\tInstalling ".$filter." (version ".$newversion.")\n"; 	
				copyFilter($filter,$FilterDir);
			}
			else{
				print "Filter will not be installed.\n";
				print "Please be aware that the driver might not work correctly!\n";
			}		
		}
	
	}
}

sub getFilterFiles{
	my $inifile= shift @_;
	my @filterfiles=();
	open(INIFILE,$inifile);
	while(<INIFILE>){
		if(m/<filterfile>/){
			s/<filterfile>//;
			s/<\/filterfile>//;
			s/\s//;
			s/\n//;
			push (@filterfiles,$_);
		}
	}
	return @filterfiles;		
}

sub getFilterVersion{ 
	my $filter=@_[0];
	my $version="";
	open(FILTER,$filter) or die "Cannot open $filter: $!";
	while(<FILTER>){
		if(m/Version/){
			s/\#//;			
			s/[ \t\r\n\v\f]+//; # remove any whitespace 
			s/\s+//;			
			s/\s+//;			

			my($versionmarker,$versionnumber)=split/:/,$_,2;
			$version=$versionnumber;
			last;
		}
					
	}
	close(FILTER);
	return $version;
				
}

sub copyFilter{
	my $filter=@_[0];
	my $FilterDir=@_[1];
        $filter =~ s/^\s+//;
        $filter =~ s/\s+$//;

	`cp $filter $FilterDir`;	
    chmod (0755,$FilterDir."/".$filter);
}


# routines for PPD installation	
sub installPPD{	
	my $PPDDir=shift @_;
	print "\nInstalling PPD file(s) to $PPDDir\n";
	my @PPDs=getPPDFiles(cwd());
	my $ppd;
	
	foreach $ppd (@PPDs){
		my $newversion=0;
		my $oldversion=0; 
		my $VersionCheck=0;

		$ppd =~ s/^\"+//;
		$ppd =~ s/^\s+//;
		$ppd =~ s/\s+$//;
		print "\n\t$ppd\n";
		print "\t".getPPDModel($ppd);
		$newversion=getPPDVersion($ppd);
		if(-e "$PPDDir/$ppd"){
			print "\tPPD file with same name already exists in destination folder!\n";
			$oldversion=getPPDVersion("$PPDDir/$ppd");
			if($newversion==0 || $oldversion==0){
				if($oldversion==0){
					print "\told version: cannot be obtained!\n";
				}
				else{
					print "\told version: ".$oldversion."\n";
				}
				if($newversion==0){
					print "\tnew version: cannot be obtained!\n";
				}
				else{
					print "\tnew version: ".$newversion."\n";
				}
				$VersionCheck=1;
			}
			if($newversion lt $oldversion){
				print "\told version: ".$oldversion."\n";
				print "\tnew version: ".$newversion."\n";
				print "The existing PPD is newer than the PPD selected for installation.\n";
				$VersionCheck=2;				
			}
			if($newversion eq $oldversion){
				print "\told version: ".$oldversion."\n";
				print "\tnew version: ".$newversion."\n";
				print "The existing PPD is the same version as the PPD selected for installation.\n";						
				$VersionCheck=3;
			}
			if($newversion gt $oldversion){
				print "\told version: ".$oldversion."\n";
				print "\tnew version: ".$newversion."\n";
				print "The existing PPD is the older as the PPD selected for installation.\n";						
				$VersionCheck=4;
			}
		}	
			
		if($VersionCheck==0){
			print "\tInstalling ".$ppd." (version ".$newversion.")\n"; 	
			`cp $ppd $PPDDir`;	
		}
		else{
			print "Do you like to overwrite the existing PPD file [y]? ";
			my $overwrite_decision="n";
			$overwrite_decision=<STDIN>;
			chomp $overwrite_decision;
			if(($overwrite_decision eq ('y' or 'Y'))or(length($overwrite_decision)==0)){
				print "\tOverwriting PPD !\n";
				print "\tInstalling ".$ppd." (version ".$newversion.")\n"; 	
				`cp $ppd $PPDDir`;
			}
			else{
				print "PPD will not be installed.\n";
				print "Please be aware that the driver might not work correctly!\n";
			}
		
		}

	}
}

sub getPPDFiles{
	my @files=();
	my $folder=shift @_;
	if ((-e $folder) && (-r $folder)){
		if (opendir my($fh), $folder){
			while (defined(my $file= readdir ($fh) )){
				next if $file=~ /^\.\.?$/;
				next if $file!~ /\.[pP][pP][dD]$/;
				if(-f "$folder\/$file")
				{
					push @files,"$file";
				}
			}
			closedir $fh;
		}
		else{
			print "Cannot access current working directory."
		}
	}	
    return @files;
}

sub getPPDModel{ 
	my $ppd=shift @_;
	my $version="";
	open(PPD,$ppd) or die "Cannot open $ppd: $!";
	while(<PPD>){
		if(m/ModelName/){
			s/\*//;
			s/[ \t\r\n\v\f]+//; # remove any whitespace 
			s/\"//;
			s/\"//;
			
			my($versionmarker,$versionnumber)=split/:/,$_,2;
			$version=$versionnumber;
			last;
		}
					
	}
	close(PPD);
	return $version;
				
}

sub getPPDVersion{ 
	my $ppd=shift @_;
	my $version="";
	open(PPD,$ppd) or die "Cannot open $ppd: $!";
	while(<PPD>){
		if(m/FileVersion/){
			s/\*//;
			s/[ \t\r\n\v\f]+//; # remove any whitespace 
			s/\s+$//;			
			s/\"//;
			s/\"//;
			my($versionmarker,$versionnumber)=split/:/,$_,2;
			$version=$versionnumber;
			last;
		}
					
	}
	close(PPD);
	return $version;
				
}

