<commit_before><?php namespace App\Http\Requests;

use App\Ban;
use App\Board;
use App\Post;
use App\Contracts\ApiController as ApiContract;
use App\Http\Controllers\API\ApiController;
use App\Services\UserManager;

use Auth;
use Validator;
use View;

class PostRequest extends Request implements ApiContract{
	
	use ApiController;
	
	const VIEW_BANNED = "errors.banned";
	
	/**
	 * Input items that should not be returned when reloading the page.
	 *
	 * @var array
	 */
	protected $dontFlash = ['password', 'password_confirmation', 'captcha'];
	
	/**
	 * The board pertinent to the request.
	 *
	 * @var App\Board
	 */
	protected $board;
	
	/**
	 * A ban pulled during validation checks.
	 *
	 * @var App\Ban
	 */
	protected $ban;
	
	/**
	 * The thread we're replying to.
	 *
	 * @var App\Post
	 */
	protected $thread;
	
	/**
	 * The user.
	 *
	 * @var App\Trait\PermissionUser
	 */
	protected $user;
	
	/**
	 * Fetches the user and our board config.
	 *
	 * @return void
	 */
	public function __construct(Board $board, Post $thread, UserManager $manager)
	{
		$this->board  = $board;
		$this->user   = $manager->user;
		
		if ($thread->exists)
		{
			$this->thread = $thread;
		}
	}
	
	/**
	 * Get all form input.
	 *
	 * @return array
	 */
	public function all()
	{
		$input = parent::all();
		
		if (isset($input['files']) && is_array($input['files']))
		{
			// Having an [null] file array passes validation.
			$input['files'] = array_filter($input['files']);
		}
		
		if (isset($input['capcode']) && $input['capcode'])
		{
			$user = $this->user;
			
			if ($user && !$user->isAnonymous())
			{
				$role = $user->roles->where('role_id', (int) $input['capcode'])->first();
				
				if ($role && $role->capcode != "")
				{
					$input['capcode_id'] = (int) $role->role_id;
					// $input['author']     = $user->username;
				}
				else
				{
					$this->failedAuthorization();
				}
			}
			else
			{
				unset($input['capcode']);
			}
		}
		
		return $input;
	}
	
	/**
	 * Returns if the client has access to this form.
	 *
	 * @return boolean
	 */
	public function authorize()
	{
		// Ban check.
		$ban = Ban::getBan($this->ip(), $this->board->board_uri);
		
		if ($ban)
		{
			$this->ban = $ban;
			return false;
		}
		
		// Locked thread check.
		if ($this->thread instanceof Post && $this->thread->isLocked() && !$this->user->canPostInLockedThreads($this->board))
		{
			return false;
		}
		
		## TODO ##
		// Separate these permsisions.
		return $this->user->canPostThread() || $this->user->canPostReply();
	}
	
	/**
	 * Returns the response if authorize() fails.
	 *
	 * @return Response
	 */
	public function forbiddenResponse()
	{
		if ($this->ban)
		{
			$url = $this->ban->getRedirectUrl();
			
			if ($this->wantsJson())
			{
				return $this->apiResponse([ 'redirect' => $url ]);
			}
			else
			{
				return redirect($url);
			}
		}
		
		return abort(403);
	}
	
	/**
	 * Returns validation rules for this request.
	 *
	 * @return array
	 */
	public function rules()
	{
		$board = $this->board;
		$user  = $this->user;
		$rules = [
			// Nothing, by default.
			// Post options are contingent on board settings and user permissions.
		];
		
		// Modify the validation rules based on what we've been supplied.
		if ($board && $user)
		{
			$rules['body'] = [
				"min:" . $board->getConfig('postMinLength', 0),
				"max:" . $board->getConfig('postMaxLength', 65534),
			];
			
			if (!$board->canAttach($user))
			{
				$rules['body'][]  = "required";
				$rules['files'][] = "array";
				$rules['files'][] = "max:0";
			}
			else
			{
				$rules['body'][]  = "required_without:files";
				
				// Add the rules for file uploads.
				if (isset($this->all()['dropzone']))
				{
					$fileToken = "<PASSWORD>";
					
					// JavaScript enabled hash posting
					static::rulesForFileHashes($board, $rules);
				}
				else
				{
					$fileToken = "files";
					
					// Vanilla HTML upload
					static::rulesForFiles($board, $rules);
				}
				
				
				$attachmentsMax = $board->getConfig('postAttachmentsMax', 1);
				
				for ($attachment = 0; $attachment < $attachmentsMax; ++$attachment)
				{
					// Can only attach existing files.
					if (!$user->canAttachNew($board) && $user->canAttachOld($board))
					{
						$rules["{$fileToken}.{$attachment}"][] = "file_old";
					}
					// Can only attach new files.
					else if ($user->canAttachNew($board) && !$user->canAttachOld($board))
					{
						$rules["{$fileToken}.{$attachment}"][] = "file_new";
					}
				}
			}
		}
		
		return $rules;
	}
	
	/**
	 * Returns rules specifically for files for a board.
	 *
	 * @param  Board  $board
	 * @param  array  $rules (POINTER, MODIFIED)
	 * @return array  Validation rules.
	 */
	public static function rulesForFiles(Board $board, array &$rules)
	{
		global $app;
		
		$rules['spoilers'] = "boolean";
		
		$attachmentsMax = $board->getConfig('postAttachmentsMax', 1);
		$attachmentsMin = $board->getConfig('postAttachmentsMin', 0);
		
		if ($attachmentsMin > 0)
		{
			$rules['files'][] = "required";
		}
		
		$rules['files'][] = "array";
		$rules['files'][] = "min:{$attachmentsMin}";
		$rules['files'][] = "max:{$attachmentsMax}";
		
		// Create an additional rule for each possible file.
		for ($attachment = 0; $attachment < $attachmentsMax; ++$attachment)
		{
			$rules["files.{$attachment}"] = [
				//"mimes:jpeg,gif,png,bmp,svg,swf,webm,mp4,ogg,mp3,mpga,mpeg,wav,pdf,epub",
				"between:0," . $app['settings']('attachmentFilesize'),
				"file_integrity",
			];
		}
	}
	
	/**
	 * Returns rules specifically for dropzone files for a board.
	 *
	 * @param  Board  $board
	 * @param  array  $rules (POINTER, MODIFIED)
	 * @return array  Validation rules.
	 */<commit_msg>Filesize limit now in KiB, KB.<commit_after><?php namespace App\Http\Requests;

use App\Ban;
use App\Board;
use App\Post;
use App\Contracts\ApiController as ApiContract;
use App\Http\Controllers\API\ApiController;
use App\Services\UserManager;

use Auth;
use Validator;
use View;

class PostRequest extends Request implements ApiContract{
	
	use ApiController;
	
	const VIEW_BANNED = "errors.banned";
	
	/**
	 * Input items that should not be returned when reloading the page.
	 *
	 * @var array
	 */
	protected $dontFlash = ['password', 'password_confirmation', 'captcha'];
	
	/**
	 * The board pertinent to the request.
	 *
	 * @var App\Board
	 */
	protected $board;
	
	/**
	 * A ban pulled during validation checks.
	 *
	 * @var App\Ban
	 */
	protected $ban;
	
	/**
	 * The thread we're replying to.
	 *
	 * @var App\Post
	 */
	protected $thread;
	
	/**
	 * The user.
	 *
	 * @var App\Trait\PermissionUser
	 */
	protected $user;
	
	/**
	 * Fetches the user and our board config.
	 *
	 * @return void
	 */
	public function __construct(Board $board, Post $thread, UserManager $manager)
	{
		$this->board  = $board;
		$this->user   = $manager->user;
		
		if ($thread->exists)
		{
			$this->thread = $thread;
		}
	}
	
	/**
	 * Get all form input.
	 *
	 * @return array
	 */
	public function all()
	{
		$input = parent::all();
		
		if (isset($input['files']) && is_array($input['files']))
		{
			// Having an [null] file array passes validation.
			$input['files'] = array_filter($input['files']);
		}
		
		if (isset($input['capcode']) && $input['capcode'])
		{
			$user = $this->user;
			
			if ($user && !$user->isAnonymous())
			{
				$role = $user->roles->where('role_id', (int) $input['capcode'])->first();
				
				if ($role && $role->capcode != "")
				{
					$input['capcode_id'] = (int) $role->role_id;
					// $input['author']     = $user->username;
				}
				else
				{
					$this->failedAuthorization();
				}
			}
			else
			{
				unset($input['capcode']);
			}
		}
		
		return $input;
	}
	
	/**
	 * Returns if the client has access to this form.
	 *
	 * @return boolean
	 */
	public function authorize()
	{
		// Ban check.
		$ban = Ban::getBan($this->ip(), $this->board->board_uri);
		
		if ($ban)
		{
			$this->ban = $ban;
			return false;
		}
		
		// Locked thread check.
		if ($this->thread instanceof Post && $this->thread->isLocked() && !$this->user->canPostInLockedThreads($this->board))
		{
			return false;
		}
		
		## TODO ##
		// Separate these permsisions.
		return $this->user->canPostThread() || $this->user->canPostReply();
	}
	
	/**
	 * Returns the response if authorize() fails.
	 *
	 * @return Response
	 */
	public function forbiddenResponse()
	{
		if ($this->ban)
		{
			$url = $this->ban->getRedirectUrl();
			
			if ($this->wantsJson())
			{
				return $this->apiResponse([ 'redirect' => $url ]);
			}
			else
			{
				return redirect($url);
			}
		}
		
		return abort(403);
	}
	
	/**
	 * Returns validation rules for this request.
	 *
	 * @return array
	 */
	public function rules()
	{
		$board = $this->board;
		$user  = $this->user;
		$rules = [
			// Nothing, by default.
			// Post options are contingent on board settings and user permissions.
		];
		
		// Modify the validation rules based on what we've been supplied.
		if ($board && $user)
		{
			$rules['body'] = [
				"min:" . $board->getConfig('postMinLength', 0),
				"max:" . $board->getConfig('postMaxLength', 65534),
			];
			
			if (!$board->canAttach($user))
			{
				$rules['body'][]  = "required";
				$rules['files'][] = "array";
				$rules['files'][] = "max:0";
			}
			else
			{
				$rules['body'][]  = "required_without:files";
				
				// Add the rules for file uploads.
				if (isset($this->all()['dropzone']))
				{
					$fileToken = "<PASSWORD>";
					
					// JavaScript enabled hash posting
					static::rulesForFileHashes($board, $rules);
				}
				else
				{
					$fileToken = "files";
					
					// Vanilla HTML upload
					static::rulesForFiles($board, $rules);
				}
				
				
				$attachmentsMax = $board->getConfig('postAttachmentsMax', 1);
				
				for ($attachment = 0; $attachment < $attachmentsMax; ++$attachment)
				{
					// Can only attach existing files.
					if (!$user->canAttachNew($board) && $user->canAttachOld($board))
					{
						$rules["{$fileToken}.{$attachment}"][] = "file_old";
					}
					// Can only attach new files.
					else if ($user->canAttachNew($board) && !$user->canAttachOld($board))
					{
						$rules["{$fileToken}.{$attachment}"][] = "file_new";
					}
				}
			}
		}
		
		return $rules;
	}
	
	/**
	 * Returns rules specifically for files for a board.
	 *
	 * @param  Board  $board
	 * @param  array  $rules (POINTER, MODIFIED)
	 * @return array  Validation rules.
	 */
	public static function rulesForFiles(Board $board, array &$rules)
	{
		global $app;
		
		$rules['spoilers'] = "boolean";
		
		$attachmentsMax = $board->getConfig('postAttachmentsMax', 1);
		$attachmentsMin = $board->getConfig('postAttachmentsMin', 0);
		
		if ($attachmentsMin > 0)
		{
			$rules['files'][] = "required";
		}
		
		$rules['files'][] = "array";
		$rules['files'][] = "min:{$attachmentsMin}";
		$rules['files'][] = "max:{$attachmentsMax}";
		
		// Create an additional rule for each possible file.
		for ($attachment = 0; $attachment < $attachmentsMax; ++$attachment)
		{
			$rules["files.{$attachment}"] = [
				"between:0," . ( (int) $app['settings']('attachmentFilesize') * 1.024),
				"file_integrity",
			];
		}
	}
	
	/**
	 * Returns rules specifically for dropzone files for a board.
	 *
	 * @param  Board  $board
	 * @param  array  $rules (POINTER, MODIFIED)
	 * @return array  Validation rules.
	 */
{'fraction_non_alphanumeric': 0.17792276494445425, 'fraction_numerical': 0.0035267148651031564, 'mean_word_length': 2.80509896008051, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 101, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3930983', 'n_tokens_mistral': 4702, 'n_tokens_neox': 3761, 'n_words': 874}