#!/usr/bin/perl # fs2web - make your filesystem accessible from the web # (C) 2002-2004 by Amir Malik. All Rights Reserved. # web: http://www.unoc.net/a/fs2web/ # email: a@unoc.net # Copyright (c) 2002-2004 Amir Malik. # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. # If you are using Apache, here is a sample configuration to make / point # directly to the fs2web script so that all paths can be handled by it. # # Alias /images/ /home/webfs/www/images/ # ScriptAliasMatch ^/(.*) /home/webfs/cgi-bin/fs2web/$1 # ServerName webfs.us.unoc.net # #################### CONFIGURATION start ############### # url to this script; no slash / at the end $url = "http://webfs.us.unoc.net"; # stream the multimedia file (eg. RealServer), leave blank if none $streamurl = "http://streaming-webfs.us.unoc.net:8000"; # allow downloads, 0 if no, but dir browsing is still possible $download = 0; # redirect to $baseurl if set to 1 to d/l file, 0 to just pipe it out $redirect = 0; # allow deletion of files (ANY file; BE CAREFUL HERE!!!) $deletion = 0; # allow uploads to ANY directory (BE CAREFUL HERE!!!) $upload = 0; # enable dynamic playlist generation (M3U format) $dynamicm3u = 1; # if $redirect is 1, then where are the files located locally $basedir = "/storage/webfs"; # url of the path containing the downloadable files (required if redirect=1) $baseurl = "http://yoursite/ftp"; # url to the images $imageurl = "/images"; # file types we have images for @filetypes = ("avi", "bmp", "gif", "html", "jpg", "mov", "mpeg", "swf", "txt", "zip"); # streamable file types %streamtypes = qw( avi 1 mpg 1 mpeg 1 mp3 1 m3u 0 pls 1 mp2 1 mov 1 rm 1 swf 1 ogg 1 ); # extensions that will always be allowed to download inline %allowdown = qw( txt 1 nfo 1 jpg 1 jpeg 1 gif 1 png 1 ); #################### CONFIGURATION end ################# # CGI.pm is EXTREMELY slow! comment out the following block to speed things up # you could actually have 2 script; one main one and another for uploads... if($upload && $ENV{'REQUEST_METHOD'} eq 'POST') { use CGI qw/:standard/; $CGI::DISABLE_UPLOADS = 0; $CGI::POST_MAX = 1048576 * 100; # max 100 MB uploads } $VERSION = '1.1'; if($^O !~ /win32/i) { $SIG{'ALRM'} = sub { exit(0) }; alarm(600); # max 10 min on Linux/UNIX systems, then exit script } $ENV{'HTTP_REFERER'} = $url if(!$ENV{'HTTP_REFERER'}); $ENV{'PATH_INFO'} =~ s/(\\\/|\/\\|\\\\|\/\/)//g; $ENV{'PATH_INFO'} =~ s/\/$//; $ENV{'PATH_INFO'} =~ s/^\///; $vir_path = $ENV{'PATH_INFO'}; $vir_path =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; $vir_path = '' if(length($vir_path) > 512); my @in = split(/[&;]/,$ENV{'QUERY_STRING'}); foreach $i (0 .. $#in) { $in[$i] =~ s/\+/ /g; ($key, $val) = split(/=/,$in[$i],2); # splits on the first =. # Convert %XX from hex numbers to alphanumeric $key =~ s/%([A-Fa-f0-9]{2})/pack("c",hex($1))/ge; $val =~ s/%([A-Fa-f0-9]{2})/pack("c",hex($1))/ge; # Associate key and value $in{$key} .= "\0" if (defined($in{$key})); # \0 is the multiple separator $in{$key} .= $val; } if($vir_path =~ /\.\./) { print "Content-type: text/html\r\n\r\n"; print <<_EOF; fs2web - Error

Error

The requested resource /$vir_path does not exist. You are not allowed to access a higher level directory with ..
Return to $ENV{HTTP_REFERER}. _EOF exit(0); } else { $path = "$basedir/$vir_path"; } # 2004.05.22 - dynamic m3u playlist generation if( $in{'pls'} eq 'm3u' ) { print "Content-Type: audio/x-mpegurl\r\nContent-Disposition: attachment; filename=fs2web-playlist.m3u\r\nCache-Control: none\r\n\r\n"; opendir(DIR, $path); my @files = readdir(DIR); closedir(DIR); foreach my $file (@files) { my $ext = lc(substr($file, rindex($file, "\.")+1)); if($ext ne 'm3u' && $streamtypes{$ext} == 1) { $file =~ s/ /\%20/g; $file =~ s/\#/%23/g; # 2004.08.11 - support space in directory names # 2004.09.03 - support # in file names $vir_path =~ s/ /\%20/g; $vir_path =~ s/\#/%23/g; print "$streamurl/$vir_path/$file\n"; } } exit(0); } if(-d $path) { $action = 'browse'; } elsif(-e $path) { $action = 'get'; } else { print "Content-type: text/html\r\nStatus: 404 Not Found\r\n\r\n"; print <<_EOF; 404 Not Found

404 Not Found

The requested resource /$vir_path does not exist.
Return to $ENV{HTTP_REFERER}. _EOF exit(0); } $thisdir = ($vir_path eq "") ? "" : "$vir_path/"; ($action eq "browse") ? &browse : &get; sub browse { print "Content-type: text/html\r\n\r\n"; #$thisdir =~ s/ /\%20/g; $uri = $vir_path; print <<_EOF; fs2web - Index of /$uri

Index of /$uri

_EOF if($deletion && $in{d}) { $in{d} =~ s/(\\|\/|\.\.)//g; my $file = "$basedir/$vir_path/$in{d}"; if(unlink($file)) { print "

The file, $in{d} was successfully deleted.

"; } else { print "

The file, $in{d} could not be deleted!

"; } } if($upload && $ENV{'REQUEST_METHOD'} eq 'POST') { my $file = param('fileupload'); if($file) { $file =~ m/^.*(\\|\/)(.*)/; my $name = $2; $name =~ s/(\\|\/|\.\.)//g; my $msg; if(open(X,">$basedir/$vir_path/$name")) { binmode(X); while(<$file>) { print X; } close(X); $msg = 'was successfully saved.'; } else { $msg = 'could not be saved!'; } print <<_EOF;

The file, $name, $msg

_EOF } } opendir(BASE, $path); @names = readdir(BASE); closedir(BASE); foreach $name (@names) { $isdir = opendir(DIR, "$basedir/$thisdir$name"); if ($isdir == 1) { push(@dirs, $name) unless ($name =~ /^\./); } else { push(@files, $name) unless ($name =~ /^\./); } closedir(DIR); # some of the following code is based on CGIHK's ftpex # see http://www.cgihk.com/ for more info (undef, undef, undef, undef, undef, undef, undef, $size, undef, $modified, undef, undef, undef) = stat("$basedir/$thisdir$name"); ($min, $hr, $day, $mon, $yr) = (localtime($modified))[1,2,3,4,5]; $min = "0$min" unless $min >= 10; $hr = "0$hr" unless $hr >= 10; $day = "0$day" unless $day >= 10; $mon = "0$mon" unless $mon >= 10; $yr += 1900; $mon++; # they are from 0 to 11 (man localtime) $filesize{$name} = int((1023 + $size) / 1024) . " kb"; if($filesize{$name} > 1024) { $filesize{$name} = int((1023 + $filesize{$name}) / 1024) . " Mb"; } # 2004.05.13 - changed to conform to ISO 8601 date format #$last_mod{$name} = "$day-$mon-$yr $hr:$min"; $last_mod{$name} = "$yr-$mon-$day $hr:$min"; } # 2004.08.11 - moved here to support directory names with spaces $thisdir =~ s/ /\%20/g; print <<_EOF; _EOF foreach $dir (sort @dirs) { $dir_url = $dir; $dir_url =~ s/ /\%20/g; print <<_EOF; _EOF } foreach $file (sort @files) { # 2004.05.22 - hide dot-files next if(substr($file, 1) eq '.'); $ext = substr($file, rindex($file, "\.")+1); $type = "file"; foreach $filetype (@filetypes) { if (lc($ext) eq $filetype) {$type = $filetype;} } $file_url = $file; $file_url =~ s/ /\%20/g; print <<_EOF; "; if($deletion) { $delete = ""; } if($streamurl && $ext =~ /(m3u|pls|mp3|mpg|mpeg|avi|mov|rm|swf)/i) { print <<_EOF; $filetitle (stream it!) _EOF } else { print <<_EOF; $filetitle _EOF } print <<_EOF; $delete _EOF } print "
Name Options Last Modified Size
_EOF if($vir_path ne "") { $temp = $vir_path =~ tr#/#/#; if ($temp > 0) { $pardir = "$url/browse/".substr($vir_path, 0, rindex($vir_path, "/")); } else { $pardir = ""; } print " Parent Directory\n"; } print <<_EOF;
$dir   $last_mod{$dir} $filesize{$dir}
_EOF my $filetitle = $file; $filetitle = substr($file, 0, 31) . '...' if(length($file) > 29); my $delete = " delete$last_mod{$file} $filesize{$file}
\n"; # 2004.05.22 - playlist link printf <<_EOF if($dynamicm3u);
Generate a playlist.
_EOF print <<_EOF if($upload);

Upload a file to this directory:
_EOF print <<_EOF;

Powered by fs2web $VERSION
_EOF } sub get { $download = 1 if("/$vir_path" eq '/robots.txt'); my $inline = 0; my $inlinetype = 'text/plain'; my $inline_ext = substr($vir_path, rindex($vir_path, "\.")+1); if($allowdown{$inline_ext} == 1) { $download = 1; $inline = 1; if($inline_ext =~ /(gif|jpg|jpeg|png)/i) { $inlinetype = "image/$1"; } } if(!$download) { print "Content-Type: text/html\r\n\r\n"; print <<_EOF; fs2web - Error

Error

Sorry, but the administrator has disabled file downloads! Possible reason(s):
Return to $ENV{HTTP_REFERER}. _EOF exit(0); } if($redirect) { print "Content-Type: application/octet-stream\r\n"; print "Location: $baseurl/$vir_path\r\n"; print "URI: $baseurl/$vir_path\r\n\r\n"; } else { if($inline) { print "Content-Disposition: inline\r\nContent-Type: $inlinetype\r\n"; } else { print "Content-Type: application/octet-stream\r\n"; } # TODO: handle HTTP's Range parameter so that one can resume a download #my $rset = int($ENV{'HTTP_RANGE'}); open(FILE,"$basedir/$vir_path"); binmode(FILE); $size = -s "$basedir/$vir_path"; print "Content-Length: $size\r\n\r\n"; #if($rset && $rset < $size) { # seek(FILE,$rset,0); # $size = $size - $rset; #} while(!eof(FILE)) { read(FILE, $x, 16384); print $x; } close(FILE); } }