* getOptions("'option ,, quoted',abd,'2,3',"); * // array { * // 'option ,, quoted', * // 'abc', * // '2,3', * // '', * // } * * * @param string $optionString comma separated options * * @return string[] */ public function getOptions($optionString): array { if ($optionString === '') { return []; } $transformOptions = explode(',', $optionString); $result = []; while (($option = array_shift($transformOptions)) !== null) { $trimmed = trim($option); if (strlen($trimmed) > 1 && $trimmed[0] == "'" && $trimmed[strlen($trimmed) - 1] == "'") { // '...' $option = mb_substr($trimmed, 1, -1); } elseif (isset($trimmed[0]) && $trimmed[0] == "'") { // '..., $trimmed = ltrim($option); $rtrimmed = ''; while (($option = array_shift($transformOptions)) !== null) { // ..., $trimmed .= ',' . $option; $rtrimmed = rtrim($trimmed); if ($rtrimmed[strlen($rtrimmed) - 1] == "'") { // ,...' break; } } $option = mb_substr($rtrimmed, 1, -1); } $result[] = stripslashes($option); } return $result; } /** * Gets all available MIME-types * * @return array array[mimetype], array[transformation] * * @staticvar array $stack */ public function getAvailableMimeTypes(): array { static $stack = null; if ($stack !== null) { return $stack; } $stack = []; $sub_dirs = [ 'Input/' => 'input_', 'Output/' => '', '' => '', ]; foreach ($sub_dirs as $sd => $prefix) { $handle = opendir(ROOT_PATH . 'libraries/classes/Plugins/Transformations/' . $sd); if (! $handle) { $stack[$prefix . 'transformation'] = []; $stack[$prefix . 'transformation_file'] = []; continue; } $filestack = []; while ($file = readdir($handle)) { // Ignore hidden files if ($file[0] === '.') { continue; } // Ignore old plugins (.class in filename) if (str_contains($file, '.class')) { continue; } $filestack[] = $file; } closedir($handle); sort($filestack); foreach ($filestack as $file) { if (preg_match('|^[^.].*_.*_.*\.php$|', $file)) { // File contains transformation functions. $parts = explode('_', str_replace('.php', '', $file)); $mimetype = $parts[0] . '/' . $parts[1]; $stack['mimetype'][$mimetype] = $mimetype; $stack[$prefix . 'transformation'][] = $mimetype . ': ' . $parts[2]; $stack[$prefix . 'transformation_file'][] = $sd . $file; if ($sd === '') { $stack['input_transformation'][] = $mimetype . ': ' . $parts[2]; $stack['input_transformation_file'][] = $sd . $file; } } elseif (preg_match('|^[^.].*\.php$|', $file)) { // File is a plain mimetype, no functions. $base = str_replace('.php', '', $file); if ($base !== 'global') { $mimetype = str_replace('_', '/', $base); $stack['mimetype'][$mimetype] = $mimetype; $stack['empty_mimetype'][$mimetype] = $mimetype; } } } } return $stack; } /** * Returns the class name of the transformation * * @param string $filename transformation file name * * @return string the class name of transformation */ public function getClassName($filename) { // get the transformation class name $class_name = explode('.php', $filename); $class_name = 'PhpMyAdmin\\' . str_replace('/', '\\', mb_substr($class_name[0], 18)); return $class_name; } /** * Returns the description of the transformation * * @param string $file transformation file * * @return string the description of the transformation */ public function getDescription($file) { $include_file = 'libraries/classes/Plugins/Transformations/' . $file; /** @psalm-var class-string $class_name */ $class_name = $this->getClassName($include_file); if (class_exists($class_name)) { return $class_name::getInfo(); } return ''; } /** * Returns the name of the transformation * * @param string $file transformation file * * @return string the name of the transformation */ public function getName($file) { $include_file = 'libraries/classes/Plugins/Transformations/' . $file; /** @psalm-var class-string $class_name */ $class_name = $this->getClassName($include_file); if (class_exists($class_name)) { return $class_name::getName(); } return ''; } /** * Fixups old MIME or transformation name to new one * * - applies some hardcoded fixups * - adds spaces after _ and numbers * - capitalizes words * - removes back spaces * * @param string $value Value to fixup * * @return string */ public function fixUpMime($value) { $value = str_replace( [ 'jpeg', 'png', ], [ 'JPEG', 'PNG', ], $value ); return str_replace( ' ', '', ucwords( (string) preg_replace('/([0-9_]+)/', '$1 ', $value) ) ); } /** * Gets the mimetypes for all columns of a table * * @param string $db the name of the db to check for * @param string $table the name of the table to check for * @param bool $strict whether to include only results having a mimetype set * @param bool $fullName whether to use full column names as the key * * @psalm-return array|null */ public function getMime($db, $table, $strict = false, $fullName = false): ?array { $relation = new Relation($GLOBALS['dbi']); $browserTransformationFeature = $relation->getRelationParameters()->browserTransformationFeature; if ($browserTransformationFeature === null) { return null; } $com_qry = ''; if ($fullName) { $com_qry .= 'SELECT CONCAT(`db_name`, \'.\', `table_name`, \'.\', `column_name`) AS column_name, '; } else { $com_qry = 'SELECT `column_name`, '; } $com_qry .= '`mimetype`, ' . '`transformation`, ' . '`transformation_options`, ' . '`input_transformation`, ' . '`input_transformation_options`' . ' FROM ' . Util::backquote($browserTransformationFeature->database) . '.' . Util::backquote($browserTransformationFeature->columnInfo) . ' WHERE `db_name` = \'' . $GLOBALS['dbi']->escapeString($db) . '\'' . ' AND `table_name` = \'' . $GLOBALS['dbi']->escapeString($table) . '\'' . ' AND ( `mimetype` != \'\'' . (! $strict ? ' OR `transformation` != \'\'' . ' OR `transformation_options` != \'\'' . ' OR `input_transformation` != \'\'' . ' OR `input_transformation_options` != \'\'' : '') . ')'; /** * @psalm-var array $result */ $result = $GLOBALS['dbi']->fetchResult($com_qry, 'column_name', null, DatabaseInterface::CONNECT_CONTROL); foreach ($result as $column => $values) { // convert mimetype to new format (f.e. Text_Plain, etc) $values['mimetype'] = $this->fixUpMime($values['mimetype']); // For transformation of form // output/image_jpeg__inline.inc.php // extract dir part. $dir = explode('/', $values['transformation']); $subdir = ''; if (count($dir) === 2) { $subdir = ucfirst($dir[0]) . '/'; $values['transformation'] = $dir[1]; } $values['transformation'] = $this->fixUpMime($values['transformation']); $values['transformation'] = $subdir . $values['transformation']; $result[$column] = $values; } return $result; } /** * Set a single mimetype to a certain value. * * @param string $db the name of the db * @param string $table the name of the table * @param string $key the name of the column * @param string $mimetype the mimetype of the column * @param string $transformation the transformation of the column * @param string $transformationOpts the transformation options of the column * @param string $inputTransform the input transformation of the column * @param string $inputTransformOpts the input transformation options of the column * @param bool $forcedelete force delete, will erase any existing * comments for this column */ public function setMime( $db, $table, $key, $mimetype, $transformation, $transformationOpts, $inputTransform, $inputTransformOpts, $forcedelete = false ): bool { $relation = new Relation($GLOBALS['dbi']); $browserTransformationFeature = $relation->getRelationParameters()->browserTransformationFeature; if ($browserTransformationFeature === null) { return false; } // lowercase mimetype & transformation $mimetype = mb_strtolower($mimetype); $transformation = mb_strtolower($transformation); // Do we have any parameter to set? $has_value = ( strlen($mimetype) > 0 || strlen($transformation) > 0 || strlen($transformationOpts) > 0 || strlen($inputTransform) > 0 || strlen($inputTransformOpts) > 0 ); $test_qry = ' SELECT `mimetype`, `comment` FROM ' . Util::backquote($browserTransformationFeature->database) . '.' . Util::backquote($browserTransformationFeature->columnInfo) . ' WHERE `db_name` = \'' . $GLOBALS['dbi']->escapeString($db) . '\' AND `table_name` = \'' . $GLOBALS['dbi']->escapeString($table) . '\' AND `column_name` = \'' . $GLOBALS['dbi']->escapeString($key) . '\''; $test_rs = $GLOBALS['dbi']->queryAsControlUser($test_qry); if ($test_rs->numRows() > 0) { $row = $test_rs->fetchAssoc(); if (! $forcedelete && ($has_value || strlen($row['comment']) > 0)) { $upd_query = 'UPDATE ' . Util::backquote($browserTransformationFeature->database) . '.' . Util::backquote($browserTransformationFeature->columnInfo) . ' SET ' . '`mimetype` = \'' . $GLOBALS['dbi']->escapeString($mimetype) . '\', ' . '`transformation` = \'' . $GLOBALS['dbi']->escapeString($transformation) . '\', ' . '`transformation_options` = \'' . $GLOBALS['dbi']->escapeString($transformationOpts) . '\', ' . '`input_transformation` = \'' . $GLOBALS['dbi']->escapeString($inputTransform) . '\', ' . '`input_transformation_options` = \'' . $GLOBALS['dbi']->escapeString($inputTransformOpts) . '\''; } else { $upd_query = 'DELETE FROM ' . Util::backquote($browserTransformationFeature->database) . '.' . Util::backquote($browserTransformationFeature->columnInfo); } $upd_query .= ' WHERE `db_name` = \'' . $GLOBALS['dbi']->escapeString($db) . '\' AND `table_name` = \'' . $GLOBALS['dbi']->escapeString($table) . '\' AND `column_name` = \'' . $GLOBALS['dbi']->escapeString($key) . '\''; } elseif ($has_value) { $upd_query = 'INSERT INTO ' . Util::backquote($browserTransformationFeature->database) . '.' . Util::backquote($browserTransformationFeature->columnInfo) . ' (db_name, table_name, column_name, mimetype, ' . 'transformation, transformation_options, ' . 'input_transformation, input_transformation_options) ' . ' VALUES(' . '\'' . $GLOBALS['dbi']->escapeString($db) . '\',' . '\'' . $GLOBALS['dbi']->escapeString($table) . '\',' . '\'' . $GLOBALS['dbi']->escapeString($key) . '\',' . '\'' . $GLOBALS['dbi']->escapeString($mimetype) . '\',' . '\'' . $GLOBALS['dbi']->escapeString($transformation) . '\',' . '\'' . $GLOBALS['dbi']->escapeString($transformationOpts) . '\',' . '\'' . $GLOBALS['dbi']->escapeString($inputTransform) . '\',' . '\'' . $GLOBALS['dbi']->escapeString($inputTransformOpts) . '\')'; } if (isset($upd_query)) { return (bool) $GLOBALS['dbi']->queryAsControlUser($upd_query); } return false; } /** * GLOBAL Plugin functions */ /** * Delete related transformation details * after deleting database. table or column * * @param string $db Database name * @param string $table Table name * @param string $column Column name */ public function clear($db, $table = '', $column = ''): bool { $relation = new Relation($GLOBALS['dbi']); $browserTransformationFeature = $relation->getRelationParameters()->browserTransformationFeature; if ($browserTransformationFeature === null) { return false; } $delete_sql = 'DELETE FROM ' . Util::backquote($browserTransformationFeature->database) . '.' . Util::backquote($browserTransformationFeature->columnInfo) . ' WHERE '; if (($column != '') && ($table != '')) { $delete_sql .= '`db_name` = \'' . $db . '\' AND ' . '`table_name` = \'' . $table . '\' AND ' . '`column_name` = \'' . $column . '\' '; } elseif ($table != '') { $delete_sql .= '`db_name` = \'' . $db . '\' AND ' . '`table_name` = \'' . $table . '\' '; } else { $delete_sql .= '`db_name` = \'' . $db . '\' '; } return (bool) $GLOBALS['dbi']->tryQuery($delete_sql); } }