<?php
/* Copyright (C) NAVER <http://www.navercorp.com> */
/**
 * admin controller class of the file module
 * @author NAVER (developers@xpressengine.com)
 */
class FileAdminController extends File
{
	/**
	 * Initialization
	 * @return void
	 */
	function init()
	{
	}

	/**
	 * @deprecated move to fileController
	 * @return Object
	 */
	function deleteModuleFiles($module_srl)
	{
		return getController('file')->deleteModuleFiles($module_srl);
	}

	/**
	 * Delete selected files from the administrator page
	 *
	 * @return Object
	 */
	function procFileAdminDeleteChecked()
	{
		// An error appears if no document is selected
		$cart = Context::get('cart');
		if(!$cart) throw new Rhymix\Framework\Exception('msg_file_cart_is_null');
		if(!is_array($cart)) $file_srl_list= explode('|@|', $cart);
		else $file_srl_list = $cart;
		$file_count = count($file_srl_list);
		if(!$file_count) throw new Rhymix\Framework\Exception('msg_file_cart_is_null');

		$oFileController = getController('file');
		// Delete the post
		for($i=0;$i<$file_count;$i++)
		{
			$file_srl = trim($file_srl_list[$i]);
			if(!$file_srl) continue;

			$oFileController->deleteFile($file_srl);
		}

		$this->setMessage(sprintf(lang('msg_checked_file_is_deleted'), $file_count));

		$redirect_url = $_SERVER['HTTP_REFERER'] ?? '';
		if (!$redirect_url || !Rhymix\Framework\URL::isInternalURL($redirect_url))
		{
			$redirect_url = Context::get('success_return_url') ?: getNotEncodedUrl('', 'module', 'admin', 'act', 'dispFileAdminList');
		}
		$this->setRedirectUrl($redirect_url);
	}

	/**
	 * Save upload configuration
	 *
	 * @return Object
	 */
	function procFileAdminInsertUploadConfig()
	{
		// Default settings
		$config = getModel('module')->getModuleConfig('file') ?: new stdClass;
		$config->allowed_filesize = intval(Context::get('allowed_filesize'));
		$config->allowed_attach_size = intval(Context::get('allowed_attach_size'));
		$config->allowed_filetypes = Context::get('allowed_filetypes');
		$config->pre_conversion_filesize = intval(Context::get('pre_conversion_filesize')) ?: null;

		// Image settings
		$config->image_autoconv = [];
		foreach (Context::get('image_autoconv') ?: [] as $source_type => $target_type)
		{
			if (in_array($target_type, ['Y', 'N']))
			{
				$config->image_autoconv[$source_type] = tobool($target_type);
			}
			elseif (in_array($target_type, ['', 'jpg', 'png', 'webp']))
			{
				$config->image_autoconv[$source_type] = $target_type;
			}
		}
		$config->max_image_width = intval(Context::get('max_image_width')) ?: '';
		$config->max_image_height = intval(Context::get('max_image_height')) ?: '';
		$config->max_image_size_action = Context::get('max_image_size_action') ?: '';
		$config->max_image_size_same_format = strval(Context::get('max_image_size_same_format'));
		$config->max_image_size_admin = Context::get('max_image_size_admin') === 'Y' ? 'Y' : 'N';
		$config->image_quality_adjustment = max(50, min(100, intval(Context::get('image_quality_adjustment'))));
		$config->image_autorotate = Context::get('image_autorotate') === 'Y' ? true : false;
		$config->image_remove_exif_data = Context::get('image_remove_exif_data') === 'Y' ? true : false;
		$config->image_always_reencode = Context::get('image_always_reencode') === 'Y' ? true : false;

		// Video settings
		$config->max_video_width = intval(Context::get('max_video_width')) ?: '';
		$config->max_video_height = intval(Context::get('max_video_height')) ?: '';
		$config->max_video_size_action = Context::get('max_video_size_action') ?: '';
		$config->max_video_size_admin = Context::get('max_video_size_admin') === 'Y' ? 'Y' : 'N';
		$config->max_video_duration = intval(Context::get('max_video_duration')) ?: '';
		$config->max_video_duration_action = Context::get('max_video_duration_action') ?: '';
		$config->max_video_duration_admin = Context::get('max_video_duration_admin') === 'Y' ? 'Y' : 'N';
		$config->video_autoconv['any2mp4'] = Context::get('video_autoconv_any2mp4') === 'Y' ? true : false;
		$config->video_always_reencode = Context::get('video_always_reencode') === 'Y' ? true : false;
		$config->video_thumbnail = Context::get('video_thumbnail') === 'Y' ? true : false;
		$config->video_mp4_gif_time = intval(Context::get('video_mp4_gif_time'));

		// Path to ffmpeg, ffprobe, magick
		if (RX_WINDOWS)
		{
			$config->ffmpeg_command = escape(Context::get('ffmpeg_command')) ?: 'C:\Program Files\ffmpeg\bin\ffmpeg.exe';
			$config->ffprobe_command = escape(Context::get('ffprobe_command')) ?: 'C:\Program Files\ffmpeg\bin\ffprobe.exe';
			$config->magick_command = escape(Context::get('magick_command')) ?: '';
		}
		else
		{
			$config->ffmpeg_command = escape(utf8_trim(Context::get('ffmpeg_command'))) ?: '/usr/bin/ffmpeg';
			$config->ffprobe_command = escape(utf8_trim(Context::get('ffprobe_command'))) ?: '/usr/bin/ffprobe';
			$config->magick_command = escape(utf8_trim(Context::get('magick_command'))) ?: '';
		}

		// Timeouts
		$config->ffmpeg_timeout = max(0, intval(Context::get('ffmpeg_timeout'))) ?: null;
		$config->magick_timeout = max(0, intval(Context::get('magick_timeout'))) ?: null;

		// Check maximum file size (probably not necessary anymore)
		if (PHP_INT_SIZE < 8)
		{
			if ($config->allowed_filesize > 2047 || $config->allowed_attach_size > 2047)
			{
				throw new Rhymix\Framework\Exception('msg_32bit_max_2047mb');
			}
		}

		// Simplify allowed_filetypes
		$config->allowed_extensions = strtr(strtolower(trim($config->allowed_filetypes)), array('*.' => '', ';' => ','));
		if ($config->allowed_extensions)
		{
			$config->allowed_extensions = array_filter(array_map('trim', explode(',', $config->allowed_extensions)), function($str) {
				return $str !== '*';
			});
			$config->allowed_filetypes = implode(';', array_map(function($ext) {
				return '*.' . $ext;
			}, $config->allowed_extensions));
		}
		else
		{
			$config->allowed_extensions = array();
			$config->allowed_filetypes = '*.*';
		}

		// Generate pre-conversion whitelist
		$config->pre_conversion_types = [];
		foreach ($config->image_autoconv ?? [] as $source_type => $target_type)
		{
			if (!empty($target_type) && $target_type !== true)
			{
				$config->pre_conversion_types[] = $source_type;
				if ($source_type === 'jpg')
				{
					$config->pre_conversion_types[] = 'jpeg';
				}
			}
			elseif ($source_type === 'gif2mp4' && $target_type === true)
			{
				$config->pre_conversion_types[] = 'gif';
			}
		}
		if ($config->video_autoconv['any2mp4'])
		{
			$config->pre_conversion_types = array_merge($config->pre_conversion_types, ['mp4', 'webm', 'ogv', 'avi', 'mkv', 'mov', 'mpg', 'mpe', 'mpeg', 'wmv', 'm4v', 'flv']);
		}

		// Save and redirect
		$output = getController('module')->insertModuleConfig('file', $config);
		$returnUrl = Context::get('success_return_url') ?: getNotEncodedUrl('', 'module', 'admin', 'act', 'dispFileAdminUploadConfig');
		return $this->setRedirectUrl($returnUrl, $output);
	}

	/**
	 * Save download configuration
	 *
	 * @return Object
	 */
	function procFileAdminInsertDownloadConfig()
	{
		// Update configuration
		$config = getModel('module')->getModuleConfig('file') ?: new stdClass;
		$config->allow_outlink = Context::get('allow_outlink') === 'N' ? 'N' : 'Y';
		$config->allow_outlink_format = Context::get('allow_outlink_format');
		$config->allow_outlink_site = Context::get('allow_outlink_site');
		$config->allow_indexing_format = Context::get('allow_indexing_format');
		$config->allow_multimedia_direct_download = Context::get('allow_multimedia_direct_download') === 'Y' ? 'Y' : 'N';
		$config->download_short_url = Context::get('download_short_url') === 'Y' ? 'Y' : 'N';
		$config->inline_download_format = array_map('utf8_trim', Context::get('inline_download_format') ?: []);

		// Save and redirect
		$output = getController('module')->insertModuleConfig('file', $config);
		$returnUrl = Context::get('success_return_url') ?: getNotEncodedUrl('', 'module', 'admin', 'act', 'dispFileAdminDownloadConfig');
		return $this->setRedirectUrl($returnUrl, $output);
	}

	/**
	 * Save other configuration
	 *
	 * @return Object
	 */
	function procFileAdminInsertOtherConfig()
	{
		// Update configuration
		$config = getModel('module')->getModuleConfig('file') ?: new stdClass;
		$config->save_changelog = Context::get('save_changelog') === 'Y' ? 'Y' : 'N';

		// Save and redirect
		$output = getController('module')->insertModuleConfig('file', $config);
		$returnUrl = Context::get('success_return_url') ?: getNotEncodedUrl('', 'module', 'admin', 'act', 'dispFileAdminOtherConfig');
		return $this->setRedirectUrl($returnUrl, $output);
	}

	/**
	 * Add file information for each module
	 *
	 * @return void
	 */
	function procFileAdminInsertModuleConfig()
	{
		$config = new stdClass;

		// Default
		if(!Context::get('use_default_file_config'))
		{
			$config->use_default_file_config = 'N';
			$config->allowed_filesize = intval(Context::get('allowed_filesize'));
			$config->allowed_attach_size = intval(Context::get('allowed_attach_size'));
			$config->allowed_filetypes = Context::get('allowed_filetypes');
			$config->pre_conversion_filesize = intval(Context::get('pre_conversion_filesize')) ?: null;

			// Check maximum file size
			if (PHP_INT_SIZE < 8)
			{
				if ($config->allowed_filesize > 2047 || $config->allowed_attach_size > 2047)
				{
					throw new Rhymix\Framework\Exception('msg_32bit_max_2047mb');
				}
			}

			// Simplify allowed_filetypes
			$config->allowed_extensions = strtr(strtolower(trim($config->allowed_filetypes)), array('*.' => '', ';' => ','));
			if ($config->allowed_extensions)
			{
				$config->allowed_extensions = array_filter(array_map('trim', explode(',', $config->allowed_extensions)), function($str) {
					return $str !== '*';
				});
				$config->allowed_filetypes = implode(';', array_map(function($ext) {
					return '*.' . $ext;
				}, $config->allowed_extensions));
			}
			else
			{
				$config->allowed_extensions = array();
				$config->allowed_filetypes = '*.*';
			}
		}

		// Image
		if(!Context::get('use_image_default_file_config'))
		{
			$config->use_image_default_file_config = 'N';
			foreach (Context::get('image_autoconv') ?: [] as $source_type => $target_type)
			{
				if (in_array($target_type, ['Y', 'N']))
				{
					$config->image_autoconv[$source_type] = tobool($target_type);
				}
				elseif (in_array($target_type, ['', 'jpg', 'png', 'webp']))
				{
					$config->image_autoconv[$source_type] = $target_type;
				}
			}
			$config->max_image_width = intval(Context::get('max_image_width')) ?: '';
			$config->max_image_height = intval(Context::get('max_image_height')) ?: '';
			$config->max_image_size_action = Context::get('max_image_size_action') ?: '';
			$config->max_image_size_same_format = strval(Context::get('max_image_size_same_format'));
			$config->max_image_size_admin = Context::get('max_image_size_admin') === 'Y' ? 'Y' : 'N';
			$config->image_quality_adjustment = max(50, min(100, intval(Context::get('image_quality_adjustment'))));
			$config->image_autorotate = Context::get('image_autorotate') === 'Y' ? true : false;
			$config->image_remove_exif_data = Context::get('image_remove_exif_data') === 'Y' ? true : false;
		}

		// Video
		if(!Context::get('use_video_default_file_config'))
		{
			$config->use_video_default_file_config = 'N';
			$config->video_thumbnail = Context::get('video_thumbnail') === 'Y' ? true : false;
			$config->video_mp4_gif_time = intval(Context::get('video_mp4_gif_time'));
		}

		// Set download groups
		$download_grant = Context::get('download_grant');
		$config->download_grant = is_array($download_grant) ? array_values($download_grant) : array($download_grant);

		// Unset pre-conversion type setting #2707
		unset($config->pre_conversion_types);

		// Update
		$oModuleController = getController('module');
		foreach(explode(',', Context::get('target_module_srl')) as $module_srl)
		{
			$output = $oModuleController->insertModulePartConfig('file', trim($module_srl), $config);
			if(!$output->toBool())
			{
				return $output;
			}
		}

		$this->setError(-1);
		$this->setMessage('success_updated', 'info');
		$this->setRedirectUrl(Context::get('success_return_url') ?: getNotEncodedUrl('', 'module', 'admin', 'act', 'dispBoardAdminContent'));
	}

	/**
	 * Add to SESSION file srl
	 *
	 * @return Object
	 */
	function procFileAdminAddCart()
	{
		$file_srl = (int)Context::get('file_srl');
		//$fileSrlList = array(500, 502);

		$oFileModel = getModel('file');
		$output = $oFileModel->getFile($file_srl);
		//$output = $oFileModel->getFile($fileSrlList);

		if($output->file_srl)
		{
			if($_SESSION['file_management'][$output->file_srl]) unset($_SESSION['file_management'][$output->file_srl]);
			else $_SESSION['file_management'][$output->file_srl] = true;
		}
	}

	/**
	 * Edit filename
	 */
	public function procFileAdminEditFileName()
	{
		$file_srl = Context::get('file_srl');
		if (!$file_srl)
		{
			throw new Rhymix\Framework\Exceptions\InvalidRequest;
		}
		$file = FileModel::getFile($file_srl);
		if (!$file)
		{
			throw new Rhymix\Framework\Exceptions\TargetNotFound;
		}
		$file_name = trim(utf8_normalize_spaces(utf8_clean(Context::get('file_name'))));
		$file_name = Rhymix\Framework\Filters\FilenameFilter::clean($file_name);
		if ($file_name === '')
		{
			throw new Rhymix\Framework\Exceptions\InvalidRequest;
		}

		$output = executeQuery('file.updateFileName', [
			'file_srl' => $file_srl,
			'source_filename' => $file_name,
		]);
		if (!$output->toBool())
		{
			return $output;
		}

		$this->setMessage('success_updated');
		$this->setRedirectUrl(Context::get('success_return_url') ?: getNotEncodedUrl(['module' => 'admin', 'act' => 'dispFileAdminEdit', 'file_srl' => $file_srl]));
	}

	/**
	 * Edit image
	 */
	public function procFileAdminEditImage()
	{
		$file_srl = Context::get('file_srl');
		if (!$file_srl)
		{
			throw new Rhymix\Framework\Exceptions\InvalidRequest;
		}
		$file = FileModel::getFile($file_srl);
		if (!$file)
		{
			throw new Rhymix\Framework\Exceptions\TargetNotFound;
		}

		// Validate user input.
		$width = intval(Context::get('new_width'));
		$height = intval(Context::get('new_height'));
		$format = Context::get('format');
		$quality = intval(Context::get('quality'));
		if ($width <= 0 || $height <= 0 || !in_array($format, ['jpg', 'png', 'webp']) || $quality <= 0 || $quality > 100)
		{
			throw new Rhymix\Framework\Exceptions\InvalidRequest;
		}

		// Generate filenames.
		$uploaded_filename = FileHandler::getRealPath($file->uploaded_filename);
		$temp_filename = \RX_BASEDIR . 'files/cache/temp/' . Rhymix\Framework\Security::getRandom(32, 'hex') . '.' . $format;
		$new_filename = preg_replace('/\.[a-z]+$/', '.' . $format, $file->source_filename);
		$del_filename = null;

		// Should this file be moved from binaries to images?
		if (str_starts_with($uploaded_filename, \RX_BASEDIR . 'files/attach/binaries/'))
		{
			$del_filename = $uploaded_filename;
			$uploaded_filename = preg_replace('!/files/attach/binaries/!', '/files/attach/images/', $uploaded_filename, 1) . '.' . $format;
		}

		// Resize the image using GD or ImageMagick.
		$config = FileModel::getFileConfig();
		$result = FileHandler::createImageFile(FileHandler::getRealPath($file->uploaded_filename), $temp_filename, $width, $height, $format, 'fill', $quality);
		if (!$result && !empty($config->magick_command) && Rhymix\Framework\Storage::isExecutable($config->magick_command))
		{
			$temp_dir = dirname($temp_filename);
			if (!Rhymix\Framework\Storage::isDirectory($temp_dir))
			{
				Rhymix\Framework\Storage::createDirectory($temp_dir);
			}
			$command = vsprintf('%s %s -resize %dx%d -quality %d %s %s %s', [
				Rhymix\Framework\Security::sanitize($config->magick_command, 'command'),
				escapeshellarg(FileHandler::getRealPath($file->uploaded_filename)),
				$width, $height, $quality,
				'-auto-orient -strip',
				'-limit memory 64MB -limit map 128MB -limit disk 1GB',
				escapeshellarg($temp_filename),
			]);
			if (!\RX_WINDOWS && isset($config->magick_timeout) && $config->magick_timeout > 0)
			{
				$command = 'timeout -k1 ' . intval($config->magick_timeout) . ' ' . $command;
			}
			@exec($command, $output, $return_var);
			$result = $return_var === 0 ? true : false;
		}

		// If successfully resized, replace original file and update the image size in DB.
		if ($result && Rhymix\Framework\Storage::exists($temp_filename) && filesize($temp_filename) > 0)
		{
			$moved = Rhymix\Framework\Storage::move($temp_filename, $uploaded_filename);
			if (!$moved)
			{
				throw new Rhymix\Framework\Exception(lang('file.msg_image_conversion_failed'));
			}
			if ($del_filename)
			{
				Rhymix\Framework\Storage::delete($del_filename);
			}

			clearstatcache(true, $uploaded_filename);
			$filesize = filesize($uploaded_filename);
			$relative_path = preg_replace('!^' . preg_quote(\RX_BASEDIR, '!') . '!', './', $uploaded_filename, 1);

			$updated = executeQuery('file.updateFile', [
				'file_srl' => $file_srl,
				'module_srl' => $file->module_srl,
				'upload_target_srl' => $file->upload_target_srl,
				'source_filename' => $new_filename,
				'uploaded_filename' => $relative_path,
				'direct_download' => 'Y',
				'mime_type' => Rhymix\Framework\MIME::getTypeByFilename($new_filename),
				'original_type' => $file->original_type ?: $file->mime_type,
				'is_cover' => $file->cover_image,
				'file_size' => $filesize,
				'width' => $width,
				'height' => $height,
			]);
			if (!$updated->toBool())
			{
				return $updated;
			}

			if (isset($config->save_changelog) && $config->save_changelog === 'Y')
			{
				$changelog1 = executeQuery('file.insertFileChangelog', [
					'change_type' => 'D',
					'file_srl' => $file->file_srl,
					'file_size' => $file->file_size,
					'uploaded_filename' => $file->uploaded_filename,
				]);
				if (!$changelog1->toBool())
				{
					return $changelog1;
				}

				$changelog2 = executeQuery('file.insertFileChangelog', [
					'change_type' => 'I',
					'file_srl' => $file->file_srl,
					'file_size' => $filesize,
					'uploaded_filename' => $relative_path,
				]);
				if (!$changelog2->toBool())
				{
					return $changelog2;
				}
			}
		}
		else
		{
			throw new Rhymix\Framework\Exception(lang('file.msg_image_conversion_failed'));
		}

		$this->setMessage(sprintf(lang('file.msg_image_converted'), FileHandler::filesize($file->file_size), FileHandler::filesize($filesize)));
		$this->setRedirectUrl(Context::get('success_return_url') ?: getNotEncodedUrl(['module' => 'admin', 'act' => 'dispFileAdminEdit', 'file_srl' => $file_srl]));
	}
}
/* End of file file.admin.controller.php */
/* Location: ./modules/file/file.admin.controller.php */
