#!/usr/bin/perl
# ***************************************************************************
# SQLite Active (on-demand) Pseudo(user-space)(compressed)FileSystem SAPFS v.0.2a
# skeldoy@gmail.com
# This is a program for packing and unpacking files into an sqlite-database 
# while retaining the posibility for executing and displaying contents of
# the stored files. Usage is explained and if you "ls" after packing a
# directory you will get the picture.
# ***************************************************************************
# I TAKE NO RESPONSIBILITY FOR THIS CODE AND THE MANY BAD THINGS THAT WILL
# HAPPEN TO PEOPLE WHO RUN THIS WITHOUT TAKING RESPONSIBILITY FOR THEIR
# OWN ACTIONS! Distribute freely and be nice! Stealing code should entail a
# nice email to me ;) - Sverre Eldøy (skeldoy@gmail.com)
# ***************************************************************************

# You will need lzma, the DBD::SQLite and the Compress::LZMA::External (perlmod)

# This is the start of main
$|=0; $appName = "SAPFS";
use DBI qw(:sql_types);
use Compress::LZMA::External qw(compress decompress);
# This will set up the current directory with an empty database (if none exists)
&dropSetup(); 
unless (-e "./Library") { 
	$makeIt = `sqlite3 Library <dbSetup 1>/dev/null`; print $makeIt; }

my $dbh = DBI->connect("dbi:SQLite:Library");

# This is the code for packing the archive.
if ($ARGV[0] =~ "DL-pack") {
@files = <*>; foreach $file (@files) {
	if ($file =~ "$appName") { next; } # we do not pack ourself
	if ($file =~ "Library") { next; } # or the pack
	if ($file =~ "dbSetup") {next;} # or the temporary dbsetup
	my $blobTemp = `cat $file`; my $blob = compress($blobTemp);
	($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, 
  	 $atime, $mtime, $ctime, $blksize, $blocks ) = stat($file);
	if ( -x $file ) { $executive = 1;} else {$executive = 0; }
	my $sth = $dbh->prepare("INSERT INTO dynLibrary VALUES (NULL, \"$file\",?,$executive, $mode, 1)"); # for now assume architecture 1 (Intel 386 32-ELF)
	$sth->bind_param(1, $blob, SQL_BLOB);
	$sth->execute();
	print "Added $file\n";
	$delete = `rm $file 2>/dev/null`;
	$mkSymLink = `ln -s $appName $file`;
	}
	print "Finished packing\n"; }

# This code is run when we want to unpack the archive. Archive will be deleted.
elsif ($ARGV[0] =~ "DL-ext") {
for ($n=1;$n<(&allData()+1);$n++) {
	$fileQuery = join(" ", "SELECT filename from dynLibrary where id=$n;");
	print "Extracting $n ";	
	$sth = $dbh->prepare("$fileQuery");
	$sth->execute;
	my $row = $sth->fetch; $filename = $row->[0];
	print $filename; print "\n";
	$blobQuery = "SELECT file from dynLibrary where id=$n";
	$sth=$dbh->prepare("$blobQuery");
	$sth->execute;
	my $brow = $sth->fetch;
	$fileTemp = $brow->[0]; $file=decompress($fileTemp);
	$delSymLinks = `rm $filename 2>/dev/null`;
	open (OUT, ">$filename");
	print OUT $file;
	close OUT;	
        $XmodeQuery = "SELECT executable from dynLibrary where id=$n";
        $doXMode = $dbh->prepare("$XmodeQuery");
        $doXMode->execute; $modXRes = $doXMode->fetch; $executive = $modXRes->[0];
	if ($executive) { $chmodIt = `chmod +x $filename`; }
	}
	$killLib = `rm Library 1>/dev/null 2>/dev/null`;
}

# print som help
elsif (($0 =~ "$appName") | ($ARGV[0]=~"-help")) {
print "SQLite Active FileSystem SAFS v0.0.1A by skeldoy\@gmail.com\n";
print "Usage:\n$0 DL-pack (for packing the files in your current directory)\n";
print "$0 DL-ext (for extracting from the package into current directory)\n";
print "./NAMEofFILEinPackage will either execute or print that file.\n"; }

# This following code is called whenever we execute or open a file in the package.
else {
	@call = split("/",$0); $called=$call[$#call];
	$fileQuery = join(" ", "SELECT file from dynLibrary where filename=\"$called\";");
	$sth = $dbh->prepare("$fileQuery");
	$sth->execute;
	my $row = $sth->fetch; $fileTemp = $row->[0]; $file = decompress($fileTemp);
        $XmodeQuery = "SELECT executable from dynLibrary where filename=\"$called\"";
        $doXMode = $dbh->prepare("$XmodeQuery");
        $doXMode->execute; $modXRes = $doXMode->fetch; $executive = $modXRes->[0];
        if ($executive) { 
		$killedIt = `rm $called`;
		open (OUT, ">$called");
		print OUT $file;
		close $file;
		$chmodIT = `chmod +x $called`;
		#print `./$called`;
		print `cp $called backUp`;
		print `./backUp`; 
		$slett=`rm $called 1>/dev/null 2>/dev/null;rm backUp 1>/dev/null 2>/dev/null`; 
		$lnagain = `ln -s $appName $called`;
		}
	# in case it is not an executable we just print it (for now)
	else { 

		@ProcessExtension = split(".",$called);
		$extension = $ProcessExtension[1];
		if ($extension =~ "aiff") { `cat $file | play -n`; }
		elsif ($extension =~ "mp3") { `cat $file | play -n`; }
		 elsif ($extension =~ "m4u") { `cat $file | play -n`; }
		 elsif ($extension =~ "ogg") { `cat $file | play -n`; }
		 elsif ($extension =~ "flac") { `cat $file | play -n`; }
		 elsif ($extension =~ "snd") { `cat $file | play -n`; }
		 elsif ($extension =~ "wav") { `cat $file | play -n`; }
		else {print $file; }



				} # additional handling of other formats will
				# with time be implemented
}

#This is the last thing that happens (in "main")
$killSetup = `rm dbSetup 1>/dev/null 2>/dev/null`;

sub allData() {
	$countQuery = "SELECT COUNT(id) from dynLibrary;";
	$sth = $dbh->prepare("$countQuery"); $sth->execute;
	@count = $sth->fetch; return $count[0][0]; }

# This is the configuration for the database. I put it here to contain it (cleanliness).
sub dropSetup() {
open (DB, ">dbSetup"); print DB ".echo on\n"; print DB ".mode col\n";
print DB ".headers on\n"; print DB ".nullvalue NULL\n";
print DB "CREATE TABLE dynLibrary (id INTEGER PRIMARY KEY,\n";
print DB "\t\t\tfileName text NOT NULL COLLATE NOCASE, \n";
print DB "\t\t\tfile blob, executable boolean, filemode int, archtype int ,UNIQUE (id,fileName));\n"; close DB; }
