From 68cbd735890ee1d6edf03caf1f74cb817ff46360 Mon Sep 17 00:00:00 2001 From: ValueOn AG
$0)~L3Q4dfN9e>89$%AF2G}X4~KWC0) zbpHLL;{!+mhQ$B{ydo@MayZ!vxBX)Dwy-vf?aWi}pOyfw`h9lmuaWBXX#r!eEfaI{ zGO^B;b6K}wzziV(X>6Jipu!_^h0QPlhOcwoC$Iey=UlKkcga=>;tSNd*xU;uCUq$| zm0-=rTlxKM7IX^@RbSqOlPDOROr{{4N)eg^!tQjR@sBLh?OLT)m}%g!Q> iNzW>~>&^ zpJFfku #sggE#e*P$ z(Eh=@;WI*dw$hspiDg6hFX(Arz(zk#!|Nia$dEH|cvn-y<`}eUMocR|%v7fNsct~m zbMG;I2TJ<#hDuG}{ZKELQ&r6VG?O|-a7LZ8d{m~Z{ooX?0KGP7@aMD32+>evL%Q9Y z8zZ~Z5tAO_$|ZLm(k@!z_ip5FxX)Tgj=gftvCCMP^wq`@%Qrl98<1N@C61BGv?lnA zlwKh`i8xlHC*7UM78+r$R=8@}8b^{?vH3>xm@{ zSlG-X67f=fj;Xj7r8Jpm1O&Z=TBIJPC-t|q_YrK0@L2r)7LMTACcHK|V$Gf+BaV9J zWFEE)qrb2&;+b6T%Si~HPRU;XdW;&tkjJJNVqjC;V~DVMned*4GUYepV-`j!lQJUR z&}s!?fB?WyL*;Q-#G5U6=TU&UcyI`JxN(Q|Az8=cO+>iGP=B`$HJ8#PZ<;5m&;IPh zJD~U!VO}ZV@M!2u@5XilOfUfE**7*1_?}!&j^0-mn9S_FT-@Z5ZvU~xZVcR@qc7Cf z*cO+62vO2pBJhR<#$8&~BR+ Tj9m@ZvAwko$swdp(UW%T>#*n{gs&{%%=SShOVgmU8{ zt+oZl8VtvzgF8IZoECmxN#Wg~7TtHD24I2I3(-~`1Hj7Xi(Pcxfp5S@oVR4G)-5?h z9cuDlq3M8iSvg8h;h%@V;#yKm5T3!xit`*7VfMw -t7T7 8vOI3drehq@N_ZjUY(8&1z|AJC>3F<{o+0lbAq)-hmQorb#-Q^;; z WOj{oqL|$Ea{MQ8V0${vLe<{0tW0==ste1<*8LZ%4)Dij zWl|;ZRPhm1-Ez*QHh2XA>mA+hdi55PLf~itp)B=nZ;hP6eWa9XRm&UWUPfew cg|o8_c)e)K 1h7Vr zw^I&HEXU(tE&uIxA?#cv=3?q%1_^tm6nH%D1hW!*I`8zW<#iHpRMCpsgXV@5pv1Tu z4TFRi^egT9RId&}epr}(twG#9)dr%K=4T;a$dWRJJ!Zl;*p_VI#0~D5r|*k8nvw|- zIDYn(z|7erEv93KlKbn1VB9b_Zz1N*CfAimR`#*Wkq_iTgh7oJ{@tKJPc0a@20->| zV7&7J$Tr#y;k(qYR!T?)QLtTmg&=ty#%42+VOlyFZiSi*5h5pBYFc}wHPM(N1Bic9 zs$q(ux@>1KF#V02pxcG!M_Jrtl0=iI#XtTqopsXyHv27@LwOFO^-`A`oU1tc*@Y8l z8x*vhYGn9xPEqS`#30rq$Tqmufqz`5@(5?DW@loLq~g=WW;>=J^2>j^f0kjUwu&Q$ z4pFAM6||;IL-;7fswGf{1WUZubW3pa?cu4M7g?a0>Rx5}wY`|`v6(CT*JQ|p4;~6p zjN*0iggq(Wybp~lJo?hiI%l}nayvAW$nKqV#4Qs$n9zqXUK>=-coUuA;epggGvxdD zebHqB*R2B&1Ys)rq8;9pfPH@wQH4K$!nDDH#n9?>oRC&s$^otsG}z_Z76pLBSL0y6 zk~Qh<$MsV>RxIST(i|^(yoiFaaKYc@^5!1^Juh}ahwnyr-gBh`{NP -0Ih}?bt!v z8Zf|8YcUJWslBJp}xkN8FUZ!xYNUx42f*BU4Qeo_%X!Qd!6A|J^BP7XA!{WT1i z+*}FRb$?B%C>s>Zjq)kOq_6!W=0ct)WwwG1X@h!#a?51=;%Zg(9X$Jj$-zv Lu<2W=J<-Ib2j0142slM59ZsF12xOrauMU_{n@p=cAiEyb83@ z#hK{Nf&dR4&L|l1XieANnu_UYGVY`8I^j4M4RBm$Ak;|9GgZ9{!`V;IqQc`PriAr_ zkdAf+SDcVSC#&M3;%eRk#(o9p**% 0T)wwVvi=PPuchSdOd?1x4hojg3iG zS7}Us=|jW^M%w?%{l ae2Xze&3>L6gQ(?V(%$E2Q4OsRjc0)UftVPMUov;bWPJUmihPZeU6tF=3N$DQPk< z5(gS$3U)9I{M9p8S8l9t3ZFw;U%JCgiYxq~zJlhB#@1|!m^kY;SXM$D>)n|b1;8tb zjV iqWtO%5BG=M$GTc z)b`$?%@h4=qtPP{+#I73DfN*FoLI@56sJEm)XVi!aY@g*c+kQ%y8E8B`4x!Vu(7>W z#P2MCgfR95B5v+77*>&-u>6t@cbG tYckByi7#5TWfy&F^{yZmCOQhXL0`$39$y{a&h8d({-)rjkMf_(Y6i(GcNf9tC(9 zAS{~#_4|i`D9mDT%bc*SO*+;Fqf@g5E81Img A*ap?IM*)C1q-vyEp#hEmO>5YkVS=oGSzoR-9mx zAEvZ;=$=3&qhNXmOuvhfYg6t8Ru+37F%>^!DU~!V#-DsS<7mbmM=NK+wZKovK*K;g zZ0aAfHF+1Bo)t9GJMti2M2E%(m}BX&7T!tX*DT`0Y0d)PxWH#oeA53g1eYG>4a2Yb zH3MwZ_u!KT+nTh;A&W=U)-4H`uwEQj+W;pz{AIP7vQ8$aYM@oQqqYi4Xwj=T%cmK6 zRXCIr|I7b5??#_-pq&iqS_yq7zDcO4u=C6l0;W#g-` !r)ZpT)*^<-m2QIC{FI6%c{#p9kk_mm zA;uMqjU3bug;H-hS>#uhh>ztHB8*r^m`yn=(!E;!!z@Pu^bvw&q)0beCK;+Qf~^F* zXi@Jg$Hq<5QEFBgzu|x#t#zV~*)!;Bg2pOkABOl>!`^qXc?Tuc$t%;CUvia^%i>vP zdg`nZ5MjcL|1lDcyoqyXz%Iyb;?30*p;*tZZe8aZk>7-NT*8C5zOzwLHHL&*N75qw zui;DhrT|ysYJqm q*VS}1RZS-Fl6VaQpt4Il z5|Xc~U$)fauz?DK*i*m#aAPQ+A3l YzF?% L`(1&S_A=nOkS^dSny|&z`^~Z?cm`H0c*BIXgG_eK( zx4B5 WGLr0e8Mra #lZ>4T(Aq*sNQf%VVNEBk|Ut-}#b{~WV zg-gH>J7JA(is#>aWls${YPrmT*(9u=iD$=#<#C#1D#FO^F0HNKu*om_^r%di4!}kV zQ_ma1DY}2EdXd(>-Sl?C7MQ1?JP8O7o8~HMOHnO&08p72U{ZwJKMEcHaFX;fi`pM! zto&CG7iw$DQSo4sxn5ptPu{Ivt@AZB=;84H4_E55hqF9eI2S}VJ@IiL_CkTt&Xrsl zuIV9fm}ER{3EQuHn@!TB$|sU%+3bE-E97s9`$Lbz6f%eb^9yIns2*a}S;98#VHoiY zV;rzdhpa7!Scu7?UgHtPGKZ?g(O?p9 obZ{E&fM*+{xpnNc67hmsb zP+z?wNgMhaJ7U@&h3Nml(2^ZC!m7|N?XnA^6mIK6UAckYA&()!3vy;#f7L27zjRoA zgNxp4gHZ#dRn5vPu cSnwHEM*kx1K_`ja4@gGv(}YJ>(GoD9~`M67LXr z!z#=N+(%x?4Ogy@OZt<%8w=}w*SHuz)?8$QxF1bL$t>9MNRRM5o8Yhgl5^oUo=Pm( z+}=LkDycwB8(uTb(1C%_ONX<|xb9QJUjz7KQp3Hfo;RKfurMsqyY+JT^6&S2EZq}Q z_{i4N^+(J1BbL^UK4Innq7mx^FU8r!5+hqU2pFn3eu6Vlkc*N6l{b7Yf)9s~It4J6 zvRKDz*5PKD2bpj8CU+YH+cW{xP415%ulkV)lG&sKy90>Wo1fqV>azFm(E5bk+?b1R zQL#!PN7N-K@8CxB8{le0q`@T07b#$T`g+>ypC0x*4k)3LzIyYPmU{Tw@adau+&O6H zFOUh$!_{Nw;dKrr71Wb}G$``rTZKj5cYuG_K0YEF*bT#Taqh=Ae&qtHJ<=yeo^8tF zEmGS9vmaVzHUuD_Eq2a(khbP}_idW;DHY _A}-nC`|gL2><*j?a6O&5R{)cxf8X{B!14 z;yL+)R+Cg`5*5|=`7AINxm?2k-|&vP4=zx9)KLn+G&?h&YbI7VEER^JQme+K z!Q0m9l3o6@7^r|#7-ZZfL`qW2phH_oX-ZU&U~0_6fwM{k9O(ZF$L_Vz%KfI9xIp4N Njd0o_oxBWW#u5@bP8a|H literal 23150 zcmV(yK$*`k_f`%AR}00RI55CAd3^5(yBLr}h01tDtuTK@wC0096100bZa*Q0W| zrg|5iaQ7IjUZzdf&nak>N=WGG^0nYMZ6DM`1t0*R^?Yy_WUrYi^#qTBw?5x`285Dx zw4rZQ+=v=N)nzCK2mrt*2><{9000LN03w#Wvp1*LE_&tRgs3AUM+hJQ%#_IWhVD7Z z+~u^^Ko<+*$V8{1DGdxxw}@M@ksz>T2_OJd?xLAv-2jl3c`+$9nhR5fsT?d?PaqAm zyB9CE&XB|k1ONg60000401XNa3N(dsgDnfjOsadq{ZDKby3xwpglZ284JW;M2dS%o zZNSVkfC1qA*v@5uAd&SYkg8`u`QY+TSZk{l4BTe0C#5z@oj17>2nzD9pqF-PI~Y9a zQTO@pv>_w63!-yMV?FnGHs<>PGEi|x6>XgV< =~!h>1ldxNYoS9O|J(N$cerrJJKc{6+~MGQBDQ2 zPT0go?<0Ts!O`m8AjsZeENjGyMXc15P>O=pRLh9kHa>-f2`edayce~Zh<-9{^K+N) zo`I$bw~k&J39wu~ey8lJKs;SG!9fX1(w|~Nc^RQqbkv5@ZBB6vC(ici$WdOlty(}l zI=Beu=b&rRb=9>w8hd97Q)S@n4xbb>X?RE@?-!!Am9v$HBJG1hb0bImFX05Wf>i (hKA|;y0o+L6}w=U `L%%PV zVD7>1WXaSuaNk(2-uDO^Gb0=by>yYjDlz$S4lL}as7HAGjjp&4 wWOp zt)EDOMe ppv(K zr>@RcHWZ2?g#$&lZ}cvn4Oq#4Y)Kou3+|^Pj~IJyLhnG7G!b9=wFzI*oSDDoYiMS! z?ogSn-69{JDSOy(xN%)Jb7qAl0Qp`DGk$_1%Z#Q(qgqfC_mS>r_ZV?s1uVv6cHIYA zxYz3JP>NXR`1sweh7MS3dtJ3=U070j`<&!8T7n&Z|13Fup|n6J(FL0E6KAT!-Uu+p z?&>(4tEz5_BVR8lx~NW&3u!?p-@^WZkh(&KN_TA7k9x@USSpafcbZhnFl2GCDC$~6 zi8D=02NIzOPqqM0D4{i8%@4tN;xh%)qA~ ZS^n!2g!p|z<@#B#!NPtob{s&dDC68Y?*N@p% zP1SR5P!K++OQw;GwgjLu5A$Tp2tkLfZik6+E$oCubx`!!gXLN&B&j?(rf_*^QfVKF zMZbUI&(NdC(IKT|Avx-xs*Wfp)d8tAfUpR_#~Ps&!NCMj6o}s8j{@9$py)A3?p<3* z92KJ%yUI) 4 z0;o4BOzfYzG~;n}d|z*iLBD>v7L=}M5BlbdA8#$xZ1m-HnYjuZk @DHxYrI2G z3~E89#U#{ZFTOQBs9}Ie;8x!*N_M2^XF~{2nzRyk_H|aH5Tl~=7DhIK?RVpN5Ngp^ z1#r4_Mk5A9cWr~Rxu@m)`E8)mWYZ1)`pTzI_<(>AfnY+vmgJKSDO&?p0!)B tZN%jJpe#CHjJ?G?h<6#b1vbm8)WfYcx5XI7d9DGfA^iNXDG9 zm4M$N?NH=CkV5TmUV@4H@`er1q^K8;^C=+#E%a%guGOfFRif56Jd|9m=9&31bxWjp z@lX8Xk`vkL^akYRIM~<8XgyyVW~j#MDK yS*Cf-Efb^hr zQsh w>=%zwj0=hr;>sQ?!n+&{dZG|t1`Q4Kkt*)sYiYKb@(h4kl|&u7 z|4*$aKvWOtlv+#CNbO&S5k2g_Q5r-SzxZt4WgK>z * z|6#E<(Fwk61X+ld99ykTa3{qpAHMEv=PHkA)CVfUzD#oZcNXkI35r< P*U{eV6!_^I{ttTj)SnTdA;TfN|SNv!z 6*+6o5!5c; zA8;PvW;`#lXtmJUDAOKhpM`N5;!JltrL; gjtg6tvh_}J3>~|j zKYSOl=7z9es!rmOGTp4~6kLVV^QD(-`o8vTN~tNFeY6B8vZ~}vZ~FMOzR6`Tn6F$- z>?Z9$cD0dFN4LhaZu LA=U{ph}9JYBK>`O0Qw1BV37c5C=n#vkd|2r|%- z?(}&b3k#!Zni%_W5m9)g>ec+Y61^?U5C% `qWt!L*(@p~2UF&=`KU$5k^DcMt@O=M*{bje XNh6) zT1pALiG>S82Imdah3NJMCglPKfvc2sZ_h@0%zik0u$NZKAv!2N8+zWX5CMmc{ZoTZ zg5By$|6$i=aDIpnRRbDt6hXQ Y;PJzX4U^}l8B*QZCmc4S?Ph)N&Gz3{ml z`0g4q+#i sFKo5~ywa=)O=1e{8xW_qx7ly`Ahh7eu7s$FAFfN^4X5G-ZwX`o= z#xxzNfpF;%E4Ve}=8%+VcBpjQHCSET4p#@h$|!EMP~d)Airnun(|*}dMdJ2=9k(@W z6s5vz8`WA7EFq)G&T$Qkny)ZnBa49rwZ)5GWD$G|>sO|m(oHI}Y`A$3y^re*tY9%Y zr>+Tya`cD(5b3DZUf6j6WiXMPx}SSLTd>n^)rw838&hmuWAbc`w9UvRos~XHvhtMH zi=xiSCWPuiH9HTyb&kM=4P}j?4KP&D<7f#vlwotu*JF 5Le3Xm5gfZJa|x9_^qLU=e8ycF()VFzgYw^urY)OVF?h7YW<=7*e)VVt-Gkd};7 z4&nrB(D1zrba{bA*GlY12CN<_)-2`dqzpOcQSf3rT_=M6PT2rH^ceZaLZN`N@qar) zYmDkuSo6jo@ogEUa^UZ-U^NFOUe~xfNzf-L1>+wt;!sc>ejRH44rH#d6yn!q09ojq zAurq15PoC6flM$3Mn2GBj} zCVNI5h#7!M)V+SHu5Zp^TTd^#R3y!~!^`pCjeW(0%^MGNgngGB5$!jQOy!KS(RY~l zKN*3ELz!5kTb=r_2I{Xo>U3;Hv;H;ATsO1`@QDmomY%W8UtG)T>j|s<#e28AJEIUy znd8%hK=b86xBS&D 6_bV>`QJf11{`5-1nqX|$m@n%$RG zDh(FJX33V &)%zEQ=0TP @5L{@wp`lh>Ul@FK`ANo4u1+SuDNW3n1&{v(|?x~IIJs< zy?o5AmtIoKX8>B3$4TZ0ZQ)(0csUp)8hBK72esUZDe zWNCR;q3p0;zD@M&l5zS?#f7WgdU=)6?{i6@=rh3XFu(f@wMqzj;pGz$l9Dh }yld?#0*89vi_1j|~eRBtND?WzSHsYt}g;$1c&Og|k2f zYrE7<_LfUm&A %pFl0w8$+lkc9gW@+`1&ZW)N?J>!9WxgJbYv3OgFB z<}?OOsba1Gmmy(Sc=m?(|E^#gSR FB} za3u|2aP3(H{6ML6HVMEQ5k=&7_yXqLm+GGC_XFRURlH;3jGUrN{R;j#v(c*Mt2fCv zY9lN$b}p^T)%+Fl?53qJpba7`{h}5#m;e0K5btmU> zualLCF`niQ!!-7e!XqrOPU<1dd+2_5sd?h%1GL$*Y ztglOGzpf=bH!tCJaCL>3d@J$YL-@D4b*azi-zzv;|ByX`S^2Bf0bq97I5o%QO`D*1 z7KaB{R}#MQ{--e1g{I2=B`oLblEWX&L_6E68LqTvdogMb;Kee6qi`?hn=f!$rc1EU z;}$bnGu!2_=fDB3Q>h Iw4@oknH z-9`)zphf}icArI^hkJV6!e*Ij_q@VaN<{-}ju&5!L98vRJ#)mqgJo%FgnSt_0VVzG z?PY+|S}gfV9*K&>^LSgRzDaWj3q?; M({O zI#&!392=qgkuRMdSnN&9;O+u0d9ZW0QV_HCRJ?!9aKn>;)^vFm-6VP(?4|j<{Kvm^ zHz6en7?3cLGP-6Tc>6K;iL>gyhqV#J8a&Cai5RD!=eR|Id=RP74A@j^122(>f~aw` zC_+rv5*nHV!{e `FXzM0WVq5=SRYn1rh-Byk#eNcj31~!Wc zwyhX4PSd(>_&)gE7m}BQHtXNk4D#7bmYU?JF3r8}79G= H_&CsPY1M{WUP zQ7uK=7uXVLzAPoAje9XWH-B8GZLMbKAW>KzFenk5F__f{ws;=rGZuKpNBlejdFvdk z@fn_nxN4J%7dk` -P$2_J(?f3lksCn+=6I^ap6%tQ|WVW+7z(l))bNV{lwS z-e%|<|KH4Z0Al5jsc=X~z-*~r*13E16l7K9!VBzJ+Gp5`F3OiPTM7IXVqfGJUT&=F zLDXF|>9@~8-_P)T(RGhAeIT~RwO1>qYNP;?b;7U=(PBZcvo5pyZf^li03-C^qv0}k zsR_bQlhRD?I2tgoQF{nzX%`KIxY)ER)&e~2MxvY{o2EAAau&H3*}EvCBmbh0EQRy} zgVKu}+KDggkQWme_B(?vjt)_P8VE;Ub=SIzM>M*V0x!_Xzu$K4?nZT7DRE$MY+9}E z7465m14HQ3hL{Dz!sBhkP>N}BYVK SNK;)B|DK+L zQm0}JXF^8&7Y^G7b;-34+ZyY;+)g&XZf=zFL-f2Mol|KTwbbq$>ccV9QQ{Ab8Hc+D zEj_5WWu69`vMrx;ym9@cJvNf_9k&nCx8>u _iiHvlA{aGBB#JiNo90J3I z7x!_@CKW<4AyLGNr9QlsztxV&H+kbj{>01a6m-$$BlDiP8UMX?cD|Seq6D&p44TQd z+KE87NWTg!Ra05^xtG{yZYhj@2a?(|dp BtMhFwBig160O@0JD=~M0=xZv|Xg@Gg z5flrbJrZ)W>`DF{Zcn)aT15;gA9MMPDv{46I2sb`0R!$|bw(C0Y+09djFJu)vt{S! zLAW<$mxQsO{Q~1FLDyOicGO2aT)cR57jmIFZBgeY4WXYXkVNs;obh)0=i4*FZ89F5 z$z;bn^y5F+RDqV40dN?v7dQNQPuO95Bly*e%)nzb?mqiTlxq$nTf#WZ?I4rh)|2j= z7DW%>`#BB}h2Jr=Q>XsFwr;JDgEPsIfZG&mtcPqQP9>J9`m~iUTA^U)C3sZK^%4 z*}f5X&7sS(M1fHBy1b&Ia>RYHged^@qESGlsORKNcR;8tJ+^(j7j=ZOaYiYCi8Sw( z6i1~491P@{cf{E(YlDu*JGTv;j+mc%JM=6Klt&rv6bw8zuCl6cpRclly7|O9GkO7< z#aS$mmYeI~gc#+@%ckcmkJw_&b<#fRp&~E^c9jU_(#m5tnFu3-?3l+W7gcx%KR$mo zo7}P>Qx(2xdZ;0mMMkrG^?Bv&)>D||D6!>;nNf;55*@;{dqq+*&EDt(j0`^yA|(X^ z55~ >vd(dBwBht!b?%`Z%a~c6-$6ZN>d#g=} zBwu8~@XsCci*UeJtp1qfJv!jaOl-)`T{26eFXV+nY>7>Au|BRmuomv}2U^n5gq$(v zLNpYp_k~99laJS5QkQz>9D+@>qEf5gX}(5RTOg#hlL~N?9!gbEE1(w6>8x8TyLfnf zys5qY*QgV@;!wVq3tbx|x-n)CkNW-Vp2{Lt?eq_ADp3yQaP7)_^51#pS)7ZQAMM5| zpSvV`0 F%Ey6+O5xW!?n zCCMuW{I~BU4sjSK-;~fIJh1mM%$=9WBjPK* D-LAZ$Y7T;A-g?E1gK)k$w)QgypvN}Gs zGwoAc8n=6?#OdbRO#O#A@izjiN-sUZkv^LUng1j2JV!Lk#f C zt+4q$awH#cZy?>0CrS0qW-t7s3B*rv+gt5N6^1OJ@=vLfRM!01OVMXv_5H(g=w^#V zzcy(V)zRo+{xq^LNpyy4`HBv#AOfQqK(5@CN07F_u4DTDAL~TtWA-NV5i?mV!yjI& z5swn}{+8klg(fQ1gdZjCO0AZ>OvZF8W_5207D+C%zzNmf1!Qa9?a4Mvm!Seixy))9 zldoAXr=!&C_K~{Jl^dNJ9PHU7X6dn`qabvud(>)Dvk&IE{m~zIj=-bD&d5XRuUIpX zN|)$>Wsf4+G#j$`ZZC<#>1kwP0x; j~{G z`0GUu$=}JR!DCZkQu_PLhlf(8j@PU~IJ-+ti`VhvKjfh0Zdl^gZ81)}E4y_9^X{M5 z&`dYy4(QV04?g6pq6m{ss0O5w@W@*391tHTR3jCI&A3CSTp^$6V^)GWEKoMkqrPQq z40APR hY^7#JAwL1#+YRN{Qe-xM#C)c1q{f~hMAgMk z Gn)ye2u2Z(8bFBhrg`gFVO01~&r-x|L?dD*dUrt7jR%chvQ_$Q>@BiHZ< zw2DS-e_-(P-w~O(d@WYy(95ty+~&MgNjM`bV2hK8?RLxr>nb%3EH0(n#+pQ@#3`F> zb~%r^5;es~l9OHPu|JezlFMSyg QG-aF2oikuYaMNpr+G7U zirIx(9Nd2u+r|3pWEXyGrIg3hZAvzsUbbOC@SRMSvg;*5$6Kkx&4`x3U$4w^+x&Su zDR*a;Lx)Cig;w7dje11ZnPC@TSg#*1=%tpmI0Q1$3_NttGTT(_zeU$`l8%n;*8w 7neO7=U@D8H0El9Z(=M>`}7r*kE=q6x1b+GGSD$Qmj%OR?LSP)jB0< zHC+>h C-DFHl}D%G0aL|r8#2S ;L2hW}5IEZf-5OiKt*Gg@`P{ L@wNxWyrwk zVg=6pK6v}Smi+SwZrP5U?{0JIBO>Unq%l&?IRn?I)NsC3l1~K{I{sj@lVjHoV;IB2 zahja!ps}8zrZt9evR^cIDya)wwMp-Q<{TnJ00Qz6rBF#)Fv$ZaYP`!(1yi3Z54|-; zb06a<=KE#Ng3~ niw%e*L #MyW7b_xS6o9llWq8nzcELi)WwEFmq@MGlOw?{tPt-|x%D zy`j6$J8(I`K(kXC(tEXAvcE&gr$1ZOAE5k?EiyKn=4v0cfSE=35>cC4$8+@%OirX> z(*hqN(87YpW7^hBVi?9{c%%C^f*)nB>2rV<#iuIW1qSYXv~7CFTi*fb(g5dM+;bO6 z83NU5CC_50#Bs4#f6F>RjgVH71HP#$4jS#PCefd)u6%c_TlGrU+MJrmhyB9L$TEm? zW;W`u4xi;j3BWetr6iQ)NxpJ4YSm-H)0ZYwfu98=dWZe8?^@P4nTb$2_9YkOJ`}ZT zWWZS`#B*b8g?CAqn+}St7gR4o=^tCc;@sUCoy^H___}@#=i%M(9K;2L!P)%#*eri6 z2t0H? djUjrTUpP)jR=552~Rc=TwE4;kaq zOlq{phghKI!`5LV8kZ^>r1+Wv`OE7CS2IfL#ytk{)V4qUdveDNS*ERepRd|;<~q`9 zO?`W*7@!?YKpFLc?mkPHy1V(use7>0^9HI0kKsSFW7#@tOjuIarnG6I#( ^gdj7Q^0i;$2j7pddl5OtuU64*dLP=uDevJa1;Lvi-C*G?9p zm{_T<$e^JDHuKQuO>$See{j)&DIQIdnPOv-FH~RAzS(Nf>_gKng+dK zq4>w7=s%VfJPv+kgad&G+_cz9-%N3}_6dc~VOr$&Tc*UiM-%XjWS$5~!Zv(s&%`(3 zZ_w_9rK_SKkGl0mML|;@q->H^*pB*Kz2B?z^?o)yig+hHXYy?W6oa5EUoCSsO)GBS zBuuqA&FHbbC}Vc>XJHH9{>@vj70?k^Q>_P;<;Xer4{+s`F7Vzj;B1l-gfC`Agn(k# zW}I@JpJS!%#;JIeC{I9*X40@p0-Dbb@y+>(wLrIRsy<2b5NL1V*ey+zGaAf0@VMnt zXLW?HzBhP&qQ$=k*UUVw#yYQ`fM_Il^PQBdq!G4Oh8&&v`#kDZ*50K(F{=RIwW@kg zB!k4w8Rm1eIc7!jk1Su-w+C|uW=6fZgZ^ezRlI}t8O@*1XE^HU0DEHaDzPn8o^0=n zTEJLXfnn D$Q_*pW zt69Rf^~S6ydlnFOuf&IALJPnY!S>tN&yrez4Ijrt&tweXeYb9rtz3q=k0Br!^1S;4 zEcUU-w4t!oe8yI2CMvSPzl>c>RmJG{;REd+8(qJrA<))3QMF7-))Zl$$V0KFJ)4-? zfbxJQf_TSSbWaMA1Bco(YFVYq5J*U!Pb8XF%M{S+!GZF*xxnwef2Q-ZzN@GtWWvpa z$Q`h5I86D&{sy@+X4uKD27gfcOK+$*iyHfJc1~Y%!Quf4apmb}&hP+C2m2Ox;ae|z z-kgWX?~*7193$A16^ZXjvic*sSQ(nfs0|A_NdW+D;jcQ*^k)l%lZFdG5X!F>ZTf*O zXbrf`Vy3+!I|}*5DluewXRJSq?_ZV_rME~c;Ml7|PIPF$_m_4Stg`(drM|pF%qlN? z+gnLZj6Rmzl7oZhMT99um0=9T29kK2 cu JBh_8OFg#8$tB*` KMe{Qf!WzV^?vw3;Ns!9cryR#Mn{;J)=H@5Njsl+b3?TH zT%6@Ok8W1|R%sLw>Nq1^zIQZRoKSw*2=RTh3~*I(Tr&}948^nL>Jx3fA&_fg?J#JJ za4H**2zRDpssAd$fk3ap%+o&w=pJ6Frk>sqz6>UT3@>PLeC{X0d&~ejkU-b7Bhws! zR LTu$17&`^)?T4#NTppP_DkbIl}y-;r9y& zeE`_J2F>_CkQcVf-dHlh&`z+<@5-wUO$_qs%teHhxFSuHTX1?oeEZe0%NZ9I?8b6Z zN;n?NGT)aFWs7KW&y+RH^$#Wo^gd43uFlob8i^F=GqTaT3>e lxi890hd~1Lt8t%U{W7a9*!gH>Og3`o(&Bd#GP)cZewa=f>oO+o+D(Te^(9Z zcv3-$$HMm %14=?(PRWjkgY zz4JKN-FI9yCkN;ADtK0{Y!X1QKEr4-WZh^KpywlsDFqYn YoT;KX+MH`?8LlBxPhOIMS}P zurUTXsqXRRWgEI(b<1qYG~@UWogcq0^O}LuzBDN-x%5Hv8qF0_{y$>_poYcJPhXWK zfBTc$)mbbKYASf783ge;um-}@E%CxA+Q|cG;XCU*)Zw&6A?(CW9?aV?-GpEIfr~4Z z8dV(Lx|D_KWkM@k#@{GJetGlON0=AOO9h#7zYTt1H&7Wfk(wdgxoa#X4m;Qhy@X`S zBp6 Kiit5_a)n z4vu5 od zp=Beg1u=$DK)d~l`^M@oY-TFG;jKGLnj9G+PErD#hT+?umC8Mv264LTbFfGhrs0nC zzcLE)>m5!OhI9pi>sclGa%pZoXPyRoNeiH%Q`>h+$lb0!Rv`1zv7R{&IRK1$eMy?1 zAP!z3B=?%svGQJ8-nml(@b94#JC9!3@?!f0l1Xe53@^K;!gYzOI&H6FI&W%l0S|i0 zA|im9-{FfRv?bC1a^>UP->>$w%A!Cb1gMVPS0rj=%=_U7T$(g7+sy+BU$}!nLYMws zTN^oOJ;K=66;tx#Txh9)=Z1k2{xcL0SDK#I1e;yvPU}j?ns`R8^C{q#w&oPW-)B{z z!?wA4n;0&LB6Yx+AykT|CWH?YwO6C{^wG<2uCM2~s?m1B%~G$9UH>DrrfazjU-uFd z*5!srvdr*1oph#on7{C%2{FOtKu_W$hU+f%vrzG- Gjp-UU%^z}MWB02c#zg)C8W%{i#%i`avE0;7jpMJrZ{f|6 z1Q&nSDI?RrNtr<6K>08`q75vTp1bz+O*J52GNW!~nKZP$Hlx;hhb8SNNsoJ)*gj^( zCAe&qOyMM#b}Oih03UNCX7zE-)j!PY-pm7tv0pQU;_qRTNaF2mTf)W0vn3U^nzCq* zH}bwI8N_H|6gVmc@Xp( V=ZI z&`YCx7=w%UO;%@E &pi@ptWSTdukPODU@1zwKBoM)IWY16~YU=rHTzG0Id{ zSjQ7x0K~dZ@Qdf+J@BCvv!}EZX_wR !K$60@BRdb>!upNpl}UUJ+yqU4%cuzwH!mn z+)Ih{TszxSLeGK$=IV6pUaQ1s%C&ptt1XUpOwxBGc_83Hu-_d?m8&1nWu(a(3xCs1 zP@}#hnLZVSDi_R-F86WsBsQdj>YcELqh6;;mAEPDvVLa0Q3&YZ@?PAybwUB!6~9x^ zi(k8cfz)P~thUf08B#4_2P^2mVIe!^j+uf7MSnpJQ3b$Lur=yh0{bZ6@b&9=REBh8 zSg?>pgx=n3+ULn8V6Bot5cZs`lflSYmeT{WcI JAI1zr9$fCFNkj%EPG!xV5ScM zPdod|&t)zujI%l2dwq!pA8AE>UN|wPq=#K0%j )2-`*?vnPYg^9#e&Aw>M;JZNw5Nv4%#$I9!+InXrh_S~Y4=ObS>aa(QOFY|` z9kj@Uf%eq0H+4rqA~s=@os(M3O#Rpskr8gL_I}~lZzbAuYy?YA3%Xbus7+zWGZvj| zkfF8^YSI}j?D#Bz)p(gvDYu3Z_2V4`F6}0~?DwBZ^k4{w^e54L$iDA{5c8jR+mEc6 z2;%;O0Iy$>S-r$9L?-Tw{t+U;m_+BfWWBR;IDi})jKdw|`pDa7&T9Sw8jKkgZ*=+g z8gkd#XqqYYrZnVw|F0m8a-2$Y_4F7)07T&gU56eUuq7U7Hun_E^qS$Z&-yk9MeK}} zFOGd0*lDD;r;n3CQ1TZ3A`RfYD)_A_fNC*<6oIN*fxSrh1H^<^^ndI{oXXyX$U@ka zsZw?qqyN~q!pMZ68jG8U??73|dM4jw@7FpW(4?P+8Q>wx7K$xR*i>p}S9xxq$HlNm z)q4J_u?PM>)?nM4~18#d%IaHIE+Kf|zm7 uzDP&+Ex# z#*22e*uZt7aAa`;+}f 1cJ<~y8?0>;=XT!Pg5vhF4C zw5fE=B2a6Q?47koQK&zZw~+tn3$Gum;;bI0KIM#qPjI7xSV~PILPkG0VMb_FUEO zRz_R>N8;bCPT*7Beui}sYhaxCp 6H ?Up5h;&GQwr7ASI}7R>#?`f?VE%a5BD>?5#xOP;RJmwNHWky7JXm2ZbE* z6M4lsKTMA@cADY`)`=)f9!%3(Z09#%is)`qFllI6rN`21MDicTEl-fi=g;iKCf7+A zT|W~=0G1I4YcZ2u#0iF_EXETVOc6PjHR3{+h-W!~3tcV_2mT$pdvbE`akXYynulYG zX!zP}QMDl`U%&QEy7<9_to`}K<0 jvwHyft{<^Ej_}Cl zk^%CZL|Y0F^t#=aJ~5EzEgM|LygB7jrb$v?;8TWhr559z=HCkHGM4gqB}4qGyiEx6 z5w5Z1CJ?~;Gh>h-tX{(efFI@~-aEJM!YLB9+BA4Z?s*54M+b~ETkxTCoF%lSO9aHl zX!UrfaO3_sBnJ>3q3sn(zqN;wQt#D<8A{BJ2y3J#dDx>vP4c&3hnG9RS2+$bl&FB7 zi^!-BqwXypz1^sa6JnsU!%zt_Lw1CmsQ6bUZJ3*Rt=OQfVobF$7J=ueoh}7s>7uLS zd5vF=rS7L$*T#@cjyVtL(jIKi56~R6kt$$&QtZz@=ABP)PCBAnV|bf N?mf~QIY#flAlly)+hm?T?4oyXRy~e0^gyG#9FE{mi**+i3j?P+f mo!<%!o{0LRyJ_yM$4iU33)$+*#q zy{p(plLqbF1V;) BC^b8vmg`srVn6CU5iS>B&}Fzc#vYuG0jw37&qJG z-eQ%XLvtYs{15I7P64<^!>s<#gW7H33xJlfwPtUAdA$npI9N6I06X489s!BIK97_( zzrJ7zLEO6jhoylClGGv?Mtf~Xe~ME!X2(M+kWrV?J}Yd!b*kltG8Y?#az} 2 j^2EGj>CLj zz@%2-dh{b5gFYzWRh! 4A6TemC+}AEUb?9t~~gSB;^0<#=;@-gTW+~lPLTY*Wzl1DHx6oa>Av}+Fesz z2~VARUzAO{f~1d37hZ66q_0AdKY*6^z|r=v+3hRvwd|7@)bbD6SFdx&Y&TYfl(SbI z06t_o1k}4MVA03vKPbU>GPaA2GTd;o;&G>~8fC#2q*)&GHx7W50+sXytCW-5T1DJl zEe1=^z|XSsl4y;9*ghQ`M^06*WVHwv|J^TFYwOZ(5S6}}1qM| uQ?-i#_0pGDTi19_lJrt8ZwX*v_BZ6g9Kt2(EI41v4fp#V^S(S)=*5VMj=` zx??H%4JCo5>u$wJ@&AP7xR(EP@9!-utBgtvDr#gXeBn_O{Y`cwrJE<++W8UTTvQBu z1cCH1k%$hB&=4`B4xbMDpY8}}P<`7vh3A7usSjB|F cgLLt69KVY!1#yhj^F4}%maQ{=B6o%oS9e!{ Dm6J1R=qFSjSS8cW 2$0-_5JG9`Zu>6~bZuxmlq z{}I@a`Q+*xTwHl^-?(1SLS5 ;Zfbf=Fy~5nuA;$K_ua+2a0*uAQVW&0q8Gwx$HqG6T ztqkwU{EF4}q%~1}cf{~v(e(TY`7WM(2)N+Z9LN!*awhS5Q@<=MlNYRWPwdUWyEoM= z2go0rxi5vy*z2Ds6HM4hc4l?g)D6!pcw@2mfNy_o0&$W;pbl726JIH%!CVD(CQcBh zym6oYy|dy!17@=VI*WzD)TJ>en61jaK^RWJx=os4tKiLWVA1Nx6&K8N*m1Jb#x0@Z z0h12PJ$jGNoVunH{wn)fosD<3weg5hyk$=Z!h5u}Lgo7Cyj&~#FgTKSI}sVFb=+qp zkAE{e)Uwcel&0?JWRQE!YOtU1;!=41nt;sfa7u0}9)yP29;1Y5wE-9Z_#s%d^5c3a zyB^@uHk02VZo&M?Gn@4Q3CXiis^e=jy9{R3YUsY7gqZM Oa8`r&Ik2{uVn2h z<6bw*ASiy~0lPz|X*zUTgXWloCxkQxbG-()Kg{SV+}rNp$io!Jny$U}L*XT}0mLgz z5UPx}P8Z#enXjX1zlUj@nFH82W fTW%`cRxGD7+RGjPkKqxGc!V=OHD2O~ljK5V78}ucSxGnb!ZaRo>!1qw ;{ds4se0V2<3mX20*8yxZrt%qt<9X_dfXRxO5?$ClgtQz zWY25?ZbnpqMy%$BAyX3eq?;Q0bXD1lX3&R?$Mej7+A~|Qv_e=a{c=P^L|m`;OY zd^dD`nsai}F93lPIa|qVfbyOd-d^vHN}(Wi(TY~MO?!PLxh$(KQcjiGXN1K9^7V fo#l$NOy*{z~{hFKai-5SzU|5!( zOpxlcR 7S?Wk0u>ISQ1rwdADudYs+lIX88RWY0}&Kbf*R zCX>yT-7Genz@~;`=Wep=ce*_hh_Et-i4F~Q<&b!|OzN}t2G i?3vLf=t+$1DF_e_4OGU1+N%GdEx6KhDIf*y}sZ@Li*nis$fI`y2#(p{)GA@jefu z1mKfmlmzGR{s`b~WrakSw3a%eBQKFA*UT!D-pNp;X8eYqWR_1Xt}S3r w^*M`$f@?CEI z;#ls~cx` J;Rs=&$$VY&znbNAb8;E)8WRHErDr&n72H}R?ff*Qr$ zG^oWHnlph^V1CLnFf`~3ELivnMyaW~+;zB1>K%_J(WTN|Ggfq>(VU6glwO!WwxuG4 zY*vwv?eEq!(Uyf)4DT~Q5|J_7cRwfP@5~|GrdxGLx|AG4g9?0m(Q1b@pFQRRhu=%4 zqGaELyh3P(L^s-&&6D~$Y_Tn_08v@Q{+dF}ubX#K773wC7k27943;T&Cx4hM A*>3s3%)Q~Tg>tPUQOg;t*bu_Y9PCwrjd8R{yc~9t#R!Ov;3F{=9S$c_gZPw zF!HX&S3r8I{=N9NU-i!P);81_;Hp^~EM_2q_qu9`V-Ps2vMXs(GpgM&Vjio0lQ>4U z<>m4<0b+mqSWdu6F+sMLN++7*%bJ|-9WsHSL$%3l`4QqhU)h*O5_AOVkq ca7 z82{Op+Dv-KrgQ%gkO3i|<=FbTWf)a}u0_dt{Vl$pY6<-*Bp+qSK7S`A5eDN5#@vGR zaCYmP9M_=^pB{eOf;@v5p#>yhSz)aCmoeO9+}NLWYFq|1Aw*6}Y0OfMfs*uq#;^WE zaC2b=cwkN$t+_;}Z2>mPIdOFD?LbKUjX=A&2wzI Yy{KC-YOQ<3;Lia>)Z=k zCE^ MiJ`sE$d-u8Wfx!rYb2Jyt1xAg zhZBdS(O^0w0lhLS);NZ%`i$5Z&-_;XS1BqHShd}lUv&1wH9bCGB|Cp0 6c1X#g8 z>kI4vTbot7$K}Pi<}l?nTV@UNm(LZB><_TLH!laswJ{zca7w%oCjzfgu{v>~V5|0{ zAZ@(j!R-3h^3>2slu{ZoOFprMB3v)LJ1An%1(h&*{qZsSBaa~yFD7{!EcN0I53jf3 zVz1!$a?-HivSF&xNtnXhGWtSfGF;r*GufqfyU&KU~JH8+m|vXh}cY;B(Eg% zjEbRcmN(MQawggUcp%jsd+P16Qwg5mzpqeSb&z&45jr9yCiJ&~&c%QCWc!!iYM75} z3@k>+9m-VnTJSAhM(=)hG6M^g7Va!14>gDCxZy9xCjs?NvQv{N}C~!hN!Nc>ao-P&Y-Di#e%r z*op5qKcVG{uj53g7Dt%{kqSvUAOP3l))$oA+03Ank^=>6UZbuFbN1*CP85@9$jUZR zGBuJUZjCS^ziB`iK9NxdP5JZ4RJND>6u|u5R$lRo2lRe>9p^(}zLG4&W-G`L*S6Kr z^B#^kN#XLXn@(` Dcij*f7dligtfQ?4#a#d)mu5XK2JwKI!y9sKvy_wR z6LiD9zA_U7YD9E!sNyW7c<4(YW@GO0%hYd6bf4s@f#2(m)fQ%1Qa=J|ws@a|l^MQN zp%nD6r(R?~TltVoS7b66*Vl5nB+hu?)AZBk%PIi*9(**#0KFP{GrI{>yDo3GcLF~O zB^w_WC5c#=M0aywyfqnbtRP+I`d_~C5{ao_gf*P+`{+bjgf=fdtMRiWAmkug%JW41 ze1zdwV9njcG(e@&yW}x{a}@lFQD5A}uKV!lcB`EyZuvHPDNCx!K%~lEus2b?B(xYf zI9{b|UZGumo3P(RH6iWT+>$N<1|A3I>R^0tpQaM%Frwx@EoIu9_6Z1Qb#BaaouSR^ z$r)5K&&nw)^c9un;b9{;mkRd|e +)c_uGE>j69jG*zhe}3y zXEoE88U-Q4WBwH0!1Z#(HQFUGhMRrLsQQHK>!{h+lh(JZ*6bo+lC(@HD@^`edLM(k zdx)<^wqd> + zSAY}F$+}x1uo9=Cf)|Azx7#NP$Em;F^Cc*Wd!LZ^h|=fh%*1dd_p;(=Bu-wD?5-}B zKlW!KOQdwwq?*|gTqNn#j~{Ibm8B}g@KtBh4H>Dk30;tGDlmZ-wVQ`~cQWcLzGlfm z@zmzcE63a*%p}E}v~0)dz8|BTN;j)+(J`r(l3NN9h`n)3fhCVz?PKj3l>*M>%p!ge zA |KvFzbHltTEvCQ=K{}A6w1)wQ-h1(&nS@r8gP@5&d`0wxWYFh!2D|jLbXcFWty;G zR%y9rLmV_Kw~8=Mh@EbkIv7k=4AeUjPZ3%6W;uREbXhJP>H`2}Bo;ldp9Z=PbY2gA zE5I(TT31SQ=PSZ6ID6bHkefRmfCjw9%Yvnt9NE^Y=syrHq+qr^Ghs;&A-iuf!-S}D zzs?cbxu8S^dk(B>z}zEnoWpM=kU2Pgo&jcI26oMuKq0)gZx!81E5Q*G 4H`b}xV{R;eoWE}ZOk8OyHCVa6=_<*r{Q&VR+IkYUhZib!e zGIn^^EsnZO _7m;Hp ziICWWa`%^SI$d|&qeti-Rn_A;lqT#ZPs}5q+}=+riVq3AUQ8YZq@(!s{6oGPKi}?H z3IQ*%451l!YuQhgwYNRV)q=wqNn;QW>$IBdOE&-lSYD! nWy*hd0c{^>dAuiSd82)ku|^*2)%Y}D;wryKcZ%C z(F&vKrIetEFK1se58By0L^$6Sb7#nR5EX?~90puTw%7*;UZL| XGbuQKH8|60ti zM+BPB! JY2a6kq8$+0Y|9j>Vnw=_62}#$E iyRhDw6*1 za%z|YthR&f>`=G9X0rB88Z!c9(AE#X3I2JcwDgnF>O%ayJ#M*iQVnj-?jr7L8$$eg zP5J@&G)jdUiWa%^i>a4?zfpT0B($aX^QvC+ G~m-ay&r5-Gmju8}|cby3S%XYj;OvIZXfqtzco`@fo#R zObnSomcHS8!l+dH4h$^`0wu#yAAR@VXyLXtHc0A^_vH tbs)9-qM6XqV{v)? zma_~fRLDFsKzSv=BhWGn9TVoAQV6s|Ypm&}UZ_bc9}nw?m?)k^N{1U~GkXaG3zDU7 zO*R(ua;Y0|`(N1t@+@BS2_?X2>xYz)VC#kw=0~_b&Y& PVy`D%}M{io=YaFbT!kc@;WM}x} zsB-I`^4klZpl-YyL-?*C{=rd_v3X9h`}K$UUF1x7n1SZ8`q(>*_Y$e)X1#an3ml&F zy8;1sZcYIFp#E>2RkjC%QhQpofN~O8xv$e)X!0JxlC}cVi1SRT8x+4f1ZRaMpftk@ zdux?Vx-s@UQD1$p1KJrHK){+xaBrGLx#?( JZIri>Ag z?(51XN;J}R_ jOM#?H&*RJ?Wm6M1!pIOtL|qB(!W8 zW^VbJQL!MRQKOHEb&G#ck|oS)XZBH~p-L?BaiseVKbzP5Tq#xVjE9mTzjjQI)C8U6 zAiJ7A!Z3QdK5ChT_k#s)>Zq)k%p-KMF*9CfLj%G<;KJUlU>GZ-uaeB-ku I7q ;IB!sF7!%VHn29KHyo7!=@ zYIyqXRT_|8PAQWi^mJ^NIFvDiPDT#8P9O-5DYfW+DCFaul=J{WYc@cFdi>o@FZxLB zG|gjO>l>;F41(Evs+j*SY{p{L0Au9vRS0Z%!$LBop7WS$QNBQnxE8fhj~+~Jx!i~u zhXbKkStI rN12#z&s-a=r!Yigy{j<|mz zn8n1sQ}@-ml>yi|n~2Quia{YJO@e43PR8Evfslo{Oml_HuJPo!yCex7TGuDqG@Y^= zm~2>;y*rP @6l zWMP#YTp+;*`6B8;%eLksLqWKEw^X3>0T%ewzE+KjTO;e@t`VHH1caaMXgle9mfS~9 zzXmP~zBD{rm7w3mJAqx}MoDct+hBD=5;pm@;5}*<<$+;>?xsQjpO=z*SBq#i*jLFq zDSJS1E_B*^tjA{R)bkz49MZP<;}fAqxih&hrRcODnXQac6RStE-|Jnp>D(v+B;h%L z=Al#N1}0x6OlsZ48+zlIrLGcJFR_^g;o;=F=AHyNYOn%mO%D4a?TZ3ZadntYAi}bT zgY)P&HjvX|Tiw;CghiLONc-~c%3!45@HBfoAa{xV1KISN!fUp2DN?3=lgH;V4XO;J zKf*s!3*EKBXiY`K(x{wel*Vijk49c^kE-^9S+k>na9L7nk%WVnvt%&UnV^tN4T!`b zcpIiT$;ch$$?QC+AbPFr>8)+vp I(alnh7hY0Wca;_@;A9U!zLYpYtqNjPUM2>M zj1g@ChYC;8@)Cw`g8mv?j;FwRiOxk@NLCAW0GzPs`Cp2xJ7C$)ImE>^gfz2qrJ~pO z&$pJbN45lhlAr-kpXLrL;&Fqk@C2SY7x$k%3rzqMwp&fJMquSmZ3_b(_$kE7#VWqL z85{NtNdHO=@<+#4W=hH>S!kt69s#;41uNjSG_BraA=j8AE|Vi)+nntLKCxHB*$}{{_fvZ^*X&sH7rtMRie?DGg$t_I0E{f9}guFalE87o$ z>*`nd|H<(B>f9pdi$W-M{}l_*gvajgaZuoFm^_-9>?`Rkc8P0K_|u}&0VBNN<75RV z6UhzK>r4@9*Kp=}OO~w=zJlPv$isK(`$mFO&%i ZLfyWkC;g|p$gGN&u1R(4P2hTBnXQ*x3}yfT diff --git a/d-guides/google-oauth-setup.md b/d-guides/google-oauth-setup.md index 051ee5b..64531b9 100644 --- a/d-guides/google-oauth-setup.md +++ b/d-guides/google-oauth-setup.md @@ -3,6 +3,8 @@ ## Overview This guide explains how to set up Google OAuth 2.0 authentication for the Porta application. +**Checkliste (Google Cloud + Gateway-Env):** [google-registration-checklist.md](google-registration-checklist.md). + ## Prerequisites - A Google account - Access to Google Cloud Console (https://console.cloud.google.com/) diff --git a/d-guides/google-registration-checklist.md b/d-guides/google-registration-checklist.md new file mode 100644 index 0000000..bd9e182 --- /dev/null +++ b/d-guides/google-registration-checklist.md @@ -0,0 +1,116 @@ +# Checkliste: Google Cloud — PORTA (OAuth + Speech) + +Operative Liste: **was im Google Cloud Projekt** (z. B. Organisation `poweron-center-ai-org`, Projekt **PowerOn Porta**) **eingerichtet** werden muss und **welche Gateway-Variablen/Code-Stellen** dazu passen. + +Detaillierte OAuth-Schritte und Troubleshooting: [`google-oauth-setup.md`](google-oauth-setup.md). + +--- + +## 1) Google Cloud Console — APIs aktivieren + +Projekt auswählen → **APIs & Services** → **Library**. + +| API | Wofür im Gateway | +|-----|------------------| +| **Gmail API** | Mail-Connector (readonly-Scope) | +| **Google Drive API** | Drive-Connector (readonly) | +| **Google Calendar API** | Kalender-Connector (readonly) | +| **People API** | Kontakte (readonly) | +| **Cloud Speech-to-Text API** | STT (`/voice-google/stt/*`) | +| **Cloud Text-to-Speech API** | TTS über `ConnectorGoogleSpeech` | + +--- + +## 2) OAuth-Zustimmungsbildschirm + +**APIs & Services** → **OAuth consent screen**. + +- App-Name, Support-Mail, Developer Contact wie von Google gefordert. +- **Scopes** müssen zur Code-Basis passen (Single Source of Truth): + +`gateway/modules/auth/oauthProviderConfig.py` → `googleAuthScopes`, `googleDataScopes` + +Kurzreferenz: + +| Zweck | Scopes | +|-------|--------| +| Nur Login (**Auth-App**) | `openid`, `userinfo.email`, `userinfo.profile` | +| Datenverbindung (**Data-App**) | wie Auth **plus** `gmail.readonly`, `drive.readonly`, `calendar.readonly`, `contacts.readonly` | + +Hinweis: Nach **Scope-Erweiterung** müssen Nutzer Google-Verbindungen in der UI **Reconnect** auslösen (siehe `google-oauth-setup.md`). + +--- + +## 3) OAuth 2.0 Client(s) — „Web application“ + +**APIs & Services** → **Credentials** → **Create credentials** → **OAuth client ID**. + +Das Gateway unterstützt **getrennte Auth- und Data-Clients** (unterschiedliche Client-IDs) oder **einen gemeinsamen Client** (in den Envs sind ID/Secret für Auth und Data oft identisch). + +**Authorized redirect URIs** — pro öffentlich erreichbare Gateway-Basis-URL **zwei** Einträge (Pfad exakt, inkl. `http`/`https`): + +| Pfad | Verwendung | +|------|------------| +| `{origin}/api/google/auth/login/callback` | Login-Flow (`Service_GOOGLE_AUTH_REDIRECT_URI`) | +| `{origin}/api/google/auth/connect/callback` | Connect/Reconnect (`Service_GOOGLE_DATA_REDIRECT_URI`) | + +**In diesem Repo typische Origins** (jeweils beide Callbacks eintragen): + +- Lokal: `http://localhost:8000` +- INT: `https://gateway-int.poweron.swiss` +- PROD: `https://gateway-prod.poweron.swiss` +- Forgejo/ALT-Prod (falls genutzt): `https://api.poweron.swiss` — in `env-gateway-prod-forgejo.env` Google `*_REDIRECT_URI` ggf. noch setzen. + +Backend-Routing: `gateway/modules/routes/routeSecurityGoogle.py`. + +--- + +## 4) Speech / Voice (ohne User-OAuth) + +**Credentials** → **API key** (empfohlen:auf Speech + TTS beschränken) **oder** Service-Account nach Vorgabe des Connector (siehe `gateway/modules/connectors/connectorVoiceGoogle.py`: `Connector_GoogleSpeech_API_KEY_SECRET` kann API-Key oder SA-JSON sein). + +Doku STT/TTS: [`b-reference/gateway/voice-google.md`](../b-reference/gateway/voice-google.md). + +--- + +## 5) Gateway — Env-Dateien anpassen + +Alle Werte **ohne** echte Secrets im Wiki; in den Deploy-Envs mit eurem Verschlüsselungsworkflow pflegen ([`encrypt-env-secrets.md`](encrypt-env-secrets.md)). + +| Variable | Bedeutung | +|----------|-----------| +| `Service_GOOGLE_AUTH_CLIENT_ID` | OAuth Client (Login) | +| `Service_GOOGLE_AUTH_CLIENT_SECRET` | Geheimnis (verschlüsselt) | +| `Service_GOOGLE_AUTH_REDIRECT_URI` | Muss **1:1** mit Google Console Login-Callback übereinstimmen | +| `Service_GOOGLE_DATA_CLIENT_ID` | OAuth Client (Connector); darf = Auth sein | +| `Service_GOOGLE_DATA_CLIENT_SECRET` | Geheimnis (verschlüsselt) | +| `Service_GOOGLE_DATA_REDIRECT_URI` | Muss **1:1** mit Google Console Connect-Callback übereinstimmen | +| `Connector_GoogleSpeech_API_KEY_SECRET` | Speech/TTS API-Key oder SA-JSON (verschlüsselt) | + +**Dateien (Stand Repo):** `gateway/env-gateway-dev.env`, `env-gateway-int.env`, `env-gateway-prod.env`, `env-gateway-prod-forgejo.env`, ggf. `gateway/.env`. + +Zusätzlich — wenn neues **Frontend** oder neuer **API-Host**: + +- `APP_API_URL` muss die Basis sein, unter der `/api/google/...` erreichbar ist (Cookie/SameSite-Kontext). +- `APP_ALLOWED_ORIGINS` — CORS für das UI, falls neue Origins dazukommen. + +--- + +## 6) Gateway — Python (nur bei Scope-/API-Änderungen) + +| Datei | Wann anfassen | +|-------|----------------| +| `gateway/modules/auth/oauthProviderConfig.py` | Neue/entfernte Google-Scopes für Login oder Datenverbindung | +| `gateway/modules/routes/routeSecurityGoogle.py` | Nur bei Flow-/Route-Änderungen (unüblich) | +| `gateway/modules/auth/tokenManager.py` | Nutzt `Service_GOOGLE_DATA_*` für Refresh — keine manuelle Anpassung bei reiner Registrierung | + +Token-Refresh und Microsoft/Google Data: `gateway/modules/auth/tokenManager.py`. + +--- + +## 7) Smoke-Tests + +- [ ] Login mit Google: redirected zu Google, Rückkehr ohne `redirect_uri_mismatch` +- [ ] **Verbindungen** → Google connect/reconnect +- [ ] Drive/Mail/Kalender/Kontakte je nach Feature smoke-testen +- [ ] STT/TTS: kurzer Aufruf über Voice-Route (siehe `voice-google.md`) diff --git a/d-guides/microsoft-entra-registration-checklist.md b/d-guides/microsoft-entra-registration-checklist.md new file mode 100644 index 0000000..0f5b263 --- /dev/null +++ b/d-guides/microsoft-entra-registration-checklist.md @@ -0,0 +1,113 @@ +# Checkliste: Microsoft Entra ID — PORTA (OAuth) + +Operative Liste: **was in der App-Registrierung** im Ziel-Tenant (z. B. **poweron.swiss**) **konfiguriert** werden muss und **welche Gateway-Variablen/Code-Stellen** dazu passen. + +Hintergrund Konzept Auth- vs. Data-App: [`z-archive/concepts/OAuth-Auth-vs-Data-Connection-Konzept.md`](../z-archive/concepts/OAuth-Auth-vs-Data-Connection-Konzept.md). + +**Hinweis:** App-Registrierungen lassen sich nicht „in einen anderen Tenant verschieben“. Neu im Ziel-Tenant anlegen (oder bewusst Multi-Tenant), Secrets neu erzeugen, Nutzer müssen sich ggf. neu anmelden / Microsoft-Verbindungen neu verbinden. + +--- + +## 1) Entra Admin Center — App registration + +[Azure Portal](https://portal.azure.com) → **Microsoft Entra ID** → **App registrations** → **New registration**. + +- **Name:** z. B. PowerOn PORTA. +- **Supported account types:** + - Nur eigener Tenant (**Single tenant**), oder + - **Multitenant** — dann passt typisch `Service_MSFT_TENANT_ID = common` zum Authority `login.microsoftonline.com/common`. +- Redirects werden unter **Authentication** ergänzt (nächster Abschnitt). + +--- + +## 2) Authentication — Redirect URIs (Web) + +**App registration** → **Authentication** → **Platform** „Web“ → **Redirect URIs**. + +Pro öffentlich erreichbare Gateway-URL **zwei** URIs (exakt, inkl. Schema): + +| URI | Env-Variable | +|-----|----------------| +| `{origin}/api/msft/auth/login/callback` | `Service_MSFT_AUTH_REDIRECT_URI` | +| `{origin}/api/msft/auth/connect/callback` | `Service_MSFT_DATA_REDIRECT_URI` | + +**In diesem Repo typische Origins** (jeweils beide URIs): + +- Lokal: `http://localhost:8000` +- INT: `https://gateway-int.poweron.swiss` +- PROD: `https://gateway-prod.poweron.swiss` +- Forgejo/ALT-Prod (falls genutzt): `https://api.poweron.swiss` + +Implizite Flow / SPA: für dieses Gateway-Backend-Redirect-Muster **nicht** nötig (Confidential Client mit Secret). + +Backend-Routing: `gateway/modules/routes/routeSecurityMsft.py` (Prefix `/api/msft`). + +--- + +## 3) Certificates & secrets + +**Certificates & secrets** → **New client secret**. + +Wert in `Service_MSFT_AUTH_CLIENT_SECRET` und `Service_MSFT_DATA_CLIENT_SECRET` eintragen (bei **einer** App-Registrierung für beide Flows: **dieselben** Secrets — in den Envs oft identisch). + +Verschlüsselung siehe [`encrypt-env-secrets.md`](encrypt-env-secrets.md). + +--- + +## 4) API permissions (Microsoft Graph — Delegated) + +**API permissions** → **Add a permission** → **Microsoft Graph** → **Delegated permissions**. + +Single Source of Truth für die Scope-Liste: `gateway/modules/auth/oauthProviderConfig.py` → `msftAuthScopes`, `msftDataScopes` + +| Zweck | Graph permissions (delegated) | +|-------|-------------------------------| +| Login (`msftAuthScopes`) | `User.Read` | +| Datenverbindung (`msftDataScopes`) | `User.Read`, `Mail.ReadWrite`, `Mail.Send`, `Files.ReadWrite.All`, `Sites.ReadWrite.All`, `Team.ReadBasic.All`, `OnlineMeetings.Read`, `Chat.ReadWrite`, `ChatMessage.Send`, `Calendars.Read`, `Contacts.Read` | + +Viele dieser Permissions erfordern **Administratoreinwilligung** im Tenant: **Grant admin consent for {tenant}**. + +Zusätzlicher Flow im Gateway: Admin-Consent-URL unter **`/api/msft/adminconsent`** mit Callback **`…/api/msft/adminconsent/callback`** (Redirect wird aus `Service_MSFT_DATA_REDIRECT_URI` abgeleitet — siehe `routeSecurityMsft.py`). + +--- + +## 5) Gateway — Env-Dateien anpassen + +| Variable | Bedeutung | +|----------|-----------| +| `Service_MSFT_AUTH_CLIENT_ID` | Application (client) ID für Login-Flow | +| `Service_MSFT_AUTH_CLIENT_SECRET` | Client secret (verschlüsselt) | +| `Service_MSFT_AUTH_REDIRECT_URI` | Muss **1:1** mit Entra Login-Redirect übereinstimmen | +| `Service_MSFT_DATA_CLIENT_ID` | Application (client) ID für Connect; darf = Auth sein | +| `Service_MSFT_DATA_CLIENT_SECRET` | Client secret (verschlüsselt) | +| `Service_MSFT_DATA_REDIRECT_URI` | Muss **1:1** mit Entra Connect-Redirect übereinstimmen | +| `Service_MSFT_TENANT_ID` | `common` (Multi-Tenant-Anmeldung) **oder** Verzeichnis-ID (GUID) des Tenants **poweron.swiss** für Single-Tenant-Authority | + +**Dateien (Stand Repo):** `gateway/env-gateway-dev.env`, `env-gateway-int.env`, `env-gateway-prod.env`, `env-gateway-prod-forgejo.env`, ggf. `gateway/.env`. + +Wenn neues **Frontend** oder neuer **API-Host**: + +- `APP_API_URL` — öffentliche Basis des Gateways. +- `APP_ALLOWED_ORIGINS` — CORS für das UI. + +--- + +## 6) Gateway — Python / Sicherheit (Referenz) + +| Datei | Inhalt | +|-------|--------| +| `gateway/modules/auth/oauthProviderConfig.py` | `msftAuthScopes`, `msftDataScopes`, `msftDataScopesForRefresh` | +| `gateway/modules/routes/routeSecurityMsft.py` | Login, Connect, Admin-Consent; liest alle `Service_MSFT_*` | +| `gateway/modules/auth/tokenManager.py` | Refresh für Microsoft Data (`Service_MSFT_DATA_*`, `Service_MSFT_TENANT_ID`) | +| `gateway/modules/auth/csrf.py` | CSRF-Ausnahmen für `/api/msft/auth/*` und `/api/msft/adminconsent*` — bei **neuen** OAuth-Pfaden ggf. erweitern (selten) | + +Keine Code-Änderung nötig, solange **gleiche Routen** und **gleiche Scope-Menge** wie in `oauthProviderConfig.py` verwendet werden. + +--- + +## 7) Smoke-Tests + +- [ ] Login mit Microsoft: Rückkehr ohne AADSTS50011 (Redirect mismatch) +- [ ] **Verbindungen** → Microsoft connect/reconnect +- [ ] Nach Tenant-Wechsel: Admin Consent im neuen Tenant ausgeführt +- [ ] Je nach Feature: Mail, OneDrive/SharePoint, Teams/Chat, Kalender, Kontakte kurz testen