JezK
Edit File: file.php
<?php defined('ABSPATH') || die('Restricted Access'); use AcyMailing\Helpers\ImageHelper; /** * Function to return the number of bytes of a val like 2M */ function acym_bytes(string $val): int { $val = trim($val); if (empty($val)) { return 0; } $last = strtolower($val[strlen($val) - 1]); switch ($last) { case 'g': $val = intval($val) * 1073741824; break; case 'm': $val = intval($val) * 1048576; break; case 'k': $val = intval($val) * 1024; break; } return (int)$val; } //Create a dir and an index.html file inside function acym_createDir(string $dir, bool $report = true, bool $secured = false): bool { if (is_dir($dir)) { return true; } $indexhtml = '<html><body bgcolor="#FFFFFF"></body></html>'; //Create the directory with an index file inside try { $status = acym_createFolder($dir); } catch (Exception $e) { $status = false; } if (!$status) { if ($report) { acym_display('Could not create the directory '.$dir, 'error'); } return false; } try { $status = acym_writeFile($dir.DS.'index.html', $indexhtml); } catch (Exception $e) { $status = false; } if (!$status && $report) { acym_display('Could not create the file '.$dir.DS.'index.html', 'error'); } if ($secured) { try { $htaccess = 'Order deny,allow'."\r\n".'Deny from all'; $status = acym_writeFile($dir.DS.'.htaccess', $htaccess); } catch (Exception $e) { $status = false; } if (!$status && $report) { acym_display('Could not create the file '.$dir.DS.'.htaccess', 'error'); } } return $status; } function acym_importFile(array $file, string $uploadPath, bool $onlyPict): ?string { // Check the token... no import without token! wp_verify_nonce(acym_getVar('cmd', '_wpnonce'), 'acymnonce') || die('Invalid Token'); $config = acym_config(); $additionalMsg = ''; if ($file['error'] > 0) { $file['error'] = intval($file['error']); if ($file['error'] > 8) { $file['error'] = 0; } $phpFileUploadErrors = [ 0 => 'Unknown error', 1 => 'The uploaded file exceeds the upload_max_filesize directive in php.ini', 2 => 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form', 3 => 'The uploaded file was only partially uploaded', 4 => 'No file was uploaded', 6 => 'Missing a temporary folder', 7 => 'Failed to write file to disk', 8 => 'A PHP extension stopped the file upload', ]; acym_enqueueMessage(acym_translationSprintf('ACYM_ERROR_UPLOADING_FILE_X', $phpFileUploadErrors[$file['error']]), 'error'); return null; } acym_createDir($uploadPath, true); if (!acym_isWritable($uploadPath)) { acym_chmod($uploadPath, 0755); if (!acym_isWritable($uploadPath)) { acym_display(acym_translationSprintf('ACYM_WRITABLE_FOLDER', $uploadPath), 'error'); return null; } } if ($onlyPict) { $allowedExtensions = ['png', 'jpeg', 'jpg', 'gif', 'ico', 'bmp']; } else { $allowedExtensions = explode(',', $config->get('allowed_files')); } // We don't allow to upload anything else than a picture extension if (!preg_match('#\.('.implode('|', $allowedExtensions).')$#Ui', $file['name'], $extension)) { $ext = substr($file['name'], strrpos($file['name'], '.') + 1); acym_display( acym_translationSprintf( 'ACYM_ACCEPTED_TYPE', esc_html($ext), implode(', ', $allowedExtensions) ), 'error' ); return null; } // We will never allow some files to be uploaded... // This should never happen... only if there is an hack tentative so no need to translate this error message if (preg_match('#\.(php.?|.?htm.?|pl|py|jsp|asp|sh|cgi)#Ui', $file['name'])) { acym_display( 'This extension name is blocked by the system regardless your configuration for security reasons', 'error' ); return null; } // We remove all dots or space from the file name to avoid the double extension security issue and the fact some mail clients don't like spaces $file['name'] = str_replace( ['-', '.', ' ', '\''], '_', substr($file['name'], 0, strrpos($file['name'], '.')) ).'.'.$extension[1]; if ($onlyPict) { // Extra security: we make sure we only have a valid picture format $imageSize = @getimagesize($file['tmp_name']); if (empty($imageSize)) { acym_display(acym_translation('ACYM_INVALID_IMAGE'), 'error'); return null; } } if (file_exists($uploadPath.DS.$file['name'])) { $i = 1; $nameFile = preg_replace('/\\.[^.\\s]{3,4}$/', '', $file['name']); $ext = substr($file['name'], strrpos($file['name'], '.') + 1); while (file_exists($uploadPath.DS.$nameFile.'_'.$i.'.'.$ext)) { $i++; } $file['name'] = $nameFile.'_'.$i.'.'.$ext; $additionalMsg = '<br />'.acym_translationSprintf('ACYM_FILE_RENAMED', $file['name']); if ($onlyPict) { $additionalMsg .= '<br /><a style="color: blue; cursor: pointer;" onclick="confirmBox(\'rename\', \''.$file['name'].'\', \''.$nameFile.'.'.$ext.'\')">'.acym_translation( 'ACYM_RENAME_OR_REPLACE' ).'</a>'; } } if (!acym_uploadFile($file['tmp_name'], rtrim($uploadPath, DS).DS.$file['name'])) { acym_display( acym_translationSprintf( 'ACYM_FAIL_UPLOAD', '<b><i>'.esc_html($file['tmp_name']).'</i></b>', '<b><i>'.esc_html(rtrim($uploadPath, DS).DS.$file['name']).'</i></b>' ), 'error' ); return null; } if ($onlyPict && $imageSize[0] > 1000) { $imageHelper = new ImageHelper(); if ($imageHelper->available()) { $imageHelper->maxHeight = 9999; $imageHelper->maxWidth = 700; $message = 'ACYM_IMAGE_RESIZED'; $imageHelper->destination = $uploadPath; $thumb = $imageHelper->generateThumbnail(rtrim($uploadPath, DS).DS.$file['name']); if (!empty($thumb)) { acym_moveFile($thumb['file'], $uploadPath.DS.$file['name']); $additionalMsg .= '<br />'.acym_translation($message); } } } acym_enqueueMessage(acym_translation('ACYM_SUCCESS_FILE_UPLOAD').$additionalMsg, 'success'); return $file['name']; } function acym_inputFile( string $name, string $value = '', string $class = '', array $attributes = [], string $downloadUrl = '' ): void { $hasValue = !empty($value); $noFileText = acym_translation('ACYM_NO_FILE_CHOSEN'); $baseName = substr($name, -2) === '[]' ? substr($name, 0, -2) : $name; ?> <div class="cell <?php echo esc_attr($class); ?> grid-x acym__input__file__container"> <input type="hidden" name="<?php echo esc_attr($baseName); ?>" value="<?php echo esc_attr($value); ?>" class="acym__input__file__clear"> <input <?php foreach ($attributes as $oneAttribute => $oneValue) { if (is_array($oneValue) || is_object($oneValue)) { $oneValue = json_encode($oneValue); } elseif ($oneValue === true) { $oneValue = $oneAttribute; } echo ' '.esc_attr($oneAttribute).'="'.esc_attr($oneValue).'"'; } ?> style="display: none" type="file" name="<?php echo esc_attr($name); ?>"> <button type="button" class="acym__button__file button button-secondary cell shrink"> <?php echo esc_html(acym_translation('ACYM_CHOOSE_FILE')); ?> </button> <span class="cell shrink margin-left-2 margin-right-2 acym__input__file__name" data-no-file="<?php echo esc_attr($noFileText); ?>"> <?php echo esc_html($hasValue ? $value : $noFileText); ?> </span> <i class="acym__input__file__download cell shrink acym__color__blue cursor-pointer acymicon-download margin-right-1" <?php echo $hasValue ? '' : ' style="display: none;"'; ?> <?php echo !empty($downloadUrl) ? ' data-download-url="'.esc_url($downloadUrl).'"' : ''; ?> title="<?php echo esc_attr(acym_translation('ACYM_DOWNLOAD')); ?>" aria-label="<?php echo esc_attr(acym_translation('ACYM_DOWNLOAD')); ?>"></i> <i class="acymicon-close acym__color__red acym__input__file__delete cursor-pointer cell shrink margin-left-1" <?php echo $hasValue ? '' : ' style="display: none;"'; ?> title="<?php echo esc_attr(acym_translation('ACYM_DELETE')); ?>" aria-label="<?php echo esc_attr(acym_translation('ACYM_DELETE')); ?>"></i> </div> <?php } function acym_getFilesFolder(bool $ignoreVariables = false): string { $config = acym_config(); $uploadFolder = $config->get('uploadfolder', ACYM_UPLOAD_FOLDER); if ($ignoreVariables) { $uploadFolder = str_replace(['{userid}', '{groupname}', '{year}', '{month}', '{day}'], '', $uploadFolder); } $uploadFolder = trim($uploadFolder, '/'); $uploadFolder = str_replace('{userid}', acym_currentUserId(), $uploadFolder); $uploadFolder = str_replace('{year}', gmdate('Y'), $uploadFolder); $uploadFolder = str_replace('{month}', gmdate('m'), $uploadFolder); $uploadFolder = str_replace('{day}', gmdate('d'), $uploadFolder); return acym_replaceGroupTags($uploadFolder); } /** * Find all sub folders... * Return an array of all folders ordered properly */ function acym_generateArborescence(array $folders): array { //Recursive algorythm to catch subfolders $folderList = []; foreach ($folders as $folder) { $folderPath = acym_cleanPath(ACYM_ROOT.trim(str_replace('/', DS, trim($folder)), DS)); if (!file_exists($folderPath)) { acym_createDir($folderPath); } $subFolders = acym_listFolderTree($folderPath, '', 15); $folderList[$folder] = []; foreach ($subFolders as $oneFolder) { $subFolder = str_replace(ACYM_ROOT, '', $oneFolder['relname']); $subFolder = str_replace(DS, '/', $subFolder); $folderList[$folder][$subFolder] = ltrim($subFolder, '/'); } $folderList[$folder] = array_unique($folderList[$folder]); } return $folderList; } function acym_makeSafeFile(string $file): string { $file = rtrim($file, '.'); $regex = ['#(\.){2,}#', '#[^A-Za-z0-9\.\_\- ]#', '#^\.#']; return trim(preg_replace($regex, '', $file)); } function acym_getFolders( string $path, string $filter = '.', bool $recurse = false, bool $full = false, array $exclude = ['.svn', 'CVS', '.DS_Store', '__MACOSX'], array $excludefilter = ['^\..*'] ): array { $path = acym_cleanPath($path); if (!is_dir($path)) { acym_enqueueMessage(acym_translationSprintf('ACYM_IS_NOT_A_FOLDER', $path), 'error'); return []; } if (count($excludefilter)) { $excludefilter_string = '/('.implode('|', $excludefilter).')/'; } else { $excludefilter_string = ''; } $arr = acym_getItems($path, $filter, $recurse, $full, $exclude, $excludefilter_string, false); asort($arr); return array_values($arr); } function acym_getFiles( string $path, string $filter = '.', bool $recurse = false, bool $full = false, array $exclude = ['.svn', 'CVS', '.DS_Store', '__MACOSX'], array $excludefilter = [ '^\..*', '.*~', ], bool $naturalSort = false ): array { $path = acym_cleanPath($path); if (!is_dir($path)) { acym_enqueueMessage(acym_translationSprintf('ACYM_IS_NOT_A_FOLDER', $path), 'error'); return []; } if (count($excludefilter)) { $excludefilter_string = '/('.implode('|', $excludefilter).')/'; } else { $excludefilter_string = ''; } $arr = acym_getItems($path, $filter, $recurse, $full, $exclude, $excludefilter_string, true); if ($naturalSort) { natsort($arr); } else { asort($arr); } return array_values($arr); } function acym_getItems( string $path, string $filter, bool $recurse, bool $full, array $exclude, string $excludefilter_string, bool $findfiles ): array { $arr = []; if (!($handle = @opendir($path))) { return $arr; } while (($file = readdir($handle)) !== false) { if ( $file == '.' || $file == '..' || in_array($file, $exclude) || (!empty($excludefilter_string) && preg_match($excludefilter_string, $file)) ) { continue; } $fullpath = rtrim($path, '/').'/'.$file; $isDir = is_dir($fullpath); if (($isDir xor $findfiles) && preg_match("/$filter/", $file)) { if ($full) { $arr[] = $fullpath; } else { $arr[] = $file; } } if ($isDir && $recurse) { $arr = array_merge( $arr, acym_getItems( $fullpath, $filter, true, $full, $exclude, $excludefilter_string, $findfiles ) ); } } closedir($handle); return $arr; } function acym_copyFolder(string $src, string $dest, ?string $path = null, bool $force = false, bool $use_streams = false): bool { if (!empty($path)) { $src = acym_cleanPath($path.'/'.$src); $dest = acym_cleanPath($path.'/'.$dest); } $src = rtrim($src, DIRECTORY_SEPARATOR); $dest = rtrim($dest, DIRECTORY_SEPARATOR); if (!file_exists($src)) { acym_enqueueMessage(acym_translationSprintf('ACYM_FOLDER_DOES_NOT_EXIST', $src), 'error'); return false; } if (file_exists($dest) && !$force) { acym_enqueueMessage(acym_translationSprintf('ACYM_FOLDER_ALREADY_EXIST', $dest), 'error'); return true; } if (!acym_createFolder($dest)) { acym_enqueueMessage(acym_translation('ACYM_CANNOT_CREATE_DESTINATION_FOLDER'), 'error'); return false; } if (!($dh = @opendir($src))) { acym_enqueueMessage(acym_translation('ACYM_CANNOT_OPEN_SOURCE_FOLDER'), 'error'); return false; } while (($file = readdir($dh)) !== false) { $sfid = $src.'/'.$file; $dfid = $dest.'/'.$file; switch (filetype($sfid)) { case 'dir': if ($file != '.' && $file != '..') { $ret = acym_copyFolder($sfid, $dfid, null, $force, $use_streams); if ($ret !== true) { return $ret; } } break; case 'file': if (!@copy($sfid, $dfid)) { acym_enqueueMessage(acym_translationSprintf('ACYM_COPY_FILE_FAILED_PERMISSION', $sfid), 'error'); return false; } break; } } return true; } function acym_listFolderTree(string $path, string $filter, int $maxLevel = 3, int $level = 0, int $parent = 0): array { $dirs = []; if ($level === 0) { $GLOBALS['acym_folder_tree_index'] = 0; } if ($level < $maxLevel) { $folders = acym_getFolders($path, $filter); foreach ($folders as $name) { $id = ++$GLOBALS['acym_folder_tree_index']; $fullName = acym_cleanPath($path.'/'.$name); $dirs[] = [ 'id' => $id, 'parent' => $parent, 'name' => $name, 'fullname' => $fullName, 'relname' => str_replace(ACYM_ROOT, '', $fullName), ]; $dirs2 = acym_listFolderTree($fullName, $filter, $maxLevel, $level + 1, $id); $dirs = array_merge($dirs, $dirs2); } } return $dirs; } /** * @param mixed $buffer */ function acym_writeFile(string $file, $buffer, int $flags = 0): bool { if (!file_exists(dirname($file)) && !acym_createFolder(dirname($file))) { return false; } $file = acym_cleanPath($file); return is_int(file_put_contents($file, $buffer, $flags)); } function acym_copyFile(string $src, string $dest, string $path = ''): bool { if (!empty($path)) { $src = acym_cleanPath($path.DS.$src); $dest = acym_cleanPath($path.DS.$dest); } if (!is_readable($src)) { acym_enqueueMessage(acym_translationSprintf('ACYM_COULD_NOT_FIND_FILE_SOURCE_PERMISSION', $src), 'error'); return false; } if (!@copy($src, $dest)) { acym_enqueueMessage(acym_translationSprintf('ACYM_COULD_NOT_COPY_FILE_X_TO_X', $src, $dest), 'error'); return false; } return true; } function acym_fileGetExt(string $file): string { $pathInfo = pathinfo($file); return empty($pathInfo['extension']) ? '' : strtolower($pathInfo['extension']); } function acym_cleanPath(string $path, string $ds = DIRECTORY_SEPARATOR): string { $path = trim($path); if (empty($path)) { $path = ACYM_ROOT; } elseif (($ds === '\\') && substr($path, 0, 2) == '\\\\') { $path = "\\".preg_replace('#[/\\\\]+#', $ds, $path); } else { $path = preg_replace('#[/\\\\]+#', $ds, $path); } return $path; } function acym_createArchive(string $name, array $files): bool { $contents = []; $ctrldir = []; $timearray = getdate(); $dostime = (($timearray['year'] - 1980) << 25) | ($timearray['mon'] << 21) | ($timearray['mday'] << 16) | ($timearray['hours'] << 11) | ($timearray['minutes'] << 5) | ($timearray['seconds'] >> 1); $dtime = dechex($dostime); $hexdtime = chr(hexdec($dtime[6].$dtime[7])).chr(hexdec($dtime[4].$dtime[5])).chr(hexdec($dtime[2].$dtime[3])).chr( hexdec($dtime[0].$dtime[1]) ); foreach ($files as $file) { $data = $file['data']; $filename = str_replace('\\', '/', $file['name']); $fr = "\x50\x4b\x03\x04\x14\x00\x00\x00\x08\x00".$hexdtime; $unc_len = strlen($data); $crc = crc32($data); $zdata = gzcompress($data); $zdata = substr(substr($zdata, 0, strlen($zdata) - 4), 2); $c_len = strlen($zdata); $fr .= pack('V', $crc).pack('V', $c_len).pack('V', $unc_len).pack('v', strlen($filename)).pack( 'v', 0 ).$filename.$zdata; $old_offset = strlen(implode('', $contents)); $contents[] = $fr; $cdrec = "\x50\x4b\x01\x02\x00\x00\x14\x00\x00\x00\x08\x00".$hexdtime; $cdrec .= pack('V', $crc).pack('V', $c_len).pack('V', $unc_len).pack('v', strlen($filename)).pack('v', 0).pack( 'v', 0 ).pack('v', 0).pack('v', 0).pack('V', 32).pack('V', $old_offset).$filename; $ctrldir[] = $cdrec; } $data = implode('', $contents); $dir = implode('', $ctrldir); $buffer = $data.$dir."\x50\x4b\x05\x06\x00\x00\x00\x00".pack('v', count($ctrldir)).pack('v', count($ctrldir)).pack( 'V', strlen($dir) ).pack('V', strlen($data))."\x00\x00"; return acym_writeFile($name.'.zip', $buffer); } function acym_loaderLogo(bool $withContainer = true): void { if ($withContainer) { echo '<div class="cell shrink acym_loader_logo">'; } ?> <svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="80" height="80" viewBox="0 0 1024 1024"> <path class="acym__svg__loader" fill="#a6a9ab" d="M553.074 174.168c-12.201 7.319-26.84 10.573-40.668 10.573s-27.655-3.253-40.668-10.573l-242.376-139.081-229.362 132.575v732.011l254.576 143.963v-430.26l219.602 124.442c11.388 6.507 24.401 9.76 37.415 9.76 0 0 0 0 0 0s0 0 0 0c12.201 0 25.214-3.253 36.6-9.76l221.23-124.442v430.26l254.576-144.775v-732.011l-229.362-131.762-241.563 139.081zM491.261 701.215l-217.164-122.815-61.001-34.161v430.26l-173.243-97.601v-662.063l422.94 245.629c0 0 0 0 0 0 8.947 4.881 17.895 8.135 27.655 10.573v230.178zM983.334 876.086l-173.243 98.416v-431.073l-61.001 34.161-217.164 122.815v-229.362c9.76-2.441 18.707-5.694 27.655-10.573l423.753-246.444v662.063zM539.246 425.493c-17.080 9.76-38.227 9.76-55.307 0 0 0 0 0 0 0l-422.94-245.629 168.362-97.601 222.043 127.696c18.707 10.573 39.853 16.267 61.001 16.267s42.294-5.694 61.001-16.267l222.043-127.696 168.362 97.601-424.566 245.629z"></path> </svg> <?php if ($withContainer) { echo '</div>'; } } function acym_fileNameValid(string $filename): bool { if (empty($filename)) return false; if (strpos($filename, '..') !== false || strpos($filename, '/') !== false || strpos($filename, '\\') !== false) return false; return true; } function acym_getImageFileExtensions(bool $allowSvg = false): array { $imageFormats = ['jpg', 'jpeg', 'png', 'gif', 'ico', 'bmp']; if ($allowSvg) { $imageFormats[] = 'svg'; } return $imageFormats; } function acym_isSvgFileSafe(string $path): bool { if (!is_file($path)) { return false; } $content = @file_get_contents($path); if ($content === false || trim($content) === '') { return false; } // Decode HTML entities so obfuscated payloads (e.g. "javascript:") are caught, and drop NULL bytes. $content = str_replace("\0", '', html_entity_decode($content, ENT_QUOTES | ENT_HTML5)); $dangerousPatterns = [ '#<\s*script#i', '#<\s*(foreignObject|iframe|embed|object|audio|video|handler|listener)#i', '#\son[a-z]+\s*=#i', '#javascript\s*:#i', '#data\s*:\s*text/html#i', '#<!\s*(DOCTYPE|ENTITY)#i', ]; foreach ($dangerousPatterns as $pattern) { if (preg_match($pattern, $content)) { return false; } } return true; }