From 03b49a52685f270f9bc44094aa3345f561811ed0 Mon Sep 17 00:00:00 2001 From: Joseph Milazzo Date: Fri, 14 May 2021 08:07:03 -0500 Subject: [PATCH] Implemented the ability to change the JWT key on runtime. (#217) * Implemented the ability to change the JWT key on runtime. * Added .7z file extension support * Cleanup * Added Feathub link * Code cleanup * Fixed up a build issue on CI --- API/Controllers/ReaderController.cs | 15 --------- API/Parser/Parser.cs | 2 +- API/Program.cs | 20 ++++++++++-- API/Startup.cs | 2 -- Kavita.Common/Configuration.cs | 47 ++++++++++++++++++++++++++++ Kavita.Common/Kavita.Common.csproj | 4 +-- README.md | 1 + favicon.ico | Bin 1150 -> 30395 bytes 8 files changed, 67 insertions(+), 24 deletions(-) diff --git a/API/Controllers/ReaderController.cs b/API/Controllers/ReaderController.cs index 0436e7d65e..14fc9aead2 100644 --- a/API/Controllers/ReaderController.cs +++ b/API/Controllers/ReaderController.cs @@ -288,21 +288,6 @@ public async Task> GetNextChapter(int seriesId, int volumeId, return Ok(-1); } - private int GetNextChapterId(Volume currentVolume, int currentChapterId) - { - var next = false; - foreach (var chapter in currentVolume.Chapters) - { - if (next) - { - return chapter.Id; - } - if (currentChapterId == chapter.Id) next = true; - } - - return -1; - } - private int GetNextChapterId(IEnumerable chapters, int currentChapterId) { var next = false; diff --git a/API/Parser/Parser.cs b/API/Parser/Parser.cs index 9612fb7cd4..3cffb87e64 100644 --- a/API/Parser/Parser.cs +++ b/API/Parser/Parser.cs @@ -9,7 +9,7 @@ namespace API.Parser { public static class Parser { - public static readonly string ArchiveFileExtensions = @"\.cbz|\.zip|\.rar|\.cbr|\.tar.gz|\.7zip"; + public static readonly string ArchiveFileExtensions = @"\.cbz|\.zip|\.rar|\.cbr|\.tar.gz|\.7zip|\.7z"; public static readonly string BookFileExtensions = @"\.epub"; public static readonly string ImageFileExtensions = @"^(\.png|\.jpeg|\.jpg)"; public static readonly Regex FontSrcUrlRegex = new Regex("(src:url\\(\"?'?)([a-z0-9/\\._]+)(\"?'?\\))", RegexOptions.IgnoreCase | RegexOptions.Compiled); diff --git a/API/Program.cs b/API/Program.cs index 2240e6ad6c..db3ad9a1fa 100644 --- a/API/Program.cs +++ b/API/Program.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Security.Cryptography; using System.Threading; using System.Threading.Tasks; using API.Data; @@ -14,7 +15,6 @@ using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Sentry; -using Sentry.Extensions.Logging; namespace API { @@ -26,12 +26,26 @@ protected Program() { } + private static string GetAppSettingFilename() + { + var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"); + var isDevelopment = environment == Environments.Development; + return "appSettings" + (isDevelopment ? ".Development" : "") + ".json"; + } + public static async Task Main(string[] args) { // Before anything, check if JWT has been generated properly or if user still has default + if (!Configuration.CheckIfJwtTokenSet(GetAppSettingFilename())) + { + Console.WriteLine("Generating JWT TokenKey for encrypting user sessions..."); + var rBytes = new byte[24]; + using (var crypto = new RNGCryptoServiceProvider()) crypto.GetBytes(rBytes); + var base64 = Convert.ToBase64String(rBytes).Replace("/", ""); + Configuration.UpdateJwtToken(GetAppSettingFilename(), base64); + } + - - var host = CreateHostBuilder(args).Build(); using var scope = host.Services.CreateScope(); diff --git a/API/Startup.cs b/API/Startup.cs index 90c7969441..29cce38329 100644 --- a/API/Startup.cs +++ b/API/Startup.cs @@ -1,8 +1,6 @@ using System; -using System.IO; using System.IO.Compression; using System.Linq; -using System.Reflection; using API.Extensions; using API.Interfaces; using API.Middleware; diff --git a/Kavita.Common/Configuration.cs b/Kavita.Common/Configuration.cs index e69de29bb2..5503e5dc1f 100644 --- a/Kavita.Common/Configuration.cs +++ b/Kavita.Common/Configuration.cs @@ -0,0 +1,47 @@ +using System; +using System.IO; +using System.Text.Json; + +namespace Kavita.Common +{ + public static class Configuration + { + + public static bool CheckIfJwtTokenSet(string filePath) + { + try { + var json = File.ReadAllText(filePath); + var jsonObj = JsonSerializer.Deserialize(json); + const string key = "TokenKey"; + + JsonElement? tokenElement = null; + if (jsonObj?.TryGetProperty(key, out tokenElement)) + { + return tokenElement?.GetString() != "super secret unguessable key"; + } + + return false; + + } + catch (Exception ex) { + Console.WriteLine("Error writing app settings: " + ex.Message); + } + + return false; + } + + public static bool UpdateJwtToken(string filePath, string token) + { + try + { + var json = File.ReadAllText(filePath).Replace("super secret unguessable key", token); + File.WriteAllText(filePath, json); + return true; + } + catch (Exception) + { + return false; + } + } + } +} \ No newline at end of file diff --git a/Kavita.Common/Kavita.Common.csproj b/Kavita.Common/Kavita.Common.csproj index 736bcdb7a0..1b93ba6228 100644 --- a/Kavita.Common/Kavita.Common.csproj +++ b/Kavita.Common/Kavita.Common.csproj @@ -9,6 +9,7 @@ + @@ -16,9 +17,6 @@ D:\Program Files\JetBrains\JetBrains Rider 2020.3.2\lib\ReSharperHost\TestRunner\netcoreapp2.0\JetBrains.ReSharper.TestRunner.Merged.dll - - ..\..\..\..\..\..\..\Program Files\dotnet\shared\Microsoft.NETCore.App\5.0.5\Microsoft.Win32.Registry.dll - diff --git a/README.md b/README.md index 83229889c4..286dbc50ea 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ your manga collection with your friends and family! [![Discord](https://img.shields.io/badge/discord-chat-7289DA.svg?maxAge=60)](https://discord.gg/eczRp9eeem) ![Github Downloads](https://img.shields.io/github/downloads/Kareadita/Kavita/total.svg) +[![Feature Requests](https://feathub.com/Kareadita/Kavita?format=svg)](https://feathub.com/Kareadita/Kavita) ## Goals: diff --git a/favicon.ico b/favicon.ico index 1ed03f4f7601513b81a27d9c247681639e82347a..cc5e7351906a05e59c0b9b2c1ab228a1b54a4dc6 100644 GIT binary patch literal 30395 zcmdUW2RxSD|Nm{zjFMT15;C(QJ3B;(P|9d2Dr9C=c1gCf%PJWWvXxnp9a$kOd++Oi z&aJzrdh$FyeV^~|_xqpM>wRD6I@f2N&)MTfp|DVRC<+P`crv5T;i6E-P$(2F?Z&$o z5eiiV(wLby-je{G0vQU$$M@xZ4+=HT2pB+s{CoDJP$6 zRH1?js#GBWPep(PfL?%=FMx5Fruc>CtC+w_B@{@9palp37zaQ$0sIDOAT8=U-b$79 z;3^g5Tih#AK_I?OgK=O(Iz~otr3yZH54cyTjQGSWB3`lbh&O;|q&(tiaR?0e`5cJ|6-v3Vv+U9m&nRkF|xe0^wa$3CW8C}+Hd@) z1j=&EFI534dJ&JTkLDn&BhQf4W#IeOH8fTimkK01X^24PZ?)eZ0u-tQ2ZeIQ!btl2C!~cukQeqdpsPZG z?g-ugpt(Vx^(z6C0qRTp6$SQt0aO8u0<3-kjKef9Kr8x%_HRW4I#fut3Krnw1Be6| z23QB!tY3oPAPuDDgS=42&%zg*@c^9(0BnDo$=oa*@|Xbmcu>~20qR7jSE$$mn%PZd z!#)sVbKQ{F7RrJ$VSvwoQdL-2q1`i(AGV<%5m>5%c!n#Y0pmY@N4L8T-7`=YlnG^n zI0QdHPv9#R#3xP(xfLuAAom@v+sh(XRHe~?@gKiK9>@!2Kv`d8Zq$>F@Cz08SvG1@rJA**{m=0+n59Eb1psX)4p=?;c(P?9cy2`A{}& z^FQ&=zhXb8KlyLU|8M(elk!E7l|?Y$fOQC#wgV`%+@0d4F<^S)Egjw`S0adRUwe~$M*H54a5E$2`mGB*j*K*?fq*6 znSYNgwOJ!;Gfimu&`+595B;{$E}-ugVD$$))o5NQ<4b+S^bcSAH<%t$rH*_6bA_el zNn`=|W?^XzSy-M%76Cp@HK76HFb#vH)j2c|z^@W0PqtWyU^oHUgCanc zpm5M|1v9398XymbAIgFx0V+N20fa^eXfD6EX7@%*#7>^6`&JOZ@9v~T@1z-{Y z(~rV9OoQLfLt4lKd4D&c^;Rn509isnW-ve}09=1!+RA26NCRnuA#X5{@lWgsu2P2W z8qP21z|#`|&I32o^>^uz*Yk@k;78@ZbO-Pm3e0n0Jre|cB>;alNBuk9nD0`72fCl1MXg6d^LmfZ*0poL6Ucamx=nMesc8Fip59wOJ z==jbCkOqAY%NzQ9BTsNnyBR>9y(1M7JCj3bz&POFoF2;g!%wg*=m5~g0PAK*l`=Na z3+o!1?slpYa>GXfarc)0DO_FU5bI+y$Yo_|G+^A-Mee7xPzIC*WqxCWC)5db1D!DM z!r(cC)(>oj^j3%E5NCZY#Px*EpJ52ow2>fNOEh5I6`l5HS||g`f-=8ZqYQn~3CjWI z9k%a{as>LJ%`OI7NMm^^()+#%`Pka{XS6gTqn~=w_z2=Zqk+6o29yP5exo1i3J$IY zx;Of@jvw{A8|fjVgP*`0VEya917`mPKH=UA{00Hz-+sfy;WsD)%KC}^jxV~;1Kn`n z<-7btp0S}pwEU&{Ib>;W_A3@a9Igd00mfn4w=|Fk>W4Bm(GPV(-N|2dLmR&M|EqqW zhi`s6r&gOgrEOLKhzD! zt_|P+EdRiVU;RH1d<^ju|D%2X&G%pMKh%jCLpRqC@_@)+qDzZKBF;9BV0_5Z(A{!sUMfKB?JA<%~`AgiCV0kV+wm05IM{M-7EZU?{E|NKS& zJ2njTuZ$v#E$0EwA?r)SXg|Pq0NXq!{AT+{_rK^d;3wmc`$^dULj7Yv_l5sVKalm4 z{(ocq+1MxhWBdj41ivR>zbMQQnV1{~`X`X357*IHUm8WPOVRy5SgT?JT(`osZvo2) zu4^YIhtRU1OfcSH+BqESp-%K(-FNd3&`hzId_kQR?re2hk~h2eS_ z#$g)#_AMYUlmTT0{h|N6`4`l`+58i(ckh*hviJdkU*0k6VfY=B_Geo3`WTb%pUyuq zHvHrH_aFYm$p5P+oYVhw{tflR@V~kK*s$QcviR55U!ZMh1G5R)p!nrZPzp(yP z`A6%|AMHWg@n2kjBd%D;Itt~oib6>(qEJkeC=_`Q3Pn(YLgBcAtpfb*u>n{Dn{ojL0e)o<1k(>dTF8@&mH}iz znNaqBD}enLV7C%nS8)N{2D-Zepnowo{q7UW=tj!~vY`&B>)#5&Z+%p0JAfNk~n>pQR=o_V?7Qb6oZgMCN<7)PIj{byy- zgSHJl|M_zspzY{BvO*>Q$2tSg=mTH=0stNKx2vl>a#d9pxqA2uK-|?u9{uf~$okO^ z@Scyh5!m`|fWA*`6oq@9uz&wVi6EVG$TG+i`Kmd|?KNxALdW8mzr-cRl0^dEf4yX(2bOO46Vn4L;HnbVq{-w>b zfwZp8?1w(jhzLSv$3G*}qr<<1nb8qsVFsKD1HkyN@TSDtX68RA!HF*I;*0_uV~|H6LQA48jAJ0Jy5I0ybq z`*Y%>zHUdXt*oH;iQrz@p8@Uwu7EvkXgel=I81~4Nq?r_NQW{p0qXi&`+?11Zb1t2 z0nhXXe%XFt3*5tjcZfCvtTXH24k;$UI857&9;QQ?m^}ZY{R3#*E7ALea6R$M{$B_C zMbM_d0>*aC^RH;p-@e&{`bGO!fcaW`mZ2CXqe`qsit^Move^>vffM5TN&FlZ4X*c+PtNufq zG5Z9+Za?tG(RS-^W1f1XQV`V!3ZXEfiXebZn4V*j%-fB2{K zH&}lb2a&a@w`i;{4uAv%eMX0wpKU&VZ9eDw-TB+^%%4|=pCStl$B~8lyv(G{965u`SZryAFjdvcKz~o{sydtHZQ!>L1Seo1#Le(J4WB(faBRu#xIzL8LR&M z+&KS63j#mCRDZs#Ux4k<{>|5qU=3}lD20}Zk-svOx{3W8SoHHtr7zXyS}zq6e@*aMM9avuG${@U=rHpqh(D2rrtwpPEc$u`zluw7#U zjBl*F;J2USyZH~$1$9E*(1xG%?_m9|2-fc#dnjMZ;7|J}kk3C`8S##nM_#^*#h8Pv ze9l1@Tg=gbcOYPZXY3p8cM84Mf_)1n!1%`c>(}O=P#4q*b^pY6c-FxM_Z~0-<|DpA z_^100e8(e3$sh(2*f`PP#4q*b^l;LdjBfvhy5#< z4_F2uPul_gpZ5>Jd3)u@81T2eRb+Jy%z+`+!5SU{^ZvtgZh0Ij=?q2##y6w;F&*lF zy5Jf954OYipY9(*`(YWtJOKP;|5h1&-#)YDI+D@i^q0u|@;&k01qA*F5CX;_ALiR% z$$&aG?)Lww?)0E-L;LxUy+dd_CcwSv;7#_=;XREX?=bw;`?vnv?7qZi{6E|YfqqBt zpKsXqbK5a_faUP7onLG+FZ@Zp_*q@Sr2X>w1tt&h{0QLx%=r=8mR~$S!sKD&oErWA z0C*PD^ACN7@!fy&g!LVq-+})d{N}kfJcIoI_5Ac}Ie;@{@IRl8zV(0b{2HzEKRmz2 z-~EjreGpu&fC_%81Iy%(`!j#+M`HHHF#9uq z+@FCoU+>QVUMK^|`rUvaC=?p-0uhY&fD1+NSpqH>!6y_9e!#sVaI*;hzX}8?6bg_7 zhCiO*4idP9gaZG&0)gOF~ez@cB)CF!}s3 z{}{hu`~+Nv_S;uK_I&Xx#?OEN`rTVYU2zLBJu#r(qO2r$bmLkW7K#ubq!?aJI)g&- zk1ET_Xxp`qMcU_{&Vaj4?7{Kv0jA9H1A z$w|wf&ki~^^^)kGYlw=jPW-jj)=r&sT7PP)L~V3;%xM@mgA0N*)7!KHsxmK#kXzL4fh8S`(4`lsGKP96@DO| z1XkF?q1{)+=mLn0Rr6ESu`g{qr}GF;2@C6-hS=kq(uDo;L%Ksa!`lN2M-0hRIxm>| zGdsE!`iwTPcbEhfa?X14&99)LYnO*f)Xm(^SD@zUzfSD>w&O> zn4ge<`~j`tXSV|c+!{61sbaTOB^i^SOSp3^|Mks+(|hE+T%T|j5*~D=iEKP8Z>Jx8 z_C){5tLpig5;%EsV%5v)mQ=kJ46d|#na-jCR=Y3WOy6R0)i91QH&h%u77+s@9n5Oe+7Aync_|Jr)NFu#&b$8XQCM z@ZkoQhdskvW2Lm(mGX>2o%8M6=BCt4^3Z#lmsw`x3+_}2>nwz^6l)JgUAyU6a`W-v zNv52KTiirY+l@bt7#z>(pD`>llPBKxz*vrl;pN(Qrh7U*&xAf*5@Qf6rpS5Nx789=Q?+D{cnGw2RX-iw$6@~Fjr8aa{xg(A z@+H`N~&+E$_rJmdT`I>>4M1Wg?in-kgFX%Tcqi7jX&>X z4zP(3r^~SNOQ$kpEF%qisJpCrbU!{`P$;GFL9^}jmxPYmZO^X9v9jC%ZZAVDF09%~%Vpw8kF%3I(kqfBDYnNOjhUn;${)QLh9u|id2+Y_ zi+0!HoF^scO;eTM2v%^(lEr{FKTPCL^|e~ z(-iAF9PZfKBF<)kbs@a`bJ6QM8~Q*4w&w#ZIZf?N3%B_fh_7x_@4n;k$$vYZcHn{Q z`OG48Cga({b8CkEA?g<4@0YD2D|&6ZZyVG^R5+p(W2`!fw@O{>m)gD4Z3{JB|Me2t zATp_(v@0qZ?mVY%9CF#J@UgwA`@HVdnn!c!@^I<>DaH~RuCCcjC{=f|wb%VFO6{+X zkWNc!JgrF9U3ZWUZYb=%aAVc~q1vh(vVVyQP}%h{I;z-_qr#qz-+#~|7Tnnub&MhG zHSSV+3AVgJfv?eE(AL|_+l4tDY^h!-CYZJ_pB(*A-f~!qA;&tSaT5R3aK)!2tMm6& zYqzm7$sfmVAK&isK$Dnup81JpqLdf$t+&Ri7R~Mqy&+udr_alYiSX`is!C9uHuPKa zB5aLQow8UzkVwV)KDDOsz{1Oded61_ZXc`5-(^wVOLr$1FG={Ya94E?&m(*K#?<%Z z^dEV(Mc?jozOl=lkXP)}M5MyPd$*=*t=j#udC^`5nImL}I+pWsZ6;I zY7`z8TE5~sRwAQUmU5Jk@Bn?zn1yfr+wumpFy! zT1$!af=RMzg+#$*;}u_>aP!*+u2%)dT-FuWho0=|C&n357$uPJcL-`+Gw2pzQRyln zNqJFr-uj4GTgS}nvn`YD(Q6aAd-hG3Rd7p-9)G^1KKd9(uF>EO9jgdVYmeZz9K$HF z3MJ~rWGjdJon6J;VtL`711D&5w9QO9XkE<=CU9vUsol*b?)OcqowmGwg~*B9CpS0xrQRhMvl;s zX47^Geqbb?r5-HHmseen&CAYF z)w7Z*sa1Q;LG8<7GFmCuzwhLdgpF^bGRyRK@a|J?oMP2Dox4ZOFJc-PnO2gVfNd^y zT5cWxk%UFKcoaYD?1APq4^8V(%IdVIu}?}Prcfak$Gub7a2TB4>?=NN7@NfqP)u}j zl3z!Sr}OB+IUJ$WsE|wgZqz>B%!HE0az*{Y-2&t6bZ)$OGLw|tRNGz)r+OK%#NdMl zJMf8=njlpzv5U7VRCCPBfXzRVCFvUBwAp?2LYMRd=MIYS`WwXAZ2^=AtLEmEv=ZhD zS*C@w_f|?UGaixGjO1c5GOx)h5@~H^jlAFYmNx6k+U3Fag9omc3XzUF+a&X!5lv|{ z*-7M|=#b?TM&hpB*qHb>Z)9N`O>O_e!V@3)yL!j4LM#@^yq}ZB8P2m%<}uhjbGqlB z7)v4jMp$PpUF|F*_x!aCPLJA`ItwTsB$4-JKcF1HX!6F^jRdp=`k-WNOOlzT)O+d^ zq9((5;)si|wvOvaB`QJ9&Dbt(%V?`>o@7pUV|q~3TApyaQ9Nx_RThU1O>7GOskVXO zT;un~PnHTwnnAg2@#n>wXiA#iD^%KH`Ab4OPWM6reTG8#$${9B8`>(3h-l}oy z*1A+CbG7S5%EkJmVRJe3AAD%gkjox%$!|?F+0jzx>D#_!UX|MlIeAsrOK|6_4P8RA zLv^m1#q?gvzNe-f#~yLPuMqFiNKM+73zr_gdEV)7vYma)6j#PT2s^P<#6^|yC#!Qh zclQ|RQoLXfCOc|G{fRB>#r0L(SRtj;<$`Z;o=6X|T;+K<#6rclf4Wi+OP5$9fG;GA zh|n)Jb-JFIg)l&Dxy)%UdS`&fEz3`3t1cvdZ_|={=KHtjM~7ZAEM1+c9jNb?Tws## zcRUcOm+|-jwoe12Y3>xa?*Wd+Eni z-u{rr2sZx5W2Rm0;<8V@Z9sDb8$!%pTUVEsFEs4owY^z!3R^t zB5TX%8INnwv1$Z}OpmTuFbAtn`*w>Te67EN*QBGfv|v(x`KSYSqrmOsMuINWbhA_h z_Hm8}rMj04qK3P@uUbDYMxN#5L{&K^M7_*sQ%#hpZ{kyNS#%{lX3QC?8Od&3G?`Xx zKi{oY-SoU|p|d1Vmz#H`PWu%~HPTw_v|bnmZt2@A1Z(u=b5dIsZr(8!2#vffJaJJf z+|g)7hw_ZhD@Ru2XWmhK{7aQ=iG*&AKw z5`HB^cYT+!%QbD8yo&Z7oV+N;XIa=wSY|?C3|S|ZVUbUWUz4SLswkwDnlZg2I!NvE z+pHP8x+dEly;y{0jtNCK%o8FNYuI|J8?E?K=qR7oS!Ws1?mlXmnOE@PPH;2I^x+LY zF4Zh!OIgvv6r4|V)EwI`k0}pM-DwH*()QN2SNyDO=-6QY(69SqGMlD)jAE#-%1K=KJxw1h%GtKz?%exExNNV6TBXpjsE_>lYgAngg5f!WYG#hLdi(c7k;DN`==Ns^(T7_n?AXkt|pATj^I4x#;IJxAe^y2$j+`zz$ z#GoAk?;8vyOb#{sgh^SB+$Er}H|9#D&&C?+$h;@3r@AMw+TX8L2vPbtSCANTMdvQ_&2? zb0Ytq+^LL;H=ze`Tq<9rahkZOKN3{jU!r8A;<*&bcXf7dke!-UyOJ$4j+Qk`@KKfN zJ(GgPL)B+UX2}Pxs6RHM6Y-czZ+X`c#1hG9<`GZ2HW5~*zk?>xt17!%t~c5*z>^o~ zu<=!}z*R6q-BXSY^VPo*w8pM7XAn+&4!586vLXYMyq4x$UKLX-dHiR$G9!;1(wKgD z)H~B2yO!hn4SV8SUJj1o)Gr%w?y2Ij&f{v!5U>a`U2P%}k*;A08DYRS^YL=Z3iD-D zV|!#Nm`Iy}m23J|MEOx5)%2}$_Q)&e!W@N;%zi#;eRe%b^10Td_gx-ELN^Ced=EI) zh&%7MR%xEj8(B$Yw@Fy>tdfcd_np5w9YOSwB2*j}mROy- zuXc{D5Eq107T#l>s@!`@U0tVb+dE;CH%4a&u71`ES8Y`)mSDk2x9%Ln9mai1!GlGu zU07^{Td#F}&nKy_EkYXP>lawkt+Pb>9)MaM{fknETRUr04oeA*rukQ)l=jPn zy|&e3aH7b1(aefXWmrANbe2{Nj9PgVu}@Bx+sWAu#P%?oq>SNZc~ud-G<3ebdmHOC zw#RkFx}{Gd=K`qo9O?KA*03&%2@a-RGMf@)IqD$XSGCHy){p(9@QM7<2Q#lNNmm}f z6h`f(**`1acI?I~U5~kq^^_*fO~qAXlbma>KX z6YDkPZ{7m$!i0LWGat7TZ+VT{y_dGkiHBQZo$3+4!Kj0H_ML`Z3hRPz1{ups|nkG1y`xq5!nTHcPA>KQJ+-#QpAII`bWrxv$3@1IxX<>c3<^$ygYupQhYTRUs?@S{k&1Gzb4}8 zrBto5s`}Q@cnSrx1NX`HU_Uy?X~kV~>Ul{?&$?WUgJx2LU6x>Cwcm5Yg1%W30Qi$1Y*ZtRCu?)kjo5#Y}>_^OG z$sRoO+q+ZDs4&Gp#NN-GNsX3YcjEJS^j#H3gG>IyShy82aTBsO9X2b;+f}krl;wPJ zJB}ois$ta_k=%`Q;gh1mNn-Nzi=~b^$#H4lwbNx0w%%3kCOaLpLCtI=o?g2y+}z-R z*TYb$<8_oQPzmt4Rl}I9^$&^nR#t5V!H-q>~dPm?=!A zcI>Y>p(uzlF2TRbIV3)@?kH}fqHH_Z#%!Om!>q6MC25J-XG>N-BGRk;HGa8irF~O= zSY{q>zBt=1w=I7lTUq48Ikk;0b7`GQfg0m9dw+TNzXp-^^I9FH+!4) zxuk7R;33iyWvvEN0s?naP62YTL^iIx9ndg;vaP@BwGq`2VF}})Z=0dp#ix40;fI&x zwkP4yR%x-foaaW_AFYnLB7xPMML3gr0|&8g2(n(he?wC26^hV3ySln-jkn(Tmj96> z6nEnG79Bmh?oA}R?iS3RzVB@F$w(=ZxyH)eqc|E7_@u-0cWy`lwy+YGnhz9R4MQgQ zd&pxN7#z=#CF8OCzX@)qaV>S~)VVnky;W%RgNsy|!NpVEY*(pp(oc2nw9XRDNVDz1 zzDsoF^#^ubf>!JxEVr5#Y;Aj|3UNm4=h!Q|n|9XVYV!`AyL2)KFkCFiYGUtq#j4uP zl|MfxxaXcXiRPutR>w~`al0l?CwT>s*p7WZU$c9(XWd2Ovf`tx+tb=X$8F9INHErA zaFj=!p$ka9s z*lL4t+(`n$ z+81km@8zVm3G1k03v0(M%c}-_X36_U_Ahj{V($=RETZn&B6p4^Sl|-NV(T*tPf-A|G0-f!?{-_p!|h`IuQ!V-m}`^;1u&R^L9BdL%Rl z^m@0%c9I*BTxNHOz-{p1qow7i4fDAgXN&5J;RxIvNj&m&@zGI^{I@pxB&QxW^>zu! z4dX}AdPVxA3s9Iy+r03~YBso;(?DzDk>lcUqsJVpf`FyL~O2BIo#*1rkAE7F4`8*{^xaUMP3nw=gACZ}C$Npv($%Rh; z%G^f}X9BUGs4ASB-{pEpRD(q0tT$>UFpTpUiH2w2M{}$@uJ4{4VJQ{;8dtN09kUJchyZ}X#V9FM-@pIAykVbXp(MLk6%rxx^2 zlz2Lkrnv3BA?1Bj+DvZ3I3pt1Q+D)$$Kq8EcO117j~atS4!B2dHIG^o!Z!hmr^NUHNW0gOabZThw=r zk9DgXowTD5L+!>bW_?#W%Dd~s#F;JwNvWGTk zKlbNT+nYxC;>0XTR-IT?jLg`@qC=%k=Hn~t2{CWgKBZ34)4mbVNK}*Ar^&!i=H)8k z#-FKs^W6m5Dt;{o4w!Y$f$_EEl$$lD=JVPT&3;;1uBJ=eB`Eub{RD!rO2^2WswFe5aT@zVl3OP8COms9O$_T()#vHt zS18*~*w*j#uM`lIlQzwXTVwMplj+HRVbC*YCmt_{b&}-e<#*y!Z=Tr?c0BINp0T6H zD-~TB*;XfcUu354;1Rb_d9PmT*Pu#7?K>WqA51eV;HsJHG)Y_hTtp5?a z51}DPv12~%FkZ28Rq27jq-SQ9xHTbIi)Z4hnom5eBBU*TJhdyS;jwxSt4fddm62;~ zyWeA}KF&XAxC{Ro-ZH`TThOu62uVDKg#<9_Np3#pP=W>h##xGm=g%&6#^d_WG`r z5k_-!HL~$x67tp0SUBkxr8b6BX|`D6ci!D@pv}<*@>Ac18@+miyl}EYfwM1TC2Y#aapEgf8{OEJp_qdz%o2>$1@EN)w57L zJkjNZklD+!=v>uw_UH6(>5SwNI6C1*VMH?Nu{h_tEecyLpPvu2^+pTh`Br8SnelPqOqlI@t7wY0)=sV?K_D|%Y+~wQY94=?z zTrz&JmmkbS4F+6p8wVYV{17*3*g?}fWv5X7ETuj~!|I~*g62i02Ub*>H`1`p zR|QM@k_A+%rO0WlZlt}4&qXei;-p_#pL?jU&#H4B_1+VVmwV@vM{ueWo)5*^8%d7H zlxEaYhf5zFGnp9#$zRbPqG}!% z(0WnlznyhKj>9&7Fv*j#5!Fv3pP+6rF+Nte{)Rc!jjgxnc|gyD_o-EkN0N(Re9p_bL?X|GFx0}UpNwco-N-c@40@l_vXNOq@%+f z`+oi{B_4;R$}^2Ryi+9oGV0EnK!lm>GE1n)t~jEDDoyDToWf2LGBQtzP#59sEqZ z7mm+7b8|HNhsNLTznfZIh=^Ot;_4Je%TDd%Fc~>Z+1X-dFfP|@qsOp{JL{9eLrF3u zEjQ&-c;|yAZA*W-*Sm&$rS!hSLk`cgJ)XCmIa7NeK+2)@gWZQV0p;Cf6QiswRKD`g z3$uq;r@gzU8eSh98e>T6HVxwkThhKm6Ln;kKK&&R!gfzZACZl?WYf4;#c^GNLjL25 zoNDVKt2BmrUC%Kr)VWxtckNA|h(AiOWTLVX3Q>tFM<%2@j9=A`OLU4OWsk*|U%T{m z(nP(CHWZDn4s0hc==7tUA07LMbQXKNTp!H=(`%0cHwA|=rtoE2oMGK&M|bO#eWMIJ z+C&tWwGNAPd;1zHvK%+#Jw?UnI;f$2*!f+4twf-dOZRrw2k*&BimpqnzC6O%(mI5)f_25+1t}?T;UwmWT8s|RI%e#D}OQ8h9GoSZHOK1r6B=3Ewt20rXW%^>eEg)wc>^A8Y_dlb`v^N}L zl+o*a7u)L~f9i95@u$4OoSBZ94{ggsmJIuKURUVRI|pK*xpR;l=KrlF6+u zU2l@cmtV+7KcrQl@+ns%UU1-zVWeDN8AmseJGIoPGrecQl_@ zxHPX@x3;^QIec#elY0Thf}Nd$2Y(qTiP5gcF_GG&BPW>~nAmQZI?r{~b4dB|hcHs_ zTlnGk zYB2985KoNF=(o>9P;Jysw7bhAalB`*Cm||ZOa{}xa4RU zAGYn@`?gfC0E!qJTsRaX8P z<%=Az4^b}*!{qaQrl?0q+w9Lj3k#=v^M-W)9&f?pN`hWkd~k=f#!i>WcJ~y!`Lgb@d_wpBBBglCU5g#ADw)Myh~F7^Gp)OKV#F+2@P-KqpN9Ee zceFa+_IjU-8RbbY!R2EVyBEx|3%2CdTtKzdt!zEbf8_b&L4KWM%)KJise(Pxu|86!_q54lPXBuz(`@OUiKh@C(WHROsf$$GVty^XE{Cn z&VcJF<1Myz4zHg(?WzNZS7}vs?^UXiUMX?8UVq+)J;ZecZ1pTv<%^P%+Z7|Y&p6QI zvsfEt^l$NN!O<#lziuGlU0WTdtem#mEWKM>?|9Xq^W@0*_|V6tR^9PL&gLFw^3s0tl8uD|Jt#b4jM&HcoI#Z@{ ze$10+(7jwOD$uq(yHA&a)y21Q+vBNxMl$xa`CGZIGee*4JGxj%)luT}TMBvs{lN!~{)L}sx4Fy@S*1-?t=6|Lk%y*~4~AaUSLcZ)(T~uV@+**C^Z9&c374oIF z3n%+B)%Mj_Wxu!X{fy0|o4B1s*5S^}`x6Dai|;8|1IiZS45wy{)sRCvb~8+iV;77@ zKN(EO25-9^oUhUK`G$LxM?%o(OdU_d5wG;VQc)A$shvbRMj@Yvc~5a4jALJBNiRQ3 z>_8uK)TEAElj?fC-nH3TniL+{<}D>HwMQriW_CyDM;%+ts-v0}+v~G#ZarW_8SZ&B!`Yqj$lFu>Hi^sOrBQ`QL(Yu+G2Q}x zVNGL~iE9=o)t}zYejDCGPCUH@To&LCtkmL{n`d9nbGz6rF>K1lse3kB(|6l~9g6Ei z7B-Cs(S7yn+IQ1p+9`8X8IQD44j-L8Tz_hvfQ^!hNnax}d)>r`DhIW4{Ds+`+PsI^ z^L+~a)Isy`!bOe8yFP78@36G)j;Ng!cwdUDXL^-snx`<-}hG8i4+GCmQR;l2d1ZWJeYWJ3NmjviNH`|lKW2QQJn*S<^`rdMoBLQQ-QJut`12kRS#?$1@@9rb6UROvi z?`DixQP$pLR5j=llM=n1HLW!vdD`xdt1GSj2^}c|2aiW=!7fTo?T@Txu9g|sCNywQ zY!CRLp+1w16BUsV^Z|)W>3UE%hgjWJsw`@<<+{*-^S*AxUgVS-=SXM9XGE;oKe)le zy#0(lJsqW->3yv=y}COmkk$xowS7`exmYN@XOjvMdsW|g@~pq&)LA&a-AKAWSw*C^x%eN%rV=d^f7EIbcKDQD`axS{#MWPxp67UhJzjYgo?7**V3vV>;7-*(+^7_0Y?cq&ouT+7 zFERJj+u3!^c|}Z?u0$_|`^J$ocX$REgX>`xq6$8Q3wi9zB{vqNW(4Y&b)1t-$WKY3 zuVD=n^}qb)8df6i&#qy`I1j8S<~^Az%}lj*_ibQPGQ^vfm)bKKbbD7Q)2ulUSC?l- zp67t}waTcJ6Z#$4L4ja8{Azv+MRxX^{R$TNPfO3US>%b^_yurHY7Nc`=NGydSYH(& zf6KsBLvE8rdSFKsvO++Y>$FuMAu9VZ?En*{sXwhK$tw}sDL+z5zG?TAt`Wgy2eG!< z>9lK>A6s>u_gPfeIEG&#(cqxATqIkPZ4neXlKYN6kLVMzj#?#mXyQC&z>YGUfJVzL zq}Ea)DrXE0)ei1+P6Zsw@XFmVHcOee@r{LD(?r^JlNnfblpJs8q7TjSG!Io`c|R4$ zJ64z6&5m5*6}7-hDFvO`mGY`E>s~|Fs}h&F&pkHcm_!}YXTDxisBUxX_4erfM4wag zbBo^7X^iKUVN}BcUS_ZTnf2Z5LkLbWkxW; zAcKkOBd=4m8-BlZr=-h-;EPJKhtKm9SjdI$P9MUdcU?1lK737j8 z-cSZe;f2IJxS7I~uP-Y{d6J+dX+%nF@VWEV+Dq4MpCH)eM$4^N4(mQ!^t9Xb_U-Zk z89y&7iYVRY6`i@0&AnP`-qz!vo}O5}Fw6~jXY6qJWWilCx5g^{z_9av$K9_AmEAhj zh$9u%95efT0-F)XdEkM;w!GfM3Kz33Jhh!sbay$WSY4);BM^hfcGrS@Yw40gKk+H= z61o&qrzbIQyqIN=eH1-)TkCqst@^b2+|sCHfyRe!Jni$}4(>shY%k8m@wzBCVtONd zpX^>f@-Vl1vlXX;g0R5>qS>iuoR76_9tU(hJ{3>E^K`9fCHsyD%FJ}WuIO!t?|r-F zJXuZ(fkv-zR&W$YgVi_{RXWANah>`-nRtqNtC)?!AsgPv>k9jeKhfL}x6d}J*hkZa z()POb*wU=?UY*raNHB+AxP@FTO~4W}P3-b9H(`0Tmjo1ty~QwBxSQ3~=X4L`td-_P zzcG3-@cH7Qaih#at3XngjQeB7<0e-8JC24|rSyhi@COjw&qtN)74 zxb2`sEK$a%)?ap?PS-niG=akeC)`Kq?Bo3(XsvjLjz z=^@j#c<81(xE%jMChAJ9#GbN1x$$B4rbx$UH`)^9?y31!o^6fP2bbgNd+s%NCxmg| z8?SnJJB-Zyt?Eqy&01lFT3q!*qMA2_Z;|?NAIIG_ALdR-U`om2HhMR60(H__JzCz5 z{!<(KRbEk>6M0WB@&qWS7d6&>>VqObef_fl^}b6L?a9V(taTzeQUqRw1@+s(UWJ+x@QhrgFvmYOe<r$U_j~MYQVoig;f1s4*)#aYbo^t4?{mMLE5r!@ayh}zYhrFxh^s<~ z>nu)jCdqRUf@@=!63O==4{c{6XYA%@O;>eHPYI;Bj~r@4kU>gwmAlr*;^*ptN>%?h zS)M9|K60qRGXOFj-Bn7pqheV5bcbxeI8>;?qdWOh1{D=AKV!RUq;2M@yn2hu4Hm~o z^W#$G7biJ*;01#*7GB@DZ23w{e_NpPgL_oBmuSUWr03>iYS@Mp`pBUM54>Q&ntO%& zPmfeuT)RoD%NofR#Y8bHZRjJ%4IX&G=*(93Ri}EG@b%><($nh(1jWoj={t(>fCmHC z^wa?7h1`G-eo>DVN0AizP?iS+_PqS*_4g_K&GU7;C-eW?Pj>#n`qT4kj8pwtg(zrI zc)U(wn5-Rp_;39+-amVBemHyi{k%d;+nS(_H#MT4U#Q<{^4vUIKT+KKLlD}=D}&&* zm;sCbfaVluOm8Y`wja{h&Uv25j1NM=4ePLTLNb zWb99o;c|2*6XBhO8a(i(TsEHP{-ooab$@YI+RM}T_s10>zFidJ!v($zBk3q-M|j`` G1NJX<5p^E`