############################################################################### # Attachments.pm # # $Date: 01 Sep 2026 $ # ############################################################################### # YaBB: Yet another Bulletin Board # # Open-Source Community Software for Webmasters # # Version: YaBBForum 3.2 # # Packaged: 01 Sep 2026 # # Distributed by: https://yabbforum.nz # # =========================================================================== # # Copyright (c) 2000-2026 YaBB (yabbforum.nz) - All Rights Reserved. # # Software by: The YaBB Development Team # # with assistance from the YaBB community. # ############################################################################### use CGI::Carp qw(fatalsToBrowser); our $VERSION = '3.2'; $attachmentspmver = 'YaBBForum 3.2'; if ( $action eq 'detailedversion' ) { return 1; } sub Attachments { is_admin_or_gmod(); fopen( AMS, "$vardir/attachments.txt" ); my @attachments = ; fclose(AMS); my $attachment_space = 0; foreach (@attachments) { $attachment_space += NumberFormat( ( split /\|/xsm, $_, 7 )[5] ); } my $remaining_space; if ( !$dirlimit ) { $remaining_space = "$fatxt{'23'}"; } else { $remaining_space = NumberFormat( ( $dirlimit - $attachment_space ) ) . ' KB'; } fopen( FILE, "$vardir/oldestattach.txt" ); $maxdaysattach = ; fclose(FILE); fopen( FILE, "$vardir/oldestpmattach.txt" ); $pmMaxDaysAttach = ; fclose(FILE); fopen( FILE, "$vardir/maxattachsize.txt" ); $maxsizeattach = ; fclose(FILE); fopen( FILE, "$vardir/maxpmattachsize.txt" ); $pmMaxSizeAttach = ; fclose(FILE); fopen( PMATTACHLOG, "$vardir/pm.attachments" ); my @pmAttachments = ; fclose(PMATTACHLOG); my $pmAttachmentSpace = 0; foreach (@pmAttachments) { $pmAttachmentSpace += NumberFormat( ( split /\|/xsm, $_, 4 )[2] ); } my $pmRemainingSpace; if ( !$pmDirLimit ) { $pmRemainingSpace = "$fatxt{'23a'}"; } else { $pmRemainingSpace = NumberFormat( ( $pmDirLimit - $pmAttachmentSpace ) ) . ' KB'; } my $totalattachnum = @attachments; my $pmTotalAttachNum = @pmAttachments; $yymain .= qq~
$admin_img{'xx'} $fatxt{'24'}
$fatxt{'25'}
$fatxt{'26'}
$fatxt{'27'}
$fatxt{'28'} $totalattachnum
$fatxt{'29'} $attachment_space KB
$fatxt{'30'} $remaining_space

$fatxt{'28a'} $pmTotalAttachNum
$fatxt{'29a'} $pmAttachmentSpace KB
$fatxt{'30a'} $pmRemainingSpace
$fatxt{'31'}
$fatxt{'32'} $fatxt{'58'} 
$fatxt{'33'} KB 
$fatxt{'31a'} | $fatxt{'63'}
$fatxt{'31b'}
$fatxt{'32a'} $fatxt{'58'} 
$fatxt{'33a'} KB 
$fatxt{'31c'} | $fatxt{'63a'}
~; $yytitle = "$fatxt{'36'}"; $action_area = 'manageattachments'; AdminTemplate(); return; } sub RemoveOldAttachments { is_admin_or_gmod(); my $maxdaysattach = $FORM{'maxdaysattach'} || $INFO{'maxdaysattach'}; if ( $maxdaysattach !~ /^[0-9]+$/xsm ) { fatal_error('only_numbers_allowed'); } # Set up the multi-step action $time_to_jump = time() + $max_process_time; automaintenance('on'); opendir ATT, $uploaddir || fatal_error( 'cannot_open', "$uploaddir", 1 ); my @attachments = sort grep { /\w+$/xsm } readdir ATT; closedir ATT; my $info; if ( !@attachments ) { fopen( ATT, ">$vardir/attachments.txt" ) || fatal_error( 'cannot_open', "$vardir/attachments.txt", 1 ); print {ATT} q{} or croak "$croak{'print'} ATT"; fclose(ATT); $info = qq~
$fatxt{'48'}.~; } else { fopen( AML, "$vardir/attachments.txt" ); foreach (@attachmentstxt) { my @line = split /\|/xsm, $_; $att{ $line[7] } = $line[0]; } if ( !exists $INFO{'next'} ) { unlink "$vardir/rem_old_attach.tmp"; } my %rem_attachments; for my $aa ( ( $INFO{'next'} || 0 ) .. ( @attachments - 1 ) ) { # -M => Script start time minus file modification time, in days. my $age = sprintf '%.2f', -M "$uploaddir/$attachments[$aa]"; if ( $age <= $maxdaysattach ) { # If the attachment is not too old $info .= qq~
$attachments[$aa] = $age $admin_txt{'122'}.~; } elsif ( exists $att{ $attachments[$aa] } ) { $rem_attachments{ $att{ $attachments[$aa] } } .= $rem_attachments{ $att{ $attachments[$aa] } } ? "|$attachments[$aa]" : $attachments[$aa]; $info .= qq~
$attachments[$aa] $fatxt{'1'} = $age $admin_txt{'122'}.~; } if ( $time_to_jump < time() && ( $aa + 1 ) < @attachments ) { # save the $info of this run until the end of 'RemoveOldAttachments' fopen( FILE, ">>$vardir/rem_old_attach.tmp" ) || fatal_error( 'cannot_open', "$vardir/rem_old_attach.tmp", 1 ); print $info or croak "$croak{'print'} rem_old_attach"; fclose(FILE); $yySetLocation = qq~$adminurl?action=removeoldattachments;maxdaysattach=$maxdaysattach;next=~ . ( $aa + 1 - RemoveAttachments( \%rem_attachments ) ); redirectexit(); } } RemoveAttachments( \%rem_attachments ); } automaintenance('off'); $yymain .= qq~$fatxt{'32'} $maxdaysattach $fatxt{'58'}.
~; fopen( FILE, "$vardir/rem_old_attach.tmp" ); # $yymain .= join( q{}, ) . $info; $yymain .= do { local $INPUT_RECORD_SEPARATOR = undef; } . $info; fclose(FILE); unlink "$vardir/rem_old_attach.tmp"; fopen( FILE, ">$vardir/oldestattach.txt" ); print {FILE} $maxdaysattach or croak "$croak{'print'} oldestattach"; fclose(FILE); $yytitle = "$fatxt{'34'} $maxdaysattach"; $action_area = 'removeoldattachments'; AdminTemplate(); return; } sub RemoveBigAttachments { is_admin_or_gmod(); my $maxsizeattach = $FORM{'maxsizeattach'} || $INFO{'maxsizeattach'}; if ( $maxsizeattach !~ /^[0-9]+$/xsm ) { fatal_error('only_numbers_allowed'); } # Set up the multi-step action $time_to_jump = time() + $max_process_time; automaintenance('on'); opendir ATT, $uploaddir || fatal_error( 'cannot_open', "$uploaddir", 1 ); my @attachments = sort grep { /\w+$/xsm } readdir ATT; closedir ATT; my %filelist = (); foreach my $i (@attachments) { $filelist{$i} = 1; } fopen( FILE, "$vardir/attachments.txt" ); @attachmentstxt = ; fclose(FILE); my ( %att, @line ); foreach (@attachmentstxt) { @line = split /\|/xsm, $_; if ( $filelist{$line[7]} ) { $att{ $line[7] } = $line[0]; } } my $info; if ( !@attachments ) { fopen( ATT, ">$vardir/attachments.txt" ) || fatal_error( 'cannot_open', "$vardir/attachments.txt", 1 ); print {ATT} q{} or croak "$croak{'print'} ATT"; fclose(ATT); $info = qq~
$fatxt{'48'}.~; } else { if ( !exists $INFO{'next'} ) { unlink "$vardir/rem_big_attach.tmp"; } my (%rem_attachments); for my $aa ( ( $INFO{'next'} || 0 ) .. ( @attachments - 1 ) ) { my $size = sprintf '%.2f', ( ( -s "$uploaddir/$attachments[$aa]" ) / 1024 ); if ( $size <= $maxsizeattach ) { # If the attachment is not too big $info .= qq~
$attachments[$aa] = $size KB~; } elsif ( exists $att{ $attachments[$aa] } ) { $rem_attachments{ $att{ $attachments[$aa] } } .= $rem_attachments{ $att{ $attachments[$aa] } } ? "|$attachments[$aa]" : $attachments[$aa]; $info .= qq~
$attachments[$aa] $fatxt{'1'} = $size KB~; } if ( $time_to_jump < time() && ( $aa + 1 ) < @attachments ) { # save the $info of this run until the end of 'RemoveBigAttachments' fopen( FILE, ">>$vardir/rem_big_attach.tmp" ) || fatal_error( 'cannot_open', "$vardir/rem_big_attach.tmp", 1 ); print $info or croak "$croak{'print'} rem_big_attach"; fclose(FILE); $yySetLocation = qq~$adminurl?action=removebigattachments;maxsizeattach=$maxsizeattach;next=~ . ( $aa + 1 - RemoveAttachments( \%rem_attachments ) ); redirectexit(); } } RemoveAttachments( \%rem_attachments ); } $yymain .= qq~$fatxt{'33'} $maxsizeattach KB.
~; fopen( FILE, "$vardir/rem_big_attach.tmp" ); # $yymain .= join( q{}, ) . $info; $yymain .= do { local $INPUT_RECORD_SEPARATOR = undef; } . $info; fclose(FILE); unlink "$vardir/rem_big_attach.tmp"; fopen( FILE, ">$vardir/maxattachsize.txt" ); print {FILE} $maxsizeattach or croak "$croak{'print'} FILE"; fclose(FILE); automaintenance('off'); $yytitle = "$fatxt{'35'} $maxsizeattach KB"; $action_area = 'removebigattachments'; AdminTemplate(); return; } sub Attachments2 { is_admin_or_gmod(); fopen( AML, "$vardir/attachments.txt" ); my @attachinput = ; fclose(AML); my $max = @attachinput; my $action = $INFO{'action'}; my $sort = $INFO{'sort'} || 6; my $newstart = $INFO{'newstart'} || 0; if ( !$max ) { $viewattachments .= qq~$fatxt{'48'}~; } else { $yymain .= qq~
~; my @attachments; if ( $sort > 0 ) { # sort ascending if ( $sort == 5 || $sort == 6 || $sort == 8 ) { @attachments = sort { ( split /\|/xsm, $a )[$sort] <=> ( split /\|/xsm, $b )[$sort]; } @attachinput; # sort size, date, count numerically } elsif ( $sort == 100 ) { @attachments = sort { lc( ( split /\./xsm, ( split /\|/xsm, $a )[7] )[1] ) cmp lc( ( split /\./xsm, ( split /\|/xsm, $b )[7] )[1] ); } @attachinput; # sort extension lexically } else { @attachments = sort { lc( ( split /\|/xsm, $a )[$sort] ) cmp lc( ( split /\|/xsm, $b )[$sort] ); } @attachinput; # sort lexically } } else { # sort descending if ( $sort == -5 || $sort == -6 || $sort == -8 ) { @attachments = reverse sort { ( split /\|/xsm, $a )[ -$sort ] <=> ( split /\|/xsm, $b )[ -$sort ]; } @attachinput; # sort size, date, count numerically } elsif ( $sort == -100 ) { @attachments = reverse sort { lc( ( split /\./xsm, ( split /\|/xsm, $a )[7] )[1] ) cmp lc( ( split /\./xsm, ( split /\|/xsm, $b )[7] )[1] ); } @attachinput; # sort extension lexically } else { @attachments = reverse sort { lc( ( split /\|/xsm, $a )[ -$sort ] ) cmp lc( ( split /\|/xsm, $b )[ -$sort ] ); } @attachinput; # sort lexically } } $postdisplaynum = 8; $newstart = ( int( $newstart / 25 ) ) * 25; $tmpa = 1; if ( $newstart >= ( ( $postdisplaynum - 1 ) * 25 ) ) { $startpage = $newstart - ( ( $postdisplaynum - 1 ) * 25 ); $tmpa = int( $startpage / 25 ) + 1; } if ( $max >= $newstart + ( $postdisplaynum * 25 ) ) { $endpage = $newstart + ( $postdisplaynum * 25 ); } else { $endpage = $max; } if ( $startpage > 0 ) { $pageindex = qq~1 ... ~; } if ( $startpage == 25 ) { $pageindex = qq~1 ~; } foreach my $counter ( $startpage .. ( $endpage - 1 ) ) { if ( $counter % 25 == 0 ) { $pageindex .= $newstart == $counter ? qq~$tmpa ~ : qq~$tmpa ~; $tmpa++; } } $lastpn = int( $max / 25 ) + 1; $lastptn = ( $lastpn - 1 ) * 25; if ( $endpage < $max - (25) ) { $pageindexadd = q~... ~; } if ( $endpage != $max ) { $pageindexadd .= qq~$lastpn~; } $pageindex .= $pageindexadd; $pageindex = qq~
$fatxt{'64'}: $pageindex
~; $numbegin = ( $newstart + 1 ); $numend = ( $newstart + 25 ); if ( $numend > $max ) { $numend = $max; } if ( $max == 0 ) { $numshow = q{}; } else { $numshow = qq~($numbegin - $numend)~; } my ( %attach_gif, $ext ); foreach my $row ( splice @attachments, $newstart, 25 ) { chomp $row; my ( $amthreadid, $amreplies, $amthreadsub, $amposter, $amcurrentboard, $amkb, $amdate, $amfn, $amcount ) = split /\|/xsm, $row; if ( $amfn =~ /\.(.+?)$/xsm ) { $ext = $1; } if ( !exists $attach_gif{$ext} ) { $attach_gif{$ext} = ( $ext && -e "$htmldir/Templates/Forum/$useimages/$ext.gif" ) ? "$ext.gif" : 'paperclip.gif'; } $amdate = timeformat($amdate); $amkb = NumberFormat($amkb); if ( length($amthreadsub) > 30 ) { $amthreadsub = substr( $amthreadsub, 0, 30 ) . q{...}; } my $amfna = $amfn; if ( length($amfn) > 30 ) { $amfna = substr( $amfna, 0, 30 ) . q{...}; } $viewattachments .= qq~ $amfna $amkb KB $amdate $amcount $amthreadsub $amposter ~; } $viewattachments .= qq~
<=  
$pageindex ~; $yymain .= qq~ ~; } my $class_sortattach = $sort =~ /7/sm ? 'catbg' : 'windowbg'; my $class_sorttype = $sort =~ /100/sm ? 'catbg' : 'windowbg'; my $class_sortsize = $sort =~ /5/sm ? 'catbg' : 'windowbg'; my $class_sortdate = $sort =~ /6/sm ? 'catbg' : 'windowbg'; my $class_sorcount = $sort =~ /8/sm ? 'catbg' : 'windowbg'; my $class_sortsubj = $sort =~ /2/sm ? 'catbg' : 'windowbg'; my $class_sortuser = $sort =~ /3/sm ? 'catbg' : 'windowbg'; my $rsort; $yymain .= qq~
~; $rsort = ( $sort == 7 ? -7 : 7 ); $yymain .= qq~ ~; $rsort = ( $sort == 100 ? -100 : 100 ); $yymain .= qq~ ~; $rsort = ( $sort == 5 ? -5 : 5 ); $yymain .= qq~ ~; $rsort = ( $sort == 6 ? -6 : 6 ); $yymain .= qq~ ~; $rsort = ( $sort == 8 ? -8 : 8 ); $yymain .= qq~ ~; $rsort = ( $sort == 2 ? -2 : 2 ); $yymain .= qq~ ~; $rsort = ( $sort == 3 ? -3 : 3 ); $yymain .= qq~ $viewattachments
$admin_img{'xx'} $fatxt{'39'}
$fatxt{'38'}
$fatxt{'55'}
$fatxt{'28'} $max $numshow
$pageindex
$fatxt{'6c'} $fatxt{'40'} $fatxt{'40a'} $fatxt{'41'} $fatxt{'43'} $fatxt{'41a'} $fatxt{'44'} $fatxt{'42'}
~; if ($max) { $yymain .= '
'; } $yytitle = "$fatxt{'37'}"; $action_area = 'manageattachments'; AdminTemplate(); return; } sub DeleteAttachments { is_admin_or_gmod(); if ( !$FORM{'formsession'} ) { automaintenance('on'); } my %rem_att; foreach ( keys %FORM ) { if ( $_ =~ /^del_(\d+)$/xsm ) { my $thread = $1; $rem_att{$thread} = $FORM{$_}; $rem_att{$thread} =~ s/, /|/gsm; } else { next; } } RemoveAttachments( \%rem_att ); if ( !$FORM{'formsession'} ) { automaintenance('off'); } $yySetLocation = $FORM{'formsession'} ? qq~$scripturl?action=viewdownloads;thread=~ . ( keys %rem_att )[0] . qq~;newstart=$FORM{'newstart'}~ : qq~$adminurl?action=manageattachments2;newstart=$FORM{'newstart'}~; redirectexit(); return; } sub FullRebuildAttachents { is_admin_or_gmod(); if ( !defined $INFO{'boardnum'} ) { automaintenance('on'); unlink "$vardir/newattachments.tmp"; $yySetLocation = qq~$adminurl?action=rebuildattach;topicnum=0;boardnum=0~; redirectexit(); } # Set up the multi-step action $time_to_jump = time() + $max_process_time; # Get the board list from the forum.master file require "$boardsdir/forum.master"; @boardlist = sort keys %board; # Find the current board: my $curboard = $boardlist[ $INFO{'boardnum'} ]; # store all downloadcounts in variable my %attachments; if ( ( -s "$vardir/attachments.txt" ) > 5 ) { my ( $atfile, $atcount ); fopen( ATM, "$vardir/attachments.txt" ); while () { ( undef, undef, undef, undef, undef, undef, undef, $atfile, $atcount ) = split /\|/xsm, $_; chomp $atcount; $attachments{$atfile} = $atcount; } fclose(ATM); } # Get the topic list. fopen( BOARD, "$boardsdir/$curboard.txt" ); my @topiclist = ; fclose(BOARD); my ( $topicnum, @newattachments, $mreplies, $msub, $mname, $mdate, $mfn, $nexttopic ); for my $i ( $INFO{'topicnum'} .. ( @topiclist - 1 ) ) { ( $topicnum, undef ) = split /\|/xsm, $topiclist[$i], 2; fopen( TOPIC, "$datadir/$topicnum.txt" ); my @topic = ; fclose(TOPIC); chomp @topic; $mreplies = 0; foreach (@topic) { ( $msub, $mname, undef, $mdate, undef, undef, undef, undef, undef, undef, undef, undef, $mfn ) = split /\|/xsm, $_; foreach ( split /,/xsm, $mfn ) { if ( -e "$uploaddir/$_" ) { my $asize = int( ( -s "$uploaddir/$_" ) / 1024 ) || 1; push @newattachments, qq~$topicnum|$mreplies|$msub|$mname|$curboard|$asize|$mdate|$_|~ . ( $attachments{$_} || 0 ) . qq~\n~; } } $mreplies++; } if ( time() > $time_to_jump && ( $i + 1 ) < @topiclist ) { $nexttopic = $i + 1; last; } } if (@newattachments) { fopen( NEWATM, ">>$vardir/newattachments.tmp" ) || fatal_error( 'cannot_open', "$vardir/newattachments.tmp", 1 ); print {NEWATM} @newattachments or croak "$croak{'print'} NEWATM"; fclose(NEWATM); } # Prepare to continue... if ($nexttopic) { $INFO{'topicnum'} = $nexttopic; } else { $INFO{'boardnum'}++; $INFO{'topicnum'} = 0; } my $numleft = @boardlist - $INFO{'boardnum'}; if ( $numleft == 0 ) { fopen( NEWATM, "$vardir/newattachments.tmp" ); @newattachments = ; fclose(NEWATM); fopen( ATM, ">$vardir/attachments.txt" ); print {ATM} sort { ( split /\|/xsm, $a )[6] <=> ( split /\|/xsm, $b )[6] } @newattachments or croak "$croak{'print'} ATM"; fclose(ATM); unlink "$vardir/newattachments.tmp"; automaintenance('off'); $yySetLocation = qq~$adminurl?action=remghostattach~; redirectexit(); } # Continue $action_area = 'manageattachments'; $yytitle = "$fatxt{'37'}"; $yymain .= qq~
$rebuild_txt{'1'}
$rebuild_txt{'5'} $max_process_time $rebuild_txt{'6'}
$rebuild_txt{'9'} ~ . ( @boardlist - $INFO{'boardnum'} ) . q{/} . @boardlist . qq~

$rebuild_txt{'2'} $rebuild_txt{'3'}
~; AdminTemplate(); return; } sub RemoveGhostAttach { is_admin_or_gmod(); $yymain .= qq~$fatxt{'62'}

~; fopen( ATM, "$vardir/attachments.txt" ); my @attachmentstxt = ; fclose(ATM); my %att; foreach (@attachmentstxt) { $att{ ( split /\|/xsm, $_ )[7] } = 1; } opendir DIR, $uploaddir; my @filesDIR = grep { /\w+$/xsm } readdir DIR; closedir DIR; $yymain .= qq~$fatxt{'61'}:
~; foreach my $fileinDIR (@filesDIR) { if ( !$att{$fileinDIR} && $fileinDIR ne 'index.html' && $fileinDIR ne '.htaccess' ) { unlink "$uploaddir/$fileinDIR"; $yymain .= qq~
$fatxt{'61b'}: $fileinDIR~; } } $yymain .= qq~

$fatxt{'61a'}~; $yytitle = $fatxt{'61'}; $action_area = 'manageattachments'; AdminTemplate(); return; } sub RemoveAttachments { # remove single or multiple attachments stored in a hash-reference my $count = 0; my $ThreadHashref = shift; # usage: ${$ThreadHashref}{'threadnum'} = 'filename1|filename2|...' # all attachments of thread are included if filname is undefined (undef) if ( !%{$ThreadHashref} ) { return $count; } fopen( ATM, "+<$vardir/attachments.txt", 1 ) || fatal_error( 'cannot_open', "$vardir/attachments.txt", 1 ); seek ATM, 0, 0; my @attachments = ; truncate ATM, 0; seek ATM, 0, 0; my ( $athreadnum, $afilename, %del_filename ); foreach (@attachments) { ( undef, undef, undef, undef, undef, undef, undef, $afilename, undef ) = split /\|/xsm, $_; $del_filename{$afilename}++; } for my $i ( 0 .. ( @attachments - 1 ) ) { ( $athreadnum, undef, undef, undef, undef, undef, undef, $afilename, undef ) = split /\|/xsm, $attachments[$i]; my $del = 0; if ( exists ${$ThreadHashref}{$athreadnum} ) { if ( defined ${$ThreadHashref}{$athreadnum} ) { foreach ( split /\|/xsm, ${$ThreadHashref}{$athreadnum} ) { if ( $_ eq $afilename ) { $del = 1; last; } } } else { $del = 1; } } if ($del) { # deletes the file only if NO other entry for the same filename is in the attachments.txt if ( $del_filename{$afilename} == 1 ) { unlink "$uploaddir/$afilename"; } $del_filename{$afilename}--; $count++; } else { print {ATM} $attachments[$i] or croak "$croak{'print'} ATM"; } } fclose(ATM); return $count; } sub PMAttachments2 { is_admin_or_gmod(); fopen( PMATTACHLOG, "$vardir/pm.attachments" ); my @pmAttachInput = ; fclose(PMATTACHLOG); my $max = @pmAttachInput; my $action = $INFO{'action'}; my $sort = $INFO{'sort'} || 1; my $newstart = $INFO{'newstart'} || 0; if ( !$max ) { $viewattachments .= qq~$fatxt{'48a'}~; } else { $yymain .= qq~
~; my @pmAttachments; if ( $sort > 0 ) { # sort ascending if ( $sort == 2 || $sort == 1 ) { @pmAttachments = sort { ( split /\|/xsm, $a )[$sort] <=> ( split /\|/xsm, $b )[$sort]; } @pmAttachInput; # sort size, date numerically } elsif ( $sort == 100 ) { @pmAttachments = sort { lc( ( split /\./xsm, ( split /\|/xsm, $a )[3] )[1] ) cmp lc( ( split /\./xsm, ( split /\|/xsm, $b )[3] )[1] ); } @pmAttachInput; # sort extension lexically } else { @pmAttachments = sort { lc( ( split /\|/xsm, $a )[$sort] ) cmp lc( ( split /\|/xsm, $b )[$sort] ); } @pmAttachInput; # sort lexically } } else { # sort descending if ( $sort == -2 || $sort == -1 ) { @pmAttachments = reverse sort { ( split /\|/xsm, $a )[ -$sort ] <=> ( split /\|/xsm, $b )[ -$sort ]; } @pmAttachInput; # sort size, date numerically } elsif ( $sort == -100 ) { @pmAttachments = reverse sort { lc( ( split /\./xsm, ( split /\|/xsm, $a )[3] )[1] ) cmp lc( ( split /\./xsm, ( split /\|/xsm, $b )[3] )[1] ); } @pmAttachInput; # sort extension lexically } else { @pmAttachments = reverse sort { lc( ( split /\|/xsm, $a )[ -$sort ] ) cmp lc( ( split /\|/xsm, $b )[ -$sort ] ); } @pmAttachInput; # sort lexically } } $postdisplaynum = 8; $newstart = ( int( $newstart / 25 ) ) * 25; $tmpa = 1; if ( $newstart >= ( ( $postdisplaynum - 1 ) * 25 ) ) { $startpage = $newstart - ( ( $postdisplaynum - 1 ) * 25 ); $tmpa = int( $startpage / 25 ) + 1; } if ( $max >= $newstart + ( $postdisplaynum * 25 ) ) { $endpage = $newstart + ( $postdisplaynum * 25 ); } else { $endpage = $max; } if ( $startpage > 0 ) { $pageindex = qq~1 ... ~; } if ( $startpage == 25 ) { $pageindex = qq~1 ~; } foreach my $counter ( $startpage .. ( $endpage - 1 ) ) { if ( $counter % 25 == 0 ) { $pageindex .= $newstart == $counter ? qq~$tmpa ~ : qq~$tmpa ~; $tmpa++; } } $lastpn = int( $max / 25 ) + 1; $lastptn = ( $lastpn - 1 ) * 25; if ( $endpage < $max - (25) ) { $pageindexadd = q~... ~; } if ( $endpage != $max ) { $pageindexadd .= qq~$lastpn~; } $pageindex .= $pageindexadd; $pageindex = qq~
$fatxt{'64'}: $pageindex
~; $numbegin = ( $newstart + 1 ); $numend = ( $newstart + 25 ); if ( $numend > $max ) { $numend = $max; } if ( $max == 0 ) { $numshow = q{}; } else { $numshow = qq~($numbegin - $numend)~; } my ( %attach_gif, $ext ); foreach my $row ( splice @pmAttachments, $newstart, 25 ) { my ( undef, $pmAttachDate, $pmAttachKB, $pmAttachName, $pmAttachUser, undef ) = split /\|/xsm, $row; chomp $pmAttachUser; if ( $pmAttachName =~ /\.(.+?)$/xsm ) { $ext = $1; } if ( !exists $attach_gif{$ext} ) { $attach_gif{$ext} = ( $ext && -e "$htmldir/Templates/Forum/$useimages/$ext.gif" ) ? "$ext.gif" : 'paperclip.gif'; } $pmthreadid = $pmAttachDate; $pmAttachDate = timeformat($pmAttachDate); $pmAttachKB = NumberFormat($pmAttachKB); my $pmfna = $pmAttachName; if ( length($pmAttachName) > 30 ) { $pmfna = substr( $pmAttachName, 0, 30 ) . q{...}; } $viewattachments .= qq~ $pmfna $pmAttachKB KB $pmAttachDate $pmAttachUser ~; } $viewattachments .= qq~
<=  
$pageindex ~; $yymain .= qq~ ~; } my $class_sortattach = $sort =~ /3/sm ? 'catbg' : 'windowbg'; my $class_sorttype = $sort =~ /100/sm ? 'catbg' : 'windowbg'; my $class_sortsize = $sort =~ /2/sm ? 'catbg' : 'windowbg'; my $class_sortdate = $sort =~ /1/sm ? 'catbg' : 'windowbg'; my $class_sortuser = $sort =~ /4/sm ? 'catbg' : 'windowbg'; $yymain .= qq~
$viewattachments
$admin_img{'xx'} $fatxt{'39a'}
$fatxt{'38a'}
$fatxt{'55a'}
$fatxt{'28'} $max $numshow
$pageindex
$fatxt{'45'} $fatxt{'40'} $fatxt{'40a'} $fatxt{'41'} $fatxt{'43'} $fatxt{'42a'}
~; if ($max) { $yymain .= '
'; } $yytitle = "$fatxt{'37a'}"; $action_area = 'managepmattachments'; AdminTemplate(); return; } sub DeletePMAttachments { is_admin_or_gmod(); if ( !$FORM{'formsession'} ) { automaintenance('on'); } my %rem_att; foreach ( keys %FORM ) { if ( $_ =~ /^del_(\d+)$/xsm ) { my $thread = $1; $rem_att{$thread} = $FORM{$_}; $rem_att{$thread} =~ s/, /|/gsm; } else { next; } } RemovePMAttachments( \%rem_att ); if ( !$FORM{'formsession'} ) { automaintenance('off'); } $yySetLocation = qq~$adminurl?action=managepmattachments2;newstart=$FORM{'newstart'}~; redirectexit(); return; } sub RemoveOldPMAttachments { is_admin_or_gmod(); my $pmMaxDaysAttach = $FORM{'pmmaxdaysattach'} || $INFO{'pmmaxdaysattach'}; if ( $pmMaxDaysAttach !~ /^[0-9]+$/xsm ) { fatal_error('only_numbers_allowed'); } # Set up the multi-step action $time_to_jump = time() + $max_process_time; automaintenance('on'); opendir PMATTACHDIR, $pmuploaddir || fatal_error( 'cannot_open', "$pmuploaddir", 1 ); my @pmAttachments = sort grep { /\w+$/xsm } readdir PMATTACHDIR; closedir PMATTACHDIR; fopen( PMATTACHLOG, "$vardir/pm.attachments" ); my @pmAttachmentstxt = ; fclose(PMATTACHLOG); my ( %att, @line ); foreach (@pmAttachmentstxt) { @line = split /\|/xsm, $_; $att{ $line[3] } = $line[0]; } my $info; if ( !@pmAttachments ) { fopen( PMATTACHLOG, ">$vardir/pm.attachments" ) || fatal_error( 'cannot_open', "$vardir/pm.attachments", 1 ); print {PMATTACHLOG} q{} or croak "$croak{'print'} ATT"; fclose(PMATTACHLOG); $info = qq~
$fatxt{'48a'}.~; } else { if ( !exists $INFO{'next'} ) { unlink "$vardir/rem_old_pm_attach.tmp"; } my %rem_attachments; for my $aa ( ( $INFO{'next'} || 0 ) .. ( @pmAttachments - 1 ) ) { # -M => Script start time minus file modification time, in days. my $age = sprintf '%.2f', -M "$pmuploaddir/$pmAttachments[$aa]"; if ( $age <= $pmMaxDaysAttach ) { # If the attachment is not too old $info .= qq~
$pmAttachments[$aa] = $age $admin_txt{'122'}.~; } elsif ( exists $att{ $pmAttachments[$aa] } ) { $rem_attachments{ $att{ $pmAttachments[$aa] } } .= $rem_attachments{ $att{ $pmAttachments[$aa] } } ? "|$pmAttachments[$aa]" : $pmAttachments[$aa]; $info .= qq~
$pmAttachments[$aa] $fatxt{'1'} = $age $admin_txt{'122'}.~; } if ( $time_to_jump < time() && ( $aa + 1 ) < @pmAttachments ) { # save the $info of this run until the end of 'RemoveOldPMAttachments' fopen( FILE, ">>$vardir/rem_old_pm_attach.tmp" ) || fatal_error( 'cannot_open', "$vardir/rem_old_pm_attach.tmp", 1 ); print $info or croak "$croak{'print'} rem_big_attach"; fclose(FILE); $yySetLocation = qq~$adminurl?action=removeoldpmattachments;pmmaxdaysattach=$pmMaxDaysAttach;next=~ . ( $aa + 1 - RemovePMAttachments( \%rem_attachments ) ); redirectexit(); } } RemovePMAttachments( \%rem_attachments ); } automaintenance('off'); $yymain .= qq~$fatxt{'32a'} $pmMaxDaysAttach $fatxt{'58'}.
~; fopen( FILE, "$vardir/rem_old_pm_attach.tmp" ); # $yymain .= join( q{}, ) . $info; $yymain .= do { local $INPUT_RECORD_SEPARATOR = undef; } . $info; fclose(FILE); unlink "$vardir/rem_old_pm_attach.tmp"; fopen( FILE, ">$vardir/oldestpmattach.txt" ); print {FILE} $pmMaxDaysAttach or croak "$croak{'print'} FILE"; fclose(FILE); $yytitle = "$fatxt{'34a'} $pmMaxDaysAttach"; $action_area = 'removeoldpmattachments'; AdminTemplate(); return; } sub RemoveBigPMAttachments { is_admin_or_gmod(); my $pmmaxsizeattach = $FORM{'pmmaxsizeattach'} || $INFO{'pmmaxsizeattach'}; if ( $pmmaxsizeattach !~ /^[0-9]+$/xsm ) { fatal_error('only_numbers_allowed'); } # Set up the multi-step action $time_to_jump = time() + $max_process_time; automaintenance('on'); opendir ATT, $pmuploaddir || fatal_error( 'cannot_open', "$pmuploaddir", 1 ); my @attachments = sort grep { /\w+$/xsm } readdir ATT; closedir ATT; fopen( FILE, "$vardir/pm.attachments" ); @pmAttachmentstxt = ; fclose(FILE); my ( %att, @line ); foreach (@pmAttachmentstxt) { @line = split /\|/xsm, $_; $att{ $line[3] } = $line[0]; } my $info; if ( !@attachments ) { fopen( ATT, ">$vardir/pm.attachments" ) || fatal_error( 'cannot_open', "$vardir/pm.attachments", 1 ); print {ATT} q{} or croak "$croak{'print'} ATT"; fclose(ATT); $info = qq~
$fatxt{'48a'}.~; } else { if ( !exists $INFO{'next'} ) { unlink "$vardir/rem_big_pm_attach.tmp"; } my (%rem_attachments); for my $aa ( ( $INFO{'next'} || 0 ) .. ( @attachments - 1 ) ) { my $size = sprintf '%.2f', ( ( -s "$pmuploaddir/$attachments[$aa]" ) / 1024 ); if ( $size <= $pmmaxsizeattach ) { # If the attachment is not too big $info .= qq~
$attachments[$aa] = $size KB~; } elsif ( exists $att{ $attachments[$aa] } ) { $rem_attachments{ $att{ $attachments[$aa] } } .= $rem_attachments{ $att{ $attachments[$aa] } } ? "|$attachments[$aa]" : $attachments[$aa]; $info .= qq~
$attachments[$aa] $fatxt{'1'} = $size KB~; } if ( $time_to_jump < time() && ( $aa + 1 ) < @attachments ) { # save the $info of this run until the end of 'RemoveBigPMAttachments' fopen( FILE, ">>$vardir/rem_big_pm_attach.tmp" ) || fatal_error( 'cannot_open', "$vardir/rem_big_pm_attach.tmp", 1 ); print $info or croak "$croak{'print'} rem_big_pm_attach"; fclose(FILE); $yySetLocation = qq~$adminurl?action=removebigpmattachments;pmmaxsizeattach=$pmmaxsizeattach;next=~ . ( $aa + 1 - RemovePMAttachments( \%rem_attachments ) ); redirectexit(); } } RemovePMAttachments( \%rem_attachments ); } $yymain .= qq~$fatxt{'33a'} $pmmaxsizeattach KB.
~; fopen( FILE, "$vardir/rem_big_pm_attach.tmp" ); # $yymain .= join( q{}, ) . $info; $yymain .= do { local $INPUT_RECORD_SEPARATOR = undef; } . $info; fclose(FILE); unlink "$vardir/rem_big_pm_attach.tmp"; fopen( FILE, ">$vardir/maxpmattachsize.txt" ); print {FILE} $pmmaxsizeattach or croak "$croak{'print'} FILE"; fclose(FILE); automaintenance('off'); $yytitle = "$fatxt{'33a'} $pmmaxsizeattach KB"; $action_area = 'removebigpmattachments'; AdminTemplate(); return; } sub RemovePMAttachments { # remove single or multiple attachments stored in a hash-reference my $count = 0; my $ThreadHashref = shift; # usage: ${$ThreadHashref}{'threadnum'} = 'filename1|filename2|...' # all attachments of thread are included if filname is undefined (undef) if ( !%{$ThreadHashref} ) { return $count; } fopen( ATM, "+<$vardir/pm.attachments", 1 ) || fatal_error( 'cannot_open', "$vardir/pm.attachments", 1 ); seek ATM, 0, 0; my @pmAttachments = ; truncate ATM, 0; seek ATM, 0, 0; my ( $athreadnum, $afilename, %del_filename ); foreach (@pmAttachments) { ( undef, undef, undef, $afilename, undef, undef ) = split /\|/xsm, $_; $del_filename{$afilename}++; } for my $i ( 0 .. $#pmAttachments ) { ( $athreadnum, undef, undef, $afilename, undef, undef ) = split /\|/xsm, $pmAttachments[$i]; my $del = 0; if ( exists ${$ThreadHashref}{$athreadnum} ) { if ( defined ${$ThreadHashref}{$athreadnum} ) { foreach ( split /\|/xsm, ${$ThreadHashref}{$athreadnum} ) { if ( $_ eq $afilename ) { $del = 1; last; } } } else { $del = 1; } } if ($del) { # deletes the file only if NO other entry for the same filename is in the attachments.txt if ( $del_filename{$afilename} == 1 ) { unlink "$pmuploaddir/$afilename"; } $del_filename{$afilename}--; $count++; } else { print {ATM} $pmAttachments[$i] or croak "$croak{'print'} ATM"; } } fclose(ATM); return $count; } sub FullRebuildPMAttachments { is_admin_or_gmod(); automaintenance('on'); fopen( ATM, "$vardir/pm.attachments" ); @pm_attach = ; fclose(ATM); foreach my $pmattach ( @pm_attach ) { chomp $pmattach; ( $atid, $atdate, $atsize, $atfile, $atuser, $atusername ) = split /\|/xsm, $pmattach; if ( -e "$pmuploaddir/$atfile" ) { push @newattachments, qq~$atid|$atdate|$atsize|$atfile|$atuser|$atusername\n~; } } if (@newattachments) { fopen( NEWATM, ">>$vardir/newpmattachments.tmp" ) || fatal_error( 'cannot_open', "$vardir/newpmattachments.tmp", 1 ); print {NEWATM} @newattachments or croak "$croak{'print'} NEWATM"; fclose(NEWATM); } fopen( NEWATM, "$vardir/newpmattachments.tmp" ); @newattachments = ; fclose(NEWATM); fopen( ATM, ">$vardir/pm.attachments" ); print {ATM} @newattachments or croak "$croak{'print'} ATM"; fclose(ATM); unlink "$vardir/newpmattachments.tmp"; automaintenance('off'); $yySetLocation = qq~$adminurl?action=remghostpmattach~; redirectexit(); return; } sub RemoveGhostPMAttach { is_admin_or_gmod(); $yymain .= qq~$fatxt{'62a'}

~; fopen( ATM, "$vardir/pm.attachments" ); my @attachmentstxt = ; fclose(ATM); my %att; foreach (@attachmentstxt) { $att{ ( split /\|/xsm, $_ )[3] } = 1; } opendir DIR, $pmuploaddir; my @filesDIR = grep { /\w+$/xsm } readdir DIR; closedir DIR; $yymain .= qq~$fatxt{'61c'}:
~; foreach my $fileinDIR (@filesDIR) { if ( !$att{$fileinDIR} && $fileinDIR ne 'index.html' && $fileinDIR ne '.htaccess' ) { unlink "$pmuploaddir/$fileinDIR"; $yymain .= qq~
$fatxt{'61b'}: $fileinDIR~; } } $yymain .= qq~

$fatxt{'61a'}~; $yytitle = $fatxt{'61c'}; $action_area = 'manageattachments'; AdminTemplate(); return; } 1;