| @@ -9,12 +9,13 @@ | ||
| 9 | 9 | use UserAccessManager\Cache\Cache; |
| 10 | 10 | use UserAccessManager\Config\MainConfig; |
| 11 | 11 | use UserAccessManager\Config\WordpressConfig; |
| 12 | 12 | use UserAccessManager\Controller\Controller; |
| 13 | +use UserAccessManager\Controller\Frontend\Authentication\LoginControllerTrait; | |
| 13 | 14 | use UserAccessManager\Database\Database; |
| 14 | 15 | use UserAccessManager\File\FileHandler; |
| 15 | -use UserAccessManager\File\FileObject; | |
| 16 | -use UserAccessManager\File\FileObjectFactory; | |
| 16 | +use UserAccessManager\File\Delivery\FileObject; | |
| 17 | +use UserAccessManager\File\Delivery\FileObjectFactory; | |
| 17 | 18 | use UserAccessManager\Object\ObjectHandler; |
| 18 | 19 | use UserAccessManager\UserGroup\UserGroupTypeException; |
| 19 | 20 | use UserAccessManager\Util\Util; |
| 20 | 21 | use UserAccessManager\Wrapper\Php; |
| @@ -24,8 +25,9 @@ | ||
| 24 | 25 | { |
| 25 | 26 | use LoginControllerTrait; |
| 26 | 27 | |
| 27 | 28 | public const POST_URL_CACHE_KEY = 'PostUrls'; |
| 29 | + public const REDIRECT_TO_PARAMETER = 'redirect_to'; | |
| 28 | 30 | |
| 29 | 31 | public function __construct( |
| 30 | 32 | Php $php, |
| 31 | 33 | Wordpress $wordpress, |
| @@ -72,48 +74,126 @@ | ||
| 72 | 74 | |
| 73 | 75 | return $postUrls[$url]; |
| 74 | 76 | } |
| 75 | 77 | |
| 76 | - private function getFileSettingsByType(string $objectType, string $objectUrl): ?FileObject | |
| 78 | + private function normalizeAttachmentUrl(array $uploadDirs, string $objectUrl): string | |
| 77 | 79 | { |
| 78 | - $fileObject = null; | |
| 80 | + $uploadDir = str_replace(ABSPATH, '/', $uploadDirs['basedir']); | |
| 81 | + $regex = '/.*' . str_replace('/', '\/', $uploadDir) . '\//i'; | |
| 82 | + $cleanObjectUrl = preg_replace($regex, '', $objectUrl); | |
| 83 | + $uploadUrl = str_replace('/files', $uploadDir, $uploadDirs['baseurl']); | |
| 79 | 84 | |
| 80 | - if ($objectType === ObjectHandler::ATTACHMENT_OBJECT_TYPE) { | |
| 81 | - $uploadDirs = $this->wordpress->getUploadDir(); | |
| 82 | - $uploadDir = str_replace(ABSPATH, '/', $uploadDirs['basedir']); | |
| 83 | - $regex = '/.*' . str_replace('/', '\/', $uploadDir) . '\//i'; | |
| 84 | - $cleanObjectUrl = preg_replace($regex, '', $objectUrl); | |
| 85 | - $uploadUrl = str_replace('/files', $uploadDir, $uploadDirs['baseurl']); | |
| 86 | - $objectUrl = rtrim($uploadUrl, '/') . '/' . ltrim($cleanObjectUrl, '/'); | |
| 85 | + return rtrim($uploadUrl, '/') . '/' . ltrim($cleanObjectUrl, '/'); | |
| 86 | + } | |
| 87 | 87 | |
| 88 | - $post = $this->objectHandler->getPost($this->getPostIdByUrl($objectUrl)); | |
| 89 | - $postType = $post->post_type ?? ''; | |
| 88 | + private function isRegisteredSize(int $attachmentId, string $fileName): bool | |
| 89 | + { | |
| 90 | + $metaData = $this->wordpress->getAttachmentMetadata($attachmentId); | |
| 91 | + $sizes = is_array($metaData) === true ? (array) ($metaData['sizes'] ?? []) : []; | |
| 90 | 92 | |
| 91 | - if ($postType === ObjectHandler::ATTACHMENT_OBJECT_TYPE) { | |
| 92 | - $multiPath = str_replace('/files', $uploadDir, $uploadDirs['baseurl']); | |
| 93 | + return in_array($fileName, array_column($sizes, 'file'), true); | |
| 94 | + } | |
| 93 | 95 | |
| 94 | - $fileObject = $this->fileObjectFactory->createFileObject( | |
| 95 | - $post->ID, | |
| 96 | - $objectType, | |
| 97 | - $uploadDirs['basedir'] . str_replace($multiPath, '', $objectUrl), | |
| 98 | - $this->wordpress->attachmentIsImage($post->ID) | |
| 99 | - ); | |
| 100 | - } | |
| 101 | - } else { | |
| 102 | - $extraParameter = $this->getRequestParameter('uamextra'); | |
| 96 | + /** | |
| 97 | + * Returns the file the request asks for and tells through $isImage whether it can be shown as image. | |
| 98 | + * A requested generated size is only used if it is registered at the attachment and stored inside the | |
| 99 | + * upload directory, so no arbitrary and no missing file becomes reachable. Generated sizes are always | |
| 100 | + * images, also for documents like PDFs, where they are the preview images shown in the media library. | |
| 101 | + */ | |
| 102 | + private function getAttachmentFile( | |
| 103 | + int $attachmentId, | |
| 104 | + string $attachedFile, | |
| 105 | + string $requestedUrl, | |
| 106 | + string $uploadBaseDir, | |
| 107 | + ?bool &$isImage | |
| 108 | + ): string { | |
| 109 | + $requestedFileName = basename((string) parse_url($requestedUrl, PHP_URL_PATH)); | |
| 110 | + $sizeFile = dirname($attachedFile) . DIRECTORY_SEPARATOR . $requestedFileName; | |
| 103 | 111 | |
| 104 | - $fileObject = $this->wordpress->applyFilters( | |
| 105 | - 'uam_get_file_settings_by_type', | |
| 106 | - $fileObject, | |
| 107 | - $objectType, | |
| 108 | - $objectUrl, | |
| 109 | - $extraParameter | |
| 110 | - ); | |
| 112 | + if ($requestedFileName !== basename($attachedFile) | |
| 113 | + && $this->isRegisteredSize($attachmentId, $requestedFileName) === true | |
| 114 | + && $this->isInsideUploadDirectory($sizeFile, $uploadBaseDir) === true | |
| 115 | + ) { | |
| 116 | + $isImage = true; | |
| 117 | + | |
| 118 | + return $sizeFile; | |
| 111 | 119 | } |
| 112 | 120 | |
| 113 | - return $fileObject; | |
| 121 | + $isImage = $this->wordpress->attachmentIsImage($attachmentId); | |
| 122 | + | |
| 123 | + return $attachedFile; | |
| 114 | 124 | } |
| 115 | 125 | |
| 126 | + private function getAttachmentFileObject(string $objectUrl): ?FileObject | |
| 127 | + { | |
| 128 | + $uploadDirs = $this->wordpress->getUploadDir(); | |
| 129 | + $requestedUrl = $this->normalizeAttachmentUrl($uploadDirs, $objectUrl); | |
| 130 | + $postId = $this->getPostIdByUrl($requestedUrl); | |
| 131 | + | |
| 132 | + if ($postId < 1) { | |
| 133 | + return null; | |
| 134 | + } | |
| 135 | + | |
| 136 | + $post = $this->objectHandler->getPost($postId); | |
| 137 | + | |
| 138 | + if (($post->post_type ?? '') !== ObjectHandler::ATTACHMENT_OBJECT_TYPE) { | |
| 139 | + return null; | |
| 140 | + } | |
| 141 | + | |
| 142 | + // Unfiltered, because the plugin denies the path through the get_attached_file filter for | |
| 143 | + // users without access. Filtered it would hide the file from the access check below, which | |
| 144 | + // then could not answer the request with the no rights page any more. | |
| 145 | + $attachedFile = $this->wordpress->getAttachedFile($post->ID, true); | |
| 146 | + | |
| 147 | + if ($attachedFile === false | |
| 148 | + || $this->isInsideUploadDirectory($attachedFile, $uploadDirs['basedir']) === false | |
| 149 | + ) { | |
| 150 | + return null; | |
| 151 | + } | |
| 152 | + | |
| 153 | + $file = $this->getAttachmentFile( | |
| 154 | + $post->ID, | |
| 155 | + $attachedFile, | |
| 156 | + $requestedUrl, | |
| 157 | + $uploadDirs['basedir'], | |
| 158 | + $isImage | |
| 159 | + ); | |
| 160 | + | |
| 161 | + return $this->fileObjectFactory->createFileObject( | |
| 162 | + $post->ID, | |
| 163 | + ObjectHandler::ATTACHMENT_OBJECT_TYPE, | |
| 164 | + $file, | |
| 165 | + $isImage | |
| 166 | + ); | |
| 167 | + } | |
| 168 | + | |
| 169 | + private function isInsideUploadDirectory(string $file, string $uploadBaseDir): bool | |
| 170 | + { | |
| 171 | + $realFile = $this->php->realpath($file); | |
| 172 | + $realUploadBaseDir = $this->php->realpath($uploadBaseDir); | |
| 173 | + | |
| 174 | + return $realFile !== false | |
| 175 | + && $realUploadBaseDir !== false | |
| 176 | + && str_starts_with($realFile, $realUploadBaseDir . DIRECTORY_SEPARATOR); | |
| 177 | + } | |
| 178 | + | |
| 179 | + private function getFileSettingsByType(string $objectType, string $objectUrl): ?FileObject | |
| 180 | + { | |
| 181 | + if ($objectType === ObjectHandler::ATTACHMENT_OBJECT_TYPE) { | |
| 182 | + return $this->getAttachmentFileObject($objectUrl); | |
| 183 | + } | |
| 184 | + | |
| 185 | + $extraParameter = $this->getRequestParameter('uamextra'); | |
| 186 | + | |
| 187 | + return $this->wordpress->applyFilters( | |
| 188 | + 'uam_get_file_settings_by_type', | |
| 189 | + null, | |
| 190 | + $objectType, | |
| 191 | + $objectUrl, | |
| 192 | + $extraParameter | |
| 193 | + ); | |
| 194 | + } | |
| 195 | + | |
| 116 | 196 | /** |
| 117 | 197 | * @throws UserGroupTypeException |
| 118 | 198 | */ |
| 119 | 199 | public function getFile(string $objectType, string $objectUrl): void |
| @@ -187,8 +267,12 @@ | ||
| 187 | 267 | $url = $this->getRedirectUrlAndPermalink($permalink); |
| 188 | 268 | $currentUrl = $this->util->getCurrentUrl(); |
| 189 | 269 | |
| 190 | 270 | if ($url !== null && $url !== $currentUrl && $permalink !== $currentUrl) { |
| 271 | + if ($this->mainConfig->appendRedirectToParameter() === true) { | |
| 272 | + $url = $this->wordpress->addQueryArg([self::REDIRECT_TO_PARAMETER => $currentUrl], $url); | |
| 273 | + } | |
| 274 | + | |
| 191 | 275 | $this->wordpress->wpRedirect($url); |
| 192 | 276 | $this->php->callExit(); |
| 193 | 277 | } |
| 194 | 278 | } |
| @@ -197,12 +281,12 @@ | ||
| 197 | 281 | { |
| 198 | 282 | $postableTypes = implode('\',\'', $this->objectHandler->getPostTypes()); |
| 199 | 283 | |
| 200 | 284 | $query = $this->database->prepare( |
| 201 | - "SELECT ID | |
| 202 | - FROM {$this->database->getPostsTable()} | |
| 203 | - WHERE post_name = %s | |
| 204 | - AND post_type IN ('$postableTypes')", | |
| 285 | + "SELECT `ID` | |
| 286 | + FROM `{$this->database->getPostsTable()}` | |
| 287 | + WHERE `post_name` = %s | |
| 288 | + AND `post_type` IN ('$postableTypes')", | |
| 205 | 289 | $name |
| 206 | 290 | ); |
| 207 | 291 | |
| 208 | 292 | return (int) $this->database->getVariable($query); |