]> BookStack Code Mirror - bookstack/blobdiff - app/Http/Controllers/Controller.php
Fixed streamed outputs in more extreme scenarios
[bookstack] / app / Http / Controllers / Controller.php
index 2c4c2df1e384e2b2b433062d82d82aa959c3e1c8..4c979c26a1ac1526f46e9afcf37d9414fb2e5dd9 100644 (file)
@@ -2,16 +2,17 @@
 
 namespace BookStack\Http\Controllers;
 
+use BookStack\Exceptions\NotifyException;
 use BookStack\Facades\Activity;
 use BookStack\Interfaces\Loggable;
 use BookStack\Model;
 use BookStack\Util\WebSafeMimeSniffer;
 use Illuminate\Foundation\Bus\DispatchesJobs;
 use Illuminate\Foundation\Validation\ValidatesRequests;
-use Illuminate\Http\Exceptions\HttpResponseException;
 use Illuminate\Http\JsonResponse;
 use Illuminate\Http\Response;
 use Illuminate\Routing\Controller as BaseController;
+use Symfony\Component\HttpFoundation\StreamedResponse;
 
 abstract class Controller extends BaseController
 {
@@ -53,14 +54,9 @@ abstract class Controller extends BaseController
      */
     protected function showPermissionError()
     {
-        if (request()->wantsJson()) {
-            $response = response()->json(['error' => trans('errors.permissionJson')], 403);
-        } else {
-            $response = redirect('/');
-            $this->showErrorNotification(trans('errors.permission'));
-        }
+        $message = request()->wantsJson() ? trans('errors.permissionJson') : trans('errors.permission');
 
-        throw new HttpResponseException($response);
+        throw new NotifyException($message, '/', 403);
     }
 
     /**
@@ -125,6 +121,22 @@ abstract class Controller extends BaseController
         ]);
     }
 
+    /**
+     * Create a response that forces a download, from a given stream of content.
+     */
+    protected function streamedDownloadResponse($stream, string $fileName): StreamedResponse
+    {
+        return response()->stream(function() use ($stream) {
+            ob_end_clean();
+            fpassthru($stream);
+            fclose($stream);
+        }, 200, [
+            'Content-Type'           => 'application/octet-stream',
+            'Content-Disposition'    => 'attachment; filename="' . $fileName . '"',
+            'X-Content-Type-Options' => 'nosniff',
+        ]);
+    }
+
     /**
      * Create a file download response that provides the file with a content-type
      * correct for the file, in a way so the browser can show the content in browser.
@@ -140,6 +152,27 @@ abstract class Controller extends BaseController
         ]);
     }
 
+    /**
+     * Create a file download response that provides the file with a content-type
+     * correct for the file, in a way so the browser can show the content in browser,
+     * for a given content stream.
+     */
+    protected function streamedInlineDownloadResponse($stream, string $fileName): StreamedResponse
+    {
+        $sniffContent = fread($stream, 1000);
+        $mime = (new WebSafeMimeSniffer())->sniff($sniffContent);
+
+        return response()->stream(function() use ($sniffContent, $stream) {
+           echo $sniffContent;
+           fpassthru($stream);
+           fclose($stream);
+        }, 200, [
+            'Content-Type'           => $mime,
+            'Content-Disposition'    => 'inline; filename="' . $fileName . '"',
+            'X-Content-Type-Options' => 'nosniff',
+        ]);
+    }
+
     /**
      * Show a positive, successful notification to the user on next view load.
      */