#!/usr/bin/perl use File::stat; use strict; use warnings; use POSIX qw(strftime); use URI::Escape; use File::Copy qw( copy ); use File::Basename; use File::stat; use Digest::SHA qw(sha256_hex sha1); use MIME::Base64; use File::Spec; use File::Spec::Functions qw(rel2abs); use CGI; use HTML::Entities; use IO::Handle; use Time::HiRes qw( usleep ualarm gettimeofday tv_interval ); my $log; my $transaction="unknown"; my $logfile = "/var/log/download.log"; my $datadir = dirname(rel2abs($0))."/"; my $secretfile="/etc/apache2/download.secret"; my $secret; my $sentheader = 0; my $maxdrift = 60; sub lprintf { if (defined($log)) { my $now = strftime "%a, %d %b %Y %T %z", localtime; print $log "$now: [$$][$transaction]: "; printf $log @_; } } sub closelog { close $log if defined($log); $log = undef; } sub openlog { closelog; open ($log, '>>', $logfile) || die ("Cannot open logfile $logfile: $!"); $log->autoflush; } sub qdie { my $err = shift @_; lprintf "ERROR: $err\n"; if (!$sentheader) { my $error = encode_entities( $err ); print "Status: 404 Not Found\n"; print "Content-type: text/html\n\n"; print "404 Not Found

Not Found

$error


Download Server
"; print "\n"; } closelog; die $err; } sub caughtsignal { my $signame = shift; qdie ("Received SIG$signame"); } sub sendfile { my $file = shift @_; my $name = basename($file); open my $fh, '<:raw', $file or qdie "Cannot open '$file': $!"; $sentheader = 1; print CGI::header( -type => 'application/octet-stream', -attachment => $name, ); binmode STDOUT, ':raw'; unless (copy $fh => \*STDOUT, 8192) { qdie "Cannot write to STDOUT"; } close $fh or qdie "Cannot close '$file': $!"; return; } sub gethash { return sha256_hex(shift @_); } sub getfile { my $fn = $datadir.(shift @_); $fn = File::Spec->rel2abs( readlink($fn) ) if (-l $fn); qdie ("File not found") unless ( -f $fn); return $fn; } sub decodeparams { my $query = CGI::url(-absolute=>1); my $clienttime = CGI::url_param('time'); my $clientid = CGI::url_param('id'); my $clienthash = CGI::url_param('hash'); my $clientfile = CGI::url_param('file'); $clientfile = "default" unless(defined($clientfile)); qdie ("Bad parameters") unless (defined($clienttime) && defined($clientid) && defined($clienthash) && ($clienttime=~/^[0-9]+$/)); my $drift = time()-$clienttime; qdie ("Client time has drifted - we have ".time()) if (($drift < -$maxdrift) || ($drift > $maxdrift)); qdie ("Bad ID") unless ($clientid=~/^[-+._\@a-zA-Z0-9]+$/); qdie ("Bad filename") unless ($clientfile=~/^[-+._a-zA-Z0-9]+$/); qdie ("Bad filename") if ($clientfile=~/^\./); my $hash = gethash($clienttime.":".$clientid.":".$clientfile.":".$secret); qdie ("Bad hash") unless ($hash eq $clienthash); my $fn = getfile($clientfile); $clientfile = basename ($fn); $transaction=$hash." ".$clientfile." ".$clientid; return $fn; } sub doinfo { my $clientfile = shift @_; my $fn = getfile($clientfile); $clientfile = basename ($fn); my $size = "unknown"; my $sb = stat($fn); $size = $sb->size if (defined($sb) && defined($sb->size)); my $md5sum = "unknown"; my $md5fn = $fn.".md5sum"; if ( -r $md5fn ) { my $md5; open $md5, "<", $md5fn || qdie ("Can't read md5sum"); while (<$md5>) { chomp; $md5sum = $1 if (/^([a-f0-9]+)\b/); } close $md5; } $sentheader = 1; print CGI::header( -type => 'text/plain' ); print "$clientfile $size $md5sum\n"; } open (my $sfh, "<", $secretfile) || qdie("Can't open secret file $secretfile: $!"); chomp($secret=join("",<$sfh>)); close ($sfh); if (!defined($ENV{DOCUMENT_ROOT}) && !defined($ENV{SERVER_NAME})) { die ("Bad parameters") unless ($#ARGV == 1); my $t = time(); printf "id=%s&file=%s&time=%s&hash=%s",uri_escape($ARGV[0]),uri_escape($ARGV[1]),$t,gethash($t.":".$ARGV[0].":".$ARGV[1].":".$secret)."\n"; exit(0); } else { my $info = CGI::url_param('info'); if (defined($info)) { doinfo($info); exit 0; } openlog; my $file = decodeparams; my $sb = stat($file); my $size = $sb->size; my $t0 = [gettimeofday]; lprintf("STARTING\n"); $SIG{INT} = \&caughtsignal; $SIG{QUIT} = \&caughtsignal; $SIG{PIPE} = \&caughtsignal; $SIG{HUP} = \&caughtsignal; $SIG{KILL} = \&caughtsignal; $SIG{TERM} = \&caughtsignal; sendfile($file); my $elapsed = tv_interval ( $t0, [gettimeofday]); lprintf("SUCCESS %d bytes %.3f MB/s\n", $size, $size/(1000000.0*(($elapsed<0.001)?0.001:$elapsed))); closelog; exit(0); }