0a1,97
> <?php
> 
> /**
>  * @package          
>  * @copyright        Uwe Jacobsen <uwe@jacobi22.com>
>  * @author           $Author$
>  * @license          GNU General Public License 2.0
>  * @version          
>  * @revision         $Revision$
>  * @since            $Date$
>  * @lastmodified     $Date$
>  */
> 
> 
> // /framework/helpers/SecureFileHandler.php
> 
> namespace bin\helpers;
> 
> class SecureFileHandler
> {
>     private $wb;
>     private $allowedTypes = ['image/gif', 'image/png', 'image/jpeg', 'image/webp'];
> 
>     public function __construct(\frontend $wb)
>     {
>         $this->wb = $wb;
>     }
> 
>     public function handleRequest()
>     {
>         // 1. Authentifizierung prüfen
>         if (!$this->wb->is_authenticated()) {
>             $this->sendForbidden();
>             return;
>         }
> 
>         // 2. Dateinamen holen und bereinigen
>         if (empty($_GET['file'])) {
>             $this->sendBadRequest('No file specified.');
>             return;
>         }
>         $fileName = basename(urldecode($_GET['file']));
> 
>         // 3. Pfad dynamisch und sicher zusammensetzen
>         // HIER IST DIE MAGIE: Wir holen uns den Namen des Media-Verzeichnisses aus der Konfiguration!
>         $mediaPath = WB_PATH . MEDIA_DIRECTORY; // MEDIA_DIRECTORY kommt aus der config.php
>         $privatePath = $mediaPath . '/private/';
>         
>         $realBasePath = realpath($privatePath);
>         $realFilePath = realpath($privatePath . $fileName);
> 
>         // 4. Sicherheits- und Existenzprüfung
>         if ($realFilePath === false || strpos($realFilePath, $realBasePath) !== 0) {
>             $this->sendNotFound();
>             return;
>         }
> 
>         // 5. MIME-Typ-Prüfung
>         $mimeType = mime_content_type($realFilePath);
>         if (in_array($mimeType, $this->allowedTypes)) {
>             $this->sendFile($realFilePath);
>         } else {
>             $this->sendForbidden('File type not allowed.');
>         }
>     }
> 
>     private function sendFile($filePath)
>     {
>         header("Content-Type: " . mime_content_type($filePath));
>         header("Content-Length: " . filesize($filePath));
>         header("Content-Disposition: inline; filename=\"" . basename($filePath) . "\"");
>         header("Cache-Control: private, no-cache, must-revalidate");
>         header("Pragma: no-cache");
>         ob_clean();
>         flush();
>         readfile($filePath);
>         exit();
>     }
> 
>     private function sendForbidden($message = 'Access Denied.')
>     {
>         header('HTTP/1.0 403 Forbidden', TRUE, 403);
>         die($message);
>     }
>     
>     private function sendNotFound()
>     {
>         header("HTTP/1.0 404 Not Found");
>         die("File not found.");
>     }
> 
>     private function sendBadRequest($message)
>     {
>         header("HTTP/1.0 400 Bad Request");
>         die($message);
>     }
> }
\ No newline at end of file
12,14c12,14
<  * @version         $Id: class.frontend.php 4 2025-08-08 07:53:04Z Uwe $
<  * @filesource      $HeadURL: file:///G:/SVN_Projekte/WB_Entwicklung/branches/WB_Neuentwicklung/framework/class.frontend.php $
<  * @lastmodified    $Date: 2025-08-08 09:53:04 +0200 (Fr, 08 Aug 2025) $
---
>  * @version         $Id: class.frontend.php 1 2019-07-13 20:51:57Z root $
>  * @filesource      $HeadURL: svn://svn.websitebaker.org/wb/2.13.x/branches/main/framework/class.frontend.php $
>  * @lastmodified    $Date: 2019-07-13 22:51:57 +0200 (Sa, 13. Jul 2019) $
225c225
<              // Check if the page language is also the selected language. If not, send headers again.
---
> //            \trigger_error(\sprintf('[%03d] page[language] %s != language %s ',__LINE__, $this->page['language'],$this->oReg->Language),E_USER_NOTICE);
227,228c227,228
<                 if (defined($bHeaderOldLocation) && $bHeaderOldLocation != false) {
<                     $sUri = $this->page_link($this->page['link']).'?lang=' . $this->page['language'];
---
>                 if ($bHeaderOldLocation) {
> /* */
230,231c230
<                         // check if there is an query-string
<                         header('Location: ' . $sUri . '&' . $_SERVER['QUERY_STRING']);
---
>                         header('Location: '.$this->page_link($this->page['link']).'?'.$_SERVER['QUERY_STRING'].'&lang='.$this->page['language']);
233c232
<                         header('Location: ' . $sUri);
---
>                         header('Location: '.$this->page_link($this->page['link']).'?lang='.$this->page['language']);
235d233
<                     exit();
238c236,245
<                     header("Refresh:0");
---
> //  check if there is an query-string from shorturl
>                     if ($this->oRequest->issetParam('_wb')) {
>                         $sLocation = $this->oRequest->getParam('_wb');
> //        \trigger_error(\sprintf('[%03d] $sLocation %s',__LINE__, $sLocation),E_USER_NOTICE);
>                         \header('Location: '.$this->oReg->AppUrl.$sLocation);
>                     } else {
>                         $sLocation = $this->page_link($this->page['link']);
> //        \trigger_error(\sprintf('[%03d] sLocation -> %s',__LINE__, $sLocation),E_USER_NOTICE);
>                         \header('Location: '.$sLocation);
>                     }
239a247
>                 exit();
422a431,472
>     }
>     
>     public function handleSecureFileRequest($requestedFile)
>     {
>         // 1. Authentifizierung prüfen
>         if (!$this->is_authenticated()) {
>             header('HTTP/1.0 403 Forbidden', TRUE, 403);
>             die('Access Denied.');
>         }
> 
>         // 2. Dateinamen bereinigen (Schutz vor Path Traversal)
>         $fileName = basename(urldecode($requestedFile));
> 
>         // 3. Pfad dynamisch und sicher aus der CMS-Konfiguration zusammensetzen
>         $mediaPath = WB_PATH . MEDIA_DIRECTORY;
>         $privatePath = $mediaPath . '/private/';
> 
>         $realBasePath = realpath($privatePath);
>         $realFilePath = realpath($privatePath . $fileName);
> 
>         // 4. Sicherheits- und Existenzprüfung
>         if ($realFilePath === false || strpos($realFilePath, $realBasePath) !== 0) {
>             header("HTTP/1.0 404 Not Found");
>             die("File not found.");
>         }
> 
>         // 5. MIME-Typ-Prüfung (Whitelist)
>         $allowedTypes = ['image/gif', 'image/png', 'image/jpeg', 'image/webp'];
>         if (in_array(mime_content_type($realFilePath), $allowedTypes)) {
>             header("Content-Type: " . mime_content_type($realFilePath));
>             header("Content-Length: " . filesize($realFilePath));
>             header("Content-Disposition: inline; filename=\"" . $fileName . "\"");
>             header("Cache-Control: private, no-cache, must-revalidate");
>             header("Pragma: no-cache");
>             ob_clean();
>             flush();
>             readfile($realFilePath);
>             exit();
>         } else {
>             header('HTTP/1.0 403 Forbidden', TRUE, 403);
>             die('File type not allowed.');
>         }
28c28
<  * @revision     $Id: class.wb.php 4 2025-08-08 07:53:04Z Uwe $
---
>  * @revision     $Id: class.wb.php 15 2020-08-22 12:14:23Z Manuela $
1332c1332
<     public function ReplaceAbsoluteMediaUrl($sContent,$sUrl=null,$sMedia=null)
---
>     public function ReplaceAbsoluteMediaUrl($sContent, $sUrl = null, $sMedia = null)
1334,1367c1334,1335
<         if (is_string($sContent)) {
<             if (is_null($sUrl))
<             {
<                 $AppUrl  = $this->oReg->AppUrl;
<             }
<             else
<             {
<                 $AppUrl = rtrim($sUrl,'/\\').'/';
<             }
<             if (is_null($sMedia))
<             {
<                 $MediaDir  = $this->oReg->MediaDir;
<             }
<             else
<             {
<                 $MediaDir= ($sMedia ?? $oReg->MediaDir);
<             }
< 
<             $sRelUrl = preg_replace('/^https?:\/\/[^\/]+(.*)/is', '\1', $AppUrl);
<             $sDocumentRootUrl = str_replace($sRelUrl, '', $AppUrl);
<             $sMediaUrl = $AppUrl.$MediaDir.'';
<             $aSearchfor = [
<                 '@(<[^>]*=\s*")('.preg_quote($sMediaUrl).
<                 ')([^">]*".*>)@siU', '@(<[^>]*=\s*")('.preg_quote($AppUrl.'').')([^">]*".*>)@siU',
<                 '/(<[^>]*?=\s*\")(\/+)([^\"]*?\"[^>]*?)/isU',
<                 '/(<[^>]*=\s*")('.preg_quote($sMediaUrl, '/').')([^">]*".*>)/siU'
<             ];
<             $aReplacements = [
<                 '$1{SYSVAR:AppUrl.MediaDir}$3',
<                 '$1{SYSVAR:AppUrl}$3',
<                 '\1'.$sDocumentRootUrl.'/\3',
<                 '$1{SYSVAR:MEDIA_REL}$3'
<             ];
<             $sContent = preg_replace( $aSearchfor, $aReplacements, ($sContent));
---
>         if (!is_string($sContent)) {
>             return $sContent;
1368a1337,1373
> 
>         // Konfiguration
>         $AppUrl = is_null($sUrl) ? $this->oReg->AppUrl : rtrim($sUrl, '/\\') . '/';
>         $MediaDir = is_null($sMedia) ? $this->oReg->MediaDir : ($sMedia ?? $this->oReg->MediaDir);
>         $sMediaUrl = $AppUrl . trim($MediaDir, '/') . '/';
>         $sPrivateFolderName = 'private'; // Der Name deines geschützten Ordners
> 
>         // Das ist der "magische" Teil: preg_replace_callback
>         // Wir suchen nach JEDEM Link, der ins /media-Verzeichnis zeigt.
>         $sPattern = '@(<[^>]*?(?:src|href)\s*=\s*")(' . preg_quote($sMediaUrl, '@') . ')([^"]*)(".*?>)@siU';
> 
>         $sContent = preg_replace_callback(
>             $sPattern,
>             function ($matches) use ($sPrivateFolderName) {
>                 // $matches[3] enthält den relativen Pfad innerhalb von /media,
>                 // z.B. "public/logo.png" oder "private/geheim.jpg"
>                 $relativePath = $matches[3];
> 
>                 // Die ENTSCHEIDUNG: Ist es ein privater oder öffentlicher Link?
>                 if (strpos($relativePath, $sPrivateFolderName . '/') === 0) {
>                     // JA, es ist eine private Datei.
>                     // Entferne den Ordnernamen 'private/' aus dem Pfad für den Platzhalter.
>                     $fileIdentifier = substr($relativePath, strlen($sPrivateFolderName . '/'));
>                     // Baue den SECURE_FILE Platzhalter.
>                     $replacement = '{SECURE_FILE:' . $fileIdentifier . '}';
>                 } else {
>                     // NEIN, es ist eine öffentliche Datei.
>                     // Baue den normalen SYSVAR Platzhalter.
>                     $replacement = '{SYSVAR:MEDIA_REL}' . $relativePath;
>                 }
> 
>                 // Gib den kompletten, ersetzten HTML-Tag zurück.
>                 return $matches[1] . $replacement . $matches[4];
>             },
>             $sContent
>         );
> 
