Added initial iso-image.pl cgi wrapper
authorSteve McIntyre <steve@einval.com>
Sun, 13 Mar 2005 21:49:00 +0000 (21:49 +0000)
committerSteve McIntyre <steve@einval.com>
Sun, 13 Mar 2005 21:49:00 +0000 (21:49 +0000)
iso-image.pl [new file with mode: 0755]

diff --git a/iso-image.pl b/iso-image.pl
new file mode 100755 (executable)
index 0000000..8250e01
--- /dev/null
@@ -0,0 +1,255 @@
+#!/usr/bin/perl -w
+#
+# iso-image.pl
+#
+# (C) 2004 Steve McIntyre <steve@einval.com>
+#
+# CGI wrapper to work with mkimage
+#
+# Parse the parameters and call mkimage as appropriate, paying 
+# particular attention to byte ranges
+#
+# GPL v2
+#
+# v 0.1
+
+use strict;
+use File::Basename;
+use Socket;
+
+# Configure these for your system
+my $mkimage = "/usr/local/bin/mkimage";
+my $logfile = "/var/log/jigdo-logs/log.$$";
+my $template_dir = "/mirror/jigdo";
+my $matches = "-q -m Debian=/mirror/debian -m Non-US=/mirror/debian-non-US";
+
+my $size;
+my $image_name;
+open LOGFILE, ">> $logfile" || die "Unable to open logfile!\n";
+
+# Log an error to the user
+sub user_error ($) {
+    my $my_name = "iso_image.pl";
+    print "Status: 400 Invalid Request\n";
+    print "Content-type: text/html\n\n";
+    print "$my_name: @_<p>Abort.\n";
+}
+
+# Log a message to the logfile and stop
+sub log_die ($) {
+    print LOGFILE @_;
+    die @_;
+}
+
+# Convert the .iso filename into a .jigdo
+sub jigdo_name ($) {
+    my ($jigdo_name) = @_;
+    $jigdo_name =~ s/\.iso$/\.jigdo/g;
+    $jigdo_name = $template_dir . "/" . $jigdo_name;
+    return $jigdo_name;
+}
+
+# Convert the .iso filename into a .template
+sub template_name ($) {
+    my ($template_name) = @_;
+    $template_name =~ s/\.iso$/\.template/g;
+    $template_name = $template_dir . "/" . $template_name;
+    return $template_name;
+}
+
+# Grab the image size out of the template file
+sub image_size ($) {
+    my ($image_name) = @_;
+    my $image_size;
+    my $cmdline = "$mkimage -l " . $logfile . " -z -t " . template_name($image_name);
+
+    open (FH, '-|', $cmdline);
+    $image_size = <FH>;
+    close FH;
+    return $image_size;
+}
+
+# We have no range headers; simply generate the full image
+sub produce_full_image ($$) {
+    my ($image_name, $size) = @_;
+    my $output_name = basename($image_name);
+    my $cmdline;
+    my $err = 0;
+
+    $cmdline = "$mkimage -l " . $logfile;
+    $cmdline = $cmdline . " -t " . template_name($image_name);
+    $cmdline = $cmdline . " -j " . jigdo_name($image_name);    
+    $cmdline = $cmdline . " " . $matches;
+
+    print "Status: 200 OK\n";
+    print "Content-Type: application/octet-stream\n";
+    print "Content-Disposition: inline;filename=$output_name\n";
+    print "Content-Length: $size\n\n";
+
+    $err = system($cmdline) >> 8;
+    if ($err) {
+        log_die ("Failed to rebuild image; error $err\n");
+    }
+}
+
+# Generate the desired range
+sub produce_range ($$$) {
+    my ($image_name, $start, $end) = @_;
+    my $output_name = basename($image_name);
+    my $cmdline;
+    my $err = 0;
+
+    $cmdline = "$mkimage -l " . $logfile;
+    $cmdline = $cmdline . " -t " . template_name($image_name);
+    $cmdline = $cmdline . " -j " . jigdo_name($image_name);    
+    $cmdline = $cmdline . " -s " . $start;
+    $cmdline = $cmdline . " -e " . $end;
+    $cmdline = $cmdline . " " . $matches;
+
+#    print "X-output: cmdline $cmdline\n";
+    $err = system($cmdline) >> 8;
+    if ($err) {
+        log_die ("Failed to rebuild image; error $err\n");
+    }
+}
+
+# Calculate start, end and length from the supplied range
+sub parse_range ($$) {
+    my ($range, $size) = @_;
+    my @offsets;
+    my $content_length = 0;
+
+    if (length($range) == 1 && $range =~ m/-/g ) {
+        $offsets[0] = 0;
+        $offsets[1] = $size - 1;
+    } else {
+        @offsets = split(/-/, $range, 2);
+    }
+
+    if (!defined($offsets[0]) || !length($offsets[0])) {
+        $offsets[0] = -1;
+    }
+    if (!defined($offsets[1]) || !length($offsets[1])) {
+        $offsets[1] = $size - 1;
+    }
+    if ($offsets[0] == -1) {
+        $offsets[0] = $size - $offsets[1];
+        $offsets[1] = $size - 1;
+    }
+
+    # Make sure we have been given numbers
+    $offsets[0] = int($offsets[0]);
+    $offsets[1] = int($offsets[1]);
+
+    # Check they're valid
+    if ($offsets[0] < 0 || $offsets[0] >= $size) {
+        print "Range start $offsets[0] invalid!\n";
+        log_die "Range start $offsets[0] invalid!\n";
+    }
+    if ($offsets[1] < 0 || $offsets[1] >= $size) {
+        print "Range end $offsets[1] invalid!\n";
+        log_die "Range end $offsets[1] invalid!\n";
+    }
+    if ($offsets[0] > $offsets[1]) {
+        print "Range start $offsets[0] after end $offsets[1]!\n";
+        log_die "Range start $offsets[0] after end $offsets[1]!\n";
+    }
+    $content_length = $offsets[1] + 1 - $offsets[0];
+    return ($offsets[0], $offsets[1], $content_length);
+}
+
+# We've been asked for ranges. Calculate which ones, then start generating them
+sub produce_ranges ($$$) {
+    my ($image_name, $size, $ranges) = @_;
+    my $output_name = basename($image_name);
+    my $cmdline;
+    my $err = 0;
+    my @range_array;
+    my ($start, $end, $range, $length);
+    
+    chomp $ranges;
+    $ranges =~ s/^.*\=//g;
+    @range_array = split (/,/, $ranges);
+    if (scalar(@range_array) == 1) {
+        ($start, $end, $length) = parse_range($range_array[0], $size);
+        print "Status: 206 Partial content\n";
+        print "Content-Type: application/octet-stream; filename=$output_name\n";
+        print "Content-Range: bytes $start-$end/$size\n";
+        print "Content-Length: $length\n\n";
+        $err = produce_range($image_name, $start, $end);
+    } else {
+        print "Status: 206 Partial content\n";
+        print "Content-Type: multipart/byteranges; boundary=THIS_STRING_SEPARATES\n\n";
+        for my $range (@range_array) {
+            ($start, $end, $length) = parse_range($range, $size);
+            print "--THIS_STRING_SEPARATES\n";
+            print "Content-Type: application/octet-stream; filename=$output_name\n";
+            print "Content-Range: bytes $start-$end/$size\n\n";
+            $err = produce_range($image_name, $start, $end);
+        }
+        print "--THIS_STRING_SEPARATES\n";
+    }
+}
+
+################################################################################
+#
+# All starts here
+#
+
+my $remote_host = $ENV{'REMOTE_HOST'};
+my $remote_addr = $ENV{'REMOTE_ADDR'};
+my $ultimate = $ENV{'HTTP_X_FORWARDED_FOR'};
+my $iaddr;
+my $ult_name;
+
+# Sanity checking
+if (!defined($ARGV[0])) {
+    user_error("You must specify an image name to download.");
+    log_die "No file specified...!\n";
+}
+
+if (!defined($remote_host) || !length($remote_host)) {
+    $iaddr = inet_aton($remote_addr);
+    $remote_host = gethostbyaddr($iaddr, AF_INET);
+}
+
+print LOGFILE "Connection made from $remote_addr ($remote_host)\n";
+if (defined($ultimate) && length($ultimate)) {
+    $iaddr = inet_aton($ultimate);
+    $ult_name = gethostbyaddr($iaddr, AF_INET);
+    print LOGFILE "Proxy for:         $ultimate ($ult_name)\n";
+}
+
+print LOGFILE "Asking for $ENV{SCRIPT_NAME}/$ENV{QUERY_STRING}\n";
+print LOGFILE "Generating $ARGV[0]:\n";
+print LOGFILE scalar localtime;
+print LOGFILE "\n\n\n";
+
+$image_name = $ARGV[0];
+
+if (! -f template_name($image_name)) {
+    user_error("No template file found to match image name \"$image_name\"");
+    log_die ("Couldn't find template file for image $image_name\n");
+}
+
+if (! -f jigdo_name($image_name)) {
+    user_error("No jigdo file file found to match image name \"$image_name\"");
+    log_die ("Couldn't find jigdo file for image $image_name\n");
+}
+
+$size = image_size($image_name);
+chomp $size;
+
+if (defined($ENV{HTTP_RANGE})) {
+       # We have range(s) specified. Parse what we've been given and call
+       # mkimage for each range
+    produce_ranges($image_name, $size, $ENV{HTTP_RANGE});
+} else {
+       # If we don't have a Range: header, simply return the whole image
+    produce_full_image($image_name, $size);
+}
+
+print LOGFILE "Done\n";
+print LOGFILE scalar localtime;
+print LOGFILE "\n\n\n";
+exit 0;