#!/usr/bin/perl #------------------------------------------------------------------------------ # mwForum - Web-based discussion forum # Copyright (c) 1999-2015 Markus Wichitill # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. #------------------------------------------------------------------------------ use strict; use warnings; no warnings qw(uninitialized redefine); # Imports use TyfMain; #------------------------------------------------------------------------------ # Init my ( $m, $cfg, $lng, $user, $userId ) = TyfMain->new( $_[0] ); # Get CGI parameters $m->{ajax} = $m->paramBool('ajax'); my $action = $m->paramStrId('act'); my $postId = $m->paramInt('pid'); my $attachId = $m->paramInt('aid'); my $caption = $m->paramStr('caption'); my $embed = $m->paramBool('embed'); my $change = $m->paramBool('change'); my $delete = $m->paramBool('delete'); my $submitted = $m->paramBool('subm'); $postId or $m->error('errParamMiss') if $action eq 'upload'; $attachId or $m->error('errParamMiss') if $change || $delete; # Get attachment my $attach = undef; if ($attachId) { $attach = $m->fetchHash( " SELECT * FROM attachments WHERE id = ?", $attachId ); $attach or $m->error('errAttNotFnd'); $postId = $attach->{postId}; } # Get post my $post = $m->fetchHash( " SELECT * FROM posts WHERE id = ?", $postId ); $post or $m->error('errPstNotFnd'); my $postIdMod = $postId % 100; my $boardId = $post->{boardId}; my $topicId = $post->{topicId}; # Get board my $board = $m->fetchHash( " SELECT * FROM boards WHERE id = ?", $boardId ); # Check if user can see and write to board my $boardAdmin = $user->{admin} || $m->boardAdmin( $userId, $boardId ) || $board->{topicAdmins} && $m->topicAdmin( $userId, $topicId ); $boardAdmin || $m->boardVisible($board) or $m->error('errNoAccess'); $boardAdmin || $m->boardWritable( $board, 1 ) or $m->error('errNoAccess'); # Check if user owns post or is moderator $userId && $userId == $post->{userId} || $boardAdmin or $m->error('errNoAccess'); # Check if attachments are enabled $cfg->{attachments} && ( $board->{attach} == 1 || $board->{attach} == 2 && $boardAdmin ) or $m->error('errNoAccess'); # Check if topic or post is locked !$m->fetchArray( " SELECT locked FROM topics WHERE id = ?", $topicId ) || $boardAdmin or $m->error('errTpcLocked'); !$post->{locked} || $boardAdmin or $m->error('errPstLocked'); # Check authorization $m->checkAuthz( $user, 'attach' ); # Disable embed if not allowed $embed = 0 if !$cfg->{attachImg}; # Process form if ($submitted) { # Check request source authentication $m->checkSourceAuth() or $m->formError('errSrcAuth'); # Process upload form if ( $action eq 'upload' ) { # Get upload, check filename and size my ( $upload, $fileName, $fileSize ) = $m->getUpload('file'); length($fileName) or $m->error('errAttName'); $fileSize or $m->error('errAttSize'); # Make sure filenames don't clash my ( $name, $ext ) = $fileName =~ /(.+?)(\.[^.]+)?\z/; $name = substr( $name, 0, $cfg->{attachNameLen} || 40 ); $ext = substr( $ext, 0, $cfg->{attachNameLen} || 40 ); my $webImage = $ext =~ /^\.(?:jpg|png|gif)\z/i ? 1 : 0; my $like = $m->{pgsql} ? 'ILIKE' : 'LIKE'; my $num = ""; for my $i ( 0 .. 100 ) { $num = $i ? "-$i" : ""; my $nameExists = $m->fetchArray( " SELECT 1 FROM attachments WHERE postId = ? AND LOWER(fileName) $like LOWER(?)", $postId, $webImage ? "$name$num%" : "$name$num$ext" ); last if !$nameExists; $i < 100 or $m->formError("Too many filename collisions."); } $fileName = "$name$num$ext"; # If there's no error, finish action if ( !@{ $m->{formErrors} } ) { # Create directories and attachment file my $path = "$cfg->{attachFsPath}/$postIdMod"; $m->createDirectories( $path, $postId ); $m->saveUpload( 'file', $upload, "$path/$postId/" . $m->encFsPath($fileName) ); # Add attachments table entry $webImage = 2 if $webImage && $embed; $caption = substr( $caption, 0, 100 ); my $captionEsc = $m->escHtml($caption); $m->dbDo( " INSERT INTO attachments (postId, webImage, fileName, caption) VALUES (?, ?, ?, ?)", $postId, $webImage, $fileName, $captionEsc ); $attachId = $m->dbInsertId("attachments"); # Resize image $m->resizeAttachment($attachId) if $webImage && $cfg->{attachImgRsz}; # Log action and finish $m->logAction( 1, 'post', 'attach', $userId, $boardId, $topicId, $postId, $attachId ); if ( $m->{ajax} ) { $m->printHttpHeader(); print $m->json( { ok => 1 } ); } else { $m->redirect( 'post_attach', pid => $postId, msg => 'PstAttach' ); } } } # Process delete all action elsif ( $action eq 'delAll' ) { # If there's no error, finish action if ( !@{ $m->{formErrors} } ) { # Delete all attachments my $attachments = $m->fetchAllArray( " SELECT id FROM attachments WHERE postId = ?", $postId ); for my $attachment (@$attachments) { $m->deleteAttachment( $attachment->[0] ); } # Log action and finish $m->logAction( 1, 'post', 'attdlall', $userId, $boardId, $topicId, $postId ); $m->redirect( 'post_attach', pid => $postId, msg => 'PstDetach' ); } } # Process delete form action elsif ($delete) { # If there's no error, finish action if ( !@{ $m->{formErrors} } ) { # Delete attachment $m->deleteAttachment($attachId); # Log action and finish $m->logAction( 1, 'post', 'detach', $userId, $boardId, $topicId, $postId, $attachId ); $m->redirect( 'post_attach', pid => $postId, msg => 'PstDetach' ); } } # Process change form action elsif ($change) { # If there's no error, finish action if ( !@{ $m->{formErrors} } ) { # Update attachment my $webImage = $attach->{fileName} =~ /\.(?:jpg|png|gif)\z/i ? 1 : 0; $webImage = 2 if $webImage && $embed; $caption = substr( $caption, 0, 100 ); my $captionEsc = $m->escHtml($caption); $m->dbDo( " UPDATE attachments SET webImage = ?, caption = ? WHERE id = ?", $webImage, $captionEsc, $attachId ); # Delete thumbnail if ( $webImage && !$embed ) { my $file = "$cfg->{attachFsPath}/$postIdMod/$postId/$attach->{fileName}"; $file =~ s!\.(?:jpg|png|gif)\z!.thb.jpg!i; unlink $file; } # Log action and finish $m->logAction( 1, 'post', 'attchg', $userId, $boardId, $topicId, $postId, $attachId ); $m->redirect( 'post_attach', pid => $postId, msg => 'PstAttChg' ); } } else { $m->error('errParamMiss') } } # Print AJAX errors or form if ( $m->{ajax} && @{ $m->{formErrors} } ) { $m->printHttpHeader(); print $m->json( { error => $m->{formErrors}[0] } ); } elsif ( !$submitted || @{ $m->{formErrors} } ) { # Print header $m->printHeader( undef, { postId => $postId, maxAttachLen => $cfg->{maxAttachLen} } ); # Get existing attachments my $attachments = $m->fetchAllHash( " SELECT * FROM attachments WHERE postId = ? ORDER BY id", $postId ); # Print page bar my @navLinks = ( { url => $m->url( 'topic_show', pid => $postId ), txt => 'comUp', ico => 'up' } ); my @userLinks = (); push @userLinks, { url => $m->url( 'user_confirm', script => 'post_attach', pid => $postId, act => 'delAll' ), txt => 'attDelAll', ico => 'delete' } if @$attachments; $m->printPageBar( mainTitle => $lng->{attTitle}, navLinks => \@navLinks, userLinks => \@userLinks ); # Print hints and form errors if ( $m->paramDefined('msg') ) { $m->printHints( ['attGoPostT'] ) } else { $m->printHints( [ $lng->{attDropNote} ], 'dropNote', 1 ) } $m->printFormErrors(); # Prepare values my $sizeStr = $m->formatSize( $cfg->{maxAttachLen} ); my $label = $m->formatStr( $lng->{attUplFiles}, { size => $sizeStr } ); my $embedChk = $cfg->{attachImgDef} ? 'checked' : ""; # Print attachment form print "
\n\n"; # Print existing attachments for my $attach (@$attachments) { my $fileName = $attach->{fileName}; print "\n\n"; } # Log action and finish $m->logAction( 3, 'post', 'attach', $userId, $boardId, $topicId, $postId ); $m->printFooter(); } $m->finish();