#!/usr/bin/perl -I/server/phexproxy/PhexProxy

#########################################
##### Author:		Sebastian Enger / B.Sc
##### CopyRight:	BitJoe GmbH
##### LastModified	5.03.2007
##### Function:		Hauptdatei für PhexProxy
##### Todo:			for statt foreach: ist schneller
########################################

##########
#### Merke: $FileTypeHashRef->{ 0 } = FORMATBILD|FORMATMP3 usw
#### Merke: $FileTypeHashRef->{ 1 } = 'jpg|mp3' usw
#### Merke: CreatePhexConnection()->port=########### egal was im phex drinne steht!
##########


###########todo: result caching einbauen

# system("renice 20 $$");
system("clear");

# todo: connection counter - nach 5 mal keine ergebnisse bemühe den result cache
# connection request daten abholen: 20s, 10s, 10s, 7s, 5s, public result cache


use constant VERSION	=> "PhexProxy -gzip + crypto- 0.9.8.1 - 26.4.2007";
use constant CRLF		=> "\r\n";
use constant PIDFILE	=> "/server/phexproxy/phexproxygzip-pid.txt";


######################
### Module einbinden
######################

use Data::Dumper;
#use Devel::DProf;

use strict;
use IO::Select;
use Net::hostent;
use PhexProxy::IO;
use PhexProxy::Gzip;
use PhexProxy::Time;
use PhexProxy::Phex;
use PhexProxy::Daemon;
use PhexProxy::Logging;
use PhexProxy::CheckSum;
use PhexProxy::ResultCache;
use PhexProxy::PhexSortRank;
use PhexProxy::CryptoLibrary;
use PhexProxy::LicenceManagement;


######################
### Objekte initialisieren
######################

my $IO				= PhexProxy::IO->new();
my $GZIP			= PhexProxy::Gzip->new();
my $TIME			= PhexProxy::Time->new();
my $Phex			= PhexProxy::Phex->new();
my $LOGGING			= PhexProxy::Logging->new();
my $SORTRANK		= PhexProxy::PhexSortRank->new();
my $CHECKSUM		= PhexProxy::CheckSum->new();
my $CACHE			= PhexProxy::ResultCache->new();
my $CRYPTO			= PhexProxy::CryptoLibrary->new();
my $LICENCE			= PhexProxy::LicenceManagement->new();


###################################

my $HandyClientIPAdress;
my $HandyClientHostName;
my $IOReadFromClient;
my $CountOfNoResult					= 0;	# Zählvariable: Wieviele Versuche sollen gelten, bis die Suchanfrage im Phex verworfen wird 
my $MaxNoResultTry					= 7;	# FlagSetVariable: Wieviele Versuche sollen gelten, bis die Suchanfrage im Phex verworfen wird 
my $TIMEOUT							= 140;
my $DONE							= 0;
my $Token							= $Phex->readToken();
my $MaxNullResultCountUntilCacheTry = 5;


#todo: sizefilter einmal auslesen und die sizewerte umwandeln und bei jeder suchanfrage dieses sizewert dann an den phex übergeben

##############
#my $port							= 3383;	# für IO von proxy->handy
my $port							= 3385;	# für IO von proxy->handy
my $PhexSocket						= PhexProxy::IO->CreatePhexConnection( $TIMEOUT );
my $ProxySocket						= PhexProxy::IO->CreateProxySocket( $port );
my $FILE							= '/server/phexproxy/IDS'; mkdir $FILE;
my $NUMBEROFRESULTS					= 10;	# anzahl der ergebnisse,die dem client gesendet werden sollen
##############

##################################

$SIG{'INT'}							= $SIG{'TERM'} =	sub { warn "SignalHandler Interrupt "; $DONE++; };

############################################################

my $IN = IO::Select->new($ProxySocket);

# für die finale version aktiviere das init_server();
# create PID file, initialize logging, and go into the background
unlink PIDFILE;
#&init_server(PIDFILE);

die "can't setup proxy server" unless $ProxySocket;
die "can't setup phex socket" unless $PhexSocket;
print "[Server $0 Version " . VERSION . " accepting clients on port $port]\n";

########################
##### while accept loop
########################

while (!$DONE) {

  next unless $IN->can_read;
  next unless my $HandyClientSocket = $ProxySocket->accept();
  $HandyClientIPAdress				= $HandyClientSocket->sockhost;

# musste ich am 28-4-2007 rausnehmen, weil er reproduzierbar sich an dieser stelle aufgehangen hat, keine ahnung warum aufeinmal ^^
#  my $hostinfo			= gethostbyaddr($HandyClientSocket->peeraddr);
#  $HandyClientHostName = $hostinfo->name;
#  print "[$Token] Connection from [IP:$HandyClientIPAdress] DNS-Name: $HandyClientHostName \n";

print "[$Token] Connection from [IP:$HandyClientIPAdress] \n";

#  # Starte den IPBlocker() und gucke ob, eine HandyIP nicht erwünscht ist: Start
#  my $BlockerFlag = $IPFILTER->IPBlocker( $HandyClientIPAdress );
#  if ( $BlockerFlag == -1 ) {
#	print STDOUT "[ Connect from DNS $HandyClientHostName <-> IP $HandyClientIPAdress ] - Rejected by IPBlocker\n";
#	warn "[Connect from DNS $HandyClientHostName <-> IP $HandyClientIPAdress] - Rejected by IPBlocker \n";
#	
#	$IO->writeSocket( $HandyClientSocket, $GZIP->gzip_compress_string("FC 908"). CRLF );
#	close $HandyClientSocket;
#	exit(0);	
#  
#  }; # if ( $StatusFlag == 1 ) {}
#  # IPBlocker(): Ende


  my $child	= launch_child();


  unless ($child) {
 
	close $ProxySocket;
	
	# Nehme den Content vom Handy an
	my $ReadFromClient = $IO->readSocket( $HandyClientSocket );
	my ( @IOReadFromClient ) = split("\r\n", $ReadFromClient ); 

	#print Dumper @IOReadFromClient; exit;

	# NEW: 12.7
	# HIER DAS ENTSCHLÜSSELN ANSETZEN: START
	# $IOReadFromClient[0] = MD5 Wert der Verschlüsselten Nachricht
	# $IOReadFromClient[1] = der public schlüssel, anhand deren entschieden wird, welcher kryptkey zum entschlüsseln genommen werden muss 
	# $IOReadFromClient[2] = die verschlüsselte nachricht
	# 1.) teste, ob MD5 Wert für $IOReadFromClient[2] korrekt ist ja->goon: sende OK an handy zurück, nein: write error message an handyclient udn beende mich
	# 2.) hole den privaten KryptoKey für den public key $IOReadFromClient[1] mittels PhexProxy::CryptoLibrary->GetPrivateCryptoKeyFromDatabase() 
	#   zu 2.)und speichere diesen temporär zwischen mittels PhexProxy::CryptoLibrary->WritePrivateCryptoKeyForSession( );
	# 3.) entschlüssele die nachricht ; diese ist dann das neue @IOReadFromClient
	# HIER DAS ENTSCHLÜSSELN ANSETZEN: ENDE

	# Verschlüsseln: hole aus temporären datei den privaten kryptokey mittels PhexProxy::CryptoLibrary->WritePrivateCryptoKeyForSession(  );
	# verpacke alles: und dann verschlüssele es: 
	# schicke dem handy: MD5\r\nCRYPTCONTENT\r\n
	
	####################### crypto api implementatione #####################
	#	$IOReadFromClient[0] = MD5 Wert der Verschlüsselten Nachricht
	#	$IOReadFromClient[1] = der public schlüssel 
	#	$IOReadFromClient[2] = die verschlüsselte nachricht
		
	my $PrivateCryptoKey = $CRYPTO->GetPrivateCryptoKeyFromDatabase( $IOReadFromClient[1] );
	if ( $PrivateCryptoKey == -1 ) {
		#&ErrorFunction( $HandyClientSocket );
		print "Error: Crypto false privatekey\n";
		$IO->writeSocket($HandyClientSocket, $GZIP->gzip_compress_string("Crypto false privatekey") . CRLF);
		exit(0);
	}; # if ( $PrivateKey == -1 ) {}

	my $PlainText = $CRYPTO->Decrypt( $PrivateCryptoKey, $IOReadFromClient[2] );

	# DEBUG: print "TEXT: $PlainText\n";

	if ( $IOReadFromClient[0] ne $CHECKSUM->MD5ToHEX($IOReadFromClient[2]) ) {
		# schreibe dem client, das er die daten nochmal übertragen soll
		print "Error: Crypto Md5 error\n";
		$IO->writeSocket($HandyClientSocket, $GZIP->gzip_compress_string("Crypto Md5 error") . CRLF);
		exit(0);
	}; #if ( $IOReadFromClient[0] ne $CHECKSUM->MD5ToHEX($IOReadFromClient[2]) ) {

	@IOReadFromClient			= ();		# resette das wichtige Array
	my ( @IOReadFromClient )	= split("\r\n", $PlainText ); 


	############### Licence checken - Start ################
	my ( $Status, $FileTypeHashRef ) = $LICENCE->CheckLicence( \@IOReadFromClient );

	if ( $Status != 1 ) {
		print "CheckLicence failed - from: DNS $HandyClientHostName <-> IP $HandyClientIPAdress\n";
		
		$IO->writeSocket($HandyClientSocket, $GZIP->gzip_compress_string($Status) . CRLF);
		close $HandyClientSocket;
		$LOGGING->LogToFileInvalidLicence( \$ReadFromClient );	
		exit(0);
	}; # if ( $Status != 1 ) {}
	############### Licence checken - Ende ################


	# Checke, welche Art von Request reinkommt und benutze entsprechende Subroutine dafür
	if ( CheckStatusFlag( \@IOReadFromClient ) == 1 ) {
		&FindFunction( \@IOReadFromClient , \$ReadFromClient , $hostinfo, $HandyClientSocket, $FileTypeHashRef );

	} elsif ( CheckStatusFlag( \@IOReadFromClient ) == 2 ) {
		&ResultFunction( \@IOReadFromClient , \$ReadFromClient , $hostinfo, $HandyClientSocket, $FileTypeHashRef );
		
	} elsif ( CheckStatusFlag( \@IOReadFromClient ) == 3 ) {
		&DownloadStartFunction( \$ReadFromClient );

	} elsif ( CheckStatusFlag( \@IOReadFromClient ) == 4 ) {
		&DownloadEndFunction( \$ReadFromClient );

	} elsif ( CheckStatusFlag( \@IOReadFromClient ) == 5 ) {
		&LicenceFunction( $HandyClientSocket, \@IOReadFromClient );

	} elsif ( CheckStatusFlag( \@IOReadFromClient ) == 6 ) {
		&ResultRangeFunction( \@IOReadFromClient , \$ReadFromClient , $hostinfo, $HandyClientSocket, $FileTypeHashRef );

	} elsif ( CheckStatusFlag( \@IOReadFromClient ) == -1 ) {
		&ErrorFunction( $HandyClientSocket );

	} else {
		&ErrorFunction( $HandyClientSocket );

	}; # if ( CheckStatusFlag( \@IOReadFromClient ) == 1 ) {}

	exit(0);

  }; #  unless ($child) {}


	#  $HandyClientSocket->close( SSL_ctx_free => 1 );
  close $HandyClientSocket;

}; # while (!$DONE) {}

########################
##### while accept loop
########################

die "Normal termination\n";


############################
######## FUNCTIONS #########
############################

sub FindFunction(){

	my $ArrayRef			= shift;
	my $ReadFromClientRef	= shift;
	my $hostinfo			= shift;
	my $HandyClientSocket	= shift;
	my $FileTypeHashRef		= shift;
		
	my @IOReadFromClient	= @{$ArrayRef};
	
	$IOReadFromClient[5]	=~ s/\./ /ig;
	$IOReadFromClient[5]	=~ s/ä/ae/ig;
	$IOReadFromClient[5]	=~ s/ö/oe/ig;
	$IOReadFromClient[5]	=~ s/ü/ue/ig;
	$IOReadFromClient[5]	=~ s/ß/ss/ig;

	# Erstelle die CheckSumme für einen eingehenden Request - zusammengesetzt aus entsprechenden Werten
	# my $ClientID = $CHECKSUM->MD5ToHEX( $hostinfo->name . time() . $CRYPTO->SimpleRandom() . $CRYPTO->URandom() . $#IOReadFromClient ); 
	my $ClientID = $CHECKSUM->MD5ToHEX( $HandyClientIPAdress . time() . $CRYPTO->SimpleRandom() . $CRYPTO->URandom() . $#IOReadFromClient ); 

	# select(undef, undef, undef, 0.2 );
	$Phex->find( $PhexSocket, "$IOReadFromClient[5] .$FileTypeHashRef->{1}", $ClientID, $Token); 

	my $CurTime = $TIME->MySQLDateTime();

	# printf "[Connect from %s at $CurTime ] - Submitted Search '$IOReadFromClient[5] $FileTypeHashRef->{1}' to Mutella\n", $hostinfo->name;
	printf STDOUT "[ Connect from %s at $CurTime ] - Submitted Search '$IOReadFromClient[5] .$FileTypeHashRef->{1}' to Phex\n", $HandyClientSocket->sockhost;

	$IO->writeSocket( $HandyClientSocket, $GZIP->gzip_compress_string($ClientID) . CRLF );
	$IO->writeSocket( $HandyClientSocket, CRLF );
	$IO->WriteFile( "$FILE/$ClientID", "$IOReadFromClient[5] .$FileTypeHashRef->{1}" );	# merke die den suchbegriff für die HandyID $ClientID

	$LOGGING->LogToFileInit( $ReadFromClientRef );	
	return 1;

};	# sub FindFunction(){}


sub ResultFunction(){

	my $ArrayRef			= shift;
	my $ReadFromClientRef	= shift;
	my $hostinfo			= shift;
	my $HandyClientSocket	= shift;
	my $FileTypeHashRef		= shift;
		
	my @IOReadFromClient	= @{$ArrayRef};
	my $ClientID			= $IOReadFromClient[6];
	my $SEARCH;

	chomp($ClientID);

	my $tmpSearch	= $IO->ReadFileIntoScalar( "$FILE/$ClientID" );
	my $SEARCH		= ${$tmpSearch};
	my $CurTime		= $TIME->MySQLDateTime();
	
	# printf "[Connect from %s] - Searving Request for ID '$QueryClientID' and Query: '$SEARCH' \n", $hostinfo->name;
	printf STDOUT "[Connect from %s at $CurTime ]  - Serving Request for Query: '$SEARCH' \n", $HandyClientSocket->sockhost;
	
	my $DownloadResults			= "";
	$DownloadResults			= $Phex->result( $PhexSocket, $ClientID, $Token);
	my $SortedResultsArrayRef	= $SORTRANK->PhexSortRank( $DownloadResults, $SEARCH, $FileTypeHashRef );
	my $NumberOfResults			= $#{$SortedResultsArrayRef};

	print "############# Number of Results: $NumberOfResults ############## \n";
#	print Dumper $SortedResultsArrayRef;

	# wenn noch kein ergebnis für die suchanfrage vorliegt - 
	# todo: später: schaue nach im Cache und liefere ein Cache Ergebnis, wenn mehr als X mal ein busy flag gesendet wurde
	
	if ( $NumberOfResults == 0 || $NumberOfResults == -1 ) {
		
		######### funzt noch nicht
		##### Algo: Wenn mehr als X mal nix gefunden wurde im phex, versuche im cache nachzuschauen
		#########

		my $TempSearchID				= $CHECKSUM->MD5ToHEX( $HandyClientIPAdress . $SEARCH );
		my $NumberOfCountForNullResults	= $IO->ReadFileIntoScalar( "/server/phexproxy/counter/$TempSearchID.txt" );	# versuche die datei zu lesen, wenn nicht erfolgreich, wird -1 zurückgegeben ansonsten ein integer
		my $NewCount					= $NumberOfCountForNullResults++;

		# schreibe den eintrag
		$IO->WriteFile("/server/phexproxy/counter/$TempSearchID.txt", $NewCount);
		
		if ( $MaxNullResultCountUntilCacheTry >= $NumberOfCountForNullResults ) {	# es wurde die maximale anzahl an versuchen, ergebnisse zu holen benutzt und nun bemühen wir den cache

			my $SendString = $CACHE->readCache( 0, $NUMBEROFRESULTS, $SEARCH, $FileTypeHashRef );
	
			if ( length($SendString) != 0 ) {
				
				print "[CacheHit] Success\n";
				$IO->writeSocket( $HandyClientSocket, $GZIP->gzip_compress_string($SendString) . CRLF );
				$LOGGING->LogToFileGetResults( $ReadFromClientRef );
				return 1;

			} else {

				print "[CacheMiss] Failure\n";
				$IO->writeSocket($HandyClientSocket, $GZIP->gzip_compress_string("FC 105 C") . CRLF); # FC 105 senden - query invalid
				return 0;

			}; # if ( length($SendString) != 0 ) {}	
		
		#########
		##### Algo: Wenn mehr als X mal nix gefunden wurde im phex, versuche im cache nachzuschauen
		#########

		} else { #  if ( $MaxNullResultCountUntilCacheTry >= $NumberOfCountForNullResults ) {

			# gib aus, dass der handy client noch etwas warten soll
			$IO->writeSocket( $HandyClientSocket, $GZIP->gzip_compress_string("FC 808") . CRLF ); # FC 808 
		
		}; # ENDE: if ( $MaxNullResultCountUntilCacheTry >= $NumberOfCountForNullResults ) {

	
	} else { # if ( $NumberOfResults == 0 || $NumberOfResults == -1 ) {
		
		my $RC = 0;
		my $SendString;

		unlink "/server/phexproxy/debug/parsed.txt";
		
		foreach my $entry ( @{$SortedResultsArrayRef} ) {
			
			next if ( $RC > $NUMBEROFRESULTS );
			my ( $RANK, $PEERHOST, $SIZE, $SHA1 ) = split('###', $entry );

			chop($SHA1) if ( length($SHA1) == 33 );	# fix für PhexProxy::SortArray->Sort_Table();

			if ( $RANK != -3333 && $PEERHOST != -3333 && $SIZE != -3333 && $SHA1 != -3333 ){
							
				my $SizeKB = ( $SIZE / 1024 );
				my $SizeMB = ( $SizeKB / 1024 );
				
				$SizeKB = sprintf ("%.2f", $SizeKB);
				$SizeMB = sprintf ("%.2f", $SizeMB);
				
				open(WH,">>/server/phexproxy/debug/parsed.txt");
					print WH "RESULT: $RC\n";
					print WH "DEBUG: RANK: '$RANK' \n";
					print WH "DEBUG: SIZE: '$SIZE' Bytes und $SizeKB KB| $SizeMB MB \n";
					print WH "DEBUG: SHA1: '$SHA1' \n";
					print WH "DEBUG: HOST: '$PEERHOST' \n";
					print WH " ############ \n\n";
				close WH;

				print "RESULT: $RC\n";
				print "DEBUG: RANK: '$RANK' \n";
				print "DEBUG: SIZE: '$SIZE' Bytes und $SizeKB KB| $SizeMB MB \n";
				print "DEBUG: SHA1: '$SHA1' \n";
				print "DEBUG: HOST: '$PEERHOST' \n";
				print " ############ \n\n";

				# $SendString .= $RANK ."\r\n". $SIZE ."\r\n". $SHA1 ."\r\n". $PEERHOST ."\r\n";
				$SendString .= $RANK .CRLF. $SIZE .CRLF. $SHA1 .CRLF. $PEERHOST .CRLF;

				$RC++;

			}; # if ( $RANK != -1000 ...

		};	# foreach my $entry ( @{$SortedResultsArrayRef} ) {}

	#	$SendString .= CRLF;

		# old: nonworking $IO->writeSocket( $HandyClientSocket, $SendString . CRLF );
	
		$IO->writeSocket( $HandyClientSocket, $GZIP->gzip_compress_string($SendString) .CRLF );
		$Phex->del( $PhexSocket, $ClientID, $Token );
			
		$CACHE->writeCache( $SortedResultsArrayRef, $SEARCH, $FileTypeHashRef );
		$LOGGING->LogToFileGetResults( $ReadFromClientRef, $ClientID );	

		# unlink "$FILE/$QueryClientID";

	}; # if ( $NumberOfResults == 0 || $NumberOfResults == -1 ) {}

	return 1;

};	# sub ResultFunction(){}



sub ResultRangeFunction(){

	#	7.1 Suchergebnisse Ranged anfragen
	#	Status | Flag | IMEI | Lizenzkey |
	#	Clientversion | Suchbegriff | Results ID | RANGE FROM | RANGE TO

	my $ArrayRef			= shift;
	my $ReadFromClientRef	= shift;
	my $hostinfo			= shift;
	my $HandyClientSocket	= shift;
	my $FileTypeHashRef		= shift;
			
	my @IOReadFromClient	= @{$ArrayRef};
	my $QueryClientID		= $IOReadFromClient[6];

	my $From				= $IOReadFromClient[7];
	my $To					= $IOReadFromClient[8];

	chomp($QueryClientID);

	my $tmpSearch	= $IO->ReadFileIntoScalar( "$FILE/$QueryClientID" );
	my $SEARCH		= ${$tmpSearch};

	printf "[Connect from %s - ID $QueryClientID ] - Sending Cache Result \n", $hostinfo->name;

	# benutze ResultCache.pm um eine ergebnis Range zu holen und an den client zu senden
	# later: lese privaten oder/und public cache
	# my $SendString = $CACHE->readCache( $From, $To, $QueryClientID, );
	my $SendString = $CACHE->readCache( $From, $To, $SEARCH, $FileTypeHashRef );

	# gib die ergebnisse aus
	$IO->writeSocket( $HandyClientSocket, $GZIP->gzip_compress_string($SendString) . CRLF );
	
	# Logging
	$LOGGING->LogToFileGetResults( $ReadFromClientRef );	
	return 1;

}; # sub ResultRangeFunction(){}


sub DownloadStartFunction(){

	my $ReadFromClientRef	= shift;
	$LOGGING->LogToFileStartDownload( $ReadFromClientRef );	
	print "DEBUG: Download Start Log\n";
	
	return 1;

};	# sub DownloadStartFunction(){}


sub DownloadEndFunction(){

	my $ReadFromClientRef	= shift;
	$LOGGING->LogToFileFinishDownload( $ReadFromClientRef );	
	print "DEBUG: Download End Log\n";
	
	return 1;

}; # sub DownloadEndFunction(){}


sub LicenceFunction(){

	my $HandyClientSocket	= shift;
   	my $ArrayRef			= shift;

	my ( $Status, $HashRef ) = $LICENCE->CheckLicence( $ArrayRef );
	
	if ( $Status != 1 ) {
		$IO->writeSocket($HandyClientSocket, $GZIP->gzip_compress_string($Status) . CRLF);
		close $HandyClientSocket;
		$LOGGING->LogToFileInvalidLicence( $ArrayRef );	
		exit(0);
		return -1;	# never reached here
	}; # if ( $Status != 1 ) {}
	
	return 1;

}; # sub LicenceFunction(){}


sub ErrorFunction(){

	my $HandyClientSocket = shift;
	
	$IO->writeSocket( $HandyClientSocket, $GZIP->gzip_compress_string("FC 105 A") . CRLF );
	close $HandyClientSocket;
	exit(0);
	return 1;

}; # sub ErrorFunction(){}


sub CheckStatusFlag(){
		
	my $ArrayRef = shift;
	
	if ( lc($ArrayRef->[0]) eq 'find' ) { # fi
		return 1;
	} elsif ( lc($ArrayRef->[0]) eq 'result' ){	# re
		return 2;
	} elsif ( lc($ArrayRef->[0]) eq 'dlstartlog' ){	# ds
		return 3;
	} elsif ( lc($ArrayRef->[0]) eq 'dlendlog' ){ # de
		return 4;
	} elsif ( lc($ArrayRef->[0]) eq 'licence' ){ # li
		return 5;
	} elsif ( lc($ArrayRef->[0]) eq 'resultrange' ){ # rr
		return 6;
	} else {
		return -1;
	};

	# always return false
	return -1;

}; # sub CheckStatusFlag(){}


sub GzipString(){

	my $string_to_compress = shift;

	my $compressed_string;
	my $LoopCount = 0;

	do {
		
		return "Error: Proxy->GzipString()" if ( $LoopCount >= 10 );
		eval {
			$compressed_string = $GZIP->gzip_compress_string( $string_to_compress );
		};
		
		if ( $@ ) {
			print "ERROR: $@\n";
		};

		$LoopCount++;

	} until ( $compressed_string != -1 );

	return $compressed_string;

}; # sub sub GzipString(){



sub DeGzipString(){

	my $string_to_decompress = shift;

	my $decompressed_string;
	my $LoopCount = 0;

	do {
		
		return "Error: Proxy->DeGzipString()" if ( $LoopCount >= 10 );
		eval {
			$decompressed_string = $GZIP->gzip_decompress_string( $string_to_decompress );
		};

		if ( $@ ) {
			print "ERROR: $@\n";
		};

		$LoopCount++;

	} until ( $decompressed_string != -1 );

	return $decompressed_string;

}; # sub DeGzipString(){





# perl -MCPAN -e 'install "Crypt::Rijndael"'
# perl -MCPAN -e 'install "Crypt::CBC"'
# perl -MCPAN -e 'install "LWP::Simple"'
# perl -MCPAN -e 'install "String::Approx"' 
# perl -MCPAN -e 'install "Net::SCP::Expect"' 
# perl -MCPAN -e 'install "MIME::Lite"' 
# perl -MCPAN -e 'install "Sort::Array"' 
# perl -MCPAN -e 'install "File::Basename"' 
# perl -MCPAN -e 'install "Net::Nslookup"' 
# perl -MCPAN -e 'install "Digest::MD4"' 
# perl -MCPAN -e 'install "Digest::MD5"'
# perl -MCPAN -e 'install "IO::Socket::SSL"'
# perl -MCPAN -e 'install "CPAN"'
# perl -MCPAN -e 'install "Scalar::Util"'
# perl -MCPAN -e 'install "Net::SSLeay"'
# perl -MCPAN -e 'install "Crypt::SSLeay"'
# perl -MCPAN -e 'install "Crypt::IDEA"'
# perl -MCPAN -e 'install "Digest::SHA"'
# perl -MCPAN -e 'install "Crypt::RSA"'
# perl -MCPAN -e 'install "Crypt::Blowfish"'
# perl -MCPAN -e 'install "Crypt::GPG"'
# perl -MCPAN -e 'install "XML::Parser"'
# perl -MCPAN -e 'install "XMLRPC::Lite"'
# perl -MCPAN -e 'install "Devel::DProf"'
# perl -MCPAN -e 'install "Devel::Profiler"'
# perl -MCPAN -e 'install "Devel::FastProf"'
# perl -MCPAN -e 'install "Sort::Key"'
# perl -MCPAN -e 'install "Sort::Array"'
# perl -MCPAN -e 'force install "Inline::Java"'