Warning fixes
[jigit.git] / iso-image.pl
1 #!/usr/bin/perl -w
2 #
3 # iso-image.pl
4 #
5 # (C) 2004 Steve McIntyre <steve@einval.com>
6 #
7 # CGI wrapper to work with mkimage
8 #
9 # Parse the parameters and call mkimage as appropriate, paying 
10 # particular attention to byte ranges
11 #
12 # GPL v2
13 #
14 # v 0.1
15
16 use strict;
17 use File::Basename;
18 use Socket;
19
20 # Configure these for your system
21 my $mkimage = "/usr/local/bin/mkimage";
22 my $logfile = "/var/log/jigdo-logs/log.$$";
23 my $template_dir = "/mirror/jigdo";
24 my $matches = "-q -m Debian=/mirror/debian -m Non-US=/mirror/debian-non-US";
25
26 my $size;
27 my $image_name;
28 open LOGFILE, ">> $logfile" || die "Unable to open logfile!\n";
29
30 # Log an error to the user
31 sub user_error ($) {
32     my $my_name = "iso_image.pl";
33     print "Status: 400 Invalid Request\n";
34     print "Content-type: text/html\n\n";
35     print "$my_name: @_<p>Abort.\n";
36 }
37
38 # Log a message to the logfile and stop
39 sub log_die ($) {
40     print LOGFILE @_;
41     die @_;
42 }
43
44 # Convert the .iso filename into a .jigdo
45 sub jigdo_name ($) {
46     my ($jigdo_name) = @_;
47     $jigdo_name =~ s/\.iso$/\.jigdo/g;
48     $jigdo_name = $template_dir . "/" . $jigdo_name;
49     return $jigdo_name;
50 }
51
52 # Convert the .iso filename into a .template
53 sub template_name ($) {
54     my ($template_name) = @_;
55     $template_name =~ s/\.iso$/\.template/g;
56     $template_name = $template_dir . "/" . $template_name;
57     return $template_name;
58 }
59
60 # Grab the image size out of the template file
61 sub image_size ($) {
62     my ($image_name) = @_;
63     my $image_size;
64     my $cmdline = "$mkimage -l " . $logfile . " -z -t " . template_name($image_name);
65
66     open (FH, '-|', $cmdline);
67     $image_size = <FH>;
68     close FH;
69     return $image_size;
70 }
71
72 # We have no range headers; simply generate the full image
73 sub produce_full_image ($$) {
74     my ($image_name, $size) = @_;
75     my $output_name = basename($image_name);
76     my $cmdline;
77     my $err = 0;
78
79     $cmdline = "$mkimage -l " . $logfile;
80     $cmdline = $cmdline . " -t " . template_name($image_name);
81     $cmdline = $cmdline . " -j " . jigdo_name($image_name);    
82     $cmdline = $cmdline . " " . $matches;
83
84     print "Status: 200 OK\n";
85     print "Content-Type: application/octet-stream\n";
86     print "Content-Disposition: inline;filename=$output_name\n";
87     print "Content-Length: $size\n\n";
88
89     $err = system($cmdline) >> 8;
90     if ($err) {
91         log_die ("Failed to rebuild image; error $err\n");
92     }
93 }
94
95 # Generate the desired range
96 sub produce_range ($$$) {
97     my ($image_name, $start, $end) = @_;
98     my $output_name = basename($image_name);
99     my $cmdline;
100     my $err = 0;
101
102     $cmdline = "$mkimage -l " . $logfile;
103     $cmdline = $cmdline . " -t " . template_name($image_name);
104     $cmdline = $cmdline . " -j " . jigdo_name($image_name);    
105     $cmdline = $cmdline . " -s " . $start;
106     $cmdline = $cmdline . " -e " . $end;
107     $cmdline = $cmdline . " " . $matches;
108
109 #    print "X-output: cmdline $cmdline\n";
110     $err = system($cmdline) >> 8;
111     if ($err) {
112         log_die ("Failed to rebuild image; error $err\n");
113     }
114 }
115
116 # Calculate start, end and length from the supplied range
117 sub parse_range ($$) {
118     my ($range, $size) = @_;
119     my @offsets;
120     my $content_length = 0;
121
122     if (length($range) == 1 && $range =~ m/-/g ) {
123         $offsets[0] = 0;
124         $offsets[1] = $size - 1;
125     } else {
126         @offsets = split(/-/, $range, 2);
127     }
128
129     if (!defined($offsets[0]) || !length($offsets[0])) {
130         $offsets[0] = -1;
131     }
132     if (!defined($offsets[1]) || !length($offsets[1])) {
133         $offsets[1] = $size - 1;
134     }
135     if ($offsets[0] == -1) {
136         $offsets[0] = $size - $offsets[1];
137         $offsets[1] = $size - 1;
138     }
139
140     # Make sure we have been given numbers
141     $offsets[0] = int($offsets[0]);
142     $offsets[1] = int($offsets[1]);
143
144     # Check they're valid
145     if ($offsets[0] < 0 || $offsets[0] >= $size) {
146         print "Range start $offsets[0] invalid!\n";
147         log_die "Range start $offsets[0] invalid!\n";
148     }
149     if ($offsets[1] < 0 || $offsets[1] >= $size) {
150         print "Range end $offsets[1] invalid!\n";
151         log_die "Range end $offsets[1] invalid!\n";
152     }
153     if ($offsets[0] > $offsets[1]) {
154         print "Range start $offsets[0] after end $offsets[1]!\n";
155         log_die "Range start $offsets[0] after end $offsets[1]!\n";
156     }
157     $content_length = $offsets[1] + 1 - $offsets[0];
158     return ($offsets[0], $offsets[1], $content_length);
159 }
160
161 # We've been asked for ranges. Calculate which ones, then start generating them
162 sub produce_ranges ($$$) {
163     my ($image_name, $size, $ranges) = @_;
164     my $output_name = basename($image_name);
165     my $cmdline;
166     my $err = 0;
167     my @range_array;
168     my ($start, $end, $range, $length);
169     
170     chomp $ranges;
171     $ranges =~ s/^.*\=//g;
172     @range_array = split (/,/, $ranges);
173     if (scalar(@range_array) == 1) {
174         ($start, $end, $length) = parse_range($range_array[0], $size);
175         print "Status: 206 Partial content\n";
176         print "Content-Type: application/octet-stream; filename=$output_name\n";
177         print "Content-Range: bytes $start-$end/$size\n";
178         print "Content-Length: $length\n\n";
179         $err = produce_range($image_name, $start, $end);
180     } else {
181         print "Status: 206 Partial content\n";
182         print "Content-Type: multipart/byteranges; boundary=THIS_STRING_SEPARATES\n\n";
183         for my $range (@range_array) {
184             ($start, $end, $length) = parse_range($range, $size);
185             print "--THIS_STRING_SEPARATES\n";
186             print "Content-Type: application/octet-stream; filename=$output_name\n";
187             print "Content-Range: bytes $start-$end/$size\n\n";
188             $err = produce_range($image_name, $start, $end);
189         }
190         print "--THIS_STRING_SEPARATES\n";
191     }
192 }
193
194 ################################################################################
195 #
196 # All starts here
197 #
198
199 my $remote_host = $ENV{'REMOTE_HOST'};
200 my $remote_addr = $ENV{'REMOTE_ADDR'};
201 my $ultimate = $ENV{'HTTP_X_FORWARDED_FOR'};
202 my $iaddr;
203 my $ult_name;
204
205 # Sanity checking
206 if (!defined($ARGV[0])) {
207     user_error("You must specify an image name to download.");
208     log_die "No file specified...!\n";
209 }
210
211 if (!defined($remote_host) || !length($remote_host)) {
212     $iaddr = inet_aton($remote_addr);
213     $remote_host = gethostbyaddr($iaddr, AF_INET);
214 }
215
216 print LOGFILE "Connection made from $remote_addr ($remote_host)\n";
217 if (defined($ultimate) && length($ultimate)) {
218     $iaddr = inet_aton($ultimate);
219     $ult_name = gethostbyaddr($iaddr, AF_INET);
220     print LOGFILE "Proxy for:         $ultimate ($ult_name)\n";
221 }
222
223 print LOGFILE "Asking for $ENV{SCRIPT_NAME}/$ENV{QUERY_STRING}\n";
224 print LOGFILE "Generating $ARGV[0]:\n";
225 print LOGFILE scalar localtime;
226 print LOGFILE "\n\n\n";
227
228 $image_name = $ARGV[0];
229
230 if (! -f template_name($image_name)) {
231     user_error("No template file found to match image name \"$image_name\"");
232     log_die ("Couldn't find template file for image $image_name\n");
233 }
234
235 if (! -f jigdo_name($image_name)) {
236     user_error("No jigdo file file found to match image name \"$image_name\"");
237     log_die ("Couldn't find jigdo file for image $image_name\n");
238 }
239
240 $size = image_size($image_name);
241 chomp $size;
242
243 if (defined($ENV{HTTP_RANGE})) {
244         # We have range(s) specified. Parse what we've been given and call
245         # mkimage for each range
246     produce_ranges($image_name, $size, $ENV{HTTP_RANGE});
247 } else {
248         # If we don't have a Range: header, simply return the whole image
249     produce_full_image($image_name, $size);
250 }
251
252 print LOGFILE "Done\n";
253 print LOGFILE scalar localtime;
254 print LOGFILE "\n\n\n";
255 exit 0;