#!/usr/bin/perl # Julianster, (C) 2001-2005 Julian Haight. GPL License, all rights reserved. # Version: 1.3 # WARNING, THIS SCRIPT MAY NOT BE SECURE - IT MAY EXPOSE READ ACCESS TO # FILES OUTSIDE THE SHARE FOLDER. Attempts have been made to defeat # this sort of attack against it, but they may not be 100% effective. # Furthermore, the script should be run as an under-privileged user (nobody). # BUGS: External programs (mpg123, bladeenc, tar) should be referenced # as constants rather than hard-coded. use strict; use CGI; use DB_File; use DBI; package julianster; my($base) = '/home/share/snd'; my($q) = new CGI; my($path, $dir, $action, %settings, %stats, $fh); my(%speeds) = ('192' => '192K/second or best, from local disk '. $base, '192slimp3' => '192K/second or best, through slimp3', '192http' => '192K/second or best, from webserver', '32' => '32K/second (modem speed), from webserver)', '64' => '64K/second (ISDN speed), from webserver)'); my(@sorder) = ('32', '64', '192', '192slimp3', '192http'); my($bwlimit) = 100000; # bytes/second when dling tar my($db) = "$base/julianster"; my($locked) = 0; my($body) = '
'; julianster(); sub julianster { ($action) = $q->param('action') || 'show'; ($path) = $ENV{'PATH_INFO'}; ($settings{'speed'}) = $q->cookie('speed') || '192http'; ($settings{'mtype'}) = $q->cookie('mtype') || 'audio/x-mpegurl'; ($settings{'user'}) = $q->cookie('user'); ($dir) = "$base$path"; unless ($path) { my $host = $ENV{SERVER_NAME}; ## Port (from ENV or from HOST or 80) my $port = $ENV{SERVER_PORT} || $ENV{HTTP_PORT}; $port = (($host =~ s/:(\d+)$//) ? $1 : 80) unless (defined $port); ## For the URL my $domain = $port==80 ? $host : "$host:$port"; print "Location: http://$domain$ENV{'SCRIPT_NAME'}/\n\n"; # play (recursively or not) } elsif (($action eq 'play') || ($action eq 'p')) { play($path); # download a tarfile } elsif ($action eq 'download') { download(); # default actions, show directory } elsif (($action eq 'show') || ($action eq 'date')) { show($dir); # show settings form } elsif ($action eq 'settings') { showSettings(); # save settings with cookies, then show dir } elsif ($action eq 'savesettings') { saveSettings(); show($dir); # stream a song, downsampling } elsif ($action eq 'downsample32') { downSample(32); } elsif ($action eq 'downsample64') { downSample(64); # show yourself! } elsif ($action eq 'sourcecode') { sourceCode(); # Two random-play modes } elsif (($action eq 'random') || ($action eq 'truerandom') || ($action eq 'userrandom')) { playRandom(100); } elsif ($action eq 'search') { search(); # debugging function } elsif ($action eq 'listall') { listAll(); } elsif ($action eq 'editlist') { editList(); } elsif ($action eq 'truncate') { trunc(); } elsif ($action eq 'uploadform') { uploadForm(); } elsif ($action eq 'upload') { upload(); } elsif ($action eq 'slimp3set') { slimp3set(); } elsif ($action eq 'shim') { shim(); } else { print "Content-type:text/plain\n\nUnknown action:$action\n"; } } sub upload { my($name, $tmpname, $fh, $artist, $album, $dir, $tmpname, $file); $artist = $q->param('artist'); $album = $q->param('album'); $name = $q->param('file'); $tmpname = $q->tmpFileName($name), $name =~ m/([^\/\\\~\|]*)$/; # trim off all but filename part print "Content-type: text/html\n\n"; print 'Wrote ' . $file . ', size:' . $_[7] . '
Thanks!
Upload another? '; } sub uploadForm { print "Content-type: text/html\n\n"; print '
Truncated random-play list to $id\n"; } sub editList { my(%stats, $max, $i, $f); my($user) = $settings{'user'}; print 'Content-type: text/html
Subdirectories: '; if ($path ne '/') { print '
Play All . Download All '; } print '
'; my($let, $last); foreach $file (sort mysort @files) { if ($path eq '/') { $let = uc(substr($file, 0, 1)); if (($let ne $last) && ($action ne 'date')) { $last = $let; print '
' . $let . '
(^)
';
}
}
print '
';
if (-e "$base$path$file/" . "Cover.jpg") {
print '
';
} else {
print '';
}
print 'View .
Play .
DL .
';
print $file;
if ($action eq 'date') {
print sprintf(' (%.1f days old)', -M "$base$path$file");
}
print "
\n";
}
}
(@files) = readDirFiles($dir);
if (@files) {
foreach $file (sort mysort @files) {
if ($file =~ m/[\.\_]jpg$/) {
if ($file =~ m/back/i) {
$backimg = imgLink($path, $file);
} else {
if (!$frontimg || ($file =~ m/large/i)) {
$frontimg = imgLink($path, $file);
} elsif ($file =~ m/Cover\.jpg$/) {
$frontimg = imgLink($path, $file);
}
}
} elsif ($file =~ m/jpg$/) {
} else {
$listing .= '
Play .
DL .
' . pretify($file);
}
}
print '
Julianster is (C) 2001, 2002
Julian Haight.
Released under the GPL (GNU Public License). All rights reserved.
Perl Source Code
This page generated ' . CGI::expires('now') . '
';
}
sub headList {
my($dir, $where) = @_;
my($file, $tmp);
my($r) = '
TOP';
foreach $file (split('\/', $dir)) {
if ($file) {
$tmp .= "$file/";
if ("/$tmp" eq $where) {
$r .= '
:: ' . $file . '';
} else {
$r .= '
:: ' . $file . '';
}
}
}
return $r;
}
sub download {
$| = 1;
my($name) = $path;
my($size, $timer, $bytes, $chunk, $b);
$name =~ s/(.*\/)([^\/]+)(\/)?$/$2/;
$name .= '.tar';
chdir($base);
$size = int($size);
print STDERR "Sending $name ($size bytes)\n";
print "Content-Disposition: inline; filename=\"$name\"\n";
print "Content-type: application/octet-stream\n\n";
if (1==1) {
open (TAR, "/bin/tar -c \".$path\" |") || die $!;
$timer = time();
while ($chunk = read(TAR, $b, 1024)) {
$bytes += $chunk;
while (($ENV{'REMOTE_ADDR'} !~ m/^192\.168\./) &&
($bytes > ((time() - $timer) * $bwlimit))) {
sleep(1);
}
print $b;
}
if (!defined($chunk)) {
print STDERR "Error reading from tar: $!\n";
}
close (TAR) || die $!;
} else {
system('/bin/tar', '-c', ".$path");
}
close STDOUT;
}
sub play {
dbmopen(%stats, $db, 0644) || print STDERR ("Can't open $db $!");
if ($settings{'speed'} eq '192slimp3') {
startSlimp3();
makePlayList($path);
endSlimp3();
} else {
print "Content-type: " . $settings{'mtype'} . "\n\n";
makePlayList($path);
}
dbmclose(%stats);
}
sub makePlayList {
my($dir) = @_;
my($file, $num, $choice);
my($user) = $settings{'user'};
# collect stats on files we chose
if (-d "$base$dir") {
if ($action ne 'p') { # recurse only on "play" action
foreach $file (sort mysort readDirSubs("$base$dir")) {
makePlayList("$dir$file/");
}
}
foreach $file (sort mysort readDirFiles("$base$dir")) {
$num = $stats{'max'}++;
$stats{$num} = "$dir$file";
if ($user) {
$num = $stats{"$user-max"}++;
$stats{"$user-$num"} = "$dir$file";
}
modeLink("$dir$file");
}
} else {
$num = $stats{'max'}++;
$stats{$num} = $path;
if ($user) {
$num = $stats{"$user-max"}++;
$stats{"$user-$num"} = "$dir$file";
}
modeLink($path);
}
}
sub playRandom {
my($qty) = @_;
my($i, $f, $max);
my($user) = $settings{'user'};
if ($settings{'speed'} eq '192slimp3') {
startSlimp3();
} else {
print "Content-type: " . $settings{'mtype'} . "\n\n";
}
dbmopen(%stats, $db, 0644) || die("Can't open $db $!");
if ($action eq 'random') {
$max = $stats{'max'};
} elsif ($user && ($action eq 'userrandom')) {
$max = $stats{"$user-max"};
} else {
$max = $stats{'maxf'};
}
for ($i=0; $i < $qty; $i++) {
if ($action eq 'random') {
$f = $stats{(int(rand()*$max))};
} elsif ($user && ($action eq 'userrandom')) {
$f = $stats{$user . '-' . (int(rand()*$max))};
} else {
$f = $stats{('f' . int(rand()*$max))};
}
if ($f =~ m/\.mp3$/i) {
modeLink($f);
}
}
if ($settings{'speed'} eq '192slimp3') {
endSlimp3();
}
dbmclose(%stats);
}
sub search {
my($qry) = $q->param('q');
my(@files, $file, $dir, $olddir, $fname);
print 'Expires: ' . CGI::expires("+1d") . "\n";
print "Content-type: text/html\n\n";
print '
'; (@files) = findMatches($qry); if (@files) { foreach $file (sort mysort @files) { $file = CGI::escape($file); $file =~ s/\%2F/\//g; $dir = $file; $dir =~ s/^(.*\/)([^\/]*)$/$1/; $fname = $2; if ($dir ne $olddir) { $olddir = $dir; print '
' . headList($dir) . '
';
}
print '
Play .
DL
' . $fname;
}
} else {
print 'Nothing found';
}
print foot();
}
sub highlight {
my($text, $light, $font) = @_;
my($result, $loc, $lloc);
$loc = 0; $lloc = 0;
while ($text) {
$loc = index($text, $light);
if ($loc >= 0) {
$result .= (substr($text, 0, $loc) .
'' .
$light . '');
$text = substr($text, $loc + length($light));
} else {
$result .= $text; $text = '';
}
}
return $result;
}
sub findMatches {
my($qry) = @_;
$qry = lc($qry);
unless ($qry) { return (); }
my($max, $f, $i, @res);
dbmopen(%stats, $db, 0644) || die("Can't open $db $!");
$max = $stats{'maxf'};
for ($i=0; $i < $max; $i++) {
$f = $stats{('f' . int($i))};
if (index(lc($f), $qry) > 0) {
push(@res, $f);
}
}
dbmclose(%stats);
return (@res);
}
sub modeLink {
my($path) = @_;
if (($settings{'speed'} eq '192') || ($settings{'speed'} eq '192slimp3')) {
print "$base$path\n";
} else {
$path = CGI::escape($path); $path =~ s/\%2F/\//g;
if ($settings{'speed'} eq '192http') {
print "http://$ENV{'HTTP_HOST'}/mp3s$path\n";
} elsif ($settings{'speed'} eq '32') {
print ("http://$ENV{'HTTP_HOST'}$ENV{'SCRIPT_NAME'}$path?action=downsample32\n");
} elsif ($settings{'speed'} eq '64') {
print ("http://$ENV{'HTTP_HOST'}$ENV{'SCRIPT_NAME'}$path?action=downsample64\n");
} else {
print STDERR $settings{speed} . "\n";
}
}
}
sub listAll {
my($i, $max);
print "Content-type: " . $settings{'mtype'} . "\n\n";
print join("\n", %ENV);
return;
dbmopen(%stats, $db, 0644) || die("Can't open $db $!");
$max = $stats{'max'};
for ($i=0; $i < $max; $i++) {
modeLink($stats{$i});
}
$max = $stats{'maxf'};
for ($i=0; $i < $max; $i++) {
modeLink($stats{'f' . $i});
}
dbmclose(%stats);
}
sub rebuildFind {
dbmopen(%stats, $db, 0644) || die("Can't open $db $!");
if ($stats{'fdate'} < (time() - (60*60*24*7))) {
$| = 0;
print "
Rebuilding full file list.\n";
$stats{'maxf'} = 0;
$stats{'fdate'} = time();
makeFind('');
} else {
print '
, ' . $stats{'maxf'} . ' files in database';
}
dbmclose(%stats);
}
sub downSample {
my($rate) = @_;
# print "Content-type: text/plain\n\nrate:$rate file:$dir";
print "Content-type: audio/mpeg\n\n";
open(IN, "mpg123 -s -q \"$dir\" | /usr/local/bin/bladeenc -$rate -quiet -mono stdin stdout|") || die $!;
while (
Edit random list
';
}
sub saveSettings {
# print "Content-type: text/plain\n\n";
my($setting);
foreach $setting ('speed', 'mtype', 'user') {
if ($q->param($setting) ne $settings{$setting}) {
print 'Set-Cookie: ' .
$q->cookie(-name => $setting,
-value => ($settings{$setting} =
$q->param($setting)),
-expires => '+1y',
-path => '/') . "\n";
# print STDERR "setting cookie $setting\n";
}
}
if ($q->param('resetrand')) {
dbmopen(%stats, $db, 0644) || print STDERR ("Can't open $db $!");
$stats{$q->param('user') . '-max'} = 0;
dbmclose(%stats);
}
}
sub startSlimp3 {
open (OUT, '>/tmp/play.m3u') || die "/tmp/play.m3u $!";
$fh = select(OUT);
}
sub endSlimp3 {
select($fh);
close(OUT);
print ('Content-type: text/html