From 420f89af99dab5eedda84f9d5df5e5d63d835420 Mon Sep 17 00:00:00 2001
From: Dan Brown <ssddanbrown@googlemail.com>
Date: Wed, 8 Feb 2023 23:06:42 +0000
Subject: [PATCH 1/6] Built custom favicon.ico file creator

Followed wikipedia-defined ICO file format info, and used with
Intervention's good bmp support, to create a working proof-of-concept.
---
 app/Settings/AppSettingsStore.php | 12 +++---
 app/Uploads/FaviconHandler.php    | 69 +++++++++++++++++++++++++++++++
 2 files changed, 76 insertions(+), 5 deletions(-)
 create mode 100644 app/Uploads/FaviconHandler.php

diff --git a/app/Settings/AppSettingsStore.php b/app/Settings/AppSettingsStore.php
index 8d7b73c1c..d830df639 100644
--- a/app/Settings/AppSettingsStore.php
+++ b/app/Settings/AppSettingsStore.php
@@ -2,16 +2,16 @@
 
 namespace BookStack\Settings;
 
+use BookStack\Uploads\FaviconHandler;
 use BookStack\Uploads\ImageRepo;
 use Illuminate\Http\Request;
 
 class AppSettingsStore
 {
-    protected ImageRepo $imageRepo;
-
-    public function __construct(ImageRepo $imageRepo)
-    {
-        $this->imageRepo = $imageRepo;
+    public function __construct(
+        protected ImageRepo $imageRepo,
+        protected FaviconHandler $faviconHandler,
+    ) {
     }
 
     public function storeFromUpdateRequest(Request $request, string $category)
@@ -39,6 +39,8 @@ class AppSettingsStore
                 $icon = $this->imageRepo->saveNew($iconFile, 'system', 0, $size, $size);
                 setting()->put('app-icon-' . $size, $icon->url);
             }
+
+            $this->faviconHandler->saveForUploadedImage($iconFile);
         }
 
         // Clear icon image if requested
diff --git a/app/Uploads/FaviconHandler.php b/app/Uploads/FaviconHandler.php
new file mode 100644
index 000000000..78c9a899b
--- /dev/null
+++ b/app/Uploads/FaviconHandler.php
@@ -0,0 +1,69 @@
+<?php
+
+namespace BookStack\Uploads;
+
+use Illuminate\Http\UploadedFile;
+use Intervention\Image\ImageManager;
+
+class FaviconHandler
+{
+    public function __construct(
+        protected ImageManager $imageTool
+    ) {
+    }
+
+    /**
+     * Save the given UploadedFile instance as the application favicon.
+     */
+    public function saveForUploadedImage(UploadedFile $file): void
+    {
+        $imageData = file_get_contents($file->getRealPath());
+        $image = $this->imageTool->make($imageData);
+        $image->resize(32, 32);
+        $bmpData = $image->encode('bmp');
+        $icoData = $this->bmpToIco($bmpData, 32, 32);
+
+        // TODO - Below are test paths
+        file_put_contents(public_path('uploads/test.ico'), $icoData);
+        file_put_contents(public_path('uploads/test.bmp'), $bmpData);
+
+        // TODO - Permission check for icon overwrite
+        // TODO - Write to correct location
+        // TODO - Handle deletion and restore of original icon on user icon clear
+    }
+
+    /**
+     * Convert BMP image data to ICO file format.
+     * Built following the file format info from Wikipedia:
+     * https://en.wikipedia.org/wiki/ICO_(file_format)
+     */
+    protected function bmpToIco(string $bmpData, int $width, int $height): string
+    {
+        // Trim off the header of the bitmap file
+        $rawBmpData = substr($bmpData, 14);
+
+        // ICO header
+        $header = pack('v', 0x00); // Reserved. Must always be 0
+        $header .= pack('v', 0x01); // Specifies ico image
+        $header .= pack('v', 0x01); // Specifies number of images
+
+        // ICO Image Directory
+        $entry = hex2bin(dechex($width)); // Image width
+        $entry .= hex2bin(dechex($height)); // Image height
+        $entry .= "\0"; // Color palette, typically 0
+        $entry .= "\0"; // Reserved
+
+        // Color planes, Appears to remain 1 for bmp image data
+        $entry .= pack('v', 0x01);
+        // Bits per pixel, can range from 1 to 32. From testing conversion
+        // via intervention from png typically provides this as 32.
+        $entry .= pack('v', 0x20);
+        // Size of the image data in bytes
+        $entry .= pack('V', strlen($rawBmpData));
+        // Offset of the bmp data from file start
+        $entry .= pack('V', strlen($header) + strlen($entry) + 4);
+
+        // Join & return the combined parts of the ICO image data
+        return $header . $entry . $rawBmpData;
+    }
+}

From 1a189640f10c91eb3c519837e4228d5eb955517a Mon Sep 17 00:00:00 2001
From: Dan Brown <ssddanbrown@googlemail.com>
Date: Thu, 9 Feb 2023 13:24:43 +0000
Subject: [PATCH 2/6] Integrated favicon handler with correct files & actions

Format does not look 100% correct though, won't show in Firefox/gimp.
---
 app/Settings/AppSettingsStore.php |   2 ++
 app/Uploads/FaviconHandler.php    |  25 +++++++++++++++++++------
 public/favicon.ico                | Bin 10933 -> 3134 bytes
 public/icon.ico                   | Bin 0 -> 10933 bytes
 tests/Settings/SettingsTest.php   |  14 ++++++++++++++
 5 files changed, 35 insertions(+), 6 deletions(-)
 create mode 100644 public/icon.ico

diff --git a/app/Settings/AppSettingsStore.php b/app/Settings/AppSettingsStore.php
index d830df639..e6fc466ba 100644
--- a/app/Settings/AppSettingsStore.php
+++ b/app/Settings/AppSettingsStore.php
@@ -51,6 +51,8 @@ class AppSettingsStore
                 $this->destroyExistingSettingImage('app-icon-' . $size);
                 setting()->remove('app-icon-' . $size);
             }
+
+            $this->faviconHandler->restoreOriginal();
         }
     }
 
diff --git a/app/Uploads/FaviconHandler.php b/app/Uploads/FaviconHandler.php
index 78c9a899b..f61e7ae64 100644
--- a/app/Uploads/FaviconHandler.php
+++ b/app/Uploads/FaviconHandler.php
@@ -17,19 +17,32 @@ class FaviconHandler
      */
     public function saveForUploadedImage(UploadedFile $file): void
     {
+        $targetPath = public_path('favicon.ico');
+        if (!is_writeable($targetPath)) {
+            return;
+        }
+
         $imageData = file_get_contents($file->getRealPath());
         $image = $this->imageTool->make($imageData);
         $image->resize(32, 32);
         $bmpData = $image->encode('bmp');
         $icoData = $this->bmpToIco($bmpData, 32, 32);
 
-        // TODO - Below are test paths
-        file_put_contents(public_path('uploads/test.ico'), $icoData);
-        file_put_contents(public_path('uploads/test.bmp'), $bmpData);
+        file_put_contents($targetPath, $icoData);
+    }
 
-        // TODO - Permission check for icon overwrite
-        // TODO - Write to correct location
-        // TODO - Handle deletion and restore of original icon on user icon clear
+    /**
+     * Restore the original favicon image.
+     */
+    public function restoreOriginal(): void
+    {
+        $targetPath = public_path('favicon.ico');
+        $original = public_path('icon.ico');
+        if (!is_writeable($targetPath)) {
+            return;
+        }
+
+        copy($original, $targetPath);
     }
 
     /**
diff --git a/public/favicon.ico b/public/favicon.ico
index 41655ccba55f8dd5f250471eb2e8928b21a85992..e047657ccad8c38f318f2bda36f0809a9b0c878f 100644
GIT binary patch
literal 3134
zcmZ8jc~leU7N`He(>^O?oyla9$v#6e*%v}WLdZq}1jMk)z6l5f1gTb00a@HoK*8-j
zXtn66)&;HBeXY3FqSd-kMPJ>~y0k99`@qxp$Gelc=gT)|?!Di=zx%rr6!ZzW*zBMQ
z9VGlmP!J_3C`bryur+yq5<~+lAmG{mOK@;7i@^jM$ke|DtZ6iw7{`?wt-}+Ol$A4Y
z$@1<k+c)<0e|zZs-cwil2d?irdu`{L8~x{o4*q=q^o^&dZ@f79>+`d>o?pE4>eAg;
zm+rm1aOaP6LnBlQ72}`^sT3Hb(`X0`Aqb1d<2j>a+W+0R@AT!fH}Bqk{`&rl_rJd!
zeem+b?Wb>!T^{P_{VsE6Swec2$?i;;kyGE<yLRioo}DK)?Ksi<{i&S;zy5IL-m#kx
z&)j+>Aqc57RLJLfW8>^mPMK8dbh%3c)pg6-2W~%p_-^dkhq1TgWA|T<U4JzC=+Ci-
ze~unKf3vW>RxH7p42Tj!`FLoA%|<y~CIs^Y0z<epJknXVpsDZ3x!lq+OO)La9WyN{
zrFKDm%ksAAX)_#N-xtkYTMu77eE!zZqu2LFK3x0H=+DEW*B*`zJ$t`#`%y<+iaN|L
zSDPV-h4L^C#-&oJAt52a*XKZ{d_HB$6dILow!7<=v~1h`eb2V<Ht#yHqWi0i{F0f0
zIr)LPa+Qw97m`Z#wB+3RP3s$0e7mHhzoYN+;-2&Ab>H|(I^s*)Jvj~0DYI~y9OZC8
zdMAxfWg&=EPAXI?t-+vF>mr@*)$6}LJ~-Ug-Lrkq{w>@4mn`qFI-*I1k^{oxaxpH(
z;c)H#%8bSX>CMMyw4O?7Jf6JpNLK5K+@%Kst9F+)txqeQm6V+ypPJ=ON{x=6rni_Q
zoKCGd!W3m6yz}7pi?@fa-k-a;IlHhtt2p5DBpSm_0v=y17Gpd<f*>#q$L7@)bzaJB
zyO7m+IkW3(Zu`aj_MeJ6FVys2Ub$<qtAAkOs_yEBrjpuu$+?9w{uHM#!Idywsnhk`
ze*fiHXJ%E;t7~js*0FZks?OBxLXBR}!+4=$iBKS7u|VnAFpDh@kF)1AXDmOJy6R$1
z`^AFxvw15I7Iyzw)^jeqd2>$n!i1D`Uvf%vR!&O6Okm&a@>pH5`RmS+Mz>UHu(@NB
z(ledW9)meTCYMWaLaI>cOjeEFLaOv4F@Yd#CIb;kb=K6H*zy(5%xbGQWqRq{xZKhi
z#d8*~-<mb6+7=z7)avAFLnx`?ViJr`<jh+=V^+O;T6#fsomOYUfn9<S2!uF}D>Rx&
zk0-+JCe>P*OpUU+EQn1g3<{G=APAL6HR4b?PO1zMPMaeky{f_G&(Ve3bf!pxRLX!G
zqczf->dvc=FRb?@7e=}hg+eit$)r*!3_2a;TqqIaQn|(9R_hD|A&2NNi^ah(kqB3D
zIYORDN+`_|sWwy{9_I2J-AQIok^~1Hl?t6cRG}m_2E8>tx}YJmvMFvxsov^FSQ9V<
zFs4(fECvH;7)JC)vlL*7%Y$eP3WWl2&SJ4~wIe#P$>7OWn`6RbGNfv&*5Z|^A|!-r
zk_PAqiXm)1%2zDjIasyqtH}6lrOpB|VPJtqrP3%=2Dm5`48w4#R3w&gIa~;WCz>cY
z1V*s<<tP0eg9=BE)|sw%XXxCiwxoanS5BJcF_;j_5vn76#<=3P6HlsE?o^t+bSlUU
zpdbdD&Eo>>5IPt_nG7Zu<G~OM>;vPJ5E_lb6p~hd`=yMnPaU&&DC~J*(`HYr?2OK;
zv!_*sxl;v%hQr4ZjzC13WyZJ-xBm*PKc;i0Lol1e6LE#o<RzV@drp?F`34h^Q-g!)
zbOxQqocPfhFvL+u`t(sr#@LzOz}oDN)BgJ1v2(Xs(-!L7dAQyovv@JF0vJJg1g?(4
zm9}-aMq38n#Lnq4Pp>Fmw#89c9bY}qS5~=b*D0+dF*rB`Fc=3D;_@(_02hc9$}k&W
zBtuZ4F)7DcToaK~5j$&9V%6%XtcAGF#U~9Iq2f!mgx;mJB=F^ydHWvE+3{OsUbCm9
z&7MBDrhBu^8>@Hu0{=Qu()FV&uTg9BvrukaVI6=9jm7}g2SwI+64RG;O|SgIosyQk
zXsN|tl;3gCnzw}X<dUvzl_%GfP(~QzMJi{-)@MbVZd!{rxXU+&CC*8#TACDSEn0JC
z=GV9KdhXU8ePK>429OBVSZH*{M|uRBG)2Xg1*8fUL6T`r%PraEF{Q1^%{yXid!s8i
z*a{ns$u*=US*&&~*!w7N<Bgm(7uH^Vv*y~{hJ(-g?tduV`q2C3(Jv2=_*;%bY%yS5
z7~xPt7+^8=b1>W{4#AbkSZod|5PHhy>tiz{a<fF^4h>HvEvccF6oJwq)_KGlSNVp4
zrbEw)H(W079c(=Qy0(9$cWA8k`&Y3|gH6YuIRcx7D$8e|ea3{@R4Nl>29$%wU`{}o
zLW3ZLg<?WNg<>L<FA)%W0U5z3BQU9vudpZ`=>nOh>&V^w_QNTSJ5pQrR_}aJw(UvR
zPp{_hejc~zZ2hrU&P6Ab?u<`A{glNOL#U923b^7>2!`o&FiU|8&~7w{34tjCx`V+4
zxEF{uQ4+mPq_SWV6;Bra^!2;Nn+`bBs+_5_{eh0mmB(wgT$$f@JGJFx>&4eetFK39
z&H?c=V3dQa1cXj*_CPSOhD;m*oCTDe!I<2E!5ZP=VwH&_CLsg`6eA#w<KyGM4BgjT
zJtz;y2yJ*`dFF!Nmfd&Knhxdn4TC<iXD^tTcvC}UW)GpVs%(iu2?3fVcxo_+A5>zZ
z7!(Sq3I&`6!C*EY%uG<DiH{IE1K7WL@3G3{g-|{Zw4OlW^p}>jZg!QevsP{OwVcw$
z%ml*}2+EVN`Jjw;rOrsFLty74oF*SmDDW591R4l%3629~Y<&EWccTfVoid$|jp3oH
zNLyTnH?uM#ccmeBLqgkGS-9_?lM+IqGT9i&utH-%IoSWq0yO<^4NeLKc$q8^P;?Og
zz_s6f`AhhC7?a7CDXk8FsUxjMGp$yW(QPdI)?Uy8UI!jPR3HOXI+MfYt5h10d*Bh|
z280S8eRKg96KSE*Su7S{c)<3<k4Ca9nk;bzm`EeUm1?uslT{0dQKXL(nr0Y^HrR?*
z&=@dy5d;R_W}_&T4u?Au2}2Bm5irW7F<2jqFsVc6OeWw%U>OKlUmbp$U9(1Q_K653
zjEW)?ijxD&`D7HIw4!{uEW)q#7QrYFyi21oX>^D$6w_E(biraxLIqE(hY$`ZEC_yr
z*a2bz$AUfqpIKlQvAHn9-E--|?A3>T1&w4_ELS8av{sH##ln;VxkDKf5QV1^#zZzh
zlmW8<x=3qMsN5is85~)4xYmve<QT3Fp#a{P_+AAB4w3_K%YqSz#b$E_I2pd_(u-N^
zevHgsBv#oVmXOrjcuJqdl%a^L@GaOmz4=IN&E}Xuw<)nK!e8Q>-EB|E@}w8*Jh@@f
znG#*J#b1hw6d_=C&;j*Lz?I5iASjO~Btc(GNaOaA(YCWM9r+8np^?&Xe`S5w>Mck1
zU-<3PZ%=-C@qS=rbl31-y|@1A9eUHS?{4?`KU({*tUol^_|2J$j{SLU2hy5$MJ89U
zFd--e=qU=FiC{uZAQj?jp;Rl9>9#x`D_*@%W}8{Qbo<reC!+vQe+z&v`1@ge{Mp#}
z?_*;ppMN;~;KTkq@Am!j^oQ#s=bpabGyJyi?z@J)*W@NI1LiUjE|m$>A%u<b`C_?*
JG-yoG{{`3W2Q>fy

literal 10933
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
XHrNJO5L3!r00000NkvXXu0mjfR=v%1

diff --git a/public/icon.ico b/public/icon.ico
new file mode 100644
index 0000000000000000000000000000000000000000..41655ccba55f8dd5f250471eb2e8928b21a85992
GIT binary patch
literal 10933
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
XHrNJO5L3!r00000NkvXXu0mjfR=v%1

literal 0
HcmV?d00001

diff --git a/tests/Settings/SettingsTest.php b/tests/Settings/SettingsTest.php
index 30bb50f7c..fb952585a 100644
--- a/tests/Settings/SettingsTest.php
+++ b/tests/Settings/SettingsTest.php
@@ -52,6 +52,10 @@ class SettingsTest extends TestCase
         $this->assertFalse(setting()->get('app-icon-128'));
         $this->assertFalse(setting()->get('app-icon-64'));
         $this->assertFalse(setting()->get('app-icon-32'));
+        $this->assertEquals(
+            file_get_contents(public_path('icon.ico')),
+            file_get_contents(public_path('favicon.ico')),
+        );
 
         $prevFileCount = count(glob(dirname($expectedPath) . DIRECTORY_SEPARATOR . '*.png'));
 
@@ -71,6 +75,11 @@ class SettingsTest extends TestCase
         $resp = $this->get('/');
         $this->withHtml($resp)->assertElementCount('link[sizes][href*="my-app-icon"]', 6);
 
+        $this->assertNotEquals(
+            file_get_contents(public_path('icon.ico')),
+            file_get_contents(public_path('favicon.ico')),
+        );
+
         $reset = $this->post('/settings/customization', ['app_icon_reset' => 'true']);
         $reset->assertRedirect('/settings/customization');
 
@@ -81,5 +90,10 @@ class SettingsTest extends TestCase
         $this->assertFalse(setting()->get('app-icon-128'));
         $this->assertFalse(setting()->get('app-icon-64'));
         $this->assertFalse(setting()->get('app-icon-32'));
+
+        $this->assertEquals(
+            file_get_contents(public_path('icon.ico')),
+            file_get_contents(public_path('favicon.ico')),
+        );
     }
 }

From 2845e0003ed3a7960257ef19b139627ee69523dc Mon Sep 17 00:00:00 2001
From: Dan Brown <ssddanbrown@googlemail.com>
Date: Thu, 9 Feb 2023 15:14:41 +0000
Subject: [PATCH 3/6] Got favicons better supported, can't get transparency
 right

Digging deeper, I don't think PHPGD supports 32bit bmp output which
complicates matters.
---
 app/Uploads/FaviconHandler.php |  19 +++++++++++++++----
 public/favicon.ico             | Bin 3134 -> 3134 bytes
 2 files changed, 15 insertions(+), 4 deletions(-)

diff --git a/app/Uploads/FaviconHandler.php b/app/Uploads/FaviconHandler.php
index f61e7ae64..39f8b12ca 100644
--- a/app/Uploads/FaviconHandler.php
+++ b/app/Uploads/FaviconHandler.php
@@ -28,6 +28,8 @@ class FaviconHandler
         $bmpData = $image->encode('bmp');
         $icoData = $this->bmpToIco($bmpData, 32, 32);
 
+//        file_put_contents(public_path('icon.bmp'), $bmpData);
+//        file_put_contents(public_path('icon-test.png'), $image->encode('png'));
         file_put_contents($targetPath, $icoData);
     }
 
@@ -54,6 +56,9 @@ class FaviconHandler
     {
         // Trim off the header of the bitmap file
         $rawBmpData = substr($bmpData, 14);
+        // Double the height in the "BITMAPINFOHEADER" since, when in an ICO file, half
+        // of the image data is expected to be a mask.
+        $rawBmpData[8] = hex2bin(dechex($height * 2));
 
         // ICO header
         $header = pack('v', 0x00); // Reserved. Must always be 0
@@ -66,17 +71,23 @@ class FaviconHandler
         $entry .= "\0"; // Color palette, typically 0
         $entry .= "\0"; // Reserved
 
+        // AND mask
+//        $pxCount = $width * $height;
+//        $pxMask = hex2bin('00000000');
+//        $mask = str_repeat($pxMask, $pxCount);
+        $mask = '';
+
         // Color planes, Appears to remain 1 for bmp image data
         $entry .= pack('v', 0x01);
         // Bits per pixel, can range from 1 to 32. From testing conversion
-        // via intervention from png typically provides this as 32.
-        $entry .= pack('v', 0x20);
+        // via intervention from png typically provides this as 24.
+        $entry .= pack('v', 0x18);
         // Size of the image data in bytes
-        $entry .= pack('V', strlen($rawBmpData));
+        $entry .= pack('V', strlen($rawBmpData) + strlen($mask));
         // Offset of the bmp data from file start
         $entry .= pack('V', strlen($header) + strlen($entry) + 4);
 
         // Join & return the combined parts of the ICO image data
-        return $header . $entry . $rawBmpData;
+        return $header . $entry . $rawBmpData . $mask;
     }
 }
diff --git a/public/favicon.ico b/public/favicon.ico
index e047657ccad8c38f318f2bda36f0809a9b0c878f..e114831177f415bd8dfb65b3f6f9d5988796a7b5 100644
GIT binary patch
literal 3134
zcmd5;X-pht7+(JiODm<KMr*B#Mon$JVm)d-qQ+QbVzp6Q6QlKPjQ&uGX(egGa<m+_
z2nw`P<S4XMkXo=DrRA`tWoCC_mt}93W$%0T1nM*U?F>VqhEjgGJG=Sjo%eg*?|t5D
zM4}t;U9(1n|C>b_H;P1QB9SNqKZrz|@MGoi51#$+KC#PFxWYkKY$k@q@}3WXk+5%~
zTO!TBMP0i!U{w%v*KdWznTa~24Z{Z>q_UI9MdzlDw`GO{_UmzkoX}^wsH%E}M;<BJ
zK<C^<Zsmn{SqCelQ=x0MLXd-AlfLB(O0pt>R-9p@YJ)C=-z**b{SDrtvivEJ=1lDJ
zVuJ$kNHk#XEtKuMjeyX(t0y|M7^mKN>N`3ob@cE{UgLSEqII}r17}67X?UM;YRJK^
z78LgBOf0_f;n|Ii+XFVG^+LI<U@a$1f>ZiGK4Bc1$=;$l$LPq4ggn5RZvsNUMLy5*
zZnwHi@ynAuva<XcxI=EG`L~+S?qi%<ee)a|?Nm){I&y`T=yXJ0d_UPJFI<PfAqh1{
z-=(sXi4c)>Dp~?oCEU?8?x3>8oP51wq}e+@C`^OV%ICd?0hSQXm!QuAC^~nQu{|^B
z)R{W=(79;@0SuVW6)+B*^HVUR{B@&S-RaSEE6SfH3iwwYdEGxTWS2G0rm-YN*Yr`)
zZ6sDgL6@d(8z{039g!7hBsPNQ=Fh<Zc#v}8jz1D>j=l#6t%DVCmMGIXD-Eq*F-~2)
z4AY4a98R3cjmCno;x+UuD>o;Qairf~_Kja~jx>*ytmlN{vFa_d{kJCobJI*6KL(Lk
zZQrBfh)}lw4om+*#y+M!{vjnvaLBj}(dn?f@E)QweBc3i3V&7ATR7w-MAoA{i_ByL
z+Omt1tR#=*Mfci<s{NK>b?sYxuoR__xm0aleV?-OIj8|Y=$ph-X<qt7cQ)hF8Ct)F
zzX^oGb+(I#*>KlygU;~5hn&Mr@w$F>)_Ub;kG>a}iqc0pVKUpPEOPr$HG&%sxpgOY
z2JC9fKnZ*#I&7b5{{fXXetI`T&uwJ8kgDo6-^3-S{5Z^UaxpmJ5I7EVQjsvxC<Ff8
z>W(?q>)#1jM{Jj>WJPy#2#NkiZfVI1!=JklP;*b7G@lD7%YXzK$TGlj2!2qxd}=Am
zo^Xx*?a`f6Ry?~PvQUU{Pxk%9IMlimAM*j_B1n-40zVREsGL=@f;%n$lrnae_Sgq}
z1qsovwpNrr;!^(QG4zifdV$wL2BPkefg!&wIu#yo-_2%k*Iqg=;ViGYxJ9zM=1u>k
z+)mZXi|<QpiC0i0>IeU*g0Y&di-VdI2;mkqSmUX0krJlPEWT5LA9TF9i4gb!OTsNn
zSj!66TF#dTY@^zS?ckZ<lcowQA-~ml@*93mEr?G`QXKLob=}*1P0d$RaGfd?unRjj
zOyIhrT?@C<60xFeBeK`6?QyHx6=jbLp9<H=TerF!XhMA65i)MjnfWb94~&(d*NjXs
zwq?S1ZQ~B`U>W$CuS!mtEw`XsPfj}@pSOZ>ImwSpNs6KQGng><<l|k1K+$=tO<jA@
zmy9P9T}4^3$)oT4Z)DygQHziJFUhz^uZ~Xy)YMK&l0vK?C+&2--%Q!*y4X~BK~8wg
z`3qu2__~7m?i^{soEkm!B1~W&nNIJCguT~_EVRX1nT|RHa|#myXLiVrF^(~E2BH4a
g*1+Lj7t=BQ-I}iDLgvpy07JgXCA`1!Aj`P;7iM)(t^fc4

literal 3134
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
JG-yoG{{`3W2Q>fy


From 48f1934387b7a0fb1481182374285a161d96896f Mon Sep 17 00:00:00 2001
From: Dan Brown <ssddanbrown@googlemail.com>
Date: Thu, 9 Feb 2023 17:47:33 +0000
Subject: [PATCH 4/6] Updated favicon gen to use png-based ICO

From testing, worked on Firefox, Chrome, Gnome Web
---
 .gitignore                     |   1 +
 app/Uploads/FaviconHandler.php |  28 +++++++---------------------
 public/favicon.ico             | Bin 3134 -> 10933 bytes
 3 files changed, 8 insertions(+), 21 deletions(-)

diff --git a/.gitignore b/.gitignore
index 0a858681c..90b80e7b8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,6 +11,7 @@ yarn-error.log
 /public/js
 /public/bower
 /public/build/
+/public/favicon.ico
 /storage/images
 _ide_helper.php
 /storage/debugbar
diff --git a/app/Uploads/FaviconHandler.php b/app/Uploads/FaviconHandler.php
index 39f8b12ca..2e756c587 100644
--- a/app/Uploads/FaviconHandler.php
+++ b/app/Uploads/FaviconHandler.php
@@ -25,11 +25,9 @@ class FaviconHandler
         $imageData = file_get_contents($file->getRealPath());
         $image = $this->imageTool->make($imageData);
         $image->resize(32, 32);
-        $bmpData = $image->encode('bmp');
-        $icoData = $this->bmpToIco($bmpData, 32, 32);
+        $bmpData = $image->encode('png');
+        $icoData = $this->pngToIco($bmpData, 32, 32);
 
-//        file_put_contents(public_path('icon.bmp'), $bmpData);
-//        file_put_contents(public_path('icon-test.png'), $image->encode('png'));
         file_put_contents($targetPath, $icoData);
     }
 
@@ -48,18 +46,12 @@ class FaviconHandler
     }
 
     /**
-     * Convert BMP image data to ICO file format.
+     * Convert PNG image data to ICO file format.
      * Built following the file format info from Wikipedia:
      * https://en.wikipedia.org/wiki/ICO_(file_format)
      */
-    protected function bmpToIco(string $bmpData, int $width, int $height): string
+    protected function pngToIco(string $bmpData, int $width, int $height): string
     {
-        // Trim off the header of the bitmap file
-        $rawBmpData = substr($bmpData, 14);
-        // Double the height in the "BITMAPINFOHEADER" since, when in an ICO file, half
-        // of the image data is expected to be a mask.
-        $rawBmpData[8] = hex2bin(dechex($height * 2));
-
         // ICO header
         $header = pack('v', 0x00); // Reserved. Must always be 0
         $header .= pack('v', 0x01); // Specifies ico image
@@ -71,23 +63,17 @@ class FaviconHandler
         $entry .= "\0"; // Color palette, typically 0
         $entry .= "\0"; // Reserved
 
-        // AND mask
-//        $pxCount = $width * $height;
-//        $pxMask = hex2bin('00000000');
-//        $mask = str_repeat($pxMask, $pxCount);
-        $mask = '';
-
         // Color planes, Appears to remain 1 for bmp image data
         $entry .= pack('v', 0x01);
         // Bits per pixel, can range from 1 to 32. From testing conversion
         // via intervention from png typically provides this as 24.
-        $entry .= pack('v', 0x18);
+        $entry .= pack('v', 0x00);
         // Size of the image data in bytes
-        $entry .= pack('V', strlen($rawBmpData) + strlen($mask));
+        $entry .= pack('V', strlen($bmpData));
         // Offset of the bmp data from file start
         $entry .= pack('V', strlen($header) + strlen($entry) + 4);
 
         // Join & return the combined parts of the ICO image data
-        return $header . $entry . $rawBmpData . $mask;
+        return $header . $entry . $bmpData;
     }
 }
diff --git a/public/favicon.ico b/public/favicon.ico
index e114831177f415bd8dfb65b3f6f9d5988796a7b5..41655ccba55f8dd5f250471eb2e8928b21a85992 100644
GIT binary patch
literal 10933
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
XHrNJO5L3!r00000NkvXXu0mjfR=v%1

literal 3134
zcmd5;X-pht7+(JiODm<KMr*B#Mon$JVm)d-qQ+QbVzp6Q6QlKPjQ&uGX(egGa<m+_
z2nw`P<S4XMkXo=DrRA`tWoCC_mt}93W$%0T1nM*U?F>VqhEjgGJG=Sjo%eg*?|t5D
zM4}t;U9(1n|C>b_H;P1QB9SNqKZrz|@MGoi51#$+KC#PFxWYkKY$k@q@}3WXk+5%~
zTO!TBMP0i!U{w%v*KdWznTa~24Z{Z>q_UI9MdzlDw`GO{_UmzkoX}^wsH%E}M;<BJ
zK<C^<Zsmn{SqCelQ=x0MLXd-AlfLB(O0pt>R-9p@YJ)C=-z**b{SDrtvivEJ=1lDJ
zVuJ$kNHk#XEtKuMjeyX(t0y|M7^mKN>N`3ob@cE{UgLSEqII}r17}67X?UM;YRJK^
z78LgBOf0_f;n|Ii+XFVG^+LI<U@a$1f>ZiGK4Bc1$=;$l$LPq4ggn5RZvsNUMLy5*
zZnwHi@ynAuva<XcxI=EG`L~+S?qi%<ee)a|?Nm){I&y`T=yXJ0d_UPJFI<PfAqh1{
z-=(sXi4c)>Dp~?oCEU?8?x3>8oP51wq}e+@C`^OV%ICd?0hSQXm!QuAC^~nQu{|^B
z)R{W=(79;@0SuVW6)+B*^HVUR{B@&S-RaSEE6SfH3iwwYdEGxTWS2G0rm-YN*Yr`)
zZ6sDgL6@d(8z{039g!7hBsPNQ=Fh<Zc#v}8jz1D>j=l#6t%DVCmMGIXD-Eq*F-~2)
z4AY4a98R3cjmCno;x+UuD>o;Qairf~_Kja~jx>*ytmlN{vFa_d{kJCobJI*6KL(Lk
zZQrBfh)}lw4om+*#y+M!{vjnvaLBj}(dn?f@E)QweBc3i3V&7ATR7w-MAoA{i_ByL
z+Omt1tR#=*Mfci<s{NK>b?sYxuoR__xm0aleV?-OIj8|Y=$ph-X<qt7cQ)hF8Ct)F
zzX^oGb+(I#*>KlygU;~5hn&Mr@w$F>)_Ub;kG>a}iqc0pVKUpPEOPr$HG&%sxpgOY
z2JC9fKnZ*#I&7b5{{fXXetI`T&uwJ8kgDo6-^3-S{5Z^UaxpmJ5I7EVQjsvxC<Ff8
z>W(?q>)#1jM{Jj>WJPy#2#NkiZfVI1!=JklP;*b7G@lD7%YXzK$TGlj2!2qxd}=Am
zo^Xx*?a`f6Ry?~PvQUU{Pxk%9IMlimAM*j_B1n-40zVREsGL=@f;%n$lrnae_Sgq}
z1qsovwpNrr;!^(QG4zifdV$wL2BPkefg!&wIu#yo-_2%k*Iqg=;ViGYxJ9zM=1u>k
z+)mZXi|<QpiC0i0>IeU*g0Y&di-VdI2;mkqSmUX0krJlPEWT5LA9TF9i4gb!OTsNn
zSj!66TF#dTY@^zS?ckZ<lcowQA-~ml@*93mEr?G`QXKLob=}*1P0d$RaGfd?unRjj
zOyIhrT?@C<60xFeBeK`6?QyHx6=jbLp9<H=TerF!XhMA65i)MjnfWb94~&(d*NjXs
zwq?S1ZQ~B`U>W$CuS!mtEw`XsPfj}@pSOZ>ImwSpNs6KQGng><<l|k1K+$=tO<jA@
zmy9P9T}4^3$)oT4Z)DygQHziJFUhz^uZ~Xy)YMK&l0vK?C+&2--%Q!*y4X~BK~8wg
z`3qu2__~7m?i^{soEkm!B1~W&nNIJCguT~_EVRX1nT|RHa|#myXLiVrF^(~E2BH4a
g*1+Lj7t=BQ-I}iDLgvpy07JgXCA`1!Aj`P;7iM)(t^fc4


From da42fc7457fe076442351d2a8250bfb8c898674e Mon Sep 17 00:00:00 2001
From: Dan Brown <ssddanbrown@googlemail.com>
Date: Thu, 9 Feb 2023 20:57:35 +0000
Subject: [PATCH 5/6] Added default favicon creation upon access.

---
 app/Http/Controllers/HomeController.php |  12 +++++++++
 app/Uploads/FaviconHandler.php          |  31 +++++++++++++++++++-----
 public/favicon.ico                      | Bin 10933 -> 0 bytes
 routes/web.php                          |   1 +
 4 files changed, 38 insertions(+), 6 deletions(-)
 delete mode 100644 public/favicon.ico

diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php
index c3c8d1066..84651f653 100644
--- a/app/Http/Controllers/HomeController.php
+++ b/app/Http/Controllers/HomeController.php
@@ -10,6 +10,7 @@ use BookStack\Entities\Queries\TopFavourites;
 use BookStack\Entities\Repos\BookRepo;
 use BookStack\Entities\Repos\BookshelfRepo;
 use BookStack\Entities\Tools\PageContent;
+use BookStack\Uploads\FaviconHandler;
 use BookStack\Util\SimpleListOptions;
 use Illuminate\Http\Request;
 
@@ -127,4 +128,15 @@ class HomeController extends Controller
     {
         return response()->view('errors.404', [], 404);
     }
+
+    /**
+     * Serve the application favicon.
+     * Ensures a 'favicon.ico' file exists at the web root location (if writable) to be served
+     * directly by the webserver in the future.
+     */
+    public function favicon(FaviconHandler $favicons)
+    {
+        $favicons->restoreOriginalIfNotExists();
+        return response()->file($favicons->getPath());
+    }
 }
diff --git a/app/Uploads/FaviconHandler.php b/app/Uploads/FaviconHandler.php
index 2e756c587..3dc702ea6 100644
--- a/app/Uploads/FaviconHandler.php
+++ b/app/Uploads/FaviconHandler.php
@@ -7,9 +7,12 @@ use Intervention\Image\ImageManager;
 
 class FaviconHandler
 {
+    protected string $path;
+
     public function __construct(
         protected ImageManager $imageTool
     ) {
+        $this->path = public_path('favicon.ico');
     }
 
     /**
@@ -17,8 +20,7 @@ class FaviconHandler
      */
     public function saveForUploadedImage(UploadedFile $file): void
     {
-        $targetPath = public_path('favicon.ico');
-        if (!is_writeable($targetPath)) {
+        if (!is_writeable($this->path)) {
             return;
         }
 
@@ -28,7 +30,7 @@ class FaviconHandler
         $bmpData = $image->encode('png');
         $icoData = $this->pngToIco($bmpData, 32, 32);
 
-        file_put_contents($targetPath, $icoData);
+        file_put_contents($this->path, $icoData);
     }
 
     /**
@@ -36,13 +38,30 @@ class FaviconHandler
      */
     public function restoreOriginal(): void
     {
-        $targetPath = public_path('favicon.ico');
         $original = public_path('icon.ico');
-        if (!is_writeable($targetPath)) {
+        if (!is_writeable($this->path)) {
             return;
         }
 
-        copy($original, $targetPath);
+        copy($original, $this->path);
+    }
+
+    /**
+     * Restore the original favicon image if no favicon image is already in use.
+     */
+    public function restoreOriginalIfNotExists(): void
+    {
+        if (!file_exists($this->path)) {
+            $this->restoreOriginal();
+        }
+    }
+
+    /**
+     * Get the path to the favicon file.
+     */
+    public function getPath(): string
+    {
+        return $this->path;
     }
 
     /**
diff --git a/public/favicon.ico b/public/favicon.ico
deleted file mode 100644
index 41655ccba55f8dd5f250471eb2e8928b21a85992..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 10933
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
XHrNJO5L3!r00000NkvXXu0mjfR=v%1

diff --git a/routes/web.php b/routes/web.php
index 95b4ae535..937dc0c6c 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -40,6 +40,7 @@ use Illuminate\View\Middleware\ShareErrorsFromSession;
 
 Route::get('/status', [StatusController::class, 'show']);
 Route::get('/robots.txt', [HomeController::class, 'robots']);
+Route::get('/favicon.ico', [HomeController::class, 'favicon']);
 
 // Authenticated routes...
 Route::middleware('auth')->group(function () {

From f333db8e4f7bd164479f7cf437696345f1e52acc Mon Sep 17 00:00:00 2001
From: Dan Brown <ssddanbrown@googlemail.com>
Date: Thu, 9 Feb 2023 21:16:27 +0000
Subject: [PATCH 6/6] Added control-upon-access of the default favicon.ico file

---
 app/Http/Controllers/HomeController.php |  4 ++--
 app/Uploads/FaviconHandler.php          | 28 ++++++++++++++++++-------
 tests/PublicActionTest.php              | 12 +++++++++++
 3 files changed, 34 insertions(+), 10 deletions(-)

diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php
index 84651f653..a82710523 100644
--- a/app/Http/Controllers/HomeController.php
+++ b/app/Http/Controllers/HomeController.php
@@ -136,7 +136,7 @@ class HomeController extends Controller
      */
     public function favicon(FaviconHandler $favicons)
     {
-        $favicons->restoreOriginalIfNotExists();
-        return response()->file($favicons->getPath());
+        $exists = $favicons->restoreOriginalIfNotExists();
+        return response()->file($exists ? $favicons->getPath() : $favicons->getOriginalPath());
     }
 }
diff --git a/app/Uploads/FaviconHandler.php b/app/Uploads/FaviconHandler.php
index 3dc702ea6..c637356e0 100644
--- a/app/Uploads/FaviconHandler.php
+++ b/app/Uploads/FaviconHandler.php
@@ -35,25 +35,29 @@ class FaviconHandler
 
     /**
      * Restore the original favicon image.
+     * Returned boolean indicates if the copy occurred.
      */
-    public function restoreOriginal(): void
+    public function restoreOriginal(): bool
     {
-        $original = public_path('icon.ico');
-        if (!is_writeable($this->path)) {
-            return;
+        $permissionItem = file_exists($this->path) ? $this->path : dirname($this->path);
+        if (!is_writeable($permissionItem)) {
+            return false;
         }
 
-        copy($original, $this->path);
+        return copy($this->getOriginalPath(), $this->path);
     }
 
     /**
      * Restore the original favicon image if no favicon image is already in use.
+     * Returns a boolean to indicate if the file exists.
      */
-    public function restoreOriginalIfNotExists(): void
+    public function restoreOriginalIfNotExists(): bool
     {
-        if (!file_exists($this->path)) {
-            $this->restoreOriginal();
+        if (file_exists($this->path)) {
+            return true;
         }
+
+        return $this->restoreOriginal();
     }
 
     /**
@@ -64,6 +68,14 @@ class FaviconHandler
         return $this->path;
     }
 
+    /**
+     * Get the path of the original favicon copy.
+     */
+    public function getOriginalPath(): string
+    {
+        return public_path('icon.ico');
+    }
+
     /**
      * Convert PNG image data to ICO file format.
      * Built following the file format info from Wikipedia:
diff --git a/tests/PublicActionTest.php b/tests/PublicActionTest.php
index afc7fcef3..e21afdf33 100644
--- a/tests/PublicActionTest.php
+++ b/tests/PublicActionTest.php
@@ -155,6 +155,18 @@ class PublicActionTest extends TestCase
         $this->get('/robots.txt')->assertSee("User-agent: *\nDisallow: /");
     }
 
+    public function test_default_favicon_file_created_upon_access()
+    {
+        $faviconPath = public_path('favicon.ico');
+        if (file_exists($faviconPath)) {
+            unlink($faviconPath);
+        }
+
+        $this->assertFileDoesNotExist($faviconPath);
+        $this->get('/favicon.ico');
+        $this->assertFileExists($faviconPath);
+    }
+
     public function test_public_view_then_login_redirects_to_previous_content()
     {
         $this->setSettings(['app-public' => 'true']);