diff --git a/lib/moodlelib.php b/lib/moodlelib.php index adc256a5519..0d7a623224e 100644 --- a/lib/moodlelib.php +++ b/lib/moodlelib.php @@ -439,6 +439,11 @@ define('FEATURE_SHOW_DESCRIPTION', 'showdescription'); /** True if module uses the question bank */ define('FEATURE_USES_QUESTIONS', 'usesquestions'); +/** + * Maximum filename char size + */ +define('MAX_FILENAME_SIZE', 90); + /** Unspecified module archetype */ define('MOD_ARCHETYPE_OTHER', 0); /** Resource-like type module */ @@ -986,6 +991,21 @@ function clean_param($param, $type) { if ($param === '.' || $param === '..') { $param = ''; } + // Extract a part of the filename if it's char size exceeds MAX_FILENAME_SIZE. + // If the filename is too long, the file cannot be created on the filesystem due to exceeding max byte size. + // Limiting the filename to a certain size (considering multibyte characters) will prevent this. + if (core_text::strlen($param) > MAX_FILENAME_SIZE) { + // Exclude extension if present in filename. + $mimetypes = get_mimetypes_array(); + $extension = pathinfo($param, PATHINFO_EXTENSION); + if ($extension && !empty($mimetypes[$extension])) { + $basename = pathinfo($param, PATHINFO_FILENAME); + $param = core_text::substr($basename, 0, MAX_FILENAME_SIZE); + $param .= '.' . $extension; + } else { + $param = core_text::substr($param, 0, MAX_FILENAME_SIZE); + } + } return $param; case PARAM_PATH: diff --git a/lib/tests/moodlelib_test.php b/lib/tests/moodlelib_test.php index 9fea78d61cc..454d9cc1939 100644 --- a/lib/tests/moodlelib_test.php +++ b/lib/tests/moodlelib_test.php @@ -682,6 +682,13 @@ class core_moodlelib_testcase extends advanced_testcase { $this->assertSame(' . .dontltrim.me', clean_param(' . .dontltrim.me', PARAM_FILE)); $this->assertSame('here is a tab.txt', clean_param("here is a tab\t.txt", PARAM_FILE)); $this->assertSame('here is a linebreak.txt', clean_param("here is a line\r\nbreak.txt", PARAM_FILE)); + // Test filename that contains more than 90 characters. + $filename = 'sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium totam rem'; + $this->assertSame('sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laud', + clean_param($filename, PARAM_FILE)); + // Filename contains extension. + $this->assertSame('sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laud.zip', + clean_param($filename . '.zip', PARAM_FILE)); // The following behaviours have been maintained although they seem a little odd. $this->assertSame('funnything', clean_param('funny:thing', PARAM_FILE));