* Resolve a logical folder key (e.g. "root", "invoices/2025") to a * real path under UPLOAD_DIR, enforce REGEX_FOLDER_NAME, and ensure * optional creation. * * @param string $folder * @param bool $create * @return array [string|null $realPath, string|null $error] */
(string $folder, bool $create = true)
| 130 | * @return array [string|null $realPath, string|null $error] |
| 131 | */ |
| 132 | private static function resolveFolderPath(string $folder, bool $create = true): array |
| 133 | { |
| 134 | $folder = trim($folder) ?: 'root'; |
| 135 | |
| 136 | if (strtolower($folder) !== 'root' && !preg_match(REGEX_FOLDER_NAME, $folder)) { |
| 137 | return [null, "Invalid folder name."]; |
| 138 | } |
| 139 | |
| 140 | $storage = self::storage(); |
| 141 | $activeSourceId = class_exists('SourceContext') ? SourceContext::getActiveId() : ''; |
| 142 | if (!$storage->isLocal()) { |
| 143 | try { |
| 144 | if (FolderCrypto::isEncryptedOrAncestor($folder)) { |
| 145 | return ['success' => false, 'error' => 'Encrypted folders are not supported for remote storage.', 'code' => 400]; |
| 146 | } |
| 147 | } catch (\Throwable $e) { |
| 148 | /* ignore */ |
| 149 | } |
| 150 | } |
| 151 | $isLocal = $storage->isLocal(); |
| 152 | |
| 153 | $root = self::uploadRoot(); |
| 154 | $base = $isLocal ? realpath($root) : rtrim($root, '/\\'); |
| 155 | if ($base === false || $base === '') { |
| 156 | return [null, "Server misconfiguration."]; |
| 157 | } |
| 158 | |
| 159 | if (!$isLocal && strpos($folder, '..') !== false) { |
| 160 | return [null, "Invalid folder name."]; |
| 161 | } |
| 162 | |
| 163 | $dir = (strtolower($folder) === 'root') |
| 164 | ? $base |
| 165 | : $base . DIRECTORY_SEPARATOR . trim($folder, "/\\ "); |
| 166 | |
| 167 | if ($create) { |
| 168 | $st = $storage->stat($dir); |
| 169 | if ($st === null || $st['type'] !== 'dir') { |
| 170 | if (!$storage->mkdir($dir, 0775, true)) { |
| 171 | return [null, "Cannot create destination folder"]; |
| 172 | } |
| 173 | } |
| 174 | } |
| 175 | |
| 176 | if ($isLocal) { |
| 177 | $real = realpath($dir); |
| 178 | if ($real === false || strpos($real, $base) !== 0) { |
| 179 | return [null, "Invalid folder path."]; |
| 180 | } |
| 181 | return [$real, null]; |
| 182 | } |
| 183 | |
| 184 | return [$dir, null]; |
| 185 | } |
| 186 | |
| 187 | private static function resolveFolderPathForAdapter( |
| 188 | StorageAdapterInterface $storage, |
no test coverage detected