#!/usr/bin/perl

# giFTquerycgi: a helper application that lets you download files from
#               many users in parallel from the FastTrack network.
#
# Instructions:
#
#    You will need the giFT daemon and the giFT CGI frontend running,
#    somewhere on the web accesible to you.
#
#    You will also need the giFTget program installed locally.
#
#    When you click on a "hash" entry in the CGI output, it will send
#    you a file with MIME type "application/x-gift-query".  Make this
#    program (giFTquerycgi) the handler for that MIME type in your
#    web browser.  For best results, tell your web browser to run
#    giFTquerycgi in a terminal.  If your browser does not have this
#    functionality, setting the handler to something like
#    "xterm -e giFTquerycgi" should work.
#
# Notes:
#
#    This program makes a web connection back to the giFT CGI in order
#    to perform the query contained in the x-gift-query file.  That
#    CGI will start streaming a list of URLs at which the requested
#    file can be found.  This program will take those URLs, as they
#    come in, and pass them to the giFTget program, which will do the
#    parallel download.  Note that downloading starts from the first URL
#    usually before the whole list of URLs is even found by the CGI.

use LWP::UserAgent;

# The giFTget program to use.  You may want to specify a full path.
$giftget = 'giFTget';

# The directory in which to put the completed file
$donedir = '/tmp/share';

# The directory in which to put the file as it is downloading
$tmpdir = $donedir . "/incoming";

# Your umask will modify these permissions
mkdir $donedir, 0777;
mkdir $tmpdir, 0777;

# Watch for hints that the child is done
$SIG{'PIPE'} = 'IGNORE';
$SIG{'CHLD'} = \&sigchld;
$|=1;

# Request the URL that was contained in the file whose name is on the
# command line
chomp($url = <>);
$ua = new LWP::UserAgent;
$req = new HTTP::Request GET => $url;
$resp = $ua->request($req, \&havedata, 1);

sub havedata {
    my ($data, $response, $protocol) = @_;
    if ($giftgetting) {
	# Just stream the data to the giFTget process
	print GG $data;
	return;
    }
    # Get the metadata at the beginning of the file
    $totaldata .= $data;
    if ($totaldata =~
            /HASH:\s*([0-9a-f]*)\s+FILENAME:\s*(\S+)\s+FILESIZE:\s*(\d+)\n/i) {
	$hash = $1; $name = $2; $size = $3; $rest = $';
	$hash =~ tr/0-9a-fA-F//cd;
	$hash =~ tr/A-F/a-f/;
	$name =~ tr/.a-zA-Z0-9:-/_/c;
	$tmpname = $tmpdir . "/" . $hash;
	print "Fetching $name ($size bytes)\n";
	$ggpid = open (GG, "|$giftget $size $tmpname") or
	    die "Cannot run $giftget: $!\n";
	select(GG); $|=1; select(STDOUT);
	$giftgetting = 1;
	print GG $rest;
    }
}

# This gets called when the child exits.  We try to do as little as
# possible while we're inside the signal handler, so we just remember
# the success code for later.
sub sigchld {
    if ($ggpid > 0) {
	waitpid($ggpid, 0);
	$ggpid = -1;
	$success = ($? == 0);
	exit;
    }
}

# Run a helper program cleanly.  Helper programs can be used to
# automatically deal with complete files.  For example, after
# downloading an MP3, you could run "xmms -e filename.mp3" .
sub helper {
    my $cpid = fork;
    return if $cpid < 0;
    if ($cpid > 0) {
	waitpid($cpid, 0);
	return;
    }
    # We're the child
    exit if fork;
    setpgrp(0,0);
    exec @_;
    exit;
}

# Clean up and run any requested helper app
END {
    if ($success) {
	# Determine the real filename for this file.  Make sure it's unique.
	if ($name =~ /^(.*?)(\.[^\.]*)$/) {
	    $base = $1; $ext = $2;
	} else {
	    $base = $name; $ext = '';
	}
	$finalname = "${donedir}/${name}";
	$infcount = 0;
	while (-e $finalname) {
	    ++$infcount;
	    $finalname = "${donedir}/${base}.${infcount}${ext}";
	}

	# Rename the file and remove the stats metafile
	rename $tmpname, $finalname;
	unlink $tmpname . ".sta";

	# Run helpers, if desired
	#if ($name =~ /\.mp3$/i) {
	#    &helper("/usr/bin/xmms", "-e", $finalname);
	#}

	# Give things a chance to work out, and then quit
	sleep 1;
    }
}
