#! /usr/bin/perl -w use English; use strict; use CGI; use FileHandle; use HTTP::Date; use SafeIO; my $imgdir = 'usrimg/'; my $infofile = $imgdir . 'info.filecook'; my $quota = 40 * 1024 * 1024; my $opresult = 0; my $headerprinted = 0; my %userdata = ( ); my %magic_codes = ( 'GIF8[79]a' => 'image/gif', chr(0xff).chr(0xd8).chr(0xff).chr(0xe0).chr(0x00).chr(0x10).'JFIF' => 'image/jpeg', chr(0x89).'PNG' => 'image/png', chr(0xff).chr(0xd8).chr(0xff).chr(0xe1) => 'image/jpeg' ); my $magic_length = 10; # hash keyed by id containing: # 'comment' => comment # 'filename' => filename # 'size' => size # 'mimetype' => mime type # my $imgstat; my $imgstat_changed = 0; my $q = new CGI; # ------------ sub main { ObtainLock ( $infofile ); $imgstat = ReadFile ( $infofile, { } ); GetUserData(); # ------------------------ # handle CGI query # list cases which need a lock on the info file first if($q->param('upload')) { ensureheader(); print handle_upload_submission(); } if ( $imgstat_changed ) { WriteFile ( $infofile, $imgstat ); } ReleaseLock ( $infofile ); # list cases which don't need a lock on the info file if($q->param('get') && ($q->param('get') =~ /([0-9]+)/)) { getimage($1); } elsif($q->param('chg') && ($q->param('chg')=~/^([0-9]+)$/)) { display_imagechange_page($1); } else { display_imagelist(); } } # ------------------------ sub ensureheader { my ($text) = @_; if($headerprinted) { return; } $headerprinted = 1; $text or $text = "Image Server"; print $q->header, $q->start_html($text); } sub getimage { my ($id) = @_; my $entry = $imgstat->{$id}; if(!$entry) { ensureheader("Error fetching image"); ErrorExit ("$id: invalid filename"); } print $q->header( -type=>$entry->{'mimetype'}, -expires=>'+1h', "Content-Length"=>$entry->{'size'} ); ObtainLock ($entry->{'filename'}); my $fh = OpenFile ($entry->{'filename'}); my $buffer; read ($fh, $buffer, $entry->{'size'}); CloseFile ($fh); ReleaseLock ($entry->{'filename'}); print $buffer; exit(0); } sub display_imagechange_page { my ($id) = @_; ensureheader('Change image.'); print '

Change image '.$id.'

'; print '
'; my $entry = $imgstat->{$id}; if ($entry) { print ''; print '
'; print $entry->{'comment'}; print 'Size: ' . $entry->{'size'} . ' bytes.
'; print 'If you are sure you want to change this image, use the form below:
'; print $q->start_multipart_form; print '
File to upload: '; print $q->filefield(-name=>'new_image', -default=>'starting value', -size=>50, -maxlength=>255); print $q->hidden('id',$id); print '
'; print 'Comment (displayed in image list): '; print $q->textfield('comment',$entry->{'comment'},50,80); print $q->submit(-name=>'upload', -value=>'Upload new image and/or change comment.'); print $q->endform; } else { print "Sorry! No such image seems to exist."; } print '
Back to image list'; print '
ToothyWiki'; print $q->end_html; } sub get_list_link { my ($start, $numentries, $text) = @_; if ($start < 0) { $start = 0; } return '' . $text . ''; } sub display_imagelist { my $total = 0; my $imagecount = 0; my $start = 0; my $end = 250; if ($userdata{'img_numentries'}) { $end = $start + $userdata{'cookie'}->{'img_numentries'}; } else { $userdata{'img_numentries'} = $end; } if ($q->param('start') && $q->param('start') =~ /([0-9]+)/) { $start = $1; } if ($q->param('numentries') && $q->param('numentries') =~ /([0-9]+)/) { $end = $start + $1; $userdata{'img_numentries'} = $1; $userdata{'changed'} = 1; } ensureheader(); my $numimages = keys %$imgstat; if ($end > $numimages) { $end = $numimages; } print get_list_link($start,$userdata{'img_numentries'},"Refresh Page"); print ' | Image server manual'; print ' | ToothyWiki'; if ($userdata{'id'}) { print ' | ' .$userdata{'id'} . '. '; } my $linkline = "Showing images " . ( $start + 1 ) . " to $end of $numimages"; # change this when we allow non-numeric IDs. if ($start != 0) { $linkline .= "; show previous " . get_list_link($start-50,50,"50") . " " . get_list_link($start-100,100,"100") . " " . get_list_link($start-250,250,"250"); } if ($end < $numimages) { $linkline .= "; show next " . get_list_link($end,50,"50") . " " . get_list_link($end,100,"100") . " " . get_list_link($end,250,"250"); } if($numimages) { print "
\n$linkline\n

\n"; } foreach my $id (sort {$a <=> $b} keys %$imgstat) { my $entry = $imgstat->{$id}; my $iline = '' . ''; $iline .= '
' . $id . ': ' . $entry->{'comment'} . '' . $entry->{'size'} . ' bytes. ' . 'View/Change
'; $total += $entry->{'size'}; $imagecount++; if ( ($imagecount > $start) && ($imagecount <= $end) ) { print $iline; } } print ''; if($numimages) { print "
\n$linkline
\n"; } print 'Quota: ' . $quota . ' bytes;' . ' used ' . $total . ' bytes; ' . 'remaining ' . ($quota - $total) . ' bytes.
'; print '
'; print '
'; print '

Upload a new image:

'; print $q->start_multipart_form; print '
File to upload: '; print $q->filefield(-name=>'new_image', -default=>'starting value', -size=>50, -maxlength=>255); print '
'; print 'Comment (displayed in image list): '; print $q->textfield('comment','(type a short comment to describe the new image)',50,80); print '
'; print $q->submit(-name=>'upload', -value=>'Upload the new image.'); print $q->endform; print '

'; print get_list_link($start,$userdata{'img_numentries'},"Refresh Page"); print ' | Image server manual'; print ' | ToothyWiki'; if ($userdata{'id'}) { print ' | ' .$userdata{'id'} . '. '; } print $q->end_html; } sub set_entry { my ($id, $entry) = @_; $imgstat->{$id} = $entry; $imgstat_changed = 1; } sub calculate_total_size { my $total = 0; foreach my $image ( keys %$imgstat ) { $total += $imgstat->{$image}->{'size'}; } return $total; } sub make_unique_id { my $id = 1; while ( defined ( $imgstat->{$id} ) ) { $id++; } return $id; } sub receive_upload { my ($fh, $newid, $comment, $oldid) = @_; my $total = calculate_total_size(); my $entry = { 'filename' => $imgdir . $newid, 'comment' => $comment }; my $result = ''; my $magic = ''; ObtainLock( $entry->{'filename'} ); my $ofh = OpenFile($entry->{'filename'}, '>'); $entry->{'size'} = read($fh, $magic, $magic_length); $entry->{'mimetype'} = identify($magic); if($entry->{'mimetype'}) { print $ofh $magic; my $buf = ''; $entry->{'size'} += read($fh, $buf, 1024); while ($buf && ($entry->{'size'} < ($quota - $total))) { print $ofh $buf; $entry->{'size'} += read($fh, $buf, 1024); } CloseFile( $entry->{'filename'} ); } else { $result .= 'Image upload failed: unknown image type!'; CloseFile( $entry->{'filename'} ); unlink $entry->{'filename'}; $entry->{'size'} = 0; } ReleaseLock ( $entry->{'filename'} ); if($entry->{'size'} && ( $entry->{'size'} < ($quota - $total) )) { if ( $oldid && ( $oldid != $newid ) ) { if( $imgstat->{ $oldid } ) { ObtainLock ( $imgdir.$newid ); ObtainLock ( $imgdir.$oldid ); rename $imgdir . $newid, $imgdir . $oldid; $entry->{'filename'} = $imgdir . $oldid; set_entry( $oldid, $entry ); $result .= 'Image ' . $oldid . ' (' . $entry->{'mimetype'} . ') was changed successfully.'; ReleaseLock ( $imgdir.$oldid ); ReleaseLock ( $imgdir.$newid ); } else { ObtainLock ( $imgdir.$newid ); unlink $imgdir . $newid; ReleaseLock ( $imgdir.$newid ); $result .= 'Could not change image '.$oldid.': image does not exist.'; } } else { set_entry( $newid, $entry ); $result .= 'Image '.$newid . ' ('.$entry->{'mimetype'}.') was uploaded successfully.'; } } else { $result .= 'Image upload failed: quota exceeded!'; ObtainLock ( $imgdir.$newid ); unlink $imgdir . $newid; ReleaseLock ( $imgdir.$newid ); } return '' . $result . '

'; } sub handle_upload_submission { my $fh = $q->upload('new_image'); my $got_file = 0; my $oldid = 0; my $newid = make_unique_id(); my $comment = 0; my $result = ''; if (($q->param('id')) && ($q->param('id') =~ /^([0-9]+)$/) && ($imgstat->{$q->param('id')})) { $oldid = $1; $comment = $imgstat->{$oldid}->{'$comment'}; } if($q->param('comment')) { $comment = $q->param('comment'); } if($fh) { $result .= receive_upload( $fh, $newid, $comment, $oldid ); } else { if($comment) { if($oldid) { $imgstat->{$oldid}->{'comment'} = $comment; $imgstat_changed = 1; } else { $result = "

Couldn't set comment - couldn't find image $oldid."; } } else { $result = '

No valid data was sent by the browser.'; } } return $result; } sub identify { my ($magic) = @_; foreach my $key (keys %magic_codes) { if($magic =~ /^$key/) { return $magic_codes{$key}; } } return 0; } # -- ID and user prefs ------------------------------ sub GetUserData { my $ip = GetRemoteHost(1); my %cookie = $q->cookie('ToothyWiki'); my $id = $ip; if ($cookie{'id'}) { $id = 'ToothyWiki user ' . $cookie{'id'}; $userdata{'cookie'} = \%cookie; } $userdata{'id'} = $id; } # --------------------------------------------------- &main();