From 1fa124bd7c4bf779fffd22822842c515b3ffb1b3 Mon Sep 17 00:00:00 2001 From: ncpfs archive import Date: Tue, 28 Apr 2026 20:39:57 +0200 Subject: [PATCH] Import ncpfs 0.17 --- .downloads/ncpfs-0.17.tgz | Bin 0 -> 81655 bytes Changes | 20 ++ Makefile | 11 +- README | 26 +-- ipxdump/Makefile | 35 ++++ ipxdump/README | 45 ++++ ipxdump/ipxdump.c | 247 ++++++++++++++++++++++ ipxdump/ipxparse.c | 345 +++++++++++++++++++++++++++++++ ipxdump/ipxutil.c | 129 ++++++++++++ ipxdump/ipxutil.h | 65 ++++++ kernel-1.2/linux/ncp.h | 2 +- kernel-1.2/linux/ncp_fs.h | 6 +- kernel-1.2/linux/ncp_fs_i.h | 1 + kernel-1.2/linux/ncp_fs_sb.h | 16 +- kernel-1.2/linux/ncp_mount.h | 10 +- kernel-1.2/src/dir.c | 238 ++++++++++++++++----- kernel-1.2/src/file.c | 9 + kernel-1.2/src/inode.c | 57 ++--- kernel-1.2/src/ioctl.c | 10 + kernel-1.2/src/mmap.c | 5 + kernel-1.2/src/ncplib_kernel.c | 94 ++++----- kernel-1.2/src/ncplib_kernel.h | 14 +- kernel-1.2/src/sock.c | 2 +- man/ncpmount.8 | 35 +++- man/nwmsg.8 | 33 +++ ncpfs-0.16.lsm => ncpfs-0.17.lsm | 8 +- util/Makefile | 14 +- util/ncplib.c | 30 +++ util/ncplib.h | 9 + util/ncpmount.c | 14 +- util/ncpumount.c | 17 -- util/nprint.c | 2 +- util/{fsinfo.c => nwfsinfo.c} | 2 +- util/nwmsg.c | 219 ++++++++++++++++++++ util/pqlist.c | 2 +- util/pserver.c | 48 +++-- 36 files changed, 1594 insertions(+), 226 deletions(-) create mode 100644 .downloads/ncpfs-0.17.tgz create mode 100644 ipxdump/Makefile create mode 100644 ipxdump/README create mode 100644 ipxdump/ipxdump.c create mode 100644 ipxdump/ipxparse.c create mode 100644 ipxdump/ipxutil.c create mode 100644 ipxdump/ipxutil.h create mode 100644 man/nwmsg.8 rename ncpfs-0.16.lsm => ncpfs-0.17.lsm (83%) rename util/{fsinfo.c => nwfsinfo.c} (98%) create mode 100644 util/nwmsg.c diff --git a/.downloads/ncpfs-0.17.tgz b/.downloads/ncpfs-0.17.tgz new file mode 100644 index 0000000000000000000000000000000000000000..a06aec5af343734374ba18b43cf2c7de936b7492 GIT binary patch literal 81655 zcmV(pK=8jGiwFR#6E-md1MFK_a~nsJ)~hS_M^xy=WCRS8v<_Q(f=Ech4heDr(6XK; z8t4L0qdqWQ-30OP_sh(xK5*Ex8|&HFjp43G649NNmFJfSvc+cUethw#{^(a1Z(qOG zFSOQg-^ibruU_$={^vsLHMUtM0ld3}XXFYtda^y?S@grCZlQK9vV)cnzNrf@I* z-~D8){|}F!rhncUmlqdryuOd=A9}wM{r~j(>h0w#(*K(`Z(it&f9d~E|GA5HbE9{8 zrMFR5>N3|!o`2I(sc%PjquF3Kx*t!q&GckAxVanl^)ye7PVH)4>P4P88=FFvYm?He zpfs;muCLUG3bfXyFgn72g^7}6r#D4DPfY6Y3Fr$Ayt1^5qjmI=1eccAIEnv~~2avas{21V2%lXDepuDNm}@IB=-Oru>2n z^>w_Y=kpA=if|{od|@_uQQ~(-XH_~kh2G%5G_Nv{Q%jo|w{xXQ`+DF=beUO{77}C$ z{k^iK(dKz$Akrs@AuqH)OrLFSxmIaZxW8sw2sDdzvc(5+TaJr$l&y^J_xtBPJpwa- z19^S~ZzQEDvZw@M1xA@A&%1UuTP5L+d~Bqv%s%_4Ho478QaWfln{JYw5)tZ)KSM{O zC7U9AP2bwAde)cytNt@Cl+o`C{SEgp2~2e5t!rzOL`R#Afe%ARE?OC<68p_4HO`;( z)oXqE=H10F?=F7U$@9gfC-4p;N%F;C-M6`3;IEKVmP0^!Y8Py&%OX#qZ-}j^Hl@Dc zpDPfZt&J0#j`ZA?P)3&TAi^FDpBw@bGNl29byTF#IDFy%Lj1qJfA~5YfBIjE|F3?! zc>88A{$F1GEB^m;e!lyTbnt2Xs6P$I!^z-QKRkZ89bF^*Uk}IAp^^vi^DFL;c%`p; z`rj)AxyxUExl~GD=bK%@A?WP-9KZbemmYs4@pYOnONv$cBf?1>QNSZ_Ui6hd`Sa%O zYke0vrw31_r>~=QUf6hrzupb>#nt6cIN)(QPOG z=Niw>aePW2TEqyQFA+lUW?~nJxyS_h0ueAPy(d#RgJK~07-bPT@JAt?6=pJ1M2UW=<~Um2`ioe{kt>Vks8tme$n3^Q1pU3E=ra{~{^18o z6~w1QLgqv3>);4_R?-Cmuq~Zt|LBt>tW#aqbYKmEI)U&tCn3m(bzr)dq8dy2-nAr{ z@ol1Pr*jJZ1SVERl+G_z|t9G7J2QIRdz17$YCxJ-+ZTn zuy5_Wh>D#)aWk7CwMTLPTbJ7_S^`(!m=)+WeT81kqPnO`6r1GtG>+@cH6z;Mx1aKTAvLMniG*hs^RtCOSs_-JgQCqfN zrDwdQcB~Pv6JP+33*nN8WI8ij;Wx-bv+LmOGIP&68^VlxQd|4D9@qu}c z2;KC;6GzFpYOw}1@*;<<)`r|p;a{cY8wJhhy38@-b`AVX}h)%D#^(4hlyFi z%{k*a&d5IH_1YQ+%^x`pu^QXwG1r5{K|-Vp6OqO^d?McZs}NOk7F$cB(Pu(lo{cikqEucoOB7B#rDa@6_-8voCAp*QsbE( z)#e7axnOUzQWn=_@F&uc0gB%Beb9tWkhwt3Y&Qq#=tV-cT9C89khtHx#!#`C zrZ=tcJ=~xuk6)AA>*pc{X8nM{Hl+XBb2&fdxS>Q%qDhXz^;#_)Z|h2|Z6Ro2nG@RU ztAA5}2$1pVY&f}_>cRL%U*C^!M$$9+ct6p;uIlNV(R4N$eR!mg_`$pTo6*P7HTPGD z`HMayr(;mbnpY?4;-a00wkZ(k z+_d{1s;cvk$S~mKtLduQHFsoB8BG zO{E5TP8-WtZhb;Dh%Q*QPu$oLfe7=>g_CIePJG%jFSt35m1n)!yg7F$YwF~X88DdW zDbGM%L(mNKEuh2JefcBJfT} zPMFpn%=P>6geIFX1Qyg!d-_2)UOl0jpDf z(YYjpNdle?LKf@r=I~xZETgijq~;(8We4iPR3eI&OI1|af#`iu4oJtuJ&)L!8sez* zWm>PO7Fg$omR<6Jq+8Ab97b))8Bre?nhGbMAq<)gL9dLf5=1OwZf=0jIdI{gM>sz3 zt1kj@bZulsMR1#FQGc+`JM|^w+(;(4>?v%XdLdGKeL*;QN5=!OAjm)_-8BU>GhP&0I6+OcsvZ)VS zK%gr+7y@4<1U7$;iV|x03T|tjhN@~UNu|T!)#Dh1+V##+)6mF@6y;XMaf1mF(?>QXkqu8&oEKAJ_hcyU z`CKY?+6I~&+Pi_!2Y7uXl}!ERE>$$osl9VxmoNeZ@+4BRL1}QmG zFrN~U6oNfdiehiHMO;%M13HBSLh6_6FU|vWuSFjceFnQ?TnSVgOK*m&18%HGl!g#z z{N)j75nx+2p@@=fls}{{Ev3{Dg~=nx^!W7_K?aF_mLxruOOa*|&WFm%#2dz_c3_b}%fsLL}@+tqVZ zMu-jWpog7-_mQ2^0nX#S6f%CII;+^cTxnIpB;?6WKF1tjiw zL~ieBYC2bC{es#vIv1raso-r6JzQf-h>_C7x=vD2`$>fZYa4L-q$M;$QRv?aQAl+q zAPwNA9U^s}fPesoQA0daOWdnok!lNnOaPe`XA5bE;1^tc%C!o!ii((~c?@XQaA``@ z(j1%N#a?SnK%8>_y{rSKmlG!=0!FR=9|Kias&?xLk4be`&_n|$fSHv>0G@lVp`|q! z4$V<4k%()WS%P3cFQ6=A=C3U#&<= zfvm52i0nxIRny>cm(0VJ zW)yV_L?IYZ%gp3N?IUfW)#IE@4)*L1JL3iirdfLfkLg@yfBp)u4n2`T9a8hZ~B_G(0Sc(bVXCx+ZT z{S+lu$U$ZaqI1dpH@Vl&LEN{d(PFc;|qx)5^d49J#EP?3(9@WNw3QAb*5PI`!jalTXA zt;`@#o``WxcsZxSetBi3A^IRXLJZz7= zhjOF2D?Q%D+D8j5>IAV`9zfF?-zQ^32Y(9G`Dnk8!RR>hqiD7UZc}?y8{X0c*AbeD z%RK@I))(M5gHa}i4=Tp_P91}lohV5|3c|BW$VK4}*XSLi13l4zAL(Vqt_a zJFAzs0X;%%^#tqrTi9H%MYDcWGsdpNYNGOnVsT&q^&kzp@c+vyk;szR2*o4Az1O0; zP~NqxO)|+%xmSZZOHIizoMzMU>lPer>+2IJW)r6w=88&uY1?*3xxS@`5Pm{2)DJ4% z{_I0`Z+JVg*(;?X-P+D=DXsb>+Sb#1Unm~lN9Z8A{fZ92-fP~U`3B?3 zCi}TGcWB_asI5lMk9RZ~8JvDQ#*z zK>0nW{|7hjEH&&okT-rRk%Kpekfy>Q*?%INBI3l|USey&ANYcIb1W)poNZ1FL&FM% z4^I}E!FMi~Rl)PB&ccbW!J4M}_qr}NeQ@;wfbANhSo4UhuezS(H&g^Ap|ZvQE=W&} z2l|mxD;)DZ_A0WseLY%AV&`@WT(q7HP!d4Z{{CrHo_3LrbsE zFh8JvjXslUb87u=p`Ta7T`S|xd#YuH42D^bYzSmKXMX>ME+t$6`7t6vxf~8|BK3L5 z)3oXbHz_^RuaDUCk)Dj(#Fr^uQ1g?{tyAOacV*?m??2J&?Vf(_(YT|n9hjH1X_dfD zjSP!qfPoyc^0=~z`l>y)X>IEn=#ty-t=D|&KTsk;?_iiGe9w>l4>yydpxt_VCa)3! zaC6ttYXg%iRl2z~;4b6&&A>j9e|vBXFV3tG|Owtf9Cl#z@oa1Xg6 zM;lbvuZa{5$LmhHDLd4jNMU>)IrQ zq@@_9O}b2+(lE-C)XOBu85dO&A=27nQKgQfwnVsjlr$mSw8^(NQI#|$!#pKC)EZ0d zZLazS%1lg1wU4C<>ZsFl+f)Tdup>P;Xr}V{ng1nT-`YNL%~}OD`eCX&-#z1YU0Brf zy*gZ=zP4dfdxPR6oXfCJKm=ePZ4$E%GA5kuc}?3)c_oe*rBkT8&kIgl zUnI^U2J<8iFgJ(UO0ude5BrW(MEu+j!?pcB#gBgLbuVIB*IJ*T@j+M)<-(|-pyjJm zvX#dA=euUI)vnVeMmKQeV3&xZB9~sS!^dG6z!5!)W9d7P5kTE5LyvFPJVEbVv95fZqq6&I=j#B{&dC@A&^NkNBM1o{mGcLfm{MXU!>L6i*|imm0rGQr zFpWqY2lN(;@N-yYXfXQm<2;XdN88E2^m!BJ3=^?kgrD&hrpe*x_}pA z0~1R)#LL1FA5m{m%9sO&$SaTxu01hQ74=uC-$*Kijw@W6;$nD0zDn2ZhlbjiyE#j) z*7u%0C?q6QwifDqrhQ4ln-;Z-K;dPtsp2?ghaHWkdERQEb^&!*5(^W;z*Js@t%1%_ zLBY+M9+`tWVh#s)Zw4A7gxxU_>tZ$>EPXW}=SxG!ecFk9v#`pTmU@{qu zXJ1+Wmwo+Vcs+QW4)yHwP(MuWKTQUAdNd7Nu{ZkTWH{9KANBR;!Q|7hN6$@$^hBE$ z?Z~zW;Hi85_wcvba6How!^z!fHUqT}U-jVO0mKI%Zio7I@CDKw{&qclnCUN{hhue5 z-iO0#q{d7qE8xkRiK;V+gpoaQp_;GkW8~qCP;z=AceY_ibc~567pxawL9$tgr!Q`u+4ky2ku1OAR zGJF_}Ab#4Bo=k|!{a9klRiBjipX|N)LmNl3IR1WR{)#q9YzYe?iNg-W&ch%(Y%s8Z zb3CuV`V7(tCP*4ZBN?+BzrX!kSNC+!p#vYuzBP$Kn(3puySl2nx(>jS=1+(NuZ|9J zw5QF#zk>1PU;?(W`vR~HC(~MY@8<*P99Jm4_!X)GFOtRo70~Edz1;nk4YPk0^Z+o8 z<+h6zfD{RK?mj!lId}%}91vIl2u=tt(f;ns-51TXif533x)x*diaI-K?j7I%y-|2l>O8QRWAL7N~@fji;baK9{ z2x9pAtcj&hn@2ETw4HYM_FkRB*1{541E4v31)J^Qh!(}e@zQQSINjI#iRM{7-#s{d zb(+C3==m6?5St`iGYg`T?5tcN98d?(p{uhlVfQ?=8*^+`I^G`sCp4jQ@bFh8yGJ1M z2(OC~kA**WfW7vjA+TtXi4to{%gaq{^@L?!k{3uk_TVJK%CbppxDX%kwBY@qaT#Sl6t)}$G2v}+D>qjl!p9=4B__vY z$b(H$8}SvXQ%Jnicm5>FFB_p?8KnAwD6cs90b3;BkUbvInPn|_!KQ-6tgy)l9BMR!M?$2T{EqR$kn2KH@%G4SP@8pv zu#HWmc{NP#9IW~*AT05WlTpE8j+Tl%dGV|j%g|A|Zew*~OySsy6gMm;64q5_GM77e zrUgu&CI(aev~Pc9P8WW3MnO9z)C$;UA*eZHa!b|(>{*Eda)8!pqi6u|vkM?aSb_Oq zRi|U2;aH?rG%eJQBtA~t$Qbcr#1K|rlffgDBT5vr7lmWn3L7R63#u0b5USs@G_TQl z3x<}ncio+vsojn+zznUx?z6MwLy$HOf3+p$9l`)vd^NrWbpC(jt$OpInrwpf?oap( z5&ocu{cx-rhZs-`;yY#ZMfwP~V|UknU;|QRQ)Lc>M^PTx|mc z*QChk2ZISrSkRAGSCMMd;W?g!e3NT@{KacT21~|x=zdH^0-zt!EmSLMPj_Q}EVcW= zsI1s`d*sDu}ne3m$+<{Xwcc;5E}J^-@Xn;MDV_2(-Cy`1}r(hg`E}+o>o*Ht8Z9Gk7BO@YOE$<>y(f$VESKq-cQZbGjwHtqH5LpBk%n9WdGn4zI|PSg8*=b z;Z3LPdCv}x`0?5#9#ka*)4_w4 zYV<&@l0V!3n^p9G=Pg-f|FXFouELsGh36S74Ni}=KVlP#ZGJ4>jiA1>n&q_>!Q%1d zKRweNZE%bh%9TAaEZ>&0k>{PA?w#zO|M0{iH1H7GG|$i$kA?$4bMbQ7Lp|>EgTv+% zdWaxU*JSpztr93;zT^|EpXb4gZKVh0EtT3ZY1JL|k0gE_vXAy#m_cP)R)5$9;OFw6 z;ZUu9tDZs`4M^E(rT^A`l{8gm8Yf2RWIxURg6&&YYQedH;^Mr6al}wI>;}K9k}P}W z>(Wiw>$Lq5fX$DfMctS@@leIt{UXc#2g`pP-1kq4|NQ9DhKc`OYdoU(pS7*cFY@2# z_|bYJ!h?#0h%%vv+dZ;dwxmF02o##Ps-Dr|8UvB^QDhvC@uFg^gU_Lg8TB3}jKP3- zind$}&=pz&n$c03obm^zXMXVeB$%Kebxy_@!ze~QB=!oD7~YJNM^#K6dD=4qb%94` z=evi85?ty0D#-W>(QxfJEhxWTyTYM}MjT19O6CMo0>mdiy=!e2MMh>l8HsPKS&~TF z9v;6R#KtTr9IT1uXoSc3t_Ydqsybom@*)+VapXzAtj?g8NTLQoZ;d1_47-%Xz`*75 zTzPOUj@sANHd`b{5@{tdSdG~F-3q__qaJA@^rpe0a^@a|^3-;5{RIGLr-5 z;8q3x2rz0yLg72(HB%UeFZSfv;?cC`FHYjTte3`e za4_doo>ni}M0`M+X!B`iXHd*+shF=>GDqD+GwOOu63H3hj>h50o|Z8uC_NymgH~=Z zD(@9u&8wrmljmoiC;bkhv9*us04Ic0cH)Iz)yat5phmnU5kov1L&j`n3iRcqH@RGe zRbD;m`G6dFEEfYWyyg>91nb~EfRl2|G_F=x@krM)rkziK#K|)twtbiac=CWy6BdfX zzk5a_N@EWD=oC!Rp&aseojH@@;XQw0aoS-I|WnA2P zS5UBGEhvY0NtV&)rO?-81Sp!!YRIE5M|ohrmBCEHN@SmFboJDN#S@Pd?cac-5KDll z7u$;R7o*@(o%ok@*M?@fKFl^>dL96kXZHsjR}^`DGy%ov^a5x&QWwbKFjhVBIEf`S zcpD5tvQj=sLek-mF|pOOu{!O@;kzozQRD>bzxBf&`40eW8X(VmdFv2Dj6h{w`jM9W z0y(B-Mtd2+YZ~H9e*lt6$NPaC5n;(reOTrH0jR@4MJR|GG5}?Z z*gX4oXjvm=ysAb1d81Ayyv1rY)@d9PvGdDgwg`C(hD!D zuiIB|B!z<9R!AZXx)cg6vlqPU!l*4UMDoH6;<2$S;ccWa_#Z|qkTe^KB%pY&ux$;6 zo+CSvL3?z|R=x3f((RH4PY_vn6>RlhbjiWr2W&-6;gTpzNL3w5dK82DFe)_G0pZHT zopB6zVhxHEqXBCmCU7xU zKV%gIz*CScym0m$fqwVoAQ9p30bs_%=*k}sZ$%a&A19#$r0a`0a!-id+dKbZ_vptn zBu3&yYBI+JY&eYy*U13u!MHGrqw)8>3VK5rOFtm~DFT6F5uMF@256BVf0yNhV>-$5_9M%#dDcs;CA%#G{n_-eHSlnw}LXI$+B zPbrZ}7s%8V#*{TqS*W;T5G^kl`|h|x7o$C-UxOj07!liVVAk4O#v=s`$4TKbWZeKo z_iso3HMOk?8)t9PC!rg05~6SjKv>uYDv{T)8K50|(K~8{#T+_wVXhssgeZtmbg2Q| zNz-&|qSRAwFV9x@kI%%h_GFJvJwPQpd>ZVAqZpn1lF+C;i#?Dgu%8Kf zu3v^7rkRO@wN1%TqleSsD(YcN;{7_KF%objv&aV}=8F!IktI2N>2G|~Q6>;7CXo!7 zW|?T0>$UpEm-YV>*B>5NuRf3dzp+toJd*X_s%>nc{l^C7|NNr=|MWj=E1p_Wiq;!G z(U-LS>bIMC0{b3uA59Zn!I)!ZNQIPa=)3=DwN!WALu?FkKp`|^WBmYZR-jS<1n%R z@|IeyNqq~il<^{gep)SmEa#C{OO;9_EOyG}9V+~oLNID?@_7nl-pN)EPVVy*9-~|3 zykJnvSAaG16b31kl_2vJ?)YQ>b?wbmFy8NYsr5QJ{51y&*5N0OE@ubm!q(c|`_V4> zE&Qa{YuLM;x?1WEi>4NvVOqQS>m*C4?y!=J)S*)mENP2kv(4KNhg4{xQ){poF3f}t zQNmeiv^;5^{@QwV);uMGP^+bOxhe7D^!U|@lxgIbIf0%1swrjG^UFL(hey-dMt-5A z-IvYQVe?3pHXfI4e7nHl(4*UygqCBBTZiM31e=M(V{VlYQ;VIVxxM`VKD*sBV(0TfGz46%Y7*&$gt@TV-mgiQ> z_@*@o#w{Iez)XzRw5NV%qSXK}_VvLDu&LvHo|QT3!K9PXTJ~ptD)rOxVY2%5Ef+tI zUcG$QJZ&96$6~KuHqTUPYr{bhj)B}lm4=NS<7n*nS{J>jeJ$%qk5LbpeL9?sE-BVe z_SGO7w{Bscrxf9?I^n1la8M2Fow)_bz84aRS`$E>V8ji>I}E5xXu=~iXlqT>!}=Q+ z(O@D-y&U+X_LV&ukL{KyE`qgPjMm28MVcsHMXY=@s8mT`7K!qAPxpSXUTs>k)9z^tuUHe&$^r!5 zp#yf)ylXh`n!oHFzB&VbOVU#ji_$@s))_ZS>wnriK|qtmlG6si%`6J}7vK0VRZy0ZOTT znkd%Vo>CU>gG8J85;?tvMyCfaiOMe}s|5V+u3 z^V3&Hh#>U3k$H{6HcdwUYns_6xBWN+5U}t`Z=42cmjXfnLq4K~#TCnWEih+*me<^I z4p{Qu3=3B$=e-#hy+^=Bk1wGv6GB1I%Q;wxV+*NxnqtyJJs=BaO*Gy24f{RN{a>jK z^Liumn%Xe$AKUK{PXNEgHqrXw&;ZE%KNxM7o9Mw9E}e4)g6|Kt-bPqp1X1$&~tz-*?@B4p4oHH zkH@=28?wF6tc-GN_%_HL^t_dG2R(aNapk*>IUHOC7#E;(O4RVBt!bFOtHM!pFg}{} z@3JNLqj*0Y-LWzp2#z>N+8rtb`9*S&Ap~<){xP`S!&~FKR?hEucIBh9J>HZHSH{yZ zh|Py^Qc-uQjK{v)BNV(Fn_F)dIVS_R71Pf?V%92|(6C_u33ILe<1-d{n9-n#qa{P^ zJ3{MHo?yE#LSGZ*)@D*>Be%@?!Aq;mJp)O74}*Mhhk3#*lsh>3p@}laz6ic> z4m&5s| zGGfs({#$g>#FfuhodayBd`H{|41>z&>C4nbDxaW(V0M+~tGvf)#-3-C1v4k?IS`Yh z0x5Y)WmZl@&$|BW`ugItjg@~$(a!lQGj82+V{m2M7}@2Xv+}|l!%WyY(1ZPe^TYw{ zuQ&CL@XX4*B~Js)z_$y$<8^(r@n#VMX{e*i{nAA9H;o)FqjB$!{{A;oyPmc%u;~)2 zZJk$mPpw~VxUZT==cm7t_7mk|=M`Q#w@#a9=eukMgVJ@j!0xN_<0XDAI@$Eha6E1D z!i8bQZ0aM;DD`8rdD1#QGWL6PEU?}m9k(#HaO-)KZWZ`A;JiPFbHyHM+yzMI{eJVX z35PDcrQ!A)?)&C1C%Z@ct@GVyXZ(J{eSdO#aCF{!wtIBcJf-)j61T39VOX?UBN{KP zB_jat=AXpKbGPvWStGr!Z``eo z6Mua5`f=^e;_JfJHKh4hKs{_bKO_}j0&D!++yAz@@kcTNo^JnJuWdX^?0+}O{(och z(UGXbab!%$pbl1IyHje^&6CF>BWp8$}D()v-x- zjO>YVJD zv9xF$*v2`9jJS-(oa32a(=c@^@+D3qVseM0`|%nFmDX;(_h^E#xua;zC2xZEh(`W>R1Q$_OPFL9E~uWlLd2*85o-IxK?2Acb1HdyM)MkaJ+{|D^}b~ zOYq>d^kDFyqUxAgS!Rb;a;2Q9`J#FL^7z%!`76|&bJdAd#RYP${jkE`@zGK1@c6|G zGUl+j@WFBEK@Y2@%%z|Q+)#K6($WzbOZ9BSRymiw9~}P-P=y5plAVC-tAjM4P>cSI zc#RLMT~)^ee0gcYF{diHe;Sz7Pz{Rn`d;SYKQ)iOqO7^w{{zZ#pH=~rlR zgRjuK4yX+g8;Kv-YSH=S{EFS$VM$fnZzC2Xu#!AHrikKwj6st`4H26}3i1Qvq*|RU z+-P*Me{kB`+ui%2$>7V+)`pk01Ux<2qdOH0C12XAl~Fv}LTkqeLk%I8!-E%q^l^p# z=WFjaw>H}7z`DI!PIrFx3J-`ccV8UrDJhxi^X%D4DZVIMX6_}6gveH2DVMO&>eES; z())yMpoGJ63RRAdPf8(L!EY>E`pJU{_Onp)!unhRVvyILWcw+W6*x{*{c^VUN%W{-HAv$q|;2p;E zAR115hTfoRJN2SH8DXj_tAK~r?IA4ifI{S8Qf*+ISoWF=l^hPigcCo(F*QPy>$3MR zW6@~$jbT6QJIf_T*lREwaf~IYk96J>Z`BlU71HUv%WE*N_U-Di0yxH-!nT$x}?Q139d!n@ok(H z!E$}UL$m-b^b`MoeKF*|+3}Jcb4@P>CA$%|5;7){v{~NCg90e_+(4kMc@_r3T7nVr zAfaSM^err>>j0Ka3R9! zAwEYN*?moUZLJciWg0|KykNd~`3D_-n623f2=J(-t9CIERa6XkMHQ_K68_V=@Tel~|932H6$SY2kajIVz838&Ii)&EOY$tgEcp^;e{UJdCH) z1>?NlK71ZYO)dX5lSQ#TR`AcT;-yqM4v%T~JC?jh>0f{xYKIA^)LFr&#wQ&^`5Q)Jn{YB>W`zUY7Q=QFKQYOgFJRgO= z`p=1oKb`)gw)~UR|MiB>|Fgci(Wq@A{cqH_zR>^APycbfKky1jsQKBmgv=+87Q3>s zZ+bEez>T@`eD~GidF$D$=cJyk*BTqnMT!WR|MC(FpgX3+hU)JiX*lnfp(Ad;eE6EqM5JJUUfBcO`%WJtFuXwPM5o~86V z5ozD?KEtVa1G0K?O?+!t z%ur9BJQV{sbs6ClbfTL929DOlH<4^nb9sZwS_iin0;8%6U|Y3xTbS4u8j>jr6DOkd zb=;A26-SpK=_(w_#?&BFB|vyIYUg*E8hroLlDo^)O%cDOrYO1O4W@4hD0*lz)@1p0 zfLZJ*WZzk*FNy_0xEYbzFlM0WhjAYdrzkY&(4leF-VqptoXzlHU%=-bw^6N>rV(f{ zMsBqjST=*S?hNqU*k4SB z{%Z1{@a0b~|2685GUxxsqeoxlzt1fH$$Eb@6Cg5v$hQL0uNIC7SfC-8YR}_b&{N0S zR-)m)M1$)j9#dYptW@Z@-Ltcwk54&VO`}Fy%bz%4G;$XXJCM51X8_KQpP#2=KqPMu zj?PbY1PCu*UKNX=#2m;`@6+%*M!pl#P6FqnKuM9SNTRW;u{g=~eil`*@W_}_l(QvH zrk;pPSQW|pOOc*1x4KEF;?_5p5@e}l8!8z!!_QZkyYW^{=xwRYA?}kLG~YXdv||Q^ zoT#O3p&U^~9L}bNLrYiTNhh)TH5#a(jRyw}$|(nfcpLR{Lsq5c3~z#3(L|)C=!P$0 z#E9mAQ>svEC@nQ%A_SBseLEY%_u0h&)<3!hehtvjYU~a=Wcw0zG4#CYfQsc*m*{V) z6(SQdlgyAFUD}T@s!VHa2T4l3!D}{tVX0ZZaNoZm|HouuAGwdk;=kA7ZDV8OOZ@LY zZ~Z@VI`CQY-^Qc5mjBjkl>c#~QEz;a|31gh9rJ(hJwM!iLCK|mMs3;Z&%lSn_IQ=f z<|M?a)#HX*-G6z!|LU-*R=ZI@9Itjqz2!xX(d2>|$+`uaP38Rflf)5}<1?&70JeZ5kR@VR&V>~CjJme9f} z;=77!L~IoPkjX*BhUnEuvM049Hz5c|cp)Bf`s-|ZWs@+u=ndAv&dUhZ;XXh$BEbIJ zJoi#PZl^$Kr@mk148F@cg;lLUiDf<{2RD>FIe}s?m5m51(DYN2oj8>|8z*k%e?8A+ ziFPwho7{CK-IBE9q*tai*PkQ3^t0W)A77o&3+kg*r^i1Wzid8Py8@ATtsi(E>QpDg zZRIVMco0sOXh}JppJ$pgOV^!z)0kx%dnm6#oqw)z>uX<^5?}yeN@bl>Zb$WCk$TE0 zzN7kWI1aiGmA_rq`LmOp-8$iSe=z=%F8kk<|C7Px$LnJ{|KDmnT3^@sKk8fSnEwF< zfiL|3GwOe6#qX;5adj`8IFq$9bZf^VK-RXNh;N?uMzk9A${0qwMPhQ*$f+BQMuRBt z1!|=8o@?gybc*qR=1sp?>Eyog<6cCJm8b2i@iH*a#y4emM`oyUMRYOgvN6jgJmi;DYW5vL8(C3mdjv<@jmbfpZ`{+dJ;Y_h` zmXShXF&Krpm*Hjla?GOjs43!v1%=x#-{f2d<5Et4dFC)HU9quFwU;)x@r=>8vANId z0>}XfVT=VRHRVhfW)GbVXH{;I+Rm{g#>wdggY;Cca7^JmEaP}l^T1h2S|=SR;i296 z+CvbT*(b4G}oF8(I(wOKD?;Bxk55hk_SuhWC^4~{;r-rQ4jnF>i&JMpuU6e|9k+& zsN51iTz#TSSZY;0*nNP(WtQH|5bdTQ`kypJ4+|ku8IUYu zn&&-ZJ$hnWRK7YqtU!NgnH>iKM&tzM?gP#f1F?BSg-YQkD0Bh8v4B;dJ%5u|7Wq%A zq913fUL5o=5lXgWIwEsbc9Td|#G`Wh*#HjM!=Q~BSJ5d+QQDf~GNXXgbvwnlR5!jI zw3Cbb#e9=!j+gFdC!cLjWuS@ADzU(57@M&(MdQIsNOk>+>1qX6kJpX9rw562z*J$f zkW}ICnT5FRJGF?yQ6;WL-;<^7yKm`xW{Iw0Vk~UnU2dxIfjd4Xu_Y$XbofX?0?psEe)8o!_u+^X@Qa4||=J z9Mw+T1LZ^ZRSas;G&)gL@N%n)nIy@Rka(_-JLWu8H6B?8daki2lrLs zYd*QAf>oykx|lQg`5874yu+(YpYosIQhsaJ-K+|9(o9vyYsj;r;lYUvIHw_vHg4%x z?R&o38+Gpq+3m0$jp-jUQc_Qwxmb#ryALj1tdGtgKA)4#oAUSLHkk>C~=w>gy7p?R5z zsqI!Lcnd3C+tG#GQ@<2q983!3w!{XHQ%eNFpcO>j>7~%*w8bk-p3gAiot$$gA9X>% zk?@L71o83?_a9%0;EA;jAMxM~&yRfy<+Y;6OGQU?19c8%n1>E675m0W1uI5|Hh+b2 z_N*C64N^23#RpN(Si7@QsG^t{^JjI@2o}CFyWn~ERZFUh!8p5qX0&7EJ$C22<1paSD=4;Dps4%cK4sld{F0U z17wyyq|1azA6s?Ic4f>jD>>W3h>?JR_*TknmNH0r`B8Tex`Y6fa<*C7i4PK1)QjgQ za=A-m%Y#XC#@tDR#d+C$d3^dSJ2fEX)$^1L`REwR2seG7uxsqhK^Zi+&g09tGverVRMd0yf-b}L%Bdb zQTg`^g1Bi67bI5&)Q*Wv24uTwrI$`KqeKVIvrPej@_@82D9l|42}wCn=EJAo3dR+H zi?T@}luNW+G|2*;y`wF0mA)CdG}-N3E3cuax}jp^*Z76J#B@JxEf^sCeAXdPV+~y9 zXP29GIKlLq8qeUT*WGaUCp3EJkd**UOFK^Vb$brDqGt(&Ec{eEk{2F zTkHxoU9`DOjDx(%ZAT-Nle82yP$Y;V5XmVFWGg$aKwdMG7(*sPmC;QY=ja4G5u6f1Fw3jk z_s3&A8wHfbr;U_~ufJ8{GO_=(E7H)P`DP({jp9M3 z%Zf;v51%B774^?x6gd-Z9Cg_V6)Hyq_P?Zfm;T^7#)mK9)DR8S-j(0&23lu{e!x(e zbizEX$=DK^Qnr>g?7~=zES%7+rsiMQPH1I53zNA*PeM3L#6)Ft=Wj*>=4`{q5$AE) z(nO_1Tw9*Y>DPKDx#XsYgR*-izGO*yv&De)oA+6;v$A168O?vk6z@(lr6r}l|6s#} zu1t?(i;z-pru7eo<*;&q)3=k7170@7yEWY5K!kOX<#C$6Q;pQ&^7#1Vj0kA(ZitkT ziamu^_;}Q1Olr`$wH1%>qbJCXzRS&VuUQlJRFLVONs2|$fSTI{$W}ul{U*{49Q$b} zp%OE5YOv-x=mn;zt^%lp5CT@h0hMN*-cnQg0kVB@-f4ZYFbx~r+X6lDR`zUN>?8P#>j3GlgYO)CkqP1zmNq@`K5J&sYZ zO2Q7zKFKD@X0+oWV}GkpQ&N;U*bc&R==T5xWK9jlOg8}p6#6OOdy-H-2QUgR%5A8w zANGPyT2q!qa_i8p@0l`$G)>a}Wy`XRhA?)vUxUB#yjePN*HU5hz{L94) zJLj0+g<~Ev0Q_;yV=Y134KX(}NZpO~eJ?jjikwC?(d6%D> zb0b;VxD>nKBb^W#0=U6E7|<_#o(Q&8|~l z_sqoIbCqmblSX!TWOWwYDBJ36AQ31!(d43cOXY%&J%Oau1`?Q5A~g&~j<-$T&~k}_ zIUx79>jO)P!;2m(XOg4`+Cay;Q|G~GYnGx(p`^9iMyGHsFgYz3O54vQ&rDlI`#&i< zrI6gjG6O<&Gtw=(5W_qg75Bm{HCpQ|Kuzib% zkv?ti{hRc#txPSK$)$N$o+>$S7jh8Sq`U~IrO@m?lpk3hF!i$ylyk>0)siEm&6}rg zOW{(;pLX&J;tPXMp+?NiEm&%afa1dgRMjVfD%RG9h0ZA=?;cxA0%1i>CHO_!MB#B^ z*{oNl7$ZEbL3wpGXYl&yWXn)y2f4!LjO+;(UM^_-SKyzu5>jE_>@ulkJCsa(g^_tW zlYXC+blDRYRwZ0l-@Hohb3TtI*PVIwQ}<1d#@^*@bLAVcNQia}7|3mCk}(Zr zoK93#2k@K3(Ca~T0}Lm?i$byrwTawCFqQzXy#OUHN{pv1|0OS52MxR~Fh=X7eN92g zo$JT|(^1DUsz%9gf%R{9By|q!w$M^r3(v*Fleyehv{BB~Ve|R04%h;LLorns@0B}wxT@7l;czn6+cJ-T>qx`7 z`*^mca(?sLY}u$49m!jd0X6P0KlBR7wq`@20Rv zE;H>6Y;3IElKEn>+_xm-pQ`21!Xh_a&SDTbQtP&Iu6$OTyL)@hvz=U%$&76&dpneP z-QD$HGENL)f9KMvDb`O!L+npT?8UIc8dEt(MzatlcUmpHNqhxna4dsQIfh6!O65qj zC^E7^A&07>6gi|Y)j5sn3S15eTHWf$*J-Oo9=@vub!onPnsty)PipqwOI~Dey|T-Q zjz^<-OeROUK3;iM!n|dGCa9@w6_kIbtVccx2T6n_eSiW8dWScjxtN`4_+C+kHnPP= zxm_a~|vEMTn@Jk|zDaXMely_;wV9S*yG2AZ#;C=CM$%0WmRQ0m@ z#`p?m>n~~XEJWf)i(D==^-V*W9P!07Z-<$fs2mdeqkJ^zMDh_6kbD!bS?bOywi5}J zE_4kL>keacFBCst!@=z1e#xjV^0|<1B)S;;FaxYhx6P-ln;HjjBc#tLXHEl7xkBiK z<5(6Hg{Y7a*qLNgB)nogLjszk)lAem`5GL)GFfr_A2joT;Ylj-snVgA5a{yMDp6q0 z`7uHbefYVd)tWq3txqD;^U&t^DV!zJ=Zftq;eI~(9cN^K5?Y=IYRi_;SYK~8Ee|UDF1RTXLD6lV#%ne#PMG1CCOx`&E50nANuR@N%QCr(~N$4+B`Yj-D@W8 z*D7lFlx!vbfWJSV9-Lb*1(|IH=uCv3?u97A(Tq(d+tMf9In3>G>vaF;)A=`_*monm zgao}P<{8h-Gh=s^R)Yel1XB=-7|dgY9}Zu1vF){ne(7NsUM<9|tF@~H?x4jrh_Wn4wo z-gV)}+R}n9&ZZ{x!r3veT6BflKg5@uW zOOD2Ong#%u*)qwRB0wcb@a)yuuTIbwjS4Ih7jEj@U7feB^L=)uf4Wx0Jr91)D ze#d>5EmcinPT47_oRy-pTV~0YEEhufyTnfXWEjuDd_j3b{k~xQ$H({&jq~|Y!hBj` zcyt=YmhSGg^u(E&_?>R$wKQOFGJ)OMX!2}&G!@nsVXD*lHe8ODmDZ%-w0jwi=>!+X zS3ze=^c4%&1Z+#T;~r=mE)_S(6YtD`Jey|*jBV#eaMI&V*1P363jMef#j9P; zr3=*$*mp2^1t{trQ0)MD(p-!BMA_tAM2CorE{-tV_ z^?GIV+se1KO1%O9HtUu8<9el0Z&Vr^>y^f%^~!o}qq4rfQK|8N>irI#M**n6`Ge=d z#Y=y*I~=K(0NURs1NhfdyOT?G77W!fu!W=Ot=bPLoUZ2u@5TYli*O7~nfFgj-Vxu% zt?@tI=x>v8yMeY(y^_wZXPaJW_pXrQ0doE7_@AwQI6zy-n;;&qY-PN%sl5iYJhRP* z)7yl>@5!B*uU?+5?jN5~y*0E)R6{=`b1q-KIy(48(X&z<0JZPL7>TtFO9lTus+Dor;ptPgiSZEY zo8?u!6Tq@~9WC#^MREZ(u>wuPeYIXMuQVQR)mBRUM7OZDUVdnv)S=M&R;~8%@zzGI zw!(@H>5`gQtv_U(BY56CEMdoM8|8=i7piWqmgo_dX0b`#zt7F^2)z>$g{k+qs)8I`OXEo1Hq z)7lvWLe~%#*8{)MJ;_>r}RlOyfs?(RalmWjZ?Q! zc~cBHXmua1m)ACM^;W3NQ!G;^5`{+J)GCHy&lZ(NsM{xM^IKGe-AfRrtVqpB^Ta(P za%j|VB)Sy0oP}{9L*TRTsPXUV^EUpbO~N4#baS;GgOc@ze3sBV0;5557$J97HX*YB zkQG@wezj^YIR)CJ{|p0l!Rh$myq+#V(>Ep=Dum$I-g6+pfuaGUS*f_i*( z`0Etw!2&-cva07&SMFx9$)DLlN{xJ5zlrZ*0c_&?tw&VA((OYU7FeB=ATVqAhMr?aH;xvV^ z4m4;I+Cj&Cv$ZE2(!ve?;`$PcC$SqRwV)gAb(|9mz`kprrLUCWsZ#?-lScBi`655Q zbrFD+aftLGsM6r|zxD>D2Lx5(`16@$dAmvdsVdP7D2x zT6mV-k*DKG1A_vP9?~kW);GF4;T1~hz%+JWuBjgpg3N?eYym)~$g)pK92O<+4yVXc zL?Z6Hkmm;_qP?L=DTpS)6d+Uu@BB8UB0yh3G5=@7d5LP6ofGIW??oCdoVm^^ZwvtK z$FL|evgub?CK|LGN!Hp@uw2&0x_59Un4AP%{+Asl-xux0-mBBo=FxfU{NQC%ZzvPN zCmfBH8?mC0l)ekc^HS0@BXNm(5jUaaXLn#HZsfMT**yU{dSBlZQay*q z$s&FLM-gmJdDWyigD#Y14)P|KKQMmG$sjTuLRxm21n=*=a|y#Hs3FZKGJ}}HD)K!~ zge~OGhH!@5cj*bvFDDa_dBc2xhGJk%@qmO*_l1_52km^~o=4c1@>qzyTPR>;j%Zf6 z^#xre24MchU@Ga3xXz{%hs{MQ7GXR~DUM_4{ai^t)6Xaage2gv)^>y#Bqxo-%a_(wo-XG=P`T$D_2bYW79u?k0^`iE5ywC+za(gu8 z%1K+}(D{=+bWAjiB&VX4j2(>~h$UhUzP#Wq<(*7X(kjeZ7MqPH6kv*r(wEi+jz6~o ze(%P)rO|_2Kc`ag2>zCx?3LKYAR33=+ZLS52ba^2@gc@qQ!Dt}niQw=l;{v+UT=~V z+^MLzGK#NlPqmohz*OPH<@iMLll=7SZWCsSSlWTFIRyylqyT-D`OE7e^*&hvZUR4B zL4r&hBw^tgoH^K5dw`^Y=y%bDtb&hnNzijU zT{SgK-uC|3XIE-$0b_6lRizHbCOePm0&QM9f%)bSZYi>tV?t+4ehN|B1fI55`FJu? zWAyA#f`q~L8wLFU#v6wim`O2UKL_q~ zm%Os9y4!{oJ8BdJRWeLW@{4l-DQtTrjfZhM7=axvyQ=;M{MN*DjVGf3vq6n`-qa1o z8o3F2J&bc;w*mAQ6h}!$+z|MFzVgTHh$kzk12^1!|NZx@txac~G=WT$HU~O4ovU6;>4K(4 z6aH!U@L)eLo-a>6I>XD_!)iV>`NN zrof2f{HHX1Za9A3$h{=|t~2BJB(~U;wIrSdoVM_mqLjX+tC!VUb{r2S!+~*6nuSEV zV-v(Fqx#C)_2%JE66-YI-_$L)FNU5Owc320*>PE?cjrWFwLA0+JFHD_F%^+Dg%nfn z1PYfR8e&AgLPbqxJxxai*_P+%$hetI>t}XbCII03tZY#vVP`~(V#6xekZF*nB^eu} zsqv_az4)~@ysN{`Cyx9kwM@~rdJMXorV2I@GO<{2L@FV%Qi1G7ZM*=${lkm`ah+yq`+NXumEi z-C%BP#dP?^CFk@jG-a56e$$CA^$Xx{Etq|Z(;Q!bxF#uNEze+Wy^equX6HyIXj}Ee z7;}2^u?2I2q8dZXi>4EuIyXNX2-aPFDq0tEeFHpviCUBvY}&Jfc$@ppksi5 z?!|QNZ%{_}2Z}WZZU&81N8jc2qrNZNzROXs6O5MCwptzpH}GkZ5hk6}I(0C4*wHB& z0ZMEQGBneRh>@~5ATHCW82)r#+YP)aTkE6i(>3+k@!pT7A-s}G?baFVDO-#(aSG~4r-PGXY)In}^9lc#3M##|?rm$S(B8*d4x zR}<`ua_Y2s_Ur_oQerVTZmlOv%yO}HG{6OIURA58cZpfaAQ_nXAeXAvx(LTH)!otW z`u-&dNOC-Rb<#X-z1)3quxH*gRx$PECyBs@dbk}EXI3^m?h`ti*mPbBBs&QU*aTmf zApDab30tP?`r$Zv<1U9TGxTv=xMfTOmhnWYX)?D#QE?Kdj#MVJK4$!3ez%l_+c)P~ z+8Q~J?lGc~0rXcGBhagxWhjRi~2LtWG`zX{C@&yLm`?TXG#u;gT2)N5?OlFY(c2klsw% ziQf-<`q+?)+GR4CFi~M1Wd`TneCS+fLlx= zlx4V5(b%6;-Xv?TE9wFsp{$0<$(IY|9202dn@+h(I-XT}QXrG-oI5UBR#eWWC~H%e zP6R@4{<0RuGcdECYs+E}+$H*Uo*L_o6`N5gHYfSep?wQu`lds;8wq3Xg~6m&l+8jE z|5mor+@W{ESQarA7iL^rgs4P6sON6B(((lYlKHw;LrX7e2QdQ+=zF@#|<+kFbvWL0o zilGKTUl^xkD%0uI)ydX3%aN;5u~YM8c;e#d5J_A&nz! zUw()CZA(`o&&Pd~Tgi8Wm2F5@V*gfMMK|oc0jqzlVwfR{{nHD&cnzz3B?R22p+~{^ zXN*~LCN%LPX#0~mpoBX~6VRYs9CcLWNFho|()kKq+>+?RCsl>^II2%JDs=TpQ2joM z#(rE?XU-j_+Z$if`D1;JisUgE2cv#C2P+6mer>L=_r@K%p5HzQQkP_!x-laGN^ zdbfNXxdc;J?Y73Z!yxzI!LKizt0#G8-wVhi0@``vzXcNIJfg!oN>BCVk<_yup&=dx z2UXN1r!Z)ZBSIA)kp4cDGbjt6%el$t?`HX61?}Ppur*P45nu27@wF2sm2WKYen968 zC70ii;X+VKQU{Q}l?T#_uC>KH!K-7@E~OICNE;2R;-Tosg2wG-;x~qk`o$# z!gHMY^ul~PE*DLLMi4<7z}pz)!u$9f{dP6NntYNBeLnDeXgcl&MmEj=0q4;DmlB7S zY|Q^izV5@XL~3@TwwRviK26JZ_X(hCHk+aS)Z`b$P*Kz1emNU%SeJw6zZ$sZ>pXDx zbKp*=QB0oA4fAdW=2mM`-_ltvbB1uZdv@L;FWj@cOgJ^1fSZ%X@x3wh&^_BTY7+;o ztLCc#B1W#GxuH!tw_3*QJad(4UKt3?llu|_JLS(Iw90rV>zFatwwQ^xl1pNVt+F&T zW@`yzgAr*ZZb-vcRR{AZGu;%0x5#TN>Nm3L33|P>@lVkYaqqO{PnS5bcsb6~iVuB` zM&w0$1p-@zZUUVwiu1IBf(>P0bQF!k)tBtWpi>w*`oGV5W94rK%46^DU*nR*`6*H- zF-cGY0EzV;$qP}J?o}fDzHX|U2(vSS#|W`0HaOjF?7JDMl4OT|kh_xuBGL5a<;2;E zVFtv7%^Zz7GAjd$(@2sa%}*>gfT-FE;H(OAgx>|U;Ejyu2CAr&>d=zTyq>mHkWtws}5}kbPgKv5(S%u*s(=$q*!s)WX(i4+C@TI4YOX%s#Xl9d@ zbjaOi>aSz-WNx^K-AB9@~`wy#%vk>3eA+EwiNnYla7=o758wRi_Vm<;vg&6+#6 zxHEm3({}C%Cxbc0IBOx?06sk3+ge3sKeRe_5|yNu%Bh=vSvHFZb0(kuL;Ziy&+c>i z|ER6H75{CcQ78YO`qmf!pU>(4!z=!m^#7SV*4wQ3Z1G|)JUl0P9yvwsK!pL_$g4;z zQrL?%r9n}7F(B~I&@nMEQ9#KHr{A&Sf?^70wEsz|T-*$@)Hkr)-zNcuWud=rk*=e9 znKzGeBA>)J;kbQO0^lgrCS|}TndW#8N&IQ^@2{F?=i8o@xp;`$w-{Gki=i)ff92R= z$+<}9v@xbCqVUM3i6_O%^faFtVHldgvmTs}{1S`@kI)ni4Em!G9A%hUW2-8f2bsQd zIo}!I&RvWlMp^HMZYJHhbYCi09ODyrd6G^=v7hR8$8)kj)6;5qGT@8dr>e2JsV5H2 zb#w_jP(0Nq25l=1!7vjQRdSj2lUPDdvBg;<&bb@UNux?#-}GZ1=xHO zTzGZCr-chKL}d6Sh}qCxbHSy9#IKoVq+_3o0uis*5G%%o&t%5HA&nw;(M6 z-5&=gIik6F4Tinja$NBuLP#m`I-OBs-IXi@ern-Mtq-*qMinfW zDQlVUSY?x^rl4yZmUE}SoX66L*uRSN?8%&&2Q={lSQ@NT`(gbs@@v2)9#{a0IbwaH0qRcC&h)n*c6nMRT7G1 zyC9U8mwEdBOZ<)hbLW3F!~SgX-`Ce0^@g1PH#gR|wkZDlqeoxP|DW;vuTRK$Se4~3 z)CDvu_r@P$`ote2N zCN(c<7XeI10L{>4uK)+*)JBkM*}9Beb{(Xaa>CBL=;J!7>fTJ-INoj3TBhtbwa2|* zw!f)0e#!d>FE)Ou5O_<=v_w?2f_FRH-?X*PVCiZc4SFS>m1+!V-h$l@;_9_nML2KZ z6!5zCrlK+r>u(B5H}Xoa7nR<~E4?YD2jl1}b|4+XrV}tNd_@2q^wkUNpfq6guMc({ zYYq$Du$OUEjBH8H1Q+X;=E1t9WC^u6%v2RJQvAnj(+ZZV-AtT-k;4)81Q@fUJQCv375>cnk|s`M40A%J&H+{7p4_v z+!+bo>DC|UJC3}y|KfD_WRj zkIRh@;6hT}CXuMqA}6GGiP}~_$&Z~~vTvtgimq)~%X3u746vM?TzC9z#gq~O7oK;C zqpLd6;O^;wa+k-2*CJY}apVmn-7rxNouXCQ1zyJ9GqItP zhj$*A%h?JR|1ZbgG8`dWe#cut6mTp66O{^?Y#i?5hCSKcYf`v{844HM5vUWj=vsN$ zrgbW2Fgha7KLcCulB70CEK6(`qMcj~4LN6L)FAFjmoFeumpiuRj?Yu%_>a-$TVv8X zV#o#lX!U0mXpt4QqTkX_X@v`v({?*4iyazv#c7Q~(Td9x_8aztXaZ&W{yY6e-n-vm z0Z0Bo547+97L9aym}|5irx*G!o&$UI)U^o0yyNUv9LCl&B|-?R%~c9uNJ@L;X26^A32-g!=iJdcK=_^)-$0Omn~*eYNdSJ^joi9Cd$zm(T+-lJ%kNzA;QEzY+u3b)L@K%D1Lf&etc5a>UDfvvZIDbrkEts6VPUi47&6^ zlrxdkqaj2@JaUrW*TVXip*{ylL$}KSV{w_tG_#EK{Cu?l-@=zrQ+|w>>P% z4PuM*7p5ewZp~_2Iqfq|a785xXfOGW+EIgt50ls5efLDwbq1q5sNS@KP3zY(wgoxL zw&GHvc$;0QK2M>>JcZWhDYP+9AyMFI{ary(Qs&X_%uF{BsVa!ZIPV+3R8X0TTCoZ% zUqSf#9l!E)YXc{gr0Ib%c_JI1tMHQYaX&7rhst@=i*B3-V*ER32PudY+Gc5;9O-`Z zjke}iDk|cNY`8(56_x@!N0VsY-OroJa#GhQ7_)`=BBN_`|F$zx0Vzd&WD@rzt&en) z291Ii6LGXsNSJwbFw*vQ)K1D6uLw4+Wn zOL+R~=;+|+#g3j>n;Q)L9x$Q*O*1#kLo#Ow?C)ngW1cq8&Ua7G&wf4QR|YM48c1wN zH)Qoefn#7d2=kV`2Gn634kp2lDKm|Io*h3w=Zf6slD&u`gyYE^pdLm8bel8F=nRFN zi5a zF=$>FlOq6KxEaA2fHEcAguR}QkIqsxSEso27*Q7;dIv$!p&l^8i$CgMMWPi8TqWP9 z#_fAyW48{^%XFFb^6W+Hq}lw@F2#Ad?#moa;~>`KC*&dhV)x+4+GTEFD#Kzh>6n}d z=*XO)7{y33T*ckac1LJkeA;~e>a4jxyE{SaVb3Rr-gdNDhxc4^WA&HMKxzG52FjDW zYO~SzXNAo-p9I2`f9>8B6DKdXO>F4Qui>eU-SN61df=S(za<-p|E-hurZ?koFczoUx z4dgIL`IK(Ua!~f@%iz! z8J`&jJS=N*DAPBsNV;!)ST4IVToJp=;d9478fxp2SGtDC)TDl-@|dBF0xzQ4J=@F33<^-3DWcg)sN_ zE8cavn+`P!b+4GTK_h1DJbOmlklC#7NzdikQ^)Eo3hSUWe38ziBmpq>d!W~COy$#| z7DSDMyq0EQ0Y0ctUD=3YRv5s#admfzP=@o_N+>|4ZA$S3fc8NW@h^jIIV?TN9GxV& ziE>)ZbMs1+ZsolGD8xtGpP(VA4_gjS8hyqdn8iNGOQTzAwpy+7u~}>TgFc3>f>}Ol zo>R;QjtXAhN%vqzP?3pn2UAftkxmP;NTD;Dt0$KB(=}(w1W~}0Q)s~Yb3j6nMXqy}h|3Jqy=?AnJtOiYqqP?h z#Q*BO$+wk2uKXCYGsPS#%|{DLAD<>_VcwM@w_>~&+%4?@ICrOt*|M;KzHT7b;SJ?> zAW)oyi_L(I9+?Wj-0C@sixj3R#LBMc{<&sT_&eNt?1{T zQ(ijuR6)~o_+};z+A+(DToJ99G%F>J&;;bWbj7zyuWK0c-GyycH3YA&Rz zY@qJVuzCp|eq}wf2C1KM35(w+a3{8(WjF`d-gUcHVP?>l!Z};IRx7KgPt{|tWeEY6 zf?ybS+{Vo;;pKsR-pK@#OK*19$+9I$f@IxRP-w8Ic`AG^S0MpD&mb@DG56lH<9ySW z6mSXa?1!zkz75F>3vP<*d81c$`NZ9lo7Z0D);)|>M3*RVCbqZ?2eXl>EP~ogor!X1 zFI8(>#~gOJpgV(0NcD_4qYh>|&0OZ=lM{EZx_c@sPv>th)@LldyYjbD^7Ibdq&D~d zDA*9wP&_r>>~4n__7ESck~6jy)!5v!7(ZQ$FD159RzTa&iUa9o-*3*l@c;gFwPr=L znvE$*B%_R<|Fjj8yGr!`k3f`z#zRzyQOvNq8jd226G)mo)kNYVJ;Gr`S7eLS92#TS{_wzzeWj`MD zjoHw5W`W;X1pdxP9>B)z0rZ3ZH1IKe{aA7+#?fTd4pRE48DrvzA@hz0^<~_Lju=!l zG6{S%dvGQu(yTFxUFe<1Xrj^lIYS*{W`x1z+~YLC-^A5;j0)2dsXS*47l|2G(qY4L zi5LMx%3riC-Ih$JirQb|E}=G`w_Q3Wz>50TK~pX3o3~TugmYcZ(go5iObG{85PoY8 z1>5w%UNZo0#WVH6NsDF-?vR~yD+!^MHfm)`oi`i>y$~3oFFJV+N|w7Ic8~VdWXD&x z$H(aAB|06{8O|DA)a}N>*k;JtT{T^01m}8gnA*6zS3a zH7;@ftR0h^z)?b_^$P3jZmO+m*`{*!QUbktg5oBdou;wCY{{}=2Gi48IUXB&qS3~f zbKr0$G`yp2aSVi6i#-q*_@tE8P~rS*?vb|=CSg)VZEV=<;gFO;IbRc)UW7*D0~diu zu=;cyjs0GdL?^Q+65^QDp}^HrWlEh5yUuU|f5|~)W?zr(dLZ<-Zebl4s9$f`4e$Ul z%r;t_g~8OCTXs##8AM&qSG(a%QcOw0<5rSL%C6}wdgC#zx=zwk(oG1;VGg3QowX(J zJ*OliDWa&CZ(UIZRi-~3A0F+#Y_<-YM+IUF_o1*4TXx|)7JZ>S5B6C%t40FOA7}wB zU9XiL8R)L0U^W4;rNRN_gIJ8{T3db7-swgl)tgjP(Z?s>~Qr?6K|Yv_-!ri%fn@gr3aU^1ey^$gWI zK%_4GSg;jscUtC=6yz1aj*NtynN?}qo?Dm9=|(j*ae7M6Vy5{zOWCG8-X4gV&6HYr z3BnZzk>dH%H4y|H5k)CYQVCD%yiSG~(E#)2n4Bhg8)ltE_@yKtFITI4) zJ`&Y|4wh`ZAO|-rrpQ;D-q5NOEo!l!BJU`Z3y!N4oW-1G%V>$P`DiC0!`w|pSpcQm zBxI1z`DDx`;Yg7#YO`*raYz+x$f4{Y>DI}*qf0%kSCT3=Pc>yg>JpmMgO_-fZ;#F)l`g@mSOlf4Zyg-aWDMrxYE)16)W<{2n#V#0Yp)5%;^k^{z<$a;2n7>;)V<(jj z*@bRf;a608O@`Qn)_4g#<-72Fkb?+&&JvW~Rxg4{JxLppWSX>nI5`uc|-KOTK@ z=9)`OQtb3`t){tr;E&o@dhNG0b1kA1X3C0AQBK7A6%(?48Y|{d5r7v*M5RoY0e-$8 z#rxst2+_YTzPz82K`+E*J3%jnx3f=X#RyAT7ZY{DN5H+u{~owE6`rQBd_;g>Dghs{Pj;iqa9ol)l^+su1Gch11$@z&g z_T*TMt-y@J42xjM)QY+f%`y9;NyFzgJPSz)IGNS?m31nDg*xb}i)egBk`g*R5k_EQ zTXv%vUj+k7>%&KqF?wt~h)s+?A6rCT6LeNVrsMDc0dvFHNs_lV*k6M(# zIap|4Ew-`j8gyf0^@=g+?DxDT@-ktI=_X)0!%@&_`R#U#0=dWeU>UK0swfT1$~&c% zaOm2*8f+s&2s03=`^w)xICYRZ)yN-Dcr(`ScucH+I%<+$g2Ia^cSyzBr<_BfTOc!m zSd=_!1HGCGR#Wk68YjpL_H+vefb!uO%E)`#Rl7ja<7sa;qPp^N4wZUNo zeqWWo|Ni^(bk(cY^_)5K`aC+=rM1MN8)+KFz_aT)=4dd>>f2eHu)$`yuqnpDXfTh8 zsvGrs(GB8Ch!plF>~+YCzp(0ql?R`D|Ipay*gR0qoTumJ2X#k#e20VW2v{C$rhfGH z!#0+)U(iw962#tD42{(1VjMh8cdn6Kjx#j=dFz^u z<+u$OP{H|brIP3mOQcw^V&hVHwk`#KSfkh|=>C%+ctVQ&&VoiGKvCr2&W4p0QJst< zY7Jj=c`d|{-412pr2?PL=%fI>INE4p6gtf&Q^r)DQ%CyfgAIg|X~HgMm}MsR1DyJF zhSb=2D5Jh#N0Ux_N$$kPZt#qL2jnTj9%_#Y6nQ=f$XIYJ`OyA-5Q&CYPIc&}#!j3s zO!+wxZ~fySp3E%tq5)V4GybCfpI4vO|Fd4J*K1b(&kgeb+-Q99|NK1upWOkuRUYk~ z93DJt{gC7WFi*0%EQ``TevJH(@)G?$7t6hg;i#had?_{yb8;5u-lP{Kl1xjEc~=LC z#5uyaO01YOl5GGS4BDgHA%!}*4sPeV(X+*gRBIO@CWpIiMHjzezJjnb^9V(E5?q>M z80zy9KeCQYRzq$tIiU2S%W$BF7IbEhYD(k$R~K~icCNsb6}%bxaeULsz=XSgDmXI7 zsn)QX%td|y>JIo^YCS%wr^n96G9@>t<)W!+ow&cKtx%kp154?iDjXSIuiYnEEb4-G zdV1)_^pZbL8xkEQcovFJ>!BNyMSo6$eGrru1I&LO^KRsvhtQpszCf3llg}&K%;>{K#pB=Q-9pv!>d*Ql1XUJYd)` z%nLv;^5bCM#h!n)&3xE>vGQlm!J@h#uN!=xH((-#!A))*e6HlO15QA@R9q?nd1*=UO&#A^qJH8XDCH>{zB*KoxiN@{?6yJ!QS!yO-v|vK9{{* znz#OE84~UMC9d8rDE<WZ~eYM{P3`EbL#)p z=H_Ny>;Km4b@*Ig-}<8e`#e7!^<43a!$!ssy$JxBAvkdqO`lOezqbkxwl49G?b5sc79x^k10CdwKt*BV4v zPqV(a{gw}+iQ!IY>+Ku9?>1y|Gr5nkIZ>4XmjkBeWTll*em*k5CqBAPn+yHGzhc z+dfPl@KFVuBA6rqq(EE0HZop!>Dmvu<+FqTX{x75Mc7kv)1@osRakW@@J*MF)(Y(Z zhHT~o%p{^yX(%{}sLP0sA~FpU*Z8b?`cw0iGvbw3pR!I|oUP}{uj$0IrBek4ltr0V zXW2V$o$enW9sZg`luie@)nhdyv}4%nPh@(mzx+!p7}$3}Uo2AwF+v5*v}(5qbEeUQ zGy3xRm6Cl)EMTXt@3@I1mCS&1Q?a0-G6+okBepRKa zWb>mHRF#6B^A0J3K99@DSDl$DC$JhdKxXYNH$i;&{NU*Mu|W&l+&3u)gQ!@V0R6bc z!fClgH0GfTidF8uJE7@`GwHJJLM8zoPXbQk!zY=Qfj7=-<`hYidO6L1r^jP0pqon2 z+G`vKJxN~TEdj9s2xZ(BI%^yw99&&3D^Bhp6Sca^^=eK$d~8L0fi$&+Z8ai`pSYFQ zAtHH(CZJt%w&6ryxD9E~ThB;On|?SMVD;3);V2r@&5ipwz8$pF&-~8Y(DLU^GmOd- z1=pZ?N!7d=<~@yX{2?X<%y}Hrh~Do|Vi-HkW{23B59A6RwtK|bRMbUd`SwtR^>B}r%z$9voN?V#R{-z;{qO_`i@!cIwFtuq_6-okM^XDgO7VH%DzM4sZL z6Nc>_|E!`BA^0jiMIH~#3?>9K2kHpw04Z?{owxF*m#woOc2Ar8<)u6WMFLb3aEGn< z0&N@bowW|mPMa@E<~e!YI0lZpdwb0@UH{-rXulqq5N@gM%Z}cAb$Z%7I;Tw9J9Ys$ z>W;-D1NWL9f))p~6ky5|@q74iCn*{Yk%QfLkTPZ6@s~dTf$X~W>H1I`wMUzqh37x` z5&Msg+D2mo&wsV8&Dwvd%`fM_&$#{rN℞fBZgXod37Bw$}CezwxO42;MfVcypokJjkC&)6V;yy_X0+I`vd3syiH(+|nFn^`li&>h{>q~>>RjVq`J4fF@^0xNj z;ENGuLHr|Gk+R}p^KizU^g1ZlUHBIuDBO%dLZY<8ouKPadSj0@L=zD*IR_&Zh_8ZP zPw_IvRnI~pyswHe2oI_kXs9p=U;l9Yvbk0r+z9%Slj5KQpjl8?;H$waau^43J`NEw z-(Xf%odtoX8!By9t}0fzAp*l8TWVM_)Nq~?AY$226E76X=g z`ReQ(opZNpwFgkzUH{U|ZlZT^-x@I0|%F%Tl z&DNQOqlY}bf+`PIA5>6o8$=#HVyunTW%xE2pu7q5L3y4B!^v1nxK-%6>B;^yK)`1$ z0qdR=3eUbMHDKvrb%J+etGd1AO+)fMXFPJjVUGyb4{9TN{>3S z674BjHbLs7b(hwd9?TQzMSowU6I_x(5Ta+T5U@csl1_0m$$B7mnt@zV)*U`wS&6PB zU#ann;Qax+tq*%f4kre!lfezFl^l4oM=zvjRI5N&W@Io@90xg?=MK+mcuyLBcB}Vy z#WWNXrD(IKFCz||8iG+9H_=7V#r*|s-%_zywy-_Ai=ge}9Mf$#KQy2zJWp5EuR_sC z)Q-Ac4qKUxsx!)cGxE6@HY|Frzej%{x0R5D+6FTrmNL|$sEf*pn2saoT0|DxV zF~bcF#d?W7Yj~8x6Fn4rv21qKUILB2AqF5x=f}}dPmSX7j{_+uryW zf$3H{6MjqwdC@a>!=Tsk9$<41tX?hl?e=Rub9*i!XNf=nvKFK4z{At2Vd+ITG}zJD z9luJA-^dFt9OlV~K|wfF2!-0`?YN!KHwa0>*ppc%F7Y59cfx@^m6kkXpy#i8dvGu} zXqg@pZvd_ku`)OX$ktZUav~O)hUr_TU3@yd*~hh{SzER;4a@g2NEdNtyBdm0`x9?1 zlD@g3)m1&|NlR>2KlCj#0CF3P6RycL`rN-*-P6l#4<_m1Tv{%MJ9sxlp5e4bTm(te zW9d0*@MwC2ByUATgt7i}lrf(NLC0z#BYY+}Z9SAJaHoMV4-3ieo{W?1+l++FC&TfB z*z1INLx9o~x!++KTzJXY0kWG=0I5i1c0^<`j7e*y@fum1uZt;GZAQ#o83HH!H^Qf} zAI~z)rrJF`JEn;N-f4=j7qp`V_I{&Xfs((5E zf41}g!O1VJz2l?j2QOZoHi2p^!vpnd9QML75Wi)$yyy2iux~2rV9>5Et3(~(;0$U7 zKz112t|gDKrHkOoe;cB>o6-(AwY}H>SpCN;9wOfGM}IPoTC{7sK*XNBjs|~#boT}h zyUA)n6+@cq@Lm;0XSD~J4!966QFVz`L;nDHA^4K{VrE5J?RnjleDR6W-K{JE6 zqOw!YdT{0mLcx`z15lK68eqW)SMD-+re%bTW!?+AikE`|pNg)bJw%oNrFeX-Enx{g~UcETuf#|Rieoqt} z2;W73MiO`yfpzl2E-yZCM($$w)%g#{r{0TEaH&pEy#MYJK2}l6`|mc>o@zVlKmB55 z_zCs@&Z2%!{=Z(^sHOS;*4E}1{{K1o|H09D^Yr=d-s1c};Wfbj{Z0p?2cmxq9Vk0t zJnZ?v+y=xTU80ee1(LVWjC#E~d;?9r4WZ@k2t8UTPx@;hKsV9oS`HR(yz1dewgCVk zTeq+%(5U=(?JJPL=WPh-YLWQi3@z4E6x(wYyEBBmZivQ@gG+tfXGOa*tlO)ec)O{I zRdVzS#~c&#<|=GoQIQxkK8{%<2ULCl^)r>}7!(hqL5Gg3vT8>%;~aTLnzW1Jfe{$b z)oqOYNY57@Lut+w&KYo_T^0NNVUMN4gG59TRpd5JtJ~Q&#TMOtwzuDWPOtZ=?QPW@ zU)2;nc}`uNLl>wBXjB_CFMLlyA-3pT4`8`NMeW-aY89^s(ak`lFcKq9P7hx0p8o3L z(y&})@zOlm0+y6{pDd82CTDRkgZ?Prbxnt>Lyw{%Nxh0h@w8U7?{}!EC$LpC8&~UinVZ>oXE!`zuN_= znDNsSc&d6{0LhEyh4DW-FIH9Dlrw7gQi~d!`m}Kis|!&YQ#e(`r;3HBtc7>y)D0R?SD3M9dX$u!bDLph<8rme*?pfp8dpcIxk6$)+eT3Yp-lEQQ~ z6#k|PVpjWc0%%+asVga2FI)lR8f_1NQ@DZ#-LrI}Aoh^;N^Y98tOP(fqSC=wIhXCx z+pq(&qaymhiXl%>Hbi}aw%ljCf)TYQ@&Eg*^#Anu)%oIQf5Mk6#m6?1{gEc|{03{VA9PET2zoKfdG~TUVMDa~`4yhMuo)Ml-Q|v#-N9A!e3iuk4h#3zPDVRja*C3*qg+lZ4MX4H| zxWt%^8>bVr+j?~|$~LW?4AZ3}S02lK(Q0@tsA^798&#c(VcF&|EhX^x_|Xw=KG)WqU4Z6>aJ zNE?OcnT*e6DE>#GnK;`Av0<*$z=YZjJLbV9KDe|VB*x$9kmMSeV}N$&=}-8Sr^Gya z>%H-4psT|OjW#nZi>N%%;Y&CR*knF;l3kl6ZLj^@%(FMa$cU+8+!A#!_SHxLJUYja zk2x4V3Ft-~iMe1%as;^d~*C=E1|yAwh3h)a;^d`~K+Gzh>+1?s&Mp zwszf%uKhtZzN)t0PR7;tKi7tni!~WK*+If!Frfz@6EOPurCt& zyWjCI068yi6C2{Nqh?~k)FIK`{@~UciI}mnooQf!QZZfP(2OlHsbb?#WVfHP!S?cp zh|8}QGVb?M#{KGJ8u!~Iz?je359JQWL~wIVKzU$h>UpO|Y-B!8&Y*fv(!$dWYUA($ zj<_f7faC_uzXL$Zg#?~jkql~tB*)T`zBw30(YWGZj4Ur84%;ml%pLKB|&l((<`qV_+vB}3kLBdv0-;X zBaM_zl-PRSSo#-wJCOPPg+oALCs2gnSnKDMt(`Np%NRn=e1ME(EWYdf& z5Ev&0U5S{7O@e(em3K(`tnVyW-jwJ=dB(tkZa;irll0~LOrtV5(nkigrXpOK}n)NJsU=nN2%mNXY zOHo1M@L0~!z+}u?4z9RDFpu4Ug9>ee_ZC(l#9Mu`UPBzX3i=ZB2UZU|(c&5%q1G-( z(PS7S?O*`yr&W0-R6{mr;A5gSlnl^n8x_qr12J=_YP6NKqa(nN;s?X7y&cis-I_|i z~gJ8TPPgx_&v>_jcXXG@KLu<(_>Z$HWoVz&=k_k_$nN#5>BqSOb?com;s3J zxH>TsU%8?#5Vz-ofd^#RQLug4-r~ea6i2tfGMPN495k?4lG>QufP9;FlD`i5Tc{ z)5HdiAYqGFxu|OUd_o11z)3E+LCW2}iZGgxZw-J!6gMWHWe#j!#GW>YuzrXe1zN(c z5z$9sM^xnjI^4Cz`@9c3j5H1TUct5^hp{s8fp3^FKsRXbquGt73a)|!*bT_M*X)$F z?KRoZJ5|S0@$kVN?;jFkf>#($4Ueb+uw7uZJ*_@~b^KX-ddl262S*GVP4@-~Jb0B6 z4bT!w&m>E2owu-|QR~kFlfDsQHf^1;2_N-q&nSKpdhSF|f>GMa4%%_VfLJnmhUwj- zuIzJcs2BD*qjkW6w*j?EyFEM!TwbY>9~z-2aW^9nJqtuqQ{m2s9Sie^PGlgf_(N8r z4Dy@t{I`DCBfUy?P_f!sEtjpyHoPX`49>zJk|hF~u&Wf3af}AN0_q~fsD{oe47xG7 zX#2v1M!{%Q^SHFF zJ{?#dhBl5=WPJ>#TdXmm32%iGuoDGOfOa=Gev#rscyxT;JoC&7B)(DF93h@uh~7dD z*{;mbS9oj?5{Rh@`akw@^67vk)LPrSnu|bVMrs?~L|r};X*x$RHjjQfI6Xdk**rQ| zKkc3#>^`H*!e{E$S@Tq_sl($Jr1&@V4>%f}%INq`MNt3BWfV#pm}|wC<Q-y#S4;VwSV!{=x2xqvNylgS|7)dx=Vj zPUv3_;6vDsS@E;sz@hnwlw-YHG}gJ2EWL?`G$IA?5!CX9gfDHr z?>M?-Egm#-;`_bTA7Mnz!DT%5J6>rB4Fvq93JV@xSFgfx6jujPndno`N5|n|)C=P) zMivI7>iwU)|DAlA3*cGyKbxBw|DSrT{^kDnGv5Ec%D(`fm>b|p_6m4n?trxt+>II~ zOieR!5-u!#Q}zy6_u*cKWErZ=h=!A_RZ(8(f&kXzvmh&@!m+T# zG+S2 zYPB!)|FhBmpZ5+AQ1st)$p4KH|C==F*H_;_`CTu5QqSN4A6n!h7ReEF!VDz`+?&AO zr?5_1aOC|4t)kQx)AfSli+VeR5$>^+DFNvQi=d+Gnq0^d6G`>_`1IxOxu)KDZuyn>P)uw()G~2R@?dww8 zdhyxwvvoM!0-2-tyAD$I5cy#b=sPWcz*k8+d=^yM+h0Y*S$pxSd3LT=J-U|^T{`b+ z*qLdfMb}wK1|DdlyVf6!ABaMWsILyEW$gt9l}Sf}$1j@*8P->Rm!g5i*P?0ReUpw5 z>HWj>x_YMComFgIt+hMSe~hu}##(T;4L)>P?62K%!nM+s4u{6_NXeI6xfwhs;auB? z_=1IeT}=`+hJ00%;l~d2>UmRwxXy3qe>Cj z`NjX^vz`A={(g9HhVlPv^|g8pPk&oN1T-x$XFquiC%;4do4p*h1IcG}eA3!S^YTyy zZXOhJ7^#l;CeJHS!06EtPTxi64Zs@Tm}A>x_#FyVFSlnRj#E5*P6ef z4CL6=W?XOtw66p4B-4p7jvUCc0vHH|^70RL)5eIv_8k5Ph2@vP)r41eBx`zL8Di6{ z@zGi$H5r>O9jlxyM8jUE?aNu6&vDcgYd8}d01p3{n8%_j8^PyX3B+CD(U{LuQP)e_ z?Viy|=`r!a(n8Wy9$`p~25nbrV9^D#Joyj0%`_k30 zQ?YYXz%%I|ugL=;0Rk>Z>cP^s;VOYL!wQ@(`TbinSW@FVY6BBh-?28*z7Y1AnUN(i zTC*?!vB%N$n1mT~M*BA=5tIru7K2zZ(n?NbwqgO!Ws0=>^CdFS>|<6|f`7Z@0n>s) zo8y~(yqhy9v`pA%4+@>gfAOah|7OSk*~;ku8}%>uzn@M1J4?jB+9MYK_&^xD!e4C5 zesPZQcgA}xBlpEx?3*E&#VNTg)vncJC}N8Vae#Cw{s2LW;@;08NNF+>xd0f!NqI6& zMp#PUHZCecaDO*fXvN8H0U-6X(;lJHNEL(jE8j~p1Y88vnhq-{;CWs?SXrj8$cB=qAw1@ z>}>jo_nDHjY4rH;>};7KSnhr4^V*l>Yz{t3RK#i~M-)>7Zk}8?e4RjO6tn}Fc`MYh z)wo$!T+Zv5(lhvgKsUI|sB&Ud;SDW0)+FT?0D}p(s&@bOM!=1&WhBYZrCqw%bB4@I zpgqUE%!pN(@3A)=*$N+*#_?F2(g>Mm=P5qr_Fq?Hp)#RJtjY9J!NQA@J;?+lI4x4P zhl;yjZ3o?<9nvK6%q2NkRwT(+hnFkbmVkRQYb6QnYzI7;nbmql6dh>Iz}L?n8t>d1 zLw~*O*8Gi?%?`xtL*NwSM%F~KPA5&-IsHm-!*KhfQHU;TXK*I*h7sTSi^DWdd1B2{ zRfLdZ<7o!8Ml%D!=ZXUvyTa)iyozoVMxCSx@P3FA?f3?5OhL*u&YzhXTCd@yHm;|1 zZ}vkMRc=>!hR{9u@pbIz%{7bY!%U#RK1K!La3ef=-V}yvAyhW}x??8+BF(n;$VmET z=h^U|6xbjRdflp$8axXyUhD$hK~V#r#3+K9MHO1uP0J_}gsxzX5M`{I^DJgXI7Eqc8bCKBN8T z%d;2iF-rf9Z&~^$WZ>Vn;U_)>xqmuFXQgT` zM;3DA<`2lu(K*fj%XB8RRqGlwXr2)xb*tTAjB&WcRZ=Ry4Sg%vK;oO&;eirK5o<>p zYvQ~{p_MQ;0J$~eIUn5}bFGYU;4%!3NHdxYcs$vubJ8vl41tztN93lR;xl9d8sHsU zd^>1gjiNy`iSydkZB#r-G!+d@S}>El80{GciOCju9p4B)Qil0~m^vAEq8o`BpzTLd zCK?;-KEoV!{sWX~=>Cd2@z4Qsh~8doP-iR-Ia>+qtn@1JGcsKadB|6o4|PCcSO>Sn zOGI~PN$Jc~bKvk84jfoOBQiO2;Y5Ib)3HhNJ2vrQQLf`iMG~@tk7W`(R!6>IfsRju zOjk*SqK_4IAV)ItMDGRY+e=AYP3Qr{6v8L%3Sn}q>;hMvhWYqLoqxgS)`T0Nd!~7qeoYp>BA8Mn9MpA|Le@cn{&qlqzwTbax z>$Oeb|C?X<|L0u)Pi6o*Oa5DL)J6aM=+QdI|J`bQiU0mte!lX)QhMQyu8gCh0&1*U zet&4Ti3aRd?<>zkuZUOY2Zv`$8;{Bb_TZ))Bd;~8uU7~Ax?t9L)H{22aAaPSeCFZT zR`ZwU9zOfJWO^#&7ikPXo2q3`xA1kz?{v`qKYRzDDW%EF78Kh*I4!SLpt@-qqjr3? z_xy181=d^rnJhY?#@^v8kVVT1zWmrcJ!&5Am(}W}THQa|J9&QA`l)$(1~TfCe=L7p zlF#LTEPGQLvfDXs%xq{68o6w@*)~=&#Lx;%*cb$}#7q{iwHyOgYiK*_AKsE}shXaN zufGnp;p*2Vd6;AKY9@Q$1~+E4m@L_1FrK+hdU{^AdBx*N7gxL*rIr|{truQYBX8;J z(%v4dK%~C@?`$0qbuo(4*Z*x-@${!{{j9A&TN#iJz4f`z5e-iAPgzHtb*Bm>vM;O6+=HvJl*v|7-02%>2IrvR*^$f3`Lo>&*YZ z`2T*EpS2ZFt*C^uDgM3}4R6WIQ1r;YnAufW;jiJZt^n zeFb09(j$2Sg=E`)M?%IL-Y>+}tEZV408zo?RyVgoIDF?m2DxPoJv95N$3W~O_Yq}q zfhWnMWoV5iWH}jEKj{B67)4G~7^;}sl95W8QgrbfoYBkpJr2gD`}#pe)!-~2{xgWW z`cc{YN=Vn+DC~I9W{a-jO6@B^%oV)hD(`qe-X?v0)7YYP>FQtJ($VpGYw!5zsJVCE z+y~jAR`Hhbd+P+4>F!bU=v+TIZ@xS^KE=GI@1Zs5Wzr^^j=nnsXv$m4=^>0;#+)5$ zEfGj8*u91|C2rk3{k4Va!Pa5(=*^ClA#H;yD&s9#{deOQ35+2=Ut3Yn(Px(Lc-TyO zd_`V){=oGUjuGjWLLI-W)eSqkcYZ)>yw@*xe>vGb|3P zHEa?VQYzDi__RlZl38T69IfHW?%t2h^VZqH|1@D1 za}cP7dOJkOg)pSB#3byrC~EGGhU+pMQ0nEKC7d3+o`fi~A8CUYoFf?uUwIdGm>N+W z_@nj}{P`V+q+i@v&uM*?UOqC*!zhZ}5&(#;NH{B)ULC@-k1^3;u>j3j$1FH#JAcy+ zC0(KU#qU@z=}feo#mRn+P$_;*6O>s3tTK#g1_i73bxutH`FaVmN+ zRT>5T=&gibFu}*M5p%gpR>IgZ-bQr-j-)EC#`yjh^>aArVEkPiKULKtaTON?RsQbm zl+wg#1%CtG68!*9)CvI8t|%sn_~otpo)N@2{FiwZ^zE`Z6~{)a&7_x&@HsF}JLpVC zK~?R>S*~0&FkAY*v70@?ZfyAhSKum!qF^E8Oc)aC@RXueH;}I>zH)j1D2M_ zSSW`)+5qBm7tb3oy@{EOMKzL>>5-$Dk2V;r@ z&bFj@Hv`Q@db8emGp%iutqU5X3p;>rg_}yZf@R25qM$a&TI&tf^W$+VJ?%U}oz&n7;Fgu2Ju1 zkakNKn96r_+Y->i&XjpqIpgE!Qw9ldtrp$xa$v%}o+>p3B6kIHr(ZCa|F%iCB+g6P=k>JCi7`Qji56fD>4ir zJq!HTEj}5Dw8bz2>P>x5l%SJ=5J|>-oH*5<4&*zdm?l6eOy|hUbku9%kxMpM(dH(x zn#qk-!fK?5YDkgkt=mg&$U!1(W?L4DMre%U-i$qut#ras>k0&wUXUix<116pJ_l0{ z7wI8?d^Qvq1u`5=-3n7%=W7}+N9HbL*qs-XF$OWq12@y!)K%!>%NbZUbCU9wN##vj zAt)rGVTlg~#V&|5#R$qC=JP`3h-A~Ca=Pc19QMsA(czqMGj(W7^CO}e7Jr?0dIgZ_ zP|O2@-T%Cu2O~cY=2_hNSJ%vg-OngM)SF|FQLmHR1wPLkD3Q3}Cbteg$2vK)ZK9BV zEE2&xWvC;YO&^>#a9=B_zpux&(`UQeA*` z-$}*XcRU>T8NfIU13dgp2F@&Yq82K-Cc_!SN~AOrGUV$DAWbr^1p85qh_M2*!`#zc6t6=(Fhf=h_0Q8)!Wm~Q^mbtW2OvG6TPPtK-szig6>nT z;P2FSP60Uc@G@mqm3u?sa$Soz%+uRq-PtvW^jbY~MycezU>v`+q?}X}sLC7x`TsVDXvRCMXleWix%*o8Nz@=*0>@oC|qDn zlWGNiIei)lk$S@$>M<6a)n!qSGdtw$)U9#E>f~__;<1YX@6e(pZezJX6=!-|9MoavHj2bm-vr=g8fhX!|Z>w@r<@6TJ}67!ekUg zyX4|_9PHT7U{9g0(0K-gt-}vf#ynxv9{14N2DX;_2E9q)1%RP&-*HmLF$Qo{H~xqs z?2{7=fEm1__%}nk7r}ZZQu)EgooXUvnfjP#!DuvyvNlKBnke-&Yv_cJ#iz-6KEe2Q zomAQIa^UyeXWjN-oO;wIf9)LWCoFBTY*~?>)M;5H}%3d zjlwtUg>N3g5M{8FG0k5LKqv=pa6vJ_J<{SKWJ3MMtD#!m!H76 zuuY5iQ5gN8{T5H!B)=dcR1^+IR*)DPMlCW+N%n?4|I*x7<=_yibnqTp*e*V%MktUX zN9tgr(}9yKa0tpaT2aBf9b+bm#oqcoHGXSsTjNP@jCk^Vzx6-O)8o>8n@(de%HI@v!bQzg-%?t&ZP9$8Cj7l)>52k0<#2iK-zx6_n(j5icnYGqv`xPVK=j z1RQ#UU*orcTu+{;)w+va8gfE0Y!RS3ICJ4LHKZ%EDqMutf~HVUJsld;`xoR-J@GQ< zWyaN@7YC^xR#7}yBH0tZHO~)THusNTojbU#A0C~8>Bal^>Vc_I5Ci7cb zN4F&$Q2|XbW3%E^XpQN|ID$Am>)Gk??*88H**UB!6w!nZJ|d+v z{ljq~GGT`_4BEQ_XBgqezg3a}x_&?G-6CD!aXK$`jMgXWY8(xEs4{YEQyO!nq(Gc4 zfVjg2kkp^;p0rNRe?4jLctmPSXuQK?O>gX#G9j)WB>_Hjm@A3s$#MEt%7{mD6`_ID zkQ_C4Pn&1wt-rql^hB_8s^2dWA_9{&)vaAo-uZyZP0yW~pySi?c_(q^Ql%NRRRJ|| z)Sy8uV#Q@y8%2a?90`HJX{e};3OUN)ZqK2_ykg2(GWDIR6)u^#B>NM<-rPaIRxZi( z&rn?(0wf9K3sk4`AHs>Hzf)T%;ZSX>QT=I^j)qH1+V%&(yeFiQ(vRv2$s za!R8M{5m;4I%^U|X2S6#nT-rhDH8@gYzP5IdMV;}4t6;2lO?65;Hgknex5F#;FDV% zpXXqL+Z7>0svDN;jo4<<^-NKEX84tPR%T>kDvbh$XV!lkh-^Zw;kBmPVs{Noj+OOW zm$E0!I**rO`dTzp06qd|e(U%_=PZl)jK=Izg8J^D%Ov^~)jcDk$oXR4$X;#dRrx+Q zRE0m6{y4btZ@JBLQo>AWb39j@op@|5^GdSuK!%xFB7iGc# zg9#>Kr$pOB?`|Vk;bp~6h7l%Y5+E&W9*ZmMa9L%sS!MWa!f5lPP1D2NoK|I6t+IHn z%3-$3=C<~*|H|z4^uT4e%Hp@Gh~cKl_pG6Q0J9_TW-vQ@2%m=80W}GSRWwfK&|7H; zPn&tGkSK9-Sm%AHXQmag|I;zib&H8w0x!X2O7V&^t4udX?JZ@` zAX&OXV@+{t>p+qnH2Uv4=aG8Zz{V`xD*fTS+*wt_$azI=1`fSv*Y#d{VW-S~LZIpB zg_wJK{=D^K_q_S@?yp3YGYmbAHA$JAT@;TB-vRu>S@o(jucZqgP1z2P_jdR9Pmz={ ztSmV_e7IxJS}BAT*et92_Z7+pgmUyn2s=>r-50wDN6gc0KJIXAnNesUk2f4O&tDy# zHh1@aNT0fqkuN!%oCBP+&_}qy4NI%lhCVs+4&g-p1ii3^$eT3lGOn<~Z?NX-Q_B-s z4xDg;Hs{TmhjhkN+OaeC2m^6F7H45murR0?1-azx?lecEeG6r+xkTnhS{BAwQQf-W0nQFr|Dhbj@)yurs0zJHKV zEtfXIFDVDbetodxOp?PYXnGpSq~!4jnISIV>WIqv{!1Hv;D+Ig0gl7lHNrBfZ-H3` zBXQ_M5B4JGoAOR^J($z!b!nHvG%`wJQ5&5T2usUkQn5`t)FcJurtke@uxtirL&A5N z?Q80A3IGKV%ixwQ*q$JJ$?0V>ShSyMePk5Vorrx$f#gl|?2#x26nnrv%CL1Hzmxnt zIwt88yg)6LiVMOK^}ehQ^Vo8^tm7IOO)h^KkHSF98AiTvVZg`;y5SLZUMhTFPW8K! zkR;vLQ=2x@lT}(FTd^x!h?~+OCo0NrnzxHRd;FwU$=zepR#+wZOT4Lgt2u8HswBXC zXUSG+WjIRJW=wQOkxkt&GZ=Xc2o~eM(X`6riS# z&koKHermR$$+9aM8fZnxa@sdPPiJ++>NoD~w0Uy)t6d12lZB}o;s1$kvvzoVZp3f| z4GAyRLIs3Gl)x&w75%WRXHZ~~U3uda)(>+FUxk-fnZo!GwunvaVR4d}Ah4voFM#X= z5xY(V)P(!K&lq~JL$-7@jRfeZ5Hq&_$`y2*s1@?}eQNhj*~$1#<+SK*=V|ScNUA~5 z6PhM7(+H@|MN)xUY=*L8RCxDGSF2A+eOn{-EmyU}zTgtArsTKmM3q-nL(BSt2c}~b z#;tHM1_KcdwAMM>uT|2~PutUH?uQMTW1*hQn8-a9+$SL6qs64`5tQ?}^*q_qxtmKU zj)T6^W&YG8D{iSYsS>=weWQc+z#?{n^e||pXpeE<5?qI$=gS0UIVg9)w)&<6(m+Lk zEazR{E#>Sl4EgYxx7Ymz_SdYfmBT6j`Wr_jJLgLC9AN^4sXGP*4IO_-+#QRXM;ZVt zI@Faz%#~rvNw^5WF;#ol?e3_gG*ffH#p2>MK+dO6)no2s?8n!7Zwge&&bK!{Pq8h- z8CSK`%sCph%w3_BH(LmM8RhILQ_#f2(gpQ^I1brP(H8e|*w%vE{gZ6^c~m zZSs@vMwqL0XRAqL-$=*kK@fC;&P=M1Bk|6n3iGS&-Jog}neXY@Pg&h;(V0@_S+2HExeiZyEK5fq$WXty)DLV-crk)=_DQbAh>| zCVPqwo`mZu%4<|ep8s%g);emQ&#AuV;#zZ^(bLL>gJ1GCmMmW)nUn(_-3Qd|oocnTc?7 zjmJFza^PtcJdBRh3 zxvFBYiWsUsXI{BiQySe_(R|O*G~4RBoaDa#^zR{ z2TSoOH`1z2!3v!qc>c*AgoA6TL~X;xSl}|Ns24G(5MF)M8#Bs0rOs^0g%?fFXl6T4 zJMJ3^WpF#~G*TdX6q?NyTis=hN^yd7P5-pP3tG9iI3TD{^onAqj6xAMePqqZVmvZw z%pP1`qt;Os2aZN48Xq~Jn|1@4Hqq#<(&~h@Yt3neHG+T7;3unS?x>@8t|4+E^IWsuEUXpzu7J6m>&(k#1#A=z-$ zoy})$_K-VxtJ+Lrv#^O4V+~LyV~v@2M%1MWj4;zjJkzLnbuGqE>$8WM%SIgp+sQ*d zq#GH5JL5+xB;M4}pI!e@9a@848Mja)YLo1oBU~j$B)fZi&9huN*d3k?0u5jev5d&n zjBv+`kSPw4i_w2~GBk_B>grE6|4LB|GxCF@pLP#-@)U;&=W|H(y%dd3H~8$;*{?fA zl!Rgyqm>EIfQu-8)+5gK(ClC3+n(DKevE%yohXW9y8sRzrqlCXeMXC8q&h z9afZ?yDTr;e;DyyOzX1LQ)AiE66z%pU4Uz%^H|cb>`<0aF0Sii4k=tWM9k)*RkB<(2MQ)?5 z^wTZaH{}^~87-3PWcMsJzW~1R{H%3w^!!-o0W+80`Ilo#-;P;TE6F*rT>j1iqEN=l z!Uq#R$kNeV9CGGdFo;@p+c5E{(|F<3C#=x5lgiHI=VQT6GOIZ!ZzFYLu`bU9ZO|N> zvD9m8@2AAQxfJh3gi|w^6hTf{OdhMt5j3p|JJ~zwdd6)zmFX^ zxPD`0YIuDLdyz144(tIaFc>rUCh4D2=_L05Mx-)NsAJpCsU!`sE*tsmM!GSpcAh?Q{dlI>G|G}|>X+9XmZdZ;PjDI9S0hvU z!P*1E8mL5?>YKFF0jT-u5o@p9mXuwCNPrS(Uk|$F-k2=|_ zk?vtS{dU{eG)0zm*8J%ZP02Jqp_xyHlbKA0*# zXj`Xo#x|#%G>_hxgSVySAC6x(moqneTBS)TEr9skbjQCB9_(rIx3bLxs`yX}wV z;quyY=9vK^5y{QF`|&vMcwzNP^M{6>V+v`h;CK(G%hR8KIaYrt{`8`p?;o31;+R&K z3~sW@g<a&{MhF1oFRbYN2zCD%w=ReEFJ0u=Pj?WOc(S~5WnZynD0YtlN`n$<)8*rd9p z0nTGSrj)Hp&EbxzWth(V<^j{?eX`*tbfCSYJDC@{4t+_K(4tMU(_q>rZ7GezeNnNq z&f!V!%|zM)AmKJ%5hG1a(|pWup6SZxg$~vd@tT3UCRz$qThqE?R+k*_Kmg-1X)SAlK1mnK?k1yT6@XoFgXN8RwnU*=VoEyliRB-{ zeK?z2^VrOF4C8gqMPM53>^cd8S@qHf5?-o%r7-r#iP0_fn;1=aJx9S^weOzvMs=lqluO`S0GdE$ziIhIo^7jTbL7Zwo6oXqME#Pg z;$}1~yB_u{PGRTdb2nnE_bPKe&TqO7Ei5q)ZN9s)TV^KHEsvISG zf-&hdTYIoav3p6$`&^)b-f?(<|h(t`nO7^#2~oMJj5op?Bgt~$CKlVI3MRqTm# zGp2iOj{*kGF2vmsj6&Jz$wLN4eV2@rhG_x%CIvUuW7wr~a)O4jVl(KpNyT#R6EcFa z76Ogyu?8@-pt4mQ1Wfn_RT;<+Rv+Z3;-pNOJ-Cy_O%AA8{iYc0=;rHhbTu-yd2se% zTjPg|eH48!#lZemgbQ}J-0rI>?WXP(7ijflN~_sh_(fVC6t`TPr$u|~JCrmp%tx|f zn#-kVlFi>VYSnhKcSLeCl$d*IV+%IfQ8}k7&9j*M+b1T&+w>`A<`uaG8VjhlnFM-} zCW+eOVYY$9vMy0Vtu~L2&#}=Rj#~Z^f7UPv`bYcW=6(ozAb2Es=|8Z! zj|<&Lox21bQ6)-P*$)RSSTOco*qfo`e|J$<-z#5js|)AO@`my_i`_b6BIR?q4g8TZ zX|_ymtyT-Qa%e%^p<8|`+Xn)8HD}q5f2-zg_X?w=%nJih;BhctSq>XBU)dWA;Nr6H zlkg*)_}as1jaqFdMYcQBI?JDBD?0A036-%mP5cA_;!o9ap93Y6OByzTr%@b{Qv@c& zDZ&%v6yfphNKjt@lx?4?+YEf0dMCR7y(WcdgDymHJLS)|@f*23cyY z=3b%ouW5Skn;Ck4$KWwJ0brh(E5D-nFN0`2PwW@J(`jLDlp;QX@6@|lp;=Z+R4x+; z<#pD7_YHD(^)7M2wccsTgeP#P2woH#ir^(VXbyO(+{b{oF&o~_G=MwvLfrXiAaBeD z8Fu${>mv@|&3mR|lBUcWg+xM~V+0kY+opC4$6}&EOdZaUljzKo5H&U~2+n z7t+|>kWl6fnKS=SW=AkE3zrU@kP6|H3uG&vCcav&8MvJx>a}s`2G_5ZcQ9Ska1``H zU_E&3p%J0vk~i^oU&&--$K-^*VubRNFaQ`5V%mr}W34S0rFP1U_yX#oH1WpaHaHFo zifQCNABu#$r^dpSq=M5Y&b8I&GzwVMr>1vv_t{Bx+PO=wo%y>ZfqukFC3MF|H?3G* z1aM~Kykwsw6_x8Dwc{;@_!5Gh{5lK#Bis|%`g+;6FiJ;qOW)i$&JspVhoV0_m8Vcb z@5ZvB^Il;W`G%)DMYN6@-U;INC>%-%4Tm6NAR-a+>E5wlY#jXb|_a>K1~_d$o0@!SAMVWjk{F{e&l z1Zf}Hq@k0d4ZV`UTb@cltRXlBwV)-`G8xaO-zWA&nbr-zGi={%D0>nMwpG2HX!C^i z(gKv#iYnRtm9zB86*EZe0hrGqa;J#>~ALljUDu za48li`^i}~6*URY&$*(pUFI9Knt$wGc;+?6*$Y1fA$@!&Vt`Rhg3+zG^1%k0n)f$L z2>AIFYOv%aLwCC?M1*2I*j)cpH~2@zs?Cl0&}wtzQ{a^jePULg9alNMVoDgb`Aw>x z+*{;{NlNN%lr8#|s?I4ya*=uqka}Zrtj3gjDLk8k(=M~i<#ckd?T^}5BAQji5cT6> z_2y?|Zq7hHIre9f?Bk-?2V@l39VguiZoDfJhssaL){bZ79musyg`!bb-rDgI`h@A2#rB!y1bb2 z7)=ve{_7S`C7=eObn1JykSFY89m=>rJlHq6I=BamfYFta(*(u%Z#;gJd0|HR`f)Z4 zQ+x&TbPtYvqNBP`{fc=(r=o02w@#T-1khyP=*cVL$T)L=>8LzihTh26!kX%q5IJRM zIdpmE4s+=6C*LS{z(us`n9upQI3N17V#dfOWkit^Lc_F}v6XP_6Up%7FO%3kL$_18 zkO2t5RNex`TbW(u@{l9vkjb+pEj!3EUsRj?gOA)*q=J^PgBP#L{lryfq5jfDPfW|5 z7!WcVYoVw2 z&Z%Z=e7O8Mj>dkkb%C{9>CF{BEe_Zn1%a+mSi_O`h7Qa<=5>T` zUXXT?8(*1%Rx{Xz>0t_uoa3g;8fyZ@l!FI4OwO|=TOWaRI+!?hg>(%V0#S%tQYuY7 zxYx@rVd2o0s0yD^aowrtx6#zJH3~1U#>J=ed9i3IAC0s{@-z+UH1z1k_&2#DHD+&d z&Q8v2?XGB+i)Rc?YI&zh|GI{~yydxSSnFCYf+v}ck3fsIKHvzE1N3xMtd(=Q9IcpE zFJ$;f^0t6`Y@~EkFL!^T$h^qyFexmB@oxM|RDx^lhdRFtkgtOP#LD zYq+|+_9&o~oH)i{*6wa>@(gvRsB9f-PchgJ_Vp#iN9Qh&KQeb|H15D%=uZEx+-2hfxXa_abC>nn z-MPzp;{&+M#vQl|xs^{N2+q#eDEeVrx0bJCmd5eUZ7ul5pMQ|i-B)FKr6_>^-V zYU$K+jDu65#Xl(xy(5`4@O~kdUx?+u9kH~|`b+>?gWzVW2(>i7y2v5|OY;a!mN=LY zqykw}NX-hA6!{ki3v|MC%s~gm}&rUC?SxdEC z=gngZl@yKtF6FIOXuGO@rlTlDniLEEup~zQTT%@v+I)x%6t+h7A~98v`va-G#fm!jC@M{Ig5r5@HRNfrBSW6HiZ zudPtomS?o3t>{J3^<+3hG%i>HKuuG`V(=Jmtnc5dXwbV=7XkbYQrI-L)%3dREygiF ztbLm@g_J;WP@1eYbR0O(j?MPO#{R@QLjs4r_mt#~4M%oS4xzS7CsyT)O~`{-jiQKX zKJ$lUpX5rMdD{XH@VMJ-Cu(Q6WqD~QirE?^9{TNIR%wUxezI~lEtcZ?{|qgXTNJ}x zX0n&Zj~w<=%wIALhNG|<`Z?A1YDthRt2R-#u~l*J3EJV%IPm=VDv3tuP)Oa`6T}|6 zIzOUo^BosndbuCP`{C$_@c;dSN1CmC^jZkJ<}U2Vssr#imF(MX!<=v<`n{f0&|Z+! zgO`|q+8$FD!!$>?xOlzdrLk*4*$&gkc?UVqu9}Q*V`*9EDVhl4E104AX9Uk7uu(x&ngkQ?1a|=Z78Y zAAQmtkbAIkaQ<?2M@K(nFx!2M{i#YqapG=ARrnML?Y!Q4qj1IL6ju0dvEHMKF9T24?McSY)7?!snO(M@b2g`0M;CN7^Q<3QM0p4=OK)%XSUYvRXaj z&5-?`XGdOICWNUbU|=BNc3OVBEoPDVV9jH>VSEz-EK1u}TXeV8Kh17S&r?y8;#5v1WoS?C`EQ1 zr*n*jX~U-_4H?v_JLbL=y@gOvjv8~hX4uQ_>kwACm!`(?WKMx^6G?>;(L&MbbOSs`|rLCj*CuCHY7iQh6Zz{RJnl{~iE*FNIG?^CD(XOqzFP|ipS zyq!GapmJ^?Wb4pwMf|pe>_x~*V{WoyfVzXm`$IOF4m15}cUi`yA)e+MEb?bkww%&# z@#;NECT)IKbC|<3mS>Q@1+X`6$5$9T(25!(*aZa^Wv2xw3cqp+6|1N2E;X*tjc@>y zHJMOtDz}3LLbwCJuS(y4|9yGdVO$@_&m6gN_Njb{@S7Hk_&stWr&e-KxnCmi5sSW^ zzV#X$X%+UyI2aW(b)!S*M!jBiLy1CAC3h3{I&FW{DXREj<-up$J%~j!Hx1%ic|oR9 zPpJ_W;sYaKb+AGD(c8OjmefALTO>>T#IGq4w4fqhF4=M`d@VSK>HG}RRictb_Mkh= z$r>j;xaD;}=4m-k1-#fc-uN`)Xk9ZADoT+P7Crxw*%+x8@C}8MRX?z3bb@-xB0-pE z+G0vY;;boY_*AjT?oGXD66l;80s@;3W^({7%RL9srsM|$jj)9SvVO~fT!us%V2OEK zeD$pnSiKG#t-~oErV+K-9&38rk=_O1*dw_ZXe;!hev2+R=e5m$MRJCoCP52I zv|y=bnEou;XHTxkp#fczVVYDc@N1!q@kV`m1SH*QIbXAOzXTnwKK(7B)bjoFg=R4T z6!MTqj|nj7TiNX_aGOLt|W^Sxa5C(jQ@=AKhW0?PAGP z8KUtQiYWqQ+y}XI9P$2|Y2#qc@?y3<3+$J_!1P%9V45Gq{CQ^R!{(^*Z#75B`DpYu zY+D3+dZ1RbNkuV=6y1bw`B~f(6!?2r)my*<`P6 zT?z&BOFoG#w7To{4=2ev4&>Q}YykgaWcwl-jqki7=>=JA_8T8__WugBKI>bBfd?0G zK@;mFo>q=suc_OZ8M7>Wj=!=+mvNXLhct6L>bI{d*RWC#8*jXW!_i@ zGh+@nfOy)oy>kak-G#9NX003SBwZHPh6FwS!>%qa#`ph zJhyt1YE}YD=hb)IpOsRS!fKAM!dS9Q@l#n`XbRH^Xb4u7V21ieLsix`6`Y!L)q%vy z_HAvY6e{ZZNH)ikHcEk`kqcgunbFRVyIMDfR|4L{CpqI1bG+Pl&LriOkYTuwVK((9 z&=8s2$nWhiXLjoa4xBf5(Y831DIzn}oG#;}+Oe_+$p*l(<6!V_L2+nY1;=PTG*fIY z!nFRugh}QPn%6xqPEGE#{8Q;kX3al7T=GIAUVy%22#iMCG>ze@ zEG9B~Yhb)U!I=9QKo=l1xq}pCT+!?F)a93xab?~x-C-P`kMKwnu689F-p=EqXkikM ziYmR{dXrazX>6v1SjBL4Sqt}_RLp(HDD6H27>8kiiEatyJ}jJ6O3*$T$~{!`KP@9u#dQ z&RJ8=gpX|>u7#2@&Epcbh&$K9OR(TkndT(E!UOZnnenC?8jD#9mY~qvN^#@1%^Te> zf28YsR0uB3eet)nh}GxP99Ew!f+a&NFJ#JfD?+^s@FHyHye-YkML1# ztM*^2_BkeCf|p22{Z~JjRW2CC|Mh?FXKm%{JLebozQ+I6IY^-(c60)DzFn{IcYmO;l0SmuE}R)IZq?Qt*9{L$!E9r=ScddNXbZNGbL7EjN?fFX9M*ND1ioKx8o0``HKKUyJMhfvIwKygJZyAKSjOkV5AO% zK__TmgG}C&PyY=hcT%fYFK;@R)lTpfyTuB}?Qw)bXzSm8yIED-*@q;1|ES61J@0^Y z9xy@vSRF-ggI*70`L2SVZ8VBvGK?t+0@MmRVbBhNrQ9musXWI}*|RVp9>bxustzy?_BAK{ z4A3JIlgAPgGS-c%YCpQTfW`kCh}HvGeL=!kgHF_*U?vn;Q2=HTjIcF7zJ~9AgASUb zuzelFhwpxi2LIjZ{8oj*Jyre%pq_{Q^@2+n9x7}mv7*tz5I`7i6XGBY+n(pGuC99f z{y5l%Ax@B2<8sqd19{ut*x0T=Ru5}H8olQL@OClwJoV0_ZDqmYolPjn|8@qFTg=sN z3|TtC_W;Kaux`;kT+J(3Fzaaq7IgOLb?DKs7ohVXtltgV!_gFpU`tq{d-jGvRinZ8FiHO4I<5ko5Bf^d z8v_;u?}ojIvo%ANVHC&VMcBjiLk3)5VQhOKfaB} zLB9eB8^TtH3{6!%Cwzn%Jdh@O;WbqUqCmY{ zh^j@h7$?MYXpTh_3(%2U?*kcy_3Yzn#{B>&rUyr%!6k-Rjwfhk1vmf z2;TL>0RT$7LCOQ!?LbSgidFS&g79}D54ZC50Hz@9@~CJ+Vwn+vnAeWlnqZ0voZPB9 z288i@Xe0@Jz70Hv_rj^@gNWDdD*w_CQ5!(mgNZsZf`VlGJR^Su83JoRH6`@O_M&Y21Y+w+g0TiGe?NlFLA;ux^M%2Qw46e-{pkNC; zzt=E<*d6SpK?gAw0BQ#v^qxl=#FOp8Sv9Dt6UOZ^q6t*JVSIoS1j5sapre=m;1+uU zjK<~mJ8zK@RXlRZ;f+q8A3piepNH7Yy$tbie+~w?PyrE)T#lsfaWng?>yK)W>*_@m zjB%L)kb_?YqdpF2o}YUkYfwNvJzTHLXv`$Y*xAB%ya`6zOkbID787mnm(`cRgP^=R z+dEcgL<7`M3LmP{Tg?3oRo1JGGSS$A*-7Z_X)q7X%~E}!`J3AtjqS}Xb+Ui%dAwW| zZ*K~}0_NNEktm3y6eIHmtb7}G;2~l-tcuX>38Lj5pyjA{TcHW_Cu2C#D&f6a*b*GQ z2)s+!v;%Yo>V)H}XU`zdtoL&`maWe-j(7}YrBksvFYn6%5-Rurd(u}I@Efypd3&o! z8>{441Qh%-#4({ekrAYbw(=N3g7RT6A+aLaVMqeo>eIHT3q>$N0OkV&4^Uc|DA?Vd z6umOX7@=1vKM>6VZUs|z5p`~1&qhS~VuF=*SDq>cuZ-J3SV!$ z-hkKoDl0d6*ce4;QsJp5-oI+^9#v}Z9>c$y{M~|o8}P3we>dUZ9{j6Uxa>3d_Z

sBk+YC}HPr1mo^i!C&y&1X_;UrN`dwM}X1k<_lq`z`v1E!5=g zy1>$qn(NZ?Bl){0fAuJLsWlD5x&_(3w4|Tu@#_}q^8Q;Yi!FR>L2S?9o{T`xW0`+5osV?wr$@~3^9=&d1SDw_R_NKt`%v!;wwN{&^Wo+S@)z7;1 zwkdtCOY6@pTv#_Vs^?)};8>TIHmrHkv#R0PlomH>POybNYYjG2e;e}mTj^oX8u2q~ z8C%%2pweU1_}G-vO$%3a%MF7M8H&=YmYp$aOHU=!N7*u7kd^??ppKEkQOyw z>QOYUo_G`*=j&3dX|3vpg$H}`S3h4jqu3M3)~yncEj-X;*DdUtoej_5C!~akmnI?C z!7Zk+Lh7+WTL4?%pv{QweoG5XI71W0yW<%Rgk}jlXyB_&nm`=yGeWmbD!GM#)ezEs zoQ_9CFK8>!oD*^~u0F@XZeYXvga-JjNo0ud9xLwEa1>6oDM^%AFY%y2AQ@KD$>=QY zFe#?h#YQ$w06V&9R8)ux(~Av#Ba2dN>7qej;l26}eO1-NhZhael~RG2-^ciR zo!-aE{oy*gqa%E;<2Mg4Hp=S%ldq*1zusT3zfr69sz5mofkR-xuZIlf(vq}-PuIq90cZvta@Z$@*J2=;&v}O{u4())vY7{(XA> zTHdX~XgKWyPnA4=qLTMJ))XhptkxUw_u)gCVY4hXDK4f$8XOJE6O3OxC*CBD>QDb4 z6xd5RMT8M+v0rY@_khe+e1u(maP35e( zcg5qpXmDZf0Dj3>X|V5eSH*!nuU$aE*Ri^<*56pUR_nAID|Y4#H?CchTQwJVyCa{k z&c>=8CI`d(`yWaogrUxKDz*WQ#RpKE;!JzB0`D|m#v&n>r+DF;F*+wEnh ziRlAP(>2f0HBZp>>-9QdPW&df+M{H0!(OUheN*b;^!?X=+>dqt6QgqGspvvQMF_`bJUI{a-O0hpFKQ5s zZ=%t4Raf6LC+X5&nRVW9!cQrwxGq|E^E*VM$n@JC2u zH<}DIJEeBv%sK&xs@Gcye>SZfA6g39i(X?jZpnCN;wkR>=(1HXO0ED;i6xdS@L8##R zD?>7ToD7G-sI)S)yK+N;lSiPUX0=|!UyT^1B#F+oyt>%WoKH~alEX@3s_b)nVu2HD3AjFSh6HJU$y-~3Pts-J8c-7N}Uu$XX}1`);#@* z6L!<0I|c7UeMvvYbDkK+3`Y^H-I&y8!=2<6er*B0KqCJ}!w8qQMsEkjtK|M0o){wQ{`n`$4!Mrgi+1666QY@6^qvm;QcYpu1d3LsJ zl_f~ctcWN&e8(w$TS6*JH0sr-@|BNGdMvQ&{J^RPSYYZ%XebgW6D(i0H$zEgiOiVs z@R00|)b==vgucg2v8;xS$1P71i&vX{0G=ZZ1r=?e;XV1jQqFNsDF|FKBN4n z*Z!^&Tz7ys0xb{*4-cNTe(=76uNW9Hd1A?V-}S=5NM)zhXwDI200Moza@ zQ^|FCD+$BcY5~t4`mm|kF_zKH{77|_Jzj4-elxQ=qj#n{3ma;#>PZGSQ{8$rt2*wU zRP~|tXlnJ;)P-oh&<{ExXEWm=6jy)GfluKlgZHORK)^HFH}!JP+XU+x2{NG!HVTI= z>VcM)UFuMXDB!b$9MRRm|(1vFhH3v}0lI#oJ%1YLUwc7?9Er~W)-%1irsE| zFz&gJg3)L&hnhH1|K@3(_4S!(TSw6)N?be6ts3cADLYizd7-X?-cUdP5an`8TTGLk z(G}BEOH)kWNjpsO*2$HuSV{eRB9bIglzvMdGS$aGETf=}9vH*lblc)LT*--tpv}Nu zKdyhIioan-6=@PYaZ9$cFJy&JDN4Az|V@;kCqP+%zF;9UF+i(ds)AJCW zECza66{#p=GC}682Wx@_;jHpo^|$IdUH!5q`C#AG10#b&^F_J5!7BI!4f)0M4<<1V z#u!z~G}U|;w9i9I9gR&T(Fe075ORF{x9H*=Pc=`(AFw9!F1oVn_rbgIeh{}ojK=Y! z>NHZ;gJoL6;cwNS#3Gk3O@`R##Tkbrc+3y>(CauhozeW^cBrxfcI~{*KQ75MdN9V0)yGL+Z-_dpvlF$d>0Qk#j;2|pwwXvMJT=zKd*c0At_j;F6 zpPNw!?}q4m0iYy{PTvTw#|5-+M)OCe`9XxDR9|%iojWqq*3oh6`SIz?*7Ig_Kd-Up z32nxfbi_)tNVQM9NZq82mhYg9K(ZCw+gOvx?fV2F@ zzE(fleMuS~Yq0tf`ggmZi0)|g@N9bnXQY-i#ADTGA}*CTu#OoR%!w_5eiWvfFYV?r z*CM$%kI5FbI)m9x?;?-sE@9SBSy%kZ2i=V1`93)sL0jlHd5_L-?bW&nxmHE@kYf&l z`ozZ#ywL|ZIjEUpcvM?<>`TZT4MltkdxG-0&Mci|HI*`W=(3wSZk_HQA07TGkv*tG zD*Byer=iboZTIYk<(6}1!gDafbe1P+@u{M0dP>V#`qYs??Bz&Wx)p8xAxp1YoZ4V6 z%qbBrWwkQr*yfiaQ=|Jxf}}e{+s;}-D&c0>>!EA#Dd{tI0q5ZiKm4td423O@p@$Y5 zt`Rjcx`i3GRLQy9wIn+E`>W=wWLF7ihqCQRBjOwJ8`@-qiCS%kT^nkc z>LAgDN|H-ThwPZDrJv5*XHIKgVv%{K^<_UT$#}e=hYf3;6d6-*g;C$bYXwzd13351|R&y^^bMOEimni^F0tdQ; zBN0QXC%2W~l_hbpf_=>+m9`*0#CWj$5W=SyINe{b#oZL?NewoaMO`2jA-mVYrOAx$M5RFBF%PFUvVM+FDN{#hzbf5>T#Z*G^D?W8L6>sDr~D&{`~&r(#7`aRKBB#MJPy#X$NeN%SP@)btGtEihW z0E&Dyeh`1ZTo&!U4FU%roDRX%Bmc)d@$2vJZCcigvxJQIr9;ZxAl5*=cZ%Logsryg z{%R$zhJS@s$+c|91iRj`Rac#;)r+F*$*{!GaR6w*v$J2%kcrT0eirPQ`f_xPqRxj>Eo1wrAKADkvoIAySD)(vf z%Z?&l_s4F6-^A8!i%8|{L0Gl&_}*9k{=sQkeXo`=t?{zjR?DOrw`y@E%F%ixIwHLp zMo0jOSp+E{IE?l-Ue8fDHj3A2{17eGzOKMAFKLhOyKkpcNEz7K=^_3joZ0>@`l=b%G|}6lX->Vd zzJae zuWG7^%>LkjdZuES1AvRvYYdO>=-Y2MzWt*Az3cuT{{D#qK+VwqHr5-Pvi~2|>s#yO z|FiyO|9?*XuU`9104U4jC&mjhUq{9-(i3mpSx=m&nat`OZN*)o)Inm$-2yoMo(`IQ+~4xEqWkR+zQ4`sHGR2T)^ zB~8MgvIS&fqqa+CkJps3xmq^28$eOUj0oTBvgNr3_G^+zjq`AEo*YKK z1=eukp=nd)j#bmilnbQoB_<;C6-b_uPS7*HBI`-YUUag1{zFx%Q>qnnMz;70R;W*^ zV+`fQEC4$=7^5x-4K}ZUG(uy&V6=im4F>UKBpK@P9kz-^F_T+((F>r5uo_Ss1`xcC zFD1`bK|kt*-7x3?$VmN}UU)9lHYveAnTGK9WTKzQ z9I6sdN9*P8FBngrmdazaB%4|w5R^67vsMouSmj26-=nfrqms;F*{Nk76B**We*jiL zr9iM()PJ@DhEPZHH|r|EA(b}0!-6X|He2uX%@*+?puYH0zr)Z(P;cyCq@Us42|u?O zK~CHWd!xg~4Xj;0Ct2!)B#9>GnCuRb8jyZM&a~0E$cA127&H0;%0ox&dw9gofyKC6 zEz+tr#V`#vV(K^<<J;%LqN<^Nq~ z2LJQvAIGh|)8_6u{B8cScL>~m3w@AYAQzL%;F-l ze3^f&do0p$TX;eOscim%RzBv4iAN0J_;+p5*Sk~DQP7Xx1{M*WmvlOc#U=0mxc={x zsQ+f@|JECiHfw49U*n7a$7k06-Cg|$>$TRXt$n+tR@K><&RR!R^+Py{G1S!+{r&Ht zf8kdrZ~N7tGb!tO2kdi5I0(}6kU|8@ajX~lnCKR54sHTE8Bby)z}J|{xQAzBYQmq4 zBP@!9dD6pJF=I?g4^=wk8+qfi17|0~{IsZ!gEQFyvZbKI4d7)_C&ZgCIBs!z@Lu4* z#p})qA4JiBhybCEFzKxce%Qp(0m1veKHb- z7VXZ&316ZscVlfW5WlY18kkU78xK_ApCy|x5)M&cZ0l1vGx-k1lvI0HK&BCXCb1c? z(IE%Agp)y+j4y87W@;;t5b2E>K~uYjP|Tnrw~zk8BhFSXN17i;IAQDQr9T z3ai?l0I&X8ZKZF#@2M{OkU+oYcDRndcVT2=;$Oi$beY1IFjWzSV9gbvZM<*P9ED2cLakpHZWvnkgSvV{q47w?w+27 zMY19%Cy-bYGu_o&b#+yBEe0V*TXNa}lt@fR6PYEs;QSCVMLD+7Jn6I~Hi!iuZba-y zl3#qDq0EHI1WGClAoc8@s_ ze$1M|DAm#mK;?e`uf)0^z5Id+{RU+0_F=OH<%-Y!po=1NexA(6D*y{{a@v7!HB&vmpF z{XAxC*u_l+aN#GO9DK`5FQ!D+6)whNbw_<8bU5$1w--8UHmB#aw<|3Es^kh1UgCV~s+S2rDlMX6r4QP>uXlU9 z)7^{R!ES%|YBQk=M43)K{x}-q!QVEXrb=qb$yi zm57K5#FZ^HXGgyjYi!sVjSOi7hRj4lLK@boGJc%g)M-H>(AW0D7<7?8J`yxxMzbNs$@0W^T&p-r)|^af>&29%xK!!SVL zVwR1am=UFc-<@1bax%GwInOOUbHq|#_YEm%sn%yN`k2!x<5b^zy>Gql%dh`z?liIX z;ljbh6rWG7*^24*5a%M-^h=P-I9^uB^>;!pW8wZxJcX%0~_p!t-b;YPSiW|x$6dP;0?gOOFt%#z&f&r9@`W{c!fyWhQXhiSe2 z*&R+(Si1!=&1LMJcmvUDbLV=@{Wm6HZEfknClk{oa?o6Hkpu}~jq{a^p(F%Hm<1hBwV;ooyv!>V z)q&43P@_oB9J`-kZy@M*sY#&7<{>6{89snvZ0gwF*t<JT9Zbxye>rA|C*ZAhv|IkjaB5fC%jajb7xhe z`*#>ZL7+qCXcABgGV{)wVerj0-4)?{8JAZP7u9sjsa^4UY@H8n4VP*bXkd7oTq>pQ z1(mdw`F2G_q!LbRStK2asc96MWTsIGCwn?z85J@TDi?&Qj3`8Cx=}12-v+TGau(ji zPtxe2zL#ot7lZvsS+k{Swr98X2kEJQfqJ)Sg^@SIg)X@ zy_8 zj_9IxU+%4vQ)0D$np3WHI*N7f)KjI)$63c`_`8IempQpS)5o*U zuV)RV=tp01b3zLuo{d_p=O5v3P5cOWe*S>ojS=vo&Zi;zyYVaQ=G+!Hfbhyb3xnUY zPkv?%2Ll*1a4ygvs8~cBuP`{wK483j2Czq#ceBrN#IY)6kMeGWbz#9_$5t75Jxmr? zV&sgcfej~4I{zgvt;Bw08jSlAoG&u7U2--bNr?IVeLauLUcvxpU@doxcahOZeoq+B zB)-iU0V4yM(y7RzdD5efXBkc8k8NCZ<5`CB17a5~B3{&_|1*OBcb zUwfA=YgWC*u9em9v~llYZNgs9S)SXjjskj@1v16rh+Dw3q-ZQ(g~Ci!uvI*9>|8Q< z!GR2^`>Nf2_41(mbNyJKFlb5FMf;k4QPo#-AnABFKuD(K`cT~?VF$0T(W#ESk64Wn zX6bgf1@wU*5Kdb@q~_ zFd*uF)NwIUMxM_S!%_->@QwJr=3S?~9eDO{(R*0^3RisWa;&1ig_Bx}So=jjYSt6e zW$X5RkGgK%#Y3}i`YBO-(J|{p?lP4eTbgMJ#nQdy9EQG8(lsh4y`s<${viuoE1g z6jRzdoQPl!j#-R{=?1iZ`5D=>=D>o}WHzX9>U+s~aD~%{33LWG1SpITXX+^vxa-f#M)sf?)MDRJ1!_BFBMbc)!(ah4dK+ChSC~NXhQo3!0mCG1PIZ~naIizDsc+qubxJzOq zr#K;d>a;}I?>R<)ttt!BEd^&Gz_chh7!3*lk46^e(D)oiyNY+{w>>j#6;Kjy)CH+w zYNLtwg2-8D;g2Cn8B}^Q0%da^j;laqD$>J3qFd{~<)iIP+~F|RPHNe|Q?k;p!ZmS# zQM39gVW~=3YVt`EH8~intjy=AM%pl?{cz$vjso~VsZL3LhYuy50s#0dXXLfat9f^N zu@1nBI3pYNQ?zJ+{n%mBXkTGSCEa$d3;=r3%KhjB+UL`YUFqE1w?N7nJO8oogUwK! zg1%plM2&4XT0b{hI@(IgJCQ4YDwoSUd9ATCHyP3*XN-^{As9pY3*9fJRY|(DUfkj$>X>KTQWAM8=P9+lba}h7;FP>%_fb+FYIi;KvBZR| z_yWXopIsv0sKDeuQr8vwM92#Tt2(k{zsM4o~m&(D_Sp=%6`cnCEMv6y`g5* zELZkoInT9zT9ycL)7$~nDO8vNua;Yrj3bnnoy8~8vC=}fGAFt-_4GXZ}7Y-ih9W1hU~$k-xlB@9WqpPZRV9ntoh$c z#sZrhzj>NTa(GV%rQQi~DrT_c#!xfLcQ)Fi2-Sko!NN{$UUmsGQvMs0`k3@4;}30n zk~5LkjPTyK0}IpJ!GRrHp^_4q@_UP;!GEEI6O(R;VM>u&=4Xe6U-Irsns9~<&=7tr zKBrFXtAmstC_ZQD*J zwr$(ClZkEHwr$&Z&-WKj-_%{NRd>~^pW1tyv?3d`vo7rq)XqmZBZi(5;g53PD~R!uG70c?A|G(DY=YhG#X=VJ!L>L3^F8kvIvh0u7UA5 z2dugHKnKBB01ds&Lsc_lTLi#^?~&m*Zoj&I$X5Ef$ccj2r)Ml``tr3f3n>`caakPHNJwqsFBf45`@mChZ5?M#@; z)4BP2M9s=Xl%_o<))YI29LD&e6s)&c(LU!FN9cG^lTG?BR1nS)HJRg$i1r4L)dwd$ z!cmZPvhBaLSFQ%u-#>epy${e}E=vC;ub9ktOTESyyzZ&a%4RbTyqPVbfSFm?ytIwQ z%JXXLqNr)pBr`FZvWVbf9FJ0rJ~QoJD~xXEQq|`-8#!d|OeJY6A&J&E^oXdGZSQJz zm2G1t4eXa_Shb}RqL(YQuI-IB(W~+>xut^1xNW*Uevob5^;Sv@J$2Y)CZ;x;ts3EL z&$T%(d*&Z8k+i%E8^TAX#LK*wZ`l>%p{RT2nznlEoRu5q@vFO> z5tkzMFrcKDF&Ab^7^psrgFQ$B`}yx79KLJLY9qD>o@Q0 zuH9>5nuB4q7P7o7>K%2G9Tn^|F8+4McNEVu85k`nNP1Z8&mV-Hh8fq5{Z$D#T?8%; z(CleaBmOWs!RR=dR0@2e3G{nLPu?z#S4}tQ8!nnjGJOakF-Ba&-@_mfIp}ER+KPf( z+isqqiGnMJ9I71T?QljOj@#v@n**TJrNYca;KP&efvYk6Td4my2o^%JVAVm>fPXe0*D zx&7QxH;W8AciWV0x#;AA=@kjGeO7m^(+sgY^IWK$H>{MJUzpf zJeU>oF!9Ju8;Mo_*gGXRMbKVWL=P5En7|o*iWUcglFiIgla-No!oDYJcS$%6e6-HC zSBe}u#R=gT90L9TidhUzxrDyLs>)OggIK>}>J{NzHWJ%cESZ4H?_B1fN^V@c7B2Hz zfh_=q)&683aaby2do zWb!Zz!*ZtXwfIcyNRx79W0q<1y<9NO#WS{qmGQX+ORhGyyGp|s0B}Bs{RHfQ=yLT| zYGpuM(|-`@h2O5mj+R9LU-_>>O^@4^sd57w! zGTFXU>)}6fn|~eHmW}9IRgNwvs5GUjD5Z}zHHo)I(aQ*C>x5)-3F|@Oa7>ieV0BAd zHPCv5Q)O4Oq-cezSp=PFDNJ^st$R*ngmK3jNSxU^xWBBhaQ#4S5)!QeSLdv%YVEuK*#rK^(F~|QvK{#7)7nTo1DKUI z>J5GMO`guaUVi&WC?u0DIDZXdH@O2*-9_|KY*VJmq*_7!kZQG?wt@dG*K%{yx zwc9rLpTG|wrXzJgAW_vwcB|B-($jrokoNUQH{|BtxKZ?X zI|cZn+-zOWaX zH{?#qk?=j_MTV?F2Q0Dr4=n0EvkMWpZ(B~GnB^>0A`T6(PnP02!KHK#jZPK$M=#rn zRV(Ri7(2d_L)f^<RM`T@c_|HlFx* zn|uXJ#RI~?BQagBw1lj4-c?IxZ-@-0kLCP0a&TSPGILM}FyMmJoOh77t3@LNR9MH; zB5EaB8+Qs#zsQR1r}(psksf0A8m`&rij?cE&lD$UGm{SGXCQj*tjwx1sn}N2i1s47193z}*C{ixooE~7TN~?# z7&N6$feSh$?6VItbG?Y>oUi5>_h$%<>4E~)%5!*pV+EOz-*^Q*gPkJ{?cyYY*TGKc zG)dGmmr8#L_O^#!3V$YqqctDw5m_8S{R8#{G?NcY+SDp?)5+60dY=;6?~cM?t{(2^K8?}f16ESqz6u%2Z_s_FTh&;wH|>N1ULnNN`sOox~O7r1S_oZl!JH# zJ#Cz+*hyp>#Ihv{gaCrC*l9^naEB`iOli3D;wWoxctu=t)8^;S9!Aj#HxQQHYi$9t zt$K4^g=|4AO^W*A!zxJ>S>gLBHRI zoHvGfidop_Qji`;IF1&2L)#JNzr^`ZT4>D8upseJg<1T`7HY>3i#a0Fw)5$ zKq82`aTEKeezSuw5GkO|7jx4>nB-Q*SyvpgR3BBK2^itFk*Iu2W3x+8Ql?P;$sU?R znTsF!5kFiur3_bvoHOoUa3LE<_EJ;RYPDi($no#7IufSxvwweXqBAJAiXDDlZ{q=g zPykH#0g7A%6jtuw=_9=-u>m!gkj)*c40M?FwzZ0U%~44n^gzo;4wD1?&IiaPGitz&#v^Yz7o631RV`rY}Cypr@JH+0@7G?soPw#)J&zN zjn&ism6pDy%j@$_6}X7=Uox&Ws1maT=O;Q6jPJa^*_^sJn1z+-(OATFTYF**OHjr~ zM!oC)p*e4FeX3MdLqkn3*k6}Jt#E|8NGa@L88(N;4AS109sG6{vWT@YyJ=x}jS1cM zy@~@kKnB0vjovjz3L94MYK=JHCYmSSV?6;TapW&iDyqaW>Ydl{Cb#r19;1Q}LlA&N z)B3w~QwL3Mo<+IDqXm8ocs84rb62mzl;o#r*X>-=0^h8QYkqo1?i!|m3yncb-rmnG zGRfmE$8JtOeH^yS+tPqNow7D9osdurr@M_z{k{Y4g&}rLe0l>XEc(cWmHWFB{U2T} zwY0#^YpV-sC%kK;_2csg8G+BYmes~HU)P}^E*tpwD5WY^qJwEr>WQFjUNxMeKm$Db^)@9}vKx}&{=uA&~5lE2kmRLAu<6y&Xy z4|Q;hyf;iAK zn8`tO4^O;vqbywG;x+%4M3$2kfehb9E_Q+UaC3TjYThju>M+kcS=FImr_KB1gVaT3 z1X7K+2K=~c0(b9>8pFP)5p|jeE5=LAchUCPR36FT3f_V0^ynYm3<^~RraVJnbm#F4 zDVZDVeLIR5%nm?{|z@UC8(nqD~VkW69 z*heS7G2USk3BvNiz@f}v-_pT$Pl-1(sdw8WB%h#&RUa^Z#>|YXQ16CCb40wE)9GXN z{OU0WT!Esw^T{G8FzjgOV_ddr!t&fY|6aFX%ciZk_+u@R zF;oio&AH`|O)_m?jvM(vHFcKxh2MEQc2R=Uo8W}RHxK&`iFa`c`Y`tXm1)Hp?`Y7d z`<65$w)L?|o?lq2`_%kfwVRaDnM(~5ueEDMkQ z6ULyxlxe6eWE=P#_g4^}QWvsAm(%%SHdB7%%hgxHhU6IOV&y7v%Hx98<5Ptv(=Qt2<-DQ+~iBARx&##0WbNWHN*tCJcj zHI7D9h?&ZHIrkMR*>U~ci5_be|4?)?zROF(g(cFghG+Ja`o^NDR&CqC)~!=nAD5$Bgpjly;NhXRVYvz;R8V zj$ON$no^S~XHWiIJa#gLH@2rI#U;eZOUX`^Js(!PZgTN)+4k}C@Yv4aJ2+48q6gp8 z&ibC6sbDK?J+YYWA3xVWJY6qz4dxFnN~S4uU^vA!4~Rf#tO^%9dwiannBmaHNrbxz z8`4m2zwKN-)aY2}ed`M7bA}o);|pIkqc;+*JeCCqE;-M~+nL1c_AmCM@l~|WCw=RJ zu}%A@tF!+@L;$~aj;YhgL5xN+NXE)4G~f@XYUX=_V%XfWdM6!r{y+r=9h}NQB`lJS zQ2T^YycrPf(yz1qo3js>R<2Vn28B4{uyt4J~$!%{9E!eW47vmvuv2T@D`Bbw+D<%#h+Bt`=!_MDYGdc z2XTBjy-T2K^^enlF*Vz`&E8mct33&dz!>GheC2H#VK{$HdD3o{vg?H(OZA38nj6Vk zUY_SA|7r$c1>G5fmd>)|C%weJSUlFqKdcr@y%~kt^Q^B0x(RJuo|&Y2xnKGgzJfVldh>OqEX?MVuD^3fE4J~Z^A;YbcfPRt z^FryDMDdlvhG=u*^(*!&U~C-$IrozRL>C3n-K`wC_HUg`%}xE86Bz%cn29py$}aG) ztx>R8O73bFI00w2eAXEBF%Q#{9|V)wST??!M?~{r>9xG!v5}qIbUYgra^rRg$K+rN zXHa`q@O$$y;H4Kh)#3Jggw68-a9Z)r+DB=!ntkI7Hy|rQFj00$Yw;?9N2v~R79NgX zR{Xv?x<_$9kGOuGFJU3~c)dBFZFc;T?*bXp`Zv`yha%tYSY-Dbgf$S;V7!@+QWN z9=`OSg`~8gx(ZAYb~nn;GEyfyMx9qOCs=%PG~6!L#IH<(D>i+l==$u-N2s-qyH>Un zh$XR{vK-jFJ{qdf8-xinF_oAQ`uowuK(&Kc9${vvvpP50CbT;Og#%1H_!xRFpx-9U z0H6s1+MgXS(Hw0zJO+T|( zRI_XWfc+r|18TfugLnq{F(_?pLRkW=v`)69{@xMb1CFjP!TLnMFd%1js4LK)L88p_ zA5JMD09f1|I^iUV(26F8nqN}R*RCr0GpaG-g4^OUmGbUZhVWY;TUBDYgz0H6=-b&t z0p34J7%mKgukEdNoJv-Z0b2vO9s|N2z5*E|vl48v(hBX(Ma&ghDcEDBD=A7BhAj4v zPOQHfYlyQ$7?+fC0)-ikj|VO(So&E5^PPep|5mszd#l~C?JxV1YUYT0q9d(j<`iG< z*hG5U6u(e2A(rhI_Tf(KZ-`R+qUrWEp!Y+e|K8nI{=VD_eO&!SRvMiAq*Mav%0ng9 zVt&z5n)G!6MUf`TBuy#w=&1~t7-L^eYz@xK9I>-k$?C;^_eVAq?BsS)VAO0>qa`mk zFbOC_AG6CzUQaG12<1)Rbw}B0rwNB#VJgkVPn>~$SfHw%D<8dJ>B-y2Qo@MTMt!|S zoVm-m>~V0)&VVTWO&(Dch+CI!3QJu%t9E*pbh1;{ZHpdl4{H&{iS#t#`yuXeT1l!k zkqX-y)oFKiCKje2>+>}8XH(OxkG6S0Yt4d*#n&4yq|e3sUI}32eQ)K)$rTOobyXwa z^X8@T<_c9$`0H+JUX~L9XzPgr{1E1UsCQg&-(z7vX+ghgQ9IYP_lZG_{h5l>-P3IX zJMIMjNC79iV7;3~t3>s^S9c8VGO`VG6ts-96{NCzNA#5U6wG*faXwMdIdS45pdy^9 zq~^I-wQ4Zo|<0xroGO!l5o9met5!}e}KfRzh4f?z}~{o+)Pa^)^l%? z6RbQ8r!b7Kwp*4`MLxB%IB82hWvj#`9!dFczOq877e*gs3IP9Z8rM%ms$t`#n+ZED z?U5;v{A=u2;mr+%z5u9sG`s%Z+pesB1k|1bd>8x+gscU)4^w%xVd8_pcD}oEU{AEl z1~iJl&Bi@#phC|+YG{&r@7I?K67j+9K9L&l!8%_4HncpG<%q}tyx;xjGPjGt=Zn7Y zwo==_HMVMP|GQQo?3czF(_Gk+%YjGy<>DPeFx$%wfcyudr`?63_uQ?9@IL3s@~?Xr zaeB7q{NH?bZ|T(ji~pdOXDrm0ec!54nriP0DixhrNHQf2!ylygudKRkM47Jkk8jDk z>}^&};OTWP%21x!6g7(~BV}8E=uwTWADGhfj7Zen5Z^6ae^M0nY(84GogE zqh(RgYO3jvcbAV$SAQW4c8i*bA&W3Y0_nHsyhumn<_g@@Zg$6V@QlP~XfH2=B%*l6VBDPguL+zaG>YbJBz zeFh(0TwsrFo~k@TF>~Zywp0(T@~zh~2H575J;>#Wqx7A4VylTguuke`J70hF7g|_p zR$*s(9u@VeZybH2FQ(8bL)Cp0D!(yd1EEGU|Er%qN~gpU)=+U(_Fo}X@t$sNj7Fiw zqRlfy37XKZI72N?`>4M!9I&r2>&5ZgEFED1tkqcb4>>qwr7tPOcz7*@r#6SLG>5k| zhi6+NwC$F`VYm;0$SX6n>jpBMkrsa&R4EDgthC$X8yo(f&1irB_BD0_d@~fzkpEzL z+7V@f{0?BGwKw+Qm+j%@38v$n$*=iO-#rR%}X{aVyve zyXf==0sW3xpLX3s49$XEYOc#pdoG zBMP-7hz|M(KJ!`FiLy?D3xjBkHIvwZnE&Y*2jNy~pwC>EeK7!)DEpY~9`0$l+ zLmtc;efUntq=Xe{k|1qu6E-etz)J|;4nn;7VC~L*9qsJRfn#f-ks^>f^@0Hz9$QRn zjVxo8<+*_*F3@i;qJn{fca;;D zIsmq@kFS|mpv{oziSdTIQPkx!b{Y+67Ml1*SY%NZ(QFRiTn&=8gPM`c>cIt%HsjoK zHA<(t6I3a0Nh=jxkijT|e7+r4{T@kMNKnb7K)r_M9zq9gRurZa9o!{1zwBa)Ak@aN;o!AwiVo^dO2K;f-d)FX}c+qX%QG%gpGbqrsY1Y;n?P{9Xrkx*QDS) z7|Q8j4iHw*P?RkzW*)G}7@aP%tdX3j5v$?_EMDj+p$~ZQl!GUpoS{4dW8TDQ3#5sd zU^X`pD3bEFdjQBhRYsQVkL#JL;(m1Tr;rFP(99((&`kt{-l6{9zQNI00N7GLULgY2 zg8D7VJ=nv?VN2-9HILt1k1%O}C^>9Tvkx;if19B(5r)$NkmqQ#p++1TF$`I?bGdGk zZ_37sXqV$Q=Q-_~$Zqbs2ocKJKNsXK2$DYrK*@I`CwOm#ry2U*q6b1||9N^0eUb?c zi43%ksBD?(gUZkt8|s;X7GA_HgM&wW3MXWN4?bE+U;96fe)40peH0IURwsrg2xpOj z_m7QLY|LrpN|+jBmfKD|aS|r*7QAfzi5edj9B*L5UFaf*AvUo#rB|lPU~E`9pAuqV z!G@4`U3f?F4<_LBravWh#ntZ?v1C3$C@&=FZL2)N)+E4ixkAue|IvT`2@z;qM#-{} zQQo1|!#-5-IQ^PUg^T8OIxunL(?{_^^FR+kS$+w2&Ip8}{`c0>LK$lIa;UWE7|CB|OTZ4q~)%hZ&7KvTHBI+u7y zDfUj!&plUs7kIgBS|4!|LFs>7+s$`PS(;?Vjk)n4Zjx-!I~!rZ%Q-%5-2Cj_#GapB z8(xBgyD2-PN0I6>as}#l)|DFS$%1|3)TZfk2#c9UHfY!Kvi4IhH#U-IXP5+<>hb=$ znuXFjlys@yT*F@W6nR08)gdrt-*e-d&51>3uG+M6G}kbpzKR!d&QHOX>T;7O^iQeC z;s;oeLFljrp_Ys~B`8-)ct^PVV)&bxcs|^;!FQ+YrGt;YQs*f_|449kf*Iha{o1Rw zB0u|=0z3=8`s~>S86n8|zUr>~9O@>-JcSm2Tp9yAnB?-H$jU^mhUmXs#Rih$6e&B>7?E;4jhZ0d-y`t;eg)8*ot;aZCKnR|bTaD8#sQcr25 zL1z7>&XAp2{@PXs=mvZ91)(hf6g8rh0a`9kCw~D#{0}p|cX3S&$MoI-%3l#hzUWKkKEm4EePjljdk#szjl?Z?2>E!`Dsbf%IYn>G0(SLX z;pa?$5w-LyE)009?F$=lcKT=}Q5$;?l4f6&iU|X+*HF&jA?@xG5TZ#Ce$qyV)xk=_#t!i-2%`|!!eyr#mL0KnDivp^_4Kkvo1F`KE$kCd9*!KNnY4wH> z+f}{#Fn3E3Iq~<& zP&i5-1;%@6|9$s10#?bHVK%6rqXlM$+*rZ@*x|Zts$$-_-w#3}TuirdfhAF(K!h|_ z^{3vSN~%v~uSp4rN;{}{Hrc4VnrOIN^4Hf(VmDL5G|Hcp&l&yrH))~%{U@@uht`bc zpnJ1Apw;tq^xMV-SPXkmsQvQv@mROHFh4_Qq@nci$xa@5`nWc1+TpUo=Xkv!(&tPK ziUtUx;LC7z1$`nriBy6(NmEANK-1=94Tz(T$Cw^m)hezuW$M;3NRyf9us(=>-{`@K zDxHgu5SRhK>;A{~DLQ;R?AR~?n>?TNK?|C!o8A3ydIhLm_X%AZ|JavytL;>;ND^=B zo&Y=&6f6clor_K?|#JkQ5FxSg?qJk8>fQX?JA274K+_LHu2uOGa;;c zX?F%)`g;r(WY|=3AZvIWCL{;C`oL-H(>TMC+jC1sntvZ5m7<48GUR)XQeGBB1uN2XPjYYk%u_;Zr0iUPQ zC~wenEbIx*8uLh3FC<8|NQ|#hcszouR&cnFeFW7@=S4EWt>2L7}$SnCA*N@DGZ`Z^dQz?VFSFtqh_QGICN2}hM0)>?5dKs|hn*}= zqdXigvTv1|)h(krB4eBqJQ3@7eHJTC67!ENyz)a;Clu!>`gHLwR`z>2@)!O473g8} zeB>x4@(jxQucv;jH9Ty4V~WbkP~sLf5o|P`ULCw&9Gi2Cfkb!-A_XT}WBzfR{udON zc*EH{pbNQ5a>;1bUBSk3GxzC1dRzIH-J&2i-ccvFm7`KCwzQp=!k%3Qjn)QgbO8hz zN{08AXV>^2QXv-8ir|^Tv8zgeY=_B$ldc?y&L5sd-VX>%J*wlYjhv0%AYg?FY+k}J z3=ZtB>v?*@Jp}!xYJEClG>(uNM!vP-D4CYt;3a0qdGEu&m!3elgjeJ(0I_^8Fb`-_ z2(JsCvk2ES6-#JYy<~YY3(x6fuw1|fo?plYr+6Jq|I2k#;rA6}#+hY@a%cOkLCWXr zQy(3mY`Ae3^wB?6yQ8(@y-(WKJo~Y4Mg7em;c-KAt;O63K3dSBU(gp}?1Bh6RBk~V)ej)(@5~z## z{Mq7E^4dK4Vo)Jxb{;}*9;cgEeY7npB#O_W!6F>a-~hJ7dU$JRD334$Y*Dwjus#tn zWI#%L>v860)Q4db$IlG3@RS5T)VeF$v|o~E>%2el5i7`f$i?k}4ruWSLo{Mn2jUe=e~Ub~ufL;~ z0p%N9md#s-=gr_gPg*@ImiXQLwQa*2SF0PAT}p?rejYKP0bDA)1!~0cIJsL0Ro~e7 zDaSkH3ZMK9jmg;~(cyx$(gma)Ne;wsjA4B9H$e&VDt1m<4PNbyKGE8UTRb|4XgRJ$ ze&Erbe`9C)%KU--dn==zT9m(f3H95qBELf_CjQu`!#fBy4hW@Vo{%-7&(6@C!y1Jy z3{mwQlipa)XP3C+nDIM!6XKFGGmnZ4gUycXuSc?#GI#4PHRzi>f|zhMJ$Tp?qhG~> zWFn@2uF5(;`zZ_$l_vZVLQ*%PE~%}9~&J|xLK=X zK2eakmtq|$5P&!@%D<-Tf%6LF$a#UUr`jYJmpbp||F}4Qo{@sUb4#|@nQ4OUt^7-j z*=#}RFIzxpI(#7d;FcS0Pp|cTjX~WjW@#3n(m9Z=cX9!@qfq7IBxtKzF0%)}6F@f7 zyY;le0psRW>$`e=tWuSq+aiwzT6;WDDL%>2M+tAbDb)XTX5I5 z<8N>F#DpmAzz*$i$D5C}QnE3%ZNZaMmvo}qH6`D*plWFJ2u5|n7d1#iFyy1G%-}aNu}JJXFWPdo(I;ms5V2gcoUK?irFZXy zN<*pM-B{M-Vw6CX7{M$$A-G$`C^C45!vZVYZ~u-myF`S0p9hOcx>|bl)T=4-HPB+h zN_-GraYlt2_KlrOlu^)@eR9`kDk#Qvgz6k&p;Wu$v%z~Y?^LdIiK>^fbkb3N+?2Uz z3WgyJO7NZnTn4{Fr4lnSW9*a8JnCLJP7}G!sgi5e-{1f36UvX{dfv>3DO}{XP?sNbd z3YOATq~DQ{d?awsMaEEJY+OLkcjX3u!ISDgC{!-}XXNiA zNruHeJ#f;ZCAg3y{xWf|4c;Q0oFI>3w3&tHFnUHJ#ntg&6f7LF8EqCXO*XIbQ%&uh zHyhKYP`d+qm`i1`6J%q_-NF(Q5pGc5z>#^%>d|4(&fj&6`G?vZpxk^W?p8O>)-Bj+ z1sqK|IN8xZQML|(@C_Ig~S3W z67CPdOqStUjm)I6#eoYMX_YZSqQd1&6w$?n!v?Y@bbtc{iZ&Uspljm_Ndx(Ygd!OI zl*v)ZI>2-n)+9O^-deVwR`1f_CoejUD$6-`>f|pdMz-)g2`7wqf3W&KW@QM3gBYst&ot)q<4>BENRav=ouf zUjmZfU#XY@<4Hm(zYvF&XsD-0t0c{jmKuB_?YS|W4l-{K5;!2)PTU#44BKT6FH0I1 zk1#ynIdD&FZ?pP&l+@%9_QJ^i94TE9H?1`t$7cxi``k!cC zr2C>@P7F!lA8vd2q#`2BwvMW8a1zlHYVDa($ zD%JhOR#xg;^&fgn+jV@6l+}C5kiiYJeAqE~nB_(gMc+6rbp+4V`@ zw9+n>$FfcJ}_w9I?_i=aVmcVply|Iy5}tXr-2@khbb74XFPCSU?085piv z^AGn!Olda(A@ra(K6L1+a9aq!E`lBA-B~7r8$yHo*{(YNFjEgq%DJ89lFaKq*k9{X zPcB&bPGD}S&<5j>L(b?Fj>i4i&n{b$z?)%B&LaBbSDT{3YBRejfJ+tCP1(;(6)N>c(f zA!rpQSv7Pze(`}S%Wo#0MDQRg})vUK-)<-4#!_4_KyXNhFa6T@h*j$Q} z37Gxisba{VJEY-oN;N~B_>X)Z`>vNi=5dnNgh zR`J&9ljJ1&@j;Q^rl}!&81eILb^LilJN-=fZJ_wyP`r(KhXV})C@&YNynFhzmE+r1^|$Gn zYkHNmWH>&Ly3`MC5&bGB@oKfD4v*+mB>ZCfmNxs6kqy8b9*g%<`%A%q>SRP7JuVoz z{YML|^hmln{Y0`!tk+G2MW$`kL3&~N4xmqe4&c8NgUiew0?(l z6DR>S-vap3zu9C!wIMyJ^=-(nw@T|cK&x<~7%knHLq%W4ZP_ajN)KaCF}K7}>%`=0 zpTo1ql{z{fWGwh?g$eFraH!~N8B+1eDm6?A+O z*yIGr=?I9|n5$dDv1xl$nprIJ+_d=-6*Jtyh6&vv8TD&79L_krZJRu<+ zRu_wqQawna{aBv}f+lTeTUb!0i1W^63bxzROvft(JkqseX^O*!ZV@J$&y@@mnxCXa z9yAc6JcWAOapdTJuFs&xS^pg`;~q?C0K(EJYlsJ8m^o+x259~Ev*S3V*|N&P15k?^ z^;q3U)?(mR&B#BUiMRRPCu===zYnijGk0fpX&=HzliP6&HM>o%L8U8h8U zTc#Vq(r(4cS@x<&cR1ecp<0@6ek|-8@I#1Ka@O@h+#QQ49AT`ZUK)+CMcay{U8PJ5=&Cjc1I0v_SzsabF zz649HxaeSefh9r9+$DMT1dA-<<6ARKDA|isIs)SZ z-$a?*e8YRumz|F@Vc*seUe=T3DQ#dfb|c(N3c|AOo6aP=Y40hLug z9MHVpSj*xK@eGoY^TVM6lT#{NZ)ggAdI>w|qdiFm~rk zkoqIQL-ypQ=1yYE%CynR23C#2i+3;_-cn&?%VGOCZf<`2Inr+n zx8ux*q*UKBQ^#F-bEzjT7D3yl-|lK2`!Ba1uY(f(Dww4s@17EBli9e-quW3Hk zH;QYOS^P+GyWYnqaZ=`}`U@L38SkLlds;r&n=mQJCd!yF!$N6QvJ>G|Sg^(R0x9!o zO_c7fV@Id0A}7ExF58fETaVDBt@uJVM;k0sz|pLv%@qhL@)KATP#|>;Df`h#!7r$E zCS{8AOFX5?FI6u!;8s3|_>qmeSZCJHOijX>kWj;;jqd6+i> z&kTLJuR_AL;y20JdsX^kKN=uyeIcUQwLmocmuF_;+u3cSfO{f>BlTu;Y32<|DN1an zO@U@lBbi}sZm1njy67;01zQb*Q2w*SWF5Zy6D!%UB{1G(AJ+pYR+^|4J2fhGM>6(QhR#(!n(upth~$?iH9E0%Mp(AN+SoNcsvfBN5QL(SOT zWc&n-I*T(S>#p?uf2$tbT-4Bg2aBGM&HRuR!U79FOrGwJrf^&#Qp40av3tN9_zT`y zdr^T2EaAgbxXd=A4wxEb_=13iB0ihkF!fRaM;oi0{Nmpc-E77irpDrHe>e8RfO1uiDjL8Zy1i%3DQ= zkBDxwCuBmmW#qnM#RU$(zwzxS4R9tuBw-1dQPLIKpCM_&nX;v-G`6D^ zR6*ktmf^|zNyD+^qMF|*s>uPfw#NZj;#6PBz9NoqE7!&ALwbS*M$-do#3cRO*Dsb1 z7hVU-vy7D(9T`d*{9n)>h|AUen|XWJyZ=tHC@$$g;fukr(nHwXILMcr|1oWRCL}>X zw9;N55n-*9Aqq*m`A>>@X5dJ84gZQ8i5)!kDbNsv3Ux9%%WE?s(LtcnM?=^#>WoD4 oxOg$Hr<;!jtpBVKwSHFfSIu2E{r}~=haW(IZjRXkbPo>nKYX@>O#lD@ literal 0 HcmV?d00001 diff --git a/Changes b/Changes index fc712e9..8fbd2d2 100644 --- a/Changes +++ b/Changes @@ -1,6 +1,26 @@ I only began this file with ncpfs-0.12. If you're interested in older versions, you can find them on linux01.gwdg.de:/pub/ncpfs/old. +ncpfs-0.16 -> ncpfs-0.17 + +- Changed the name of fsinfo to nwfsinfo, to avoid a name clash with + the X windows utility. Thanks to Henning Brockfeld + for this hint. (still + waiting for your scripts.. :-)) +- made nwmsg available. This enables you to receive NetWare user + broadcast messages. Please note that you need at least kernel 1.3.68 + for this feature. +- pserver now prints debugging output via syslog(). +- Included ipxdump, a nice little utility, that has helped some + people. + +- And now the big one: you can re-export ncpfs-mounted directories + with nfsd! You have to mount single volumes by specifying -V volume + to ncpmount, and call nfsd and mountd with the option --re-export. + See the manual page of ncpmount for more information. Please note + that I will send Linus the required patch on 1. March 1996, so you + will have to use kernel 1.2.13 or wait at least for 1.3.70. + ncpfs-0.15 -> ncpfs-0.16 - Included ipx-1.0, made available by Greg Page , diff --git a/Makefile b/Makefile index 22ef024..f6c8a25 100644 --- a/Makefile +++ b/Makefile @@ -2,10 +2,11 @@ # Makefile for the linux ncp-filesystem routines. # -VERSION = 0.16 +VERSION = 0.17 TOPDIR = $(shell pwd) BINDIR = /usr/local/bin +SBINDIR = /sbin INTERM_BINDIR = $(TOPDIR)/bin SUBDIRS = util ipx-1.0 man @@ -15,14 +16,14 @@ SUBDIRS = util ipx-1.0 man # the following lines. You have to recompile your kernel # and say 'y' when 'make config' asks you for IPX and ncpfs. # -#SUBDIRS += kernel-1.2/src -#INCLUDES = -I$(TOPDIR)/kernel-1.2 +SUBDIRS += kernel-1.2/src +INCLUDES = -I$(TOPDIR)/kernel-1.2 # If you are using kerneld to autoload ncp support, # uncomment this (kerneld is in linux since about 1.3.57): -# KERNELD = -DHAVE_KERNELD +#KERNELD = -DHAVE_KERNELD -export INCLUDES BINDIR INTERM_BINDIR KERNELD VERSION +export INCLUDES BINDIR INTERM_BINDIR SBINDIR KERNELD VERSION all: for i in $(SUBDIRS); do make -C $$i; done diff --git a/README b/README index c8fc29a..8fd7d96 100644 --- a/README +++ b/README @@ -78,6 +78,8 @@ Ales Dyrak has written lwared, which was the initial start for ncpfs. Alan Cox has found some bugs I would probably never have found. +Look at the file Changes for others. + LIMITATIONS (compare these with smbfs :-) @@ -87,27 +89,7 @@ limitation is the lack of uid, gid and permission information per file. You have to assign those values once for a complete mounted directory. -The second limitation is just as annoying as the first: You cannot -re-export a ncp-mounted directory by nfs. It is not possible because -the NFS protocol defines access to files through unique file handles, -which can be mapped to the device and inode numbers in unix NFS -servers. NCP does not have unique numbers per file, you only have the -path name. I implemented a caching scheme for inode numbers, which -gives unique inode numbers for every open file in the system. This is -just sufficient for local use of the files, because you can tell when -an inode number can be discarded. With NFS the situation is -different. You can never know when the client will access the file-id -you offered, so you would have to cache the inode numbers -indefinitely long. I think this should not be done in kernel mode, as -it would require an unlimited amount of RAM. - -Those who looked at the kernel code a bit closer will have found out -that the last section is a little white lie. As I found out after the -first version of ncpfs, NetWare does indeed offer something like inode -numbers, although are only unique per volume. So one way to make ncpfs -re-exportable by nfs is to allocate a superblock per volume and show -the inode numbers to the user. I was just too lazy to do this -yet. Maybe once we will force Novell to make NetWare NFS -affordable... ;-) +You will not be able to access servers that require packet +signatures. This seems to be one of Novell's bigger secrets :-(. Have fun with ncpfs! diff --git a/ipxdump/Makefile b/ipxdump/Makefile new file mode 100644 index 0000000..b67fab8 --- /dev/null +++ b/ipxdump/Makefile @@ -0,0 +1,35 @@ +EXEC= ipxdump ipxparse + +CFLAGS= -Wall -O2 +OBJECTS= ipxutil.o + +all: $(EXEC) + +ipxdump: ipxdump.o $(OBJECTS) + $(CC) -o $@ ipxdump.o $(CFLAGS) $(OBJECTS) + +ipxparse: ipxparse.o $(OBJECTS) + $(CC) -o $@ ipxparse.o $(CFLAGS) $(OBJECTS) + +clean: + rm -f *.o $(EXEC) *~ + + +modules: ncpfs.o + +SRCPATH=$(shell pwd) +SRCDIR=$(shell basename $(SRCPATH)) +DISTFILE=$(SRCDIR).tgz + +mrproper: clean + rm -f $(DISTFILE) + +dist: tgz + make all + +tgz: mrproper + (cd ..; \ + tar cvf - $(SRCDIR) | \ + gzip -9 > $(DISTFILE); \ + mv $(DISTFILE) $(SRCDIR)) + diff --git a/ipxdump/README b/ipxdump/README new file mode 100644 index 0000000..f4607e2 --- /dev/null +++ b/ipxdump/README @@ -0,0 +1,45 @@ +This is a VERY stupid packet sniffer for IPX ethernet packets. + + ============================================= + ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! + ! ! ! S E C U R I T Y W A R N I N G ! ! ! + ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! + ============================================= + +If you are using unencrypted passwords, and use this tool to send a +dump to somebody else or store it on a computer, you might very well +store passwords there. So, be VERY careful! This is exactly the kind +of tools Novell designed the encrypted passwords for (or against). + + + +I hacked it together to be able to help people with problems with +ncpfs. The socket handling was taken from Statnet-2.0. + +You can use it to watch commercial NetWare clients when they talk to +servers. I divided the program into 2 parts, ipxdump and ipxparse. + +ipxdump simply pumps all the IPX frames it receives to stdout. + +If you use ipxdump to watch a workstation, you can use the simple +filter function ipxdump provides. You can call ipxdump with the node +address of the workstation you want to watch. This way only the +packets this workstation sends and receives are monitored. As an +example, I call ipxdump as + + ./ipxdump 00001B038B11 + +to look at my 286/10MHz test 'workstation'. ipxdump still generates +huge amounts of data, so you should be very careful to start it just +before you perform the operation (such as file creation for OS/2 +clients with NW4.1 as a server, or a 'dir' on a directory with long +and short file names, or an encrypted password change ;-)) and stop it +directly after that. And, please gzip -9 and uuencode it before you +send it to anybody. + +ipxparse will eventually take apart the dump that ipxdump +generates. They can as well be used in a pipe. Currently ipxparse does +not do anything sensible, but that will definitely change. + +Volker Lendecke + \ No newline at end of file diff --git a/ipxdump/ipxdump.c b/ipxdump/ipxdump.c new file mode 100644 index 0000000..6d6ff20 --- /dev/null +++ b/ipxdump/ipxdump.c @@ -0,0 +1,247 @@ +/* ipxdump.c */ + +/* Copyright 1996 Volker Lendecke, Goettingen, Germany + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ipxutil.h" + +struct ipx_address +{ + unsigned long net; + unsigned char node[IPX_NODE_LEN]; + unsigned short sock; +}; + +struct ipx_packet +{ + unsigned short ipx_checksum; +#define IPX_NO_CHECKSUM 0xFFFF + unsigned short ipx_pktsize; + unsigned char ipx_tctrl; + unsigned char ipx_type; +#define IPX_TYPE_UNKNOWN 0x00 +#define IPX_TYPE_RIP 0x01 /* may also be 0 */ +#define IPX_TYPE_SAP 0x04 /* may also be 0 */ +#define IPX_TYPE_SPX 0x05 /* Not yet implemented */ +#define IPX_TYPE_NCP 0x11 /* $lots for docs on this (SPIT) */ +#define IPX_TYPE_PPROP 0x14 /* complicated flood fill brdcast [Not supported] */ + struct ipx_address ipx_dest __attribute__ ((packed)); + struct ipx_address ipx_source __attribute__ ((packed)); +}; + + +void handle_frame (unsigned char *buf, int length, struct sockaddr *saddr); +void handle_ipx (unsigned char *buf); + +static int filter = 0; +static IPXNode filter_node; + +static int exit_request = 0; +static void +int_handler() +{ + exit_request = 1; +} + +void +main (int argc, char *argv[]) +{ + int sd; + struct ifreq ifr, oldifr; + char *device = "eth0"; + struct sockaddr saddr; + int sizeaddr; + unsigned char buf[4096]; + int length; + + signal(SIGINT, int_handler); + + if (argc > 1) + { + if (ipx_sscanf_node(argv[1], filter_node) != 0) + { + fprintf(stderr, "usage: %s [node]\n", argv[0]); + exit(1); + } + filter = 1; + } + + if ((sd = socket (AF_INET, SOCK_PACKET, htons (ETH_P_ALL))) < 0) + { + perror ("Can't get socket"); + fprintf(stderr, "You must run %s as root\n", argv[0]); + exit (1); + } + + /* SET PROMISC */ + + strcpy (oldifr.ifr_name, device); + if (ioctl (sd, SIOCGIFFLAGS, &oldifr) < 0) + { + close (sd); + perror ("Can't get flags"); + exit (2); + } + + /* This should be rewritten to cooperate with other net tools */ + ifr = oldifr; + ifr.ifr_flags |= IFF_PROMISC; + + if (ioctl (sd, SIOCSIFFLAGS, &ifr) < 0) + { + close (sd); + perror ("Can't set flags"); + exit (3); + } + + while ( exit_request == 0 ) + { + /* This is the main data-gathering loop; keep it small + and fast */ + sizeaddr = sizeof(saddr); + length = recvfrom (sd, buf, sizeof(buf), 0, + &saddr, &sizeaddr); + if (length < 0 ) continue; + handle_frame (buf, length, &saddr); + } + + /* This should be rewritten to cooperate with other net tools */ + if (ioctl (sd, SIOCSIFFLAGS, &oldifr) < 0) + { + close (sd); + perror ("Can't set flags"); + exit (4); + } + + close (sd); + exit (0); +} + +void +handle_ipx (unsigned char *buf) +{ + int i; + struct ipx_packet *h = (struct ipx_packet *)buf; + struct sockaddr_ipx s_addr; + struct sockaddr_ipx d_addr; + int length = ntohs(h->ipx_pktsize); + + + memset(&s_addr, 0, sizeof(s_addr)); + memset(&d_addr, 0, sizeof(d_addr)); + + memcpy(s_addr.sipx_node, h->ipx_source.node, sizeof(s_addr.sipx_node)); + s_addr.sipx_port = h->ipx_source.sock; + s_addr.sipx_network = h->ipx_source.net; + + memcpy(d_addr.sipx_node, h->ipx_dest.node, sizeof(d_addr.sipx_node)); + d_addr.sipx_port = h->ipx_dest.sock; + d_addr.sipx_network = h->ipx_dest.net; + + if (filter != 0) + { + if ( (memcmp(filter_node, s_addr.sipx_node, + sizeof(filter_node)) != 0) + && (memcmp(filter_node, d_addr.sipx_node, + sizeof(filter_node)) != 0)) + { + /* Not for us */ + return; + } + } + + for (i = 0; i < length; i++) + { + printf("%2.2X", buf[i]); + } + printf("\n"); + if (!isatty(STDOUT_FILENO)) + { + fflush(stdout); + } +} + +void +handle_other (unsigned char *buf, int length, struct sockaddr *saddr) +{ + struct ethhdr *eth = (struct ethhdr *)buf; + unsigned char *p = &(buf[sizeof(struct ethhdr)]); + + if (ntohs(eth->h_proto) < 1536) + { + /* This is a magic hack to spot IPX packets. Older + * Novell breaks the protocol design and runs IPX over + * 802.3 without an 802.2 LLC layer. We look for FFFF + * which isnt a used 802.2 SSAP/DSAP. This won't work + * for fault tolerant netware but does for the rest. + */ + + if (*(unsigned short *)p == 0xffff) + { + printf("802.3 "); + handle_ipx(p); + return; + } + + if ( (*(unsigned short *)p == htons(0xe0e0)) + && (p[2] == 0x03)) + { + printf("802.2 "); + handle_ipx(p+3); + return; + } + + if (memcmp(p, "\252\252\003\000\000\000\201\067", 8) == 0) + { + printf("snap "); + handle_ipx(p+8); + return; + } + } +} + +void +handle_frame (unsigned char *buf, int length, struct sockaddr *saddr) +{ + /* Ethernet packet type ID field */ + unsigned short packet_type = ((struct ethhdr *)buf)->h_proto; + switch( packet_type ) + { + case __constant_ntohs(ETH_P_IPX): + printf("EtherII "); + handle_ipx(&(buf[sizeof(struct ethhdr)])); + break; + default: + handle_other(buf, length, saddr); + break; + } +} diff --git a/ipxdump/ipxparse.c b/ipxdump/ipxparse.c new file mode 100644 index 0000000..c6023ae --- /dev/null +++ b/ipxdump/ipxparse.c @@ -0,0 +1,345 @@ +/* ipxparse.c */ + +/* Copyright 1996 Volker Lendecke, Goettingen, Germany + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ipxutil.h" + +struct ipx_address +{ + unsigned long net; + unsigned char node[IPX_NODE_LEN]; + unsigned short sock; +}; + +struct ipx_packet +{ + unsigned short ipx_checksum; +#define IPX_NO_CHECKSUM 0xFFFF + unsigned short ipx_pktsize; + unsigned char ipx_tctrl; + unsigned char ipx_type; +#define IPX_TYPE_UNKNOWN 0x00 +#define IPX_TYPE_RIP 0x01 /* may also be 0 */ +#define IPX_TYPE_SAP 0x04 /* may also be 0 */ +#define IPX_TYPE_SPX 0x05 /* Not yet implemented */ +#define IPX_TYPE_NCP 0x11 /* $lots for docs on this (SPIT) */ +#define IPX_TYPE_PPROP 0x14 /* complicated flood fill brdcast [Not supported] */ + struct ipx_address ipx_dest __attribute__ ((packed)); + struct ipx_address ipx_source __attribute__ ((packed)); +}; + +#define NCP_ALLOC_SLOT_REQUEST (0x1111) +#define NCP_REQUEST (0x2222) +#define NCP_DEALLOC_SLOT_REQUEST (0x5555) + +struct ncp_request_header { + __u16 type __attribute__ ((packed)); + __u8 sequence __attribute__ ((packed)); + __u8 conn_low __attribute__ ((packed)); + __u8 task __attribute__ ((packed)); + __u8 conn_high __attribute__ ((packed)); + __u8 function __attribute__ ((packed)); + __u8 data[0] __attribute__ ((packed)); +}; + +#define NCP_REPLY (0x3333) +#define NCP_POSITIVE_ACK (0x9999) + +struct ncp_reply_header { + __u16 type __attribute__ ((packed)); + __u8 sequence __attribute__ ((packed)); + __u8 conn_low __attribute__ ((packed)); + __u8 task __attribute__ ((packed)); + __u8 conn_high __attribute__ ((packed)); + __u8 completion_code __attribute__ ((packed)); + __u8 connection_state __attribute__ ((packed)); + __u8 data[0] __attribute__ ((packed)); +}; + +void handle_ipx (unsigned char *buf, int length, char *frame, int no); +void handle_ncp (struct sockaddr_ipx *source, + struct sockaddr_ipx *target, + unsigned char *buf, int length, int no); + +void +handle_ipx (unsigned char *buf, int length, char *frame, int no) +{ + struct ipx_packet *h = (struct ipx_packet *)buf; + struct sockaddr_ipx s_addr; + struct sockaddr_ipx d_addr; + + memset(&s_addr, 0, sizeof(s_addr)); + memset(&d_addr, 0, sizeof(d_addr)); + + memcpy(s_addr.sipx_node, h->ipx_source.node, sizeof(s_addr.sipx_node)); + s_addr.sipx_port = h->ipx_source.sock; + s_addr.sipx_network = h->ipx_source.net; + + memcpy(d_addr.sipx_node, h->ipx_dest.node, sizeof(d_addr.sipx_node)); + d_addr.sipx_port = h->ipx_dest.sock; + d_addr.sipx_network = h->ipx_dest.net; + + printf("%6.6d %s from ", no, frame); + + ipx_print_saddr(&s_addr); + printf(" to "); + ipx_print_saddr(&d_addr); + printf("\n"); + + if ( (ntohs(s_addr.sipx_port) == 0x451) + || (ntohs(d_addr.sipx_port) == 0x451)) + { + handle_ncp(&s_addr, &d_addr, buf + sizeof(struct ipx_packet), + length - sizeof(struct ipx_packet), no); + } +} + +void handle_ncp (struct sockaddr_ipx *source, + struct sockaddr_ipx *target, + unsigned char *buf, int length, int no) +{ + struct ncp_request_header *rq = (struct ncp_request_header *)buf; + struct ncp_reply_header *rs = (struct ncp_reply_header *)buf; + unsigned char *data = NULL; + int data_length = 0; + int i; + + if (ntohs(rq->type) == NCP_REQUEST) + { + /* Request */ + printf("NCP request: conn: %-5d, seq: %-3d, task: %-3d, ", + rq->conn_low + 256 * rq->conn_high, + rq->sequence, rq->task); + + data = buf + sizeof(struct ncp_request_header); + data_length = length - sizeof(struct ncp_request_header); + + switch(rq->function) + { + case 87: + printf("fn: %-3d, subfn: %-3d\n", + rq->function, data[0]); + switch(data[0]) + { + case 1: + { + unsigned char *p = &(data[0]); + printf("Open Create File or Subdirectory\n"); + printf("Name Space: %d\n", p[1]); + printf("Open Create Mode: %x\n", p[2]); + printf("Search Attributes: %x\n", + *(__u16 *)&(p[3])); + printf("Return Information Mask: %x\n", + (unsigned int)(*(__u32 *)&(p[5]))); + printf("Desired Access Rights: %x\n", + *(__u16 *)&(p[9])); + break; + } + case 2: + printf("Initialize Search\n"); + break; + case 3: + printf("Search for File or Subdirectory\n"); + break; + case 6: + printf("Obtain File Or Subdirectory " + "Information\n"); + break; + case 8: + printf("Delete a File Or Subdirectory\n"); + break; + } + data += 1; + data_length -= 1; + break; + case 22: + printf("fn: %-3d, subfn: %-3d\n", + rq->function, data[2]); + switch(data[2]) + { + case 21: + printf("Get Volume Info with handle\n"); + break; + } + data += 3; + data_length -= 3; + break; + case 23: + printf("fn: %-3d, subfn: %-3d\n", rq->function, + data[2]); + data += 3; + data_length -= 3; + break; + case 24: + printf("fn: %-3d\n", rq->function); + printf("End of Job\n"); + break; + case 34: + printf("fn: %-3d, subfn: %-3d\n", rq->function, + data[2]); + data += 3; + data_length -= 3; + break; + case 62: + printf("fn: %-3d\n", rq->function); + printf("File Search Initialize\n"); + break; + case 63: + printf("fn: %-3d\n", rq->function); + printf("File Search Continue\n"); + break; + case 64: + printf("fn: %-3d\n", rq->function); + printf("Search for a file\n"); + break; + case 66: + printf("fn: %-3d\n", rq->function); + printf("Close File\n"); + break; + case 73: + printf("fn: %-3d\n", rq->function); + printf("Write to File\n"); + break; + case 75: + printf("fn: %-3d\n", rq->function); + printf("Set File Time Date Stamp\n"); + break; + default: + printf("fn: %-3d\n", rq->function); + } + } + + if (ntohs(rs->type) == NCP_REPLY) + { + printf("NCP respons: conn: %-5d, seq: %-3d, task: %-3d, ", + rs->conn_low + 256 * rs->conn_high, + rs->sequence, rs->task); + printf("compl: %-3d, conn_st: %-3d\n", + rs->completion_code, rs->connection_state); + + data = buf + sizeof(struct ncp_reply_header); + data_length = length - sizeof(struct ncp_reply_header); + } + + if (data == NULL) + { + data = buf; + data_length = length; + } + + i = 0; + while (i < data_length) + { + int j; + for (j = i; j < i+16; j++) + { + if (j >= data_length) + { + printf(" "); + } + else + { + printf("%-2.2X", data[j]); + } + } + printf(" "); + for (j = i; j < i+16; j++) + { + if (j >= data_length) + { + break; + } + if (isprint(data[j])) + { + printf("%c", data[j]); + } + else + { + printf("."); + } + } + printf("\n"); + i += 16; + } + printf("\n"); +} + + +void +main (int argc, char *argv[]) +{ + unsigned char buf[16384]; + unsigned char packet[8192]; + unsigned char *b; + int len; + int i = 1; + + while (fgets(buf, sizeof(buf), stdin) != NULL) + { + if (strlen(buf) == sizeof(buf)-1) + { + fprintf(stderr, "line too long\n"); + exit(1); + } + + b = strchr(buf, ' '); + if (b == NULL) + { + fprintf(stderr, "illegal line format\n"); + exit(1); + } + + *b = '\0'; + b += 1; + len = 0; + + while ((b[0] != '\0') && (b[1] != '\0')) + { + unsigned int value; + if (sscanf(b, "%2x", &value) != 1) + { + fprintf(stderr, "illegal packet\n"); + exit(1); + } + packet[len] = value; + b += 2; + len += 1; + } + handle_ipx(packet, len, buf, i); + i += 1; + } + + exit (0); +} + diff --git a/ipxdump/ipxutil.c b/ipxdump/ipxutil.c new file mode 100644 index 0000000..7af985d --- /dev/null +++ b/ipxdump/ipxutil.c @@ -0,0 +1,129 @@ +/* + IPX support library - general functions + + Copyright (C) 1994, 1995 Ales Dryak + Copyright (C) 1996, Volker Lendecke + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#include +#include +#include "ipxutil.h" + +void +ipx_fprint_node(FILE *file, IPXNode node) +{ + fprintf(file,"%02X%02X%02X%02X%02X%02X", + (unsigned char)node[0], + (unsigned char)node[1], + (unsigned char)node[2], + (unsigned char)node[3], + (unsigned char)node[4], + (unsigned char)node[5] + ); +} + +void +ipx_fprint_network(FILE *file, IPXNet net) +{ + fprintf(file,"%08lX",net); +} + +void +ipx_fprint_port(FILE *file, IPXPort port) +{ + fprintf(file,"%04X",port); +} + +void +ipx_fprint_saddr(FILE *file, struct sockaddr_ipx *sipx) +{ + ipx_fprint_network(file,ntohl(sipx->sipx_network)); + fprintf(file,":"); + ipx_fprint_node(file,sipx->sipx_node); + fprintf(file,":"); + ipx_fprint_port(file,ntohs(sipx->sipx_port)); +} + +void +ipx_print_node(IPXNode node) +{ + ipx_fprint_node(stdout,node); +} + +void +ipx_print_network(IPXNet net) +{ + ipx_fprint_network(stdout,net); +} + +void +ipx_print_port(IPXPort port) +{ + ipx_fprint_port(stdout,port); +} + +void +ipx_print_saddr(struct sockaddr_ipx *sipx) +{ + ipx_fprint_saddr(stdout,sipx); +} + +void +ipx_assign_node(IPXNode dest, IPXNode src) +{ + memcpy(dest,src,sizeof(IPXNode)); +} + +int +ipx_node_equal(IPXNode n1, IPXNode n2) +{ + return memcmp(n1,n2,sizeof(n1))==0; +} + +int +ipx_sscanf_node(char *buf, IPXNode node) +{ + int i; + int n[6]; + + if ((i = sscanf(buf, "%2x%2x%2x%2x%2x%2x", + &(n[0]), &(n[1]), &(n[2]), + &(n[3]), &(n[4]), &(n[5]))) != 6) + { + return -1; + } + + for (i=0; i<6; i++) + { + node[i] = n[i]; + } + return 0; +} + +int +ipx_sscanf_net(char *buf, IPXNet *target) +{ + if (sscanf(buf, "%8lX", target) == 1) + { + return 0; + } + return -1; +} + +IPXNode ipx_this_node={0,0,0,0,0,0}; +IPXNode ipx_broadcast_node={0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}; +char ipx_err_string[IPX_MAX_ERROR+1]="no error detected"; diff --git a/ipxdump/ipxutil.h b/ipxdump/ipxutil.h new file mode 100644 index 0000000..b5588ff --- /dev/null +++ b/ipxdump/ipxutil.h @@ -0,0 +1,65 @@ +/* + + IPX support library + + Copyright (C) 1994, 1995 Ales Dryak + Copyright (C) 1996, Volker Lendecke + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#ifndef __IPXUTIL_H__ + +#define __IPXUTIL_H__ + +#include +#include + +#define IPX_MAX_ERROR (255) +#define IPX_THIS_NET (0) +#define IPX_THIS_NODE (ipx_this_node) +#define IPX_BROADCAST (ipx_broadcast_node) +#define IPX_AUTO_PORT (0) +#define IPX_USER_PTYPE (0) +#define IPX_IS_INTERNAL (1) + +typedef unsigned char IPXNode[6]; +typedef unsigned long int IPXNet; +typedef unsigned short int IPXPort; +typedef unsigned short int hop_t; +typedef unsigned short int tick_t; + +void ipx_print_node(IPXNode node); +void ipx_print_network(IPXNet net); +void ipx_print_port(IPXPort port); +void ipx_print_saddr(struct sockaddr_ipx* sipx); + +void ipx_fprint_node(FILE* file,IPXNode node); +void ipx_fprint_network(FILE* file,IPXNet net); +void ipx_fprint_port(FILE* file,IPXPort port); +void ipx_fprint_saddr(FILE* file,struct sockaddr_ipx* sipx); + +int ipx_sscanf_node(char *buf, IPXNode node); +int ipx_sscanf_net(char *buf, IPXNet *target); + +void ipx_assign_node(IPXNode dest,IPXNode src); +int ipx_node_equal(IPXNode n1,IPXNode n2); + +extern IPXNode ipx_this_node; +extern IPXNode ipx_broadcast_node; + +extern char ipx_err_string[IPX_MAX_ERROR+1]; + +#endif diff --git a/kernel-1.2/linux/ncp.h b/kernel-1.2/linux/ncp.h index 895c5ae..ab3f653 100644 --- a/kernel-1.2/linux/ncp.h +++ b/kernel-1.2/linux/ncp.h @@ -1,5 +1,5 @@ /* - * ncp_fs.h + * ncp.h * * Copyright (C) 1995 by Volker Lendecke * diff --git a/kernel-1.2/linux/ncp_fs.h b/kernel-1.2/linux/ncp_fs.h index fb2cd44..5dab842 100644 --- a/kernel-1.2/linux/ncp_fs.h +++ b/kernel-1.2/linux/ncp_fs.h @@ -42,6 +42,7 @@ struct ncp_fs_info { #define NCP_IOC_NCPREQUEST _IOR('n', 1, unsigned char *) #define NCP_IOC_GETMOUNTUID _IOR('u', 1, uid_t) +#define NCP_IOC_CONN_LOGGED_IN _IO('l', 1) #define NCP_GET_FS_INFO_VERSION (1) #define NCP_IOC_GET_FS_INFO _IOWR('i', 1, unsigned char *) @@ -126,9 +127,12 @@ extern struct inode_operations ncp_dir_inode_operations; void ncp_free_inode_info(struct ncp_inode_info *i); void ncp_free_all_inodes(struct ncp_server *server); void ncp_init_root(struct ncp_server *server); +int ncp_conn_logged_in(struct ncp_server *server); int ncp_stat_root(struct ncp_server *server); void ncp_init_dir_cache(void); -void ncp_invalid_dir_cache(unsigned long ino); +void ncp_invalid_dir_cache(struct inode *ino); +struct ncp_inode_info *ncp_find_inode(struct inode *inode); +ino_t ncp_info_ino(struct ncp_server *server, struct ncp_inode_info *info); void ncp_invalidate_all_inodes(struct ncp_server *server); void ncp_free_dir_cache(void); int ncp_date_dos2unix(__u16 time, __u16 date); diff --git a/kernel-1.2/linux/ncp_fs_i.h b/kernel-1.2/linux/ncp_fs_i.h index a72fb07..c0ab3da 100644 --- a/kernel-1.2/linux/ncp_fs_i.h +++ b/kernel-1.2/linux/ncp_fs_i.h @@ -28,6 +28,7 @@ struct ncp_inode_info { number of references in memory */ struct ncp_inode_info *dir; struct ncp_inode_info *next, *prev; + struct inode *inode; struct nw_file_info finfo; }; diff --git a/kernel-1.2/linux/ncp_fs_sb.h b/kernel-1.2/linux/ncp_fs_sb.h index fc5e955..26e76ac 100644 --- a/kernel-1.2/linux/ncp_fs_sb.h +++ b/kernel-1.2/linux/ncp_fs_sb.h @@ -22,7 +22,6 @@ struct ncp_server { it completely. */ struct file *ncp_filp; /* File pointer to ncp socket */ - struct file *wdog_filp; /* File pointer to wdog socket */ void *data_ready; /* The wdog socket gets a new data_ready callback. We store the @@ -35,7 +34,8 @@ struct ncp_server { u8 completion; /* Status message from server */ u8 conn_status; /* Bit 4 = 1 ==> Server going down, no - requests allowed anymore */ + requests allowed anymore. + Bit 0 = 1 ==> Server is down. */ int buffer_size; /* Negotiated bufsize */ @@ -56,6 +56,18 @@ struct ncp_server { char root_path; /* '\0' */ }; +static inline int +ncp_conn_valid(struct ncp_server *server) +{ + return ((server->conn_status & 0x11) == 0); +} + +static inline void +ncp_invalidate_conn(struct ncp_server *server) +{ + server->conn_status |= 0x01; +} + #endif /* __KERNEL__ */ #endif diff --git a/kernel-1.2/linux/ncp_mount.h b/kernel-1.2/linux/ncp_mount.h index 83007be..cbc58aa 100644 --- a/kernel-1.2/linux/ncp_mount.h +++ b/kernel-1.2/linux/ncp_mount.h @@ -13,7 +13,7 @@ #include #include -#define NCP_MOUNT_VERSION 1 +#define NCP_MOUNT_VERSION 2 #define NCP_USERNAME_LEN (NCP_BINDERY_NAME_LEN) #define NCP_PASSWORD_LEN 20 @@ -26,14 +26,14 @@ struct ncp_mount_data { int version; unsigned int ncp_fd; /* The socket to the ncp port */ unsigned int wdog_fd; /* Watchdog packets come here */ - unsigned int message_fd; /* Not used yet, maybe for messages */ + unsigned int message_fd; /* Message notifications come here */ uid_t mounted_uid; /* Who may umount() this filesystem? */ struct sockaddr_ipx serv_addr; - unsigned char server_name[49]; + unsigned char server_name[NCP_BINDERY_NAME_LEN]; - unsigned char username[NCP_USERNAME_LEN+1]; - unsigned char password[NCP_PASSWORD_LEN+1]; + unsigned char mount_point[PATH_MAX+1]; + unsigned char mounted_vol[NCP_VOLNAME_LEN+1]; unsigned int time_out; /* How long should I wait after sending a NCP request? */ diff --git a/kernel-1.2/src/dir.c b/kernel-1.2/src/dir.c index 5747571..46ae470 100644 --- a/kernel-1.2/src/dir.c +++ b/kernel-1.2/src/dir.c @@ -47,7 +47,7 @@ static struct inode * ncp_iget(struct inode *dir, struct nw_file_info *finfo); static struct ncp_inode_info * -ncp_find_inode(struct inode *dir, const char *name); +ncp_find_dir_inode(struct inode *dir, const char *name); static int ncp_lookup(struct inode *dir, const char *__name, @@ -102,7 +102,7 @@ static struct file_operations ncp_dir_operations = { NULL, /* write - bad */ ncp_readdir, /* readdir */ NULL, /* select - default */ - ncp_ioctl, /* ioctl - default */ + ncp_ioctl, /* ioctl */ NULL, /* mmap */ NULL, /* no special open code */ NULL, /* no special release code */ @@ -129,6 +129,58 @@ struct inode_operations ncp_dir_inode_operations = { }; +/* Here we encapsulate the inode number handling that depends upon the + * mount mode: When we mount a complete server, the memory address of + * the npc_inode_info is used as an inode. When only a single volume + * is mounted, then the DosDirNum is used as the inode number. As this + * is unique for the complete volume, this should enable the NFS + * exportability of a ncpfs-mounted volume. + */ + +static inline int +ncp_single_volume(struct ncp_server *server) +{ + return (server->m.mounted_vol[0] != '\0'); +} + +inline ino_t +ncp_info_ino(struct ncp_server *server, struct ncp_inode_info *info) +{ + return ncp_single_volume(server) + ? info->finfo.i.DosDirNum : (ino_t)info; +} + +static inline int +ncp_is_server_root(struct inode *inode) +{ + struct ncp_server *s = NCP_SERVER(inode); + + return ( (!ncp_single_volume(s)) + && (inode->i_ino == ncp_info_ino(s, &(s->root)))); +} + +struct ncp_inode_info * +ncp_find_inode(struct inode *inode) +{ + struct ncp_server *server = NCP_SERVER(inode); + struct ncp_inode_info *root = &(server->root); + struct ncp_inode_info *this = root; + + ino_t ino = inode->i_ino; + + do + { + if (ino == ncp_info_ino(server, this)) + { + return this; + } + this = this->next; + } + while (this != root); + + return NULL; +} + static int ncp_dir_read(struct inode *inode, struct file *filp, char *buf, int count) { @@ -142,6 +194,7 @@ ncp_dir_read(struct inode *inode, struct file *filp, char *buf, int count) all inodes that are in memory. That's why it's enough to index the directory cache by the inode number. */ +static int c_dev = 0; static unsigned long c_ino = 0; static int c_size; static int c_seen_eof; @@ -156,7 +209,7 @@ ncp_readdir(struct inode *inode, struct file *filp, int index = 0; struct ncp_dirent *entry = NULL; struct ncp_server *server = NCP_SERVER(inode); - struct ncp_inode_info *dir = (struct ncp_inode_info *)(inode->i_ino); + struct ncp_inode_info *dir = NCP_INOP(inode); int filldir(struct dirent *dirent, const char *name, int len, @@ -170,9 +223,9 @@ ncp_readdir(struct inode *inode, struct file *filp, return 1; } - DDPRINTK("ncp_readdir: filp->f_pos = %d\n", (int)filp->f_pos); - DDPRINTK("ncp_readdir: inode->i_ino = %ld, c_ino = %ld\n", - inode->i_ino, c_ino); + DPRINTK("ncp_readdir: filp->f_pos = %d\n", (int)filp->f_pos); + DPRINTK("ncp_readdir: inode->i_ino = %ld, c_ino = %ld\n", + inode->i_ino, c_ino); if (!inode || !S_ISDIR(inode->i_mode)) { @@ -180,6 +233,11 @@ ncp_readdir(struct inode *inode, struct file *filp, return -EBADF; } + if (!ncp_conn_valid(server)) + { + return -EIO; + } + if (c_entry == NULL) { i = sizeof (struct ncp_dirent) * NCP_READDIR_CACHE_SIZE; @@ -193,8 +251,9 @@ ncp_readdir(struct inode *inode, struct file *filp, if (filp->f_pos == 0) { - ncp_invalid_dir_cache(inode->i_ino); - if (filldir(dirent,".",1, filp->f_pos, (int)dir) < 0) + ncp_invalid_dir_cache(inode); + if (filldir(dirent,".",1, filp->f_pos, + ncp_info_ino(server, dir)) < 0) { return 0; } @@ -204,7 +263,8 @@ ncp_readdir(struct inode *inode, struct file *filp, if (filp->f_pos == 1) { - if (filldir(dirent,"..",2, filp->f_pos, (int)(dir->dir)) < 0) + if (filldir(dirent,"..",2, filp->f_pos, + ncp_info_ino(server, dir->dir)) < 0) { return 0; } @@ -212,7 +272,7 @@ ncp_readdir(struct inode *inode, struct file *filp, return ROUND_UP(NAME_OFFSET(dirent)+i+1); } - if (inode->i_ino == c_ino) + if ((inode->i_dev == c_dev) && (inode->i_ino == c_ino)) { for (i = 0; i < c_size; i++) { @@ -234,7 +294,7 @@ ncp_readdir(struct inode *inode, struct file *filp, { DDPRINTK("ncp_readdir: Not found in cache.\n"); - if (inode->i_ino == (int)&(server->root)) + if (ncp_is_server_root(inode)) { result = ncp_read_volume_list(server, filp->f_pos, NCP_READDIR_CACHE_SIZE); @@ -251,6 +311,7 @@ ncp_readdir(struct inode *inode, struct file *filp, if (result < 0) { + c_dev = 0; c_ino = 0; return result; } @@ -258,6 +319,7 @@ ncp_readdir(struct inode *inode, struct file *filp, if (result > 0) { c_seen_eof = (result < NCP_READDIR_CACHE_SIZE); + c_dev = inode->i_dev; c_ino = inode->i_ino; c_size = result; entry = c_entry; @@ -282,24 +344,35 @@ ncp_readdir(struct inode *inode, struct file *filp, /* We found it. For getwd(), we have to return the correct inode in d_ino if the inode is currently in use. Otherwise the inode number does not - matter. (You can argue a lot about this..) */ + matter. (You can argue a lot about this..) */ - struct ncp_inode_info *ino_info; - ino_info = ncp_find_inode(inode, entry->i.entryName); + ino_t ino; - /* Some programs seem to be confused about a zero - inode number, so we set it to one. Thanks to - Gordon Chaffee for this one. */ - if (ino_info == NULL) + if (ncp_single_volume(server)) { - ino_info = (struct ncp_inode_info *) 1; - } + ino = (ino_t)(entry->i.DosDirNum); + } + else + { + struct ncp_inode_info *ino_info; + ino_info = ncp_find_dir_inode(inode, + entry->i.entryName); - DDPRINTK("ncp_readdir: entry->path= %s\n", entry->i.entryName); - DDPRINTK("ncp_readdir: entry->f_pos = %ld\n", entry->f_pos); + /* Some programs seem to be confused about a + * zero inode number, so we set it to one. + * Thanks to Gordon Chaffee for this one. */ + if (ino_info == NULL) + { + ino_info = (struct ncp_inode_info *) 1; + } + ino = (ino_t)(ino_info); + } + + DPRINTK("ncp_readdir: entry->path= %s\n", entry->i.entryName); + DPRINTK("ncp_readdir: entry->f_pos = %ld\n", entry->f_pos); if (filldir(dirent, entry->i.entryName, entry->i.nameLen, - entry->f_pos, (ino_t)ino_info) < 0) + entry->f_pos, ino) < 0) { return 0; } @@ -353,9 +426,9 @@ ncp_read_volume_list(struct ncp_server *server, int fpos, int cache_size) DPRINTK("ncp_read_volumes: found vol: %s\n", info.volume_name); - if (ncp_do_lookup(server, NULL, - info.volume_name, - &(entry->i)) != 0) + if (ncp_lookup_volume(server, + info.volume_name, + &(entry->i)) != 0) { printk("ncpfs: could not lookup vol " "%s\n", info.volume_name); @@ -438,15 +511,17 @@ ncp_do_readdir(struct ncp_server *server, struct inode *dir, int fpos, void ncp_init_dir_cache(void) { + c_dev = 0; c_ino = 0; c_entry = NULL; } void -ncp_invalid_dir_cache(unsigned long ino) +ncp_invalid_dir_cache(struct inode *inode) { - if (ino == c_ino) + if ((inode->i_dev == c_dev) && (inode->i_ino == c_ino)) { + c_dev = 0; c_ino = 0; c_seen_eof = 0; } @@ -517,7 +592,8 @@ ncp_iget(struct inode *dir, struct nw_file_info *finfo) root->next->prev = new_inode_info; root->next = new_inode_info; - if (!(inode = iget(dir->i_sb, (int)new_inode_info))) + if (!(inode = iget(dir->i_sb, ncp_info_ino(NCP_SERVER(dir), + new_inode_info)))) { printk("ncp_iget: iget failed!"); return NULL; @@ -567,6 +643,7 @@ ncp_init_root(struct ncp_server *server) root->finfo.opened = 0; i->attributes = aDIR; i->dataStreamSize = 1024; + i->DosDirNum = 0; i->volNumber = NCP_NUMBER_OF_VOLUMES+1; /* illegal volnum */ ncp_date_unix2dos(0, &(i->creationTime), &(i->creationDate)); ncp_date_unix2dos(0, &(i->modifyTime), &(i->modifyDate)); @@ -581,6 +658,25 @@ ncp_init_root(struct ncp_server *server) return; } +int +ncp_conn_logged_in(struct ncp_server *server) +{ + if (server->m.mounted_vol[0] == '\0') + { + return 0; + } + + str_upper(server->m.mounted_vol); + if (ncp_lookup_volume(server, server->m.mounted_vol, + &(server->root.finfo.i)) != 0) + { + return -ENOENT; + } + str_lower(server->root.finfo.i.entryName); + + return 0; +} + void ncp_free_all_inodes(struct ncp_server *server) { @@ -611,7 +707,7 @@ ncp_free_all_inodes(struct ncp_server *server) complete linear search through the inodes belonging to this filesystem. This has to be fixed. */ static struct ncp_inode_info * -ncp_find_inode(struct inode *dir, const char *name) +ncp_find_dir_inode(struct inode *dir, const char *name) { struct ncp_server *server = NCP_SERVER(dir); struct nw_info_struct *dir_info = NCP_ISTRUCT(dir); @@ -658,9 +754,14 @@ ncp_lookup(struct inode *dir, const char *__name, int len, return -ENOENT; } - DDPRINTK("ncp_lookup: %s, len %d\n", __name, len); - server = NCP_SERVER(dir); + if (!ncp_conn_valid(server)) + { + iput(dir); + return -EIO; + } + + DDPRINTK("ncp_lookup: %s, len %d\n", __name, len); /* Fast cheat for . */ if (len == 0 || (len == 1 && __name[0] == '.')) @@ -679,7 +780,7 @@ ncp_lookup(struct inode *dir, const char *__name, int len, parent->state = NCP_INODE_LOOKED_UP; } - *result = iget(dir->i_sb, (int)parent); + *result = iget(dir->i_sb, ncp_info_ino(server, parent)); iput(dir); if (*result == 0) { @@ -694,7 +795,7 @@ ncp_lookup(struct inode *dir, const char *__name, int len, memcpy(name, __name, len); name[len] = 0; - result_info = ncp_find_inode(dir, name); + result_info = ncp_find_dir_inode(dir, name); if (result_info != 0) { @@ -706,7 +807,7 @@ ncp_lookup(struct inode *dir, const char *__name, int len, /* Here we convert the inode_info address into an inode number */ - *result = iget(dir->i_sb, (int)result_info); + *result = iget(dir->i_sb, ncp_info_ino(server, result_info)); iput(dir); if (*result == NULL) @@ -722,7 +823,7 @@ ncp_lookup(struct inode *dir, const char *__name, int len, found_in_cache = 0; - if (dir->i_ino == c_ino) + if ((dir->i_dev == c_dev) && (dir->i_ino == c_ino)) { int first = c_last_returned_index; int i; @@ -747,15 +848,24 @@ ncp_lookup(struct inode *dir, const char *__name, int len, if (found_in_cache == 0) { + int res; str_upper(name); DDPRINTK("ncp_lookup: do_lookup on %s/%s\n", NCP_ISTRUCT(dir)->entryName, name); - if (ncp_do_lookup(server, - dir->i_ino == (int)&(NCP_SERVER(dir)->root) - ? NULL : NCP_ISTRUCT(dir), - name, &(finfo.i)) != 0) + if (ncp_is_server_root(dir)) + { + res = ncp_lookup_volume(server, name, &(finfo.i)); + } + else + { + res = ncp_obtain_info(server, + NCP_ISTRUCT(dir)->volNumber, + NCP_ISTRUCT(dir)->DosDirNum, + name, &(finfo.i)); + } + if (res != 0) { iput(dir); return -ENOENT; @@ -790,6 +900,11 @@ ncp_create(struct inode *dir, const char *name, int len, int mode, iput(dir); return -ENOENT; } + if (!ncp_conn_valid(NCP_SERVER(dir))) + { + iput(dir); + return -EIO; + } strncpy(_name, name, len); _name[len] = '\0'; @@ -806,7 +921,7 @@ ncp_create(struct inode *dir, const char *name, int len, int mode, return -EACCES; } - ncp_invalid_dir_cache(dir->i_ino); + ncp_invalid_dir_cache(dir); str_lower(finfo.i.entryName); finfo.access = O_RDWR; @@ -847,6 +962,11 @@ ncp_mkdir(struct inode *dir, const char *name, int len, int mode) iput(dir); return -ENOENT; } + if (!ncp_conn_valid(NCP_SERVER(dir))) + { + iput(dir); + return -EIO; + } if (ncp_open_create_file_or_subdir(NCP_SERVER(dir), NCP_ISTRUCT(dir), _name, @@ -858,7 +978,7 @@ ncp_mkdir(struct inode *dir, const char *name, int len, int mode) else { error = 0; - ncp_invalid_dir_cache(dir->i_ino); + ncp_invalid_dir_cache(dir); } iput(dir); @@ -877,8 +997,14 @@ ncp_rmdir(struct inode *dir, const char *name, int len) iput(dir); return -ENOENT; } - if (ncp_find_inode(dir, name) != NULL) + if (!ncp_conn_valid(NCP_SERVER(dir))) { + iput(dir); + return -EIO; + } + if (ncp_find_dir_inode(dir, name) != NULL) + { + iput(dir); error = -EBUSY; } else @@ -892,7 +1018,7 @@ ncp_rmdir(struct inode *dir, const char *name, int len) NCP_ISTRUCT(dir), _name)) == 0) { - ncp_invalid_dir_cache(dir->i_ino); + ncp_invalid_dir_cache(dir); } else { @@ -915,8 +1041,14 @@ ncp_unlink(struct inode *dir, const char *name, int len) iput(dir); return -ENOENT; } - if (ncp_find_inode(dir, name) != NULL) + if (!ncp_conn_valid(NCP_SERVER(dir))) { + iput(dir); + return -EIO; + } + if (ncp_find_dir_inode(dir, name) != NULL) + { + iput(dir); error = -EBUSY; } else @@ -929,7 +1061,7 @@ ncp_unlink(struct inode *dir, const char *name, int len) NCP_ISTRUCT(dir), _name)) == 0) { - ncp_invalid_dir_cache(dir->i_ino); + ncp_invalid_dir_cache(dir); } else { @@ -955,6 +1087,12 @@ ncp_rename(struct inode *old_dir, const char *old_name, int old_len, goto finished; } + if (!ncp_conn_valid(NCP_SERVER(old_dir))) + { + res = -EIO; + goto finished; + } + if (!new_dir || !S_ISDIR(new_dir->i_mode)) { printk("ncp_rename: new inode is NULL or not a directory\n"); @@ -962,8 +1100,8 @@ ncp_rename(struct inode *old_dir, const char *old_name, int old_len, goto finished; } - if ( (ncp_find_inode(old_dir, old_name) != NULL) - || (ncp_find_inode(new_dir, new_name) != NULL)) + if ( (ncp_find_dir_inode(old_dir, old_name) != NULL) + || (ncp_find_dir_inode(new_dir, new_name) != NULL)) { res = -EBUSY; goto finished; @@ -983,8 +1121,8 @@ ncp_rename(struct inode *old_dir, const char *old_name, int old_len, if (res == 0) { - ncp_invalid_dir_cache(old_dir->i_ino); - ncp_invalid_dir_cache(new_dir->i_ino); + ncp_invalid_dir_cache(old_dir); + ncp_invalid_dir_cache(new_dir); } else { diff --git a/kernel-1.2/src/file.c b/kernel-1.2/src/file.c index 0770b0d..6032c07 100644 --- a/kernel-1.2/src/file.c +++ b/kernel-1.2/src/file.c @@ -99,6 +99,10 @@ ncp_file_read(struct inode *inode, struct file *file, char *buf, int count) DPRINTK("ncp_file_read: inode = NULL\n"); return -EINVAL; } + if (!ncp_conn_valid(NCP_SERVER(inode))) + { + return -EIO; + } if (!S_ISREG(inode->i_mode)) { @@ -178,6 +182,10 @@ ncp_file_write(struct inode *inode, struct file *file, char *buf, DPRINTK("ncp_file_write: inode = NULL\n"); return -EINVAL; } + if (!ncp_conn_valid(NCP_SERVER(inode))) + { + return -EIO; + } if (!S_ISREG(inode->i_mode)) { @@ -239,6 +247,7 @@ ncp_file_write(struct inode *inode, struct file *file, char *buf, if (pos > inode->i_size) { inode->i_size = pos; + ncp_invalid_dir_cache(NCP_INOP(inode)->dir->inode); } DPRINTK("ncp_file_write: exit %s\n", NCP_ISTRUCT(inode)->entryName); diff --git a/kernel-1.2/src/inode.c b/kernel-1.2/src/inode.c index f85fe62..0add779 100644 --- a/kernel-1.2/src/inode.c +++ b/kernel-1.2/src/inode.c @@ -61,50 +61,25 @@ ncp_read_inode(struct inode *inode) inode->i_ino. Just to make sure everything went well, we check it's there. */ - struct ncp_inode_info *inode_info - = (struct ncp_inode_info *)(inode->i_ino); + struct ncp_inode_info *inode_info = ncp_find_inode(inode); -#if 1 - struct ncp_inode_info *root = &(NCP_SERVER(inode)->root); - struct ncp_inode_info *check_info = root; - - do + if (inode_info == NULL) { - if (inode_info == check_info) - { - if (check_info->state == NCP_INODE_LOOKED_UP) - { - DDPRINTK("ncp_read_inode: found it!\n"); - goto good; - } - else - { - printk("ncp_read_inode: " - "state != NCP_INODE_LOOKED_UP\n"); - goto good; - } - } - check_info = check_info->next; - } - while (check_info != root); + /* Ok, now we're in trouble. The inode info is not there. What + should we do now??? */ + printk("ncp_read_inode: inode info not found\n"); + return; + } - /* Ok, now we're in trouble. The inode info is not there. What - should we do now??? */ - printk("ncp_read_inode: inode info not found\n"); - return; - - good: - DDPRINTK("ncp_read_inode: read entry %s\n", - inode_info->finfo.i.entryName); -#endif inode_info->state = NCP_INODE_VALID; NCP_INOP(inode) = inode_info; + inode_info->inode = inode; if (NCP_ISTRUCT(inode)->attributes & aDIR) { inode->i_mode = NCP_SERVER(inode)->m.dir_mode; - /* for directories in dataStreamSize seems to be some + /* for directories dataStreamSize seems to be some Object ID ??? */ inode->i_size = 512; } @@ -176,7 +151,7 @@ ncp_put_inode(struct inode *inode) { DDPRINTK("ncp_put_inode: put directory %ld\n", inode->i_ino); - ncp_invalid_dir_cache(inode->i_ino); + ncp_invalid_dir_cache(inode); } clear_inode(inode); @@ -204,6 +179,8 @@ ncp_read_super(struct super_block *sb, void *raw_data, int silent) printk("ncp warning: mount version %s than kernel\n", (data->version < NCP_MOUNT_VERSION) ? "older" : "newer"); + sb->s_dev = 0; + return NULL; } if ( (data->ncp_fd >= NR_OPEN) @@ -253,6 +230,7 @@ ncp_read_super(struct super_block *sb, void *raw_data, int silent) server->wait = NULL; server->packet = NULL; server->buffer_size = 0; + server->conn_status = 0; server->m = *data; server->m.file_mode = (server->m.file_mode & @@ -302,7 +280,7 @@ ncp_read_super(struct super_block *sb, void *raw_data, int silent) DPRINTK("ncp_read_super: NCP_SBP(sb) = %x\n", (int)NCP_SBP(sb)); - if (!(sb->s_mounted = iget(sb, (int)&(server->root)))) + if (!(sb->s_mounted = iget(sb, ncp_info_ino(server, &(server->root))))) { sb->s_dev = 0; printk("ncp_read_super: get root inode failed\n"); @@ -393,6 +371,11 @@ ncp_notify_change(struct inode *inode, struct iattr *attr) int info_mask; struct nw_modify_dos_info info; + if (!ncp_conn_valid(NCP_SERVER(inode))) + { + return -EIO; + } + if ((result = inode_change_ok(inode, attr)) < 0) return result; @@ -480,7 +463,7 @@ ncp_notify_change(struct inode *inode, struct iattr *attr) result = 0; } - ncp_invalid_dir_cache((unsigned long)(NCP_INOP(inode)->dir)); + ncp_invalid_dir_cache(NCP_INOP(inode)->dir->inode); return result; } diff --git a/kernel-1.2/src/ioctl.c b/kernel-1.2/src/ioctl.c index 3b85ea9..beb6951 100644 --- a/kernel-1.2/src/ioctl.c +++ b/kernel-1.2/src/ioctl.c @@ -79,6 +79,16 @@ ncp_ioctl (struct inode * inode, struct file * filp, return server->reply_size; + case NCP_IOC_CONN_LOGGED_IN: + + if ( (permission(inode, MAY_WRITE) != 0) + && (current->uid != server->m.mounted_uid)) + { + return -EACCES; + } + + return ncp_conn_logged_in(server); + case NCP_IOC_GET_FS_INFO: if ( (permission(inode, MAY_WRITE) != 0) diff --git a/kernel-1.2/src/mmap.c b/kernel-1.2/src/mmap.c index f585f00..344f6eb 100644 --- a/kernel-1.2/src/mmap.c +++ b/kernel-1.2/src/mmap.c @@ -135,6 +135,11 @@ ncp_mmap(struct inode * inode, struct file * file, struct vm_area_struct * vma) { DPRINTK("ncp_mmap: called\n"); + if (!ncp_conn_valid(NCP_SERVER(inode))) + { + return -EIO; + } + /* only PAGE_COW or read-only supported now */ if (vma->vm_flags & VM_SHARED) return -EINVAL; diff --git a/kernel-1.2/src/ncplib_kernel.c b/kernel-1.2/src/ncplib_kernel.c index bcbd667..09e0ef9 100644 --- a/kernel-1.2/src/ncplib_kernel.c +++ b/kernel-1.2/src/ncplib_kernel.c @@ -266,56 +266,19 @@ ncp_extract_file_info(void *structure, struct nw_info_struct *target) target->entryName[*name_len] = '\0'; return; } - int -ncp_do_lookup(struct ncp_server *server, - struct nw_info_struct *dir, - char *path, /* may only be one component */ - struct nw_info_struct *target) +ncp_obtain_info(struct ncp_server *server, + __u8 vol_num, __u32 dir_base, + char *path, /* At most 1 component */ + struct nw_info_struct *target) { - __u8 vol_num; - __u32 dir_base; int result; - char *volname = NULL; if (target == NULL) { return -EINVAL; } - - if (dir == NULL) - { - - DDPRINTK("ncp_do_lookup: looking up vol %s\n", path); - - /* Access a volume's root directory */ - ncp_init_request(server); - ncp_add_byte(server, 22); /* subfunction */ - ncp_add_byte(server, 0); /* dos name space */ - ncp_add_byte(server, 0); /* reserved */ - ncp_add_byte(server, 0); /* reserved */ - ncp_add_byte(server, 0); /* reserved */ - ncp_add_handle_path(server, 0, 0, 0, /* no handle */ - path); - - if ((result = ncp_request(server, 87)) != 0) - { - ncp_unlock_server(server); - return result; - } - - dir_base = ncp_reply_dword(server, 4); - vol_num = ncp_reply_byte (server, 8); - ncp_unlock_server(server); - volname = path; - path = NULL; - } - else - { - vol_num = dir->volNumber; - dir_base = dir->DosDirNum; - } ncp_init_request(server); ncp_add_byte(server, 6); /* subfunction */ @@ -323,8 +286,7 @@ ncp_do_lookup(struct ncp_server *server, ncp_add_byte(server, 0); /* dos name space as dest */ ncp_add_word(server, 0xff); /* get all */ ncp_add_dword(server, RIM_ALL); - ncp_add_handle_path(server, vol_num, dir_base, 1, - path); + ncp_add_handle_path(server, vol_num, dir_base, 1, path); if ((result = ncp_request(server, 87)) != 0) { @@ -333,14 +295,54 @@ ncp_do_lookup(struct ncp_server *server, } ncp_extract_file_info(ncp_reply_data(server, 0), target); + ncp_unlock_server(server); + return 0; +} - if (volname != NULL) +int +ncp_lookup_volume(struct ncp_server *server, + char *volname, + struct nw_info_struct *target) +{ + int result; + __u8 vol_num; + __u32 dir_base; + + DPRINTK("ncp_lookup_volume: looking up vol %s\n", volname); + + ncp_init_request(server); + ncp_add_byte(server, 22); /* Subfunction: Generate dir handle */ + ncp_add_byte(server, 0); /* DOS name space */ + ncp_add_byte(server, 0); /* reserved */ + ncp_add_byte(server, 0); /* reserved */ + ncp_add_byte(server, 0); /* reserved */ + + ncp_add_byte(server, 0); /* faked volume number */ + ncp_add_dword(server, 0); /* faked dir_base */ + ncp_add_byte(server, 0xff); /* Don't have a dir_base */ + ncp_add_byte(server, 1); /* 1 path component */ + ncp_add_pstring(server, volname); + + if ((result = ncp_request(server, 87)) != 0) { - target->nameLen = strlen(volname); - strcpy(target->entryName, volname); + ncp_unlock_server(server); + return result; } + dir_base = ncp_reply_dword(server, 4); + vol_num = ncp_reply_byte(server, 8); ncp_unlock_server(server); + + if ((result = ncp_obtain_info(server, vol_num, dir_base, NULL, + target)) != 0) + { + return result; + } + + DPRINTK("ncp_lookup_volume: attribs = %X\n", target->attributes); + + target->nameLen = strlen(volname); + strcpy(target->entryName, volname); return 0; } diff --git a/kernel-1.2/src/ncplib_kernel.h b/kernel-1.2/src/ncplib_kernel.h index 3278cdf..8d46aa6 100644 --- a/kernel-1.2/src/ncplib_kernel.h +++ b/kernel-1.2/src/ncplib_kernel.h @@ -114,10 +114,16 @@ ncp_write(struct ncp_server *server, const char *file_id, const char *source, int *bytes_written); int -ncp_do_lookup(struct ncp_server *server, - struct nw_info_struct *dir, - char *path, /* may only be one component */ - struct nw_info_struct *target); +ncp_obtain_info(struct ncp_server *server, + __u8 vol_num, __u32 dir_base, + char *path, /* At most 1 component */ + struct nw_info_struct *target); + +int +ncp_lookup_volume(struct ncp_server *server, + char *volname, + struct nw_info_struct *target); + int ncp_modify_file_or_subdir_dos_info(struct ncp_server *server, diff --git a/kernel-1.2/src/sock.c b/kernel-1.2/src/sock.c index cc519a1..bb48a3d 100644 --- a/kernel-1.2/src/sock.c +++ b/kernel-1.2/src/sock.c @@ -491,7 +491,7 @@ ncp_request(struct ncp_server *server, int function) if (result != 0) { - DPRINTK("ncp_completion_code: %d\n", result); + DPRINTK("ncp_completion_code: %x\n", result); } return result; } diff --git a/man/ncpmount.8 b/man/ncpmount.8 index e15b1ef..6c68d2c 100644 --- a/man/ncpmount.8 +++ b/man/ncpmount.8 @@ -34,6 +34,9 @@ ncpmount \- mount all volumes of a specified Novell fileserver. .B -d .I dir mode ] [ +.B -V +.I volume +] [ .B -v ] mount-point @@ -188,6 +191,36 @@ can very well choose a file mode that tells that you have. This certainly cannot override the restrictions imposed by the server. .RE +.B -V +.I volume +.RS 3 +There are 2 general ways you can mount a NetWare server's disk space: +Either you can mount all volumes under one directory, or you can mount +only a single volume. + +When you choose to mount the complete disk space at once, you have the +advantage that only one Linux mount point and only one +NetWare connection is used for all the volumes of this server. Both of +these are limited resources. (Although raising the number of Linux +mount points is significantly cheaper than raising the number of +available NetWare connections ;-)) + +When you specify to mount a single volume by using the option +.B -V +.I volume, +you have the big advantage that nfsd is able to re-export this mounted +directory. You must invoke +.B nfsd +and +.B mountd +with the option +.I --re-export +to make nfsd re-export ncpfs mounted directories. This uses one Linux +mount point and one NetWare connection per mounted volume. Maybe +sometime in the future I will make it possible to mount all volumes on +different mount points, using only one connection. +.RE + .B -v .RS 3 Print ncpfs version number @@ -212,7 +245,7 @@ Most diagnostics issued by ncpfs are logged by syslogd. Normally nothing is printed, only error situations are logged there. .SH SEE ALSO -.B syslogd(8), ncpumount(8) +.B syslogd(8), ncpumount(8), nfsd(8), mountd(8) .SH CREDITS ncpfs would not have been possible without lwared, written by Ales diff --git a/man/nwmsg.8 b/man/nwmsg.8 new file mode 100644 index 0000000..a4d538b --- /dev/null +++ b/man/nwmsg.8 @@ -0,0 +1,33 @@ +.TH NWMSG 8 02/29/1996 nwmsg nwmsg +.SH NAME +nwmsg \- Deliver NetWare user broadcast messages +.SH SYNOPSIS +.B nwmsg +.I mount-point +.SH DESCRIPTION +.B nwmsg +is called by kerneld when a broadcast message arrives from a NetWare +server. +.B nwmsg +fetches this message via the mount point and delivers it to +the user using the same way write(1) uses. + +Please note that +.I kerneld +must run when broadcast messages should be delivered to users. + +NetWare servers can send asynchronous broadcast messages to users, +either on explicit request by another user, or when the server is +shutdown. The client workstation is informed about this event by an +IPX packet on a special socket, the message socket. + +This can happen at any time, so the user has to be informed about this +event whenever it appears. I chose to use the kerneld feature of the +Linux kernel to call the program nwmsg. For nwmsg, I used the relevant +parts of the +.I write +program, so you can expect the NetWare broadcast +messages to appear where user messages would appear. + +.SH SEE ALSO +ncpmount(8), kerneld(8), write(1) \ No newline at end of file diff --git a/ncpfs-0.16.lsm b/ncpfs-0.17.lsm similarity index 83% rename from ncpfs-0.16.lsm rename to ncpfs-0.17.lsm index 1476842..9928716 100644 --- a/ncpfs-0.16.lsm +++ b/ncpfs-0.17.lsm @@ -1,7 +1,7 @@ Begin3 Title: ncpfs -Version: 0.16 -Entered-date: 21. February 1996 +Version: 0.17 +Entered-date: 29. February 1996 Description: With ncpfs you can mount volumes of your netware server under Linux. You can also print to netware print queues and spool netware print queues to the @@ -13,7 +13,7 @@ Author: lendecke@namu01.gwdg.de (Volker Lendecke) Maintained-by: lendecke@namu01.gwdg.de (Volker Lendecke) Primary-site: linux01.gwdg.de:/pub/ncpfs Alternate-site: sunsite.unc.edu:/pub/system/Filesystems/ - ~73k ncpfs-0.16.tgz - ~ 1k ncpfs-0.16.lsm + ~81k ncpfs-0.17.tgz + ~ 1k ncpfs-0.17.lsm Copying-policy: GPL End diff --git a/util/Makefile b/util/Makefile index bb42bbb..6434b38 100644 --- a/util/Makefile +++ b/util/Makefile @@ -2,9 +2,12 @@ # Makefile for the linux ncp-filesystem routines. # -UTIL_EXECS = ncpmount ncpumount nprint slist pqlist fsinfo pserver -UTILS = $(addprefix $(INTERM_BINDIR)/,$(UTIL_EXECS)) +USERUTILS = slist pqlist nwfsinfo pserver UIDUTILS = ncpmount ncpumount +SBINUTILS = nwmsg + +UTIL_EXECS = $(USERUTILS) $(UIDUTILS) $(SBINUTILS) +UTILS = $(addprefix $(INTERM_BINDIR)/,$(UTIL_EXECS)) #CFLAGS = -Wall $(INCLUDES) $(KERNELD) -g -DNCPFS_VERSION=\"$(VERSION)\" CFLAGS = -Wall $(INCLUDES) $(KERNELD) -O2 -DNCPFS_VERSION=\"$(VERSION)\" @@ -14,8 +17,11 @@ all: $(UTILS) ncptest install: all for i in $(UTIL_EXECS); \ - do install --strip $(INTERM_BINDIR)/$$i -m 755 $(BINDIR); done - for i in $(UIDUTILS); do chmod 4755 $(BINDIR)/$$i; done + do install $(INTERM_BINDIR)/$$i -m 755 $(BINDIR); done + for i in $(UIDUTILS); \ + do install $(INTERM_BINDIR)/$$i -m 4755 $(BINDIR); done + for i in $(SBINUTILS); \ + do install $(INTERM_BINDIR)/$$i -m 755 $(SBINDIR); done $(UTILS): $(addsuffix .o,$(UTIL_EXECS)) ncplib.o $(CC) -o $@ $(addsuffix .o,$(notdir $@)) ncplib.o diff --git a/util/ncplib.c b/util/ncplib.c index 949d1af..b5124a1 100644 --- a/util/ncplib.c +++ b/util/ncplib.c @@ -2742,6 +2742,36 @@ ncp_write(struct ncp_conn *conn, const char *file_id, return already_written; } +int +ncp_copy_file(struct ncp_conn *conn, + const char source_file[6], + const char target_file[6], + __u32 source_offset, + __u32 target_offset, + __u32 count, + __u32 *copied_count) +{ + int result; + + ncp_init_request(conn); + + ncp_add_byte(conn, 0); /* reserved */ + ncp_add_mem(conn, source_file, 6); + ncp_add_mem(conn, target_file, 6); + ncp_add_dword(conn, source_offset); + ncp_add_dword(conn, target_offset); + ncp_add_dword(conn, count); + + if ((result = ncp_request(conn, 74)) != 0) + { + ncp_unlock_conn(conn); + return result; + } + + *copied_count = ncp_reply_dword(conn, 0); + return 0; +} + int ncp_get_broadcast_message(struct ncp_conn *conn, char message[256]) { diff --git a/util/ncplib.h b/util/ncplib.h index 942e4f1..1352706 100644 --- a/util/ncplib.h +++ b/util/ncplib.h @@ -257,6 +257,15 @@ int ncp_write(struct ncp_conn *conn, const char *file_id, off_t offset, size_t count, const char *source); +int +ncp_copy_file(struct ncp_conn *conn, + const char source_file[6], + const char target_file[6], + __u32 source_offset, + __u32 target_offset, + __u32 count, + __u32 *copied_count); + int ncp_do_lookup(struct ncp_conn *conn, struct nw_info_struct *dir, diff --git a/util/ncpmount.c b/util/ncpmount.c index ba275c9..d7efae9 100644 --- a/util/ncpmount.c +++ b/util/ncpmount.c @@ -195,7 +195,7 @@ main(int argc, char *argv[]) upcase_password = 1; - while ((opt = getopt (argc, argv, "CS:U:c:u:g:f:d:P:nhv")) != EOF) + while ((opt = getopt (argc, argv, "CS:U:c:u:g:f:d:P:nhvV:")) != EOF) { switch (opt) { @@ -285,6 +285,14 @@ main(int argc, char *argv[]) } password = optarg; break; + case 'V': + if (strlen(optarg) >= sizeof(data.mounted_vol)) + { + printf("Volume too long: %s\n", optarg); + exit(1); + } + strcpy(data.mounted_vol, optarg); + break; case 'n': password = ""; break; @@ -346,6 +354,7 @@ main(int argc, char *argv[]) data.version = NCP_MOUNT_VERSION; data.mounted_uid = conn_uid; + memcpy(data.server_name, spec->server, sizeof(data.server_name)); if (data.dir_mode == 0) { @@ -466,7 +475,8 @@ main(int argc, char *argv[]) } if ( (ncp_open_mount(&conn, mount_point) != 0) - || (ncp_login_user(&conn, spec->user, spec->password) != 0)) + || (ncp_login_user(&conn, spec->user, spec->password) != 0) + || (ioctl(conn.mount_fid, NCP_IOC_CONN_LOGGED_IN, NULL) != 0)) { fprintf(stderr, "%s: login failed\n", strerror(errno)); ncp_close(&conn); diff --git a/util/ncpumount.c b/util/ncpumount.c index 5b76bd4..2c72bf5 100644 --- a/util/ncpumount.c +++ b/util/ncpumount.c @@ -178,20 +178,3 @@ main(int argc, char *argv[]) return 0; } - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * Emacs will notice this stuff at the end of the file and automatically - * adjust the settings for this buffer only. This must remain at the end - * of the file. - * --------------------------------------------------------------------------- - * Local variables: - * c-indent-level: 8 - * c-brace-imaginary-offset: 0 - * c-brace-offset: -8 - * c-argdecl-indent: 8 - * c-label-offset: -8 - * c-continued-statement-offset: 8 - * c-continued-brace-offset: 0 - * End: - */ diff --git a/util/nprint.c b/util/nprint.c index f2e41c7..e476be3 100644 --- a/util/nprint.c +++ b/util/nprint.c @@ -1,5 +1,5 @@ /* - * nwprint.c + * nprint.c * * Send data to a NetWare print queue. * diff --git a/util/fsinfo.c b/util/nwfsinfo.c similarity index 98% rename from util/fsinfo.c rename to util/nwfsinfo.c index 3b86fcb..7aec919 100644 --- a/util/fsinfo.c +++ b/util/nwfsinfo.c @@ -1,5 +1,5 @@ /* - * fsinfo.c + * nwfsinfo.c * * Print the info strings of a server, maybe sometime more. * diff --git a/util/nwmsg.c b/util/nwmsg.c new file mode 100644 index 0000000..490d09a --- /dev/null +++ b/util/nwmsg.c @@ -0,0 +1,219 @@ +/* + * nwmsg.c + * + * Fetch NetWare broadcast messages and write to the user + * + * Copyright (C) 1996 by Volker Lendecke + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ncplib.h" + +static int search_utmp(char *user, char *tty); + +static char *progname; + +void +main(int argc, char *argv[]) +{ + struct ncp_conn conn; + char message[256]; + struct ncp_fs_info info; + struct passwd *pwd; + char tty[256]; + char tty_path[256]; + FILE *tty_file; + FILE *mtab; + struct mntent *mnt; + + + progname = argv[0]; + + openlog("nwmsg", LOG_PID, LOG_LPR); + + if (argc != 2) + { + fprintf(stderr, "usage: %s mount-point\n", + progname); + exit(1); + } + + if (ncp_open_mount(&conn, argv[1]) != 0) + { + fprintf(stderr, "%s: could not open connection %s\n", + progname, argv[1]); + exit(1); + } + + if (ncp_get_broadcast_message(&conn, message) != 0) + { + fprintf(stderr, "%s: could not get broadcast message\n", + progname); + ncp_close(&conn); + exit(1); + } + + if (strlen(message) == 0) + { + syslog(LOG_DEBUG, "no message"); + exit(0); + } + +#if 0 + syslog(LOG_DEBUG, "message: %s", message); +#endif + + info.version = NCP_GET_FS_INFO_VERSION; + if (ioctl(conn.mount_fid, NCP_IOC_GET_FS_INFO, &info) < 0) + { + fprintf(stderr, "%s: could not ioctl on connection: %s\n", + progname, strerror(errno)); + ncp_close(&conn); + exit(1); + } + + ncp_close(&conn); + + if ((pwd = getpwuid(info.mounted_uid)) == NULL) + { + fprintf(stderr, "%s: user %d not known\n", + progname, info.mounted_uid); + exit(1); + } + + if ((mtab = fopen(MOUNTED, "r")) == NULL) + { + fprintf(stderr, "%s: can't open %s\n", + progname, MOUNTED); + exit(1); + } + + while ((mnt = getmntent(mtab)) != NULL) + { + if (strcmp(mnt->mnt_dir, conn.mount_point) == 0) + { + break; + } + } + + if (mnt == NULL) + { + syslog(LOG_DEBUG, "cannot find mtab entry\n"); + } + + if (search_utmp(pwd->pw_name, tty) != 0) + { + exit(1); + } + + sprintf(tty_path, "/dev/%s", tty); + if ((tty_file = fopen(tty_path, "w")) == NULL) + { + fprintf(stderr, "%s: cannot open %s: %s\n", + progname, tty_path, strerror(errno)); + exit(1); + } + + fprintf(tty_file, "\r\n\007\007\007Message from NetWare Server: %s\n", + mnt->mnt_fsname); + fprintf(tty_file, "%s\n", message); + fclose(tty_file); + fclose(mtab); + return; +} + +/* The following routines have been taken from util-linux-2.5's write.c */ + +/* + * term_chk - check that a terminal exists, and get the message bit + * and the access time + */ +static int +term_chk(char *tty, int *msgsokP, time_t *atimeP, int *showerror) +{ + struct stat s; + char path[MAXPATHLEN]; + + (void)sprintf(path, "/dev/%s", tty); + if (stat(path, &s) < 0) { + if (showerror) + (void)fprintf(stderr, + "write: %s: %s\n", path, strerror(errno)); + return(1); + } + *msgsokP = (s.st_mode & (S_IWRITE >> 3)) != 0; /* group write bit */ + *atimeP = s.st_atime; + return(0); +} + +/* + * search_utmp - search utmp for the "best" terminal to write to + * + * Ignores terminals with messages disabled, and of the rest, returns + * the one with the most recent access time. Returns as value the number + * of the user's terminals with messages enabled, or -1 if the user is + * not logged in at all. + * + * Special case for writing to yourself - ignore the terminal you're + * writing from, unless that's the only terminal with messages enabled. + */ +static int +search_utmp(char *user, char *tty) +{ + struct utmp u; + time_t bestatime, atime; + int ufd, nloggedttys, nttys, msgsok, user_is_me; + + char atty[sizeof(u.ut_line) + 1]; + + if ((ufd = open(_PATH_UTMP, O_RDONLY)) < 0) { + perror("utmp"); + return -1; + } + + nloggedttys = nttys = 0; + bestatime = 0; + user_is_me = 0; + while (read(ufd, (char *) &u, sizeof(u)) == sizeof(u)) + if (strncmp(user, u.ut_name, sizeof(u.ut_name)) == 0) { + ++nloggedttys; + + (void)strncpy(atty, u.ut_line, sizeof(u.ut_line)); + atty[sizeof(u.ut_line)] = '\0'; + + if (term_chk(atty, &msgsok, &atime, 0)) + continue; /* bad term? skip */ + if (!msgsok) + continue; /* skip ttys with msgs off */ + + if (u.ut_type != USER_PROCESS) + continue; /* it's not a valid entry */ + + ++nttys; + if (atime > bestatime) { + bestatime = atime; + (void)strcpy(tty, atty); + } + } + + (void)close(ufd); + if (nloggedttys == 0) { + (void)fprintf(stderr, "write: %s is not logged in\n", user); + return -1; + } + return 0; +} diff --git a/util/pqlist.c b/util/pqlist.c index 0449326..325ea03 100644 --- a/util/pqlist.c +++ b/util/pqlist.c @@ -62,7 +62,7 @@ main(int argc, char **argv) { found = 1; printf("%-52s", q.object_name); - printf("%08X\n", q.object_id); + printf("%08X\n", (unsigned int)q.object_id); } if ((found == 0) && (isatty(1))) diff --git a/util/pserver.c b/util/pserver.c index f19b5ad..3a2ac81 100644 --- a/util/pserver.c +++ b/util/pserver.c @@ -9,10 +9,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include "ncplib.h" @@ -77,26 +79,29 @@ terminate_handler() term_request=1; } -static void -daemonize() +/* Daemon_init is taken from Stevens, Adv. Unix programming */ +static int +daemon_init(void) { - int fd,c; - - if ((c = fork()) > 0) exit(0); - if (c < 0) - { - fprintf(stderr, "ipxripd: can't fork: %s\n",strerror(errno)); - exit(1); + pid_t pid; + + if ((pid = fork()) < 0) + { + return -1; + } + else if (pid != 0) + { + exit(0); /* parent vanishes */ } + /* child process */ + setsid(); + chdir("/"); + umask(0); close(0); close(1); close(2); - if ((fd = open("/dev/tty", O_RDWR)) >= 0) - { - ioctl(fd, TIOCNOTTY, NULL); - close(fd); - } + return 0; } int @@ -137,12 +142,13 @@ main(int argc, char *argv[]) if (debug == 0) { - daemonize(); + daemon_init(); + openlog("pserver", LOG_PID, LOG_LPR); } if (ncp_initialize_as(&conn, &argc, argv, 1, NCP_BINDERY_PSERVER) != 0) { - perror("Could not open connection"); + perror("Could not open connection"); return 1; } @@ -268,13 +274,13 @@ poll_queue(struct nw_queue *q) if (pipe(fd) < 0) { - perror("pipe"); + syslog(LOG_ERR, "pipe error: %m"); goto fail; } if ((pid = fork()) < 0) { - perror("fork"); + syslog(LOG_ERR, "fork error: %m"); goto fail; } @@ -301,7 +307,7 @@ poll_queue(struct nw_queue *q) if (waitpid(pid, NULL, 0) < 0) { - perror("waitpid"); + syslog(LOG_ERR, "waitpid: %m\n"); } } else @@ -314,7 +320,7 @@ poll_queue(struct nw_queue *q) { if (dup2(fd[0], STDIN_FILENO) != STDIN_FILENO) { - perror("dup2"); + syslog(LOG_ERR, "dup2 error: %m\n"); close(fd[0]); exit(1); } @@ -322,7 +328,7 @@ poll_queue(struct nw_queue *q) } execl("/bin/sh", "sh", "-c", q->command, NULL); - perror("exec"); + syslog(LOG_ERR, "exec error: %m\n"); close(fd[0]); exit(1); }