#!/usr/bin/perl
# ~~Just for fun Project~~
# h0use - File Transfer Toolkit
#  by Iman Karim (iman.karim@smail.inf.fh-bonn-rhein-sieg.de)
# 		  http://home.inf.fh-rhein-sieg.de/~ikarim2s/


use strict;
use IO::Socket;
use Time::HiRes qw(gettimeofday);
use Digest::MD5;
use Switch;
our $File;
our $FileSize;
our $Directory;
our $Password;
our $Chunksize = 1000;
our $done = 0;
our $sock;
our $new_sock;
our $modus = shift;
our @tokens;
our $packet;
our $myHost = shift;
our $myPort= shift;
our $Start;
our $Stop;
our $cnt_dw;
our $cnt_up;

if ($modus eq '') {Help()};

if ($modus == 1){

	$sock = new IO::Socket::INET (
		LocalHost => "$myHost",
		LocalPort => "$myPort",
		Proto => 'tcp',
		Listen => 1,
		Reuse => 1);
	die "Could not create socket: $!\n" unless $sock;
	print "[+] Listening on Interface '$myHost' on Port '$myPort'...\n";
	$Password = shift || "imax";
	$Directory = shift || ".";
	print "[+] Your Password: '$Password'\n";
	print "[+] Saving File to $Directory/\n";
	print "[*] Ready. Waiting for Client...\n";
	$new_sock = $sock->accept();
	$Start = gettimeofday();

		while(<$new_sock>) {
			Parse ($_);
			PickTime();
		}

	close($sock);
	print "Abnormal Termination. Aborting.\n";

}elsif($modus == 2 ){
	print "Connecting to $myHost:$myPort...\n";
	$sock = new IO::Socket::INET (
		PeerAddr => "$myHost",
		PeerPort => "$myPort",
		Proto => 'tcp'	);
	die "Could not create socket: $!\n" unless $sock;
	$Password = shift;
	$File = shift;
	$Chunksize = shift || 2000;
	print "Sending \'$File\'...\n";
	print "Chunksize: $Chunksize\n";
	print "Using Password: '$Password'\n";
	open (file,$File) or die "[E] Can't open source file!\n";
	binmode(file);
	$FileSize = -s $File;
	SendPacket ("create|$File|$FileSize|$Password");
	$Start = gettimeofday();	

		while(<$sock>) {	
			Parse ($_);
			PickTime();
		}

	close($sock);
	print "Abnormal Termination. Aborting.\n";

}else{
	print "Invalid Modus selected!\n";
	Help();
}

sub Parse(){
	$cnt_dw=$cnt_dw+length($packet);
	$packet = shift;
	@tokens = split(/\|/,$packet);
	my $DataLength= length((substr($packet,length($tokens[0])+1,length($packet)- length($tokens[0]) -2)));#."\n";
	$packet =~ s/\[NW\!\*\]/\n/g;
	$packet =~ s/\n$//;
	
	if ($tokens[0] == $DataLength){
		Symantize ();
	}else{
		die "[E] FATAL=> Sorry! An error is occured! Reason: Packet MALFORMED!!\n";
		#Currently no realtime packet rebuilding!
		#Perl & Linux seems to handle packets very good even without real packet cut and merging!!
	}
}

sub SendPacket( $ ){
	my $Data = shift;
	$Data =~ s/\n/\[NW\!\*\]/g;
	$cnt_up=$cnt_up+length($Data);
	if ($modus == 1) {
		print $new_sock length($Data) . "|" . $Data ."\n";
	}else{
		print $sock length($Data) . "|" . $Data ."\n";
	} 
}

sub Symantize (){
	switch(rtrim($tokens[1])){
		case "create"	{
					print "[I] Checking Password...";
					if ($Password ne rtrim($tokens[4])){ 
						print "INCORRECT!\n Remote user wants filetransfer but Password is wrong!\n Remotes Passwort Guess: '". rtrim($tokens[4]) ."'\n";
						SendPacket("err|Sorry but invalid Password!");
						die "Transfer Aborted.\n";
						}
					print "Password OK.\n";
					print "[I] Remote is requesting to upload file...\n";
					print "[I] FileName:\t$tokens[2]\n";
					$File = $tokens[2];
					open(file,">$Directory/$tokens[2]" || die "[E] Cant create file! ('$tokens[2]')\n");
					print "[I] FileSize:\t".$tokens[3]/1024 ." kB\n";
					$FileSize = $tokens[3];
					SendPacket ("ok");
				}

		case "data"	{
					print file substr($packet,(length($tokens[0])+length($tokens[1]))+2);
					SendPacket ("ok");
				}

		case "ok"	{
					if (!$done){
						my $buf;
						if (($FileSize - tell(file))=>$Chunksize){
							read(file,$buf,$Chunksize);
							if (tell(file)==$FileSize){
								$done = 1;
							}
						}else{
							read(file,$buf,$FileSize - tell(file));
							$done = 1;
						}	
						SendPacket("data|". $buf );
					}else{
						print "Transfer done!\n";
						print "Calculating MD5 for transfered file...";
						close file;
						my $myMD5 = GetMD5();
						print $myMD5."\n";
						SendPacket("done|".$myMD5);
						die "Alright! All done!\n";
					}
				}

		case "done"	{	
					print "Yeah! Transfer finished!\n";
					print "Remote say MD5 have to be \t\t".rtrim($tokens[2])."\n";
					print "Calculating MD5 for transfered file...\t";
					close file;
					my $myMD5 = GetMD5();
					print $myMD5."\n";
					if ($tokens[2]==$myMD5){
						die "Transfer Done! MD5 check OK.\n";
					}else{
						die "Transfer Done! But MD5 check FAILED!\n";
					}						
					
					
				}

		case "err"	{
					die "[REMOTE ERR] $tokens[2]\n";
				}
	}

}

sub rtrim($)
{
	my $string = shift;
	$string =~ s/\s+$//;
	return $string;
}

sub PickTime(){
	$Stop = gettimeofday();
	my $res = $Stop-$Start;
	if ($res>=5){	
		$cnt_up = $cnt_up/1024/5;
		$cnt_dw = $cnt_dw/1024/5;
		print "[D] Completed: ". int(tell(file)/$FileSize*100) ."%\t[UP: ". $cnt_up . " kB\\second\t- DOWN: ". $cnt_dw ." kB\\second]\n";
		$cnt_dw=0;
		$cnt_up=0;
		$Start = gettimeofday();
	}
}

sub Help(){
print "[:] h0use - File Transfer Toolkit
 by Iman Karim (iman.karim\@smail.inf.fh-bonn-rhein-sieg.de)
                http://home.inf.fh-rhein-sieg.de/~ikarim2s/


Usage:
	h0use.pl <MODI> <PARAM>
Modis:
	h0use.pl 1 <interface_ip> <port> [<passwort>] [<directory>]
		Starts a Server waiting for a File.
		<password> is by default 'imax' w\o quotes(!!!)
		<directory> is by default the current directory.

	h0use.pl 2 <remote_ip> <port> <passwort> <filename> [<chunksize>]
		Connect to a Server and sends <filename>.
		<chunksize> is by default 2000\n";
	die "Quitting...\n"		
}

sub GetMD5(){
	my $md5 = Digest::MD5->new;
	open(file,"$File" || die ("Can't open $File for MD5 calculating!\n"));
	binmode(file);
	return $md5->addfile(*file)->hexdigest;
	close(file);
}

