2011-06-15 19:11:52 +02:00
< ? php
2012-04-11 18:49:22 +02:00
/*
2014-04-12 20:12:42 +02:00
* Copyright ( c ) 2010 - 2014 Tinyboard Development Group
2012-04-11 18:49:22 +02:00
*/
2021-02-13 19:24:03 +01:00
require_once 'inc/bootstrap.php' ;
2012-04-11 18:49:22 +02:00
2024-04-09 11:20:04 +02:00
use Vichan\ { Context , WebDependencyFactory };
use Vichan\Driver\ { HttpDriver , Log };
2024-04-01 19:36:03 +02:00
use Vichan\Service\ { RemoteCaptchaQuery , NativeCaptchaQuery };
2024-04-30 12:32:40 +02:00
use Vichan\Functions\Format ;
2024-04-01 19:36:03 +02:00
2024-01-16 19:01:13 +01:00
/**
* Utility functions
*/
/**
* Get the md5 hash of the file .
*
* @ param array $config instance configuration .
* @ param string $file file to the the md5 of .
* @ return string | false
*/
function md5_hash_of_file ( $config , $path ) {
$cmd = false ;
if ( $config [ 'bsd_md5' ]) {
$cmd = '/sbin/md5 -r' ;
}
if ( $config [ 'gnu_md5' ]) {
$cmd = 'md5sum' ;
}
if ( $cmd ) {
$output = shell_exec_error ( $cmd . " " . escapeshellarg ( $path ));
$output = explode ( ' ' , $output );
return $output [ 0 ];
} else {
return md5_file ( $path );
}
}
2024-01-30 14:26:19 +01:00
/**
* Strip the symbols incompatible with the current database version .
*
* @ param string @ input The input string .
* @ return string The value stripped of incompatible symbols .
*/
function strip_symbols ( $input ) {
if ( mysql_version () >= 50503 ) {
return $input ; // Assume we're using the utf8mb4 charset
} else {
// MySQL's `utf8` charset only supports up to 3-byte symbols
// Remove anything >= 0x010000
$chars = preg_split ( '//u' , $input , - 1 , PREG_SPLIT_NO_EMPTY );
$ret = '' ;
foreach ( $chars as $char ) {
$o = 0 ;
$ord = ordutf8 ( $char , $o );
if ( $ord >= 0x010000 ) {
continue ;
}
$ret .= $char ;
}
return $ret ;
}
}
2024-01-29 14:53:18 +01:00
/**
* Download a remote file from the given url .
* The file is deleted at shutdown .
*
2024-04-01 19:36:03 +02:00
* @ param HttpDriver $http The http client .
2024-01-29 14:53:18 +01:00
* @ param string $file_url The url to download the file from .
* @ param int $request_timeout Timeout to retrieve the file .
* @ param array $extra_extensions Allowed file extensions .
* @ param string $tmp_dir Temporary directory to save the file into .
* @ param array $error_array An array with error codes , used to create exceptions on failure .
2024-04-01 19:36:03 +02:00
* @ return array | false Returns an array describing the file on success , or false if the file was too large
* @ throws InvalidArgumentException | RuntimeException Throws on invalid arguments and IO errors .
2024-01-29 14:53:18 +01:00
*/
2024-04-01 19:36:03 +02:00
function download_file_from_url ( HttpDriver $http , $file_url , $request_timeout , $allowed_extensions , $tmp_dir , & $error_array ) {
2024-01-29 14:53:18 +01:00
if ( ! preg_match ( '@^https?://@' , $file_url )) {
throw new InvalidArgumentException ( $error_array [ 'invalidimg' ]);
}
2024-04-01 19:36:03 +02:00
$param_idx = mb_strpos ( $file_url , '?' );
if ( $param_idx !== false ) {
$url_without_params = mb_substr ( $file_url , 0 , $param_idx );
2024-01-29 14:53:18 +01:00
} else {
$url_without_params = $file_url ;
}
$extension = strtolower ( mb_substr ( $url_without_params , mb_strrpos ( $url_without_params , '.' ) + 1 ));
if ( ! in_array ( $extension , $allowed_extensions )) {
throw new InvalidArgumentException ( $error_array [ 'unknownext' ]);
}
$tmp_file = tempnam ( $tmp_dir , 'url' );
function unlink_tmp_file ( $file ) {
@ unlink ( $file );
fatal_error_handler ();
}
register_shutdown_function ( 'unlink_tmp_file' , $tmp_file );
$fd = fopen ( $tmp_file , 'w' );
2024-04-01 19:36:03 +02:00
try {
$success = $http -> requestGetInto ( $url_without_params , null , $fd , $request_timeout );
if ( ! $success ) {
return false ;
}
} finally {
fclose ( $fd );
2024-01-29 14:53:18 +01:00
}
return array (
'name' => basename ( $url_without_params ),
'tmp_name' => $tmp_file ,
'file_tmp' => true ,
'error' => 0 ,
'size' => filesize ( $tmp_file )
);
}
2024-03-08 10:24:02 +01:00
/**
* Try extract text from the given image .
*
* @ param array $config Instance configuration .
* @ param string $img_path The file path to the image .
* @ return string | false Returns a string with the extracted text on success ( if any ) .
* @ throws RuntimeException Throws if executing tesseract fails .
*/
function ocr_image ( array $config , string $img_path ) : string {
// The default preprocess command is an ImageMagick b/w quantization.
$ret = shell_exec_error (
sprintf ( $config [ 'tesseract_preprocess_command' ], escapeshellarg ( $img_path ))
. ' | tesseract stdin stdout 2>/dev/null'
. $config [ 'tesseract_params' ]
);
if ( $ret === false ) {
throw new RuntimeException ( 'Unable to run tesseract' );
}
return trim ( $ret );
}
2024-04-01 19:36:03 +02:00
2024-03-21 16:02:09 +01:00
/**
* Trim an image ' s EXIF metadata
*
* @ param string $img_path The file path to the image .
* @ return int The size of the stripped file .
* @ throws RuntimeException Throws on IO errors .
*/
function strip_image_metadata ( string $img_path ) : int {
2024-04-03 19:51:58 +02:00
$err = shell_exec_error ( 'exiftool -overwrite_original -ignoreMinorErrors -q -q -all= -Orientation ' . escapeshellarg ( $img_path ));
2024-03-21 16:02:09 +01:00
if ( $err === false ) {
throw new RuntimeException ( 'Could not strip EXIF metadata!' );
}
clearstatcache ( true , $img_path );
$ret = filesize ( $img_path );
if ( $ret === false ) {
throw new RuntimeException ( 'Could not calculate file size!' );
}
return $ret ;
}
2024-08-09 09:39:08 +02:00
/**
* Delete posts in a cyclical thread .
*
* @ param string $boardUri The URI of the board .
* @ param int $threadId The ID of the thread .
* @ param int $cycleLimit The number of most recent posts to retain .
*/
function delete_cyclical_posts ( string $boardUri , int $threadId , int $cycleLimit ) : void
{
$query = prepare ( sprintf ( '
SELECT p . `id`
FROM `` posts_ % s `` p
LEFT JOIN (
SELECT `id`
FROM `` posts_ % s ``
WHERE `thread` = : thread
ORDER BY `id` DESC
LIMIT : limit
) recent_posts ON p . id = recent_posts . id
WHERE p . thread = : thread
AND recent_posts . id IS NULL ' ,
$boardUri , $boardUri
));
$query -> bindValue ( ':thread' , $threadId , PDO :: PARAM_INT );
$query -> bindValue ( ':limit' , $cycleLimit , PDO :: PARAM_INT );
$query -> execute () or error ( db_error ( $query ));
$ids = $query -> fetchAll ( PDO :: FETCH_COLUMN );
foreach ( $ids as $id ) {
deletePost ( $id , false );
}
}
2024-01-16 19:01:13 +01:00
/**
* Method handling functions
*/
2016-08-14 16:24:17 +02:00
$dropped_post = false ;
2024-08-11 00:56:16 +02:00
$context = Vichan\build_context ( $config );
2016-08-14 16:24:17 +02:00
2016-08-15 00:56:06 +02:00
// Is it a post coming from NNTP? Let's extract it and pretend it's a normal post.
2016-08-14 16:24:17 +02:00
if ( isset ( $_GET [ 'Newsgroups' ]) && $config [ 'nntpchan' ][ 'enabled' ]) {
2016-08-15 00:56:06 +02:00
if ( $_SERVER [ 'REMOTE_ADDR' ] != $config [ 'nntpchan' ][ 'trusted_peer' ]) {
error ( " NNTPChan: Forbidden. $_SERVER[REMOTE_ADDR] is not a trusted peer " );
}
2016-08-14 16:24:17 +02:00
$_POST = array ();
$_POST [ 'json_response' ] = true ;
$headers = json_encode ( $_GET );
if ( ! isset ( $_GET [ 'Message-Id' ])) {
if ( ! isset ( $_GET [ 'Message-ID' ])) {
error ( " NNTPChan: No message ID " );
}
else $msgid = $_GET [ 'Message-ID' ];
}
else $msgid = $_GET [ 'Message-Id' ];
$groups = preg_split ( " /, \ s*/ " , $_GET [ 'Newsgroups' ]);
if ( count ( $groups ) != 1 ) {
error ( " NNTPChan: Messages can go to only one newsgroup " );
}
$group = $groups [ 0 ];
if ( ! isset ( $config [ 'nntpchan' ][ 'dispatch' ][ $group ])) {
error ( " NNTPChan: We don't synchronize $group " );
}
$xboard = $config [ 'nntpchan' ][ 'dispatch' ][ $group ];
$ref = null ;
if ( isset ( $_GET [ 'References' ])) {
$refs = preg_split ( " /, \ s*/ " , $_GET [ 'References' ]);
if ( count ( $refs ) > 1 ) {
error ( " NNTPChan: We don't support multiple references " );
}
$ref = $refs [ 0 ];
$query = prepare ( " SELECT `board`,`id` FROM ``nntp_references`` WHERE `message_id` = :ref " );
2024-01-29 22:57:45 +01:00
$query -> bindValue ( ':ref' , $ref );
$query -> execute () or error ( db_error ( $query ));
2016-08-14 16:24:17 +02:00
$ary = $query -> fetchAll ( PDO :: FETCH_ASSOC );
if ( count ( $ary ) == 0 ) {
error ( " NNTPChan: We don't have $ref that $msgid references " );
}
$p_id = $ary [ 0 ][ 'id' ];
$p_board = $ary [ 0 ][ 'board' ];
if ( $p_board != $xboard ) {
error ( " NNTPChan: Cross board references not allowed. Tried to reference $p_board on $xboard " );
}
$_POST [ 'thread' ] = $p_id ;
}
$date = isset ( $_GET [ 'Date' ]) ? strtotime ( $_GET [ 'Date' ]) : time ();
list ( $ct ) = explode ( '; ' , $_GET [ 'Content-Type' ]);
$query = prepare ( " SELECT COUNT(*) AS `c` FROM ``nntp_references`` WHERE `message_id` = :msgid " );
$query -> bindValue ( " :msgid " , $msgid );
$query -> execute () or error ( db_error ( $query ));
$a = $query -> fetch ( PDO :: FETCH_ASSOC );
if ( $a [ 'c' ] > 0 ) {
error ( " NNTPChan: We already have this post. Post discarded. " );
}
if ( $ct == 'text/plain' ) {
$content = file_get_contents ( " php://input " );
}
elseif ( $ct == 'multipart/mixed' || $ct == 'multipart/form-data' ) {
2024-08-11 00:56:16 +02:00
$context -> get ( Log :: class ) -> log ( Log :: DEBUG , 'MM: Files: ' . print_r ( $GLOBALS , true ));
2016-08-19 23:15:08 +02:00
2016-08-14 16:24:17 +02:00
$content = '' ;
2016-08-19 23:15:08 +02:00
$newfiles = array ();
foreach ( $_FILES [ 'attachment' ][ 'error' ] as $id => $error ) {
if ( $_FILES [ 'attachment' ][ 'type' ][ $id ] == 'text/plain' ) {
$content .= file_get_contents ( $_FILES [ 'attachment' ][ 'tmp_name' ][ $id ]);
2016-08-14 16:24:17 +02:00
}
2016-08-19 23:15:08 +02:00
elseif ( $_FILES [ 'attachment' ][ 'type' ][ $id ] == 'message/rfc822' ) { // Signed message, ignore for now
2016-08-14 16:24:17 +02:00
}
else { // A real attachment :^)
2016-08-19 23:15:08 +02:00
$file = array ();
$file [ 'name' ] = $_FILES [ 'attachment' ][ 'name' ][ $id ];
$file [ 'type' ] = $_FILES [ 'attachment' ][ 'type' ][ $id ];
$file [ 'size' ] = $_FILES [ 'attachment' ][ 'size' ][ $id ];
$file [ 'tmp_name' ] = $_FILES [ 'attachment' ][ 'tmp_name' ][ $id ];
$file [ 'error' ] = $_FILES [ 'attachment' ][ 'error' ][ $id ];
$newfiles [ " file $id " ] = $file ;
2016-08-14 16:24:17 +02:00
}
}
2016-08-19 23:15:08 +02:00
$_FILES = $newfiles ;
2016-08-14 16:24:17 +02:00
}
else {
error ( " NNTPChan: Wrong mime type: $ct " );
}
2016-08-19 23:15:08 +02:00
$_POST [ 'subject' ] = isset ( $_GET [ 'Subject' ]) ? ( $_GET [ 'Subject' ] == 'None' ? '' : $_GET [ 'Subject' ]) : '' ;
2016-08-14 16:24:17 +02:00
$_POST [ 'board' ] = $xboard ;
if ( isset ( $_GET [ 'From' ])) {
list ( $name , $mail ) = explode ( " < " , $_GET [ 'From' ], 2 );
$mail = preg_replace ( '/>\s+$/' , '' , $mail );
$_POST [ 'name' ] = $name ;
//$_POST['email'] = $mail;
$_POST [ 'email' ] = '' ;
}
if ( isset ( $_GET [ 'X_Sage' ])) {
$_POST [ 'email' ] = 'sage' ;
}
$content = preg_replace_callback ( '/>>([0-9a-fA-F]{6,})/' , function ( $id ) use ( $xboard ) {
$id = $id [ 1 ];
$query = prepare ( " SELECT `board`,`id` FROM ``nntp_references`` WHERE `message_id_digest` LIKE :rule " );
$idx = $id . " % " ;
2024-01-29 22:57:45 +01:00
$query -> bindValue ( ':rule' , $idx );
$query -> execute () or error ( db_error ( $query ));
2016-08-14 16:24:17 +02:00
$ary = $query -> fetchAll ( PDO :: FETCH_ASSOC );
if ( count ( $ary ) == 0 ) {
return " >>>> $id " ;
}
else {
$ret = array ();
foreach ( $ary as $v ) {
if ( $v [ 'board' ] != $xboard ) {
$ret [] = " >>>/ " . $v [ 'board' ] . " / " . $v [ 'id' ];
}
else {
$ret [] = " >> " . $v [ 'id' ];
}
}
2024-04-04 11:07:15 +02:00
return implode ( " , " , $ret );
2016-08-14 16:24:17 +02:00
}
}, $content );
$_POST [ 'body' ] = $content ;
$dropped_post = array (
'date' => $date ,
'board' => $xboard ,
'msgid' => $msgid ,
'headers' => $headers ,
'from_nntp' => true ,
);
}
elseif ( isset ( $_GET [ 'Newsgroups' ])) {
error ( " NNTPChan: NNTPChan support is disabled " );
}
2021-11-13 01:24:34 +01:00
session_start ();
if ( ! isset ( $_POST [ 'captcha_cookie' ]) && isset ( $_SESSION [ 'captcha_cookie' ])) {
$_POST [ 'captcha_cookie' ] = $_SESSION [ 'captcha_cookie' ];
}
2012-04-12 16:18:19 +02:00
if ( isset ( $_POST [ 'delete' ])) {
2012-04-11 18:49:22 +02:00
// Delete
2024-01-29 22:57:45 +01:00
2012-04-12 16:18:19 +02:00
if ( ! isset ( $_POST [ 'board' ], $_POST [ 'password' ]))
2012-04-11 18:49:22 +02:00
error ( $config [ 'error' ][ 'bot' ]);
2024-01-29 22:57:45 +01:00
2024-06-20 15:11:47 +02:00
if ( empty ( $_POST [ 'password' ])){
2012-04-11 18:49:22 +02:00
error ( $config [ 'error' ][ 'invalidpassword' ]);
2024-06-20 15:11:47 +02:00
}
$password = hashPassword ( $_POST [ 'password' ]);
2024-01-29 22:57:45 +01:00
2012-04-11 18:49:22 +02:00
$delete = array ();
2012-04-12 16:18:19 +02:00
foreach ( $_POST as $post => $value ) {
if ( preg_match ( '/^delete_(\d+)$/' , $post , $m )) {
2012-04-11 18:49:22 +02:00
$delete [] = ( int ) $m [ 1 ];
2011-01-21 03:14:11 +01:00
}
2012-04-11 18:49:22 +02:00
}
2024-01-29 22:57:45 +01:00
2012-04-11 18:49:22 +02:00
checkDNSBL ();
2024-01-29 22:57:45 +01:00
2012-04-11 18:49:22 +02:00
// Check if board exists
2012-04-12 16:18:19 +02:00
if ( ! openBoard ( $_POST [ 'board' ]))
2012-04-11 18:49:22 +02:00
error ( $config [ 'error' ][ 'noboard' ]);
2019-03-09 04:54:18 +01:00
if (( ! isset ( $_POST [ 'mod' ]) || ! $_POST [ 'mod' ]) && $config [ 'board_locked' ]) {
2024-01-29 22:57:45 +01:00
error ( " Board is locked " );
2019-03-09 04:54:18 +01:00
}
2024-01-29 22:57:45 +01:00
2012-04-11 18:49:22 +02:00
// Check if banned
checkBan ( $board [ 'uri' ]);
2014-05-06 00:03:51 +02:00
// Check if deletion enabled
if ( ! $config [ 'allow_delete' ])
error ( _ ( 'Post deletion is not allowed!' ));
2024-01-29 22:57:45 +01:00
2012-04-12 16:18:19 +02:00
if ( empty ( $delete ))
2012-04-11 18:49:22 +02:00
error ( $config [ 'error' ][ 'nodelete' ]);
2024-01-29 22:57:45 +01:00
2012-04-12 16:18:19 +02:00
foreach ( $delete as & $id ) {
2017-05-17 20:03:48 +02:00
$query = prepare ( sprintf ( " SELECT `id`,`thread`,`time`,`password` FROM ``posts_%s`` WHERE `id` = :id " , $board [ 'uri' ]));
2012-04-11 18:49:22 +02:00
$query -> bindValue ( ':id' , $id , PDO :: PARAM_INT );
$query -> execute () or error ( db_error ( $query ));
2024-01-29 22:57:45 +01:00
2013-08-01 02:51:43 +02:00
if ( $post = $query -> fetch ( PDO :: FETCH_ASSOC )) {
2014-05-06 21:53:05 +02:00
$thread = false ;
if ( $config [ 'user_moderation' ] && $post [ 'thread' ]) {
$thread_query = prepare ( sprintf ( " SELECT `time`,`password` FROM ``posts_%s`` WHERE `id` = :id " , $board [ 'uri' ]));
$thread_query -> bindValue ( ':id' , $post [ 'thread' ], PDO :: PARAM_INT );
$thread_query -> execute () or error ( db_error ( $query ));
2024-01-29 22:57:45 +01:00
$thread = $thread_query -> fetch ( PDO :: FETCH_ASSOC );
2014-05-06 21:53:05 +02:00
}
2023-11-01 06:03:09 +01:00
if ( $post [ 'time' ] < time () - $config [ 'max_delete_time' ] && $config [ 'max_delete_time' ] != false ) {
2024-04-30 12:32:40 +02:00
error ( sprintf ( $config [ 'error' ][ 'delete_too_late' ], Format\until ( $post [ 'time' ] + $config [ 'max_delete_time' ])));
2023-11-01 06:03:09 +01:00
}
2024-01-29 22:57:45 +01:00
2024-06-20 15:11:47 +02:00
if ( ! hash_equals ( $post [ 'password' ], $password ) && ( ! $thread || ! hash_equals ( $thread [ 'password' ], $password ))) {
2012-04-11 18:49:22 +02:00
error ( $config [ 'error' ][ 'invalidpassword' ]);
2024-06-20 15:11:47 +02:00
}
2024-01-29 22:57:45 +01:00
2024-06-20 15:11:47 +02:00
if ( $post [ 'time' ] > time () - $config [ 'delete_time' ] && ( ! $thread || ! hash_equals ( $thread [ 'password' ], $password ))) {
2024-04-30 12:32:40 +02:00
error ( sprintf ( $config [ 'error' ][ 'delete_too_soon' ], Format\until ( $post [ 'time' ] + $config [ 'delete_time' ])));
2012-04-11 18:49:22 +02:00
}
2024-01-29 22:57:45 +01:00
2024-03-15 10:48:56 +01:00
$ip = $_SERVER [ 'REMOTE_ADDR' ];
2012-04-12 16:18:19 +02:00
if ( isset ( $_POST [ 'file' ])) {
2012-04-11 18:49:22 +02:00
// Delete just the file
deleteFile ( $id );
2024-03-15 21:22:45 +01:00
modLog ( " User at $ip deleted file from their own post # $id " );
2012-04-11 18:49:22 +02:00
} else {
// Delete entire post
deletePost ( $id );
2024-03-15 10:48:56 +01:00
modLog ( " User at $ip deleted their own post # $id " );
2011-01-21 03:14:11 +01:00
}
2017-05-17 20:03:48 +02:00
2024-08-11 00:56:16 +02:00
$context -> get ( Log :: class ) -> log (
2024-04-03 23:51:02 +02:00
Log :: INFO ,
'Deleted post: /' . $board [ 'dir' ] . $config [ 'dir' ][ 'res' ] . link_for ( $post ) . ( $post [ 'thread' ] ? '#' . $id : '' )
2012-04-11 18:49:22 +02:00
);
2011-01-21 03:14:11 +01:00
}
2012-04-11 18:49:22 +02:00
}
2024-01-29 22:57:45 +01:00
2012-04-11 18:49:22 +02:00
buildIndex ();
2013-08-26 05:23:02 +02:00
2012-04-11 18:49:22 +02:00
$is_mod = isset ( $_POST [ 'mod' ]) && $_POST [ 'mod' ];
$root = $is_mod ? $config [ 'root' ] . $config [ 'file_mod' ] . '?/' : $config [ 'root' ];
2015-03-31 09:17:22 +02:00
2013-09-15 20:42:13 +02:00
if ( ! isset ( $_POST [ 'json_response' ])) {
header ( 'Location: ' . $root . $board [ 'dir' ] . $config [ 'file_index' ], true , $config [ 'redirect_http' ]);
} else {
header ( 'Content-Type: text/json' );
echo json_encode ( array ( 'success' => true ));
}
2015-03-31 09:17:22 +02:00
2024-01-29 22:57:45 +01:00
// We are already done, let's continue our heavy-lifting work in the background (if we run off FastCGI)
if ( function_exists ( 'fastcgi_finish_request' ))
@ fastcgi_finish_request ();
2015-03-31 09:17:22 +02:00
2024-08-16 18:32:32 +02:00
Vichan\Functions\Theme\rebuild_themes ( 'post-delete' , $board [ 'uri' ]);
2015-03-31 09:17:22 +02:00
2012-04-12 16:18:19 +02:00
} elseif ( isset ( $_POST [ 'report' ])) {
2014-05-06 00:03:51 +02:00
if ( ! isset ( $_POST [ 'board' ], $_POST [ 'reason' ]))
2012-04-11 18:49:22 +02:00
error ( $config [ 'error' ][ 'bot' ]);
2024-01-29 22:57:45 +01:00
2012-04-11 18:49:22 +02:00
$report = array ();
2012-04-12 16:18:19 +02:00
foreach ( $_POST as $post => $value ) {
if ( preg_match ( '/^delete_(\d+)$/' , $post , $m )) {
2012-04-11 18:49:22 +02:00
$report [] = ( int ) $m [ 1 ];
2011-02-20 07:19:57 +01:00
}
2012-04-11 18:49:22 +02:00
}
2024-01-29 22:57:45 +01:00
2012-04-11 18:49:22 +02:00
checkDNSBL ();
2024-01-29 22:57:45 +01:00
2012-04-11 18:49:22 +02:00
// Check if board exists
2012-04-12 16:18:19 +02:00
if ( ! openBoard ( $_POST [ 'board' ]))
2012-04-11 18:49:22 +02:00
error ( $config [ 'error' ][ 'noboard' ]);
2019-03-09 04:54:18 +01:00
if (( ! isset ( $_POST [ 'mod' ]) || ! $_POST [ 'mod' ]) && $config [ 'board_locked' ]) {
2024-01-29 22:57:45 +01:00
error ( " Board is locked " );
2019-03-09 04:54:18 +01:00
}
2024-01-29 22:57:45 +01:00
2012-04-11 18:49:22 +02:00
// Check if banned
checkBan ( $board [ 'uri' ]);
2024-01-29 22:57:45 +01:00
2012-04-12 16:18:19 +02:00
if ( empty ( $report ))
2012-04-11 18:49:22 +02:00
error ( $config [ 'error' ][ 'noreport' ]);
2024-01-29 22:57:45 +01:00
2012-04-12 16:18:19 +02:00
if ( count ( $report ) > $config [ 'report_limit' ])
2012-04-11 18:49:22 +02:00
error ( $config [ 'error' ][ 'toomanyreports' ]);
2015-03-13 05:45:57 +01:00
if ( $config [ 'report_captcha' ]) {
2024-04-01 19:36:03 +02:00
if ( ! isset ( $_POST [ 'captcha_text' ], $_POST [ 'captcha_cookie' ])) {
error ( $config [ 'error' ][ 'bot' ]);
}
try {
$query = new NativeCaptchaQuery (
2024-08-11 00:56:16 +02:00
$context -> get ( HttpDriver :: class ),
2024-04-01 19:36:03 +02:00
$config [ 'domain' ],
$config [ 'captcha' ][ 'provider_check' ]
);
$success = $query -> verify (
$config [ 'captcha' ][ 'extra' ],
$_POST [ 'captcha_text' ],
$_POST [ 'captcha_cookie' ]
);
if ( ! $success ) {
error ( $config [ 'error' ][ 'captcha' ]);
}
} catch ( RuntimeException $e ) {
2024-08-11 00:56:16 +02:00
$context -> get ( Log :: class ) -> log ( Log :: ERROR , " Native captcha IO exception: { $e -> getMessage () } " );
2024-04-01 19:36:03 +02:00
error ( $config [ 'error' ][ 'local_io_error' ]);
2015-03-13 05:45:57 +01:00
}
}
2024-01-29 22:57:45 +01:00
2013-08-01 00:59:54 +02:00
$reason = escape_markup_modifiers ( $_POST [ 'reason' ]);
2012-04-11 18:49:22 +02:00
markup ( $reason );
2017-03-11 14:34:58 +01:00
2024-03-15 16:37:59 +01:00
if ( mb_strlen ( $reason ) > $config [ 'report_max_length' ]) {
2017-02-16 02:07:50 +01:00
error ( $config [ 'error' ][ 'toolongreport' ]);
}
2012-04-12 16:18:19 +02:00
foreach ( $report as & $id ) {
2016-08-12 19:18:54 +02:00
$query = prepare ( sprintf ( " SELECT `id`, `thread` FROM ``posts_%s`` WHERE `id` = :id " , $board [ 'uri' ]));
2012-04-11 18:49:22 +02:00
$query -> bindValue ( ':id' , $id , PDO :: PARAM_INT );
$query -> execute () or error ( db_error ( $query ));
2024-01-29 22:57:45 +01:00
2016-08-12 19:18:54 +02:00
$post = $query -> fetch ( PDO :: FETCH_ASSOC );
2024-04-01 19:20:23 +02:00
if ( $post === false ) {
2024-08-11 00:56:16 +02:00
$context -> get ( Log :: class ) -> log ( Log :: INFO , " Failed to report non-existing post # { $id } in { $board [ 'dir' ] } " );
2024-04-01 19:20:23 +02:00
error ( $config [ 'error' ][ 'nopost' ]);
}
2017-03-11 14:34:58 +01:00
2024-01-29 22:57:45 +01:00
$error = event ( 'report' , array ( 'ip' => $_SERVER [ 'REMOTE_ADDR' ], 'board' => $board [ 'uri' ], 'post' => $post , 'reason' => $reason , 'link' => link_for ( $post )));
if ( $error ) {
error ( $error );
}
2017-03-11 14:34:58 +01:00
2024-08-11 00:56:16 +02:00
$context -> get ( Log :: class ) -> log (
2024-04-03 23:51:02 +02:00
Log :: INFO ,
'Reported post: /'
. $board [ 'dir' ] . $config [ 'dir' ][ 'res' ] . link_for ( $post ) . ( $post [ 'thread' ] ? '#' . $id : '' )
. " for \" $reason\ " "
);
2013-08-11 13:22:25 +02:00
$query = prepare ( " INSERT INTO ``reports`` VALUES (NULL, :time, :ip, :board, :post, :reason) " );
$query -> bindValue ( ':time' , time (), PDO :: PARAM_INT );
$query -> bindValue ( ':ip' , $_SERVER [ 'REMOTE_ADDR' ], PDO :: PARAM_STR );
2018-07-27 13:20:38 +02:00
$query -> bindValue ( ':board' , $board [ 'uri' ], PDO :: PARAM_STR );
2013-08-11 13:22:25 +02:00
$query -> bindValue ( ':post' , $id , PDO :: PARAM_INT );
$query -> bindValue ( ':reason' , $reason , PDO :: PARAM_STR );
$query -> execute () or error ( db_error ( $query ));
2012-04-11 18:49:22 +02:00
}
2024-01-29 22:57:45 +01:00
2012-04-11 18:49:22 +02:00
$is_mod = isset ( $_POST [ 'mod' ]) && $_POST [ 'mod' ];
$root = $is_mod ? $config [ 'root' ] . $config [ 'file_mod' ] . '?/' : $config [ 'root' ];
2024-01-29 22:57:45 +01:00
2013-09-15 20:42:13 +02:00
if ( ! isset ( $_POST [ 'json_response' ])) {
2015-03-13 05:45:57 +01:00
$index = $root . $board [ 'dir' ] . $config [ 'file_index' ];
2022-08-29 16:50:45 +02:00
echo Element ( $config [ 'file_page_template' ], array ( 'config' => $config , 'body' => '<div style="text-align:center"><a href="javascript:window.close()">[ ' . _ ( 'Close window' ) . " ]</a> <a href=' $index '>[ " . _ ( 'Return' ) . ' ]</a></div>' , 'title' => _ ( 'Report submitted!' )));
2013-09-15 20:42:13 +02:00
} else {
header ( 'Content-Type: text/json' );
echo json_encode ( array ( 'success' => true ));
}
2016-08-14 16:24:17 +02:00
} elseif ( isset ( $_POST [ 'post' ]) || $dropped_post ) {
if ( ! isset ( $_POST [ 'body' ], $_POST [ 'board' ]) && ! $dropped_post )
2012-04-11 18:49:22 +02:00
error ( $config [ 'error' ][ 'bot' ]);
2014-04-18 14:32:05 +02:00
2014-04-27 15:48:47 +02:00
$post = array ( 'board' => $_POST [ 'board' ], 'files' => array ());
2014-04-18 14:33:50 +02:00
2014-04-18 14:32:05 +02:00
// Check if board exists
if ( ! openBoard ( $post [ 'board' ]))
error ( $config [ 'error' ][ 'noboard' ]);
2019-03-09 04:54:18 +01:00
if (( ! isset ( $_POST [ 'mod' ]) || ! $_POST [ 'mod' ]) && $config [ 'board_locked' ]) {
2024-01-29 22:57:45 +01:00
error ( " Board is locked " );
2019-03-09 04:54:18 +01:00
}
2024-01-29 22:57:45 +01:00
2012-04-12 16:18:19 +02:00
if ( ! isset ( $_POST [ 'name' ]))
2012-04-11 18:49:22 +02:00
$_POST [ 'name' ] = $config [ 'anonymous' ];
2024-01-29 22:57:45 +01:00
2012-04-12 16:18:19 +02:00
if ( ! isset ( $_POST [ 'email' ]))
2012-04-11 18:49:22 +02:00
$_POST [ 'email' ] = '' ;
2024-01-29 22:57:45 +01:00
2012-08-30 12:07:23 +02:00
if ( ! isset ( $_POST [ 'subject' ]))
$_POST [ 'subject' ] = '' ;
2024-01-29 22:57:45 +01:00
2012-04-12 16:18:19 +02:00
if ( ! isset ( $_POST [ 'password' ]))
2024-01-29 22:57:45 +01:00
$_POST [ 'password' ] = '' ;
2012-04-12 16:18:19 +02:00
if ( isset ( $_POST [ 'thread' ])) {
2012-04-11 18:49:22 +02:00
$post [ 'op' ] = false ;
$post [ 'thread' ] = round ( $_POST [ 'thread' ]);
} else
$post [ 'op' ] = true ;
2014-04-18 14:32:05 +02:00
2016-08-14 16:24:17 +02:00
if ( ! $dropped_post ) {
2024-03-14 14:40:55 +01:00
if ( $config [ 'simple_spam' ] && $post [ 'op' ]) {
2024-03-14 14:10:18 +01:00
if ( ! isset ( $_POST [ 'simple_spam' ]) || strtolower ( $config [ 'simple_spam' ][ 'answer' ]) != strtolower ( $_POST [ 'simple_spam' ])) {
2023-09-10 23:46:38 +02:00
error ( $config [ 'error' ][ 'simple_spam' ]);
}
}
2017-11-06 13:19:35 +01:00
// Check if banned
checkBan ( $board [ 'uri' ]);
2024-04-01 19:36:03 +02:00
// Check for CAPTCHA right after opening the board so the "return" link is in there.
try {
// With our custom captcha provider
if ( $config [ 'captcha' ][ 'enabled' ] || ( $post [ 'op' ] && $config [ 'new_thread_capt' ])) {
2024-08-11 00:56:16 +02:00
$query = new NativeCaptchaQuery ( $context -> get ( HttpDriver :: class ), $config [ 'domain' ], $config [ 'captcha' ][ 'provider_check' ]);
2024-04-01 19:36:03 +02:00
$success = $query -> verify ( $config [ 'captcha' ][ 'extra' ], $_POST [ 'captcha_text' ], $_POST [ 'captcha_cookie' ]);
if ( ! $success ) {
error (
2024-04-01 19:46:04 +02:00
" { $config [ 'error' ][ 'captcha' ] }
< script >
if ( actually_load_captcha !== undefined )
actually_load_captcha (
\ " { $config [ 'captcha' ][ 'provider_get' ] } \" ,
\ " { $config [ 'captcha' ][ 'extra' ] } \"
);
</ script > "
2024-04-01 19:36:03 +02:00
);
}
2023-09-16 10:27:20 +02:00
}
2024-04-01 19:36:03 +02:00
// Remote 3rd party captchas.
else {
// recaptcha
if ( $config [ 'recaptcha' ]) {
if ( ! isset ( $_POST [ 'g-recaptcha-response' ])) {
error ( $config [ 'error' ][ 'bot' ]);
}
$response = $_POST [ 'g-recaptcha-response' ];
2024-08-11 00:56:16 +02:00
$query = RemoteCaptchaQuery :: withRecaptcha ( $context -> get ( HttpDriver :: class ), $config [ 'recaptcha_private' ]);
2024-04-01 19:36:03 +02:00
}
// hCaptcha
elseif ( $config [ 'hcaptcha' ]) {
if ( ! isset ( $_POST [ 'h-captcha-response' ])) {
error ( $config [ 'error' ][ 'bot' ]);
}
2024-04-16 22:39:32 +02:00
$response = $_POST [ 'h-captcha-response' ];
2024-08-11 00:56:16 +02:00
$query = RemoteCaptchaQuery :: withHCaptcha ( $context -> get ( HttpDriver :: class ), $config [ 'hcaptcha_private' ]);
2024-04-01 19:36:03 +02:00
}
2024-03-31 07:46:35 +02:00
2024-04-01 19:36:37 +02:00
if ( isset ( $query , $response )) {
$success = $query -> verify ( $response , $_SERVER [ 'REMOTE_ADDR' ]);
if ( ! $success ) {
error ( $config [ 'error' ][ 'captcha' ]);
}
2024-04-01 19:36:03 +02:00
}
2023-09-16 10:27:20 +02:00
}
2024-04-01 19:36:03 +02:00
} catch ( RuntimeException $e ) {
2024-04-03 23:51:02 +02:00
$context -> getLog () -> log ( Log :: ERROR , " Captcha IO exception: { $e -> getMessage () } " );
2024-04-01 19:36:03 +02:00
error ( $config [ 'error' ][ 'remote_io_error' ]);
} catch ( JsonException $e ) {
2024-04-03 23:51:02 +02:00
$context -> getLog () -> log ( Log :: ERROR , " Bad JSON reply to captcha: { $e -> getMessage () } " );
2024-04-01 19:36:03 +02:00
error ( $config [ 'error' ][ 'remote_io_error' ]);
2023-09-16 10:27:20 +02:00
}
2024-04-01 19:36:03 +02:00
2014-10-06 12:35:37 +02:00
2016-08-14 16:24:17 +02:00
if ( ! (( $post [ 'op' ] && $_POST [ 'post' ] == $config [ 'button_newtopic' ]) ||
( ! $post [ 'op' ] && $_POST [ 'post' ] == $config [ 'button_reply' ])))
error ( $config [ 'error' ][ 'bot' ]);
2024-01-29 22:57:45 +01:00
2016-08-14 16:24:17 +02:00
// Check the referrer
if ( $config [ 'referer_match' ] !== false &&
( ! isset ( $_SERVER [ 'HTTP_REFERER' ]) || ! preg_match ( $config [ 'referer_match' ], rawurldecode ( $_SERVER [ 'HTTP_REFERER' ]))))
error ( $config [ 'error' ][ 'referer' ]);
2024-01-29 22:57:45 +01:00
2016-08-14 16:24:17 +02:00
checkDNSBL ();
2024-01-29 22:57:45 +01:00
2014-10-06 12:35:37 +02:00
2016-08-14 16:24:17 +02:00
if ( $post [ 'mod' ] = isset ( $_POST [ 'mod' ]) && $_POST [ 'mod' ]) {
check_login ( false );
if ( ! $mod ) {
// Liar. You're not a mod.
error ( $config [ 'error' ][ 'notamod' ]);
}
2024-01-29 22:57:45 +01:00
2016-08-14 16:24:17 +02:00
$post [ 'sticky' ] = $post [ 'op' ] && isset ( $_POST [ 'sticky' ]);
$post [ 'locked' ] = $post [ 'op' ] && isset ( $_POST [ 'lock' ]);
$post [ 'raw' ] = isset ( $_POST [ 'raw' ]);
2024-01-29 22:57:45 +01:00
2016-08-14 16:24:17 +02:00
if ( $post [ 'sticky' ] && ! hasPermission ( $config [ 'mod' ][ 'sticky' ], $board [ 'uri' ]))
error ( $config [ 'error' ][ 'noaccess' ]);
if ( $post [ 'locked' ] && ! hasPermission ( $config [ 'mod' ][ 'lock' ], $board [ 'uri' ]))
error ( $config [ 'error' ][ 'noaccess' ]);
if ( $post [ 'raw' ] && ! hasPermission ( $config [ 'mod' ][ 'rawhtml' ], $board [ 'uri' ]))
error ( $config [ 'error' ][ 'noaccess' ]);
}
2024-01-29 22:57:45 +01:00
2016-08-14 16:24:17 +02:00
if ( ! $post [ 'mod' ]) {
$post [ 'antispam_hash' ] = checkSpam ( array ( $board [ 'uri' ], isset ( $post [ 'thread' ]) ? $post [ 'thread' ] : ( $config [ 'try_smarter' ] && isset ( $_POST [ 'page' ]) ? 0 - ( int ) $_POST [ 'page' ] : null )));
if ( $post [ 'antispam_hash' ] === true )
error ( $config [ 'error' ][ 'spam' ]);
}
2024-01-29 22:57:45 +01:00
2016-08-14 16:24:17 +02:00
if ( $config [ 'robot_enable' ] && $config [ 'robot_mute' ]) {
checkMute ();
}
2012-05-07 15:51:15 +02:00
}
2016-08-14 16:24:17 +02:00
else {
$mod = $post [ 'mod' ] = false ;
2012-04-11 18:49:22 +02:00
}
2024-01-29 22:57:45 +01:00
2012-04-11 18:49:22 +02:00
//Check if thread exists
2012-04-12 16:18:19 +02:00
if ( ! $post [ 'op' ]) {
2015-04-03 08:56:28 +02:00
$query = prepare ( sprintf ( " SELECT `sticky`,`locked`,`cycle`,`sage`,`slug` FROM ``posts_%s`` WHERE `id` = :id AND `thread` IS NULL LIMIT 1 " , $board [ 'uri' ]));
2012-04-11 18:49:22 +02:00
$query -> bindValue ( ':id' , $post [ 'thread' ], PDO :: PARAM_INT );
$query -> execute () or error ( db_error ());
2024-01-29 22:57:45 +01:00
2013-08-01 02:51:43 +02:00
if ( ! $thread = $query -> fetch ( PDO :: FETCH_ASSOC )) {
2012-04-11 18:49:22 +02:00
// Non-existant
error ( $config [ 'error' ][ 'nonexistant' ]);
2011-10-10 17:58:22 +02:00
}
2012-04-11 18:49:22 +02:00
}
2015-03-10 12:48:59 +01:00
else {
$thread = false ;
}
2024-01-29 22:57:45 +01:00
2012-04-11 18:49:22 +02:00
// Check for an embed field
2012-04-12 16:18:19 +02:00
if ( $config [ 'enable_embedding' ] && isset ( $_POST [ 'embed' ]) && ! empty ( $_POST [ 'embed' ])) {
2012-04-11 18:49:22 +02:00
// yep; validate it
$value = $_POST [ 'embed' ];
2012-04-12 16:18:19 +02:00
foreach ( $config [ 'embedding' ] as & $embed ) {
2013-07-20 00:36:12 +02:00
if ( preg_match ( $embed [ 0 ], $value )) {
// Valid link
$post [ 'embed' ] = $value ;
// This is bad, lol.
2012-04-11 18:49:22 +02:00
$post [ 'no_longer_require_an_image_for_op' ] = true ;
break ;
2011-05-18 09:05:48 +02:00
}
2010-11-05 17:46:20 +01:00
}
2012-04-12 16:18:19 +02:00
if ( ! isset ( $post [ 'embed' ])) {
2012-04-11 18:49:22 +02:00
error ( $config [ 'error' ][ 'invalid_embed' ]);
2011-02-22 01:09:43 +01:00
}
2012-04-11 18:49:22 +02:00
}
2024-01-29 22:57:45 +01:00
2012-04-12 16:18:19 +02:00
if ( ! hasPermission ( $config [ 'mod' ][ 'bypass_field_disable' ], $board [ 'uri' ])) {
if ( $config [ 'field_disable_name' ])
2012-04-11 18:49:22 +02:00
$_POST [ 'name' ] = $config [ 'anonymous' ]; // "forced anonymous"
2024-01-29 22:57:45 +01:00
2012-04-12 16:18:19 +02:00
if ( $config [ 'field_disable_email' ])
2012-04-11 18:49:22 +02:00
$_POST [ 'email' ] = '' ;
2024-01-29 22:57:45 +01:00
2012-04-12 16:18:19 +02:00
if ( $config [ 'field_disable_password' ])
2012-04-11 18:49:22 +02:00
$_POST [ 'password' ] = '' ;
2024-01-29 22:57:45 +01:00
2012-08-30 12:07:23 +02:00
if ( $config [ 'field_disable_subject' ] || ( ! $post [ 'op' ] && $config [ 'field_disable_reply_subject' ]))
$_POST [ 'subject' ] = '' ;
2012-04-11 18:49:22 +02:00
}
2024-01-29 22:57:45 +01:00
2013-08-12 14:51:46 +02:00
if ( $config [ 'allow_upload_by_url' ] && isset ( $_POST [ 'file_url' ]) && ! empty ( $_POST [ 'file_url' ])) {
2024-01-29 14:53:18 +01:00
$allowed_extensions = $config [ 'allowed_ext_files' ];
2015-04-22 05:54:48 +02:00
2024-01-29 14:53:18 +01:00
// Add allowed extensions for OP, if enabled.
2015-04-22 05:54:48 +02:00
if ( $post [ 'op' ] && $config [ 'allowed_ext_op' ]) {
2024-01-29 14:53:18 +01:00
array_merge ( $allowed_extensions , $config [ 'allowed_ext_op' ]);
2015-04-22 05:54:48 +02:00
}
2013-08-12 14:51:46 +02:00
2024-01-29 14:53:18 +01:00
try {
2024-04-01 19:36:03 +02:00
$ret = download_file_from_url (
2024-08-11 00:56:16 +02:00
$context -> get ( HttpDriver :: class ),
2024-04-01 19:36:03 +02:00
$_POST [ 'file_url' ],
$config [ 'upload_by_url_timeout' ],
$allowed_extensions ,
$config [ 'tmp' ],
$config [ 'error' ]
);
if ( $ret === false ) {
error ( sprintf3 ( $config [ 'error' ][ 'filesize' ], array (
'filesz' => 'more than that' ,
'maxsz' => number_format ( $config [ 'max_filesize' ])
)));
}
$_FILES [ 'file' ] = $ret ;
2024-01-29 14:53:18 +01:00
} catch ( Exception $e ) {
error ( $e -> getMessage ());
2013-08-12 15:35:27 +02:00
}
2013-08-12 14:51:46 +02:00
}
2024-01-29 22:57:45 +01:00
2012-04-11 18:49:22 +02:00
$post [ 'name' ] = $_POST [ 'name' ] != '' ? $_POST [ 'name' ] : $config [ 'anonymous' ];
$post [ 'subject' ] = $_POST [ 'subject' ];
2012-08-26 18:18:31 +02:00
$post [ 'email' ] = str_replace ( ' ' , '%20' , htmlspecialchars ( $_POST [ 'email' ]));
2012-04-11 18:49:22 +02:00
$post [ 'body' ] = $_POST [ 'body' ];
2024-06-20 15:11:47 +02:00
$post [ 'password' ] = hashPassword ( $_POST [ 'password' ]);
2016-08-14 16:24:17 +02:00
$post [ 'has_file' ] = ( ! isset ( $post [ 'embed' ]) && (( $post [ 'op' ] && ! isset ( $post [ 'no_longer_require_an_image_for_op' ]) && $config [ 'force_image_op' ]) || count ( $_FILES ) > 0 ));
2024-01-29 22:57:45 +01:00
2016-08-14 16:24:17 +02:00
if ( ! $dropped_post ) {
if ( ! ( $post [ 'has_file' ] || isset ( $post [ 'embed' ])) || (( $post [ 'op' ] && $config [ 'force_body_op' ]) || ( ! $post [ 'op' ] && $config [ 'force_body' ]))) {
$stripped_whitespace = preg_replace ( '/[\s]/u' , '' , $post [ 'body' ]);
if ( $stripped_whitespace == '' ) {
error ( $config [ 'error' ][ 'tooshort_body' ]);
}
2011-01-19 02:37:31 +01:00
}
2024-01-29 22:57:45 +01:00
2016-08-14 16:24:17 +02:00
if ( ! $post [ 'op' ]) {
// Check if thread is locked
// but allow mods to post
if ( $thread [ 'locked' ] && ! hasPermission ( $config [ 'mod' ][ 'postinlocked' ], $board [ 'uri' ]))
error ( $config [ 'error' ][ 'locked' ]);
2024-01-29 22:57:45 +01:00
2016-08-14 16:24:17 +02:00
$numposts = numPosts ( $post [ 'thread' ]);
2024-01-29 22:57:45 +01:00
2016-08-14 16:24:17 +02:00
if ( $config [ 'reply_hard_limit' ] != 0 && $config [ 'reply_hard_limit' ] <= $numposts [ 'replies' ])
error ( $config [ 'error' ][ 'reply_hard_limit' ]);
2024-01-29 22:57:45 +01:00
2016-08-14 16:24:17 +02:00
if ( $post [ 'has_file' ] && $config [ 'image_hard_limit' ] != 0 && $config [ 'image_hard_limit' ] <= $numposts [ 'images' ])
error ( $config [ 'error' ][ 'image_hard_limit' ]);
}
}
else {
if ( ! $post [ 'op' ]) {
2024-01-29 22:57:45 +01:00
$numposts = numPosts ( $post [ 'thread' ]);
2016-08-14 16:24:17 +02:00
}
2012-04-11 18:49:22 +02:00
}
2024-01-29 22:57:45 +01:00
2012-04-12 16:18:19 +02:00
if ( $post [ 'has_file' ]) {
2014-04-27 15:48:47 +02:00
// Determine size sanity
$size = 0 ;
if ( $config [ 'multiimage_method' ] == 'split' ) {
foreach ( $_FILES as $key => $file ) {
$size += $file [ 'size' ];
}
} elseif ( $config [ 'multiimage_method' ] == 'each' ) {
foreach ( $_FILES as $key => $file ) {
if ( $file [ 'size' ] > $size ) {
$size = $file [ 'size' ];
}
}
} else {
error ( _ ( 'Unrecognized file size determination method.' ));
}
2012-04-12 16:18:19 +02:00
if ( $size > $config [ 'max_filesize' ])
2012-04-11 18:49:22 +02:00
error ( sprintf3 ( $config [ 'error' ][ 'filesize' ], array (
'sz' => number_format ( $size ),
'filesz' => number_format ( $size ),
'maxsz' => number_format ( $config [ 'max_filesize' ])
)));
2014-04-27 15:48:47 +02:00
$post [ 'filesize' ] = $size ;
2012-04-11 18:49:22 +02:00
}
2024-01-29 22:57:45 +01:00
2012-05-07 10:22:20 +02:00
$post [ 'capcode' ] = false ;
2024-01-29 22:57:45 +01:00
2012-05-07 10:22:20 +02:00
if ( $mod && preg_match ( '/^((.+) )?## (.+)$/' , $post [ 'name' ], $matches )) {
$name = $matches [ 2 ] != '' ? $matches [ 2 ] : $config [ 'anonymous' ];
$cap = $matches [ 3 ];
2024-01-29 22:57:45 +01:00
2012-05-07 10:22:20 +02:00
if ( isset ( $config [ 'mod' ][ 'capcode' ][ $mod [ 'type' ]])) {
if ( $config [ 'mod' ][ 'capcode' ][ $mod [ 'type' ]] === true ||
( is_array ( $config [ 'mod' ][ 'capcode' ][ $mod [ 'type' ]]) &&
in_array ( $cap , $config [ 'mod' ][ 'capcode' ][ $mod [ 'type' ]])
)) {
2024-01-29 22:57:45 +01:00
2012-05-07 10:22:20 +02:00
$post [ 'capcode' ] = utf8tohtml ( $cap );
$post [ 'name' ] = $name ;
}
2010-11-05 17:46:20 +01:00
}
2012-04-11 18:49:22 +02:00
}
2024-01-29 22:57:45 +01:00
2012-04-11 18:49:22 +02:00
$trip = generate_tripcode ( $post [ 'name' ]);
$post [ 'name' ] = $trip [ 0 ];
2024-07-04 20:29:18 +02:00
if ( $config [ 'disable_tripcodes' ] && ! $mod ) {
2024-05-09 03:34:35 +02:00
$post [ 'trip' ] = '' ;
}
else {
$post [ 'trip' ] = isset ( $trip [ 1 ]) ? $trip [ 1 ] : '' ; // XX: Dropped posts and tripcodes
}
2024-01-29 22:57:45 +01:00
2013-12-23 18:42:01 +01:00
$noko = false ;
2012-04-12 16:18:19 +02:00
if ( strtolower ( $post [ 'email' ]) == 'noko' ) {
2012-04-11 18:49:22 +02:00
$noko = true ;
$post [ 'email' ] = '' ;
2013-12-20 23:52:54 +01:00
} elseif ( strtolower ( $post [ 'email' ]) == 'nonoko' ){
$noko = false ;
$post [ 'email' ] = '' ;
2013-12-23 18:42:01 +01:00
} else $noko = $config [ 'always_noko' ];
2024-01-29 22:57:45 +01:00
2012-04-12 16:18:19 +02:00
if ( $post [ 'has_file' ]) {
2014-04-27 15:48:47 +02:00
$i = 0 ;
foreach ( $_FILES as $key => $file ) {
2017-11-05 09:25:02 +01:00
if ( ! in_array ( $file [ 'error' ], array ( UPLOAD_ERR_NO_FILE , UPLOAD_ERR_OK ))) {
2017-11-03 15:12:35 +01:00
error ( sprintf3 ( $config [ 'error' ][ 'phpfileserror' ], array (
'index' => $i + 1 ,
'code' => $file [ 'error' ]
)));
}
2014-04-27 15:48:47 +02:00
if ( $file [ 'size' ] && $file [ 'tmp_name' ]) {
2016-05-05 10:29:13 +02:00
$file [ 'filename' ] = urldecode ( $file [ 'name' ]);
2014-04-27 15:48:47 +02:00
$file [ 'extension' ] = strtolower ( mb_substr ( $file [ 'filename' ], mb_strrpos ( $file [ 'filename' ], '.' ) + 1 ));
if ( isset ( $config [ 'filename_func' ]))
$file [ 'file_id' ] = $config [ 'filename_func' ]( $file );
else
$file [ 'file_id' ] = time () . substr ( microtime (), 2 , 3 );
if ( sizeof ( $_FILES ) > 1 )
$file [ 'file_id' ] .= " - $i " ;
2024-01-29 22:57:45 +01:00
2014-04-27 15:48:47 +02:00
$file [ 'file' ] = $board [ 'dir' ] . $config [ 'dir' ][ 'img' ] . $file [ 'file_id' ] . '.' . $file [ 'extension' ];
$file [ 'thumb' ] = $board [ 'dir' ] . $config [ 'dir' ][ 'thumb' ] . $file [ 'file_id' ] . '.' . ( $config [ 'thumb_ext' ] ? $config [ 'thumb_ext' ] : $file [ 'extension' ]);
$post [ 'files' ][] = $file ;
$i ++ ;
}
}
2012-04-11 18:49:22 +02:00
}
2014-04-27 15:48:47 +02:00
if ( empty ( $post [ 'files' ])) $post [ 'has_file' ] = false ;
2016-08-14 16:24:17 +02:00
if ( ! $dropped_post ) {
// Check for a file
if ( $post [ 'op' ] && ! isset ( $post [ 'no_longer_require_an_image_for_op' ])) {
if ( ! $post [ 'has_file' ] && $config [ 'force_image_op' ])
error ( $config [ 'error' ][ 'noimage' ]);
}
2014-04-27 15:48:47 +02:00
2016-08-14 16:24:17 +02:00
// Check for too many files
if ( sizeof ( $post [ 'files' ]) > $config [ 'max_images' ])
error ( $config [ 'error' ][ 'toomanyimages' ]);
}
2014-04-27 15:48:47 +02:00
2013-07-29 02:33:26 +02:00
if ( $config [ 'strip_combining_chars' ]) {
$post [ 'name' ] = strip_combining_chars ( $post [ 'name' ]);
$post [ 'email' ] = strip_combining_chars ( $post [ 'email' ]);
2013-07-29 02:46:00 +02:00
$post [ 'subject' ] = strip_combining_chars ( $post [ 'subject' ]);
2013-07-29 02:33:26 +02:00
$post [ 'body' ] = strip_combining_chars ( $post [ 'body' ]);
}
2024-01-29 22:57:45 +01:00
2016-08-14 16:24:17 +02:00
if ( ! $dropped_post ) {
// Check string lengths
if ( mb_strlen ( $post [ 'name' ]) > 35 )
2024-01-29 22:57:45 +01:00
error ( sprintf ( $config [ 'error' ][ 'toolong' ], 'name' ));
2016-08-14 16:24:17 +02:00
if ( mb_strlen ( $post [ 'email' ]) > 40 )
error ( sprintf ( $config [ 'error' ][ 'toolong' ], 'email' ));
if ( mb_strlen ( $post [ 'subject' ]) > 100 )
error ( sprintf ( $config [ 'error' ][ 'toolong' ], 'subject' ));
2023-06-18 05:20:05 +02:00
if ( ! $mod && mb_strlen ( $post [ 'body' ]) > $config [ 'max_body' ])
2016-08-14 16:24:17 +02:00
error ( $config [ 'error' ][ 'toolong_body' ]);
2023-06-25 14:26:29 +02:00
if ( ! $mod && substr_count ( $post [ 'body' ], " \n " ) >= $config [ 'maximum_lines' ])
2023-05-21 00:38:40 +02:00
error ( $config [ 'error' ][ 'toomanylines' ]);
2016-08-14 16:24:17 +02:00
}
2012-04-11 18:49:22 +02:00
wordfilters ( $post [ 'body' ]);
2024-01-29 22:57:45 +01:00
2013-08-01 00:59:54 +02:00
$post [ 'body' ] = escape_markup_modifiers ( $post [ 'body' ]);
2024-01-29 22:57:45 +01:00
2013-08-01 01:18:55 +02:00
if ( $mod && isset ( $post [ 'raw' ]) && $post [ 'raw' ]) {
2013-08-16 19:39:58 +02:00
$post [ 'body' ] .= " \n <tinyboard raw html>1</tinyboard> " ;
2013-08-16 13:25:56 +02:00
}
2024-01-29 22:57:45 +01:00
2016-08-14 16:24:17 +02:00
if ( ! $dropped_post )
2014-09-20 17:21:50 +02:00
if (( $config [ 'country_flags' ] && ! $config [ 'allow_no_country' ]) || ( $config [ 'country_flags' ] && $config [ 'allow_no_country' ] && ! isset ( $_POST [ 'no_country' ]))) {
2021-02-13 19:24:03 +01:00
$gi = geoip_open ( 'inc/lib/geoip/GeoIPv6.dat' , GEOIP_STANDARD );
2014-07-06 02:13:08 +02:00
function ipv4to6 ( $ip ) {
if ( strpos ( $ip , ':' ) !== false ) {
if ( strpos ( $ip , '.' ) > 0 )
$ip = substr ( $ip , strrpos ( $ip , ':' ) + 1 );
else return $ip ; //native ipv6
}
$iparr = array_pad ( explode ( '.' , $ip ), 4 , 0 );
$part7 = base_convert (( $iparr [ 0 ] * 256 ) + $iparr [ 1 ], 10 , 16 );
$part8 = base_convert (( $iparr [ 2 ] * 256 ) + $iparr [ 3 ], 10 , 16 );
return '::ffff:' . $part7 . ':' . $part8 ;
}
2024-01-29 22:57:45 +01:00
2021-02-13 19:24:03 +01:00
if ( $country_code = geoip_country_code_by_addr_v6 ( $gi , ipv4to6 ( $_SERVER [ 'REMOTE_ADDR' ]))) {
2014-07-06 02:13:08 +02:00
if ( ! in_array ( strtolower ( $country_code ), array ( 'eu' , 'ap' , 'o1' , 'a1' , 'a2' )))
$post [ 'body' ] .= " \n <tinyboard flag> " . strtolower ( $country_code ) . " </tinyboard> " .
2021-02-13 19:24:03 +01:00
" \n <tinyboard flag alt> " . geoip_country_name_by_addr_v6 ( $gi , ipv4to6 ( $_SERVER [ 'REMOTE_ADDR' ])) . " </tinyboard> " ;
2014-07-06 02:13:08 +02:00
}
2013-08-01 01:18:55 +02:00
}
2015-03-24 05:19:25 +01:00
2024-01-29 22:57:45 +01:00
if ( $config [ 'user_flag' ] && isset ( $_POST [ 'user_flag' ]) && ! empty ( $_POST [ 'user_flag' ])) {
2014-04-19 14:56:59 +02:00
$user_flag = $_POST [ 'user_flag' ];
2024-01-29 22:57:45 +01:00
if ( ! isset ( $config [ 'user_flags' ][ $user_flag ])) {
2014-04-27 15:48:47 +02:00
error ( _ ( 'Invalid flag selection!' ));
2024-01-29 22:57:45 +01:00
}
2014-04-19 14:56:59 +02:00
$flag_alt = isset ( $user_flag_alt ) ? $user_flag_alt : $config [ 'user_flags' ][ $user_flag ];
$post [ 'body' ] .= " \n <tinyboard flag> " . strtolower ( $user_flag ) . " </tinyboard> " .
2024-01-29 22:57:45 +01:00
" \n <tinyboard flag alt> " . $flag_alt . " </tinyboard> " ;
2014-04-19 14:56:59 +02:00
}
2015-03-24 05:19:25 +01:00
2015-04-23 02:41:17 +02:00
if ( $config [ 'allowed_tags' ] && $post [ 'op' ] && isset ( $_POST [ 'tag' ]) && isset ( $config [ 'allowed_tags' ][ $_POST [ 'tag' ]])) {
2015-04-22 05:54:48 +02:00
$post [ 'body' ] .= " \n <tinyboard tag> " . $_POST [ 'tag' ] . " </tinyboard> " ;
}
2016-08-14 16:24:17 +02:00
if ( ! $dropped_post )
2024-01-29 22:57:45 +01:00
if ( $config [ 'proxy_save' ] && isset ( $_SERVER [ 'HTTP_X_FORWARDED_FOR' ])) {
2015-03-24 05:19:25 +01:00
$proxy = preg_replace ( " /[^0-9a-fA-F.,: ]/ " , '' , $_SERVER [ 'HTTP_X_FORWARDED_FOR' ]);
$post [ 'body' ] .= " \n <tinyboard proxy> " . $proxy . " </tinyboard> " ;
}
2024-01-29 22:57:45 +01:00
2024-01-30 14:26:19 +01:00
$post [ 'body_nomarkup' ] = strip_symbols ( $post [ 'body' ]);
2024-01-29 22:57:45 +01:00
2013-08-01 01:18:55 +02:00
$post [ 'tracked_cites' ] = markup ( $post [ 'body' ], true );
2013-09-06 15:09:18 +02:00
2024-01-29 22:57:45 +01:00
2012-04-12 16:18:19 +02:00
if ( $post [ 'has_file' ]) {
2016-05-05 08:21:21 +02:00
$allhashes = '' ;
2014-04-27 15:48:47 +02:00
foreach ( $post [ 'files' ] as $key => & $file ) {
2015-04-22 05:54:48 +02:00
if ( $post [ 'op' ] && $config [ 'allowed_ext_op' ]) {
if ( ! in_array ( $file [ 'extension' ], $config [ 'allowed_ext_op' ]))
error ( $config [ 'error' ][ 'unknownext' ]);
}
elseif ( ! in_array ( $file [ 'extension' ], $config [ 'allowed_ext' ]) && ! in_array ( $file [ 'extension' ], $config [ 'allowed_ext_files' ]))
2014-04-27 15:48:47 +02:00
error ( $config [ 'error' ][ 'unknownext' ]);
2024-01-29 22:57:45 +01:00
2014-04-27 15:48:47 +02:00
$file [ 'is_an_image' ] = ! in_array ( $file [ 'extension' ], $config [ 'allowed_ext_files' ]);
2024-01-29 22:57:45 +01:00
2014-04-27 15:48:47 +02:00
// Truncate filename if it is too long
$file [ 'filename' ] = mb_substr ( $file [ 'filename' ], 0 , $config [ 'max_filename_len' ]);
2024-01-29 22:57:45 +01:00
2014-04-27 15:48:47 +02:00
$upload = $file [ 'tmp_name' ];
2024-01-29 22:57:45 +01:00
2014-04-27 15:48:47 +02:00
if ( ! is_readable ( $upload ))
error ( $config [ 'error' ][ 'nomove' ]);
2016-05-05 08:21:21 +02:00
2024-01-16 19:01:13 +01:00
$hash = md5_hash_of_file ( $config , $upload );
2016-05-05 08:21:21 +02:00
$file [ 'hash' ] = $hash ;
$allhashes .= $hash ;
2014-04-27 15:48:47 +02:00
}
2014-09-16 01:33:37 +02:00
2016-05-05 08:21:21 +02:00
if ( count ( $post [ 'files' ]) == 1 ) {
2014-04-27 15:48:47 +02:00
$post [ 'filehash' ] = $hash ;
2016-05-05 08:21:21 +02:00
}
else {
$post [ 'filehash' ] = md5 ( $allhashes );
2014-04-27 15:48:47 +02:00
}
2013-09-06 15:09:18 +02:00
}
2016-06-09 11:08:29 +02:00
2016-08-14 16:24:17 +02:00
if ( ! hasPermission ( $config [ 'mod' ][ 'bypass_filters' ], $board [ 'uri' ]) && ! $dropped_post ) {
2016-06-09 11:08:29 +02:00
require_once 'inc/filters.php' ;
2013-09-06 15:09:18 +02:00
do_filters ( $post );
}
2016-06-09 11:08:29 +02:00
if ( $post [ 'has_file' ]) {
2014-04-27 15:48:47 +02:00
foreach ( $post [ 'files' ] as $key => & $file ) {
2016-05-05 10:22:34 +02:00
if ( $file [ 'is_an_image' ]) {
if ( $config [ 'ie_mime_type_detection' ] !== false ) {
// Check IE MIME type detection XSS exploit
2024-04-04 11:07:15 +02:00
$buffer = file_get_contents ( $upload , false , null , 0 , 255 );
2016-05-05 10:22:34 +02:00
if ( preg_match ( $config [ 'ie_mime_type_detection' ], $buffer )) {
undoImage ( $post );
error ( $config [ 'error' ][ 'mime_exploit' ]);
}
2012-04-11 18:49:22 +02:00
}
2024-01-29 22:57:45 +01:00
2012-04-11 18:49:22 +02:00
require_once 'inc/image.php' ;
2024-01-29 22:57:45 +01:00
2012-07-19 08:16:50 +02:00
// find dimensions of an image using GD
2014-04-27 15:48:47 +02:00
if ( ! $size = @ getimagesize ( $file [ 'tmp_name' ])) {
2012-07-19 08:16:50 +02:00
error ( $config [ 'error' ][ 'invalidimg' ]);
}
2024-02-14 12:15:21 +01:00
if ( ! in_array ( $size [ 2 ], array ( IMAGETYPE_PNG , IMAGETYPE_GIF , IMAGETYPE_JPEG , IMAGETYPE_BMP , IMAGETYPE_WEBP ))) {
2016-05-05 10:17:14 +02:00
error ( $config [ 'error' ][ 'invalidimg' ]);
}
2012-07-19 08:16:50 +02:00
if ( $size [ 0 ] > $config [ 'max_width' ] || $size [ 1 ] > $config [ 'max_height' ]) {
error ( $config [ 'error' ][ 'maxsize' ]);
2012-04-11 18:49:22 +02:00
}
2024-01-29 22:57:45 +01:00
2023-07-22 04:26:55 +02:00
$file [ 'exif_stripped' ] = false ;
2024-01-29 22:57:45 +01:00
2024-04-03 21:34:51 +02:00
if ( $file_image_has_operable_metadata && $config [ 'convert_auto_orient' ]) {
2013-07-21 21:50:45 +02:00
// The following code corrects the image orientation.
// Currently only works with the 'convert' option selected but it could easily be expanded to work with the rest if you can be bothered.
2024-04-03 21:24:10 +02:00
if ( ! ( $config [ 'redraw_image' ] || (( $config [ 'strip_exif' ] && ! $config [ 'use_exiftool' ])))) {
2013-08-04 02:34:59 +02:00
if ( in_array ( $config [ 'thumb_method' ], array ( 'convert' , 'convert+gifsicle' , 'gm' , 'gm+gifsicle' ))) {
2014-04-27 15:48:47 +02:00
$exif = @ exif_read_data ( $file [ 'tmp_name' ]);
2013-08-04 02:34:59 +02:00
$gm = in_array ( $config [ 'thumb_method' ], array ( 'gm' , 'gm+gifsicle' ));
2013-08-02 03:28:16 +02:00
if ( isset ( $exif [ 'Orientation' ]) && $exif [ 'Orientation' ] != 1 ) {
2024-02-14 23:26:15 +01:00
$error = shell_exec_error (( $gm ? 'gm ' : '' ) . 'convert ' .
escapeshellarg ( $file [ 'tmp_name' ]) . ' -auto-orient ' . escapeshellarg ( $upload ));
2013-08-04 04:14:25 +02:00
if ( $error )
2014-04-27 15:48:47 +02:00
error ( _ ( 'Could not auto-orient image!' ), null , $error );
$size = @ getimagesize ( $file [ 'tmp_name' ]);
2013-08-04 06:54:27 +02:00
if ( $config [ 'strip_exif' ])
2014-04-27 15:48:47 +02:00
$file [ 'exif_stripped' ] = true ;
2013-08-02 03:28:16 +02:00
}
2013-01-23 17:24:38 +01:00
}
}
}
2024-01-29 22:57:45 +01:00
2012-04-11 18:49:22 +02:00
// create image object
2014-04-27 15:48:47 +02:00
$image = new Image ( $file [ 'tmp_name' ], $file [ 'extension' ], $size );
2012-04-12 16:18:19 +02:00
if ( $image -> size -> width > $config [ 'max_width' ] || $image -> size -> height > $config [ 'max_height' ]) {
2012-04-11 18:49:22 +02:00
$image -> delete ();
error ( $config [ 'error' ][ 'maxsize' ]);
}
2024-01-29 22:57:45 +01:00
2014-04-27 15:48:47 +02:00
$file [ 'width' ] = $image -> size -> width ;
$file [ 'height' ] = $image -> size -> height ;
2024-01-29 22:57:45 +01:00
2012-04-12 16:18:19 +02:00
if ( $config [ 'spoiler_images' ] && isset ( $_POST [ 'spoiler' ])) {
2014-04-27 15:48:47 +02:00
$file [ 'thumb' ] = 'spoiler' ;
2024-01-29 22:57:45 +01:00
2012-04-11 18:49:22 +02:00
$size = @ getimagesize ( $config [ 'spoiler_image' ]);
2014-04-27 15:48:47 +02:00
$file [ 'thumbwidth' ] = $size [ 0 ];
$file [ 'thumbheight' ] = $size [ 1 ];
2012-04-12 16:18:19 +02:00
} elseif ( $config [ 'minimum_copy_resize' ] &&
2012-04-11 18:49:22 +02:00
$image -> size -> width <= $config [ 'thumb_width' ] &&
$image -> size -> height <= $config [ 'thumb_height' ] &&
2014-04-27 15:48:47 +02:00
$file [ 'extension' ] == ( $config [ 'thumb_ext' ] ? $config [ 'thumb_ext' ] : $file [ 'extension' ])) {
2024-01-29 22:57:45 +01:00
2012-04-11 18:49:22 +02:00
// Copy, because there's nothing to resize
2014-04-27 15:48:47 +02:00
copy ( $file [ 'tmp_name' ], $file [ 'thumb' ]);
2024-01-29 22:57:45 +01:00
2014-04-27 15:48:47 +02:00
$file [ 'thumbwidth' ] = $image -> size -> width ;
$file [ 'thumbheight' ] = $image -> size -> height ;
2011-03-14 12:30:42 +01:00
} else {
2012-04-11 18:49:22 +02:00
$thumb = $image -> resize (
2014-04-27 15:48:47 +02:00
$config [ 'thumb_ext' ] ? $config [ 'thumb_ext' ] : $file [ 'extension' ],
2012-04-11 18:49:22 +02:00
$post [ 'op' ] ? $config [ 'thumb_op_width' ] : $config [ 'thumb_width' ],
$post [ 'op' ] ? $config [ 'thumb_op_height' ] : $config [ 'thumb_height' ]
);
2024-01-29 22:57:45 +01:00
2014-04-27 15:48:47 +02:00
$thumb -> to ( $file [ 'thumb' ]);
2024-01-29 22:57:45 +01:00
2014-04-27 15:48:47 +02:00
$file [ 'thumbwidth' ] = $thumb -> width ;
$file [ 'thumbheight' ] = $thumb -> height ;
2024-01-29 22:57:45 +01:00
2012-04-11 18:49:22 +02:00
$thumb -> _destroy ();
2011-03-14 12:30:42 +01:00
}
2022-08-20 18:24:46 +02:00
$dont_copy_file = false ;
2024-01-29 22:57:45 +01:00
2023-07-22 04:26:55 +02:00
if ( $config [ 'redraw_image' ] || ( $file_image_has_operable_metadata && ! $file [ 'exif_stripped' ] && $config [ 'strip_exif' ])) {
2013-08-04 06:48:28 +02:00
if ( ! $config [ 'redraw_image' ] && $config [ 'use_exiftool' ]) {
2024-03-21 16:02:09 +01:00
try {
$file [ 'size' ] = strip_image_metadata ( $file [ 'tmp_name' ]);
} catch ( RuntimeException $e ) {
2024-08-11 00:56:16 +02:00
$context -> get ( Log :: class ) -> log ( Log :: ERROR , " Could not strip image metadata: { $e -> getMessage () } " );
2024-04-03 23:51:02 +02:00
// Since EXIF metadata can countain sensible info, fail the request.
error ( _ ( 'Could not strip EXIF metadata!' ), null , $error );
2021-07-06 13:37:54 +02:00
}
2013-08-04 02:34:59 +02:00
} else {
2014-04-27 15:48:47 +02:00
$image -> to ( $file [ 'file' ]);
2013-08-04 02:34:59 +02:00
$dont_copy_file = true ;
}
2011-12-13 10:15:46 +01:00
}
2012-04-11 18:49:22 +02:00
$image -> destroy ();
2010-11-05 17:46:20 +01:00
} else {
2012-04-11 18:49:22 +02:00
// not an image
2014-04-27 15:48:47 +02:00
$file [ 'thumb' ] = 'file' ;
2013-08-18 12:53:01 +02:00
$size = @ getimagesize ( sprintf ( $config [ 'file_thumb' ],
2014-04-27 15:48:47 +02:00
isset ( $config [ 'file_icons' ][ $file [ 'extension' ]]) ?
$config [ 'file_icons' ][ $file [ 'extension' ]] : $config [ 'file_icons' ][ 'default' ]));
$file [ 'thumbwidth' ] = $size [ 0 ];
$file [ 'thumbheight' ] = $size [ 1 ];
2022-08-20 18:24:46 +02:00
$dont_copy_file = false ;
2010-11-05 17:46:20 +01:00
}
2016-06-09 11:08:29 +02:00
2016-06-10 12:41:53 +02:00
if ( $config [ 'tesseract_ocr' ] && $file [ 'thumb' ] != 'file' ) { // Let's OCR it!
2016-06-09 11:08:29 +02:00
$fname = $file [ 'tmp_name' ];
if ( $file [ 'height' ] > 500 || $file [ 'width' ] > 500 ) {
$fname = $file [ 'thumb' ];
}
2024-03-08 10:24:02 +01:00
if ( $fname !== 'spoiler' ) { // We don't have that much CPU time, do we?
try {
$txt = ocr_image ( $config , $fname );
if ( $txt !== '' ) {
// This one has an effect, that the body is appended to a post body. So you can write a correct
// spamfilter.
$post [ 'body_nomarkup' ] .= " <tinyboard ocr image $key > " . htmlspecialchars ( $value ) . " </tinyboard> " ;
}
} catch ( RuntimeException $e ) {
2024-08-11 00:56:16 +02:00
$context -> get ( Log :: class ) -> log ( Log :: ERROR , " Could not OCR image: { $e -> getMessage () } " );
2016-06-09 11:08:29 +02:00
}
}
}
2024-01-29 22:57:45 +01:00
2022-08-20 18:24:46 +02:00
if ( ! $dont_copy_file ) {
2014-04-27 15:48:47 +02:00
if ( isset ( $file [ 'file_tmp' ])) {
if ( !@ rename ( $file [ 'tmp_name' ], $file [ 'file' ]))
2013-08-12 14:51:46 +02:00
error ( $config [ 'error' ][ 'nomove' ]);
2014-04-27 15:48:47 +02:00
chmod ( $file [ 'file' ], 0644 );
} elseif ( !@ move_uploaded_file ( $file [ 'tmp_name' ], $file [ 'file' ]))
2012-04-20 11:04:37 +02:00
error ( $config [ 'error' ][ 'nomove' ]);
2014-04-29 20:50:28 +02:00
}
2014-04-27 15:48:47 +02:00
}
2012-11-14 21:33:27 +01:00
if ( $config [ 'image_reject_repost' ]) {
if ( $p = getPostByHash ( $post [ 'filehash' ])) {
undoImage ( $post );
2024-01-29 22:57:45 +01:00
error ( sprintf ( $config [ 'error' ][ 'fileexists' ],
2013-12-23 19:01:08 +01:00
( $post [ 'mod' ] ? $config [ 'root' ] . $config [ 'file_mod' ] . '?/' : $config [ 'root' ]) .
( $board [ 'dir' ] . $config [ 'dir' ][ 'res' ] .
2012-11-14 21:33:27 +01:00
( $p [ 'thread' ] ?
$p [ 'thread' ] . '.html#' . $p [ 'id' ]
:
$p [ 'id' ] . '.html'
2013-12-23 19:01:08 +01:00
))
2012-11-14 21:33:27 +01:00
));
}
} else if ( ! $post [ 'op' ] && $config [ 'image_reject_repost_in_thread' ]) {
if ( $p = getPostByHashInThread ( $post [ 'filehash' ], $post [ 'thread' ])) {
undoImage ( $post );
2024-01-29 22:57:45 +01:00
error ( sprintf ( $config [ 'error' ][ 'fileexistsinthread' ],
2013-12-23 19:01:08 +01:00
( $post [ 'mod' ] ? $config [ 'root' ] . $config [ 'file_mod' ] . '?/' : $config [ 'root' ]) .
( $board [ 'dir' ] . $config [ 'dir' ][ 'res' ] .
2012-11-14 21:33:27 +01:00
( $p [ 'thread' ] ?
$p [ 'thread' ] . '.html#' . $p [ 'id' ]
:
$p [ 'id' ] . '.html'
2013-12-23 19:01:08 +01:00
))
2012-11-14 21:33:27 +01:00
));
}
}
2016-06-21 05:03:44 +02:00
}
2024-01-29 22:57:45 +01:00
2016-06-09 11:08:29 +02:00
// Do filters again if OCRing
2016-08-14 16:24:17 +02:00
if ( $config [ 'tesseract_ocr' ] && ! hasPermission ( $config [ 'mod' ][ 'bypass_filters' ], $board [ 'uri' ]) && ! $dropped_post ) {
2016-06-09 11:08:29 +02:00
do_filters ( $post );
}
2016-08-14 16:24:17 +02:00
if ( ! hasPermission ( $config [ 'mod' ][ 'postunoriginal' ], $board [ 'uri' ]) && $config [ 'robot_enable' ] && checkRobot ( $post [ 'body_nomarkup' ]) && ! $dropped_post ) {
2012-04-11 18:49:22 +02:00
undoImage ( $post );
2012-04-12 16:18:19 +02:00
if ( $config [ 'robot_mute' ]) {
2012-04-11 18:49:22 +02:00
error ( sprintf ( $config [ 'error' ][ 'muted' ], mute ()));
2010-11-30 10:40:37 +01:00
} else {
2012-04-11 18:49:22 +02:00
error ( $config [ 'error' ][ 'unoriginal' ]);
2010-11-05 17:46:20 +01:00
}
}
2024-01-29 22:57:45 +01:00
2012-04-11 18:49:22 +02:00
// Remove board directories before inserting them into the database.
2012-04-12 16:18:19 +02:00
if ( $post [ 'has_file' ]) {
2014-04-27 15:48:47 +02:00
foreach ( $post [ 'files' ] as $key => & $file ) {
$file [ 'file_path' ] = $file [ 'file' ];
$file [ 'thumb_path' ] = $file [ 'thumb' ];
$file [ 'file' ] = mb_substr ( $file [ 'file' ], mb_strlen ( $board [ 'dir' ] . $config [ 'dir' ][ 'img' ]));
if ( $file [ 'is_an_image' ] && $file [ 'thumb' ] != 'spoiler' )
$file [ 'thumb' ] = mb_substr ( $file [ 'thumb' ], mb_strlen ( $board [ 'dir' ] . $config [ 'dir' ][ 'thumb' ]));
}
2012-04-11 18:49:22 +02:00
}
2024-01-29 22:57:45 +01:00
2012-04-11 18:49:22 +02:00
$post = ( object ) $post ;
2014-04-29 21:18:17 +02:00
$post -> files = array_map ( function ( $a ) { return ( object ) $a ; }, $post -> files );
2015-03-10 12:48:59 +01:00
2014-04-29 21:18:17 +02:00
$error = event ( 'post' , $post );
$post -> files = array_map ( function ( $a ) { return ( array ) $a ; }, $post -> files );
if ( $error ) {
2012-04-11 18:49:22 +02:00
undoImage (( array ) $post );
error ( $error );
}
$post = ( array ) $post ;
2014-04-27 15:48:47 +02:00
$post [ 'num_files' ] = sizeof ( $post [ 'files' ]);
2024-01-29 22:57:45 +01:00
2012-05-27 12:55:56 +02:00
$post [ 'id' ] = $id = post ( $post );
2015-03-10 13:28:55 +01:00
$post [ 'slug' ] = slugify ( $post );
2024-01-29 22:57:45 +01:00
2016-08-14 16:24:17 +02:00
if ( $dropped_post && $dropped_post [ 'from_nntp' ]) {
2024-01-29 22:57:45 +01:00
$query = prepare ( " INSERT INTO ``nntp_references`` (`board`, `id`, `message_id`, `message_id_digest`, `own`, `headers`) VALUES " .
" (:board , :id , :message_id , :message_id_digest , false, :headers) " );
2016-08-14 16:24:17 +02:00
$query -> bindValue ( ':board' , $dropped_post [ 'board' ]);
$query -> bindValue ( ':id' , $id );
$query -> bindValue ( ':message_id' , $dropped_post [ 'msgid' ]);
$query -> bindValue ( ':message_id_digest' , sha1 ( $dropped_post [ 'msgid' ]));
$query -> bindValue ( ':headers' , $dropped_post [ 'headers' ]);
$query -> execute () or error ( db_error ( $query ));
2016-08-15 00:56:06 +02:00
} // ^^^^^ For inbound posts ^^^^^
elseif ( $config [ 'nntpchan' ][ 'enabled' ] && $config [ 'nntpchan' ][ 'group' ]) {
// vvvvv For outbound posts vvvvv
require_once ( 'inc/nntpchan/nntpchan.php' );
$msgid = gen_msgid ( $post [ 'board' ], $post [ 'id' ]);
list ( $headers , $files ) = post2nntp ( $post , $msgid );
$message = gen_nntp ( $headers , $files );
2024-01-29 22:57:45 +01:00
$query = prepare ( " INSERT INTO ``nntp_references`` (`board`, `id`, `message_id`, `message_id_digest`, `own`, `headers`) VALUES " .
" (:board , :id , :message_id , :message_id_digest , true , :headers) " );
2016-08-15 00:56:06 +02:00
$query -> bindValue ( ':board' , $post [ 'board' ]);
2024-01-29 22:57:45 +01:00
$query -> bindValue ( ':id' , $post [ 'id' ]);
$query -> bindValue ( ':message_id' , $msgid );
$query -> bindValue ( ':message_id_digest' , sha1 ( $msgid ));
$query -> bindValue ( ':headers' , json_encode ( $headers ));
$query -> execute () or error ( db_error ( $query ));
2016-08-15 00:56:06 +02:00
// Let's broadcast it!
nntp_publish ( $message , $msgid );
2016-08-14 16:24:17 +02:00
}
2016-08-15 00:56:06 +02:00
2013-09-06 15:09:18 +02:00
insertFloodPost ( $post );
2015-04-03 08:56:28 +02:00
// Handle cyclical threads
if ( ! $post [ 'op' ] && isset ( $thread [ 'cycle' ]) && $thread [ 'cycle' ]) {
2024-08-09 09:39:08 +02:00
delete_cyclical_posts ( $board [ 'uri' ], $post [ 'thread' ], $config [ 'cycle_limit' ]);
2015-04-03 08:56:28 +02:00
}
2024-01-29 22:57:45 +01:00
2012-05-07 15:51:15 +02:00
if ( isset ( $post [ 'antispam_hash' ])) {
incrementSpamHash ( $post [ 'antispam_hash' ]);
}
2024-01-29 22:57:45 +01:00
2013-08-29 10:55:25 +02:00
if ( isset ( $post [ 'tracked_cites' ]) && ! empty ( $post [ 'tracked_cites' ])) {
2013-08-29 04:38:37 +02:00
$insert_rows = array ();
2012-04-12 16:18:19 +02:00
foreach ( $post [ 'tracked_cites' ] as $cite ) {
2013-08-29 04:38:37 +02:00
$insert_rows [] = '(' .
$pdo -> quote ( $board [ 'uri' ]) . ', ' . ( int ) $id . ', ' .
$pdo -> quote ( $cite [ 0 ]) . ', ' . ( int ) $cite [ 1 ] . ')' ;
2012-04-11 18:49:22 +02:00
}
2013-08-31 05:33:26 +02:00
query ( 'INSERT INTO ``cites`` VALUES ' . implode ( ', ' , $insert_rows )) or error ( db_error ());
2012-04-11 18:49:22 +02:00
}
2024-01-29 22:57:45 +01:00
2013-06-18 19:21:41 +02:00
if ( ! $post [ 'op' ] && strtolower ( $post [ 'email' ]) != 'sage' && ! $thread [ 'sage' ] && ( $config [ 'reply_limit' ] == 0 || $numposts [ 'replies' ] + 1 < $config [ 'reply_limit' ])) {
2012-04-11 18:49:22 +02:00
bumpThread ( $post [ 'thread' ]);
}
2024-01-29 22:57:45 +01:00
2012-04-12 16:18:19 +02:00
if ( isset ( $_SERVER [ 'HTTP_REFERER' ])) {
2012-04-11 18:49:22 +02:00
// Tell Javascript that we posted successfully
2024-05-11 12:44:16 +02:00
if ( isset ( $_COOKIE [ $config [ 'cookies' ][ 'js' ]])) {
2012-04-11 18:49:22 +02:00
$js = json_decode ( $_COOKIE [ $config [ 'cookies' ][ 'js' ]]);
2024-05-11 12:44:16 +02:00
} else {
$js = ( object ) array ();
}
2012-04-11 18:49:22 +02:00
// Tell it to delete the cached post for referer
$js -> { $_SERVER [ 'HTTP_REFERER' ]} = true ;
2024-05-11 12:44:16 +02:00
// Encode and set cookie.
$options = [
'expires' => 0 ,
'path' => $config [ 'cookies' ][ 'jail' ] ? $config [ 'cookies' ][ 'path' ] : '/' ,
'httponly' => false ,
'samesite' => 'Strict'
];
setcookie ( $config [ 'cookies' ][ 'js' ], json_encode ( $js ), $options );
2012-04-11 18:49:22 +02:00
}
2024-01-29 22:57:45 +01:00
2012-04-11 18:49:22 +02:00
$root = $post [ 'mod' ] ? $config [ 'root' ] . $config [ 'file_mod' ] . '?/' : $config [ 'root' ];
2024-01-29 22:57:45 +01:00
2013-12-20 23:52:54 +01:00
if ( $noko ) {
2012-04-11 18:49:22 +02:00
$redirect = $root . $board [ 'dir' ] . $config [ 'dir' ][ 'res' ] .
2015-03-10 12:48:59 +01:00
link_for ( $post , false , false , $thread ) . ( ! $post [ 'op' ] ? '#' . $id : '' );
2024-01-29 22:57:45 +01:00
2013-08-10 23:16:30 +02:00
if ( ! $post [ 'op' ] && isset ( $_SERVER [ 'HTTP_REFERER' ])) {
$regex = array (
'board' => str_replace ( '%s' , '(\w{1,8})' , preg_quote ( $config [ 'board_path' ], '/' )),
'page' => str_replace ( '%d' , '(\d+)' , preg_quote ( $config [ 'file_page' ], '/' )),
2015-03-10 12:48:59 +01:00
'page50' => '(' . str_replace ( '%d' , '(\d+)' , preg_quote ( $config [ 'file_page50' ], '/' )) . '|' .
str_replace ( array ( '%d' , '%s' ), array ( '(\d+)' , '[a-z0-9-]+' ), preg_quote ( $config [ 'file_page50_slug' ], '/' )) . ')' ,
2013-08-10 23:16:30 +02:00
'res' => preg_quote ( $config [ 'dir' ][ 'res' ], '/' ),
);
if ( preg_match ( '/\/' . $regex [ 'board' ] . $regex [ 'res' ] . $regex [ 'page50' ] . '([?&].*)?$/' , $_SERVER [ 'HTTP_REFERER' ])) {
$redirect = $root . $board [ 'dir' ] . $config [ 'dir' ][ 'res' ] .
2015-03-10 12:48:59 +01:00
link_for ( $post , true , false , $thread ) . ( ! $post [ 'op' ] ? '#' . $id : '' );
2013-08-10 23:16:30 +02:00
}
}
2012-04-11 18:49:22 +02:00
} else {
$redirect = $root . $board [ 'dir' ] . $config [ 'file_index' ];
2024-01-29 22:57:45 +01:00
2012-04-11 18:49:22 +02:00
}
2015-03-31 09:17:22 +02:00
buildThread ( $post [ 'op' ] ? $id : $post [ 'thread' ]);
2024-01-29 22:57:45 +01:00
2024-08-11 00:56:16 +02:00
$context -> get ( Log :: class ) -> log (
2024-04-03 23:51:02 +02:00
Log :: INFO ,
'New post: /' . $board [ 'dir' ] . $config [ 'dir' ][ 'res' ] . link_for ( $post ) . ( ! $post [ 'op' ] ? '#' . $id : '' )
);
2024-01-29 22:57:45 +01:00
2013-06-21 22:36:20 +02:00
if ( ! $post [ 'mod' ]) header ( 'X-Associated-Content: "' . $redirect . '"' );
2013-07-18 21:19:03 +02:00
2021-03-18 01:22:40 +01:00
2023-07-18 11:35:56 +02:00
if ( ! isset ( $_POST [ 'json_response' ])) {
header ( 'Location: ' . $redirect , true , $config [ 'redirect_http' ]);
} else {
header ( 'Content-Type: text/json; charset=utf-8' );
echo json_encode ( array (
'redirect' => $redirect ,
'noko' => $noko ,
'id' => $id
));
}
2015-03-31 09:17:22 +02:00
if ( $config [ 'try_smarter' ] && $post [ 'op' ])
$build_pages = range ( 1 , $config [ 'max_pages' ]);
2024-01-29 22:57:45 +01:00
2015-03-31 09:17:22 +02:00
if ( $post [ 'op' ])
2016-05-08 03:09:20 +02:00
clean ( $id );
2024-01-29 22:57:45 +01:00
2015-03-31 09:17:22 +02:00
event ( 'post-after' , $post );
2024-01-29 22:57:45 +01:00
2015-03-31 09:17:22 +02:00
buildIndex ();
2023-07-18 11:35:56 +02:00
2015-03-31 09:17:22 +02:00
// We are already done, let's continue our heavy-lifting work in the background (if we run off FastCGI)
if ( function_exists ( 'fastcgi_finish_request' ))
@ fastcgi_finish_request ();
if ( $post [ 'op' ])
2024-08-16 18:32:32 +02:00
Vichan\Functions\Theme\rebuild_themes ( 'post-thread' , $board [ 'uri' ]);
2015-03-31 09:17:22 +02:00
else
2024-08-16 18:32:32 +02:00
Vichan\Functions\Theme\rebuild_themes ( 'post' , $board [ 'uri' ]);
2024-01-29 22:57:45 +01:00
2013-09-21 04:51:23 +02:00
} elseif ( isset ( $_POST [ 'appeal' ])) {
if ( ! isset ( $_POST [ 'ban_id' ]))
error ( $config [ 'error' ][ 'bot' ]);
2024-01-29 22:57:45 +01:00
2013-09-21 04:51:23 +02:00
$ban_id = ( int ) $_POST [ 'ban_id' ];
2024-01-29 22:57:45 +01:00
2024-06-30 16:48:22 +02:00
$ban = Bans :: findSingle ( $_SERVER [ 'REMOTE_ADDR' ], $ban_id , $config [ 'require_ban_view' ], $config [ 'auto_maintenance' ]);
2024-01-29 22:57:45 +01:00
2024-03-22 10:49:47 +01:00
if ( empty ( $ban )) {
2021-07-24 14:47:56 +02:00
error ( $config [ 'error' ][ 'noban' ]);
2013-09-21 04:51:23 +02:00
}
2024-01-29 22:57:45 +01:00
2013-09-21 04:51:23 +02:00
if ( $ban [ 'expires' ] && $ban [ 'expires' ] - $ban [ 'created' ] <= $config [ 'ban_appeals_min_length' ]) {
2021-07-24 14:47:56 +02:00
error ( $config [ 'error' ][ 'tooshortban' ]);
2013-09-21 04:51:23 +02:00
}
2024-01-29 22:57:45 +01:00
2013-09-21 04:51:23 +02:00
$query = query ( " SELECT `denied` FROM ``ban_appeals`` WHERE `ban_id` = $ban_id " ) or error ( db_error ());
$ban_appeals = $query -> fetchAll ( PDO :: FETCH_COLUMN );
2024-01-29 22:57:45 +01:00
2013-09-21 04:51:23 +02:00
if ( count ( $ban_appeals ) >= $config [ 'ban_appeals_max' ]) {
2021-07-24 14:47:56 +02:00
error ( $config [ 'error' ][ 'toomanyappeals' ]);
2013-09-21 04:51:23 +02:00
}
2024-01-29 22:57:45 +01:00
2013-09-21 04:51:23 +02:00
foreach ( $ban_appeals as $is_denied ) {
2021-07-24 14:47:56 +02:00
if ( ! $is_denied ) {
error ( $config [ 'error' ][ 'pendingappeal' ]);
}
}
if ( strlen ( $_POST [ 'appeal' ]) > $config [ 'ban_appeal_max_chars' ]) {
error ( $config [ 'error' ][ 'toolongappeal' ]);
2013-09-21 04:51:23 +02:00
}
2024-01-29 22:57:45 +01:00
2013-09-21 04:51:23 +02:00
$query = prepare ( " INSERT INTO ``ban_appeals`` VALUES (NULL, :ban_id, :time, :message, 0) " );
$query -> bindValue ( ':ban_id' , $ban_id , PDO :: PARAM_INT );
$query -> bindValue ( ':time' , time (), PDO :: PARAM_INT );
$query -> bindValue ( ':message' , $_POST [ 'appeal' ]);
$query -> execute () or error ( db_error ( $query ));
2024-01-29 22:57:45 +01:00
2013-09-21 04:51:23 +02:00
displayBan ( $ban );
2012-04-11 18:49:22 +02:00
} else {
2012-04-12 16:18:19 +02:00
if ( ! file_exists ( $config [ 'has_installed' ])) {
2012-04-11 18:49:22 +02:00
header ( 'Location: install.php' , true , $config [ 'redirect_http' ]);
} else {
// They opened post.php in their browser manually.
error ( $config [ 'error' ][ 'nopost' ]);
}
2021-03-12 21:51:42 +01:00
}