From 46c8da86d8e2f462a122e0183c34c1b6d1273355 Mon Sep 17 00:00:00 2001 From: Sebastien Vincent Date: Thu, 9 Jun 2011 08:51:21 +0000 Subject: [PATCH] Ongoing work on Google Talk voice support. --- build.xml | 3 + lib/installer-exclude/ice4j.jar | Bin 302592 -> 317815 bytes .../impl/neomedia/MediaStreamImpl.java | 6 +- .../neomedia/RTPConnectorInputStream.java | 14 +- .../neomedia/RTPConnectorOutputStream.java | 11 +- .../neomedia/device/DeviceConfiguration.java | 2 + .../format/ParameterizedVideoFormat.java | 6 +- .../protocol/jabber/CallPeerGTalkImpl.java | 2 - .../jabber/CallPeerMediaHandlerGTalkImpl.java | 10 +- ...ionSetBasicInstantMessagingJabberImpl.java | 3 +- .../OperationSetBasicTelephonyJabberImpl.java | 351 +++++++++++-- ...ationSetContactCapabilitiesJabberImpl.java | 56 ++- ...perationSetDesktopStreamingJabberImpl.java | 4 +- ...ionSetTelephonyConferencingJabberImpl.java | 5 +- .../OperationSetVideoTelephonyJabberImpl.java | 9 +- .../ProtocolProviderServiceJabberImpl.java | 91 +++- .../jabber/TransportManagerGTalkImpl.java | 474 +++++++++++++----- .../extensions/gtalk/GTalkPacketFactory.java | 7 +- .../jabber/extensions/gtalk/SessionIQ.java | 1 - .../extensions/gtalk/SessionIQProvider.java | 5 +- .../jingleinfo/JingleInfoQueryIQ.java | 61 +++ .../jingleinfo/JingleInfoQueryIQProvider.java | 92 ++++ .../jingleinfo/RelayPacketExtension.java | 90 ++++ .../extensions/jingleinfo/RelayProvider.java | 114 +++++ .../jingleinfo/ServerPacketExtension.java | 96 ++++ .../jingleinfo/StunPacketExtension.java | 36 ++ .../extensions/jingleinfo/StunProvider.java | 74 +++ .../jabber/jabber.provider.manifest.mf | 1 + .../impl/protocol/sip/CallPeerSipImpl.java | 1 - .../service/httputil/HttpUtils.java | 35 +- .../protocol/StunServerDescriptor.java | 27 + .../protocol/media/TransportManager.java | 3 +- 32 files changed, 1484 insertions(+), 206 deletions(-) create mode 100644 src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingleinfo/JingleInfoQueryIQ.java create mode 100644 src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingleinfo/JingleInfoQueryIQProvider.java create mode 100644 src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingleinfo/RelayPacketExtension.java create mode 100644 src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingleinfo/RelayProvider.java create mode 100644 src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingleinfo/ServerPacketExtension.java create mode 100644 src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingleinfo/StunPacketExtension.java create mode 100644 src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingleinfo/StunProvider.java diff --git a/build.xml b/build.xml index efaf9113b..008b17931 100644 --- a/build.xml +++ b/build.xml @@ -747,6 +747,9 @@ + + ^P^&cPYYiQX|h?bvl$)d|7_vS#_X`Dg)-2Dak8rS>6`{JYAYx@{b z^CB$ib!`}w*=f`Hsby11LuSDQu4;UXiXMwro}?^bE&MwRUT;CudJ~%O4>OFDDeDmE;{CBmGL?2Lwd_nY>SEgfzIfww2RpWSV)_{4xz?~ zhZXYqHZpG!jXoeRnwqzd8Kz$3;xMCuZO0N0l3Y6~Sy`r68p-hSK^9> zwU|)%WJ&*rG-R6t@Y&M4{we5!*v_?1-Z1}fyfM_SJbEub`)(I#iOas0L887`I80?yGN$It%aL8iY5c z;f|BGq^+ld%r9`I){1wMLCB073NNTrtd(F8SpfTd`4sU0u)`Ah$%@-8_J`i4f0oGQ zK-J1&_@Vm|a$pBTKArxV?M=aUvevbNrxN(dkX^jua6l^UIb{4KkbQOOa3a^I_ML{! zT_3dSbJjt=l{P>CYK~1m;F<=vzb2O?U>quE^k-^cy#x5{Si{?LzJGRz#0*Nm6GBrf z9zFIDm&E}-Fv|g(t(v~D4wGOdpv%M^e#w9+?`f5A%xLq-2=Rw2KW99zKtWE#TJG;+ z``?JF;_dyLNcCqC8ibdQ%Q%%MoY#UC%>+Kd3w*(5Jaz*x+$20nRjmOau->c!6@dKe z{-1)hl;c!Lt>Ymlfo16u%2TWhA-Yj13hy*l9`~K8z(pNvYtMABR|K_P&hgY^+sk^= zfyJwVERPskUDxfYoce|+R;fFz&e|49Xp6X=4Ck!AEbepVxQ1KnphB=7 zyY6;AxX+HzV8`3^GQ(sq5V{a)S_yK^M?N3YtI?3Cg{~f`yz^tpt*SJi&;b$;`;}E?DUl#s5E9FCM2%k? zh)FVGrqDh9jb1)LJKHu66f2-w8-~IKYpzp81YKUKUOllZ67p}&@OQ!&WC~}C6G-{% z5Ruxe7bnsqVW?2(sId{^JqV7ChHRviSx4~q+V2^k@sQJC+_^XL+>ham3|B7pRnVzd z@{H3r+$8HV5yZ6wmTWV$s)Zax)%9oTFW!{eTT>SO+Abz-d+Srn6)~AFtpB?h8{-?% z1baIL1p#UNx3rk!|E;3_9pD6AdsIR6;BD}8JKeI+yAGA!t;WBtMW;!rxI-3EgM)r# zdUC6x|Hv5U#oXCCJ*=8pfoa$s}Ab%_}6_;?O@ zFJ4CX@}kgr(0LPKu9PvTorS0_SdBIjRm2H%py}!J50opX?im+=07ncfW$b({BPno zF4Ta9zeQyIR^k)D9#zz>>nTm{C^TkP<#qkF*3AO;iJupA8~*>EG*%YryFG3*K70q$ z?E86Fqw3>?MwYWoyv?%XhLCuH1Ey1Jo#LDu{kHwHcR4V-J7-Ikovhnsw7)|mI@ZO) z{vkS_oCGdTxMKKfvTuROyCv$?e_X^%qI&EC+^A<6zYz~A%x{k`k`H}LxT zXjdJ16Vf0kcl2Zk190YG`s2En+Y<+^PJX!K#!hc*!2BBHmJf*I*r`7V4fgQf2f`PK zJkbhAgmSBh^VllXzxeUaq{qjwi}vv@37Zq|5Ec0OaSd`0)EaC8q~G8yeQFl-QSRTr zfLZs*^*^H8#oma*>c^uDaxnka9E?KkCfTh(wTr)D#gS%W*gZ$JOLm9|3_!I@a>$6Z z-*H2M#HnEHyONdtXHy*}mZ@8{4{uFizm9j%_~$z8CMON{CiBUVzs7LP34AB#`B@NA z#kMP`M;L$0*iE|E0U+!vn0)0$iGjT-sZx=efzw#%P+qS}Q8Waj0cA_^F$5#x9kX`P zn7CSUc6-35HFvi*o0G6=_Kog1#m0!h2a^k(jK6Fa10IQx-)$2x6An<@l5tz>YEN!p&uGV^hs~K8OIIB4!l}ueJ?pNn%HD82STFykbrkW^ zkYPxQ)!}7JNi+hZ0Ci32FaqNO)`|8#-?oNQqJMz!tK5+Jig2Vc*N|i@-5B#!?ND}p zpvNeiRgf$6k>`$LskO<&vE|7R&Y*wb&xCrY_5He$_OBgCUn;eIP%fTSQQwlFY-`Bu z6fDq)FSjkTn^uvX;&iMKR%4@ZjyHzO9;or~%zg{!0iT>+tvRI`73CQb!Cw<)nWcAX zFO~C2(0U`1Emu=N= zXd-gP*nNJbRGNTM;Ru3^0uEuK$R~8kv=n<1+OteDl6BRru3S?tO~B}ZXRua6BgHP> zZX>zDSkaBsuRKBJgbMV*O&b)n70-pP!#2}t_$R8ccZ+m(@q+TsL3o8Ovy-*q47QY{ zh30v2w4BSBPXB)^=YBT6wscA?x0oZ?k0a}!E02pV#aNDi3^JkB)LsLspR0vB5sh&_7H=h(3iBvlEcUlVf%^*Ji^!9uK6R=>KHvvy)yLKha!I zgZn_r9{G-YnI{55`#9@-s| zfDK$9L3R~+p($qu*DSMZy!n~Q47z4oFLemAQu><%%9fP=Nt2vV$dBTt1k`3iRIk5& zQ5rH$mIdXNxj?H-Ngdt9J-&O$VTjhLsYV- z-WEbViNV(sfv?!kaLvf~5?4B%X)chG`$^HmAXRJ(WLW@9%Ry}irYweGTNCjB3Bn7? zG6+v`NP5hSGBOt^5Jh1L#oaVqit?pAgnXY;H1vyq_CYQQ7Pt$N?N4mtTd_U&$MnMG z!}?I#eSu0S>9GLZFET2yGb1ke*sZ!`$xb_de`2Cf!rpOeI{GByJ@T$5m2FcIM@!ty zha9ieUgrUPp%Nx*ZApL@@q!O8*c9EW9<^t?`UQsgT2-u_1}E^4Y}%s6+U|L|If9r; z#hsu!?SD}9O~IK&UAxi5wlmQiXJTVAu_v}|8y(x3*tTukww;M56HjvTedn)J=dW6| zFZyEjzUW=uy&eJmPP!n5+KI zem)|P4bLv3Z7WVP3MdC=ZH`EGy9LuezNCjIxo-@Y?>_lV0$J%l)M(mmzm@)tQPrCt zu%9uYeSzd7AXB}7gM^*)#5i|gUEXtrxPx+R#Yj0|Lf!{-3CVtLhlk#P5%*!ZBhKtP zq^spnAj!iC@G(>lHT)o)f$O?wSe7Tzg>;u_jT6Xy$@)#2D6{y99B%R#u7O53qpjkp z!om?F(5XjY-*C8c3F-VDE1|~g$f!}`zZHlq#j8CouuGyH;<}?dFyFLG#0d09MBDTJ zBez)mh6As*S{ts|b-M^LP17=HlzEUMF*YI6v)@|PPL^#Xa$A*emh$tB1HLFEN=Nm) zse~&UGqnZv7GgSUkiag`arUh2D7zzQxrJBk+sFUbCh=K9%z-2SX9g!I`8u3K2nMG7 zJ;g8i^BdtK4B~$#Z#)R@%122jZ;#WPX&D5tq~IV5gI+LFlz<4d&`=6eiLY>Rao@j3 z3bO>w8AF?a&@8Pgnlvk#<~6YzE$eJ5HR&v&XH}KyowsbOnyc4CHT85hsx}^ts@nh4 zGD{qU@#c!t{r1@NaqxB`c_ouHQ7P9v&UoBMYR+|X09ZACHIL+Oa;*4fquHu4G1bmzJ;Xt@;m{RBU{BuTdb%2(;e63=OY~yT z`q+HB8mQ$l!ja9dH-d0u#A7>x@Vqn=c4=?)?cvOx0NVNdaKjj&DT!kXm_H=>XTyx?`0$& zUo^WzA2oYB5>CDS;|V@543b+cTifLuY$UrS1B@$RB@Y4R=H7;9J4VlB^J?}+j;MK0 z3TJz0rpCc3E~CTcjmLK&cxQ3|ty6r!?l#-YeJp1LA<(to1!gn{$^3-1Y{;bDi} zTOriv31a6rB~H|x!8g{Z9EU$nNI>4fw_B|3_wtY$izhm$lkcrPf~Z2&7G0x)7c~SC zUtrGN>2c3%tdg9h(e5@@?CdPf^_J?(4V0V!s{Trwsyzunr{3|{&ZgIy30&GK-b1x%c%!L}wT!d{3)>wSbVYKh$h(*7V=PVQh9oA)iYHbx(S;`6;3x*hqOL-Z}Lw1-oyR#TNhHjTIt^6j60 zJ2FN)g^>jt>bVvU?1e2ZEHcdcBo+fHS${2{pLsUjmA4&Z&lU31e4k~Ps!yxuApiR} z)@F*0Y}pPna6s<`*(u8Pm&q0yA)@o}TVRLeZps(ozE$|3hw<3qkxUt_+34I)dxKX9 znInDbSpl|Ia@4iM9cEO0K8?fnfAL>?v;r-y)#BV0|DeU?Zs5IlcVkMXXL*Do?wK}j zc8&c#!YHds*>$^u7dJfHUqp=*4d4wad`VI}Mh*N@NS99OP^iYOI&bZKA*3_+o#K!L zv5>VTQ4Zzr0=8atLxY^CTcRW!Akis-G0BNOq-{0Uf1kiK1R+Z!W~Ta(h_M?!y)Y@8 zmLFQ{Oa?OXx3&Rk-yidL`EOPF1&PLcgt#2Y?!|nkug5WvluJG|?(aklE2NB9=b%$p zO(Td~fm%!x)=QEqR`M_;9dj-uuXXxb8=734X)HoM5Fe8T>E(_{n&^1|*iaBECu~db zxD|nK$T}{6R|-O|($q<_^6e`lQA3?rjbbVijBA9K-OKwaquUXj`Tbnxmx^0okX)nR zL!8Do(P;w;t~eyRW9sX%E$|~&GFK^ieZZwT|Ib+m`JV3jYU6|;6Dn*SHK($z)#kFh zB~R*J7oSbASk>KR3>ga01XpE8V3GVoCunm((Mb+@(W=)N7 zaYlrDo=6%SWOmSMacvvpnXaBg77t%c^(YBd8cQ)wCXpqX5J7_pl#WXIKQyX1|C3BW zz_|J?p(Xk{?zB%}BAD`P;Z2=~lpW;A^nJN-75)5Jf`U+kMB@?#d9>6af!!7*Gg<0Y z3}EXeUSlzz8lZ@oKeY;kqsy6Ep{!~p@70pzGD6|*GK7_2@a1;w#{GQGjsQn4XAp)p(?m zXG1mTRB2+vm15F^cMqvdK1z=PrEPF8ZqQYoeq~B+T zJP@ZitWHkgC7b0$hT&xG73eFD)B=6Gy+qHq#z6NIGcZSMj1mj2@yl70{REhCw=OvQWGjiG=lINi4Qsr;90pG`Tb`0;IwETlO^0vLq{w z>SFN`gehvEca@gN=n7YEUt%+g?tCK|ilO0T!^aFII1JI+$R1H>iBp zz0`yhV-u6;NMQp8ki@}((3>bb9J}C8F(G!iPoQp~Xm11h9sfgkzzy*YiIDTAh3_c` z`W<;^>81zqjsLx`S8zWE@s0Iebf5?SiL;V(SBT4acjuM_|GmZkdQXtud#4xWM)(Js z5A++NZzUWP6ubefXlaQ3uGV0_10rZkn+quqE(Nq1GP@@l#ElV2y@noHg1~@eXw}<$ zN9_RcxlF5M#%PN$YyQalUTD!v5)GXX75vQ+D#o&(NkpLGguy+d=Aj&1zRy7lNsa-P z%=WAAdaTL?A!P)I7Mm|WT9uhxNe z2FUd5B!AJ@NmOEm1hKwwIvVf3v=C}aG*lBHe-pIa5;phZY2suL2k z{3Pe+3{(HpB;RJ&yOK==DPC+bB6#pQ{2nlA4jIumQ7T%FxEB=>#zn1X^}3{Y18lpm{k;j z=_tj@Fr0K6Ob1WEa2k0&$LBm4FLFdI5?@Ey^(L&mg{VAhRR1Pq&m4q{Cp%F3}z=U4M;j**F?6Fz_4rey9PvPiu0@b>csZ^Mq63M)BX~bL2MDr$2#!;ek7n8xgr6gB=TvAYo;)n^NafrG<4pQ)Ur zr(8{xUeFd@$7~Y>^z@Ehu<&0236S17w?R@-ateJN7Fvi#gf$j70%XNMnT;-INTs`B zbv2C}d`Q>6vXc?#6=mU6{w9g5-CE=1@QGFsc;4ABhw&ubq^KNr)PsO%&t#w*-+y1PKnD0-N_ZJ>OQ1IAqqnv0+A?^kWhydWs=Dcu@%8pHOlEG;4S` z+K3e`71yXPU0OmO>lm)ODIaCl6w%fc(FSk2S__BW-Zzm4nBqD;iTQ_lezyEzIMZcy zhEF>a3OA8fQke?tF*3-YQwqigN=SOVpPUNc`E7sL{q|FdwLzSZ&-3L~p`Hd=Z&}n? zk=ZznmJ%CCPv;v?+o5m+6=`oeWKuNLRAGA|5`*&UYO1yqhDY^LX?GdLQL<}EpeozZ zNy(a?(ji8Xp*8D~NRPI)Ik zmM`g154&s)R~$$qBnOHeO>CS_L{}LeWWh0sP~{i7`nqbu{l^$sU}iU77Fw9+>+q=At+vp@>oN$>j@KB=XGz>GqsdJRxoZ9OSCExFnqg8< zsvykV9dZNNq)uf0QQL1PV>zym-D~tF4gPU1g~$1*zi^FCm2N}*$Ee}Y?m5SBSy_E? zjoi-`dX5%k=$|whl!fSHS^H`RFwc3!n8!9MjaeDyE29YaVhXA({b#P6n2T$hc5Pd{ z8EXOBi~#w>2Xg7RemzZ|x0H1?ni;a5F^^`l0Zgt(lW-E$x3z-UTce;l*k}gVBEso_ z(Wi6`*plP96Vv>2Y}KdxGUz5ZA8D_v<8-!eg5X$t?d z01RAYdxf$hpTC~fw=oR?6}@b6h?({DcwQ^_D0h3(&q^(eQ*Thk$s5uwLJuRaZ;PvT z{dQB@*0N9Ojp^%wB~NKwDLGT@oS>!DT1LLSqJ=;ORf|6@;6(=unO}-|w7#-3o+WK* zW9lXh!Dr?JI`$#`b-QwkA!B2b(#qV(Rw>)#vRy&4nq1u`lMjE$QjuEvFTvtaD78Cs zb>VR0Pc`860t5Z*Isbm~CW3F1k+^;weYBg~>9QyJTVj7CFF@jVJ=zAdhsux8W^Y~t z1ig#F5yy_~)e`bTh#&n?;MSG1A_=@!eC7A-Ai#V9P-cI+;EF66<=zuQyI>Nkj8k)+ z=@huFP`6fW7UV8?5qA+3#ObL(C5q+cmV06rD=$U;*dz6T`mnEsceZ@=ho8`AsSF@% zMKu*drQJm@4W!E@?uL@yo2W&%4KQk)w;}`kOozEE`H6IwPwnNcA{3V$aDd(KbI*9H zq3{`5yg{#vyfE)d?b^!YZaWip%{;>dV{!6?kHID-?U@vs}dE_(;plXhvb|dyZgS8=%Ujr5v+g{uWO3yk%9 z)erVg$4P~;wFKbsJEiD!sRnn*mb)M$_gWg?d(4kIX5j&ewn?#97J{k?`|F)u@jDh^ z<}@Z`N{&S$rQoA5vOA|Aw*+a4#(>!E0@pRuq@d|Q&Y-#>^WlMWY7`}n*{@ZzU+R+|aDP=ir;8p+ZUTazkpp^v zpUf#FJ*M@nXOIGIX75-;{$HG(RpbqTzXqjad>&|%y{vhdGg90h**I_1y^s@cYJK z%#n<$;mLV9xt|t&iFMsd-mjvZ1KA9;#>oEV8w3@yUkoXAuXHXf`EFk-Us1$YtT?83 zLSNEvzsBj6zGfEc^aQCLrU{Zeusf&=r?Q4e5(f_PFIf|*-;?+^APf6|m}}$=B;%2K zV0Fn$y+`!oAyR+hXwlED4HE`80BBb;elAY%HqHdg1UJQB`ot?ds{Kg$prm8^xdW1) zSw0o#5GB3Ew^}#NUrz8RKv}vvyU_#l&FNGIYrd)4f1v2e^S{LoM8H(2SnY5AixbB< z88$cyOyw)hOhtANOMQiv>E~j8?ARSLVj{^2;*nujF4@50-Xd!ki+W~%140*%+;%yO z1@?c7ICK27TgK)(n7x5wdm$tqkyIanKBLHFs6xqGIdt;Or0UDtly__^A32~1wg71* zS@fyKj`9~Z9(8_tJ5Nv&hOXUm?bm{k8DP*Mk6C^PL)kJ+tn=hx_@s|`K&W~xV*I=& zEpL3$CSHLny+cj55oj6l0nj(abA6ydTDb0~F8r7CeAQgmxh@gnkpp7H+~Dav5S9^j zBq1#AuN!fp*Ktqp*u@9e`pECur%zPsegHX*2g-7sbrlBVDYoq@8N5M9D!eZNddSD z6VqJq)sKHj2K)iL{q1I)wsm-7H7b>OVrsj(>4o7d-$3mJ=9h1=P^*L%q_<6m6kKc`$@CT zNsvt$2Qwa15In>Ex1^Pji;GYl;iaJ%NG!4xeu}**)IhpRvl6t{m~04!3jR|> z@YE2Mj|{%Z5chnDm`lx@5P!&IR_6Nm;8CrRHu%P%ihbS_K!1U`(oeaMz(jBBCm-fB zn7u)9R+F!upS_;}^a6>qUU~T2 zPD2WpN`ok4%l=;x6Z->sLLLR+A z9ExzTE1$M1gkfrJIyD#BF;XHhG?QL1J0Wr35(x% zabIB7^qLJ;*f+T&Rb$!p{oO~JU*z2Ty>BDAjU{rSW32RNHZJHrME2B9UqFt`kY|@s zQWu;6$eoz)?J8_|lqx2Z8-_?c+{are-e*EHR-+vYJcxhGucWY(&wD!7%x}I@vWoRu zG=h5EceRA}F4w&N8p-V>c=0!Ge%QcX@P5fLRGGne-TIRF=`%!6cGayciR~#+7;fRr zm_geKs=qP9+3f~QCQrBSBCR+Pd4nia8u_LhAV5)B@>FgYH--waLfE{kq|R}H;8_zG zY3$Hva0{7?n}|}F3bQJX$^^5dh#Fw(Bq^r*8-sSoa@<>_C}-Y#v~cP;6!ULc`9~0; zVvv*%BR1&@b52Iep^C?%CXMiwFvS(ptXw?3c!SQ}v%Gc$oy4AN{|OByL@V;uubGZr zK=j!g&g&`sq(HAbgTR+(&1;07!wQ$7~STi*&4mE@)))!Y=d_-Cv z?Q&fEB1Nbb>LL3q79v}PPWuiPf-A-2UiqXiHwlW6cGQu(Iws6-O58BgNhVx+8sSY+ z7>o+j14UB2)jyZ_q>7L-ej%-vIUBUp19)D){@i+-(CJoM50D-~(A4ls_E!hlW(EXP zw6)Aeuf1EfqGM)8h#FUJ2kWRuEg08wWX72iip3O5rr)AVr+@n%%SH)*zpqTQLVH1> zVcJ&g7uCa}MSQ@PzNc;w=U#V$yjn`RC90qnTaepYF=D}uoJ0r%H+b+jUGbXM6~MB( z;L04T9{PKgZrFlJe+Nd(i;Tk?gJwaviu<#%FI)fmomtL{w1e&lBbO#6P^}GLPN_Qh zXxyb%^BJO%n)5qdmmBqKuJkiY-3eWDgO1!-U?_bz!Lb(hj-;iPrgyX=#ln8aC`qP} zB(&D!Lg`7IrEE$QQeUo(1B^{1FJP{*$?((qQljaKwQjNg3P=6cr8^C!ovZN0oux4O zImD4f=hBzymt6_&oA>L}|I}~yK0(;p)?a20WjU?-ES6cx=PTg~qLZcjj4}6)P8I-9 zisNTqP3|~rF0~-Ejz{*>AB6oGy1|^4Fu@+O_qTwjKg|r?lPPyf!0v0k%S0PU3Z)3Tl2v{%dGF4ao3)|Y(G15ATdfQ z8(>lYa?(c?ZW(;Y2OeRN<=vcEv2R9Yoh&a1H98ir5IC{xX!$L4X7nT)HRY(dU~r^> zLr~bd5{rd)-0Npnv{~Fqtqs=Z+<60sXmk$Je046lt1_f1o(W*;>a1-{#qI_gX zE6?f{`?OLPU&L9kieQX6bUlBZLzUR%Gz7pjd>7SdpD_?)T6xLDM4YZZ=9`iMoBzWW zeb?y9TDX{fSGq(TIx?oQb7C>KC6H1^?!AXoIM7}zEmX4kYm=^ z_MIInf=SzB3o_W+P-;N_MIKeUreO3zl3EI6QtKkXF5d}arT(Ftzl}h!C|oN_L@DZ- zgn8!B8kT=%@Rz1l+y^?Qbf&-H+{ymOKe#uYe*ky;qMkwg3(vH|PHpsWbj8#!n2_f7 zXpKf#s)>~XQeF6!N6uiEcQk)1*Ga}sf3T$gc#sj+j9{!B5Or>7yCMC^PB3;OeZ!i` z&1RJ~ogN%Yt{_OlhnVGvv{_!%c#&-p?yjuc8f53NIH#`9VyBg6G2ST#U2I@B+mSnR z2?EG^lF{yM+bUv)8!~e3n%MU@vRYwlB~sPBVA8(4-|~cxlR`7E^p{v92yx)ez+@S&A)s zwx9Ha5a*V0a?jgjCg_ZTf1UJBjWE*^T{hxAgN8`KY`r18*MO>QpjAaCg8^M8%0Q2e zQ$)%jYt#0%+NbG=YEzhjMHKf}IjjOu$b5)eN!NWgC<6(?EYR|*wsf#^NZi9m-q6*( z(uasYs)E2iApd{zT+2!rgw_AGX8Z-g_+Lqbb25Yn@_!Tz*M$&!|GhF_0znM*U#v9w zABb$_lTVd|oAUD?IA+R+Gz4tQO$ZcHie4xbbPGcz#2);A)DWmGpUpm7d~bo+hC=!e z%Gx-Ioq6@i(UVT0>x7^Ml=^HJzN7kVw7M|a{t#YJ)DF-x{~ZMSOxQvD^kox;?`n)t zw$<`7X?DCAb)|iw>$*YGMi-%M{i6`J#$`zX(bUM>V3)lUWw!iZ>CrAXG9tavf?v2e zk0lQ=!R(?&pw3`&{f4ENVXj|EL`Q*<+6eK$JRRQH1-o!x*&~44=z4(=;;1IJZ@wys zF$O1y09WR06dTH)E#Zq)B^HG}l?@T>=49Z`!T&1kmbiGI( zn5|h%nBXWo6n#MLxBLsS7y}(aYzPW8*`4Ip*S!Vi% z^Kn3E%Wb4arZ8f~G+#XK5*j5x&Jxcy zZF~>rW2M>?P#t4g;x?rAPvwgEt+`WcA53HwtkRR~8r^@!-nhVwU4@oR`vNsOYT?jF*Uk zuv~gSu}l5PD!9 z^^n=0t+whA+?88-d{+*Mecg7rWG|JvQJtFQ{tUi1)Ns0baSh&W_W>tZbSFlyR` z?T5-ekUt!dJ%GHV$=V&uPhZ2$RU(%+3{Tvkwyv=oa_a54SAm(xvc#?v{T}LK#)ln< zm2X%NbbVtrSr?oaE%{GbiCr`A;*}qI!9KgjJ!SwZKj2(1Sofi{8fth*4)fZO*nc4g z{x;P2eqKoIS`heeN-|?U3vjCkYCrd+2mh*Wkdqsk*pEjs(v~Q#jm^&i(-OI!XD>Q> zFF-}<9#5SKz<+~=uBP>l>X-g1u6w?l-amnPyQKQgU+(@j&ez z(EsUmCsmn!dYEWGsV=xCRKwev63>r)eHl%GD{J0G(aFx|#e@|-h3zx2@dFG)v3 z&bz7#Ac~9p%x%$5A8CJ+u5)g~O~A7_&dcrT620F*c+v7E%{g`R!*miX?E_`KS2urW zGA9h^H`m_IJnDXM1u9UZaTy`b5nZ^)lGEB9wf*lL*8JmTQVyaJO&wDkwdHL=PlOp~ zB;h3x5G)F511j-58Cu{VQ0j=-{{43?_@YEW_@WMXyeb%qDmInLG4l3DnS=fpF(&M- z{Z9Dy5x9lBj5fM!bJ-m0cbVgmA%@3S%{QIjTK_I5eoPn$$U=NMRNlmq1Pye>&&K$} z78jxKy&JdkVnsi~XyV=?WYkJ$*lFZ?_&AA=MEH?tlAPZ)!T3x;yuN&o^jFMP!jruK zG}rmuwgdhwYsncNB9gXkFcGJwN@J`FXhC{ z)HT|V4h?73^j%cxF;|l)%b9EcaOjy)L0wu|oT1}HP?kWm7tEy5m+HBawaFs`u9X*- z0%*u~_#@#Gx0Q%59qT?es{IenF9Cf^L^=$rq1({j0<$>gD9XVn1-z0%rjz>N+aZXO;Tb{7ed7>ZWv15aOLObAogGV^;aq5c_&y{G&=)}d zzUqfBrX-s(kw3pyXe5%xV~3H?W)}m_bL|Z)dS+w^QTHYJWu>idMF>Mpwsuhu;!2U= zsx#U{61K}VJ&M}3@eh4KcXYf6btiKOv6@vUFQFEeoX8hz=kc;B1^sfittPRr%pD9m z2gyVW1d>fbN_)!^fSqMSH(CI|Tlxd#Y8k8@>^m1#TRmEsNC~+o>kv5j=KY?sHU;iO zs=pr6D>1o}`f zA*yhF5-nFB&5-7d;dF(G`48&2>!i!fhj|d|X;>cG;wfdd*7(p3HaBWtP*cV)@B$!pfJW3DIbG}3t z7RiTE6cGt&WzQg`Fc{iZqk0_wI@uDkZVQaVSlAt=`uivVxj-$ZSOi;uyv5am&mWWb zOI@=FG#RRPkA~35u@>9(O08u^k6=!0acLvw+BU=0*rug}riLJ?oD89&X&bs6ML)0ewJN&2h*!bC zJ@w;X7hAVa$)y&t&UfHT_+b!e(1}JPUq74cM6$0B*WY)>7zauU_l;)5Q3 zzCaQnLz8zFO@Q1LI7m_SJcD8l*BvO)1>sjLe+|Gqy%5?lcHfzv=6sc5$ZPKa41C+tXw3 zV4vNJy>=PYjCc39_cGMD-K^0HM&?xAaeD*R<0o}-<1>4I_DT>maB$Mp9jnK2Zk}lc z~n*H>LA04jRwGL7AP^W!FTnT5j5>5=T zRIxdeQf`f(Ve1K7LGX;Knlkn(W2f)p8Doq;#;0(+Y}J-MSvXm77`8M_r>t|8L=$Qj zu%vF9=XSUYJ1&x{mm=-wB=6!vo_#*qXxc8ei^1(iEO5Zx#Q0Avet@UBRxmK17SJC= z7Q0h5Gp>l6DNXSkPlm^^G>%lp4mt+<(WZW`;jAIT=oQ}`@$K&BC_KY&m$T%gHUu=4 z-$Qp*-*IeT?Ghnx!_AmDP?o*I}Tm)s#do2mGs^{fJYD;3_5 zCS_CRr_rKEgR+rn2w{QiM@V_;e!J_eLrlL?(*IW!kf;*8=q6P@K zlbu#+tqgo&cESdBTLHDGABOUL2_P1{@Ouv>hZ?koz*C!ygSxP^Q)o!=&StlQ*2vCV zSum+C)K<=VA~jq17}R%bJS||l^qK8~nolVL%BK7Cr06KnQk%dD_;j8i*8m} zV0qQP8~UETY+KK`S6+}e>qyqxRiHRq^>*bW#E^dONC!juI{oPaa~f*uTW3rUv2N~K z2T!K8pVCQKykuNoru7XHd6C`s<{Q8hikbY1D5$Y4 zY%E&sxzlIwofwVpipfTYA@hPvyN@+^_^)W9~e2~7HY z8cs3Jid&=zbn_}jHA+S+vY*&(ablb(GO;+HLwq1g>6>Cqg)AN79U9}AH#sd{PKr7E zNS{y(+K!6)U!$B-G+1Cj@Q8tfvbZ|FD~z2pH1RV2@(dC2Hfgg4@_9^#17U75O_9gC zE)*uH$4|bD81E@&GR)CGa>MeyJZfVcWnHL*F(XFy2iFuU1Eg0i6BFz@jyi=Vc3BD* zFqB5`B?`5dvq%SVgs19}v1BOU^f&J;-kt|8#5-|rCOor*M<1B#l4JJdamPSy z-X`G^$XM!d}!H1EmlHxfjHCC0tj^U!_jc} zQmkrItu`^)D9M{Uk#)8Vxr1|K-h|^*f%5zhaPpA1pB7TIRRPW3q+tr+%=p9-FT- zDgCX*eHrjigxtgXP!w$6*YsH+mCvJehD6hm!4RpY%-%i&txHCAlM49e+Cg-$K>-Li z44@~~-VT`?+uoM6-3{;^1+X29fJd( z8|v;q(33&$4o6=s7%@vZOCsF6U@r;w&7%T105I-efBW+GugR~c2kYkX z5#s$_p?60R;ptTk93a8Dd6X0YqJX<+Bl;Y{VJD_UunEvA^lEmAxXPpfz8V-#nm*O8 zAx0$xabii2O64G%F1$KxbPsInBvcRLs!6&Ya%(?S2Y4)d-vL-GXCHAdhKxPsE-eNh z+6@H;A71SWlsq_Jlm0s!ht8X!WW9SD#qcx~CK;Sl7-*4@LY16ft}}#R4p5vzOIRVM z*f=l-l=bu&x_^)npb-R!cFT)$<)KT=4G^2K4uuL@&{u{mB|O>NVpp)buEsvAi_0n+ zO}UK<$ixind(}5_i6C1$;?AO_k7wuO0}pIj6W!90s|}4o{3S|kC>_f`xhu>Xg!+`rjm_#B`fO;j zrjBddxX5RXXD8|`v#+{R-37pPg`S(#sZ1HF%7}`Y2bAkG&SQR_t&idZjPeX-(~Bxi4()fCe|`h>>D9R&(?! zCn07d1m~J?0>aBb>Vc#yl&K8I6@~>;7c4R$xV0gV+MTqUa*a+}E%Tf8QEa@`bR_E{ zZfg~;ggl5KfSHtjitMwrC#N}s+_Ll3i^R?MH4k=C<_jT$GSeEV0)IZ8F?*#`fX28~&Tw392*}}fypSC?w>)T#B&{pu5mwAmw zOKk{KikpvXW>}pIrMp%>oaU#OQ(>jLRt4!$l%1o>&L{x<;2CIk6gYx8pgYjr9X3zm zs|B)NvE8+-UHp+DBf{>T>FGDK9Do1Y$^WRpvWDj`N$#))PBXM z_oeAEv#ECh?)8u1pxe2RCdGQIgR(spD(^r=Hg5Wb&Zf(;qy z_&ehto)7FBX)kUJJ$9D94u!UqeoMi=EJ%&YUkNcRAQ}=y#I7**7U$x5<@G*6Y-p53k3}! zpHaLE+vUr-)6WitZ!!~+RpR`_EP??b{IL+1;b;y z;R2=5I^h^*0v5Ld?ZRRX6-Yyz7u2@)xla)R`X&Pk@vV_Er-R(mX67wXNyA)c>V-Ey4WEo$cf9hpFwM3!uluj+U9D5vZSj>` zE}fZgMOl23Eq6lgiFO5E&&Zx}dSb$Fj-EgQ;O{$JllS=Ka>4zs@<7&;-Sue(QgfT} zdCECgdEZPQh_+1j2w+9&Od=y;Qm%#iFRJ_&pC0I4fC z;!hfH3IaJKyU}kB+k+@~M{i7aC*tm5fh})kN7mNzY425COIjL7+DgdPc z848Xtv&t;Xyw5B$=Vcz6TD!J7swOq+>dRXao|I>3UZFS=<-g**!q}oDG&r^O(->T_ z$1nqDH$1V&s3&5=zYP0Mwu`ZmC`Af_`qWqaA70i=W~1!DDByAxXVn+?yOUIHAkJzpuEJro$$YfQebmKw8mS3k z>7-_VQ@3E_={Hvg=c(*hBE`;OrUB4QGkyL1sdWb0zY;AS5s_DB|AF8Fn9V7bPyeDQiH%Uv?_ zCwT1-N?Gbh+VD+J|H4N!?UUf>z=!0*bB{UnN9y!(&a8I0l0*Ux+1e?(?gB;nB5bMQjRgX3(e3P3(!K^=EpR)njPu;tJ%!xBk6Ec+L5CI%ldEEk|uMfe#HFw zA6wNh7TDa#SQ)24L;G+O8D!0@!Ki+`szTGAWRXt#x0SY|3KLLaAx%yP%`eD_(?mL3 z__m|x<_eRTm}TgWk(l2%jOj19O_Hgny-99O^j;Sbv&uw@j|ypXdB7Qwrsm>hXjz~_ zpm2JPJ7Gv?4H|>aZdT}7K;v#CpXgcc(wQsI%{QV(o~?+DHUp|EUCA}<@}ize_qgFi+DuD2GF)3TK_rn$*~?dFDFbd zXG`YEn@jK=_kt^8%La_g5-=^u+9;s9=h@O}vxJ)F5!1=13Z07ZHR+cUBNgj~BDvlxIB*V;Jev}y&l1>8b< z^bS-75$1#n2G3HhAQ6E4m7Woa$~1|fU^YD}InM>H16R|PF(azNf~wJ)nP7%PKU+lL z*wlc3^*T?mF4}ypG}gC5oYoaT|5bytaoKyn|v(B>U|dXNyRc>x%abpjG)VSgLk*J%y*Ty?r1 zl^LtKBm8dmvmz~HlpIt{VpZ!nF$Ah{_Kajribng7Eyo{Zluoykctz`7!#FPT9c`kisog zIqM@nzyekeU@=#q4n94!!E%1vS6$80g^chYv%Kl0Qb9d{r#Yrq-g|ae&%?Bjh z1=c2u5jLz>OoJRU$wzow6C4#Qe4eb;^@ByXk=&89*%5w@Sf|vNpsMwtru-vI`{JUd zYX{=OC+515>0zm=A0?G-#q%~b7i%72(0=n8tNyO%39~xRtDwGSB4A-(To>^{_u&PDv_7UvN6UA6qxkHz3t#4IPptC#bll@rCFh z=sdM+PhZ7tT`+QINo_^ExlqdZf=O8lZ`w58r^HAb08(P*KUQbdJ)Mqezt zwB?bX>C5A0@+9vd5?$n$`3cXeer5B9C*P=P+v$K-N6C-=NWjj&pO`RF2lzPP*a9|j zF0{>ke#Wt~ErFE9K}N{ycm%7A$|Rsx;nMdLXI>`136{2zlcIl)eH{^-$OGs3=pI4Ks{tumsr2|AHrtCXz;I z7wkl4_%(Z_ASx*w$O4sV?I>$Hqmy&Hw727@rXo+hABq|^{YAC{({269ZvXi^z$1vt zkmv8fnaFfN0B+#70@_Vq@H*ZomAzMGmeY7JSX0L-m;Bj-OKZ!{EX0qC%g|5AAjWCiMDQ6Vyn(USxKy%%(0U z9dcF?dTIq@4t>?tl)ypRsJg73*TiwTJ*(NVjLGB2^Z=HXC+iVWJp#Cgr&1au5IOza zxIO932@sS8OZQ_VqDK!_!W_!~DatGc|D`T4W*C}nzT~?RFTeRkPBsniESFrYHKtYZ z1s+>7J-!xpv<(IR)L;^2wi1Q7)8T2IMqSZ@(Zu|JYoy?C!H9FO&g|@e>g0fCe1tsX zW>kaE5gUlj93nqCa5%mh&Dn2l+*j%|667ZSdr#fK0TPep`j+7j)8zY|iu;=uw!Gwf z8vbBPTLpr@8T*jo zlXVAWf%p>TF^I2A=FK|%Pr#P=%f++SHlvHl$o_?y*0PivsF4zO>Y~8geYd3Xl`&Wx z^Bv|`i~QetoMQqw`Nem}C}3a!4Hy{c|H1CaDL*$qv3CswcS1Fn_iOW}js)@L0Z(;0 zjb!6BT?@5jWA6H{`b2dU7!4FbYN+nm_8&69cFI;YD0C+6&i@7tqGc<#fgI#c|Yq zw9fpwP94n)4$$Y<)(Zu3>kH;95AY-3zb-(KQ_<)9>;MM%PQTnZlYSykE8mVDQQv@h zDNMb@b-Yn?yf-2MltL|A8?fcZ$N|*B-SPcf<7RqscV7aZ?_wNY8t~JqyV=*%iu>Kq z*Jym7jmVX5vRyA_KfB0+0rCCa<2z@LZo2SW*E{JxfOj#{-`ij>Z+zZvv(fGQr(bd1 zdU5@L@f|JRcRA8m(h47_@~uDbHXL5V!Cw!1?TMIuPQ34Wq+P*RQaFI^egv?N(1rJo zBy>pW%xK+^M_RMJP~I1stc#4{X;lG9IvMypwO-r<^LD49#IMUiJa<}~S& zdIfo}>YCxEjj?WyQ9!ch&9qjj6+KD%&Ha{lzmh3eae;M$z(Ofv%5Krf8c3^Dv!cL`>xDBP8sZnGvM43D+Y{^9% zfc~Y|sTJQK+1*Ob2s+wXP@^V(D@KisED73TqqJY%M%KRE^_A7U&5IR_yFl15Knc$8 z4SUv<#XuSo9Q9H4q=nG5R1&2MM<)pjHKr&>KOL#m1LpYzD7;VBRc*V76Mz3s;LpxS zt9H@iZ}kP{8k#>*=Cqs8w_ixIGi{~f0PuPJZU9wAI@$uQ42|1FE3PQhBof$&f{x7xqbL-up$mP;lrDG~@`Uh0@FkX5jAPMeM$j?!SYE1v z2Vdjv*&sMQ7&`Of;m8!)ey^+W(P5F|=u5n&hXYIHWG%t4M!HS!B~=pERMZIIwbe)? zKF}xFK$csi%QPw3xM=2ZD84oUFh!l-oKW8u-C%AQyJSp-HNVeVho#&gpRLKIkcH=L z(Nw4tB_)SC5qIW%LAo^gePYbe$rw9fc?VVu-Wf?(l8zV8SFh=4>!*>S5o8BuOOv~a#joo(z<@C+ zYO}d*MpAVF5R;y7!8WRjdeHA z(g|U*nzDRPY}^FP*gTK~q)T-tRGXivRdet>BMcy$&Z}8uU?B{fur-&~n5~iO6zzhq zi?>v5%HMpsrPAu3fXfUDXkYbHQ~3-a2PgF_SY}#H7p*dDHf;b<*RncW3NCJ;a-l^M zO4%dQz54RQ9CuU#<(^d(2&q(ReXo%0pZuxH21Heq!?9Hs!Z=l|ksr#;5hKgYk<(RX zX@GfW^TJd+Dh!eKRpO;ginO9q16z4g$C~s)$abA@ox_*&!dPon&@S?X&@v6vsoDm9 zyEhA=af!&IZR7#Oa!hG&)T?AOx!2p}!XQhDXgo9+tBOuX!P=E)$zN3%34WIw%AXrh zD_V1v>~b;|gmoI?E4kr?$c)dD(pJhfVs)%-%eD(-J!%-)dUEG;5j17UGFOC#U)&59 z29OC=Oy0<=oud4aJ697ChArRy!>+!aoPRWBRa?=uRDTFai#Pt^1cYG5jl;s>)ipzB z&Q0MCFEykO*Hx28pMp%0-B zzxQQNJT4XvZ}FyJ)=->p9Aj1bRV}f)b4a7npwFQISq=`TtF7*gXV@ekBp!*qj;B?? zt(7`60*6r-bPA4Xw0vx!8zIL9E2k0A?d>_`F)p}Slm@0U*++B+I@L=9rBe|M%>`Cg z$@KDtAG#HM=MJ2}?JCUipl7dKl%{RYdtrJfCmA*C*RwwaYTeW(OyWg>dzXe z{GTdDz(WjER)O3P>ETM(_PjL@&s?;Lpy8uufJqe^9&Bm z@}Ikb)SN*HORYZE^&s4l8TxU|0bI__B}6sqFH?s&qZB}~J+l}QqfLw}U*OVdFJ_+w zFLnO?kButFQ^=Fw?l9+^{`EyV^w(5?LK!`QU;XOL^IFsc(3YO63K%yb;_k;1$^oji zl!|mjn+$*eH3j^eM^@%_xXA;rnKf-XyUtfy+_{-_wkHo|r;hZhH?+-82)Jh#>mfVD z0f?DUh#FjcGBjR6s!!IcexZ=FRQjKymCB-GD7IjZpxYp5xVX>JDy|#Z1aJZ8Y(WV}ER7E>0fZ(-sP^Rj= zh~ctoL<*AZg$Gdd30TF(;DVU}=v->EKW`G?+;KW~hI#$kOyyd7+s(jyn>_~o`8;Mr zVrjA4C0w`5LGOM6e{qgJaVzz~fW8ooJ|TbH(@wpx2D$SDTsc;M^@Q<ddnS^xK78~6*%TMLAnS(WGfU$JjI1b%ic~(A z5Zr0r#yl94ur9|b1Xe+UdHJAhT8D9pD}+&&k?#|fZAdaHCI}-)l~5qIg=DiiZPZXM zU_e4a#DcKK@{a%Fo&TjcL!aUYE-mE!V~6AzJUx{|0L@J*Hsj12jYCbkhrURGR5nL+ zB1V3=R%D`Wf1PVG;|={CkXQOXuYAeqR!W@>FLmIHeFd_NT2kr_q@p(C(Plet(!hfU zUtvhitT*nxjA*MN(jJRu=E%^ROVtNH(YbzCNj9M)t%j)3w*cw%W0EM!hR8Rj1pNe)y%{)3>w5Rr&pk*}Nu93iXs*#_Q z_$0c9`8?@vCdNALx#4eCtF7Q%TS;Z8Q#Dvn!!h0uRDS)(2Vg0TGM=4ws3+-8duFb#YA2pycMSKVB?vV zOj{xk9yf0SK#FgY%R<20@4|$IkdA2PVMPTS(N2WN^AO)%e$0FZMp&P7#4x^u)(L)` z84v?fi7%JH`J5UMwr5|fG?tg#U}#uLFeGrCI-$16I&ulvjn_?%(6;4v*bX;PGl+zRnF0FQ<0 znr45J5MpJjIC!iqgPo7TF&{Blpdv0&8k#Ly7ZxJ3$zlu1fzS$u__&OlvOIYSo?N(r zr-3JI2CZg3>M~Va!`M4a^Nr;SJ@>%K)zv59;kFmgj{vN^&t$ zXM8$OK??$DiBREz&F;%D%A`;Ijn|Ax#`eQWk=%Qe`mS()nW}31CQ>Z!wT6W=gKag_ z6~cXP9E(RdM}AW}|4KOVmi6{B(_H0JwAc{=;5XKZDZd%sIriot|3YA^e3`$4-=lEg zGydF}bd%Y3qXy|cVEXRBj6dG~6C(P|HfBg}J6Z<*Gc?hM(?t>}QH{a5&GyRV-1BE$ zVqIC|E#eVh&~UW}FRdAl!^0h)ka2cXTH^($X^thzV>Llt*p6J!oBa-Qo$fZXN9*f3 zeDwdYe4DLKfz2TQ%}^}d0+at6s%ZXr2PXV)9)sWuIPBj^a>Wl2`hVYS`~X4u4?1xR z4B`j+?~~sUAX@*iCg!0*IKcm13y%as`|p(p31so#6==vHX5jw~b-03MX+;790hNIz zdozGw{J#VW`Y2F*0OSAZ$S+jZl3U1NMPV6!F*0Q8rAPZGoYYkj*}Kt z(;yvAd7$(0ByaePPYau;>{bD%LsKV#ewa^qD@2cG67NckSNqK%` zu26zt0gvJ?`(^vb2oWrbPsCGsv=l7^OsAlz+c^O6E|D}Tu{CJn38QO)G zP|@QgGYexyYz;e$NAr?Fgk|R%mdzPq=z<{!WgTT)t{2y350PCi>YF7BM;WjuOlGHl zZx`XCpHW15_aYAY|05{RQ%HMZ=2y&i>Wm%Qm(1h6DZ{<0=1#yqA< zv{;r#!=5aVtSyo()Y#4V_j5>;jtP9q-FVk3Vj|L6Jc7SJM~`M zJ4F{%9;x`sli0!{9dinbEa12FiVr{#b4C0?;x@iko;u9Z=kC>#1o!JV01#mrIST!7 z_ve7e2|a`d^zr??i?&YYUb5TGG{FLVf{3O}SA<{@ew8pTTp}QV1;0a?wsepq9*5v$R6dd8^H=}9a z_ak0Elf}F~*u6E*ff)TU8uP^rj>OPf#$X7p*Pfv^lwp^L4~$MbWOR7(f`lEgMl3|L z5Rs+7d@L1p&TQ!a=}ky!O0tb`aX;+$eHSMRgr7s-YR3E?k^`wyNNQLAk5+N@&O7m* z5Hi15hI-1lWkU82!8ojbQNgSgRu)v|UFqzz%JIJ3bbR!{m*bPcHR9TU{TZ1OS-G@f zlV=*5{dh4mTa}Jld1t@m=5H016e@&k0^@rouCJO|_Yuf?`EbW-M}k<`qqxEwT!%gJ zq3!_tB7H0zMl(Z!L%HGLoTwC|zK)E*MdSL$l%+&XCI5T{U8#$R>k@+j8s-L4Ow_kH z!JDF=te^MJca5>jzta!;Xjh;=Gd`rOpIrCpkazcgW>xR$PvA$1y%+-O+4fPz5EC}E zFeyw?N1&K4Df7YoiLk>#QzXo2&_f@Fn_m41vE03Maf~h)p4aWUr#tH!aN_%-PuXW$ z4Hx^oddX{d#zRxr&gsb~?vNP%@2LAUEW+y=1w0dH8f-?J+jTuPb!Ov%w%Lusk@VR^X7!Ilkxug{j=G=*l&2h5$Ffgg_n z0e`Ca2!6Y|G7JSma?L@eq>fi`ag#WYrdvgm1U6ba?x`45%)JAU38b_%HKF+(31ihU zl{n_&OqvyI&brKh$JcwMIh5;nTr?YJe0Ta*XhC_>Cp{QXH3d_OshY}ZKdfPWw9yp* z^^3O>A}~Jiwjqd%0E?&;h&4T%3f_PwtcYSHaXBA+;CwVv7X!jyx*-=wHeC}E7Yj*a zzdzDde+D-a*TwWus84Qw)EqY*8+&CZ8N7 z)F5kS)}~l&(;`)jhiAz4Si_dm`WFQX&Yg1=e2x2@qxTO$uO&sYlk-8LTXyY)ijIt) zz{a7ZR>~(!e;q|ONRaa)$4Br&jEo5$iRhQflMT8D>UXBpEYz2Ywsi1&0uN1c0j; z?#c|bd<4j{&6B%u%VtZKCuS2shKukRz`7$NMMFLSvUul1bf7S}fvVHI%asSGsceJF2vA zU%AHtGDPW>{DdA8@P@0e3p|AZ=f|J9xt}xgtk$9ORsn%!xSJ=-vWjcqP`85Ab<5{l zt}%=~80b+Xes5xHf`sS?77mkJk+S5;sMPKtwucAl9wb_w5FJpMZ$Nb{SfwI}zBp-pD7u1`sqmnZLS z+})5yS`4?)0n9gqXxXuui3*825^lQoB-l)^`+8{g9WG4()#rQ#4S2SdQXY#jNzGk= zUX1WShT((X%~Y(pp|UO}%pUhSLfyb1nR4LFhD-e5)cB1m^odLmvH(>TX{;;1|mQ{KqKYvyff*1Ll#=#0g02b7&`BM1cV*(>=1amb!<6TQ+U!q(1><= z=OZA@d94b=GU2+h=nd^A^HSAu9|sd~5W>)B44^s0|5d4K=bvtyUTV9-jXqS?`z~wV zv=aD$6$}H}$hyO07uo{{(`mwDz#z<{G~`$56Z@uqnYt20KkxCdO5oDgQ{x$~BRhLj|@7XmChB@9SrgJx3Loj*tVGCd9eR|Wd%w7}CXgoNR5GqSO~SD{uyCj0nsG)E8n`Z2mv-lluvw`Sp> zzZ_--ZaKNxKoUzM+#R$3w7>P#;^7J2MUg+lMUg+1>uo#(l_bkC;mdr0L!qd&%lME@K<)i46I1D^((JD@BnrLZ9pB&*7(5Z}5_=??a-TYgmNJNUGUgegqp{!2M@8 zsDAH3R<&g398Y><-1qM}6F;KWJ&(5Wqxo*(0GT&cylqc%?xxuc87HC6;rf&$70Qo5;jPUqvh z(h{y{eqCy-0xO&AkgVm>^4DzR+);)X!+%CikT-elfg77t(3=!F&vr@4T-81jOwAQ) zuWq6EX00+4&#Y(5-{={eOAwlV4{h-QjrD{Yi|d1K7KX^Ly++KKhz`w7`+H0lM9 zuD7}d51*lgR~Q1oF)K$08yCc*9M-8E1;T+RI~!KVgs^S2^#$z)qBk1i0A4wGWQtrmY>{s&9&vwU%kP~ zmdL9L$gSav7JrkaYo4SJ=8iV}?7^#>=}`fKF}hn0T^DA6*uZpsI_g~uLihAkULD$E z9qpwFBGTs{ouPnfQy;E?Uq8Wm3S}wdhYL(e6qaYZC~gY2rOvIP0bA2krjG3y07ITg z3uO`xHIhR{PjSwEwd-K~K_Ygr@Zg&eV+UBhU;TW; z(!~*}i~y)MU21DOjzF|_+1$Xr&oQcoq8h5ezlm1Qv3-T)HLBpV#WhjOEX*?Ix~#u1 zU%G0YkQ<_kYjk^^%+IYX*+b#be-Ce)v*cUmhm-+#@HXW7XH$vQ`_l{xB%SE{C>NyX z%-K*BbJh6qLf~&ivc3X?JnA~nc^~J!)y78|vH^HPyzdcVJuunkh*KlxMz8n`AsqC{ zO~wC&aD|yzBlqLq{WM`36aC~#Yl1J|PSCK3mWJn8Tw#xNVI5WO3)9PT&>4ow5R?0( z?n&LvGS)Fa;%=5Pt;pDO>kDY#=OXu5Rw$q?1JlR$T;0Oxsc8Hb5jJaGg$NWSJK>@x+s2+)X_|t zypbiON6jkhB>9$XT+7r0UFcz(;D`ZIH~)t= zG(EEtXM>-BF$QPHjn|xrWfASUjbOe#mqBg+%|N^Dbl6Y!;n(5k@j+(7@NvefKiz|R z*h)=14)EE9+QL4`96l)UQ=%5lwu5rlpwpDV9%MAst1UnryrqA(fs1PXsB;5a|CHG8 zNJvYsdI5{<&D+Xqq|0p=$eiqEeD2>L7GF0^nhw9%@CKSyOrEXK5&bNAfLoH0Zrq5# zENj*A@lMZYV9YmjSQUjXthujG-sRBRLo=mlpY;0J8Wk=vi33g;1!}(|sp5!Qbx0kk zlg3Uxz#4s~ncQw3s!u;)eFumAAK4QhOpv$#a%rA8Ad;~E@?pc|Af~|oL0JiaFHeWR z^I_{?KtOE&k_pVovQeNAfXweC7>WYoXAx~>0vL4OI;cQd5>W%c0PV3q8cKX02x7>! z^D<9U_;}U1EfjWsXfj_f0-_hmthiT2#a!#f1l!g`Ry`lU`vqDb z)%A4tk1sn5$=>nsU(6~>Z=KKtXap+i%7GYUHbcxPg8;;b@uK7j0J3O!l-5{E+eD=q zw-|uvW1^#BfY|~yz$PW=`oMc9JdTlcoFRp z6^_-M4yqNpm}YnsT?Oe*+87_SD5?rDW;{YWTDplO5|pQ7 z`izP;h8B^LllLz>fT>opocDY!2ah2;YkUrxpjU5cOPiNIl`2SaGqyqu_ijX`R+QOl zp~guvE^$yq5Dn)qmOKfe`a02A0_XQ=zaYj4s}TdieiB>DmcTXa+mtzmN@y{w%^!qF zEcF38V>C%Rl(2$DqL_l!Q;A&}$!#pn#ApvKkqh2j+iSEbwsLS}5PdLSO@n4j~+-_G=yeh9vpi_T@A4O9BB+=i8;{)uz+*2jAgx+8g20%$Rdc6&Wt`-QzbVrlUZFf2Ze!*Wq=NHyE~EzO0rCc>28={8LsoX%Ei`F zSKmq55u22(&a`dP<#W^8=HvUeW&Y0DZ%grYTg_5*NZF$4)7|-q--DhdR?f?Fy+KRcxq^ytnzuJP?$?V; zYiZ)AMJakYb(Jw3h$_9OLtSV;uv^1~B~PQdeutM95sT8U*(9l2rnhYtB0pKCD;AjH ztwf$>L*{*piJ-u6S9{=gS7{)qGI+}876Cv<`w(SA*==_)!DTvZcc`jTOVmdQ?F^8X zXlZufVAzB`k&bBB2hyQmzBZ5odjWB(DQ-g_TRPL{?R2i1m1|jhw_V9KeWM^$lO-~^ z6+z1iXK12MFCSCf4?|2*c-$<|AfV-z%~;j}{u8F@9U8C@y(0%Er~}5NX+c5@)Bsv7 z?%?bJ*my|#BZsK$MI?%!2b_e7N6D!u+MzUl=r>I z`;a}g2WBIF;r7>|J?Wnl-XMlP(*Yj#w?nTO#0EP=9FE6~e-~L7L=wI?-60;4)ei+T z9|UCJUlpJTJzE|19vv7HX0G@5*m^Y&+d~IaCXZSNdTkD|Um5^kF`psG`^j9o(B$+k zxm5H*F}DcV!ZhIZTP&Id7Sl}Cu%f&#aY+SWc9Ltn9i@ms9o=wWz>XFr-vM+w_^q0h z$#}+IcrF!FZ)))c(`nlHT5&w}zRWcJM zmxJ(U=-r{j?LLU>+yO4e1oVoOnDO;=<}DuA!@IK&kRH6Ix)grhU>96yENkPMQGL8u z-|8sLrf^w`HMA|XH`m0ZeSrm-!ZOb+R0ZXFdaMZ1W2hjGVT1vId$%XCarl;VN!@V( z-IccE$r(2#_UU3Y=9tg|h4WS5x3(=4`(ao!>6Na%4(AP*TGvW41yq&^}9(( zhSL%nk4Rgd(=fgpDZwK}v#B0q4W5usgmrc@=W`LZCpM0ie-@Z33Q!= zzfII#ce$rJLu@U8A=n*8O5L>Pi|5O3wdnM#Y!Du{P4uIn*I6zsJ^`0dgkC>s%uc_$ zLDC@1ko9{stytKK;3>b4r6R@95WIdsf7PRpQ${_>v@naoprb$!d_dzAc0pW&Jdatp z5nLGtMx7)rcbX`dmu$hZ^g75{nwDEhH_A+4qG{Nh7~YZc21*+xQOOnI?RXbxCno;1 zM*@vndH$|)=xwR%|64X7pzr#dhAs%rKhGMh3j+L~uMIZ`Y57;GOBS&PxrF+sl~(Dp znmL=yZ37|+utE_){3Hu)-S?0&s;uNID{V?#_6sJ^G^Z47Q0IOiEIdw6A9q}FyG(4C z>E)9`6viJg`zqNz|CLa$g5!bE&c?jub!T<7H2Z`Pgtwa&PG!6302K~zE0Y_s2A}kE zolvX`-LhYI>^UV__-HAS}!ep`;xl=I1HTmP%lbkDZyyU(?ja*aeOIQm z&xaJTxnRgFv#_{>gS5-aYVz<^ZRY6b&h3e&HBV=N(2pYZEWV-Me(5PCA(Z6QK%s`|lK?f!@Nu3mKe3}m(0K9=*_ z<{Ehi!2gKveXq2Zf#%V7s5$u^YVsskJAx1=$Jl`|08F$>DI|Tvg2QT|a-iTsHY&$0j>;D5M@^MzZkI3Mf7OEsQx;`5QjLxyOin!`DjCF6 zZ?S#TF`VP+RTkA+6X#XolIt^;BG4x1L@v=ti%ZsEb;8Mo@6y+uFRc#KdTGH)tP{1Lz&C?&&kl*)q+*5PMJ71l_lWstDjvS^loIZ02K4GEtX4GCj38 zI+b$iz>|wKial@~9J}*uv!~R|x6yW1K@WA|7=EwKQz z0hp`J1ek&`rzFOz+T-*y9ka@0&O?9+VXE3gC1Yt?x{fm3;;%V(YL#--m<-QN`g!K% z0=urC4bR}=a0NPq8!I}=gj&yER7eDvW?AFB4=)xFdbB}fVxKTJLwi!#<`&b{sqSD6 z`jm;x3N*@&=_+0px9V*`NO=kE5qd?h0^Z+Zy2?7+AxlDaK>6CbU1pd433@gK4YeR^ zZ5nD_j}5P8hk3VwD-l%Eb8)=?;6N!JiV8+WnKxAh<|2cUt}Zc10Z5n z2ra(+D7XaKxOfYB9<)56F?2xMC1xT2%38^}ua;ZoD;w<|x~0|I!m~hIl$96n42H(! zs2lTXD;7fEi@!}PgRk?I6=@4hWDd_rIR!8nXp2RozJUGTapx%S*}hls!nY_S_BXhh z4+sdz&e4qC!pMXrxy=y-6GYD1IQeHB=r_PwS;y+T`+&#Qb;Ekj`OUzlQnh(ibrBub zkD#uQpnOoU0YU3^eZ_cW3Y(lXmG-#+An~6g1SGh{(~&kU&`tJBUuJyAlw=nszIa_uj;V;FU3Z;!T2JS17h@9S?u;pCdf@U|*llg$_^% zrL#g_=czPVjTA|eDEdujtT>}rdaj|uiA6gSr)9df3zx8 zOh}$uBM%I_`ytt71INt`uwU$Q0t?gb{(lhf*&n*V8{d0@@p~_b|6hB-l0Lb^@jJrV zvi#lxL*4c5wrv(|jZICTrM6nlnpzByei*c9>zb%}V9obq=_}_knZL-+j1{kIK?Uvw zJ03;uf848bOJHay{mN-gF_?NeOuow;-JgH@{zF_Jg2Qpqw%vwJqSa0nMN^}#P**2i za8)jtpv&#t8RWE2q`!jQ7=Zk0eN&e0k~T5qPQ=8L==8n|kv|*omPmO}Cdn?K_fW(S z(;bO*(mfBq7B^3<9PUlSDha4jCE!VtAOe2bRu?_Y4SCI706kI|D3zs%6TZ#rR7xIp z0^tN$DjyHlE8YQ?+mtMAlf!G;9m>30tLvCGDRS)K25as04JLbkHLSQo&Gm8~(rS~h ze|-@XuYZW01q?<;C8J#h5kar}Z_2-tA2Iz+&V>ya<(pT67IZ2w$}Ziz^)YId2T^^d z`y0!HhWKO|HVj^Ud9zV=ZEd64yy>TV1e^{KH#eiD${Sr_Ao;{ZVF31J8KqdiOcm#z%mq^m7YtI=-*_>mwv@$AJ$*-j3jHk!? z#cYE$vCKIr@eA!C=J@3WSN8`dKaS7I zFrGSR(Yt-`E#V)4#W!G;do-$YuK^#7eb0=%!e}U3FoRMUR6%0`eV4|NbJuY-E;GP- z@+9RcA674!X)A^prjGq+n9V^q%3Ep}OoOl5NEp6rdpO)}H2A`8WqLO+!AAtTgNjiv zv2w}y>=$k_sAaU4*-?d?VNVo!MF<*B!>)tV>S=m2Q7AFM)R@V75H#Wwve=`~l-qU? zj7mwcAEsWzuQz{Z^mC+u$AIkIoW!Zhurstr8rk+Qs3jz92J6w}Vru}dZDM~Wa2Lv> z^MLfkAdPQs#YvT~xpmv$teVG6+eq?>Q07WWp=#XhUnnSq)T+%DJ(+Xl|M?2D#i~ z!PCf26@o6feQsr^z8)ybOgJmdo?gd?xg-z3Ys>^4`l6>&#f=%A=IjJLb=-?1&0jH+A99qpqQ6htDea} z{CuPjs6cNb8bJI2rb~xw{*^tlzRghvHdZ%%oppTf$G7)OeA=)YsX%Jda_tZm)LX*~|7kz_Kg>wb!}94wX+(TJlzkR~o{1g=?= zyjT&5&$yQxu)Yee2aEvKV7G7jc_vTw-sCF;(0_ii1FgNZDc|D)?+Ran>+oH2@tW>Q zZ<%1Q0W#lybCaSn%Y(Nm@Y)OZzc-obS(}S8I*t|Tj>5-$CfsDaCVp4no|TE~?UAz| z?2)@+#=XLJL*Qe(R`HtbQGvx_Ck%h8HQ&atT7gNAJz2~zmSqM2KeP@Hcr0l<9BK&x zc+vA>11!YEb8cUKt-mp{Y|&)Cc@eH4V#Ty{TbXt;mxzOAM--pxcsHJ)*JK>@H8vle z<8M)_a21>T8Av6ib}er%Rf88!NgiD>g&ss64Cu@l#`42=>Pco1T+xzBrjW{J$5Oah zvh85&_`M04TYD_u`#^pKX+BeoBb(Da z)m8MOm#vA;46_@O5Z2)1R)BHNCi(%=x^dPk!g11V^Yk0sp}I()mkX4KZX{tF?uCDV0=o))pZoFtVC7R%@uKGFQa=nK3; z*JQvG2Ozjy_TgeCdk8>PHl`0tkPSCIonK;@gao*o@uH9O_hcwh`Kpv=vM; z2D45Vn=55Xq=#`!QwouSQMX1@Wr~wOExSy8hF$38Lw8F!Rje}RaR!tA<)Gm>tO>x; zsx7ebPIA*ywWW|$HU%j5$taacsM^n8sk(s3rGO7_V^4u(*S&*8c8FMd4b}3+;Q@9} zoVX&<2jOW~f3mG+s5c$V2W;o;iUt>r5O$kbqdem_(dSDR6?jmTFhT|50`L|mfR`4$0&o#RdIFnQ z8`0rCd2OHC)*c)?*V{uf1>{;k<;4VA(P^H6xT^awXtv{YFkBoix&bPbSJx#OY+M{q z;GNABM}^upGx-TG>JZ-2t)loiGtyS+sh}G4+qk@{Jy$#`!#~HZ-tUP_Q8^fyW6-?`t79%dZU04 zxtn+HP_vO$0Y!@hTHIv$5(<*kj^hDuBqQN%nGsrQ=IuP(qZ_om7K>4*TM3b9_+s^# zg$EKB(!wkijBO_dC7Ko?EO*u5xuz?oE&{jXGtIj(z(XTX#cmnsy~%=vZ%~BIuLZ|& zPfG8XU*?yn0p0%k0v9MxE*|;wbyP0Mpej0pZvu-C_iq}DMq{$q>GCdd(VkdvA(F>L zA&Lrtk_!GhhUBLrw2z^jKatxV)X#fU%zF~d9oao0wNy|^J%waZg(x4PGaT_Cyvq<0 z$o#K!MEU1dHh`_x^3;JtgLhEg-35QdA|B~Nqe5M(XF z?^y0vxhdtbozrSfSQl}m4wyn!n$Kim|2|{zO2=MOeV)0_*0@KP#yg|uv{#3vdzMC% z=^UKQ=rlVi&uWmQ7z>=M0;OTACXbL2v7;9rEtANf;;X(OqC zOPXmYQaVsV(z?>$>{ibC(aL2YMH;s{|4hI%-r{fYIfGL0=BZ=KAW(RS0hG7;VT)?X zG#Gn-q&8txM`0}C9A@&T(kjWWYDkLyDguB+vjBb%-}wmV4=HfemxFQ9mV#|u2fp~U zDvlzN@To~KTmiLLDL1+K&Jw!eRB(P6O8t@AG50P+^_SJJzx{cCbZ+U~wybb8xdY5e zqYUa=u`odJqkaQ*y4>@Ked!%@!-iNo*e5ws{b_sspW4zWukL$0c=Kl{NYZ~`h@=FN zrGME^yF?J3|4L3%$sj-e8(o-90pa~`bO9+71n0jKx?B*C|Iid=6(Fc^|L*E(1u25~ zuSRP?ijAd000a~+^$#GhodSZI9Nr6p0N{lBCI|dIT4!UvGP#YD2L92HsOx&zB@=TH^R&PnK7zct9FB|i63{zjH<|0a2~C49K6 zzjI^dS!X@h(2f;#_vz(M$F`)m9cvT;^ApeZ^58I7!wvt^VAXTu8un7@#)r-Ao4Nzw zAN+`j;(cu0VZYn^frfB<7ZZfg2DoA1E!d4-mk&a?8z!dW?IYqo4WhnbG`(}M&Dvff zPaRRf^OhVeR`C)YG}~anc(Qml^5Gqxyj$+Kx=rTKr#x(cwUny*hR-5>}E(%s#NbazX4cM7s}cU`2VQ$Rvm>5v9d5$RSWL`6{i z?y|oAU*7Nf@SL4FzY}vN?(BN!++ZB&SV_-R@e3skW?*E zZ%DXMqEHr}=vkD3>XuzPDRXl65_dx4L!1El|c(QqZwaiB56vvVU9Bm>EHyr$A;AqA3Vn$Km9^gaSuD zP^mRO$bBQgWp1R?YV#p}c(~J7t<~8HzmbHdQj{VdIvo7uEvtFsdoWS*eGim@!$Wz9 zrOyYEUbWu%&xX{7eXB@MzQv1*2;$w}Kl?#|Nk5v62`#H+BY9f&{fFZy5wDeA$P0p` z#Y<_)I45mW;W;yc<2%h$)zslvNyIpg-24qMsd$zWHQCp&zD?)niq9YWD(OH( z)WYidr?pJPT!|%W7_z%NrA8h0KAULFH447%=?^gnf94@v^Z!^%w@5&ZlH6o~`aRaz zjXf!|%PwJc?^$NiwzIP{W-~pyb%L5o>bySt$x!eifqA8fpvEalxuo=Ls8m@?>@3-e z%^(3(omqWDHMg9NB2KThRc`b>!&Du0!jO6FPm7o61MiazqYY>>Ng7PW54}67ofFOG zr5$H5Xt|>lnF1YLCt}a7ECoAWaaj_`(Xceu_W2<4XoL~92 zWGpcL2&&+3qs+~+(#^%Jbmf+zAr7HUoVm|5)(Ro{_D=tfDftSk-!7iOT z%WWdtDp46Ac=ZlHA40YN#6IcAg0l<_oi^o}T&cV!TqlhJ6ku=AbMIPrZWQV`w@cL{djW+R4CS!l$Z*P$pv%gr&_>0j)Bi6v`xXtl1Gp%`CNz+&u7=*FpK z78$HHnAqEzlq*g;1a#dNA|0v|25(nn94bV!%C+}1FKuC=E(JHWbLkIRrM-xy4;~TibJdD%edMp%%CBzoHdg%TnR9MZ|NesX-T`;gYgzm!er6>~ zhpkU?;@bL2>dT?wyh}z6V|(7geHPDD3+eSd8z(jg+uDN$P~+cVpZ+vmvXNh^`yo>m zDImuB^qtQ){NN#T&1BV~y0Hm_-bqiWLVjCxJh6`Z*5WL#GR35PS@Y_L1DtzN=G4kl zQIJ~W!hCXMG6y9+U22aqwmUe41Fe$yHM_;5O*?PMo_r(KlQmTP8qrjquz0_;-t4rg zdw$pb;CQkMH^XN_-?H-8_fLia9Zf@->3%$3F^-!g?S)qk^9hL092J85H$F=>LX(i6 zjt*yg#YRzk_@Ojqzx8=~#Pf^Rtv)+ajD*CV&~8#R;ts9k21iP`#E(YQK4%@SO3K}t z^ddn=E4xM2#{)(U-1ur&`a~Q2i;T}voa=}Z&k7Lww{E$iit^9x%QBOyT-;oOV(+X8rq6@x237KE)zaY zO8KP;lU7(6Pu|pv@7Ml(cvon5Q&G>PI00*&l2T!f*-gIUl*eObaeenV;Z`R!jz~ed zSSBCy;25=ANm6XhF%OrkPh(7%*D%{)QgcYIv)#}n+~eo&`}O?|Z9*yDP!qPN)^5*71d2NQ<~>=nznp*VJg+z>d69l!0nbFg zdIQxj)&1j!yk`ch-`<9uCl+0M)-K7%4M9&a4(UnCXrXX6m#P0bgvsU>fgrW@h-i1oWR9zW{mJI4?6fpcEDv^*HnB$XXM)h23cIj%8}P1 z1(SgVtjT*D1Sia|A1AFp-II0KX`hevTpBg_0y zGw`9WZDt{d%%zOuv1QM*$Zy}ENB9cw-w8jR8!-$gQ=wgd%2`^!`s0-n--^1r=R{m! zBnz>8C7&8SUU6sAJz0^85}qZu4EEqGe!oT;a#Qg>rC&;(y2Kj$ z8;)j?h3j$-%f(rZoQ1^q)Rn$QEYx)dsGB^E-(|U6)2HOJ9U0xnb;Z^igMQw&%5LX! zLXR1zi@vvf7t@8IV9?p*nNSBBhUrz*!97+DQDiMLgg{{Cg=XF8+?ogJ8(2f0Bp3|VQm8d(y?&jJt3^>DFKu1U8Ny;#`3O+!UslCx zNL-lW#6+hf{6yoQqDeZK{xfubrorbVIzVTH#H2jIJmI;|i6EEgQPqYi_&^j>NNK_< z@4!*Q>i)*}PoB`V&TZt#d9*SoxrY@U*>4Q=0Pk(7b$8Qo{fC<tA6*gYN6gI&6k04X19}uZ1&ZxP8Lp(DO|_8JOVki#MM^%E~*0($Z&>t z_!}(LxZ2usXR(XY+!~?doyt`y?!yM}3JgYsHh)jeHoQ_`^Nk&^K1CF;dg04S)qj6} zz12{-H$wxbcu=yprCsn#^WhUpzK|meyYt_ERQG?fntr(D%kQt)6w&Q&dO8i> z;^|Qsp=b3-geyuMEgnXDe+xmzuyJ&v>WTZ2hD(6X&r|B@OM&jAj|N{8H=MdqtY$*T1-Sg67iq$8$LF$5uyj!( zPE=TXt=>AE&6F~i`ziG-Gq+F{%jGJ!TzCvtVUP!Zk~MtRq1$XW$)3}qlA~)`C~olX~a4I4*Nbkm)geU7G!hrD$!MHT@4uP6;_*jntgR zbUoaXvmZ7#b^fAsb(Wqqnc)A^i@r$sn?D-M&W1l#rbE?LZ9TL+E0v717=!E$ZD9zC za$ee-D*mx$iNsd_h=5##`4A`IfY|2KYjc+1sC>hhR@@{SHLHKSoO2p;JES`my&QB#10Tei}*A z0hl&e(+h{HCI3o?u9#~){7u~R0?~c^M+;rnvHO77S-vXwQGXER%bka{C6&eY`eVqU z))!KrvgV;|Os6kJiu>dPiXPIbN}W^wyeswXikj>y^aA(c)RPx@>vT?h_`tWY^yKzD ze0Oq$laXTu>4@Mit0!WwrGW?94-qIx)a~z0nXH*-n?uywAyBWiIr{DnjP8zOmYz#!=Io@w-PT{^hlwPH5i{EX#bVYDCB;Jc!cdC{j^E4`EGTKS}@8y5JTg zeom4(@Z6GfS@9wCJ>pl7=35pdaXPp^jt$Q74w51X&o$2PG!>yS^{=kKC>u?}I3ZuB zI@TckXuj}f-%*;9ju~yA&=oQzfHFlXy(X?>F%8goaJBjqIDb#IxJMs$L>Klw{pF=L zihLR;H*1lCh3?FK%~*9GBcukpus}1KylmM~Cagt(5~Z~ciideYSrs~pAg&x+h=3=K zF|b}`Gv=(Wj?WPZEmp4bolBSZqj|oSCzU~ZRj|^Geh_ubpWF{UnyxXv+|EAhK|BZl zYYE!1T6w~lQa>q@uv=PsQs$TsQzSC#4sLaoTRwe0;PTkrr#ykmn)NlU{y0l zh>|PBRp$`SvMb9aFcWZ;)BBK@KS4bm5GdoJ+82-|v*FuLQej?hc!J*(7JjCWIdeI7 z9y7X!m+_d2LFS%Dv|WNrF+>|XV>U@-@C|#3!|;el)gaWw>vk0PWV|+$q7r|axMCSIq zr&{va=0N=&6z}Pk8>eqd>F1M6%L8s^My_{g^M&D-C=6&%&O&k)v5aK`sWS80d{bR1 z6_k~~5BTI7%qrWcQ_}`1WwKU9i84n7+8pQ&_OtRX)D=eFKwq*>Z=K zG(n8l=(UH$wPlEt!;JGGzQTwHp0xSNI%}%pxQm-7Q2hStxS5*DE#1h{a*TcqqR4Gy zx%l-Aw&MdQ1) z&{v|$*ELq}u<+y3H4w!e7^|Ne;09U@jkp`|cIG2d9yK{Q9%(WCokm2kIOXu#L(&wrwILG2SZ7OuLJa#S=J8Fbk*9NekhApU z+gk3lj&n5LMkX>JJ$M~&*&Ydd8PEic=x8Xhk1}OoR#-Kahx80vu8|#~GrD9eo zzu63o<5vyEuo4l*QK4_jhcr#xRMkeN!Ql9N`!WSF07;VB(HgfX~5#gIV6P7)&{82F_qE`ZgVX$?DrXWUAiBc;($XzcH=Uwj_rbm(}+n>sxwgZc4z-fSl1QvO_V_9_YH-tLj{sYNJXu7yO)(k>w zKOe8bz#gGSIph&Ow%bv zsDdgcNyZ_Ty-6sRh}s4VhP+0aGoOc<28OSvHlcftR2aqzci;OG#6V}Y-NRcOuCoZ; zdPd!o14GCxo@%1KsM=PgJ%ml7Ph^MLnI+i!$g#YWh8&Xh&h{TQ3JyryhjJQc3 ztn&FPtu7g&@9`v<#j?YpBRi^gs~nHh>Pj!ep>IfzmF6Jo?%W82*h{K65ea9F+0M?@ zXg#C%k%4dhv?RyRB-_Q6CJpchwiySip~&W?h|P)5hhw?ZI=s+qMr=1O+9|Dchfvq= z3nQh>elb;6p&HOP9<lN5$$8?Pvz>gc={HSFi2v^*(Tj1jJFv3#j>EC zbZKZC|CWK2s!PP=hVmYPK_Bskal5<;-kbP4>++sN%N%ZDzc%y(G(UyYZEyyteu73b z$#Qkm_2M1H2dyKYhzGNOj?ftVb*#}uRj`3T5>K@bIe|Z)l$&EymyW zj5=p&iv>J;&PCc13>sB_Vm^-*^6WYnSYcZkme?>Arw)lw7`&Vt;G!$~{N(j-T~^2Z z7U$eh-{n$eqrQjAYn;kdBBy!Ea%)iCFLz#_*ES;Fag>+%iDRJ2YEaewQ?tSgjabHh z^z5l-pieHgwAmF^oC1Mlc;%*Y{9UHI!;CK3`_(80?=oXE=9KT8RJ+Eti<&*7Lt{5U zzu)FW75t&)htPpCUY}m~^IYkI+Q56Q^@TCxs?$!)l?65(%jG?GZeysqY0gul&;&5k z)uM9{O|NIVKmT25QbhnmbxClgZ{HMmmwTi zWkDZiQ5Rh=Lb(xo*85Y`4466U`%zg{Qr;Wy-~Ggyl<=IHleznPr@o!hmTS^{QCs6U zrF~*fCRjSxJLBnR>cEQmshzI1w@@~3gU2yGzLRtwuE_eLiZ6ZX*L=||#Tc-`m&m*z zJNV)mBwp4#78NBF)3m-6%vBb`>1T9RLM%{bod+d`KzLE z$4qfdH{Oh>!ENOMy~YUpY>(MtrL`T&_H8>uHF4r)(YJN)1N8e2GjIEICmljj#LyyE zSUNsNh_O~@c4h>u4&@!n98*Rr*DE#)?h`L(be(<3GLkb&>G$`4SyfUed2V+9h?8|& z_uWy%QNw46Z{kdCU2ZXCKia$2+(Lfbr&{*L|6ISIdbh>rZpf(k+2v~i?#G>G9&8!Z z4@7Xqm3b2Nl`ZnMQHiqlHA{3~LRD>1`B|C5G#+E^vQ@r>B}~%E?)S45SuR=V>lw!s zF6JN587B`=62V&0TO3drM?Fs<47FBf+F>&lmfs(a8)szK611+Li*U~R!d+|e@*l;xp)92@lrmy{3daL;DsX5=CQVkMi(2JNnB|o4vsQ4(Yo|<=%H*W zM9>lqwW_V9dzD;_sk$wnu2yA>D(UeL>44bpE+w^b@1~xWa%WF?7Hz7NN+ye!*I8&# zeR3^0^!XItv_sDD=BtTw4|M4b+IP0^_%?ozwH2LLkiWLze9$1hm_S;oUX~G*I8K$n zl1YYYvS{!B9za{iZ#{U&|84DE;kxUa>BV?m`zJfIZEo(I+NM#)k`!H`UD&2uqUbSf z%?|VC`@`exxo4W?UCgzkkMKi$_}EpgK1MFIURowUuZ!@AOt?}wA998Ej($xc)u_>Z z7Rd4_(+Oonq73+G$@GcV^l_k&teAzSUqneTUiUa&v1-Td9p)hBln;1VnP|oG`*bhF z6gxiQ*J&4qHfNU3REw)go)ewtp3<5${6z9&b?@ig_I+F5jo35(U@PG$mFtS+u0aS| z5i>MgBWrrieB6AdJf|xjT5of&g!%haesxPE)Zwj?pTaw9bNpww`?>9<4}8}g zd61^lT<@&o%R4G;gg39bC2DCzePL;{dB%bDIjXfi>Y-cw{@rD#ddAZF zfxHh4HfqZQHNM&6%bo~#Yrh2C^=e}+Br-OpEF|NoSD%jKsOOxX$cE~_=Bq8&`;xyf zci-ZKW-BGii85CdF)Xc@r0!H7PtJ+K zgkvis?4w~7MGf$c7rag!@1CEW@Jrmz98WdUbdpaK^qU!F(HqvD;rE!Ck^V9$fq}S1 z)4Um=Njb#5tc7HzK$a=WTDbU zok?n~ZTF@2zV~GWZRxXUnFy22B(j}+B-xsfrS!Td{9vN;*t#KMVJ8t zG4D@z`ZQ5Yl!d$DrQLp2$z~qR-^;Yj@5k*sE!*j!sA<%OQ#iZSP~$WxQJCDd*P0?j z9eQ-Ua6jN8MA+M%l|zD=fyj&vCy??$o4c(3>O1%Qtgov#pNOW+9y4%51CN&D0YdA@ ztWX0}%{y<1uSn`!(%*)s5};Z$4XeA-SZ_NDar z!AZTxnWY?RvgO3uHn+=`?M@^QTQfFilqZ>YIDf->7I&^C>Pd~bIo1Z2rAgmw?x*X% zY28oOJ4uaZj^$7uplX~Q9*LeU46XMShCM0lU0-+?mQcNU`?82kSR`0EEM5CJuGd5m zdPHhuKR7y(+l_t}ZuO=Yjb;1^x&Y<-(Ve6g>+NUNo0v|VXRK91c6Pk_OZe-a13b56 zFDnyO@|Fh9lcj|nM~DQ zEQpwdc$4dH?xOU}-qlEW|EOc1>>;<4ExUaoe}TbFW}Dgo58A}50p}jKgy;7NS9T4p z>08m)n;wMS%HwXXm?R=TJv>n$RUG?3oIo&|JO?C-eD-)`9JboV$Y`s65*_{(^V4VO z*S9iLE`Ex33Y=$>ORbtAU)#eMV=P!^=Ezi9h(Fame9C40*r*cQ+~*YgSjHz>^-_CX zR*6a2Qncqw@?)T}O-0?U@$~LyZCKWpg*4Ao#4p=MbOX&z=NIBx$i2&Znc>DGjVkR| z!qw|$jnu@naoL3T-A0@0KS8|X0=1CXp^AZBj9T?=>(7UU4Q9ICn*IHPmsy6?#Nl#H*p0<8Mh$IR9`%+1G|Zj0)9Ozh9A7G;B;I~=@?VDhD(eNsxKvFD%= zUj1u^e|9#hQtqTUnrr`@*KO}@)CbX(K^z?S{d3ITnssN<-efw_-Y|EkfCnSbkD;zO ztFt_@M+d?&SX)m;`6ezUyhXuxYVgf}cQ_AA_CJ0aO?7ZiFqOb5LtO2`=T$`2F#c-K zkd8ceT8yzvSLWej8-rNA^x zo}J6-)Aj+M2}`h>D*JsUW`C`W;}20>1hdfF-B@3d4Vn_HiMBE}sC9F(wR&)4n1e7c zw`|QqUxrY4OgE!T1mx9{OZwMExj#bljZ+*H@Wg8USTBK^WUcYI)lAfn;(DdIMJQe3a#Ab9JLC&owp!xilWxhda5N2Z9_Iq z+v#xlK>YZ!i#Dyp;a8UPGtDmAz7B`7#$#Uri|ug%)bhbz58R2gUx8-7i22t9T26@? zi3~i`zwosu3%#`(YIPHxQCztC?zk4we{hc2~HCyU-Y9)er~?Pb+^!L zb^K&;lIS_Zt6P0IceJjxT zSqH}twDZ-dRe!OxpCUgG4!4fhna;@ijtu2>Fan64guURseKq{{s$lyVPx^PpZ^56h z1ln2=4C|Hzv76OSG9oLB=oAE?pHbwyu_(S=C@5kxydh-`I6iu&kIxz&ecY|`T$t<9(sl361GrDOo5n zn{Cpn9`P#=TUALC9`|jSGb!r{OVS>uTQsz5@?A0|Va2ygVhK5p{()aL zPo!MUr%>~EM#DkHqd_kp?V>LxaMv80>Gb72dyaW@Kas>-L(Gu)w6-gze)lacF!Rkh z@}5Vjj@7*}o865@oVc-+8-t_~B<2^tl|wD zga%e|!3Gh()*{Rc>AOxob14b=aBXr)7Q%7;@FRv2gzdUUP#vO*i-e*NM4CWEU|L?7 zK%`(4(G=)>xR<~ogV9l2hz5+3*}?gqc5wdWpE83z z#018Vu!mT{=(at?6h>to;PoXA5OY`=*%5Bv&=Fz@D|b0U9>6H66T}cky`13q87Fu= zSe)T@qMYGzTXTl9u!*>+f@eR50msF8KuoTmSI^>{Y^MTJJs`vYvL`%7a-MMCt32U0FudR~a`b}h>i2@T ziOL)9%Oh`iOjf<&HsyTa{1P8H{_;=R&=;<+!xtW70zZfr%tsqPcv}bj;CdMRA)>H) zOMi&M^>eqsYzg0YfTuL?gPCABfDV5M1E-FUubYakt;c;AM?YI#2QOPo8+g)iO%+6> zTgb5Kg$_=yF-do86~_di$sa3-tBj>As~#IBOs80#{~bm z%EDySU{I#(2sLKz*nI}HKuIKlKmhR}gbWA^fZV#)5Z=630Kq~)2nEF{{}BgiS(rr- z96A*MA-sluWkudS2OGf-9yR5@0WFG#LuV2ZApm9I^>hWK0wEMg+p+)Rtb`$=0>bGK z*xUtDVC?r9f7y6IXBY&N7T)HRTjz;Npcx}@iJ`e+iXMPTa9ulu=x$NFgNuMrNBv*l z>+%52hu6yp@aQ3EXR-!XCIc)=5wU<54jN}F5MtzAIFk&}4uUY!zznb9TJ)cyq(FGepQ!71oJVM)A|Sj2 zJ4xUl4?$WMMluEz1w&|nx?V&QfIJw2eH|1}f|tf)U}r{&ARvGvnM zUN^DO;8or#I9Psz@daO-_xCCbyM>7a1pP*&1r{DbZowjW0LmhZVqe!XE8SE)COKu2 z>H7`rpfU^u1lGT5VC{cGfb^eEQgC;-cd^yBb+HVzwE;XsAa`K;?}dQAtr7#KArM&F ze;^rFE++-*Lg06T6|jtK!ifZ!Qz9|_dlc4+^k~k5p_c?xq|@Fs%>!scA>7vi|KZ?w zJVJngFb)n>>KmvQVW1ca;e^?ofr2(Wl!4Ds2>-Q|U@Jcz1dI&>ILdD}OHdt<4uuHA zq=G|1&%!m40M2_z3_xhepDw#FS#{+ELy`@C9j3csF;Exy6bgaejsdDL(Bhr}AQ1*% zivqzivbHf2098byh3UcsErYmG7;eB6u#A9!habY=lM_1}to(jm$qF2sBcTEwk#LR! zjDuhfgL48c;qW;y2CJm8f>#DIivo_ju;_b z?1MwG1#GbJ4cWto|CMFFwuUCM((_Xqa{PC|(%%jjq5&OeB>M0CT*X8NXr3bx0iZEb;9eYjUZ}-^(@T-)4+|Ox zp@hY-5LW9+i45@kMj{60;vlzSwI5-%nN-)cGyq#XT!TnF7;kD?z#txe|9l9Rk+o=% zfzOD@od3^1-j@!OVqkEEKp$@|^LBLCB2)lT0)z}^jxzyNe82>#CO|~4@yso=^3yUPR6M9{A|UbvdZL!WUhxdP;+{lA@!;?W%I225a33ocV>$7zfxGK7Xogs7sx@G}`UVi}?O$M!W zTEj(2Q~q>}y@h3p40xLt7ya+HAjb{RN`c5+M!mmkKt3DjgV4 zh1`Xe4`5}rbO1dK!g0M%WM*K-4Bl6XX`n1tF;J5R z;l7r=PWlD9fe+3F@f)(2#mGRD7cvbj84fcX`|q?-H?WMQf3RR0DKH#q5bSG#|0de` zN2V;yp#m=OPqrPnf&V6B`5*pq)iqw}f4cvh;p2bU7O>~?r-iU%hQ{EX zu7Lzx>i%&7q-9}TFOdO^cw{D^D--U;SSDy|ava#rgm0V3vcUDJ;uSIwngs^gDC>{L zA4nqIFF_4Iz`vWDYLqEtU=;w{RR>~W-gkl;kkF@rhHMBeaFGRJxgLY+xR9VeunD%{ z?nV++^3PQkrZ^9W+GfKCR$4YFZ~F#T$$#Bn8yHv#li;G33*KyRExqA6>K9uWPT>+&Crqk9m-mg%y3j#*zRjeaM*qZ%zK+e^LDRhQ~t+ z0mRpl>Hj4H+kB1vyL|}Y7lJ<*Y<*p$f&gJx;5y`#19vtc2Xq!(H{i8vyU2F{GH`%g zdrT@>OM3x!BQq&HBh){N%EBla{s>?_h6r9`_eL`=(ZJy`4F-zmA0SA}!hE>@U;`fi znK5__;RrP#mL2?paPuqQxF7`Rdkm2P`!PEgbgW+j0`TBMxB&IsKX>28?K)HJSss8a55Aw|%mZu746bX*0sB1o4KW_9MItl- zs`4O|*CT~vccAkrI7*yB?`{s>dnUkk9)ure;Z8n;;x>;B1i{DF+lTAsZ5~hunMk@e zfJ;7v7X~hdmD8PoUKj<-%OFM8^M?S~_aNl|Z&S;aL;nu8y2AG1|Jw$^BFX;=*=uQSe`sESPpV6SRTwe{?@%m;e9( delta 83711 zcmY&ZQHiZ$%$>-wlPU2wv&l%XJSwCy)$=xYu)?fbahod{hZqB zUAtHHa}#PnHwwIxEEqT}2naL?NT75gJT+*5bRuWm-*%MfZ+k}ZU)zif_zCzc=RwiI z{*%e45chx8Ey&crYO@>c%RlLWCBd5M@LBm3|aF|6m=|ub_YR zW;&98`U<1TKb@C_;-7BHe(_gNR^qz)3lrqt1^+9F1^x#vF3gGWSNB&y`)5y4ss2l! zEUd-!*X*k0`}fbAZ;aFt|4K4YWVu+5}XfPbVE(-8l_^z+C6 zNYIz8|JehUq5mCQ)HdLsKX#w!A9?+0_CK85WyZfVeSBN+&mZxG_fM9+0si7zD*?`b zdD`(n0k(fT<&j|jh}xr}0M37r?VoS}$A1t<1i4Hq3p9P?$JIVtf(hH;kIKXMwMU8J? z{FG64;($>HoW%|?A*v`9U#hcA zGc!x~PkIvlMeRtgs;W zE>?9jELQ$r{RUk>2hq6!lk6_xNcfqJI(#}Nvj?f@Gm1f@HQfVvN%@VV*6V5YYt9<= za5m~iTWy8KJA3DAYAA7;uzAR5$Mrh&)w~U@=g!uV0-UK&%vC;QUO)v)`N zBI-C$=ImBORo3}0lIY(GLf*RjD)-f6s=XjgMRZIu3ZP$^M1Csw zc&t^>bHt>sucTIWdI3d8MrjxZlp14XS>({ul}))mzaS>Hl${|o&Gl53C4-F};xpS? z6*5}rClT%1b64s%;)>llxVPLmTlr&?ZX=^8>QqxDI;-44X?L@>=JN_`G}Qc}$gw%q z{K96{{Q?=W6?%W+S(E+2$*UY8@s;-ZJ{xiosMs*b9CPg`nhkvab;f~|NB96OX}Zq0 zD{&CD;V{>d!+?~Z)`+w-aSrJS@sK4x8*w&C;>!a{f7Gr+zkMb*jd2!f_Gr-LV8jbC zYTFJY+)qYuj2GW=(B2`M-C_2t${lC3kqT%mdYw5aq6$-DM0FAFLwFh8=}54hrnS2j z+rH({}> z+uaOT(p8Yu19KcdX1h?y8kQqa3~2zDOxPy^`3!ks!PKk-$VB`nwB~`+?aUCZ^oxm+ zAEwY6oKtpAVYdV%P6g=@`i?7<+6J`Zft9}leS)Ya%!T#2|4kez60~$DUlJ{Ai%Vpo zGLkN(ERhi|n-kUNXXHv`xs=R2P&VzwdS7ZJAN2pfPM|^lKE6Dgq+H09ARw0N>ALa& zLSQa0j31u)$po(pePNvNDBs$?YY1K03!p*J z5k%ocns8BL$Vdro2`M@dCD0YnG|)vZv{9I_un`|;UppP6BJANl!^^Wfc zioNg9AaqI$4@G@Qd=D>kaKkT&a9>vt=s>~!ym17_uy7uO`+6rW);=3)9WpJBE=&*(D;=0S1`AE0rf z^B7%v##Jc!OQP;nVSC18JWD+lCOU)iStyTkDrwa)Uwj4fy}n7HF5Mw^bhlHK*3=+n z$1n=xLo{ln**=*A{WNl`iFRBC>f~$V0J(xkv}$*eL5*89q<_6jw1h_QKpY!&_8eY3y*6vtd z;Uu2^gDZG+=FqJg~Nq6V4aU=PJO$&GOD67)1-zkgtXHTds$kS`wZmU@mkTCa9faDE%nmfBk3rG8sCwsl^#*eDT(x2HBhkSCV z>&y06msP)QO5`s#G&m>paW`;yd5|q~w_QApdd=qnO4s1n`i;Y&saRuIsH6P1$5|fB6On&Ttj7N^vPPOxq*C&x0#k zHsm`I#GOMwRu;RDSVkb$72;d%nA0a(0p%Mc4gEL-ZL@qImJdHCHRk+>!4kE)iN9SV zJVEZiWeK@;DFBI3Gwv6b5wS$sZJTDwbGyMJkwClOpsQImJ{Pw=uC0hc{4It=>|e;@q!d@%6WKvvG0S7bHbZ zEiGL)Y=ZltUo*|boQAw2(X-q)W|n=uWJjG0+2-PR2}tz07*WWPpGJb;WcJUw zr@EZ`u_C?sa9~2A{erw%BUKd1@Eow_d}pkoAs(IZj2(E0*aDxB9FUq`bWqdRRy@v! z+D@Ho*nt!*H6e02RIH?`7}ABL`yU+@e#uw z+xGhHeOhQ6=u21?N%;fkMugbLb}Y(q2%%91#DQyZ7Wc^H%#rXUavW6O7Jb8t#V)fH z98U3FQs&Qy$M!wgCAGBDs27eTIT66yrcqiUAYle_gx||l<2wTZ(xPTgR48en?LRM1 zZS9KnLmcd=P+}Qv9UK(Gm)4W8x000zn-(M&=&~q-n!mx?FZ{x@{3#v18s5?z+Me`gNQxArZ5Xuj_DPbm1ThEO-smWnBusjmBKQA|;+PCLLmGk_ zncb9IB7zNhT)0A7a z2kkl$H;Bk&&NO6j==IUeScR0tKkKs@^3&*-Ku`luG*$T^@Dh7L2E0faMV`GR{H*kJ z9&QnWp*6z4`eKu_Do42y(RMGq80Z4Wr&TN?6y7P9l*EukX zydzsy#Ua)Wa&{%Q&g1UTC2F4i3rm4fmMm#jh81tcULmh$E79B#CB8NPp<=X*VU$P5 zMtFhkW?r1ka?PaR{9!R3Z$!$84{W>@eY7_v2DUOR#5Q=i zI)zeL$CP8W*+c->T*Fg#Nd~YXI*XKz7VZD+J{-vox()Qml_I9CNALY|JXldr8|jx$Z$Tbcdjz5sUmj9t zvu06wN<5BAZ(M@loZ}x85Ttqh3^CYK$rG=nnKs?E^86$*j$$z+VT}>YkEfgKmS-uq zD6(mKJ(0MfJX10@&DJQL%)b$4*neag7x7QJ1BBtge&3O794IRwc|Oz{#qPo zu^^&A90x1IGu2UM+8lh<=|$T>?2)8IrtqnM&j;vcTYHwMb%q!9Lrc!1ZQ`V?&5>Qz zbKo_j`uS1G`%}O}6qzyEHC}~S@uZiP7G{n}976^Uc=sm$m$n_Z4*|1_0^oPjMW`f* zG}nnE9sAHSt+6@!IX1ha;-;D7nGqjzP~&niIRH#^G8O43-9k0gINXNi_N`Ze3Gz~W zcZ*hC(;wMR3?z^2uJ$~!Plel`x!U`2Q_?7eo>MwWd5jj?!f(iJKI1k@DI8-#EDV4I z7$fJZ);N1C6EQA_2;ewf>Trls)Olf_LUC=vACZiVVZX;m>5EW)mgd-|Q5;O@P_v9* zSl37TvY@vZ{NJMM;U=YOTsz7oSB|8Fkv4WxU}dIU5NclBT)eyz+9?jOj4s;K#*;)B zR#@?h&Nj78zBjgV4o1Y)xtlqvO5mVKcj3`SI420m1p--OkAN|wANx!*X)u%`vQ0=s zr}ysZFJQupm`m=1F7<7tJqIS0J2sheGTng$akQ578G`u-9e2g4Jt(f2cbn^O#>_6e z2hn=Najzd*LJcdAAya;v8`#)!Pp9(2GalV8UEc7qVZEarVcr?_#`H~-Es_L#t`FPn zfvZBcMpA>IbU?9+H%A#0oU~oBkcqFNZ)Jr@l!>~$7E7{&DmgWKZaKn@COlJgZa6by zGL$Zh(ULeb{hc@(Cg{{h&Z)o%mi>4fje~B}dZs7=oaJ4=bT3I%;6Nx2!|o_aPU)V? z1Bo1u%PUFXBvEM}J8kfvX=4@F?Rt$5?ZNuUUMsCIe;~r-8S!V7XV=gU+il%TR~Win zgkO+DUWkLi$&a!+`A7U%hFc{0?r=>f9vUdCDB14%NMIB&u~mkl!f{P)XREWd+1njA z-K0owGTY}tr6^3*$wtJCMHG*P5TOZVKONAvqvQfKB$Md_5($un+_0p-NTGgV5GDuV z(I*(g0zXE{1?GpoA{F)b8_Gc1k|X5^w&a*a1&49wKaJzeYJY%QtF8z!9U~zkhPn|X zx*YzFJ!J-Rp**ABuI-0=yqOM)Ub6hrfGJ7aQ%jir)EZ=uDsbu_8l!Q1VHfY8WV;-h z&MMkxXBaM|Rv11~+05GSx6j&FcVS78jUSmi0IIObW0%KJEZFN%jFTy<%p-~ki-h!c7S8XOqH6a#AbqlL%D_2g zXrecdjf}BTk$ZuOoGEWMgvB2a%V&;F`gQlad&QQ?4cJPt#3gjv$WU(=P$y$AQ2!cm z1nzROo73c0%BezUo_%BpBjp5iX0fOei|6?~1cAT+%lOG=sGHf%<3wPA9C^ii>DX1& z(uK1w()%hAA4f8wWoj(P$ywt8Z>8^tAbJa1XlC-2Br~*2FPQR~qpK@a_w43@O>?Dm z3n{~!u0c&9joiwE)lR8pIU(uDnzroSz!c17Dp~eY4eb%7W$OtS^UyH#PNN8Ck#8OBIEc6#5Bl;3aLv$qCuv3!kYU#Ur(DYBSqrY`e9L5Hb00WAsyV z_m$2g)i<_MLL_CTEaSowv-K&};U7%c`FUm&CrC^eCN)=|Vt6MfL&Rm&V18ms+`5C7 zSShYI+RN%m3Ev2L#{x#s)?4Oey#6Hq4C!OhjQl{j(Am(%>B7-O&XQu*1~Mm$r%kI! zN<)uBf5gp3Qi*1{)Ug<-7TFa=HA{w;+@vu+kGjq5J#2^cXj09Hu0=H0q+$n_1G$Bd z9@EFiRQ=IPgcj9yHU|pWn5cTqRc|3-idbswR&K=js0WD&_ws_skND)*`jhUUI3f@)1qo~ZLHSk=+AMtO~ zpfO2CD8qHf2@}bo?V_=f-`3Fv$)*#fhloa>EdcUqtabqH6NMr=;4mLIngFF$oRdo0 zk1c17yPUv6;#@e?rBU^F*)$Uh0%{h@hoHTm(2JClaboRJq#@_pl<~5iwG^O;2&0SN zI2^fHeJ5DERLGiWy`78gw3hNDg6XnL7LELu_eCtE2R(dQ83@ks3Z!ASk14B~pAH@T zZ0M84Xsj?kwD2aIfoPzeXnAY0CUR|Eu|tW~4R@(JWNj5m1Ri7uHJ}WgrArc2EmG8^ z7SwW`UTZVN4Ay3P8(LlW;P-8s+o74>h{D#(T2zrOi{?!Pus9<=ceTc>Kit-^|&QJdGAEu(_*-t2310fb#bh7W8gG$|^0XE_au< z)7slZjH3VD9B^y;5J7%|$|>?w9L>(BS4XnCot$5OL@I=_kW#&q6AH6UB(j79m69tl zatBUkS9`R%g=k%IO!ritRwMMNjFZjYPNev(=a0*h+8B!oCUVo6cJjf;vkvf?EOnT$ zV;`*+depJ9z|H^?pM?*2R(s_rxP!Lw(o7SZ>aFq%NtZ3Rd&sEZZK6tNlaK?yViz$; zKB2l&!JMh_<}GDyc7d{8pnGJ?TE`^QZ=;yW0`5^Ja}4a9lT%*aOa*W1u;G``*m^qO z{(NCK2g|yl)%i5b{NbfBQWSRQQRY{xgF~p=-6(}$z*&66kW;a8?mFx?<(Bt+h`N^Kf6|`d3*gv21109wQ$%P)QNWDAR&}D6E?-?iK&>GHOQlqO7O# zJACmG_0esEgD@mQlaWBR1>Ja5VC|iBfy%VNz{bbu8iGBL*{4cO@rKjL=(udHqcUsJ z0Fs5~EeyAORvpB=PQOQ29l*y=pG_DUL51p=+nP72y6QprutKo;mm^0@Qtjs_@Ckj! ztc=B?bdGeB7AIUSgOn8qn1EzJ3p^BHesg_EAFY^w=Gu7n${0#zvr*hv5$w6|%uzfL zcj?afDeF{p`MgK=$}l{B#}84yuo1pE2h3kZ+HbMonMpWcbxv!YwR_78b~OiLPoH>p zzSbq&Ltt(nd9u$W|HjE-e8=1sdF>f%+cA0L1*DI0n7`lkq}+gVB_99UXYpAE7(wHK zqxW5)zYAA#LEAk0JH-SgXEE6Pkn9(gXJmJk5H3FEvWX)Mg+UKZ;Dvqtr7FZ93M^I< zn>MkbDOOS`j2jSpdvafFOO_~1Sx@fdH%BYexD2)~-c_v2F%Y)3zzfNI;I0mRv-CVs zsM$nvb@^k;6>j`l_5tw<_bNY$ zaa$NIxfjxt1Uh-Y;610X-LotvYE%1O(!w>m!4TK%=_O=}s z-L0wN0ii`(Rcjp;AuD}l4Ck&Cak~b zsz9w0jrVTMj#R@9GK~h-0raZ_6FTpOJMVO-88DqwxoErkHNo3CS`ZsIX=v%+azE;0 z`#W(EDiTd~v|#W=F3A`%I9|ZXn0ksxY*N}VOA2%O058HAdsEcMi7<~$x;gZCqyTbd zzj8N>)d-=@h_(akd$b;s5iT-}xh;O1tbstlg|si)+Ic}5jc)EQ7oZTgA3>KB7?(5Y z;}7d)>JAu1($gQ#&BA6$7*ID~j#}1(9gDkdv2I9#3N(~Nyu3q*H{AsW)aS+H-_CTam>;~^cixeBsRePuOSnPYohiMmT?{>^F zcch#6!Wifr0?c{&iBGZXtrWmp0L~av`J!FXH`{O09dml)Z3P^rH6}Gcu-s*zi(k>f z`H@j#4(FVIOUP6aY^kJ6x{hUPxsb8jg^&77d6sS68TxIh)Tmu83oBR0nnZ0yMrMH$ zYNKkp-+JlT2v~bgdX0Dqsff-QL>g{bdS>|>(i5U*7Mr`9aeSweF5L@Gu3x_a-Z2=m z8(-%J@pP!Z{|xxz9^F1ERXa-(SH*;BA?vMS1Y2vBTy;&(zbkP3jG+pd5dBG^P`!%R z03@AwA7|SE^Om4>L+bd2td#mr7m>q?g93*$=bx%q+1}_7sYyyIt@k|w|H%EXs*PupcYQNyY zjLjB)0|FLryjc(#SLNJ7OVQ9*Iu7`+22=!3oFzHZwrhT zFU4O5l>(uNm-Q0~>uc7L23u{|W4Q^m_tnxmqV&XP>Vs!bs9fl4+}}3^9zpCdoigDh z5qqa^x(d}FnUKF=TyMZChT_M5`oZFRWtiQlLPNJ)+CX`cQ8-kP za>FbCIt=i+Nk+dz;i!h5#jxo#qSmfvk|48g*9NMshKZt$7TD#=(hE1P>BKBB!1kk( z@^fam@O*F1O;TCcQ64W8L&qM!e=0*UI1CVt0jtMC>NE+OW<1uGJRT3G;((ARP}n@? z)k}sz>qJ=vVODv2DH9@BsFq{E?bCnQIvMuCdse-nxARFnas zZ^B`R;cAp!5l~T)a#~B|q)4{jw9P9;d|tZEnrcchfe3xz5r;yYZg=Iq@bqrPdTq4f zxF%q_tU#T3asWZG)oZf;)5b-_b~RxGVG~$lGg)3eln!IJ!GO>Gp@D)NOeqVUnB2#h zCX!mw3ZIb=ej?&BMuY#G69Eg~?6z{4L~dZG`nBZOrs?Mgh``{aN4v zx<&$3j8@#>)}(HA9ynuXAtL9uV*&|$7L@7k12`|?$;uz|QXyD5f za?lVX5mcnY!Yv1@RQCcXnFFYbF{nv+AD_C2kw`K`kS=tPeo0(=AjMCc;68^S)NfNw zL>&`R=}>Z}e3`+@WCtHEl9_Z0TuGQT}{4St{`?V z)vV-yxf2Ab16;zz`na|03=AV+M*Q)RtBey%^h zpX?S4x3@f#I6SdIUv|{M6K~Yu&ko+ZD$f%)&y%Gj-;=D{<)c@y^uaVmIwde`6i3n^ zSjqeJ=b`tjl8`HGgg=D!Nsq`$j|9rokE`xTn72DcDY=L3!tbjsZ7z7j#3}q7dRfp2 z7wc8ycsR(5t#*p_roOM_QD9H~k4>G?0~KISU;|@&lr+y;ZDm0%ctgG8%YJeLPcUea zipE)7@XatIG~#_S;-TzOUoV2|hi9`TguE_YVTsN{KiE%YaVn-$@6NhhY8jQYa zmQpPIWi6h7jIA2o^a0MI6OL(@6`k<$*+()|^3=2&Y;sx1>HASRRSh>ZXA;R48?51R z9)G2px=VlQq43Wn$6}jEn7F8R-A!L{m+(p(Cq-?CNO;`yY*oF~ooirKM#iD1!y3fQ zBsc^_Z>}7Cy}Jg%9XCQ6rwFS3rWmzrvbcGsKb0zUhxM-Vhf={*0K^1cg{lQ==wyL3 zbvxY$D6BFUo69P{UVcH}wXX?ARXsa?>W z;}%kv4vlo0M|{8iYda7(?1uJX-OSye-5NhR*Apw(5I2~YQ~em2J6hAXjWSv(?Oa3V z-5pK8wZ-Rtc=O*Y$YX|R%;bhf)+oybg-*k!P`fhN!yE#hhH_9`riV-lB2d5zMT|H% zn;d4K?NlYX+F&)(bAP3j{s-LeHonLPKIx@Dm3d;;)92FH{l38IVZ{s@O#O+#h~oagS^DS-nHzJ*Tud3!iAWZ z0UerSZr)`4Qi?$E&P99Sx+&xoXzANdr4q6J@*$%#ly>e2ZH$oSsR59?-&su^&ym)d zNiNPBWqxqI_6+`0CNd(*nbe#c87%yv)FHn(;p@1*$MKhxXR!IO=&IF`hUqE7zp|*& za`uZHS#Sw=LMW(sXv0^Qd3_3W8?unCn2c@Kz`r627$pILNR^f}qE87bETT{QKfmy^ z9SQ#Xp>XU_*j-cF5fIruedF+j$%a~N;C}3A@V?<{@P?M0zS$AJ;0D_u8m_)_hNB*g zXksT{l^?QjogA7vb9KqR)YZE;d307MBsnywqA-3~{!xwex*3mB1WUXMLf;4D)$G?U z)aADU3IrZeCruJ2o&m$$F0?Du{Shf?djr%b?i+Nx0X(UgEDCIvL4NY#gh;yf;)$Yq?3j+fKbz#x5j)%NhR?Q{&>?JnYQRV79V** z)NO1hKh0VbRC0O*R&gvdIpZmdbuz@lCd1vp&jKCZLbn6iXp)jz<#`R-iM)?^OL+(x{ayLjg=2N(LL)8V@ zLJ!#PLcVgOIFpz17JLL-y$V=O^hy;7gm6!clBwnQ(Wv+NECF!UF>~sC3MH3z?btC* zY=Fdi1!;ZgRY(phl+rxU&U(T*igUj$R7*OBQ_z|o^>c{De_QZH_+#^K#_W(z9+PeB zh3`-S9qmPG5=2*JDuwNtn7Fm@-wzabFsx0|{Y+ zE~jp56!rspVrf{ZmLU+w9h**Fv(LtzzNO-#K>vzp!jYuPQ&eb6L`@J3a>+lUiWg`U z{sWakgE3%o32qPak<}^NCz1RNFoBWZM<}EHb8CP2>V<4R?jV%*mf8k}g&1>habftH zXwqKH4Ra9f1NnA9*yYawcx>Uy16gOtetf5&XG1K^O>#?A&>MtPg=UcE9}+pG`Zhi8 z+UA0>;e077yadwM$s^8km|-`+Fa;nR=q~T{i+N{%jx*F==JiMZb+{#=e2`G>iN|1y z&D+bG169#%{yh-_vS9|cb*j-LqEn?|FyA8smB#Ndl}B8q!fmMgU+;wqTc%*1oeK6-W&7oI`gzg#fUDBoTEXv`r@Z%j~NtvQ#< zh1G%Z1do(V2HzoJmn@C5-teY03yj?^E>T4RJOTX3%SZ4czS z<5XXo8e3BO0xn)9ZkRKf-EeTHed*8r*w6c7ZE?NFBp(7V2$(z1gO1oZU@^yNnvWN1 znyD4)8xB?ywYXXSbr^B|v~ zUhdat)0-!X{1fR6*8wN;|<6 zkNHdm{z7G^q%7k3eVxGbawa(zmqyV(bVP_c_X?-rL6~-)?uMPh{>ix8n>JF+ZBMj; z@;H+RE*Yj-W@Jk)8Rt4v*k!J=ed@R~uPRfDHeQPZ>o{&Kx0Hjk%N>@8rgJ^49JfYV0n| zTk^5M^gV`;R5$wYUEkxYWq+@Ac$n)D&Z&f5jTh9Pa>g7h6^<_311V1z?|NA19922EnTPjW=|75gWipBG9 zmI;%cp-~W){cQ^5qS~%b$EZ;LY42QgVD>G}=Y|WXpl==;4o_a`L<_+dZ@LZqmpysG zqC5|V(!^I(rbnH;Qh>^f=FuM@3*bmw0`v{%r^9~70NorHZCR{{e-^Tv9Z0xo@ia6K zJ?cS9`NGF_J8cig!Jh(Z720PE7*N1zZ`}H_K z(Z4K!*X-JPA`j#=*NG=uPD0qzewbelGc~OWwQN1%4YTsxw}i`)zoma|z5E<*e?5*2 zop?qj3@HRfGp*o@IS(d!()otyTWuM(vt;$hHtD8EqQ} zYi9da$5>o<`i{2dB%=Akk4!!N1&Qz)XgrD?sj7S{&Q#GdF!KwaBjtggK-~Bthj1#Qo6qp2?N2$I zp6T3kT?50RgmP+`d9_A4jh%)kDw92yHFcBxZ=^_GPY9R$l5g-Mntz6EXOQCw{Utt@ zVkjc&=qjYyn2QZ7_(H4Ml4-|GkRY1G`36v%AeW`M3Xft5SPoC9d1XEDaYKeXpWUv9 zokHh`hHcFbIuyL3KOgKihFHdT{_j;kubtht|F58*1PcTNKV8WifRi5I2o4F1)%5j4 zTSEW5HcQ^3OEznU`6faH03*XCBKk&;#7rg&gFq~ALS~hXxF)To=P`Im_YFOvt)-1< z*%GEz+#Vry1ml|q?UGFO>Uy~S)#6XIcYo`h)?UNblPLuY&SdGEcz)N}o|C-yE!UYp z*IOq$e*4rQ(YJ<-8vCl93C=OV;XiZ)Mz=)Kip?+Z(cxO~ja)PP%?^B*rN3bq1;ULT z`1glTZXq0U&77l#tr-dAA9IbJ)5Md8O%3?s7?1a1a!hZ@F%(^PRJHqb9UI5f*X|b` za-R2TUubA&J8tb5H1>khYw{0{6dLwcHeY@~&H5_GI(ps6fBHucw|n0Ju^oE)_IWS& zyXbzN^hfhOeMj=&?ML#yk%ZqJ<$FQJVC=kA<@Bcj0d!L1sJDhipu|@@1M=&r=e>Y( zix;BA^0BI#+OBdJ)dfhmTbAN?awUHlsqlG)$y}IcD~v$4qO!zNV2> zXM0*|eLLOqYn8twQmhPt_FZ+M#ox{L){BP5=jF1CkG0}a8oLv7%MIa1TN(t2h?e0x zJj2-e3&o^1=#=K_t!&gBBkX_2i&qjeO(WWwMd+cAJc_mGFL_jDwL12eNu&UDEw?>N zdkqn^edfd7?6!ZIaGns$ zh%x)xY_BTq@J*;Q&th%lHnZR%Txi1Rsj@5kjBMS7NLO8#kg*Mkq=W<=JE_@A+wBzD z;7_DQOi&;2xzXrkxHm7BIwFkXi{vEIr#qyCcPTEZhN>8-e~(nT9air#c5_!((?{_p zsBPdV9*MHX52u*~M#pnx>#;9KZ87h%O9*Sk0Qkztwr-IRv^@$qbUpYZ_1bmVbJW%4 z)%AvMv0Wi8<8;>d(2gs3D;^Ho?Cmz2e-&jcGE!jG{ zB(F|7hPGa~o+Ygc_OAIu z%s2sm8V}VJa4mA%fCA(lTPGP>Qlx6cGWexsJAxUiQu#_91(}}}iQtFyx>~6v?H2dJ zF^Bq8VTXg+(1S>)RXlkMfxCUi8r4pd!I+bo+a4xMokULzyH&z`L_tieS9y%PRZ=6t z@1*&GVqEQ(CaV$1s1YtKv!S`)BaG&keUmH$tH&d>z#1!-Hc}eVehh3HpREq61By+$ z{}xdvIw0Q7FGm7!>nC2&pSo^sv3rfhdd+WnF?ubtk@f>(1b5?8wv?B&EW@^A1;<}l zalgZR&ZY9=SZ4p=FRRA3`U+Yx>z;{2D$Z|XSBQ&9sOj?ahLDV9A+EU9{D>Xi(f$Y? z7Bv5W1NN|d#{L0aLwB1PJ6CwRs+;RDp^d>vM?}~hF8kV>?&(j6-!-QZ=S5P!ib1`a z7#;#z-=~R@E#SaOx~n&9O>iZ;>Bx)<%BS<$scD#_GCxsGk(xW!C$RAzvlhoy$j6GM zqb8td)^DtC&u=O1EUf<0+7ZU|leT4AU6$wc2f8|`s#-F+R|aF3wY7ccPE7=r^hgI$Lrn#0TQSuV|cXPCV%w_gK=-%aGeQEUXeu%4|TE zUPPE~G1H^PvFIN&A&85D!sXO$!DyA#R9P48K`5;3gj_ExvQ23pGU>b@tq4^Mr-s`1 z1CQV8j7%+VTnBE;mUG?fv>!eUr0ak9Ny+j4O3Lt#VN8s_CI2j}kER}*iwr%zusmjd zz;NA`xZkkxE8p9)Dqb<^nqLuqW5@QPJX^6iWtHAxv+;v$?P|z8bpLfv)&vbuBJ_eM zA({~}NgN&+CgDx+#o!LCX?AKpH%^=!0OD2{AG}&JQ#S-y!j|kaB1`-iolKEeLi!j^8`Cmz{H~lxTf!d3>lh})O{9n%#&Cz6Tt6N& z!u_xt9kRI=1M4+E~GE?Wf38;hZ=8yeH??K zafn%uQzuf#K8L!1sjusik~vfHtD*$^6|z5gGGk^P27g7ud-@<_pLXqg z0GCD8Z1a173YE~cY*GdWPr>TBu$y^`dS~k&m-_C73K6f{9<^O_K;@fjT;H-B#S+RdCv7ZoGe7%FF>)}?UeYPhzE;Bh2a1x7oLP?}0X zH(J_v$Y_ODom7ozkc>?O`JbxaQ3N05GQ#CE+$;HK$Nax>e`f0G9R*vYDnn?nsoKxr zgw#_+t_6?XPUAhPBfNK84*>=M&Vm$?P!&mG6;W}fRbE&}`(($!D9J{loPLtT5*wa$ zY9C5&5E(GfdAG;;R(tnTjJ%hT_x6+_bhgiy!~2 z20KxfMrTyW8_7ZInbP+f&`MK4gvUMZX1UkIW~%VqmVVR7MLRQ>wKkTza+1X#OXX>l zT92VEy=asGKThBB$PY-e%YOCC1v%nE$yAGJ?OU?e?pLm0rA~u+b#4H7gSV}I#_zt4}?R?Ol#UMNOd z5Ek)+_FgcSOLdT}bc3QP^=(l4ff-#&sztMvo>;*{t1KC@plVgeaiMvfY7?`hv3e@a zld9a`q~6aGry|<{E+9-d%6iRuNLfk51q)g|1}QL$_yzdid1`xg7(ndbZou0JzymDI z-_1g*^#^GX>5dGb*y&2?0GRe~DF6a6EVks%;$oL<>zDIr!`D1xi z#(QsfvGcCP+vRxJF=lmycYU<|+%x-CQ!i0o<{QG{Z#mT2B6KEZ|ZRAHZ3 zV(yrnotU&A#xRA<)JIMvV+Ped@Fhu-Jj&UQ`BteE)*ZkGFl7SQMRi;sGYQ+qZb0TeP{FL6_JAGVEn0>=X*S%%k zUvttWL;cX^k9>c!dS{N~n0PN^TW9I755#`rlBgf$&IuD;=dSFg6&94mK(00i4=ONV zbsnQ&5;~%*A_Ebse;rQU^1(O7GKL?xbXxW@+y=|?wfoohwjT0Z9AksNM7q3qX-Quv z$v;3eXmsh9DyrLN=H3#77ik%D7Uj3Jj}q`e^>C7^6$)oqb77;Xd`8vqS12-Q2d$R0 zNo!T32an8bv}i^3m#&cqfM%1kZ!OQtui~;7JU`4;c!*b6-t@qv!*Xx?$IH6XeQj|y zs5R9Z^}0&!Pip`Ro%g=*vP1T5C!S(#HwfXjj{T^&40#b9@#Q2Ad-%wl$e4{m&)$Bt z`@C%8Q>=ixUQ}eP6-5i4yPziazFT@NPnGhmlF*7pEyZT9(g2_~N z6DVkfv%VLF@5V9R;RJ#;SxIcmdvF9o*G`yxeZorc{XQiJU+g7*z!An^a$(pdkiLDu zVWH#BT|AUxiI4}6;MG6+o^10Q#VVq?$ag(`(p;er}01_;tWZBAZ%Hi$Y6Al*E7g1H*?66^`}D zD=|*|hEKvh@)6erviRFlA8q}SfB(@Ko9_Rd8&#Kc-&3{^n6&xkqVpU>~GmfY} zUwH0zKFAQo1oUgkewn*Cm3%NaGyQhM-1O%hSkkn5at0HGa3&*^#WV6i3!Uf9PBray zTwysAR$`k+?Q{It;5NuQ`XV@nsI(+QDpg7o`)#S5Z9pyM4t8_086xwpNq#YZpO0Bl z8JCbq@}BTaks@4?ujh^Nf6pJ|0su#&_MU724$|K(#cNf7qrbCBI(7qK_1|RjF!uqX zm;TlO5C)}UODfr8h7XHQS3!X>N8<=B-ky|?Ds3`6|d1kU&Xi3tYHDsz8Y+Glxk2%M`G zea$`}F>_^};V-(^&56#qZ)rRWgpEJp4eo(G*89zVX0Mn`oiK18*_Oy5#A!%{=jNAiIyNo$0bQYOkvl_GK20QVMNz6iY3)BDbg{H~=pFXA3w2FjdFfNbRUq@MI=y4}~b9PY6_4ScD>)1T;)u4pdB} zk4)_UadnOlowt9m&$jK&wl~|_Y-_W&`efVIH`})D+PvAW&9&z~56*-0`zx;ZHS?O8 z;EMz#)~xA}%*bC=k|~{b1_;dM8lp@T68It_Da~u0Epxr5dL?zjR~HxeS{F@o|J~`Y z*SP|AK_7|lS0@60bo@_p0^53h{&G?9M;kq;#Pp7X+#hjqKl{S%M}b}W*AGo$z;)q| z&|ztvxf28_^=rK^=j&QneJ(MxhmSRHm-#Nqp!a3s92)KgN!FzJ!zsdS`VQ+ltNv`P zlXGDB=mM@INo1O|H>=L+F~vH!uI1?X*u`Fh;OxvuqyGN>9Z8m$QM*@AI6}wr2&T~C z!BWWinfIyU{jIt15HO_Pt{|)~q(9p$Si38SDy+%wHhyf2n}2rPB*>Xu27lC4!m~Yd zGHx73luO6aWbRd)Z5NWd?wEIQ+`-PJoblM{-HSBq`T)U`&$6w{^Lv7iab=1N(c%vU zD$6kpz9E@H{L?LlW6F-&tHwl`tXBHbolCFE&gSJYj*VU_FJLi*fZG;{#*#EQa&MD< zR!H(#d3c;kaU*Yjt%hrKygwDXhV9s^cKRZ@TebCw%(K0}hjQ)a+&@%xC&gIne5Z>G zn;9Yd6D|}Yaf2D)B#2;-t^6C#ADsK8ZR|~j7YAiFKnQ_OZS})zhTts5p?eaD?d#;h zxvWgnGn~bgTici@&|(c?{M3%dja+TaRs_0)D3WGhD%CT8RtW0{kTNHB zwsgDn-v}-laPGp3g!W`&D(cyYbkvD2;S~BRJGt19_kH5OLrgL?$n^i2;S{zY>|H@7 z4lD_rE%>87_GMzQhXSPkCF*wYLb!&^(L#x(x{lDX)jQ6RG3S-A1;e)Qn!Tk(;O{kJFVesU)@|q=}fIBO?lx` z5hJ@n^P2|F9(|ErGvc2@H!}WYt+Q52hfenFFB>X2h7uoG6D-ZNK<2|1Zuco`9SE=; z%5AsM2FzHotk16uu_Kz_ez=>Iiu9Dvkit){NI3#0TYHg%8ou`MY7cln=oh#^(JIz-POb2tbJ_6dpilv1N%;#X+DuP_VinG%-yUu z1G4qySZ~M;BEzJJ(N9ECFwGewwlxhZV0`8kKr630+8H}uFh`i8RW9S@j1X#=WSP+9 z;k`m1#_@I-sXPzM!U$xzWNM!6Zy5es9Ooky2UFRPZpw~2+HJww8`%t*zrWoLEMAHN z647*BaiBEvG}6y(8OIwr(eQQBY^W$mm8B=snmF2Wmd!(k$L;-sv{ojpEt2qaKi!s$ zfiM#bZvw4!F)|v_3tys2#aI%4YTaa26E}j$<@VjUP*G@SIO;q!-*i%wiHs4H_sX4> zDw7l)yAw0R!o%_yZIj+&R?MB5*TQxeCdanxs|Hpc%~B753SunVX<`EG_YO9kiJ5Z& z!x}LX-OcGGvO9%wX_9Nibs2fBVQ60gVB+hpbDGC=QGL-p5v=3L%Y5OrF>%^Y6CG2Y zIHTJ8ZL542W$BSf=8q!XW<^iuD(+7pS{|%f zo5WeM+LCzN0ztLEP!(KQy~|d{m=4WydKm*%w6IRIl2w~$;~3t6B!x6ePGo)tSRd0h zB>23m3}IJaNXxCAPDk+i&TGO3Z*4x)u*M!7DQi``Oax#tj@`SiaX2s+I7eJ zoD;{)QFz-N@{5u=CDA?5ZZJ9Md0*_TN>>90lEIc=gqX&jnHlwDi{v5{-lKD###rpU}{{VR_!F z!eXVQYA8`H_vc}@n@{+T#G*>@9?wfQyJwS<^qC^=pQ490FCj1Xpm#6{;FQjXAk*qT z(P7>e4>z7fB$xlsBw>J1u(jT>YxZoLxL#DZJglo4j z#0zH}KI+_pwM!3=NcH`c0ElMyr9|$#Y@~#pu?-1_xRR21>!T|_4Afo7V2J^~Ri&Gc z!FU{_!z@~T0-;sn6jqDs~nniEK?-dW~AI;HiQAmAd4;&xW(QT3K ztrs6e#7nQ<{<K6RXTjt6x&C9yhrdGmTeU(%ub~#Zguwepi3Q*V_l$`&dEUG z+gAH9iCXaE^o02!m^P(93dCq6ScjL6`~`LkV%9mqIZpE`BJbT_56aiNN^s zw+|s9@QFi)sYc9}%dHs+MvjHqm>(2(IkG0~2wKM&S7g6q0Q}Q2RXhrGIB@Efj9okUT;Ztb@8R5OH%npT0Y1N<2}`etJzs-1B2j~D>-iiaprf9sf4Jy zGwkE1{jFh)(aWe%O^*Q$Ee~0g@8fLYUNm7Vb{1T}8FJdmn6em(RQI@HLj$h{VW1=b zaUM!!H9)>M1k4rKT*yI|&pt~Rc9N_4=}rrLgEPdG0nEkE@`&51!QA<9xj9$bnkR2y zAuqvyvaiUjX1Oyr^Qj>{gE+MKFpfr zdOl{l3v|7WjEjs_1~TzHD~}u*k>*)b)PV5NkxLqqHYSxvlLkS57kTE{{nTf+WA2#f z4t}ZzQvEr_mHDdCEq>Pi*Q(nG33!&q+zkHNwmE>9IMO6N>$Xi4E62$c+E8l*6?s9? z^hPhBvNnPY413et^*63d z#Kor=o>jga-4CdJ%M#;m6x&Op?yb>di%K{Hb<1ky8>{<`*y>M|U<*(8y03kxRcXQZBwcEwRQjSDyY+ z0dpF* zdoukxLwce^^x%pn4*UJhbD7uo(=dF%lxZcdNv#VWf=sAB{wQyl2#Py>0s)B6I@Z0 zPPms>F=olenO{gLxj$m5p2A9rp|X^ZlN9P?>9vw8gPsDm_n(BtSIc`6GNZ1TCpF2~%@cP*)E)o*nk$-Cd~hgOm%clWH#c}wxI%-+i~hZbWPA;r zkhG?lALfPrin<`V`DPLK@!JN=8N3KN6wdP&Id&QJ#JQvU&|xU z`EVqf!mHG(HRR^0lE-53B~V1OF23mnM?Y8i2oqmS`atWG9(@`w7$$tuFs)zhwg!2% z1$WLu4$&@Jlj0#7&TL!cnrg(Kntub6z_(kGUco28+YV#cgYNr<{_Ch3G8Cxjcr(kA zWLaV`CElH^M?n_6R#l~S)QuB8K$&MKjzoGfm9huWZTPnp? zJPc?MXH>$fBPs`0bTO8qB;$E9GkOqcUkXAq9wQDYBMO#r(ctunN>Bf)OjSgSyg0dO zjNr9Ms#D;*tVem)0~AV0Rz1v#dihWGNXf*^NlCK2AGr8XNlSPX=j{e4w=<$h3No*t zo1PXsL^`r~XB;2D75Kz~uecs?q6&r1Kd=lAd%H%r%|ouXLTfX|ExkgwOc<=1=8A%Z z!SBe(!ZzK8qi*ToG*hFBZ62X(aHuDKe!t5D*Hh|GdtmkH1%f20J)=)j?F+*fmD1rn zk@?!Y`ga{=2|QepPgd9M|$<}xSpexrPafn8sH8m12zgF5+@Ozf3L*!6`2y~hEJ$kXON-on^H7v4sq3g=feF-%m+HCm$$Tqz=>JB`sp#G8meGl0j zNm|_V!Ag8I1BlGgocj4E#oaAE;{QDI^@mAmyHoIw+kAmG91`Fg?n<@c6#vXlz>|wR z5MlO-3ygt4u$`U2oZ$=4h_n%-^uqpxJDu3I5bh~)DSQqL{|TbOzZ8IngjCv0`KN>d z$pNt>sI3F4`!D_}#z=B(DM!WRd?d~pgQ||+r-b<;10diiuy^3Wbl~Z)p{Gyt&dS%r zQY#X2G}Vb~v}?d%zuROQtEnH^aa^|I$B9W7`Y~W@;qaHrw#O~qNAvI|NPiVVbP2tm ziUkLS>^)K*#85UeHW=);^1oCl>y%8<$nk*PP#1v64r?m8NcbS3vF|p)GCWO`zYkFU zDRX_X0yqh;AcUomKjdwXjTUsj5=^as?#7>b2-iY2WC^N7FR7p-G^_c<`$MQIhi&** zBBZcmnQhLy&g;JXYl4ebvMu45_QPGWIqsEL>BoDQ3OpCBj*8&PqG+YOk%xOyi=na+ zwmmu-NwaTDqGE{K|6Hz)JmALOtjjTBX)d#G2Xx*-pN8WIq)UF^F}iDvR!Dwyep9N+ zuD~9`X4gkAYn{=E-;-qTJ}n%x)(Yw$LF06p5Vnn9nw)X-ku&s;jKm0|G>WP=4kjk( zFvNyo3Ny?R7DW_iftC4E-?0n0`_0(14EFYo*v&>$qc~SKx!HzOJzFWd8Q(3JJDl6v z6*%*E5bh~oSzxZoGaT*x+@N?XY}G!q)5zq|z`hf~6z#AW!E; zhydZx|0C*f=PpK_2ym-mqA3OfLImII^SpTz9&TA`^!-RY5P!_Nke=_F?_DJ7Fo~fs zW$i~OG0+Okgls7F$*m!(IV+YNI@rW{0r*RcQ4(_ciC}Nk-e=jgeX2iE*Zud+@JG*F z%KzQh9CBOz;eNCV|GF&wN+bKE{NSouO=DbaL7x9}#VOVmJZkGH8vdN1+!a5&d|lSm z0&5fa1jn?@ts2~#sbw8s$&nkjixRBz)i2EbwNnIkkv{rjrGQ{*4Ft5F+xa{~ z{lt>M@Y?;RB!IEba>IYW48h}#kwB|MKS_-_{nr(*x(&%|EcujRehO9?V;iVBCCRJI z7FmZ|R;<_|lrg{TZo@IFwnyOs99To%5FWqM)GZ+{ZrLD5unVUP`g_sruUki4($!45 zK)Ip!*#5-ru(DHV)#7Q>_LLWDz>x6vN#NoE3J}Qtg7)m%<#{;ed1SM%zYN|0kS>7avy(>eOB7c1Xw6glp+U8BA-|BeR^dD(-9E7PHxWU045%U1$%FdF z$AimeJ>!wp(!7t1;7IK`a)BjS?Q~2Z%7Gc<4`Ex&J*fq0)9JyGjuD`5*Ewy25!#G8 zQCoOFAD6864n@ANPEhihCfD+3f#^y(ZN1~}MOn2is$r%j$F|m|)a#+IFpn#vXGNj0 zs%CoPzBH+r2fq9btAL;06Zr2XM%QiWQ;U4^q+jYSk|Wgn?vX-9aiz6-6Ik4uxoYIV zrV*R;_8e9CyQGK?B!G@&Ly&K^`gtpyi0TFgeJxzmsIDa&SF_OCwato@Z^QEW3O z<(&4@su&*sJEePheqs(Gp5vn(Xw5@o{6_k~BS4o#Y*4=>9=cNT6@Z@bAh2m&qMPH+ zEL|&|=hVZWX-rkmt)V!=E%t?`yGL9!f54@3N-yVDZTc#EW13g`7mC0Si{xq5U++N8 zXWs-9U{c{EDf%y1PEdfv&1x7@&u`Z2HZ!%?AZELf+wh+AUHy;|?kcHPywhZ5TUtoA z@?08kGxV^PYYB}IL7zsC0sB8JdDZIyhv#!W3M+X9VPQV8nOP~(56dre^jfQ0yLwQW z^G)C25g5-d5pGpwsi7pehG}qjBlb=pe1AcieE%QF@NMJsUI_*a>@}Tf;y<=9SIf>G zOB3tsxbe!(6P9)gWRK|P-KMuuBDm}b9=LeE-^kP~s4JOy{ohaB(JF0yJuRF}SOgtQ z5^Dff6@<(-Os0ZXBq0+ufCm4S`ZDw)qoD9|z@j>rd;J6*YRmoWy641suH%o;$yUeP z^J^Vg%D#|5saa@*94uaC<1HLOO6pD)kcuK0MrKs&Vhkgu-;k9|0Gqg?-x-#y+gt5w z%(NP*J;%herWzl{D0tnExpvy0>=l))s84NGxDd!np3L!yRVN;2NO~wgVAD*4+WT&E-0kI{z9r3nY-&hc;ZX~Iz!Au4$*=u2P93x~1 zY0LiR{AOQyRoSt^ULH{qWtqvS#fX!2{}8^AgHL?nXvhTL;}5om|VIpqbGEitsG3K z4oWd9wlK8zODpXTEsAZ%b%TDE*#%-l+`mg^u^u^9)z$Vv=tK}>siZ1}W6@$*>1)OE zZ%0Y=9}b+Pv_udjDwc#bkFTm$(ZtrPmAI@?BL|AXqw0#0cS8U!Zzw8u1Szsp$+nC| zco)HkKQRpV z4mF9$cKN9Cs560rh!-IW!{l3j)CV%u2jmo4-Vh%602D5q2P)l_cIhYWzXvuN=1mfV zdir;)KYR(LC5TdQQFgF*-{Ui-CYC)nIxWQ$DK(T_1Z%B1>6qbAdIRqV1+**V3la19 z7;jSGTzIRg%EwRCGFk8)XK+v!^>D5#z8XADFb6e6v^s!V2X=E?(F26*n4Fjl_k~`WXF9kW+EY6h-mQraDRs@R-DAak@z+A3NY=#^}k5;Kalzo9lZ!Y9q zo=fLqstpjk(Ow8Fz@v;4_XP7uAU5WZZ&U@aISUzDm2Z_`$o`G}2paey(C&Iz+G)~) zuFAE(rn}U4RlJZrwxIrXgUH%%w&(Y5ydyO2ZS)@q=RTxRmZrFpbt+0EnWo!e4_-4m4}u{U?{5xz_d0>I;$Aoy2A{D}Lk3FJqBVF*1{#zyPlqgo$4cy9G*>Eihf&4-aHCLzMUT%L3&fe7X{8cBn-N*AlBEr8}u*&b?=FaMw&OI z>}mz(^xR;Ao*}e9bz+>>I}=phP%;hBY*s_7o9cf#Snb}<@6#n$nVIMJr@E zN&zq&tg#arGYZot0xFXpgVOi##nL3KxDViVqm-tPpyT`~Gfexmw6R|VK}TMPMS`2* z9h&nXb=dz>2m#w}rYO<~`?ABwyA*ho-!Meo_MQ46cb2niKim6CQ! zdx)iP8=e`ZSZ3NteMpzOUk%2uB|J`v^nrY{YCC;r+)>vS(D>!BM;2cfE4hYmsB!%< zU*W!EHFNFrCi9(4^qfi2_n};8=qBrM&OAr4MDEEe@Fqm>=8R?dUk%x-$=j_{xlV5! zHf~mimm$%N2I`#&e+~wgUX{gB;nXjJ)73a`a6`COaT801Sa1wHqRmeqtV*yyp8(M; z$F8GcQP8IILazHA)g_cEioqYekb2yA_=L|#oclX0W>EI1qkfCjE^jSnPvm!}=uy3w zC4#zxu#uaNq}=hcGq#9JNSRB@&Lri;Atj3RToID@7>olfxiuTPm?m1YlZFffk#Fx9 z;H$A{?=XaGZENGTCJgT=U2DjfCcvKq)_3r+HAUY+|6%7ROr?hWJFa)=-)nX4N*CG9 zU%9J5!=LI4qX48s(IC>Jlr=)yT1jmML3rr~C{%TbltN_rhH1I?%?eHvZN~)!Ii-G4 z-KDeaPOM9#|KDkgVD{mv_>XW#=K%v__eI{)ap0f$`ZMnGq26eh?^4jTH!Q|k z-Th)y&YJYlk&a^kePt9$r24r_5K^?;yVX3WIR(_D4MQe~!uLoDLxi-gugVaY=Qm4j88S zB@+jGY}t=7c_6X93B$k(umyd7v@gf0hDF~|&c=h&IF2}kwiA2a96?n_kyTA1_EOzG-y+vvE9YcT&`^-q3yhH`zJ$ z@O|TGMy8=lkLlRm>+pUr*fb$`Rn>`l+Tlh`2Q^5o$vW#6V|5Sj-2PjRW82;L$d*?q zd9S6IeW8Dj7d$hT1g^Z&iDB`-%lhyP?F<3Ye=PE@-S?kw-vv9B2S|DMn%c+We(B7; zYiB;6fa@FBjlp_Ze*dXhuK;`LH6Is#`BFfPDsV@>eEA|y*BUH%Ve&LjS=|mo5Z)h_ zUVl-)%?OI8d!dAqPM@oDe`&Q=g=%3US}mF(H)1RuK|>Hr+%e3nv;T9sd2UA(0V@u~ z3gRL?bGJ^lgu;b>_Lk$s@%&wcEG!bjxN<;xYvId&U8gOBpG1#F4f(Y%{0}Fj0Ve}b znd^xP7k+U)-#kK_wn4=ab(E?!&-mvWE@^}<+zkFyG zbY%WS=`yko{56H(zP9+MY)zm2~MHCMS^6P3+YGLd@A=R3~eT*Mf#(6 z-qwh7yf0Q(&sRO6J>SFUvK~Vgb)h{6AIrca1s=1D@#WHT(YlIp=wY55^ zh1@`p4FN8uWf7R>^qFX#livJa+h}NO?x%vT@FC@oN5^No&zMM@=nt(yxP!2x=oH4Y ztR}bv4({f%nkGO_Iqs#+dXXwAwjSrF{oROPkE&6AuS9^HMwgq2S*#v$Z+yt@U)$L2 zH;RGvchG=L3Rd1VYp<6e!4@lk_M4z9=5OgC(iGjRA@)*HZm;lJ7@glEns$47DnDAO zqGE|l_4F~wPZK*wAm5t+-#s8yg< zmB==*K-tKO_)V2A?H(hWGTJX@@Z90 z*qiU?r*8i^B7etLua7^~Mk2i3s44+Yh|9!}Us7s#bXHw0y)s+$5?W%d#qgu45vjLda>c; z#ho=37KQ(RIv8fPr7`Vdt;efVms6}t9CO4So5V)Gg z@P48{wK0x&9BhcItHk&}t(jcmv$$ld>5RlGW@ObMyRcKe;U|2$6b^g~P4^k6D-{e6 zoFUsX`LBOELLo<#1(Cz`faNu{XJ;!$Gk^J3c1mTi>X`rf#ov@fMmeM5g3Uecm|9>8P&3%DtNk^V@I0!I7F zUzmHv!Si$q(vhIV!Jqtipu2u)#<%r$R5X>;l-QK~CPum#Z0C_GB58{PMxHf32z%@H z|GeIT!-oU^`hm(9mXEe5(tQByqb`bcPgWBQ91cqi+k3Dy%2%}jP7>S0R|y?OlUK4( z&Q7utFOa38NIRvxNF#-=NFSY2QD0# znkMmzqbF;!h3e$cm#Q9cDrlGA{(M0ztkrw4)6j!(%oiYn|2>w3znoi5RV_Zz6@JFu zo{5_YOOuDnY!u<~yr6BVfoT*{v)2njhh<#DU5m_q_GBi7(9$_wE+5bYXi{TeAV$P&6aerM>W0}stb`{FhM3$u~$I8wz>&eak@ysNgm~Ngq^J> zxuynLW^_S%J}r##I=8*8<(>li!i&m|Xus2-N)R(@d^=$+{%|8{s=YGGtX|yvbp7`H zrMKC0(pK|S%Oj9v;;80DLkjy}VWhtt`9#V#LF74axUl|IE|>h0ix0g(rtAckcQdm~ zT#<@iPxEo}jgr3jdHCFPU;y+@@p+E6`r6u2RM6Q;Rky7Nmq%Etr~^FEz*x6ookm4m z7A?Y5V-X#?8&e2{ZwhPV4rL}~Ge7=-*uD{Fqms`s-B=n!ouWK=Co@Ow@*nf}QN>S~ zg$fI0DYC{5^J-{P&AeEtUHPF4RarO7q^#tG(!dC9qZQ1V7v|4M2xo%7UMTgWV_Mo2@yZXg-nb>#;iCS)^*6>@WXAR5pLERrlB1)%NmI2ZJmk>VAb)CO=x_>R*1uXyPtWS&o~mknMPI6C$Nm(vG+#qYPdA{M zODtcSsI-U~c5WTmv(D6!4Zm<;Y<1I?pPB3YUaZ7@He8Wl0y~2c{upHJ5l(5k_4v}R z@Zg+~qvYdx|K$9E_8H+FugCXDPkwXslHTOQA*$J&xxvE^HsUwnG@V3mzcOP#IxQmm zx8|w9xlX2oqK2V$YdqazPObar{Rn@{mHm0W@A@y~#0pmcpWn8I6mqL9$Rs>Xn((W? zqq1ZD!eQ^u>ytn0<)@My5klr)+O(C#tbGC*j_;(!bu7!6)!+io!P&(D~#%=Jt}F|ZYUN=2;DvCgGR;zWAizRyCrT~;W?_|0!ba-A^q*cjzi z+A(Vv9k79bX#>B3B=sXRgrjPKf0AX^JIc-)yR@h5-!E(Man=)R#&0FkCQY&AaX^gk z76BY?bS-U}L1~ST|LNFlFG3}z?BCmclm?CxxKtYRA6N4ZQ<3P%@bqRx#YmF7qZce_ zt*;DwXE!T6_iGXZokXacnnAxBQ<=!>9zw_UqLQ(I)t4aW4o+?csG4>c5=%Pd)bN?j ztm8uqyI162qPlwMxdpMD5_G04M)?u{E%G`PeSb3azz15ApD;MY9GDn#5 zqX~hiIzt{JypykHP<-s}gc8UUYd}Jl*nlj!gssl)CADv9K@DI6fBM{AWAJ=% z#X4MC634Ox7^zpTmDP9pE(ivYNlzm#_I)O>(mImig=G~}dL9!8iGfRt zDvGGS3y(7pCI;+poWaw|E-+Hsf}Nn(D3u2tv7&b}K2qa9N)5vCg25vDgd_RzEEpcQ zf3c?2*5vwwP4Y*u*IxOi7`cZR==W#6nFa4YuARqs*h`;du)462Gl$+uz@7e#^1T46 zHN?P$SiorXCD1M+bL9&Nd{v}_u97KE*x1ye|K1juPrzICn|R3}{QK|zvQm0PYZU_% zOh2qSEgyo4ITwTj3AJ|mu~LKA2CJM+c&_We!sAt4RN2zx95kLCIK!F!0hJIR5XcIUG*h=ZDb;Em|< zbFzduz6q(o(ZW5g_L;<43=kDcI*<&Gk-OH(ffC9lw@3Pg5_^t0Uo7601FsDPO^=bN zU4XQsE|WVuipLwAh$k1 zA(v|%h#Dv-MbK6 zKmgeXJj^c3ExZ?QZAjlh*)HVbaNn*LFMJ!N_TbMpeE+UJNv}vZRhfi73J2Gh9;7Jr3fC&KA$FqFjWPW?t#LxZx zqy<46-cII}t60PTM0VsS6mEw+`C0>9)Od}2JrR99gyp_ zG?TVdXm*RL{0gF!I*C-Qfy?YS31P}dS@ZRDJ28G$!2vZG|KKB(Yawa##b_@idT(mn!nsJlHeC|4N4;SpZhtz8nA}(7!LH+ z$@J5B1m%YRK*{rE$0rOK(6ok0=Tckjv873QP_der9b(|?pE3sme#~!bI!o+Zz?}6I7P(pJW*E(6 z(Q)s8WTp7vWth5#>a3ltrq2(yVEsi<`;9yZu{!@(z=XPwrkKz?A{NSvxCGo+ zT>)ole5EXs-=ENC*BwEj-$EmKqr%aBkX9cYvmUl|kKTGF{ zxm#Y%-S6s2bR$+1*}Tx$2f-4tds0qnvZeF{XPR_I-3os%4dg_X?z*d=y;`@|9!aym3GGmpjK7IhH3vhL8=dgcJdK@cbQk zv-3ylcxquv8mvlx?#e*y)|-;&Yf}J<>-8Iz9v>qbs08}>lymyAXS{luqa+Uc+x~wY zq?44NCy67xeu!T`X{3I-LeJ1QKX}pW{B}MOop@@3f7ueu{^ow72XO#{5P^UYOlYCu z+dHTg^>Z7X;e4uBWPqoxAHo|`9Osh#P_7^MFENqy>KThOQ2RV8Dfn@3tm_K6bHJMR zE(rR4`{DUllVO;tLL++)dU*NJl>cWSZ*aFI`|Fej9;K~GV|n<9DNJ*e&OF_*SYHP0 z?}*piZ+n4t5lv*|vw`Wu7+#vqGBx?q1Q*3|&ywiZ{PDCW$%TDXezP=>HJ)f z+}jVCi~mg=UfAdQzR|JH-DX?>G0d(cHXn9jW7@-AD~p0VNvndEZos`gUFZ0Z&cZkp zX^vsJ3oEt8!k!!kVi<9sgIs% zz6|v-oQRLVnf~|)=Sn)m>>H>I|5Bmf375MAU%$*(q5OhI-WSjK_62zo5kKxvQy2@S zpBi4;oP1_JB|R$0Z#^p=+6b5L@q>^+hMV05`HD>L@>HaZ8~_PIL*|}M{CLhlU`|M8 zmK9q8FF&DlYt|{5RsN$7eEU&OOhI`6v-$elIJ&mIh)>LrFZlC41rPCOf?>f@A_(Jp z2~L?V?a27Q@jwGx^(jR?WwtdY`tVK6`gbzl1OM^O?18pyxg8lP1);09v#XTKF5+J3 z{eS-xH=+fh8<`@5fyMuqHToYD1TP)#92^H95LO z5%>ZX8ZtFFrU)go-?ZRh@PvZz1NFX|XO%;XuB0AX3v-QT19o~~l;(0}&1!9{t)8A< zn{JElW@XLxtap=nk^;=tbE3fW_OENce?rgSPx7Xg{(0Y;fFYi8Vt=-mz+E_1akc>! zFZ{jaJ)4@on4abBn4T3M9rB~s%!6&W4%pAeZ9%^@t=mUp-w&VtZAcQRnU{WPT#k5R z36`58Jkfc{40vj4?3rM%cu727c^;^~dMRX$jhsK<2R?i@?!up~L@p;U3VxgHWtl;h zxVFKb+YP7B8;LJ|ZHUU>En|P@!2S-5xk9|-BKXs(r{cyePt0+(OaE$v5KyL zl~^E3d&NE^rEKpMY*Mqp69RY@7cpSWi(j}+rQ94AOzoIU!9R?a+r@_rN%eDwE51)Z zDM}g3=bIo45HjL7Qh-wuTyZJI@(;ZKYCP zp#o_{SkEzJmu8W-0oF(oVu`Fj3zOql*^V+IiBq&*; z+m;rNQxPbVgQ{kaJ5ppcmg{2P_Kb`kHd-HGv3zVzYYFV- zicK?zFQ2bAjh^h40^p`}IK6_;PN#yM##WrI^^?k+72pn=?hib3PEs+p3+N$?2ytM* zKxjCCe`q4OLWYo`F3VSi-8D6y5xm(UpsT*e;)!X3t-%HlL$n@u`E63(o&2No|jUnI1vf0O4^o=j2j z3`_=zQd*zq@D_1H5+5#+VxC?GJEEVt9*5p3*_x8RL&@lmx1%)D@>? z1sUJiQdK6CYnn;Ikj91t&MYMkuOUwLp_TS9SFva*!>p18QBFM&ALkEHuuWc=ymMw) zv{QqWaOKiCi=q%0EvFGk%2w?ZEf!~9EEc6i5`nlBSTMC+J$2$2LywhTgJUY8gimfN zR*y|nWg8ORR}F(^t|FTiIB`>0aIg+_QNkpvFtw&BO#7d7x-Ww6O}tJEyG=oYHN)~KA~ ziG7xha}p5$SLK@GOrq=N#Zg$H>_J)TZA$g&#e)Z)+tlr;5XZ0PHTmk%n0|wNQ|>mX zKBkI0@NZ%XXD`A2_{i1teH(*9s96rQvM7)T_+XzeS;zWTr& zSRh59R>vT7zn~aXGhJxtt>{#$o2|h)+y0jh(^;6xp3&-$f`iQES9O@m{1YR`qsp`T zMy)}Ct6wP0Ih;6v>)k5gu%*HdV!@}(FOXOAZX=dOa@KeoMVt(6U2wxo{mX0#tSIQ` z-R8YsCR3Wpa`sZ8%~}i}i1t2pLgKgpYT(t1zYCa+_E(>3eqvfj;iC}f{cy+u^DPr3 zHYF^Y`26w8FVN&uuoly5h4aI=|8HVpwy2N&RYl=V~ z^&!=GeZsq=!lZAYAdAb=%zr+tyixyPjr)jlRc7v(eudQC%pQg4=(zl)MIU7oDg!Hn*4p$a zbznv2>L`*Hd1^9pCEorwrC6qW4P2gSkKB#CIt}mXu$#}``yr>>TFE52YrMJseglOX zB<;e-=8ra3CsT3WiyS`oYsxkdDU7~T%U|Uv#c+Xw>_ROhtI@;fh;m_?hUIF0TGV10 zVH~~lZ~z^?O3S!q>`M2NhU2PJZF?E!nVsTEHZwZ)y75wT5!yM?Q9117z#XTqR{-3^ zn||dC*WB7tCMSHOpb#rW5F%1<;aZ@XVLU8Q)TK_cL*#WpU(VO-tjZ}h%J(k!Y7}c? zwspUVE^tL|aNjXFkJ%qZBu!lzG#V*t`A*92)qwIjJ|wG+ifel~0b3wu81a!!GbeeH zO&a-874(8_)k*6>A_ z+M-}Bx)Wyxf?HVgI}LQ1X{}fS*Vc00duUHV;koKs4P<-q z9q{R1Q3u-XsNqjP81J!e22A2FEl+xt>9Ij<-YVIdm?A8kk0#8kros=!nW#nC-U~ze z+H>}B_s*fODy=4P0?z!70kNAI#9dC*BqMxYFt`VN{KyHk<*)znFEYac z{Iw_u!+#W;q*CN&9oSZT_N9}|`g2}xrCS^y?>aeCG5&GxIpGg%k$2Ee;Ln`;<1hyH zRiQQd|CK}&HSW3E)8zxWE9o=NFHP-Tdvw#zzT1~7byN#=yn=C*PlY9YrV}KLe#PW* zls8K1ePQU}3R;&I=fdNEP#dhCLGbYcB)8P|#WsE8>rB6UZx1j=6L=7|OWGOHW4#f* z=IGyT((1bfM8_-~Y)TDJS^t(_@h+Va^^n#uL6ZQbu6nL})s=$|zYpeJe9Txj^tLjm zU94CcfFJUSLup5O6ejXgIW@+Hn$9Zj8bm`v$AC1&{;=TY_th2#Vp3fGyF@VdDe%Qi zuts+1!vv&}u_KjB7c4+`xz^yaRf}eiYhRombkDgylrehsa5;{f;mdNT+8yIVy)4!p zNeN_h)_BDyAHjCtQz|8s4i)?ADh(y)hQsQBH1%FWqM=J+tG4yqQ<%mVa1N zgG^l%qc4->%*?W(W^#^uXDr>M`*`+lH9R!`URaWSnADG4G>$?C%CX}bG4_%IGpH7q}nl8DM$!_-9}ZPeyJRrD(Qi9Xron|`$fJB z%&QJ_O@65l933GKndZT{Ic`#Zl=o@y>`oDf zzd(C_uD-nT&fs+b=#FY^s6x6>O5M@Q=B5#UFf?+*1ai}qqZ8$->nAU@uXNl>v8Rub zEao~d8ca19;B3eyZ)~;G)H>9$fuSpnbaE@M#h~4Y%{z{ozX|hvKpVNiZ+!^;=U0RN z=5Rv)mxSJy_5z{?`5&p|&ld9RCHQG$TND3B`_*9arjJWvte2JlV8C9!S%-9 z>-F0Ixku>V+qu;btvx*inOM8ENHAK&iJMwn(RPJlaW=r#PAs3Z6}p#AJKi&(qMZv? zY>-vHikYLCm>?{1mj(ex-mbx!I{KY_INemIlDWKJ#Bk809hd)w78ZBKRPz2B7{df4 z!zJts!6h6RRQ*Dc@l_u-Q$0OgctfMjQ@=|M{^lvN4rnx3z9h1kmB98`l#@?SSf~zy zI0dR^*W?^KX*v`0T()PXT0{e?kxEAl$WD(5R(I1Cy9OR0F>l+;Aam_^#-nnK2AN%L z=p0^5KZ3fk&-g2d`Oiwif>%uFs3hUmW%lFoQaEWEO-9)-L&ELGcP&iU@>}^KE~=Iw zi!ip`eZ!273xna(@ADCl5P*nU{94FP*r|^pUv%xx@g2`{XmB4XH#iN*WWY(I+QQk_bS+TeGlWQdK^3vy z0i}QBvG@BaJO^_GN<{%+(J*lPw zS_;F&ByTstS~|A&5;PxrZr&7@V}Yw`kRN^O(H)3|QIpRUv)#(T8a_?_YHHo%+WMhn zhMECHcb~$;eJ~hk_-(vyvoHHZ2*Gf;NU8!#w=YlO-W{u@-d%Ob(Oq|dK;U#x5!@r* zy7w9-lf;Y~N1_1G=2_97LFe5WP_fx_Ah_8xz_>Z*4u970D>xLx*SbrJ&uy0xIqhYn zuDlA3PjFEkrQ$9`pQSj3^Bed4)&6jKFkF=y8(&{yWps$w`n!~u zBFJv8uL}1|wYFd;tg?V=fkUgSc%~VJZQ$fL4jwOeGoS*nfMaU`c~aFr({O-6Cem;G zYyW)z4=ql`X4{d*wd@zveq_>YjJ~djQ?6PrM~SP$1PjOP2A_{~#wD5xXP3xk;`T3w zj$3=-)R3xZ^*jR2bngUQ9n0$v^I8SM*f&L+*#LuCB}mlbGXG^a=c}RsJpq_%IO{Kc zI|&>HXtHVmu}ZN`8;zv+2CFP87{_sE#`n|aI~dTg&nr~EF?E%-h?xX7=Q~R9>RD@q zk9WFWb~`u@=yfXtFwJMixucTZ`yU6*ZW1e*C~kxG#r=uubLJcI?rLw!gluqiPU85C z*sWS=7piFpV=Cl_^vf}RrBmC5JtG+UH$m7vkvD)z^87wOmob_FgH&Hcy1L&V9P!Nm zq_6&5pXY|TroTVTjcOcXwd2#~&(d6X%DR^5gMfF@xnQ)6Z7;V|CdMD=>PIMRC#5hq zY$OCFQ&7x2(ApSkp`6B17Yp=~xWnspb;dwHN2Gm~4_x9(f~tA)gob| zCwL1c?0I)2Swns!`Nx3@JG(JVq>fTtuQ3kLiDM7Jh_ngTo|QOA>^X;&)*0Romo&kn zt~mDhLsnT2TTTk4IwlgU@uW*umlWiAzYpv;xlSuex>MAzbi7Ip%92<76-D>igJ4?- zCvaHJv`|__PGBxPT9$x?#;h{skdy3*L;JfIc}ItH$*EV(**R~Vg#fu%zGUo@$A=NX zNIaP4w{=hA3WSZ&%$U+#pUj&t>;(@^*Db@V6_^w%`}}}MM!Ymo)dHAB%^?~~PFXyq zGG6+bnxtw+F1b*eQBxEX(|&}#bc+)zW~7yNH@ozB5uCWH{@7+l6a(A_uIIz~i2Xom z=tclyHttL5KY=YZi3rS+wdY-Y^-W={{r{-b8?i(Y)4)+~P$9q%RAu;-#9P-nVP#Zd zFd1QJVp2FU*2E-p${Bg@msYrC-Q7~!4OwuGvtQ8#C=w{G8a0k~wbgnnT6Qh9D_*tR z8)=vS0v;?_`=eTpfbZWYj2)-`GXh-vfR8A#ACxjFcpTm%ak~7~hZP8C7jB36{HF)E zY+Sf^-MT#m;r{^cho>)LxQ6P3OxxZty{-oqEiX*pN>4RnGI>yspj**N)c6l^m48kj zx}z)}e*ih&3jRh~gLu)S*|y%3al3nMygMs*d|V^6cOwS`R682GyIm}h0A*`{x@vEE z`13Y50$|}TDEUncOK@OR(eGfS{ADUi$m+oDLju9*>;+Ig@L@y%a66DIecjD{5h5_^ zU?CXwc_#xp^(B#U|nYm zj(r;I^ysfBXxJa<3m4YV;$Jc>H7>mEtb%lN>g=mc->2YB!=239u(mCyTi$184N%c_ zT#h!vv0h}WD!bxZ<+j=G2TM#>XQi{R%;=O5i%SRHW)K{0;6#mNck-@)7h6FM_FEGZ zMp1IGv>2j8w@LC=$c)4)bED_eG^E8D(Kf6~=mIL06ve3V<&musYiy9J29wf--xq}0 zOI9%F2%X@D!G{r&&N{1<<(&V7+1A*?j(l4fFMx?EvGgOMrjw%PZ7iJCbcBV+E8#01 z5+SvJ;ke{;mp5<`Mx=`i=t&H?vFoc&jUD8P0D=;5&8@KEu)w-{!<&dlSX|y6jinbL(O{m+}WJ-QHMxz-c@kjvl$hOU11f1$usDYP10H! zvdr`t$kkT1;ws!}fqM+dP%enV4hG!y3U@6?_GvQeH%LWzrRsKSKFiX`&JsY%FssFH zcJw%}N^s^3zQUQ|PZ3_nvp$^5OY>;*;E~w-;k&>p@fjS3E6rC=rg(P=yY(Y`d2A)9&x_2e7JIDvreorn=40ktc4O!!7Th)!Myk$VC1XtA|1?k#QM zN^wB(kW`X(677Y$`zrD!BCtkT14XVQ_)5#nOl_O5GuG`y996S1d7hN#-NZ)AF&){g zenUKHXg!a=;$oIjolxE=lxP%IeXJf=Af*nu%$NLNdk}Vv=89*mqlq1^oW+*L)tsm{ zNRVeGZYANF8I(%e*$aSWP_aMDFuv4f_kF{cilM#t_sqxWJBndqUU1=4<#Ht=xCob; zGaWx--YgW)^(y{!rsCQ&T`=usYv&wB!Kw;LuZlZYV9s?iu`#4c8-8{7cXzmWwn>!TX<0FN!2tN)J`s#dw;$(KidMHe$95=u2fnh_+0ulf{pk|5)>o|L&7 zC;7M0&y<6vUZVgqR-Pq?5#1tEB|bvTH#)~xZWq}vKd7aj2b8fF-!EjdMxjzNe~1{Y zCzkitnQzF+qh0zC0oy zt6`o>5Q&g4u(*)CP!*)h&F=BE{NOU!rb1{b$IZTle5wZovn`09N7sieR9V-^pbK5t z0Qr>FQ~EXaHJ=sctE&T9#pEh?ZZ;-~VtBobq0WUK-Xx97JKRQ0?3=0jj|O=$?_DgS zwO1=|-dyg!NSCbwRLVk3VQ<_uQe85?$z(oNOzl4E9tnaR_|j>L5RPFu3B9=Rp9GFS z<)7V)3;6rz4?FO5a(pFka6?_&@qCUyMXxv$HlF|`(8tAL!pH8>rpg&9)UqT488his zkXZ9{EI&yVW1N^Fh-TcXBB04ks~T7@WjPF3s(S95>YI+UPs6s+?}BjW6Oc0hR0Ac8c``89{kzaCt}ll2Dx*sZ zq=>>V-}-aI27Jp4%Pu_b0Ye-agwLcnF#v++sJUr zpD$LVnDIs=mb)lZKdI`(Gb>T4T-SC%&y-ynA#9Istp>xGr5s?QdEnQ@@J7k>j#}sm z#_yg07YvW`24lOWwA^=C+DC%#dB(9OVhs{13&>ZkpT!XXluq}SwDf)>=gW2|mK*>S zk_WDk<Ls@}i49>-5Dt>FlMLwu=Jj7CP5nS%?O)RJ#)CPqtqXQC ztg<$#d#zd{l8Vj&&etFOEv0o1UZhR#*sg8crPQgLvI0~FtnjZ|`fhG+9l3Zrbo_bb zeIk94bo?{q#KlWleAVNHThHDVv{^iwcx)Lc=9eBEAJ$)ziP}r3|N1E;2$MT!63JL4zmH zk>-{4<_{pnJ&WYF0cNgo9r=+p|BT^9Tnl2L7{|rZj|S<;m^GqV3?z#TB~1W5u_HdJ z!HKD?!6U{ovBS%(gj}*b0rVJag*)1+VG$Onc_*8HBISpfj@Y{?0=lUdZ-bWhm|Q92 zAHL(p5K^YOWI>FOi(iR((ww3eUyL(Gp8M==QYpW3=wgoiY|=4xXtBmwWxMqmsqABF z600pr{Sx?A$89@AR>l-i*~tJuQRrsfyF+gFjBqxI)T$Y3*bsTrTpR~VcwoAMI-M*& z-6}FhmMxF0#x<+eeUDl7LvAwD&NsgB8?ex_d8qTVZF<#sf|`nKBSs%ens|%*T*m6( z@1uQL7H%GSe|P?F(cAaV-9S7uEH_%UT>D_m-Zj{}?JTQ$oTYQNcZmXYteF&beq^?0 za;6&sfg~>aB&a5~3XM9vAK|OSwJL^PJu+!l^)97vN9#8#5m#)!Z+T3b=0Iv*vYWti zX}Ls?*|T$B>3MnRBhQ*A?;Jv(J}xcB-1}zw80oLgmDhYUk7E!xhJ0`1kCs=9B68;eXzDF5%TegXz;dY?Xpe zD|FXCI(GDFxwj59_wG&Vw#9%xZ%iBrNTBcgMZ=VZ z8)VcVje+wd#qC%rHY2cDx~@;V0~X_+Kz#z!a#k~kQL9+Du3yKMf$i~Kd!$BhW=)TV z%QS5(l*5&c?GIGs1gGUPl1u4U_|3#syJ4$Y_EwPIvEmk^&_M1lFt5q&b}dhq$?M?| zSE46bq^C*8#|Z$Kx9k)11>u0(FRa-~!EVO)J1QS=qKV=c@t=2AKKkrDV^hAe_eY7b zNrSG@_(`s>mGa3}=F#|Rwmyn8srf8IM#hX^IXusx50}7SvWBMdzv*l5tD48_Gvh`x z;_AmWeGFR1^?n%y(D5C4x#oA1bsazdQu>mvx*3nj=_LgaA-|iw#`%#tON6^U8|D>mLd3y*(*)<7m8$gJuCUjXS4%+NYCy+J}ogiVr+IiuXR~ zW2X<)YufozATj=gSVD&RqXNVBs{EOM_}bxQBKfio(g#X&V~=$qG_5sga+hSA!mAYj z5N=JX$&Pv1I9ViAL-JOtK(Sc9io_axVJlJ|rFOgE|8)6dM(xYHZmiIXTeHxQplN6u zvJ3_gOS;d5LNey()3H%j&=7l7;nq-F-lG%xv1_O}33YjGg$Ch3=&%#$veA)I!EtEs zfRk>EWt@iDr&|l*u%YX2O`=~?I|^9~2DHVZ_*@7#DdS3=(ApPd+xwaL;?%+NIOn+K zB~anbZ_`Q0-iw%-{-1W28(#uZ1{pV!C-CJ0V5D> zv-vJK!RPO!v|H~z@~6pN-dLW_gR**TwJyk@E2yUfQ}K!yezDGqdOH;L3R8ZrwZDEl zTK9@`YQFUl|7yk@>&$$%4Ff6h1YFSO3&EGW>CpazFLjrw8C~&GAa!fg?(N0ovS|rW z(+J9vkCZu(J4QRzbR)m(ZTDOrD%bNWtX%SRh!V3zwnA2(ZYW4rzwv}#_G~IC){)SZ zWjMtj%jWB{k_Ty2RTvJ`JE^{s*nT3nV#%IYu}^TI0r5j#-%!IYB^a^3!pW&2;Gnoy zX|c!5kb3Pwj-@S452I@db=$8b0ncD7`O#Vk{(=Yb!UypJTkO#4#L!2= z?*QYRLeXcRA7LZlC7DD^Aup%vJl;I}Kh5^o9^8%&4V z=7bNQj4n1?ff6u|2>4i#219a^Bv0|`7KOP z{(nRGZCkRS=3xJ+7ov)wvj5XF;*~-F{y#EC(Ix#=J?M`g{~&-#>fbU(i@Kf?nhNq4 z;+3O~&XK0@k?J?BC*9JPbSQr)gi;la$dBVr3g;Am zrPQ(1sV8S9lkLI(VS4K6wBBhKq^2N46t)9Bd2TpKX~9E8JBEh7A9=+!IX(?s6V1D? zpxv_N59?Gaew(E>Mvo`JG}U17DJ%Y>w}|T62LjUL`&71f%sJ-@<9j^4ym6mBD}LIc z_oBbKT66i)=e2npt`zMd#AZpM%kuKI&SDi-ufsq5C0y+oUW{V$#7Qqs>^gmVkk?02 zPI0*ACcBQ}I*9F>NRLpAa-ew(~QCFwW<|9^8wl5xM zRz3V;tVgrqKxYF^|IIs&T+K~A=h0Ld3-rwtJ$TAV*W1rdxAx~rx<%5n1w3rq-RIpBqh4WICF2ahmnExn(Ko>ayNN+=yX;hp(61w>G| zWYN~59A_Vm$Re+m)a6H4P_&t^>M%dRn#?LSzIrTHeoNmFtl*qbOE@R}}U4Io6NV#<-w6(YdU(x}Eps{WngIRI|aMBN7$ z8OhGTsdF5uQ(*ebjO|$lYLsD**R~a=-U}5B%{=a!o3NFC04nwkWWdFrTz&2#(syZGEJ!c{or{kRu!1It55u? zRK&UkP|zP#5pV_BDl?HP#>rrAW23f~YMWeU!J^P;6LO49 z)s-^coSkXm)oOzRqVO9Dok{l&l)Z?$d5n@>6s1E${T5VRTm<<>4+Z*W`ts6oLE-au z)@k=E;3i;q_x-oeOPZJ$h&Bl|>ra=`SYCaMA--Eh!x8bzv^&P`Y~b_ly01jH5Wm)M&1b1B_*_8`lTu~o5D|Fga#XU>jBdy z-L5eJ0s}>{MR|zVZXhl)LMz|ktB`_vo#+CsX8)8;dVMtAMUJ2D+!pFI9=T5lvwO1W zE3rkj9{__ic4k_w@SV>TVa^|W6ZujXd$W&n^3fAX1btgxt1deWtG(4Fwpj{X4FIJz z&~#u{;6wPWDJq9#trPMOi_8D1Aqj16#m+)ag{-iCqu?9?=}nw2^ZvG^a$7Ur6SM}07?K-4)g#8l&&k8UrTA8ADWLXn4hM+c#FxC(9( z!BMNSc~&l;Sy|Ver08nJV-q6TNUpJ|x}8&*mF0Gz8Bt>|e?mTr{sB3Adr$ut!v``W zw`q4lBQeH1UYw^n#=1!F$Z@h6flWzrS!Iw=Q^CKj*4B`GcK;jvF$kABUAz?9^y&)% z*z5j40ifPlK>+%VFfRKoKuwRavEi$Fg=F!1=8wu3p!aiZLTEM>(@tLFzw-CG$r*J!nc0YQCsz)FX{Uii8*SsKT!R5N@H5q5eE+Pn;%v*qax*OOb(MQI z6Ea(EfI*CUX-UaGPPJUsYDA z1tGIjJKcv;Ym)=`CBbFft?@KNYYVR~;+S+!hHNgZ*Wb%Sjy{5taTSyzlxKD{G2q3x zSq#uv><45Udl{}woat<4!hRs;jX9d7sSoMd;OgN=Y2|CIRRP4h>&pSvz|jy;4m1yL zyExW+g6Rioi{pakvR~wSO+chjP;o%%Sb?%QBXZAc&fCE0H{V=E0{ErzbM$|eTu`XB#h756qn!46)qPi}y7SN}C!7@M4 zYe@rn_?VR2oBV+E@0iis1SKI9Ot?f-Rc+dPqruccqDYAJFtBL`l3nnlj7xysbAp*0 zy*KiN$Q0dPhQGGdBl@2U{KtFAW%$BdeW+nKxHv96b5;GIq|X4B^PlFWJcq= zZ#1r&^#)-&P^o|i6CP@v>cH?9KR*55i^cvP_|n}u!QrBv=+|eY0!jOJQ%hn(_^8WDhTMDHB#_lnFJ@<6kv>cEPaC=zZ1id+S!gL)cICv#@{VrR4Qo+5JA&eyl7RePhYMoiVn$0X zIZiFIZ4XG0Wx9QRDnDSG?kSq=N>iGvCa2dG)v9@wqwU>pb2t&Zs*7+{(Pa54F~6ae zmy!qC(F@tHNY0mXg*mbmbcWoShgpD*ep?s)>^5WN=NVu2^!xfpXVKOLPgzC8U6X!C zIM=PpsbMejy_@n^y5~Pb73_RPg~DodgEp+CpHkIK=O*o^%ou=AkURtwx~EW(Q0w0~ zf7>B0ocTa}KzX*J=$x^koS6_&sBebEyaNMYu|Xbabj#1X8X*(l1o>%dh8IQgXQ6*T z&MzmNhJx^u>xW|DkA2@xRBSnPn;O{_!M+Ke+2ulT56+S#q5^u19rRBWD%FfaIX+F8 zM-%**C`!rS8#qyhM%Ol`LFJY@C$){bXLA1p0tUYS`D2;(h3#jL0p} zvgHO`c=H@R!>9-SzdzQm8!6U9zSE%FrXoN`LH=hd@;|pw`-%U}ZWIC*Mt$!X=>IXZ zGPl>a<%vVVw-Q5d5q*%=W83-3VMIV>0z?Mh46jH^>M zL(-}$bcAbPXtnifHEVU>8)|D;bnP7M^z0@9Gr8;QQk-xB|NadE`MjR2l4J)7>)(#6z&gG#MK#6hzoCFoQqB3b;PvOz6qu`GmpATeNEV9 zJg}tULeOc42X-}}Wb3cR(!F?;!fBbr!0EZPz;PbpUe|i;^Xkv?ZFqZzz;PW6Gj@YB z9cx9cStvdpSYp8&cg6f9zmU%U_pq<4-8VA(hE>r0V4JZU>gG6UQ2zSed+-xia4-0Zdq^QwD&E;G zwe9j`hsBZ5eP8T_neCr1uHXRKXzmNO;7Q*oPCiw}hSL4*a|aKge_F;d&vg$egHx$V z_#+YN6Qv$75;V(uJBsKRkMqpm^hkx^Vgvtkg)N3@%5`kaZ+M} z`S1hu5$QzE>A&5*WF*?G#zPK$6X z98=|~`@xf+d3LrHlZhl#mhO;9wBs;X`TF_aoiDY9?zMSBh$Wf3R7Hu4eogzUen!i& zMfS@tU`(fSFI2LiI6mXarQSV*dQLwe8DI&QqU@Db0Eh>ijj+eZU8>Vlf_5bp1`v0y843)@p3HLc% z?#DDDZ-z5*luS1_A9zHMETJfr=B3kC&%#lT0;%8#>7gx^FF#llO+HtSagu9uqGC|s zmO+Ym6JyZp^t#6urTr_pA{iAl9uJlAf--f@w<)zfILb*% z&#_a*`URuZ{e$NnTqrQ^r)Wn zPbH{S@YSJ`;d@PrsEXV8HFbO>FD3qsKjz0W#LgPJpj>gyLCVSlJ-Og)$LfC zv$yZGt@q?O_m9}OAqkc}2^=P|4U3(8ewDWRb>(GI0P%<)%^uA!-BcT1d@DygBj2v( zqJK`&5;fR%gsqf&S24<=hyTDQ4NUa?7I%mj*&~PwdxXV^Q{0wG(GMZt*LGhreJNn> zQeykX{C}tJk+ZTe8C3W|icshz0CF4#TP;}FCubOV-ej=Pi; zy7|^a6{C&Ca{UF!jia8mK1B?3oob$-s&O^R6)*HY3Y~{Kli1`3HYV1{>AHf9?d+4q zI6G8F5Qf%Ej+mBd(5wb~qOC0Mm3D#(yr? zo5orFa?qt=+ty?@@gFw3rpYIkaWCOo-qT-rN?pn~Ib))#%~-l?23fo_wSgIVP_3`L z+%l{Co6cQ`Q2UCkKkgvpe(y8F2D-OnAznBOT~dw9O{02v{|I;IhJE*VLEI`GBzdU| z?_Id__-G05oxf{)2@mgG1l&E}X2d()@9FV=f$Xl_Ch+!zd|RCiKAPfh_Kn#6j7ED8 z@CmNNeceCsz6=JD+&`ec)CK|PFC<^t%0nHz-Jr9!FG=zL`v3j$mJMV?1}MkB&iifO2 zC*1Q){FzhH*L0s0v3N{l8C{39Lv5_kRnJM4j;Q7jA3abcy>b{w(8Pvy25MtK1>HfP zVI`6fg@UlyRcd0Q07c?hDMRK4@+fK^EDG|gF1U)T!|^GxaUQ& zF4X0aJE3jtD*YuHHG>Sd9?3OJ=IyPsyW9OwrR`a~#a;jr^4)_=LP#jtrtA<~B0UP^ZD+}cuEI>cqnEGw0 zqB)fV1{R>+X?mjAS^R4pg!nLAs1(+N_WdI4P~?6jbP=WYEy6*95wvyo@LoB%=iB9e4Q)CvlcXpE=*87H>W7>R@g>=~xCG7QAd)i8NqDT~zVfDN@pAzbw3f|hm~iHLP!AHCR0qdcI6p6Fl`^lyPcS2W=k;{;a|TkeMtT= z3lA&d_76&|Uz~MSH!C4WIq;S^w?X{1B3o7hIFsiuN_&+PvGbqYcuQAc_$!BdvHI{# zc;PmGdqW`OdrpWuKk$Rir9Hj0rX_`sbPIVTjk6>PMARJ}Yx-b689w2HnxBwGpv}Fn zrf%i!d`!3;lipOwQgiDD{(iwvZ+e2!Z{)!OeLv#*N&3D zm&u$AJ1^y*Rt1$^B1n-``DH#_wUf$+XO!wEjkzu8=qEKS8{BAtD0?w=5Vj@K5W`B@ z624n0!=Ov4=Mj@~G8%5V8;?$$vanVHSUU2lY2>ju9aw-Lp$|K<=|2fy+{<5qsONw^ zPS3~;yJumU%_T0T9j69#6&^E4zNU9kTEwtnEsp6r$M+MV&@S@Eq6Gi(p}8=#w$i?d z1dk&@eMsslM&s4{0I^a1l_J=+AU?K=c3F<6AT-^P{-gas)3L;f?e33R@D-sZz=e&R z=lU>O>Se~vAKz{KxXeNRGE$~62v655N#NY%{2IN2tT2qYbWL#fMr}?5awr;Z8Co`Na@Xk^1Ai1smxwy zkyofsw-2ddx>}Da+_n*dce7wWkxhm*9>-TY`nbJ9ZC?$iI#OQ`?w4$4?MUSdDek1*|UxcLd2U;0Yy@rgnCOh{N^ z9R=9}DQLkHe2JtXjYiBEQ0^7CLm;+C?}iu@v(t2DKoorWiyxZ6ax+QWGoToNP_(SiN1)DCi&hh7XoT#A>`u+4Bzy)+=*kytOTbO*N z3@%>+JWurKplKARp{A*P;P4hax5U-aDG$4dBh~~)gb*$4P6308xVQU&ItK%SO9<|) zl28>V5=j-{#@t@NuU1 zO;_S{^SOV4^&lY^;Owj+ZZaSkrMV9y13FMg4Li(_92RB44ITz1LKOOO5@KBa9dP8M zpo1=ZeU$f!46?HCcZ=+dM4zg32)fuWbHyYaVl*GUi+s>aw(<#b(nnIe>Q^pHDag6#bJij@=Hn1vX@`2!)?o z$D7oaav5qk6UkFrs}(VO{IK`F5o4~MPUriCp5k`wCmPl^6*NEe#*Tx4u|n6jLeH22 zdG`*zVYqEtu=_;BWgor;p= z0iEyNFOJRul+)^UbeX5qMO1?tICT)yGiL4DqVsV+XwiGLSsh%`#ZzfELnoK5Bcn1d zbeqzHRtO>(2pSS5p08*jd)BmoqJtNcjjAL+#S#QGlV5(g5piZ6gBS(-wAe9t1wXh3 z)VsO&Z*5NYX|euI557}OM&(Q$H0kKMPD-oK+SKs_K;nN*Q0L%TbuFQ9!FpaoVnGr^ zwTTgi_r;<3$l{1hkeK*k1W+=ij?s$|UzQlu_s@YL z(0tGbAd0mkoe+k=f#X0a(JDfr?j32fe=&5M46K`OlnmVGBvVh>Re8goAoZO^OPL6w z_=})p?!>rcEWGWT92|l%!xj&XMxd&<%?&iol~h8$mfZFNtKKNPEh_N7!mTILKjS9w zebE{H&@p}N9XDmPXP!>TbC}}bat#j~yRN7J{bl&1jnxirn=(Xy#?VV~E53atN+b@I zyHFU-`U8zP1Z@nkaz)PbJ!^rDQ+^~qx)Vi9dyU0XZktMdsBJ$%tF~>?(|zzAM`5Vl z(9$Ob6x$xixf4VWiDNlgoEPoTt`@f9so58KhifHFAQEzZC=h^i^rt_Ip=^J0j13q8 zlHGd{52FxM?!1`6c7rot5MjQ-MCktzC+~zBF#mmLazbW@f_0NZ{ghw~9pZjT#YWPK zMs3%W562ruUt=iiH)H{GL}A+xtOyigfxE`E8kBJn%ubeCHe= z-X5wK8sIBIfGPm}5}(=b-)d-ohQv7rc$Exwi%`B1(9AkX4;GE>_?kCp1i#RBBLOH3 zccdX7o!ITDjJ9C`*5!e}tihxk0OeJN=VUvo<#|Foh} zuPD1cCc0HZeM6;=0Q5(Q7uXB|;sGi7uEI1BDE|ucQUr?HUs3ddeXogAoDoq@K*vWZSjk6*GyRoNJdRBq-jo z8El&Kn0=E&gHf_ppRBf- z?H=R8dPs6*2e3JnaRSvA$3aUwsC}#m8&1pJQq6@uwFtAa#Tni9BkIRF z+J$Y0cVqa!_ZG9vx$fG;u>JnsH)VqsX^v^v^L)^sX*`NPE1rTE;LX4M5pCX0aJ&X2 z#VtY$B!xLdASHhQFi=NOBH{bIOE);B4M(ugQd~vB5ECSUlWhnYYAh(fWKd`yxwS*_ zM+FoOlQm8>=}O^wkW$)Lh1npg@N4RlW)qwV8Og&oL5&Keq4s{ZD+oRhPTs%5w%(yJ zN!I&uncFj6{kWqXUn7zp*`dVoMY0`wpQHYgCEN#V7ytNHxQ2zsxBS2b4$v=^k?T}* zoNEt)FE9ukHShN?Cb-w?Vev(hW(oeT>wTcC zKVqv{hns~F9!UJFZIF%Wis|NXXwCWHaPVAosW{@6CMK+0Nwt;gAu}=?HJSCAs`S)6 z;wv>yf>K_0;r`Bvls)Vl2`oZ*0_TGa=eg3ktV_KD6wsJZt1w2;Okgw!zp~S-)CEHx z+gg`AV;1!4T!Bs?jHC>E4ZtrRY|1zw#MG-mnrr((y@t5m(%Wp8D=Q`0 zDgayHMG`DreCcC0&2+c^COTb9H(xtW$iQC&MBxZp;hpD&h*jm@Me3BHO$2mqaEWLk zl4E}W>@Qya-|NA==ttZp5}n}?}7Asa0KLF#8_2YcdF>C{KkYpE+f z`tkdosv;^NkLs&i_`@gaJIGl-|`{_voy7Y~JQ| zBi>{S`J;<|kxI}m`D|R9^6!uK9N$O)LynL`VOtJAw#1DNzsg|iIS9^HSGSZ2u)b`l z<5W{8d8~~26D`E}p`!V2Nx6f4zSZ?9!lF4@jz;C>Xs=QacYI|RDu3d4@LIs}Ugbq8 zj2`ZDFe*|)2udX{Y<^D&IQVqUb&1O_Hzbst1S%*9L3=WAJ2@ zZO0LR5a{Re{knWzbbf7vqTug6GAb#_xD`W@D-7orza4NDZpm3Q@oh6qAu#)zv^E*) z-4Og^|1mAWP*jXU^%jwqEQp8Y`37wQIIK#u0uc|fCSDNE3W+@pxxVsrQ6MkQ#~<~x z;xEM(ct)+$K@3}ptrE?CK@U#AR#_g>a|L0$Q7)8H1zd^#6bYje85(Djd{ZFgfd-5s zsiTHknM3TW2oN%!=bt%!)B#a-#@~>OseuW2(oc=J;{Iz~K8LH-1~3a#XDp zJd-3%>5|>pi-M_`WsAge#t71uq`FS{vT6lPU7yqr7aEb$FLKmXJqrgwxps6lF(taJ z%W;KYc$8)lO@{o+qOdd4)u~2{J(%F8I!Q_~EDl>7U9rAXlW6}OES*)7M}l_FEl)Js zL>w_mB{C_S<6qW>>fAlmE@7=tX3cHMmuJeO(3bT3r7Q^)zNyj5F^ep5{qlWxFfA2n zI_~65-F79)%`mDGMduBGXB-Yc_FpR50Rc|3;Eolua${#zo>Lo1(8Qc_o78d+Jd)C< zc0C5K%`MNFH^E(g=-qp6c!j6|4>0Gv`2>S6xNrVWeK}$T7Vv1NPas%{ws&mX zw(aS$xntY5ZCg9Gxnq8_|NGsD_q`Dr)fuO%E2^UUWL9)O=l6vBo)-lq0mhCMo{=g? zZVy0!pzVqHWo&H-G(aDyA52q=ePK<@T6#Ip*!8PDu`89Y{T|NT#N%~I zMB4G2%Y|lZ@caYM95~APAmJ4ay#;-sKFjz*;T}p-OS+>HmegqWxl&L}e;h_)SqGca z45g7&z$@0kOmO1)nT$c}ZA0Z~l@r^Jp2-G7uI3p6&_ zV#VC~hhoPMuGy69n|;#X@lEwoAKtm`zfcZP5{PlY%klrq zNMVLRMGE$e|3B9^TLOwftp4LFT-Sln{in#nZv^px`zOFk?+4laFXA%YeTzXRusAX{LF|Abhod|Hl2-^--J-?~Pklp1_c)RgNr5JZ5YjNQU71fTU* z7Y18FA$|(VKG%gVs2# z->z1FvewdC?1t@*SFW%5fp|ukp(|GF^!NS4)5Am2V0_qiP`=;1b?jnkP-;u+Vr6(3 z>t<^2B!?BdOetEU`2qoR5+Hr@S7Yip(LcxC6m!f3gt8{0@|LCM zXA`>JJ$7M*lW*nSH#y??)R@BIF(uRF@$Ux5j`O-tT%DVKcZwc5EF08r`P>c3kA(f< zk`*nZ-;&a)<{`JyyOFXj2&#ughYe|mb4QF7s=*c^I+)8~!GQ$`S5}{5G#}*}C5>}< zbN^N#P{fwuA%!bB9GwhDKD0mnEyiSj1j`k{Wo>ofAC2~>z17SJGy{g4{eyqt(a^K_ z#{h1c%NLSB z;14QPi5i0}xq5X>og(81MT~8qrp7FqQfs;D@`lr}JBoUl#s;VMRp*YE9ZCGN8hLP%?hCNfS2a2@5JNp!CTcvo(99Iqkipuw^;P zF7B$jprMd+WpQneuw}h%`21bpn3`#ee0$GaxcySSt&pF;Y*z)b71O{HY+(W!W^i6{+i(mBX)3>0omO!=7X0jT%lq&Dcg%Oh$S{}8J1^xg#W zyvI>>S4V`ZFjhtklyl@qh$-KeQpJ?-F{*eAp~hD1rBY?f4<(d87gJ>`4lR^FCoAy` z81qd`nYDVvlzp-F@Xky@N4;^sKjuhp-8$atIAke#_hhO@qG#pI@q}KFcDW)a|di0i(2gXEK0bAgx#5>7!fqnRFs6IqoEzwJk5d*a zBWTd`{qaqCQ#$_8D?j=X1^Ver`*S4WjPu7P+szFbe)uComd_u93)u+t&?8;IIIy!} z0t(zhS6)B-?J>;ztx&SFGVo8%&MwB?j6T)7I*1W8me!ivlIDU{nW+3xf2b3f3RaIq z^)RErF)&qPq+ruqCt%|emy&gkub`T*2T+-N&tSb zIeZn{D+}?HRz|Z|L4QJ1^$QO_s*C5aGN3J*rzEIu!l0@Q1}lk5WpIpyv|f}~2ZSR> z39+A76ngQzo2&G$pq>2L!24MhRT-evcn8!G=5Q z?kHC2rsAycCDw@Ft&0}(L|Ty62Vil%#If1>eesJZf?{_YPbQE7EaFkQ-A=ZXym{w0 z=ga2Nv!h*P3h-OdeRK8;^fd2G&Yv~>slk?Ecr*vK10*L;rWAMFMvuK?p7)n zQRu-1QTLJ|61#9}O^Mn?cG@H9mK$NIKRN-^jdyYrV~4plgKCxP6t~z?%BVNfaNFsu z2#qyWRgtm4QnIj?qB+dLWqKY&ZE}!q)t7fUKSS9g_k{}=t6?G91-s#>4OXgeg9kRV zbZk^5uYvgRPoP7H{F8Kem;jE7I?JQ)SvIOCGwRryxMvq31tLT`ealVpenmL?sF%P1 zFzw2dvy0mP20X7!<)bE^3g$&y5i^@sCy_$zEdxe2YB7^zEYyWP`GP zQi4sFpXI8^757FXE4D)mt@380b~crxsAp;fVPNgc&H~TLPCOwWy?|Ht&}G-g7{NYu zxZXZgfALwxFC`6QD-d;_bahM#*uAbROth_bU3kXJZdh{VslncMpw6;}iFC|N;{{lH zAgm8_Ocy%1Z zWnStm)XcoN#oQ`95&*e~e>;1{OZF@h1o(Lz8@59k7KVQqvsyt3XY;Qt7aD=q8Ixs) z|Mf4o-ut^+N0J?7mJDp2+9n$}Rg+_-!og{j*O#SbbSmy_1;U&Cs4T})N0%PMZ0-Dq zX??#}*^Em#8b)7M>@j@5A)h|kBGNSfvd&n~w4)o*(E*}V5FP+GMbHl9cbCTV@@{o(oJlzvy$J{^&%p#Hy|;~v~Duj52B{;CihJ|M5|>hCQq6a-X8-- z7S##_kWGI?yr)~Y7x>TQ`+P9Vjmrmnz&8J7nWT}_`@xjT0uVm_tu(4xK3|trro)IV zVl1rx_MROvS)m#Z|Oq&MMNZ6)Ian zy@M!8G(5v!eckYb@h6={^QglR9ZWEmtu;Ievqh-@G+9DjBR?W8hQ3jhCdVHnC49%T z)7NnTV*|jH`r*FB=wr?qCJ{pYV3xEnZ{1&(%QC{|B(mr&&-A7euCUpJF0$BY=!{xj zz>cP(wTwXu5DyU(B`sS3tr>fHwToQMkkOO?4^~O2Drwt}B~w!1b$_FhCOPu9H)c*$ zf+{9Q)kHj9ynrudCspKjqyhId;<>lJ$J>oZ+zOcF&@lhAhy`=j4eo!^5xt2jGnjY1 z3e_Ngq`A*HMJWJjXU_9LYJbY%+<@s?MSSbnY0W@!Ap=c>ny1AWOEW z!w~dihY#JH>9v524*~Du)ZaTYlzN{O_v#r4M6i>5oQP#yI@{?~-sXJ!Z|(qA4SI45L)4ru)~#nh_Ox;Lf^gxpdi5Ii z?0eC!^j1Z%zN*ZNL?xEfBqdf(ne4o=Q^O{*nM7uUP$`Y$gz%KiqOl*GLJ7>^gIvBS zDS{kOWG)4_>D7>(x4Of#Cy6rYnWP!4uY?~dqB;5JS&q$Q$D<8dj@4wxV=n>IQ&ND5 z^_&*gtb{EL!yg>}PaZFt(^k)IDbZCT$G*T68JCwa0+&||t=Eow)YNrI-#F1U{ zx10wSdG12WIMveA=u*-jn1)$?cf3(ox}keICOEG65UXOZa8CuFXMkjfs~WWhZ^Z`^ zQ|wE3w#gTuUG=7mZTP2IOu0n&j+g)(5c&4A>p8um<$JlR8~H=?yWRB%;Y59wtWgr< zW=iLDkSA8%(87Y#TY<=^n|tks=v=q1=N)7kp?b(>;*YrwJ!IDcPK+HYEgTuWEE|$a zVmv1;4WqRKemoC(7!t-%(&SXp=VVpjvbHJHIgNiq^t7L|=1ay@!%wFS*@6J7saICl zk2XK;;2_jOGV5g$fZGHUs1K=1U@Y%I+OYg{HrwgDsgv*b+#`yg+#dodujjTZ1MBt= z5AOsR;fI+O?b|pi!l~Uug3Y5Tpn0QlsKHERXb`2qtMGywx0Gq5Hh$4GFJwq~M1x@u z>NfKxg{6htFd`5Td9~be?#lpdK;;jEbGzy`Ib@lCI)eml%k_@mdd8&6eZs0S!b#fPs>B<5% za}J2ewkafJ96%t1N+JVX3Wd40M|sFlRKz|}I8GQHn=LDRFOb=PYXs%z4;5R^GuNZP z>^+|RqG;yUVj~Q#Cw0kfB^XmfvUO@okdeWe^Nm3dql6_+SczAWA>=HpOYfmWZ;n8= zn=P9tcEW}p;a_0lgn<4C%>B@gG#tzKuHUQSQNyZBX8UxLFTwxDq78%8bUvkQri`EXO2k-4mGXmn z*2#T-3eCrShwd})5h8LNf_P@tGGAhkiU_(a>n3z3B9^ZeoV}QHP?wU3zBNEIeTbrD za&x{#6;=>Kj7k6?OysG5JShs8WtL(rLqpSrP1Nbja0Q=*Dt%#=7@tN)CRr3-cQz^L zN%I@DcY=s!?LoPE!;d2=Yl_C3Q~72*r}x(%VKQP4Mo}Uxr1xVYimci*vU2v$6Xx}Y zYk*Y7KnOHoRZ?OUot4vE%pxw?P1}p!1Ve-28pqIV^e+OC*Ro}GhFwdv`oh6ii`AwW zR+z;SrAVgM6C^?GUd5Bwki|^W?}y^Wvf} zHY+ip18W_S^a>*g9O*X8#+l1h>NYu<%jz66hD1nJm(`_gi^`?_N*oKQUi>}}+m5kzm5-1Pys=;J5pzumF7 z=%=K;{Ik0$V?!b}eAlQ8Jk2@S(hTQcVq~cgZ$`-8>`M!B5z8wJa*^MgnSZ?*spT&~ zKiD?ta|+qBnx7tP-xKhs-M?$Dk-1R={u%~@2lkx;`$D_9mcF?L{>l@VeYh7xe&^cx zqTIcXM*93z?%&QJeg9|+>gh?md0P37sP=DbeMh>2dcJ6PuP?tNH9gJ;OYcf2=f3iYCjvYRk&3Je}LY5c8D*>u7sEhBO$TesT z(>Vg4D=JU-q>Vy_Qe#~f%V{j$xi~|{UVZ{97a7kHKPfEuS$Bs>5wypJrUnW*TWIQ+ zRx_W^uU#HNODpPZ)>;mB-sqQc$Ca4UGFJ=XW=YaRbg+h4xWuGOidJ3PQuCA=fjZdc z)qk1!2UsYRV+~5%YXL-@L<-CUWPc?H)C`tucse6_*8p`&zmV6)m{pluV--gjl=J+W z5-YPDP;eAtGvdiMz>18w16e8HNISXC(J|0c;4<3;c2IGU}(!pZvl)tJeY%hPx(7T*+>qofFbKIJw(_Dox zZyM!yQp$NG-0c3MOWjxQY=PgO#FMTAL1AV<`e2BR?hE)#(FhBXL5@biw!iV?2{61AE6*7BH2c6gf zXc1;t&AzIvywc@J%@jggzE=IDIl%y@+j zA!P;t{;VXQnQsqu9E9FJ23F7XdIf`2(>3@de^LkV$13LMm!!TKs~`LTmS>Lyli;rw zXG$bl?!D4bC72)KMUON+`P%I5G;;3f`z{g*>fvVK*|>vauCL7J1dTFrTGAkSG|aW%rZ9o9nt z$~@*fUthr4y+V!xE{tyG$ahY+2Mz8~1P@v$h_efL_dMDay+irPViuP6yqvq-;~&HE zXRG!?ojcrvuaNkQ-v;F@#NRXVkb4UCB34dRZoO_B-F3XRItgPU*@vX|==V@>*>9O| z-EPa>wccer1LNK1{${&}28Jch6WS;OgwEs3GYNDYnQ~9-71<PIBsyC5ammWia-^IAhB>{mfrV>nSPH{T?R&! zuHW+7IQYg<0-_8GTQEgmTMY_xSflRyDjA7*C%HBi22Q(8IW}=a7I?>UQ1?9m&;Zn^ zVYhY0SG!?gLJ{(FLXh4W0vy?=9DX2HOE=4?>8b^{XS^CKdYZ@qu8w4qZO|xYG%p z2OVzW@tjY6@Hax(LY%HB^Z>wwm3+KF?8wPW=Ho2gJkMT~(@na5!r2y}8%Hg(d*RMU zqDP>LU3UXJ8n($icTYmQEjL|kHdc*eY1x@^(JUk*H+vVOF>oN%zVikG4`dELTMl}J zL%C>uAij{{#HkorwaiwrTzpLYX<*6SYX#T+MXc4Ajb67t(WFe*Q3epSC5YV+U%CTT zrl!w7y)#TFWM9y33qG2_ix|ZiI$&!T9{ww#1SX-xz40dmr9eS0N!=;@oih#rmk4aj zijszFj=b*y%faB02J-CpG{_@#mnVuo%NN)li@wqx*Dp1Pqc5fsy@HD9v3c-u)Lxda>s1f7568Rlq>iI zaAl?>qc0Jm*2;!l;xJ?<7s5R_0}$F{)#7bCw>wRVnPVWFEvrhcbzA z>+gzc$qJ(d0Fa$HN(69}D!6`%QA!XF|Q}XGz%DP%J_uJCtwc?O9 zlIo@LfM4KHaba&FJf%O1&IG$VySK;4b1a^O_kQM(%KZf}E3bvM8(bo4vP*A{`YTT_ zcbM@?sboKhnS*Xz|FpSC9RjsyQNrSjjg|1o%)IMRrql~S(k(8#Lro_Ih?-64%?iF) z-HmN4{`&L}xO0ITV~1i)=7P=Rut*AXOpkFKC2!t~4z9-tx1kSGBr`P3*u`8CgrS+H zb(z4zui*y>W<1i`c!!LipL=dDxg9w+!=G?!>EN#L{M|ge+Mv#uQ+aD;;Sxl@Jg$5- zEX2&vx_}s)@X@;Org&|3%yCWhyxCeetZ7;y&>R#MJGJL4Zsx!~x6Fd-(8@wKl1^CG z+HXxjJ2yW3oo*v;e8yRDIv`cWt99+(Tu|m@rDX}g(PAi0;WxwCZ)^|&*JRCbTK=wHC|t-tElCg>WZ_U^p?EXR`Kxh9__*!)4t^+Mlq+TsbRB})fy`6;}=XkAT%C=P^o6Q;*Zr3X-*`|QK7c(L2Dw@o9OM1EQ zX43k3Xsu92gR(TvY53bQJGG1k(JATgP}{iUuG$~r(0V~@W!3P=L}=&pwaAkV;Fa^h zhgHgSFXRj5brBpiW@K=xl=93C8^n~86#aKVvo8PGi%sa2r=m6ul09>9a_;?MdA}(4 zY0Zx#?2|V`G*MiRiXLKDkVxdR!`$qFiej9tA^vr&qM-PYg~VZU%C;ObHa>x|;#UbV z3Ku^rb&KrJ0q4`Y-;%1~zm-n&EKRs&R9#cGjU(_>!~sTZL7&5nf@gLg(%y)aDG`l; z-4A*{+G)=Y1G!VQ4A~)|$~9Fhe!(=*#*IXenE|h9L5MYmUHFbw2Kt4nMIhJX-#5JD zgj!=N0e6Uw6!;~Qhgj*k+F}by=aiKj(pE`YF0RQY68>)RCl}6V!=uiUyo@GW}y+0P{dlJ4`v9L--`*4^9n6mC8CLv`&JO`{9&iM4kowT|p8j_@j!PrudLVw3lMk{l`Y7L(>)Mz>sy zK*BvSn1^()&h9zT$TRsdDJ0Ai&?%wWx6zq8R1yyjxo(p+s)BaGf&7ycJo$Rvi-=E%Z zBZ@uN29+y{Iqs!U_ciHPw9rR<-%RyCB=7AHSKW~J^h$8ZS=>BmlLXi&I|uRozI-*W zJ~yg`?)7rAryj^;yiH?o9{cM6@X=pJd=+-xC4Yd3DviT!=~b2R$WlIp#VUA*t%jdxg_n=X5yVDuRTdA+)rI@+7FjLXT{a%4rQuiXW&~{(o>%sNjS;UY z6)zMrmRRopG(cdopWtV`R3oXBNN!v36WEMo^X z)O~ID#Sc6l7w{}$y+@wxalEVJHQHmc;2#2u$3 z%xtemM6OTnToyd`NclCL1&2Ar2x}1mocmm7A zIn51nH$L(dvBSI6jkm_aug>?ER_$(r6#5^xq~03!?-BTVusc=)jeF;J6YK|fGHu>c zblb@jZtu^wK-LBzr-(ZBaScO+P-HaGXW>b0R48-R9n!KnGbg& zKk~qnp_Pwk5rm$>nnJ~%$9;B?Zf93-?FmR0cw9-GZwdmc;wfHR%6QZh%<1;4coSuk z1nM+$DU_Qo(az~ij0H%OP+1fk6RX3oiAxC_aVK(?L>aRA6cR_um-UT%6u9C$5bDtS zw%crFMJ+(7nc&1R0C`_?ruKFtSsbmttpKHwBW(c*+(wFuSrn+FsiL^iTR>l zW&B7nxB$eka7yUk)Glk-~ z=A7PcO*ekwn8G-29&D5Ucz}fIJBrE{8+};nwtedB#Fk5#;BG~Rl#rm@my2djI}&;Da-mFD zrf~qB0D)-+X#U#-aa6vfiP!rZ2?!OI|3aMhC6SqvV~ zBk%$?CH(YjAn9}CLdCY7E5nH`@mFjCT1p!(!S)dnZ}XAknzlmPmVFQHcGR7+Y3KFb zCzQNM1R!t{DgD5-P+^WfS5c8KW6gyjW%S&`8PCce@LD#Rly(sx1s<YG*@InaP5G}VFKeOzS$b;{~e$3Ox1gXk$l(xHY)5}HPqr%a^qa9xH zU2(Kvfz}&fx|IB9a3kw*m-cPq_>pXVWv*X&ShrK%x<1&NDZ_Nt8u8|+xd>UhWdhmA zNN8;m@Qg>xK9{pFtbV6h>C5Kin%Lo$Vi*5)Kh9S)rsx@BkguGK7@w1#=%#?Zo-SeP zGP8Coeb}T;#+WxIzX)s*eh-$DW=&Q$mde^=SFMctG)?nj;!M^aD~b21xQ-SXG(GVX$+Lz%`wyS*-ogaAxDlRjDrES_lBT0_(`x58x6 zjKx-m-=tw>Ry`$R^lyVl7f!dOcdPpy%fH}b73 zI#OsTb9p*Xc$XL=Pm^k^J(WGv`i@8x1MV{Oh+_EoeQWmM-6M#Ho56}=n$YVnJ*sAH z>uu<9vKHGvo(m%QcW0+?Sp@N8xI8jTE~Ino005Sl7=(v4+? zZRTo2P<6k3oZD^`hp6WItknngk!zh4Cr*{wU}snYiZ;WN9~A<>7adkuWn~W??~Pu~ zy(|rxsw8qOjjTnfqN{!DG*QjM0IcOdBd3uPE_m$4rO@uJlo)0%^42}FR+Ve_*tnuL z{}@DrZ#CH18&_CEXj5Q(No|_C%ARc?oWOTvXK#bAxjy1dn#7LYcF7rD*h|8zS~AGU zOPe?yYx#?xBIx*{sjf|~PFFkIGDeb<2r`>>M73GFZ34Bz4_EzFPmUVl1(5gEISaC3 zwoj3-PaL_JX;c?PQcifRa`S3b2kzG!D(!OcU2(t~Tn(6PwyLfwX$tc0%C32(4}Jgl zh`j0nmGpKXD%_Bl&VT(gQ_Etul!2}ZyK+;Ei4%zPFE#hDOU@riQ@S^w#7A5E)Q$sE zZN~MRvmr%nYR%SBYoFDjae#^JgGQOjxdClMMWO*o=k`#klSQQU(G(Vr?11f+8lB+wBiYAcRtB#?oU3}$Vz1d)(V|9Lp?Xh6mGvFmP=2!oE)7p1L~@Q_{WV9qt1!>P%|>v0p4V;% z`lmbrSM=E^<8KK0KmZpL<0`un(CpYI#?#8J6#7{m+C-RCgtlP2C)8Gi&Fh+|c37nJ zZs0pv@b&S+fY~NYz<>?2Is?*qdI%P`DJ9~354;2J?wF2x*e2JLtMZuBY7DNoCxw`! zn)p8X14B>D$y=1F&YrB&UZD4n=Z#2cFXkBna-Aus0PwDC&vNbP)7KqO8O+{;~CxaRR>06R5DbLRC=$M1SUDq5KaT~UwOW^gv45JaU|@*y$9+0O76%pJZB3d3+Z#3?W;WVUf!cvd=4t#qS(Fb zVG4dV@*oubv5-DVaf<`aSX#n1UezsnGeqDFslFdFMRC>(vS$N*VNkK&d*JK$drU6% zMwww%xgu>kP|%^jLWt^2CNk#53|I>7L@WeYKy&BgR_83rCql4SRO@0e{7B)=5!ZH=`l7ZP zfEr*ao9m)yzUI)#i(r-A=V;oUj9o{t%oONKOdT`Xe-K5!1kodtHaoJFBY@8V1wG?LLQ&@}fw@VNYCqmMuX@rj|L>R>9puN=@yPjnrP72gt7A$?0eTSt$j>4Rf7sV}nu zKtJ0D)>)q}?sX8x3p?@n#DhB5XRzB5sn_9fK=$4L_1Fl=9mW8Xzx8|?5yX5J2(QEZwd}3E~sDuM}mPA_7j#Nc09~rhn)9?1V8vr2*Wis4?-G z&)_|k!BtEhbq~O<+8gA4CYnqLPHVa-HN~xwNcId<>+~DpaMK6E zmW_~a+KK7qAq_#S{j3}|s;*T`uE{b{VbSEsOwbfKYlwzl%?&em1)-nDybBlMi|C}J zPQI+)a46C=7zhQ`Dv?V#5o<6}GqWJ5Qo0i((jMBXB$UC&ot(N<&90~r3lG4nwP{+M z%eP^xI?lI|2O;HWU{BtrtkC4PPp8y$K2@=4p>x^pxBE7aO^sZ>W+owmHeb~c(Kwb~ za$^Iv?N!hq->Q0p4WO{j50N@piw={ou&02G|X$=nJzRgI>Zx6B= zv?4S?pEmp_%+_Y${(QBVCI+wtfVPCZm0kcp*Vd^`Hof_i5Hl}#fR6Oy%0eMzuHc$4 z)RuaLKcsocbWi2wknBj=Oz}p`E*y6HVXe|>&86wg?!M}XvdN%t!QN{++}Z$|jLo(y zWNlZ7eN|_>Be_ZS>jup8DR>!wL<9bp>$R%Cd^N#G*Yl0U!m;>Qyb?ohPsk{r2sHh=l|{4-f|8FYWDM=-dq_OlpN|`#|Iab@IP&O zEk*@SN%xY&;jKIRH;aP zGk8%xZM8HhK$XFYa>M+_JHVkV1=vxMW&FW@4qb($QRCv68kBnxI9*M8KfgTU`pIfj2{K{@#LZ$1 zT4W+lXw+pQca!C@Miz<(=t)&%fVY0Sk>5EtnWl zF(ucvRx%0S6&jSPn3oIy#9I5CII~AbS+I5d9w`Fr^X=DdFCs*rqHPUG;2dlmna8=( z@gyvpTZ6q483j1)&BwR@{3;}sT&{23y}-@SKdq_#*jSua{%rd?;9-!7<=8X8Yrf|b zvtx|fe_3j*P4?9^^IF*YVGz|U`bf=FQMLCSQly_63d`$ZC$ zX%rEwRTi6@qWbfis~)3GXy8B5+x0hAno%DScilVnW9hH>aST@FqK0mP#_aWpwQK7n zv&w{#1{OmNqFUzxLRoV0dPB@K@m)d3)hM5+zbOlK+Cu18$=agrsfa>KeEm;Jf!p!DQzryGmUym4d*%?~}?# z7@uqzxYFLVFwEYy`UTU+Naxf+k}AOD(Q2PzkXAQj7M-_T9xd-kRha zZyg270r;0!ys@&}t~iGc1i_LD@xBD$0$>6Ngu)4eGQR}h@&1TWCZm-ov5^HmBWu?? zS*@m0kkCa@qkkf_e1h(IER2hx7$vk_cRyY6K5aOey?*TcMF;vwWzi=v+@Z<$^<)Hf zxW{M>(LE$hJXI>z8`4=}%r%l9fdwu&NQC|inBPil{^eC~Zid{}h@&k7G+*jxN24~%xet;p?yT@DgUYn5g$vUE?PtYJ1xG406spXFCjDcq@pyO>6eI^sKe(TJcK$&$kmp{w|V^d<{Z z0F*FKI2)D^iG0HbJ;4=|feu&25DdE#Ns<8xJ$^;x-ngU$lEjAEu8%Vtn5LWvDg$+s zS%sgbv7`#qQKShs?HXtYT-LI7O#rJxq^Zf7bmo8ut`M26q54QzxE2O0W5_z$Fr&d< z%w2V^6v68@2-ITR!CzIv3~}0;F_w;Vz$@{`bs25Rz07Q-^U>zw839;8{7Jkl`4xrI z%{`)`%S*ZS;VD@8UvAN5+{~P%-}P}Ws+&(S4Ran$m(wzl!b=Cr;^Vk@TzTEVF(GmZ z^ND()qB%$l%TDZJSe}0LWRHY=vlRlMReh*o{gD`0r)fzP`CzS7K}S^mFvKdA0K|1M z8JbVXY~sYfH?wdJM?c>)^8_uch1KE{OWbcC1fl7a837D($L%O@KWErk%1OH+Y~Z(W z-FZ9ieOzR&83e_0ia43Kkg+i4K{R>0Z7*{h$P2qLFz(7f`c!+hVso_C?Lxil{OsUA zb!M&z2XxOsC*!3e9`K2xN>2Kj0j?i7km?87!(dFEI(0KNWLO+-%HC z*mT~HA$N)5jZOq0>UaiEj!*)W{WftPJA>JbQaV)W%-YkkwXCg|NNAuraS1GQfZIs)@mHgOew^ znQWsbg*qzT5u61n;(BGHC@447U_^PIK!$LRzz+gMT70OD6SkWv5}X86TB=*`T{1~v zU9QDoiWn_X+G7QiGITIA?T4;O?DQP7xE{15N3n!d=UPJIcnLAtzL12++s-3m^4kOc z_Ih>VAiQz6nCdat!VSFEpu#N2IngcpoWx%V>bRv{l9;;uUi#TFk4ln&w*D&fm7Q(S zCp--3$2p>?4RCAffL=|3*2D-=HA>je9zopsoL2LsHOlL9ZbH)yye>u%67M zz~zBTevfzp2%krqUY8Du5kEhFMS>Iy?hOt8gbYeW2_sR#lXQ3W+SG2z_BybY+RHI9 zG`#P7zQ8xh*qZeIJ5`gF)6?w(af2hrqio+3g01d+s}DuW<6L*JhnQDi z)yTvhq*{?ssE{l8Vu)cZ8gw1V&NLxR@+4ct2B-^pvJ(N$4h-bWTjR6T|mp z;zjC8Wo^hJLAk9HPf8jYug5HnVgTg6QK5*Ewu_73z&9paaHDCEVYYZsM&oo>)FT`8 z`%9~R$~F$|F0~aEFcCKu>1CDDxZ)l3h!E##SWB)%InQN21+?}Lefj@vs*Y%6C`inM z1{kf+jUd06VQ=YZJn?I{kW<-*BHy-;QNSK{*BhmE{Q9z{kPU`MkM*H33a;e=u8P&z zaIBMb_vk7^WUJO->nT<3FiLqcoDffa7ts~GQLgzJPP&x{b#d(1 zN~*$VMbowS?WtZ}&5Cud_bbOSXr#pK!K@KDg^h9O?i~}3&(txS3afRD#u%H;blogU zWa-;c<;8#gRV@)$`(R^;6u&7j+7$l+P-MVmv!X&GNE05_Fqt123yG)Jc;RNf z_4!rui3sCxf*QT&T@X4Q8Av$U+0-Ea`16!2u{;YF9QR+J?aq&_40dLIU*A^{eXJHD z`P^~wpjkA3_%Vk5R+3WyDF!O+$wB3v!+r^tMsDC4QfeQjTX$;sR&d$j=PUbo)b0L!{I`)glkv)%)LurFg7Mn-s zQl3W1v zCe)wVYJEItCMGCbL+u}%#cRy3x%x2sP;@&)LSldEFv&iRGOFFIWFVT))Y~;2u+Kq0FFj~aPn%I*@**2#$gw4^$n&$x4GFSyA6@Gb1cupm?p?h$iXGG^H*$NjW znkc-==S3H&hra2dOG32=ZjHVn*8+f)FNAqD7khkQ*0$Ikh^H1lEC>WJb#SD0;V{e- zL$X(ORH4ccr$)YI^AKMcU|SOLG;BytzI1^>K6uu>yg%aEGYMRh?p#n06zj?nAQm| zXAn*3XJ>Tr7P3F9c%%PT`+pRg2Uw0YmGE9hH%XK;Iymu6d9Xuh2D2Tm15={Q=P6A7e9%WC^+B0L zuh7m@5kEqAG+s*gBJxJuJgI7=FCDWqs8*GLH${R71{u%i7cnuU9Va)JEWz{Hxh9(^5(vOwbtrf>VliIVcVQ?{>5N z#@a@;g)flr2qZ&{8+yTO@f5V!qsyHvFmMgWXqV^+qaW4K7iQf|rr6TfSMZR!T#LL^ zMcz1DKSY*1bt-3)49z}GLuK07t9r+hPj4UQnHQC9t{SJXgee?H|!&5*BR;hNb55q=?UqY zwf5o`*=DmxN0uFo$Ks6x*JrVV70vS+wbK(F$E0+*VEjq&67o#W(wG*=P2ElP-Sshk zm|}!XXzi{ic{{sgwhjckJzZRVeQkfYo{l@O>#sdp01)qU9{!ub8OM1jv?(e`*jeiB zK*C3@^W^L9KxPhpo#kq3%2b=>U|lXTVTsI3d}4tJ?(s*dkmQxbL|T+}^1J%*@!*39T!gX4W=3_aupw?&p-{d}4NcUB#e zs^z34pk#{E%6(1JST;30tu+aWK@>?FD=Oo(F$v9l+=i>EUcJ7~frQaWY$_^C8ijt5 z&RB$PQeNuvq_fT1mEPpSy(RMsxir1@7|GO>(gZRph2|-iOv!1}b&-o>J8L?1&S`VJ zKQ2p`g?TPD$#N4+1@LUYZ9w%TV&v-m|CROC0Z}&3v)Aq|2wNT+mnN_UF1gmiO) zq;x)%G)RkpNQZQ{G@^7%Db4Q={Jihi_xJp9b2~FTvopIh``mN8`_Ng?f+6mBn~%8& zFW;6s6^_mnC#K4=(P^Z<;7WY<{<0P!x6JEuFX$aV^RmNBQI>2BIxA+InP<8yis)A5 zlMKgp8C-68eMQ8g_Jwxm@6snZInp1_H5SkYWb<<+U|q;~B8JTE_XeZc93<40{=mt^ z!?kWnlJPdvdHwOWmRT*uArks$lvH-BeIvilj0bZfqfm{#U%tCjZXG31h9#vGKt1oS z-mfT1USF;GO&&$K#1dS1%SOjT8r#Rl9|G1NSW&N%?YsC9E#+08h=xMi%q7dH_n@#W zc(qEa`|E;lD)pyo_kKq6=C*waJlxa*sqa;sxv9mPrDEn6Aa?Bt1JrE@HZ=lkI_UGK z42FsyoD&Eb1<0qS&Q8+V-jt3ZjL3+OXLxFu4|ZNyHqdBq#rGzzz4v=--WPJND`ZTu zo&{Y8nfukrfWTZ5H5zne&Fjhbt;c*d=6ph%J3)yUCGU;((J}p?D>Jp?d>gzg`iaAu z<(i-hiSKaR^9MU;RRJHCAzmPpu44gX_McCE-&Hu|Ot(dN$}{p&u~*`Ulsh_wTXnYa z(nNER#$m+yO|%W1B?svIENQ!*S1Jl{ToFWSi8$Eu9BPv`;?yx$ea}Qu%HFe`Ge-I#AeqXizUo^qGH)o4k-7WP+bU#4@x!8`h~ftXJ|**8 z--Ejb-_CKKT)7LhAA>gN^)rGVEiCkHM>D0@a zKN+RY=nNA|Uzvn<=Y&cgxNYhrEsNK>jm|taZA2IR`FuD3M~qW!Kdyx-Gz4W*|Tue_p;3b06Ni^L0TRNOtDC6G#wzOR8GkH#R^`DB&Z{ZhXi z*rm;Vbw*Ot_tC?A`eTSKA?ujguZ~yWl{PN$&$K+R&(1 zZn}o#!_$9!glpZy5L}{`^FXlZihPjid zW!=Fhl-Oeua~~cyK*HhjnyGV(yJBlY^m&JaxRG`%SaW~N3K{KCs$kwyb|YK~mRvC3 z_+43m2c4hFG#wAhWGu0;Ps!&R(=;6S1MJjadr?R?$|4tISEgcdcPQ7gO50kYpG+Iy z==nSlsOw3wG-4uEF!(iiXFtC64rEn6#MN}op-6ceJxd}42~$x|(&X0<=W&bYImNd= zMzAnhi04sn%pXdTOn>iOW5ZCWEQwgiogMxtz4wHUeteeEGpZEe^Cue;Dmae*#7Cju zuRE^Z9nTR;%Pia#pE;!WV^6=ex_@;=RHkFy1L<)$E z?Nx@$v|P*%Lg_y;=-scfesJuxopxhgr=#dJtH;8!id$DySYa>a?blbI+t?oJcRWjt_vw}-Nl+y_th6F{ zGT{qi@d%%zH`$tM;M?;mo!;4>dgDFnL+m4wneA`V2_c-6Xn8qvO05~}i#L0n!CGN^{LX_3)QEHnFtR};yw(+NIC zH6mT^aYD>vZ|>L^;$Oc~*jsogmoja0o!Lbr9=OB^mA*h+YYb@)Tq}C{ZpU%`rEZs& z{pfV1ii0&9W9eaq$JnIS6Mw`s!S`RtbE;y0)m|z?yz_ANss&IbN*vd(DK2@;Ac@y_ zhkZ9{Qd(JSEATR)z%>S;B0>>vR8b6KYg~9zyiY{fZhWlU9JGNc_{x}8$ge)X%{6x% z3^!x$F%B5vpZ;BWfwWyMeikeCRqw{G50DmP#A#Hc{gwS}m!+<1$EDwhe>8@pVEOsR zdOLyXqq8;9F9_BXtF4O&G#R&$|NZPmf&J{&slI=t&Q=3@j&k=amo$H@^Er^(1Y(2C zYEP)*E#@#VaHt}+7n?cRMW}C}NKqiy+^$T#x6m7veRR_Z7q&Gg)cnxnRY1@UW)Gim9(S!uH+Y`bNX;R+1t z=yn}wxhf^vQ|hCOtK9bZzOp3qN4c6D*CmRfZ%RpJoTCl;-v+w5itL8u z=4IJO^4OwoI~<7Aq|+M=D?)*2iqj-z7FsMYjN^=izkRC3GNQk4@A4J1{}oyxMA#mB zJb+}u_6Vwq_$^}-_HLnFfTw=n(J?%Y8cGSOV{X%OE6URECqrgx?a`CoZUft{9_Qs0 zA2WF$xs+EOC;DF(pIl^KbRGwOT1JO%qOZ%(UP)!RO67lU@I3BgTp-yq`4K^_&!$Fr zRo4~kJfSq`NH{YU*)FuyNEelTZ4hlcuF8ka%>HG%G>h^)iOkd0G=B8NuSD{{R|Uu$ za#UjJV60XyifO+UH(dEzwip>8JoD02;cKjJ3@*kr#zLDi=yR+|$uv(rWZG&&kK_GU zRb_480k=~07A?cf#K57Kd#tjzVe=~-@{WwtatXhU1Q3O8@;*>w4hUP9^0h!bPs|2u z3r5VnKM{I7tLnh8PwBRwINny|u}JP4=+^Xp%33|~ytp&xR;%{cMi@=&#^W0%sa{Id z)pBWdv|-9R$L}$_M-_)XwAlq&pGzZEJ%3pe;%h?Xk#t`$Dyf^lWD!arADt_o!hXT&RvA*8I%rg=MPAjRgyEexAVL7T zA?$tyZ>-*!%vrily%_kjTV3LE)0P&DjW6foNlnua}`su}phD!kQ=g1NA999}!@-%$!;bk8hd$B(L?*ykkRfkWU(KoJ z+XIh%rG0Z}Ndgp{StKb8y|5>_NYWB0ILZj|a2T3Sav|ofoVZqWVr7auUmMh%Y$Dv) z>w!0eOO{M^@qIsNiQJHHl@`4)$ZmR6SoB}>Iooo~0t8ruQy2h z;I+lDWO+2*%pLq0bq)>#uF7x+K^rMA?@8q6mp$OZuUuGj7-+T;( zP%UOD9LKXCS8;!kJ1&86i{f&B@H%(9seko5!q@)|WNVH0oCWJvLFC*1_s`8&FK?n^ z&kcyr-}SZr{wc`mnHPFX5qrD&-9z~XOYbT(^j5?2y07DQZSD8@$m5#`rR&ncOGld< zx}aO5^_z~4TRf^8izDQtn;h9Vfal3C#)1bi&d&>-eKa$RmEmvl^m12p&5LeZJ9ye6gChSU<2p3deK zdQNI$7x{8pq{%A(E}|B)4Ur{dP>trC8Z&N)`ix(MMwFOlQ=-7OSX}ZTcO-CTUh@<8 z)5lL03-^AP=(|P6ib4#$&wR^-VvdXWCC(0yICZe!!M)U=_ui6mi`SWIH=Go5t7^)m z8uGPyc3Og1vXn>Ae}xpIzp56c8mP}aWK-pjs#%zZHo3L^;DfC;OJMGlrlRtvNSiOY z-Cd_lY4g*yYmv7z$@NaxYc)sEFshrrQvZ98IWD_lDMI2)7xi(hJ1bTNpX6l<= zOZhU__%Yg#7e15Zkmz&474gb$&Ezi~IFK^>eG@`7Q)hpJO2OTj-?AVjtF<&2XM!$< z*Xaa910)U+ze68!GNkaDphO_%K0+9C{UXAjZ=X8)bUe2WDYohf(av#?ppR7iM3pvg zp-MYrDb_d8p=A$3p*f3QGH3KT?MJbuW_psJ9xM@3687!r6p&{+(i4M8p4?ImKeZ=G zA0X>r`b-B%Jy)}%8{fhzKaqWNwD0TM1lfHl+I$))P`h}RA2f;4>nvaw-~ zO5*WEKAJ&OW#^pqO|&*uA)OmV|Y?+3_U0^Ef|%w@Lv)PUxm5T;&i+WfkZZlAzHz?y`6p91yX|u zQF#yUOuD3YzmMdVlE@J=b9*i{g#&LtB$4{Aus@(BNI+!>m2SRC)}16yE!iT5F3*ma zNS?3Wa=nWb-j}rB#m89wz32ilNUNzla#Vnpa8LxcNDT#QwVJhB+6`#z8EavK25ug%8$duPsnU9&R zjIHhBK-&(Q`vbJzdKd-g~GU~zV zmRJ^JE2y-M=pvbe&T;m%in>Muq?Xh}YQ@@?5tJT8Ob3hLs;_rIF;keIMbEnKx&-z1@}hMV{F@wMgx(VHl|Ou)>PX{7Vfq?h;rC4 z>TPH8qd|eMZ`#HlhyB7#H&D#R-6xD}uOo6%?t#Ql_)(xUO9eBE2`%!m!SNh@MeJbt zffqb{u`X687+n?mNR=ZDf1tSHC8Yap_E=bNN<+5WR$7jMeNGrPj*i8tbtqUzBq&KQB`xJf-M$ z;dJ+KPdc!VIFS_uMZBqcNuD5Nmr5I{dm_|qI`;CULE3TR1B>>FPlP}HP|T*Z;=AJH z^pYXPx|E_A(QW?^a!pKjY*&DY4cQ0&f1n241YIM zJf9NKa7#lgiyHdKRi!EBrfb{0O~5UjjMkqET|OXWBjLG5#rjNE*tzD@Ch|!Kw`O0S zU{JFp=?A?N&SnI~M~6WJxd#(2qtQHvw5Qd|Q!k4vztDXzim5%X(#-n#LvT_AU8wX} z`U0Y+*d>MNv~^mFK@hX%lss1y%k`ezTO$ZBAroFhR^}&t?B^M*mpeyY)FQ>Ie)fAz z`hFcr`tL^6LE8v6A}@;3r=%tJx=?f8J2oUD|;|0<~ralLfvX)np1FuVz*-wR+1>BTc72amN^vPHK`HSd&)l?Wr| zJTJuhNME&YD8+7~LxX)BOzmW1iYsx;3=b(Us8~_V>3s|t+`#H%Sck6H4)XIiG2yPH zB-)Ch4H!l!;|Nci{X9lqdaQ3-eUaORzwI4iBD2_`YI0~>vGAg*pe469ux>42SnL%@_vdBC<_^fJ_e)E!w~22yo2pfb0rRPr zTWQ!0kK$s*zFhU!NFz)MjVw`;H`Mxqj}Bk zoK?^xIs1=mQ@=|u(;($R6#Jp5(hnS3Z&vlvR`mvw*^IOy`hGqub8Ar; zQOZn@2y-S{np!@U7ZjJ5+t|EJFRs-U7e%PM zZA|I#h21GUe_65$N3tb`F#j-#nSsTvmH8caSrWUQBAZH>OLEaMm4^N2NzgVVy$BVM zxiFjXwU1j8Ym(huKruP7kCYijEC^d^(XC`!E`|O{^;4;{CPWTW#N_L`@QX`MF;qQ^ z)x)pDpti$hi;r^L5f!s&nbaQ;+< zk-?R4le$Ngbo+P_FK*F|SLrMv{o1s}`HZy+_@X@y^Z zbx$Poe)0(G+S7{BD{`wWPi<02m-))fG48*d!nu9MTu2>4X5+Pz%9NJOTj56cLHSw8 zDUI9^0+{-&b`IHXgEc||MriEleLsza$|12rDxAT6=NLe(YCb+}v>0(O(u;7%q}u9Y+>qYJID& zONEX&ztfC@OrYr~u-M#HZ|^u!_&j_nGT%}dHYc}_OH~c407ux-^dYgfBFkXQ3^rYb4fLVW^+5= zoEU$cEWIL*FfPbS-L^{hj$(U?FXJCgQ|6zu^tu?c)VcVXdu$JzDr1-{9gObbC`hyP&)vabgp!qz>44A~|`8APFT*K2!;@C^u$u%2FN!wk? zthzNyr95saKQob=LNjST5(a-^m9U&Kad`5Go|t98OC>;rniJMD_zF^ik2W8 z2yidh8?WlHekRzF8s9A!-PkK>XxHC8D$*LTjWfC@NxiWoo5wKr*{!dU;+h4kmN>D< zIOF^gyxkmotNiUrHdicVRcANDG#mQc3wDC(E>5|m0u32HiE-X;$VFtUSz$}@uGA1d zRt*x5rL}(k=h4~muBu(JQuhKE)^G7MCl!NkXX!I1H-Z~QUjF8@>dLZrA~$cdCOZd=Lr-x%k8AkTC)!z}ib+7Px2dU2Hq zNB>6oNFycxgB~ifJ_Jeo#IgyMRvnXvqtV!@9bo3G~nNh&v>TUG2?1YChwKikGsbRYx%&B@yk z%`Yw{L~^@CBGyB0y3>)l{MUmdN%6WeM|=WY>#KYftV*;PkRYJ&;{oYyjQp+HBU9(3 zJqz66;)jmNM-s#J9!~3cei@6SD=}YX^KRf9y#pE_1B#0;NicI>;TGb~)VN-wI7BXR z?W}kf-NYG`L5!HaCYKVa!}$8gZT27VpO9yve@--C$*`F__##hBCQ=nLj>v;O0T`Y8 zKh(0ycN8;u0vQuxYdFszt$BiVGYRquDnvZ6)aN0u?_}>|Eq{ z>b};uswBaU*d=SvU$BftsV;aY958%bQ`A{!@X1M~e^vDQL{yYWF>{>|ViFktLc-$Z^}_y-V+TYtw(J=m|+R9^3gi&^ZzMaeC7&@|x7~)RR%@n2DS&lTXU_ zz40&fW-+9OUP*XwQ1jt0sSDa>`FtNo8wp5o3;GEEnNPZZ6c75VDQx1@*VJ$eya>zR zMjhcp*0v!X@E#2y2PKOsR%d?p#SZqK*sGKa1m}rhO*z7XHnM~u!H}gKQMk;A8@U64 zSKq#n;*J!+rB06>1qp@p+Lg~rJ}wl{seg&J#${hXoxforAcp9+@BABEk0SdOlUM)Z zl*&HQ)z74DdgML>KhCbdq&KA8R7jGY%QMOj|3E|CKzRwZ&$-^I_A# zbFTX4QMKU-?}ZO$NYBiJ7=CbvG*3rY7P!MB=bA+B5{al0`N22zkOSn7c`l+GS%Qw+ zE4c@d+k~57JtgwdP|S{(?UiLrxKn|_IM$T~7Zj;el9~c`Vg+K7HH5E!sd4PRcQU=g z*kko}!m%N~PDRaTkqgfuOerv&nC(3~@EqwW+n5-%*?mmXLqqj7{+-4OO6?%Q?=!qo zfd_ccW)#j-)T3?TOcansed)9&ZM+9wFT|r)LGoHUc8*p#J`OVzawydWEA$V&(bRXa z%*o(B`;Pgj0QrgVrH2F$9ZMe1tFJPpCle|vRH_eeGN)7w`m7{vg2XJSq@pBE(<0e5 z11PwW#jWALjG6^yb7A}IKYXqIBA8qqgWA?EXut%G!6D?$(u3bk_;f$9HCvGxmyl^i zF=-*9$OZ&aWwLO^+0--3LZeTSiR^EQWZmq{qTuD|1@qFkhumBRG25JI9#;e?|C%4tF*d?(m-zhDh?6~1tVw1v%RsI0N38TQ3s z>+BaWkTqIwQ?E*TkTgt?=QMraCvuGu^XP_9yRPaAl;oXcSUr@;qq9Hu_{UbK7-)fvq@Ph+{me#str{39^FahrQF#O5HW~u*tZjs+mGJ zXG%y=mg~fAi(K{!RsMwJUWuM;S#osKYN%}4;l6ar--98mE7IIqD(?{&l&p@Ohq`^)*E%+z3&E=!y*B12 zulUr$W25-ei%MmDw_>fY60N{TW0Wi2>)12Ql<|`EVHu+4L-FSpT5ihqis+{Pg`^{j zGKxw{R7|TjLPpd~&@ZmB@&_oH@IkHPs(8AYc@N3AC|WSib*WsZj2Sm8;{Zsb$5^qO z7fxsY!)|69g0lEp<5sMMf;@T?j8yxOvf9x&-#j{}a12g)Gp+c}5}I%=d7M&G9E)Hn zJ>D9Zy67q|J#+H4SU*(DweS3HY%IG)#JNaUEZ(ss9%DZQcxRq?0F5D=HP6-EPTR34 z9QKt;9u6eXJX4P6dn*4!GxiK(p!`EKw9`qmP4>m)J8@maks8!D==D#Jr?tMk1t!Bq zBjc9!sU8v^`KH-+Djefz&w-he4kr$og(cH0xhrRlCaX+37aNkcF+Z9Wa}TU?t!LS! zH#Hkh@=Fbb9xu8UrJwQZUZ87siw0$wy5(934VrXfiYI1mD(0VuQ;Sk$wSM$bu|d*g*Gw*@BBcFU<08Z zj$%g^e2z7ATZ}SJlK&CA1Uo?l?p2npSFFI}{3*#AOD3(HtXHWj^N`|Y<~o_J*-c5M zm!X@ab$!_9rR4A33}(Naj6Z8KGu9~`)mhP%zq6#4CC#vsk3=fdS&If9<$uBPZ$06P@mGu zF0=?^#k){`WV0o`MhAy64!7!bsJ*S!ONr&IOB-C0{6=g4OB?N*8Zn})U2H#A=|LcH znIwJrBw&Eubh>kx2pcrCW>8Ggo`9FWh$kyQBN+qT6@xJHd07r}Fpd;-&F_n_WY5Ge zSN)v!eS*PLjd0tX1kFb*s!Z15`qC^VZ?sFNYP8=eFWQm?Ytna3)z_#YT8o{ICNWB- zYX!9w{W!6hQu$r1d=f^IEjvY9V`3z)QZzEAvF+@wVI=NYT)IW(M&4XnQSf{6^n}d~ z)HtPVSm^M*c$+m7lDEHxJ2`z$^Rzc&>Utsn@v_CXL6gMZvfH-!zBZOi)?7#Wr14Vj z;}zsdk)<5WpRA|g1059`^j1T9X z`Q$16H}-l_OGiy+2aYoO9NorhiMjz~15APpAJO%~$ViKUUd<1xao$OEMbp*|-tR1n zvnD=n6LRY`!$V$pC2UU_xHfpF&rN;UB0YVyt=?Mj_*76pElXX4JFijb6H4iwb%UaUpwBc{n-TFVM(%zT#(R*8AhG2`2G(X^oo3NNvH!%# zwc}>nEJnsX&kh4i-D+rp8~0(}37w#XwXPR&rGjH}Qj)q#_y)u#ti>0-96`CjgxG#c zTTjayJ@C7B`^SQl(!8OYl0t_thjPgzmq*sEMzy6ro0x?HD)l|H>t7XA4qOh(2G~X* z8T)ezxc!jh(9Qkv4~tQZ1C!U3bG5cVJ2q(2{I1>8jK0h(v{JIYk#rq#X!tz*Vq&>Z zzN3=ENPZ%_3=;Q9fRz(3iG-EJadk|yBOi@Ai-0(|X8cJ`krR=Txl+Dj6oYHQ_q?4b zV%N`V;`33q-h+I}m?SP+7J1Q2-0KquQQqF)tXG8~FHi{|u?Q_$uN8NzXF6u|4Me(& z%P^aQ76OXIE1fE~3<1=1%!n7r^IXgE*S2I}7EbfmKVKy3;UjnZ4$!V!rW zL5eO8gY>;TCr$K;E*5-olj!Ui&3dNmPi29Oto2|H{q=O-dPAwWz~rl}9aa)vVghv| z?^|s9gOnZjMdgIup!|e0B6@ydcByaNwJ$8iKdFU)f0q;NKJbA1@~pAY{n32R05bV; zn}g|3EaEk~n}c~}th}cq?K9LHcriN-&nw<9xB?Kbx59*WHf+4d{X(=S-@C?>X4kwL zZHidQAMFWf%AD)glnxtYn(QwsH#+8=GnkVlWETAn%UU}qlr6yg#>8fPDNQekovEIT zC%5GLAUNQq;HoodQQSjP+dUTJg@!_RusIQ7!bn|YNfgs2cm>@x-U;KKnW17-x23+V zp~@bln_yk%MH@jaksGRn?vP>j@MX3SYtdZ4@tP)CqUhKH3KuUDkR98 z!YjlV35R|cZJwAsXHB)`=+Fgzz4%q{k}6l_=R!jpvpv2Fe?%`c;?@V7A|f=g+od zS!#A)e%3-DIKUojVETQ!o9oC+NGQ6#&;B+>Z|pl`W^-GF50f1 zZ(~fx)G~C02+Q?+5%g6K!>x}aR3vs-Dr5~*o3P8Z7GBp@dOn=v=!&wAcuV-fnqyt1 zVgcg8J5-k8yI`oJ<(66+wA47$WIBw!XF&U*OPX-!W(^6;ANs;7v!z%1Wq7H?AL0f) z-yKErV$&|=A4h!~dwqy$WOo%;!L?LS?%CSm$yV>tnnLHC_+pt?W{Na)ciGVu^!pg8 z_lK`QH1C%e1xJhBpjg~jeONT_u6)x)W@rT<1qzLW3|h5>UUX60{94^Ke9EPK7&wDB zd-6yphh>NGsjvBmgE3wYNNces*cuwAtyFQ3CGmVQC-#5KgWTO;uYZbWw{{$SW=Sz) z*hwguKSxg?Re`+KhS|)fpp8?(SymS$x-%%ms^=;sh0OU2RKc+7tgOr=wEGAH3lhy{ zs$8tU`t7-xTdcy^eohaqRZ4~U6DuF2UueuN%wD!0`PFr=n8}_>sq7Zgljlai+ft+e zcL6Vr4BH))AH4uwYq_tj-D3T{kRVID+>HbY4$k=j931uiU;QRTU_6MnkqfRCk$-I; z-F)RJcR@ig6&~Waa*h}iBYCht3V1HJ1`oZ+Zo22UCf1}NKVNS|mhVX&U@a1X7hHxX+?e)6I>vB)t*Pk|7 zLn8CcHXn9yh#I5)oF#*Zu|=Z~g~MgG$rJB8c5`)%y&>j!^7b zL@E4qGHsWaMo!IP$%0O)Q9h8~aGtt-ry{W?9W{?jc-%^N(c5GJyCt4Ocqz}IFqTW< zgRpraO__~~{+mGUSYs|k@ z2GQ0!tl6!x4{W6Uutn4rIxkYCX|x+qdZ3^?EZN8J`soLDFvKPYadvmZP+~2xEBU1D z^>Zi1Cl)h9=esN^yz)dO%#~jL>Uvd7jbW_=%VIj5_Fa*Fg&osg^bLBtB}Ku7xkx*j zLgDW*L8N42-JR^IEDZzDKg#8khCRW4Xer`ZeUqo zakHoKw4VwlbRYU6)Bj7s#(_4iGD=)Lh1UJ(HHXN2_S$#rc9{q3JER?XN*%US8b7{Z zl9%m0NAC6AfHw}3;(et}LlQWW6u2HexU{UX7O4zEP}!1Jz{z5&X^gGOz9@G_mQHg; zemvxYyfhyTc}~`Y7RI|qhomb`IQ68U5zVeprq0z+wPd53$lwgqeeo7Ve%toRC)VfS zi-$GIO|ywas)SR7Rihy*vLEkQkzZ7(WMK)uCz|WLLk)g@;f$wwhAY6oO)dYLmwJ8`%I5UWd%1ZuN z{WHs@WBoPrGsQ!jtDcBt&zwjUE841OWKPWW4aMGQ6uw2}{$WF)kAyzYe>}RwwXl_Z zL+F9t+g;O(ry#KHAE?e7yv#zG?Cy|TXM>hwKy2#nNu9~~p_jMlSzcNH4oWqxfJCi~ zY(K%4KE$H5>}b7z@(dqDBosh7td#h3C!Z8mld|svP6WMmbx`$WhKGW^D{i;T%y(bh zD1J?<9h=Vyukgn%XxwqRvFxz~#60U;kbStMr;i!xD9|@u!p}(8KJlB!ahb=3xhr<_ zbuC`~l2Bgt>CKY(rDPj7Lg_`5F`X7-XToZ&j5*Rl3v&O4%=~eVSlR{vh}FO_=Vf=?)r5?pXxin-dzH z5u$%#Nt|quF8Jv!_zh#w`;v8|Bfk2*y7J3|1AYPDIG`H%j#Q<0mAu%2+uT&VZ#JZ( z>LB<&;aI)nFl?oMNA!PF$=%M#U2-r7Y#A9j1sDYp_Jx5KtaZ1->@_=B^X?pmuRP#i zcVC8u!L@fMKZMADt?$14d=VnCT9a5dv1iLUP zU}_SW2L{KIz*;c)BpIvO+HN&zdwV15c% z5e5%a!16FClnPdY!MA_lOzJ%!ecC}I#>e+B{RU% zcXBfB`DZfj+fVWitPR6EzXR*T;P5-JF$}V1f>mG;l6g;`&b+5{W!;w}vcQHg`c@X$ z6b2Qu!A3Axl6{Xy$pM?d$~HOo`df0q&tYZCT(AKQ`sadmVQ?n*o}VZ0-XAe}_j-2n z?)@f{e^1ZPzn2dxxaTu0xaX@YxTg~o-s|%y1Z%?N4i(z2r}W;Z zE2Z~7kuJN}Sz2~)GfMe=-RI@^<(Be$+2j@XKJ};otHIjXUvY0M!-so05g)(;FnrGk zur%$MMj^MjD}aTucCYC72e-ssvM0!nO?yEKu`3q=$pE(t?8{hVG{O-<2OA zqX-X}D#Mci*OlN06fj=h_dj)Zp*!#Bp|YUsH2=Bs1EQUQ_K#pA&i2o4VV>c)Sr{D9A{_pC7=!8muUr;MPDGpJN%=zc!-e;DE5@&imDFjf(mE$~%f zQiOs4SeXoX91f2JgbjjWQ3$BQDCv>+l!z)Y;hj+FrA3(yXfxxXyUi&761p05uYbM@ zOmN2v?`T@O4n146jN-pdMN0%8R^O!qK%^R~g((kKCI;N9!Gti?-=RnlP9Z!nRt}E~ znAU)i0OA@j?wwyxMz7!uq1$wSL#^Wcs{y9%X-TP4K2<1D#gIcicU44Wjy0Q~!PsnW;07{tepGw@|+@{Kd+54G(ajAus|zYwy+E)Iwcre0ztb z0z^N9abONF{S3u+ff0Z!d;}_Bx$d6#0*Xb5Kn95F?sKa`9keV#23XgDx$pFrhQH}? zhx*J4+RIdb=?$eo09rT@sPAOm@ycM=l@+PzldMJtjWT_(1-f6gVSne+K-Up}$ZV{oseg4vb zA_?d=+)ZadL<3aTnKV$`02a7wmm4WB^k5-4xCW>le-CIgIRwB_|K1;HQ1f7s`hUd2 zLDy>kxv=5(#3vMi*j6wRVB2_agl8jE5wg-fvZ4`8bJxZL*VBYiXd4ru+PMB#9SZXU zu$BK|#Q{j0{)|eV!dgZTXzMUg;NX=0;^$EPKm2}8_dWEX391xF^Z!ufED`7cvgZ3X zF*QTUmOB53oMD523iv|F#Rv~cM2n|qEKLk=By&a531=~i_ zC$j&McJ>W);B`Sw`nv+VEldkDaMSW9artdoXn2!C<4g3v*$8BJ&uZNICvkDE3dWK_ z!;l{uV}DB>2!pYT-MLa;DXSAfvG>WUXRh7!(KiOi?+XKxx=fw zTKE7;=lujg*$GvScYBAW#8g#;N5BI?oem8GsCONqM1*X3Agc531`?=*mB-+L;ZCq1 z%=_qF(DFJ0tV{vOcY%rTI&LdFQW_7c837u3f2U(xWMIAvn%|4N?k#VI)$c)rA@6Q2 z$-1FsXlJ$ef^mUo-S?eo2t#uGK`wKENC3rwKeKtfc-1s1RLd|992|7w`RB?HK*hNi zQpX8;4D9#+;ZKMe-hkyT*mIk|hp02rJ-?_52o<2{1ykKw^6BkE&1-0QFhg%EIR23i z%OQ@B{^6(iSALs|M;0zr{vLG3gw9z1T=@Zw^e}$uJE{L3oc3QXYDO3rC!pI0#)bLU zz7J}81j{{=^*IQc`QHw@PYc5mYJzW|{j~|5x`hAH2Wz1P`+YSOa}Wl=2K4|;pm0A_ zprjy-!U4qe-uFT_3^}U_L*fEPVIZ=94f^*Ws{cBeM(>`VWdO{0cUK5Kr^h7^FdYEn zBN!Rp)1W8yFq-@~mt+6aN?0yeHU_~tI$N>XKu%03^xx_K6QdO@UYgCWGLo_jBR5%>7EJMj~i5K=aZ+SAGD$ z9SC@m4Wb6hhyIL?yW?k|eAv+56Z(e}=1T^9z+nhX1@Py9NP*blKm7k5WAb0$t2@H@ z74Q6Vch&>IHVpMkqVv5N>tQhW-AMTRFoAzn<_Ao=!dNMR#o_xtIvj=?hV2Jnjojxw zxe;jCZu)@$u^JFP5IyoI@bC5(LPHOL4)DLtRSdb~B?20oK_efl;Ucx$%43453I+W*G>$`2k{1p7`V4j$lP! z6X>mP5Ol!&okeQufr;@yw|;+Zxcb+D&^|M42bd?oEO(>euV=M?agl!q0ndoQG(aX) zFU*#*320-yH$XrfJDA}AE!r~&e6Fd0m~2&_Cy4H!;?xi$YA;n17HKNl>*2bjQc zt`;t?|6f?oA2FOxFz<`<`H17@0 w-1~D5hP{>p1Fr+16BWguh+v(C+ROM1P?`mkVYb1w!MTdV!MPhkeF*pe0H(s9jsO4v diff --git a/src/net/java/sip/communicator/impl/neomedia/MediaStreamImpl.java b/src/net/java/sip/communicator/impl/neomedia/MediaStreamImpl.java index f1959237d..952342b17 100644 --- a/src/net/java/sip/communicator/impl/neomedia/MediaStreamImpl.java +++ b/src/net/java/sip/communicator/impl/neomedia/MediaStreamImpl.java @@ -827,8 +827,10 @@ private void doSetTarget(MediaStreamTarget target) new SessionAddress( dataAddr.getAddress(), dataAddr.getPort(), - controlAddr.getAddress(), - controlAddr.getPort())); + controlAddr != null ? controlAddr.getAddress() + : null, + controlAddr != null ? controlAddr.getPort() + : 0)); targetIsSet = true; } catch (IOException ioe) diff --git a/src/net/java/sip/communicator/impl/neomedia/RTPConnectorInputStream.java b/src/net/java/sip/communicator/impl/neomedia/RTPConnectorInputStream.java index 7a5d6cbfc..ee22ba877 100755 --- a/src/net/java/sip/communicator/impl/neomedia/RTPConnectorInputStream.java +++ b/src/net/java/sip/communicator/impl/neomedia/RTPConnectorInputStream.java @@ -86,9 +86,12 @@ public RTPConnectorInputStream(DatagramSocket socket) { this.socket = socket; - closed = false; - receiverThread = new Thread(this); - receiverThread.start(); + if(socket != null) + { + closed = false; + receiverThread = new Thread(this); + receiverThread.start(); + } } /** @@ -97,7 +100,10 @@ public RTPConnectorInputStream(DatagramSocket socket) public synchronized void close() { closed = true; - socket.close(); + if(socket != null) + { + socket.close(); + } } /** diff --git a/src/net/java/sip/communicator/impl/neomedia/RTPConnectorOutputStream.java b/src/net/java/sip/communicator/impl/neomedia/RTPConnectorOutputStream.java index 4d1c2de61..a68fa2293 100755 --- a/src/net/java/sip/communicator/impl/neomedia/RTPConnectorOutputStream.java +++ b/src/net/java/sip/communicator/impl/neomedia/RTPConnectorOutputStream.java @@ -63,7 +63,7 @@ public class RTPConnectorOutputStream /** * Used for debugging. As we don't log every packet - * we must count them and decide which to log. + * we must count them and decide which to log. */ private long numberOfPackets = 0; @@ -169,7 +169,7 @@ public void removeTargets() * We don't log every rtp traffic. * We log only first then 300,500 and 1000 packets and * then every 5000 packet. - * + * * @param numOfPacket current packet number. * @return wether we should log the current packet. */ @@ -196,6 +196,11 @@ static boolean logPacket(long numOfPacket) */ private boolean send(RawPacket packet) { + if(socket == null) + { + return false; + } + numberOfPackets++; for (InetSocketAddress target : targets) { @@ -225,7 +230,7 @@ private boolean send(RawPacket packet) packet.getBuffer(), packet.getOffset(), packet.getLength()); - } + } } catch (IOException ex) { diff --git a/src/net/java/sip/communicator/impl/neomedia/device/DeviceConfiguration.java b/src/net/java/sip/communicator/impl/neomedia/device/DeviceConfiguration.java index 29f9737ee..46bdf3920 100644 --- a/src/net/java/sip/communicator/impl/neomedia/device/DeviceConfiguration.java +++ b/src/net/java/sip/communicator/impl/neomedia/device/DeviceConfiguration.java @@ -281,6 +281,8 @@ public class DeviceConfiguration public static final Dimension[] SUPPORTED_RESOLUTIONS = new Dimension[] { + // QVGA + new Dimension(160, 100), //QCIF new Dimension(176, 144), // QVGA diff --git a/src/net/java/sip/communicator/impl/neomedia/format/ParameterizedVideoFormat.java b/src/net/java/sip/communicator/impl/neomedia/format/ParameterizedVideoFormat.java index be4e04766..5d1f4843f 100644 --- a/src/net/java/sip/communicator/impl/neomedia/format/ParameterizedVideoFormat.java +++ b/src/net/java/sip/communicator/impl/neomedia/format/ParameterizedVideoFormat.java @@ -31,7 +31,7 @@ public ParameterizedVideoFormat( String encoding, Dimension size, int maxDataLength, - Class dataType, + Class dataType, float frameRate, Map fmtps) { @@ -181,7 +181,7 @@ public Format intersects(Format format) ((ParameterizedVideoFormat) intersection).fmtps = fmtps.isEmpty() ? MediaFormatImpl.EMPTY_FORMAT_PARAMETERS - : getFormatParameters(); + : getFormatParameters(); return intersection; } @@ -214,7 +214,7 @@ public boolean matches(Format format) /** * Initializes a new Map from an array in which the key and the * value of an association are expressed as consecutive elements. - * + * * @param the very type of the keys and the values to be associated in * the new Map * @param entries the associations to be created in the new Map diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerGTalkImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerGTalkImpl.java index 41083b38c..0742672a2 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerGTalkImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerGTalkImpl.java @@ -524,7 +524,6 @@ else if (prevPeerState.equals(CallPeerState.BUSY) public synchronized void answer() throws OperationFailedException { - System.out.println("answer"); RtpDescriptionPacketExtension answer = null; try @@ -623,7 +622,6 @@ protected void sendCandidates( { candidatesIQ.addExtension(candidate); } - System.out.println("IQ: " + candidatesIQ.toXML()); protocolProvider.getConnection().sendPacket(candidatesIQ); } diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerGTalkImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerGTalkImpl.java index 46885dbeb..082fe4a20 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerGTalkImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerGTalkImpl.java @@ -183,7 +183,6 @@ else if(ext.getNamespace().equals( atLeastOneValidDescription = true; } - if (!atLeastOneValidDescription) { ProtocolProviderServiceJabberImpl.throwOperationFailedException( @@ -259,8 +258,6 @@ public RtpDescriptionPacketExtension generateSessionAccept() continue; } - System.out.println("session format " + format); - // stream connector StreamConnector connector = transportManager.getStreamConnector(mediaType); @@ -337,8 +334,6 @@ public void processAnswer(RtpDescriptionPacketExtension answer) continue; } - System.out.println("format: " + format); - // stream connector StreamConnector connector = transportManager.getStreamConnector(mediaType); @@ -366,7 +361,7 @@ public void processAnswer(RtpDescriptionPacketExtension answer) * management * @see CallPeerMediaHandler#getTransportManager() */ - protected TransportManagerGTalkImpl getTransportManager() + protected synchronized TransportManagerGTalkImpl getTransportManager() { if (transportManager == null) { @@ -456,6 +451,9 @@ public RtpDescriptionPacketExtension createDescription() description.setNamespace(SessionIQProvider. GTALK_VIDEO_NAMESPACE); + ext.setAttribute("width", 640); + ext.setAttribute("height", 480); + ext.setAttribute("framerate", 15); isVideo = true; } description.addChildExtension(ext); diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicInstantMessagingJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicInstantMessagingJabberImpl.java index 9ed90bbbc..d79833b62 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicInstantMessagingJabberImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicInstantMessagingJabberImpl.java @@ -645,7 +645,7 @@ public void processPacket(Packet packet) // for some reason ' is not rendered correctly // from our ui, lets use its equivalent. Other - // similar chars(< > & ") seem ok. + // similar chars(< > & ") seem ok. receivedMessage = receivedMessage.replaceAll("'", "'"); @@ -1043,6 +1043,7 @@ private String createMailboxDescription(MailboxIQ mailboxIQ) return message.toString(); } + /** * Receives incoming MailNotification Packets */ diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicTelephonyJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicTelephonyJabberImpl.java index a96f1e19b..ef91c5e1e 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicTelephonyJabberImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicTelephonyJabberImpl.java @@ -9,6 +9,8 @@ import java.util.*; import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.*; +import net.java.sip.communicator.impl.protocol.jabber.extensions.gtalk.*; +import net.java.sip.communicator.service.neomedia.*; import net.java.sip.communicator.service.protocol.*; import net.java.sip.communicator.service.protocol.event.*; import net.java.sip.communicator.service.protocol.media.*; @@ -28,6 +30,7 @@ * @author Emil Ivov * @author Symphorien Wanko * @author Lyubomir Marinov + * @author Sebastien Vincent */ public class OperationSetBasicTelephonyJabberImpl extends AbstractOperationSetBasicTelephony @@ -127,14 +130,22 @@ public Call createCall(String callee) throws OperationFailedException { CallJabberImpl call = new CallJabberImpl(this); + CallPeer callPeer = null; - if (createOutgoingCall(call, callee) == null) + callPeer = createOutgoingCall(call, callee); + if (callPeer == null) { throw new OperationFailedException( "Failed to create outgoing call" + " because no peer was created", OperationFailedException.INTERNAL_ERROR); } + if(callPeer.getCall() != call) + { + // We may have a Google Talk call here + return callPeer.getCall(); + } + return call; } @@ -172,7 +183,7 @@ public Call createCall(Contact callee) * @throws OperationFailedException with the corresponding code if we fail * to create the call. */ - CallPeerJabberImpl createOutgoingCall( + AbstractCallPeer createOutgoingCall( CallJabberImpl call, String calleeAddress) throws OperationFailedException @@ -199,7 +210,7 @@ CallPeerJabberImpl createOutgoingCall( * @throws OperationFailedException with the corresponding code if we fail * to create the call. */ - CallPeerJabberImpl createOutgoingCall( + AbstractCallPeer createOutgoingCall( CallJabberImpl call, String calleeAddress, Iterable sessionInitiateExtensions) @@ -226,6 +237,9 @@ CallPeerJabberImpl createOutgoingCall( getProtocolProvider().getConnection().getRoster().getPresences( calleeAddress); String calleeURI = null; + boolean isGingle = false; + String gingleURI = null; + int bestPriorityGTalk = -1; // choose the resource that has the highest priority AND support Jingle while(it.hasNext()) @@ -252,6 +266,27 @@ CallPeerJabberImpl createOutgoingCall( fullCalleeURI = calleeURI; } } + else + { + // test GTALK property + if(!Boolean.getBoolean("gtalktesting")) + { + continue; + } + + /* see if peer supports Google Talk voice */ + if(getProtocolProvider().isExtFeatureListSupported( + calleeURI, ProtocolProviderServiceJabberImpl. + CAPS_GTALK_WEB_VOICE)) + { + if(priority > bestPriorityGTalk) + { + bestPriorityGTalk = priority; + isGingle = true; + gingleURI = calleeURI; + } + } + } } catch (XMPPException ex) { @@ -271,7 +306,15 @@ CallPeerJabberImpl createOutgoingCall( } */ - if(di != null) + if(isGingle) + { + if(logger.isInfoEnabled()) + { + logger.info(gingleURI + ": Google Talk dialect supported"); + } + fullCalleeURI = gingleURI; + } + else if(di != null) { if (logger.isInfoEnabled()) logger.info(fullCalleeURI + ": jingle supported "); @@ -279,10 +322,12 @@ CallPeerJabberImpl createOutgoingCall( else { if (logger.isInfoEnabled()) - logger.info(calleeURI + ": jingle not supported ?"); + logger.info(calleeURI + + ": jingle and Google Talk not supported ?"); + throw new OperationFailedException( "Failed to create OutgoingJingleSession.\n" - + calleeURI + " does not support jingle", + + calleeURI + " does not support jingle or Google Talk", OperationFailedException.INTERNAL_ERROR); } @@ -291,16 +336,30 @@ CallPeerJabberImpl createOutgoingCall( logger.info("Choose one is: " + fullCalleeURI + " " + bestPriority); } - CallPeerJabberImpl peer = null; + AbstractCallPeer peer = null; // initiate call try { - peer - = call.initiateSession( - fullCalleeURI, - di, - sessionInitiateExtensions); + if(isGingle) + { + logger.info("initiate Gingle call"); + CallGTalkImpl callGTalk = new CallGTalkImpl(this); + MediaUseCase useCase = call.getMediaUseCase(); + boolean isVideo = call.isLocalVideoAllowed(useCase); + + callGTalk.setLocalVideoAllowed(isVideo, useCase); + peer = callGTalk.initiateGTalkSession(fullCalleeURI, + sessionInitiateExtensions); + } + else if(di != null) + { + peer + = call.initiateSession( + fullCalleeURI, + di, + sessionInitiateExtensions); + } } catch(Throwable t) { @@ -397,7 +456,8 @@ public synchronized void putOnHold(CallPeer peer) private void putOnHold(CallPeer peer, boolean on) throws OperationFailedException { - ((CallPeerJabberImpl) peer).putOnHold(on); + if(peer instanceof CallPeerJabberImpl) + ((CallPeerJabberImpl) peer).putOnHold(on); } /** @@ -410,7 +470,7 @@ private void putOnHold(CallPeer peer, boolean on) @Override public void setMute(Call call, boolean mute) { - ((CallJabberImpl) call).setMute(mute); + ((MediaAwareCall) call).setMute(mute); } /** @@ -426,7 +486,15 @@ public synchronized void hangupCallPeer(CallPeer peer) throws ClassCastException, OperationFailedException { - ((CallPeerJabberImpl) peer).hangup(null, null); + // XXX maybe add answer/hangup abstract method to MediaAwareCallPeer + if(peer instanceof CallPeerJabberImpl) + { + ((CallPeerJabberImpl) peer).hangup(null, null); + } + else if(peer instanceof CallPeerGTalkImpl) + { + ((CallPeerGTalkImpl) peer).hangup(null, null); + } } /** @@ -439,7 +507,15 @@ public synchronized void hangupCallPeer(CallPeer peer) public void answerCallPeer(CallPeer peer) throws OperationFailedException { - ((CallPeerJabberImpl) peer).answer(); + // XXX maybe add answer/hangup abstract method to MediaAwareCallPeer + if(peer instanceof CallPeerJabberImpl) + { + ((CallPeerJabberImpl) peer).answer(); + } + else if(peer instanceof CallPeerGTalkImpl) + { + ((CallPeerGTalkImpl) peer).answer(); + } } /** @@ -451,6 +527,8 @@ public void shutdown() logger.trace("Ending all active calls. "); Iterator activeCalls = this.activeCallsRepository.getActiveCalls(); + Iterator activeGTalkCalls + = this.activeGTalkCallsRepository.getActiveCalls(); // this is fast, but events aren't triggered ... //jingleManager.disconnectAllSessions(); @@ -475,6 +553,26 @@ public void shutdown() } } } + + while(activeGTalkCalls.hasNext()) + { + CallGTalkImpl call = activeGTalkCalls.next(); + Iterator callPeers = call.getCallPeers(); + + //go through all call peers and say bye to every one. + while (callPeers.hasNext()) + { + CallPeer peer = callPeers.next(); + try + { + this.hangupCallPeer(peer); + } + catch (Exception ex) + { + logger.warn("Failed to properly hangup peer " + peer, ex); + } + } + } } /** @@ -508,13 +606,21 @@ private void unsubscribeForJinglePackets() */ public boolean accept(Packet packet) { + String sid = null; + //we only handle JingleIQ-s - if( ! (packet instanceof JingleIQ) ) + if( ! (packet instanceof JingleIQ) && !(packet instanceof SessionIQ)) { - CallPeerJabberImpl callPeer = + AbstractCallPeer callPeer = activeCallsRepository.findCallPeerBySessInitPacketID( packet.getPacketID()); + if(callPeer == null) + { + callPeer = activeGTalkCallsRepository. + findCallPeerBySessInitPacketID(packet.getPacketID()); + } + if(callPeer != null) { /* packet is a response to a Jingle call but is not a JingleIQ @@ -546,19 +652,40 @@ public boolean accept(Packet packet) return false; } - JingleIQ jingleIQ = (JingleIQ)packet; - - if( jingleIQ.getAction() == JingleAction.SESSION_INITIATE) + if(packet instanceof JingleIQ) { - //we only accept session-initiate-s dealing RTP - return - jingleIQ.containsContentChildOfType( - RtpDescriptionPacketExtension.class); + JingleIQ jingleIQ = (JingleIQ)packet; + + if( jingleIQ.getAction() == JingleAction.SESSION_INITIATE) + { + //we only accept session-initiate-s dealing RTP + return + jingleIQ.containsContentChildOfType( + RtpDescriptionPacketExtension.class); + } + + sid = jingleIQ.getSID(); + + //if this is not a session-initiate we'll only take it if we've + //already seen its session ID. + return (activeCallsRepository.findJingleSID(sid) != null); } + else if(packet instanceof SessionIQ) + { + SessionIQ sessionIQ = (SessionIQ)packet; + + if(sessionIQ.getGTalkType() == GTalkType.INITIATE) + { + return true; + } + + sid = sessionIQ.getID(); - //if this is not a session-initiate we'll only take it if we've - //already seen its session ID. - return (activeCallsRepository.findJingleSID(jingleIQ.getSID()) != null); + //if this is not a session's initiate we'll only take it if we've + //already seen its session ID. + return (activeGTalkCallsRepository.findSessionID(sid) != null); + } + return false; } /** @@ -570,38 +697,75 @@ public boolean accept(Packet packet) public void processPacket(Packet packet) { //this is not supposed to happen because of the filter ... but still - if (! (packet instanceof JingleIQ) ) + if (! (packet instanceof JingleIQ) && !(packet instanceof SessionIQ)) return; - JingleIQ jingleIQ = (JingleIQ)packet; - - //to prevent hijacking sessions from other jingle based features - //like file transfer for example, we should only send the - //ack if this is a session-initiate with rtp content or if we are - //the owners of this packet's sid - - //first ack all "set" requests. - if(jingleIQ.getType() == IQ.Type.SET) + // test GTALK property + if(!Boolean.getBoolean("gtalktesting") && (packet instanceof SessionIQ)) { - IQ ack = IQ.createResultIQ(jingleIQ); - protocolProvider.getConnection().sendPacket(ack); + return; } - try + if(packet instanceof JingleIQ) { - processJingleIQ(jingleIQ); + JingleIQ jingleIQ = (JingleIQ)packet; + + //to prevent hijacking sessions from other jingle based features + //like file transfer for example, we should only send the + //ack if this is a session-initiate with rtp content or if we are + //the owners of this packet's sid + + //first ack all "set" requests. + if(jingleIQ.getType() == IQ.Type.SET) + { + IQ ack = IQ.createResultIQ(jingleIQ); + protocolProvider.getConnection().sendPacket(ack); + } + + try + { + processJingleIQ(jingleIQ); + } + catch(Throwable t) + { + logger.info("Error while handling incoming Jingle packet: ", t); + + /* + * The Javadoc on ThreadDeath says: If ThreadDeath is caught by + * a method, it is important that it be rethrown so that the + * thread actually dies. + */ + if (t instanceof ThreadDeath) + throw (ThreadDeath) t; + } } - catch(Throwable t) + else if(packet instanceof SessionIQ) { - logger.info("Error while handling incoming Jingle packet: ", t); + SessionIQ sessionIQ = (SessionIQ)packet; - /* - * The Javadoc on ThreadDeath says: If ThreadDeath is caught by a - * method, it is important that it be rethrown so that the thread - * actually dies. - */ - if (t instanceof ThreadDeath) - throw (ThreadDeath) t; + //first ack all "set" requests. + if(sessionIQ.getType() == IQ.Type.SET) + { + IQ ack = IQ.createResultIQ(sessionIQ); + protocolProvider.getConnection().sendPacket(ack); + } + + try + { + processSessionIQ(sessionIQ); + } + catch(Throwable t) + { + logger.info("Error while handling incoming GTalk packet: ", t); + + /* + * The Javadoc on ThreadDeath says: If ThreadDeath is caught by + * a method, it is important that it be rethrown so that the + * thread actually dies. + */ + if (t instanceof ThreadDeath) + throw (ThreadDeath) t; + } } } @@ -780,6 +944,85 @@ else if (action == JingleAction.TRANSPORT_INFO) } } + /** + * Analyzes the sessionIQ's action and passes it to the + * corresponding handler. + * + * @param sessionIQ the {@link SessionIQ} packet we need to be analyzing. + */ + private void processSessionIQ(SessionIQ sessionIQ) + { + //let's first see whether we have a peer that's concerned by this IQ + CallPeerGTalkImpl callPeer = + activeGTalkCallsRepository.findCallPeer(sessionIQ.getID()); + IQ.Type type = sessionIQ.getType(); + + if(type == Type.RESULT) + { + return; + } + + if (type == Type.ERROR) + { + logger.error("Received error"); + + XMPPError error = sessionIQ.getError(); + String message = "Remote party returned an error!"; + + if(error != null) + { + logger.error(" code=" + error.getCode() + + " message=" + error.getMessage()); + + if (error.getMessage() != null) + message = error.getMessage(); + } + + if (callPeer != null) + callPeer.setState(CallPeerState.FAILED, message); + + return; + } + + GTalkType action = sessionIQ.getGTalkType(); + + if(action == GTalkType.INITIATE) + { + CallGTalkImpl call = null; + + if(call == null) + { + call = new CallGTalkImpl(this); + } + + call.processGTalkInitiate(sessionIQ); + return; + } + else if (callPeer == null) + { + if (logger.isDebugEnabled()) + logger.debug("Received a stray trying response."); + return; + } + //the rest of these cases deal with existing peers + else if(action == GTalkType.CANDIDATES) + { + callPeer.processCandidates(sessionIQ); + } + else if(action == GTalkType.REJECT) + { + callPeer.processSessionReject(sessionIQ); + } + else if(action == GTalkType.TERMINATE) + { + callPeer.processSessionTerminate(sessionIQ); + } + else if(action == GTalkType.ACCEPT) + { + callPeer.processSessionAccept(sessionIQ); + } + } + /** * Returns a reference to the {@link ActiveCallsRepositoryJabberImpl} that * we are currently using. @@ -824,7 +1067,8 @@ public ProtocolProviderServiceJabberImpl getProtocolProvider() */ public boolean isSecure(CallPeer peer) { - return ((CallPeerJabberImpl) peer).getMediaHandler().isSecure(); + return ((MediaAwareCallPeer) peer).getMediaHandler(). + isSecure(); } /** @@ -837,7 +1081,8 @@ public boolean isSecure(CallPeer peer) */ public void setSasVerified(CallPeer peer, boolean verified) { - ((CallPeerJabberImpl) peer).getMediaHandler().setSasVerified(verified); + ((MediaAwareCallPeer) peer).getMediaHandler().setSasVerified( + verified); } /** diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetContactCapabilitiesJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetContactCapabilitiesJabberImpl.java index ff0434c29..ad30151ab 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetContactCapabilitiesJabberImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetContactCapabilitiesJabberImpl.java @@ -55,6 +55,15 @@ public class OperationSetContactCapabilitiesJabberImpl private static final Map, String[]> OPERATION_SETS_TO_FEATURES = new HashMap, String[]>(); + /** + * The Map which associates specific additionnal + * OperationSet class with the capabilities to be supported by a + * Contact in order to consider the Contact to possess the + * respective OperationSet capability. + */ + private static final Map, String[]> + CAPS_OPERATION_SETS_TO_FEATURES + = new HashMap, String[]>(); static { @@ -86,6 +95,24 @@ public class OperationSetContactCapabilitiesJabberImpl ProtocolProviderServiceJabberImpl.URN_XMPP_JINGLE_RTP, ProtocolProviderServiceJabberImpl.URN_XMPP_JINGLE_RTP_VIDEO }); + + CAPS_OPERATION_SETS_TO_FEATURES.put( + OperationSetBasicTelephony.class, + new String[] + { + ProtocolProviderServiceJabberImpl.CAPS_GTALK_WEB_VOICE, + }); + + /* XXX video does not work yet so don't expose possibility to video call + * to users + CAPS_OPERATION_SETS_TO_FEATURES.put( + OperationSetVideoTelephony.class, + new String[] + { + ProtocolProviderServiceJabberImpl.CAPS_GTALK_WEB_VOICE, + ProtocolProviderServiceJabberImpl.CAPS_GTALK_WEB_VIDEO + }); + */ } /** @@ -297,7 +324,9 @@ private U getOperationSet(String jid, if (OFFLINE_OPERATION_SETS.contains(opsetClass)) return opset; else + { return null; + } } /* @@ -321,8 +350,33 @@ private U getOperationSet(String jid, && !parentProvider.isFeatureListSupported( jid, features))) - opset = null; + { + if(CAPS_OPERATION_SETS_TO_FEATURES.containsKey(opsetClass)) + { + String[] extFeatures = + CAPS_OPERATION_SETS_TO_FEATURES.get( + opsetClass); + + // test GTalk + if(!Boolean.getBoolean("gtalktesting")) + { + opset = null; + } + else + if((extFeatures == null) || ((extFeatures.length != 0) && + !parentProvider.isExtFeatureListSupported(jid, + extFeatures))) + { + opset = null; + } + } + else + { + opset = null; + } + } } + return opset; } diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetDesktopStreamingJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetDesktopStreamingJabberImpl.java index cd8d6cbdc..31c920638 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetDesktopStreamingJabberImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetDesktopStreamingJabberImpl.java @@ -13,6 +13,7 @@ import net.java.sip.communicator.service.neomedia.device.*; import net.java.sip.communicator.service.neomedia.format.*; import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.service.protocol.media.*; /** * Implements all desktop streaming related functions for XMPP. @@ -217,7 +218,8 @@ public void setLocalVideoAllowed(Call call, */ public boolean isLocalVideoAllowed(Call call) { - return ((CallJabberImpl)call).isLocalVideoAllowed(MediaUseCase.DESKTOP); + return ((MediaAwareCall)call). + isLocalVideoAllowed(MediaUseCase.DESKTOP); } /** diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetTelephonyConferencingJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetTelephonyConferencingJabberImpl.java index d0837b383..fc8915979 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetTelephonyConferencingJabberImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetTelephonyConferencingJabberImpl.java @@ -414,7 +414,7 @@ protected CallJabberImpl createOutgoingCall() * @throws OperationFailedException if inviting the specified callee to the * specified call fails */ - protected CallPeerJabberImpl inviteCalleeToCall( + protected CallPeer inviteCalleeToCall( String calleeAddress, CallJabberImpl call, boolean wasConferenceFocus) @@ -434,7 +434,8 @@ protected CallPeerJabberImpl inviteCalleeToCall( } CoinPacketExtension confInfo = new CoinPacketExtension(true); - return getBasicTelephony().createOutgoingCall(call, calleeAddress, + return getBasicTelephony().createOutgoingCall( + call, calleeAddress, Arrays.asList(new PacketExtension[] { confInfo })); } diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetVideoTelephonyJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetVideoTelephonyJabberImpl.java index fc49f8b88..3d2362340 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetVideoTelephonyJabberImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetVideoTelephonyJabberImpl.java @@ -127,13 +127,16 @@ protected Call createOutgoingVideoCall(String calleeAddress) , OperationFailedException.INTERNAL_ERROR); } - CallJabberImpl call = new CallJabberImpl(basicTelephony); + MediaAwareCall call = new CallJabberImpl(basicTelephony); /* enable video */ call.setLocalVideoAllowed(true, getMediaUseCase()); + CallPeer callPeer = + basicTelephony.createOutgoingCall((CallJabberImpl)call, calleeAddress); - basicTelephony.createOutgoingCall(call, calleeAddress); - return call; + // if call is a Google Talk ones, return the CallGTalkImpl + + return callPeer.getCall() == call ? call : callPeer.getCall(); } /** diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderServiceJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderServiceJabberImpl.java index 033ff14b0..1f7e72ab7 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderServiceJabberImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderServiceJabberImpl.java @@ -20,7 +20,10 @@ import net.java.sip.communicator.service.protocol.jabberconstants.*; import net.java.sip.communicator.util.*; import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.*; +import net.java.sip.communicator.impl.protocol.jabber.extensions.jingleinfo.*; +import net.java.sip.communicator.impl.protocol.jabber.extensions.gtalk.*; import net.java.sip.communicator.impl.protocol.jabber.extensions.inputevt.*; +import net.java.sip.communicator.impl.protocol.jabber.extensions.caps.*; import net.java.sip.communicator.impl.protocol.jabber.extensions.coin.*; import net.java.sip.communicator.impl.protocol.jabber.sasl.*; import net.java.sip.communicator.service.certificate.*; @@ -120,6 +123,23 @@ public class ProtocolProviderServiceJabberImpl public static final String URN_XMPP_JINGLE_RTP_HDREXT = "urn:xmpp:jingle:apps:rtp:rtp-hdrext:0"; + /** + * Capabilities name for audio call in Google Talk web version. + */ + public static final String CAPS_GTALK_WEB_VOICE = "voice-v1"; + + /** + * Capabilities name for video call (receive side) in Google Talk web + * version. + */ + public static final String CAPS_GTALK_WEB_VIDEO = "video-v1"; + + /** + * Capabilities name for video call (sender side) in Google Talk web + * version. + */ + public static final String CAPS_GTALK_WEB_CAMERA = "camera-v1"; + /** * Used to connect to a XMPP server. */ @@ -939,8 +959,19 @@ private void registerServiceDiscoveryManager() // Remove features supported by smack, but not supported in // SIP Communicator. new String[] { "http://jabber.org/protocol/commands" }, - // Add features SIP Communicator supports in addition to smack. - supportedFeatures.toArray(new String[supportedFeatures.size()])); + // Add features SIP Communicator supports in addition to + // smack. + supportedFeatures.toArray( + new String[supportedFeatures.size()])); + + if(Boolean.getBoolean("gtalktesting")) + { + // Add Google Talk "ext" capabilities + discoveryManager.addExtFeature(CAPS_GTALK_WEB_VOICE); + // XXX video does not work yet + //discoveryManager.addExtFeature(CAPS_GTALK_WEB_VIDEO); + //discoveryManager.addExtFeature(CAPS_GTALK_WEB_CAMERA); + } /* * Expose the discoveryManager as service-public through the @@ -1219,6 +1250,16 @@ protected void initialize(String screenname, new CoinIQProvider()); supportedFeatures.add(URN_XMPP_JINGLE_COIN); + //register our GTalk dialect provider + providerManager.addIQProvider( SessionIQ.ELEMENT_NAME, + SessionIQ.NAMESPACE, + new SessionIQProvider()); + + // register our JingleInfo provider + providerManager.addIQProvider(JingleInfoQueryIQ.ELEMENT_NAME, + JingleInfoQueryIQ.NAMESPACE, + new JingleInfoQueryIQProvider()); + //initialize the telephony operation set OperationSetBasicTelephonyJabberImpl basicTelephony = new OperationSetBasicTelephonyJabberImpl(this); @@ -1606,6 +1647,50 @@ JabberStatusEnum getJabberStatusEnum() return jabberStatusEnum; } + /** + * Determines if the given list of ext features is supported by the + * specified jabber id. + * + * @param jid the jabber id for which to check + * @param extFeatures the list of ext features to check for + * + * @return true if the list of ext features is supported; + * otherwise, false + */ + public boolean isExtFeatureListSupported(String jid, String... extFeatures) + { + EntityCapsManager capsManager = discoveryManager.getCapsManager(); + EntityCapsManager.Caps caps = capsManager.getCapsByUser(jid); + + if(caps != null && caps.ext != null) + { + String exts[] = caps.ext.split(" "); + boolean found = false; + + for(String extFeature : extFeatures) + { + found = false; + for(String ext : exts) + { + if(ext.equals(extFeature)) + { + found = true; + break; + } + } + + if(!found) + { + break; + } + } + + return true; + } + + return false; + } + /** * Determines if the given list of features is supported by the * specified jabber id. @@ -1926,6 +2011,8 @@ public void run() if(logger.isInfoEnabled()) { logger.info("Jingle Nodes discovery terminated!"); + logger.info("Found " + nodes.getRelayEntries().size() + + " Jingle Nodes relay"); } service.addEntries(nodes); diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/TransportManagerGTalkImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/TransportManagerGTalkImpl.java index b43d82b1e..8e06a804d 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/TransportManagerGTalkImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/TransportManagerGTalkImpl.java @@ -7,16 +7,27 @@ package net.java.sip.communicator.impl.protocol.jabber; import java.beans.*; +import java.io.*; import java.net.*; import java.util.*; import org.ice4j.*; import org.ice4j.ice.*; - +import org.ice4j.ice.harvest.*; +import org.ice4j.security.*; +import org.jivesoftware.smack.*; +import org.jivesoftware.smack.filter.*; +import org.jivesoftware.smack.packet.*; +import org.jivesoftware.smack.packet.IQ.*; +import org.jivesoftware.smack.util.StringUtils; + +import net.java.sip.communicator.impl.protocol.jabber.extensions.*; import net.java.sip.communicator.impl.protocol.jabber.extensions.gtalk.*; import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.*; +import net.java.sip.communicator.impl.protocol.jabber.extensions.jingleinfo.*; +import net.java.sip.communicator.service.httputil.*; +import net.java.sip.communicator.service.httputil.HttpUtils.HTTPResponseResult; import net.java.sip.communicator.service.neomedia.*; -import net.java.sip.communicator.service.netaddr.*; import net.java.sip.communicator.service.protocol.*; import net.java.sip.communicator.service.protocol.media.*; import net.java.sip.communicator.util.*; @@ -42,6 +53,16 @@ public class TransportManagerGTalkImpl private static final Logger logger = Logger.getLogger(TransportManagerGTalkImpl.class); + /** + * Default STUN server address. + */ + private static final String DEFAULT_STUN_SERVER_ADDRESS = "stun.jitsi.net"; + + /** + * Default STUN server port. + */ + private static final int DEFAULT_STUN_SERVER_PORT = 3478; + /** * The generation of the candidates we are currently generating */ @@ -68,6 +89,11 @@ public class TransportManagerGTalkImpl */ private final Agent iceAgent; + /** + * Synchronization object. + */ + private final Object wrapupSyncRoot = new Object(); + /** * Creates a new instance of this transport manager, binding it to the * specified peer. @@ -78,7 +104,6 @@ public class TransportManagerGTalkImpl public TransportManagerGTalkImpl(CallPeerGTalkImpl callPeer) { super(callPeer); - iceAgent = createIceAgent(); } @@ -132,6 +157,217 @@ protected InetAddress getIntendedDestination(CallPeerGTalkImpl peer) return peer.getProtocolProvider().getNextHop(); } + /** + * Request Google's Jingle info. + * + * @return list of servers + */ + public List requestJingleInfo() + { + JingleInfoQueryIQ iq = new JingleInfoQueryIQ(); + ProtocolProviderServiceJabberImpl provider = + getCallPeer().getProtocolProvider(); + String accountIDService = provider.getAccountID().getService(); + boolean jingleInfoIsSupported + = provider.isFeatureSupported(accountIDService, + JingleInfoQueryIQ.NAMESPACE); + final List servers = + new ArrayList(); + final Object syncRoot = new Object(); + + // check for google:jingleinfo support + if(!jingleInfoIsSupported) + { + return servers; + } + + if(logger.isDebugEnabled()) + logger.debug("google:jingleinfo supported for " + + provider.getOurJID()); + + iq.setFrom(provider.getOurJID()); + iq.setTo(StringUtils.parseBareAddress( + provider.getOurJID())); + iq.setType(Type.GET); + + XMPPConnection connection = provider.getConnection(); + PacketListener pl = new PacketListener() + { + public void processPacket(Packet p) + { + JingleInfoQueryIQ iq = (JingleInfoQueryIQ)p; + Iterator it = iq.getExtensions().iterator(); + + while(it.hasNext()) + { + AbstractPacketExtension ext = + (AbstractPacketExtension)it.next(); + + if(ext.getElementName().equals( + StunPacketExtension.ELEMENT_NAME)) + { + for(ServerPacketExtension e : + ext.getChildExtensionsOfType( + ServerPacketExtension.class)) + { + StunServerDescriptor dsc = + new StunServerDescriptor(e.getHost(), + e.getUdp(), false, null, null); + + servers.add(dsc); + } + } + else if(ext.getElementName().equals( + RelayPacketExtension.ELEMENT_NAME)) + { + String token = ((RelayPacketExtension)ext).getToken(); + for(ServerPacketExtension e : + ext.getChildExtensionsOfType( + ServerPacketExtension.class)) + { + String headerNames[] = new String[2]; + String headerValues[] = new String[2]; + String addr = "http://" + e.getHost() + + "/create_session"; + + headerNames[0] = "X-Talk-Google-Relay-Auth"; + headerNames[1] = "X-Google-Relay-Auth"; + headerValues[0] = token; + headerValues[1] = token; + + HTTPResponseResult res = + HttpUtils.openURLConnection(addr, + headerNames, headerValues); + Hashtable relayData = null; + + try + { + relayData = + parseGoogleRelay(res.getContentString()); + } + catch (IOException excpt) + { + logger.info("HTTP query to " + e.getHost() + + "failed", excpt); + break; + } + + String user = relayData.get("username"); + String password = relayData.get("passsword"); + StunServerDescriptor dsc = + new StunServerDescriptor( + relayData.get("relay"), + Integer.parseInt( + relayData.get("udpport")), + true, + user, password); + // not the RFC5766 TURN support + dsc.setOldTurn(true); + servers.add(dsc); + + /* XXX wait TCP support for Ice4j + dsc = new StunServerDescriptor( + relayData.get("relay"), + Integer.parseInt(relayData.get("tcpport")), + true, + user, + password); + dsc.setOldTurn(true); + servers.add(dsc); + + dsc = new StunServerDescriptor( + relayData.get("relay"), + Integer.parseInt(relayData.get("tcpport")), + true, + user, + password); + dsc.setOldTurn(true); + servers.add(dsc); + */ + } + } + } + synchronized(syncRoot) + { + syncRoot.notify(); + } + } + }; + + connection.addPacketListener(pl, + new PacketFilter() + { + public boolean accept(Packet p) + { + if(p instanceof JingleInfoQueryIQ) + return true; + + return false; + } + }); + + provider.getConnection().sendPacket(iq); + + synchronized(syncRoot) + { + try + { + syncRoot.wait(2000); + } + catch(InterruptedException e) + { + } + } + + connection.removePacketListener(pl); + return servers; + } + + /** + * Parse HTTP response from Google relay. + * + * @param res content string + * @return String + */ + public Hashtable parseGoogleRelay(String res) + { + // Separate each line + StringTokenizer tokenizer = new StringTokenizer(res, "\n"); + Hashtable ret = new Hashtable(); + + while(tokenizer.hasMoreTokens()) + { + String token = tokenizer.nextToken(); + + if(token.startsWith("relay.ip=")) + { + ret.put("relay", token.substring(token.indexOf("=") + 1)); + } + else if(token.startsWith("relay.udp_port=")) + { + ret.put("udpport", token.substring(token.indexOf("=") + 1)); + } + else if(token.startsWith("relay.tcp_port=")) + { + ret.put("tcpport", token.substring(token.indexOf("=") + 1)); + } + else if(token.startsWith("relay.ssltcp_port=")) + { + ret.put("ssltcpport", token.substring(token.indexOf("=") + 1)); + } + else if(token.startsWith("username=")) + { + ret.put("username", token.substring(token.indexOf("=") + 1)); + } + else if(token.startsWith("password=")) + { + ret.put("password", token.substring(token.indexOf("=") + 1)); + } + + } + return ret; + } + /** * Creates the ICE agent that we would be using in this transport manager * for all negotiation. @@ -142,44 +378,84 @@ protected InetAddress getIntendedDestination(CallPeerGTalkImpl peer) private Agent createIceAgent() { CallPeerGTalkImpl peer = getCallPeer(); - Agent agent = null; + Agent agent = new Agent(CompatibilityMode.GTALK); + List servers = null; + boolean atLeastOneStunServer = false; + ProtocolProviderServiceJabberImpl provider = peer.getProtocolProvider(); + JabberAccountID accID = (JabberAccountID)provider.getAccountID(); - /* XXX wait changes from ice4j - agent = new Agent(CompatibilityMode.GTALK); agent.setControlling(!peer.isInitiator()); - */ - /* XXX no configured STUN/TURN for the moment - * it should be discovered by a Google XMPP extension - for(StunServerDescriptor desc : accID.getStunServers()) + servers = requestJingleInfo(); + + for(StunServerDescriptor desc : servers) { TransportAddress addr = new TransportAddress( desc.getAddress(), desc.getPort(), Transport.UDP); - StunCandidateHarvester harvester; - + StunCandidateHarvester harvester = null; if(desc.isTurnSupported()) { - //Yay! a TURN server - harvester - = new TurnCandidateHarvester( - addr, - new LongTermCredential( - desc.getUsername(), - desc.getPassword())); + logger.info("Google TURN descriptor"); + /* Google relay server used a special way to allocate + * address (token + HTTP request, ...) and they don't support + * long-term authentication + */ + if(desc.isOldTurn()) + { + logger.info("new Google TURN harvester"); + harvester = new GoogleTurnCandidateHarvester( + addr, new String(desc.getUsername())); + } + else + { + harvester + = new TurnCandidateHarvester( + addr, + new LongTermCredential( + desc.getUsername(), + desc.getPassword())); + atLeastOneStunServer = true; + } } else { + // take only the first STUN server for now + if(atLeastOneStunServer) + continue; + //this is a STUN only server harvester = new StunCandidateHarvester(addr); + atLeastOneStunServer = true; + logger.info("Found Google STUN server " + harvester); } - if (logger.isInfoEnabled()) - logger.info("Adding pre-configured harvester " + harvester); + if(harvester != null) + { + agent.addCandidateHarvester(harvester); + } + } - atLeastOneStunServer = true; - agent.addCandidateHarvester(harvester); + if(!atLeastOneStunServer) + { + /* we have no configured or discovered STUN server so takes the + * default provided by us if user allows it + */ + if(accID.isUseDefaultStunServer()) + { + TransportAddress addr = new TransportAddress( + DEFAULT_STUN_SERVER_ADDRESS, + DEFAULT_STUN_SERVER_PORT, + Transport.UDP); + StunCandidateHarvester harvester = + new StunCandidateHarvester(addr); + + if(harvester != null) + { + agent.addCandidateHarvester(harvester); + } + } } if(accID.isUPNPEnabled()) @@ -191,7 +467,6 @@ private Agent createIceAgent() agent.addCandidateHarvester(harvester); } } - */ return agent; } @@ -265,9 +540,8 @@ public StreamConnector getStreamConnector(MediaType mediaType) if ((streamConnectorSockets != null) && ((streamConnector.getDataSocket() != streamConnectorSockets[0 /* RTP */]) - )) - //|| (streamConnector.getControlSocket() - // != streamConnectorSockets[1 /* RTCP */]))) + || (streamConnector.getControlSocket() + != streamConnectorSockets[1 /* RTCP */]))) { // Recreate the StreamConnector for the specified mediaType. closeStreamConnector(mediaType); @@ -340,18 +614,6 @@ else if(mediaType == MediaType.VIDEO) } if (streamConnectorSocketCount > 0) { - // XXX GTalk audio has not RTCP channel - if(mediaName.equals("rtp") && streamConnectorSocketCount == 1) - { - try - { - streamConnectorSockets[1] = new DatagramSocket(); - } - catch(Exception e) - { - } - } - return streamConnectorSockets; } } @@ -423,18 +685,10 @@ else if(mediaType == MediaType.VIDEO) } if (streamTargetAddressCount > 0) { - int rtcpIndex = 1; - - // XXX GTalk audio has not RTCP channel - if(mediaName.equals("rtp") && streamTargetAddressCount == 1) - { - rtcpIndex = 0; - } - streamTarget = new MediaStreamTarget( streamTargetAddresses[0 /* RTP */], - streamTargetAddresses[rtcpIndex /* RTCP */]); + streamTargetAddresses[1 /* RTCP */]); } } return streamTarget; @@ -446,12 +700,14 @@ else if(mediaType == MediaType.VIDEO) * * @param media the name of the stream we'd like to create. * + * @param rtcp if true allocate an RTCP port + * * @return the newly created {@link IceMediaStream} * * @throws OperationFailedException if binding on the specified media stream * fails for some reason. */ - private IceMediaStream createIceStream(String media) + private IceMediaStream createIceStream(String media, boolean rtcp) throws OperationFailedException { IceMediaStream stream; @@ -459,8 +715,16 @@ private IceMediaStream createIceStream(String media) try { //the following call involves STUN processing so it may take a while - stream = getNetAddrMgr().createIceStream( - nextMediaPortToTry, media, iceAgent); + stream = iceAgent.createMediaStream(media); + int rtpPort = nextMediaPortToTry; + + //rtp + iceAgent.createComponent(stream, Transport.UDP, rtpPort, rtpPort, + rtpPort + 100); + + if(rtcp) + iceAgent.createComponent(stream, Transport.UDP, + rtpPort + 1, rtpPort + 1, rtpPort + 101); } catch (Exception ex) { @@ -476,7 +740,8 @@ private IceMediaStream createIceStream(String media) //would simply include one more bind retry. try { - nextMediaPortToTry = stream.getComponent(Component.RTCP) + nextMediaPortToTry = stream.getComponent(rtcp ? Component.RTCP : + Component.RTP) .getLocalCandidates().get(0) .getTransportAddress().getPort() + 1; } @@ -490,19 +755,6 @@ private IceMediaStream createIceStream(String media) return stream; } - /** - * Returns a reference to the {@link NetworkAddressManagerService}. The only - * reason this method exists is that {@link JabberActivator - * #getNetworkAddressManagerService()} is too long to write and makes code - * look clumsy. - * - * @return a reference to the {@link NetworkAddressManagerService}. - */ - private static NetworkAddressManagerService getNetAddrMgr() - { - return JabberActivator.getNetworkAddressManagerService(); - } - /** * Starts transport candidate harvest. This method should complete rapidly * and, in case of lengthy procedures like STUN/TURN/UPnP candidate harvests @@ -528,44 +780,36 @@ public void startCandidateHarvest( List candidates = new LinkedList(); - for(PayloadTypePacketExtension ext : ourAnswer) + synchronized(wrapupSyncRoot) { - if(ext.getNamespace().equals( - SessionIQProvider.GTALK_AUDIO_NAMESPACE)) + for(PayloadTypePacketExtension ext : ourAnswer) { - audio = true; + if(ext.getNamespace().equals( + SessionIQProvider.GTALK_AUDIO_NAMESPACE)) + { + audio = true; + } + else if(ext.getNamespace().equals( + SessionIQProvider.GTALK_VIDEO_NAMESPACE)) + { + video = true; + } } - else if(ext.getNamespace().equals( - SessionIQProvider.GTALK_VIDEO_NAMESPACE)) + + if(audio) { - video = true; - } - } + IceMediaStream stream = createIceStream("rtp", video); - if(audio) - { - IceMediaStream stream = createIceStream("rtp"); + candidates.addAll(GTalkPacketFactory.createCandidates("rtp", + stream)); + } - /* remove RTCP component for the audio as it is not used and - * remote gmail peer does not send them - */ - for(Component cmp : stream.getComponents()) + if(video) { - if(cmp.getComponentID() == 2) - { - stream.removeComponent(cmp); - } + IceMediaStream stream = createIceStream("video_rtp", true); + candidates.addAll( + GTalkPacketFactory.createCandidates("video_rtp", stream)); } - - candidates.addAll(GTalkPacketFactory.createCandidates("rtp", - stream)); - } - - if(video) - { - IceMediaStream stream = createIceStream("video_rtp"); - candidates.addAll(GTalkPacketFactory.createCandidates("video_rtp", - stream)); } /* send candidates */ @@ -595,6 +839,12 @@ public void wrapupCandidateHarvest() public boolean startConnectivityEstablishment( Iterable remote) { + if (IceProcessingState.COMPLETED.equals(iceAgent.getState())/* || + IceProcessingState.FAILED.equals(iceAgent.getState())*/) + { + return true; + } + /* If ICE is already running, we try to update the checklists with * the candidates. Note that this is a best effort. */ @@ -618,21 +868,21 @@ public boolean startConnectivityEstablishment( // change name to retrieve properly the ICE media stream if(name.equals("rtp")) { - numComponent = 1; + numComponent = Component.RTP; } else if(name.equals("rtcp")) { name = "rtp"; - numComponent = 1; + numComponent = Component.RTCP; } else if(name.equals("video_rtp")) { - numComponent = 1; + numComponent = Component.RTP; } else if(name.equals("video_rtcp")) { name = "video_rtp"; - numComponent = 2; + numComponent = Component.RTCP; } IceMediaStream stream = iceAgent.getStream(name); @@ -661,7 +911,7 @@ else if(name.equals("video_rtcp")) Component component = stream.getComponent(numComponent); - /* XXX wait changes from ice4j + RemoteCandidate remoteCandidate = new RemoteCandidate( new TransportAddress( candidate.getAddress(), @@ -675,7 +925,6 @@ else if(name.equals("video_rtcp")) (long)(candidate.getPreference() * 1000), ufrag); component.addUpdateRemoteCandidate(remoteCandidate); - */ } /* update all components of all streams */ @@ -705,24 +954,29 @@ else if(name.equals("video_rtcp")) // change name to retrieve properly the ICE media stream if(name.equals("rtp")) { - numComponent = 1; + numComponent = Component.RTP; } else if(name.equals("rtcp")) { name = "rtp"; - numComponent = 1; + numComponent = Component.RTCP; } else if(name.equals("video_rtp")) { - numComponent = 1; + numComponent = Component.RTP; } else if(name.equals("video_rtcp")) { name = "video_rtp"; - numComponent = 2; + numComponent = Component.RTCP; } - IceMediaStream stream = iceAgent.getStream(name); + IceMediaStream stream = null; + + synchronized(wrapupSyncRoot) + { + stream = iceAgent.getStream(name); + } if(stream == null) { @@ -747,7 +1001,6 @@ else if(name.equals("video_rtcp")) Component component = stream.getComponent(numComponent); - /* XXX wait changes from ice4j RemoteCandidate remoteCandidate = new RemoteCandidate( new TransportAddress( candidate.getAddress(), @@ -761,7 +1014,6 @@ else if(name.equals("video_rtcp")) (long)(candidate.getPreference() * 1000), ufrag); component.addRemoteCandidate(remoteCandidate); - */ startConnectivityEstablishment = true; } @@ -780,9 +1032,7 @@ else if(name.equals("video_rtcp")) { for (Component component : stream.getComponents()) { - if(component.getName().equals("RTCP")) - continue; - if (component.getRemoteCandidateCount() < 2) + if (component.getRemoteCandidateCount() < 1) { startConnectivityEstablishment = false; break; @@ -888,4 +1138,4 @@ public void close() iceAgent.free(); } } -} +} \ No newline at end of file diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/gtalk/GTalkPacketFactory.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/gtalk/GTalkPacketFactory.java index e31f6335a..0efb85d59 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/gtalk/GTalkPacketFactory.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/gtalk/GTalkPacketFactory.java @@ -318,8 +318,8 @@ private static GTalkCandidatePacketExtension createCandidate( TransportAddress transportAddress = candidate.getTransportAddress(); - // XXX different username/password for each candidate ? - packet.setUsername(stream.getParentAgent().getLocalUfrag()); + // different username/password for each candidate ? + packet.setUsername(((LocalCandidate)candidate).getUfrag()); packet.setPassword(""); packet.setAddress(transportAddress.getHostAddress()); packet.setPort(transportAddress.getPort()); @@ -340,7 +340,8 @@ else if(candType == CandidateType.host) } packet.setType(candType); - packet.setPreference(candidate.getPriority() / 1000); + double priority = candidate.getPriority(); + packet.setPreference((priority / 1000)); return packet; } diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/gtalk/SessionIQ.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/gtalk/SessionIQ.java index eba797f69..362c0d40e 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/gtalk/SessionIQ.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/gtalk/SessionIQ.java @@ -12,7 +12,6 @@ import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.*; import org.jivesoftware.smack.packet.*; -import org.jivesoftware.smack.util.*; /** * A straightforward extension of the IQ. A SessionIQ object is created diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/gtalk/SessionIQProvider.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/gtalk/SessionIQProvider.java index 18751d48a..952295de7 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/gtalk/SessionIQProvider.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/gtalk/SessionIQProvider.java @@ -14,7 +14,8 @@ import org.xmlpull.v1.*; /** - * An implementation of a GTalk session IQ provider that parses incoming session IQs. + * An implementation of a GTalk session IQ provider that parses incoming session + * IQs. * * @author Sebastien Vincent */ @@ -97,9 +98,7 @@ public SessionIQProvider() * instance. * * @param parser an XML parser. - * * @return a new {@link SessionIQ} instance. - * * @throws Exception if an error occurs parsing the XML. */ public IQ parseIQ(XmlPullParser parser) throws Exception diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingleinfo/JingleInfoQueryIQ.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingleinfo/JingleInfoQueryIQ.java new file mode 100644 index 000000000..4dd0502db --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingleinfo/JingleInfoQueryIQ.java @@ -0,0 +1,61 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.jabber.extensions.jingleinfo; + +import org.jivesoftware.smack.packet.*; + +/** + * The JingleInfoQueryIQ is used to discover STUN and relay server via + * the Google's Jingle Server Discovery extension. + * + * @author Sebastien Vincent + */ +public class JingleInfoQueryIQ + extends IQ +{ + /** + * The namespace. + */ + public static final String NAMESPACE = "google:jingleinfo"; + + /** + * The element name. + */ + public static final String ELEMENT_NAME = "query"; + + /** + * Returns the sub-element XML section of the IQ packet, or null if + * there isn't one. Packet extensions must be included, if any are defined. + * + * @return the child element section of the IQ XML. + */ + @Override + public String getChildElementXML() + { + StringBuilder bld = new StringBuilder(); + + bld.append("<").append(ELEMENT_NAME).append(" xmlns='"). + append(NAMESPACE).append("'"); + + if(getExtensions().size() == 0) + { + bld.append("/>"); + } + else + { + bld.append(">"); + + for(PacketExtension pe : getExtensions()) + { + bld.append(pe.toXML()); + } + + bld.append(""); + } + return bld.toString(); + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingleinfo/JingleInfoQueryIQProvider.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingleinfo/JingleInfoQueryIQProvider.java new file mode 100644 index 000000000..473d606c7 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingleinfo/JingleInfoQueryIQProvider.java @@ -0,0 +1,92 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.jabber.extensions.jingleinfo; + +import net.java.sip.communicator.impl.protocol.jabber.extensions.*; + +import org.jivesoftware.smack.packet.*; +import org.jivesoftware.smack.provider.*; +import org.xmlpull.v1.*; + +/** + * Provider for the JingleInfoQueryIQ. + * + * @author Sebastien Vincent + */ +public class JingleInfoQueryIQProvider + implements IQProvider +{ + /** + * STUN packet extension provider. + */ + private final PacketExtensionProvider stunProvider = + new StunProvider(); + + /** + * Relay packet extension provider. + */ + private final PacketExtensionProvider relayProvider = + new RelayProvider(); + + /** + * Creates a new instance of the JingleInfoQueryIQProvider and + * register all related extension providers. It is the responsibility of the + * application to register the JingleInfoQueryIQProvider itself. + */ + public JingleInfoQueryIQProvider() + { + ProviderManager providerManager = ProviderManager.getInstance(); + + providerManager.addExtensionProvider( + ServerPacketExtension.ELEMENT_NAME, + ServerPacketExtension.NAMESPACE, + new DefaultPacketExtensionProvider + (ServerPacketExtension.class)); + } + + /** + * Parses a JingleInfoQueryIQ. + * + * @param parser an XML parser. + * @return a new {@link JingleInfoQueryIQ} instance. + * @throws Exception if an error occurs parsing the XML. + */ + public IQ parseIQ(XmlPullParser parser) + throws Exception + { + boolean done = false; + JingleInfoQueryIQ iq = new JingleInfoQueryIQ(); + + // Now go on and parse the session element's content. + while (!done) + { + int eventType = parser.next(); + String elementName = parser.getName(); + + if (eventType == XmlPullParser.START_TAG) + { + if(elementName.equals(StunPacketExtension.ELEMENT_NAME)) + { + iq.addExtension(stunProvider.parseExtension(parser)); + } + else if(elementName.equals(RelayPacketExtension.ELEMENT_NAME)) + { + iq.addExtension(relayProvider.parseExtension(parser)); + } + } + if (eventType == XmlPullParser.END_TAG) + { + if (parser.getName().equals(JingleInfoQueryIQ.ELEMENT_NAME)) + { + done = true; + } + } + } + + return iq; + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingleinfo/RelayPacketExtension.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingleinfo/RelayPacketExtension.java new file mode 100644 index 000000000..4cd73f47f --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingleinfo/RelayPacketExtension.java @@ -0,0 +1,90 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.jabber.extensions.jingleinfo; + +import org.jivesoftware.smack.packet.*; + +import net.java.sip.communicator.impl.protocol.jabber.extensions.*; + +/** + * Relay packet extension. + * + * @author Sebastien Vincent + */ +public class RelayPacketExtension + extends AbstractPacketExtension +{ + /** + * The namespace. + */ + public static final String NAMESPACE = null; + + /** + * The element name. + */ + public static final String ELEMENT_NAME = "relay"; + + /** + * The token. + */ + private String token = null; + + /** + * Constructor. + */ + public RelayPacketExtension() + { + super(NAMESPACE, ELEMENT_NAME); + } + + /** + * Set the token. + * + * @param token token + */ + public void setToken(String token) + { + this.token = token; + } + + /** + * Get the token. + * + * @return authentication token + */ + public String getToken() + { + return token; + } + + /** + * Get an XML string representation. + * + * @return XML string representation + */ + public String toXML() + { + StringBuilder bld = new StringBuilder(); + + bld.append("<").append(ELEMENT_NAME).append(">"); + + if(token != null) + { + bld.append("<").append("token").append(">"); + bld.append(token); + bld.append(""); + } + + for(PacketExtension pe : getChildExtensions()) + { + bld.append(pe.toXML()); + } + bld.append(""); + + return bld.toString(); + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingleinfo/RelayProvider.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingleinfo/RelayProvider.java new file mode 100644 index 000000000..8f4b2d67d --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingleinfo/RelayProvider.java @@ -0,0 +1,114 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.jabber.extensions.jingleinfo; + +import org.jivesoftware.smack.packet.*; +import org.jivesoftware.smack.provider.*; +import org.xmlpull.v1.*; + +/** + * Parser for RelayPacketExtension. + * + * @author Sebastien Vincent + */ +public class RelayProvider + implements PacketExtensionProvider +{ + /** + * Parses a users extension sub-packet and creates a {@link + * StunPacketExtension} instance. At the beginning of the method + * call, the xml parser will be positioned on the opening element of the + * packet extension. As required by the smack API, at the end of the method + * call, the parser will be positioned on the closing element of the packet + * extension. + * + * @param parser an XML parser positioned at the opening + * Server element. + * + * @return a new {@link RelayPacketExtension} instance. + * @throws java.lang.Exception if an error occurs parsing the XML. + */ + public PacketExtension parseExtension(XmlPullParser parser) + throws Exception + { + boolean done = false; + int eventType; + String elementName = null; + RelayPacketExtension ext + = new RelayPacketExtension(); + + while (!done) + { + eventType = parser.next(); + elementName = parser.getName(); + + if (eventType == XmlPullParser.START_TAG) + { + if(elementName.equals(ServerPacketExtension.ELEMENT_NAME)) + { + PacketExtensionProvider provider = (PacketExtensionProvider) + ProviderManager.getInstance().getExtensionProvider( + ServerPacketExtension.ELEMENT_NAME, + ServerPacketExtension.NAMESPACE); + PacketExtension childExtension = + provider.parseExtension(parser); + ext.addChildExtension(childExtension); + } + else if(elementName.equals("token")) + { + ext.setToken(parseText(parser)); + } + } + else if (eventType == XmlPullParser.END_TAG) + { + if (parser.getName().equals( + RelayPacketExtension.ELEMENT_NAME)) + { + done = true; + } + } + } + + return ext; + } + + /** + * Returns the content of the next {@link XmlPullParser#TEXT} element that + * we encounter in parser. + * + * @param parser the parse that we'll be probing for text. + * + * @return the content of the next {@link XmlPullParser#TEXT} element we + * come across or null if we encounter a closing tag first. + * + * @throws java.lang.Exception if an error occurs parsing the XML. + */ + public static String parseText(XmlPullParser parser) + throws Exception + { + boolean done = false; + + int eventType; + String text = null; + + while (!done) + { + eventType = parser.next(); + + if (eventType == XmlPullParser.TEXT) + { + text = parser.getText(); + } + else if (eventType == XmlPullParser.END_TAG) + { + done = true; + } + } + + return text; + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingleinfo/ServerPacketExtension.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingleinfo/ServerPacketExtension.java new file mode 100644 index 000000000..a2161bcc9 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingleinfo/ServerPacketExtension.java @@ -0,0 +1,96 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.jabber.extensions.jingleinfo; + +import net.java.sip.communicator.impl.protocol.jabber.extensions.*; + +/** + * Server packet extension. + * + * @author Sebastien Vincent + */ +public class ServerPacketExtension + extends AbstractPacketExtension +{ + /** + * The namespace. + */ + public static final String NAMESPACE = null; + + /** + * The element name. + */ + public static final String ELEMENT_NAME = "server"; + + /** + * Host attribute name. + */ + public static final String HOST_ATTR_NAME = "host"; + + /** + * TCP attribute name. + */ + public static final String TCP_ATTR_NAME = "tcp"; + + /** + * UDP attribute name. + */ + public static final String UDP_ATTR_NAME = "udp"; + + /** + * SSL attribute name. + */ + public static final String SSL_ATTR_NAME = "tcpssl"; + + /** + * Constructor. + */ + public ServerPacketExtension() + { + super(NAMESPACE, ELEMENT_NAME); + } + + /** + * Returns the host address. + * + * @return this host address + */ + public String getHost() + { + return super.getAttributeAsString(HOST_ATTR_NAME); + } + + /** + * Returns the UDP port. + * + * @return the UDP port + */ + public int getUdp() + { + return Integer.parseInt(super.getAttributeAsString(UDP_ATTR_NAME)); + } + + /** + * Returns the TCP port. + * + * @return the TCP port + */ + public int getTcp() + { + return Integer.parseInt(super.getAttributeAsString(TCP_ATTR_NAME)); + } + + /** + * Returns the SSL port. + * + * @return the SSL port + */ + public int getSsl() + { + return Integer.parseInt(super.getAttributeAsString(SSL_ATTR_NAME)); + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingleinfo/StunPacketExtension.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingleinfo/StunPacketExtension.java new file mode 100644 index 000000000..468b19ac8 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingleinfo/StunPacketExtension.java @@ -0,0 +1,36 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.jabber.extensions.jingleinfo; + +import net.java.sip.communicator.impl.protocol.jabber.extensions.*; + +/** + * Stun packet extension. + * + * @author Sebastien Vincent + */ +public class StunPacketExtension + extends AbstractPacketExtension +{ + /** + * The namespace. + */ + public static final String NAMESPACE = null; + + /** + * The element name. + */ + public static final String ELEMENT_NAME = "stun"; + + /** + * Constructor. + */ + public StunPacketExtension() + { + super(NAMESPACE, ELEMENT_NAME); + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingleinfo/StunProvider.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingleinfo/StunProvider.java new file mode 100644 index 000000000..b6aa54464 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingleinfo/StunProvider.java @@ -0,0 +1,74 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.jabber.extensions.jingleinfo; + +import org.jivesoftware.smack.packet.*; +import org.jivesoftware.smack.provider.*; +import org.xmlpull.v1.*; + +/** + * Parser for StunPacketExtension. + * + * @author Sebastien Vincent + */ +public class StunProvider + implements PacketExtensionProvider +{ + /** + * Parses a users extension sub-packet and creates a {@link + * StunPacketExtension} instance. At the beginning of the method + * call, the xml parser will be positioned on the opening element of the + * packet extension. As required by the smack API, at the end of the method + * call, the parser will be positioned on the closing element of the packet + * extension. + * + * @param parser an XML parser positioned at the opening + * Server element. + * + * @return a new {@link StunPacketExtension} instance. + * @throws java.lang.Exception if an error occurs parsing the XML. + */ + public PacketExtension parseExtension(XmlPullParser parser) + throws Exception + { + boolean done = false; + int eventType; + String elementName = null; + StunPacketExtension ext + = new StunPacketExtension(); + + while (!done) + { + eventType = parser.next(); + elementName = parser.getName(); + + if (eventType == XmlPullParser.START_TAG) + { + if(elementName.equals(ServerPacketExtension.ELEMENT_NAME)) + { + PacketExtensionProvider provider = (PacketExtensionProvider) + ProviderManager.getInstance().getExtensionProvider( + ServerPacketExtension.ELEMENT_NAME, + ServerPacketExtension.NAMESPACE); + PacketExtension childExtension = + provider.parseExtension(parser); + ext.addChildExtension(childExtension); + } + } + else if (eventType == XmlPullParser.END_TAG) + { + if (parser.getName().equals( + StunPacketExtension.ELEMENT_NAME)) + { + done = true; + } + } + } + + return ext; + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/jabber.provider.manifest.mf b/src/net/java/sip/communicator/impl/protocol/jabber/jabber.provider.manifest.mf index 47839ae29..9622993cf 100755 --- a/src/net/java/sip/communicator/impl/protocol/jabber/jabber.provider.manifest.mf +++ b/src/net/java/sip/communicator/impl/protocol/jabber/jabber.provider.manifest.mf @@ -47,6 +47,7 @@ Import-Package: org.osgi.framework, net.java.sip.communicator.service.netaddr, net.java.sip.communicator.service.argdelegation, net.java.sip.communicator.service.certificate, + net.java.sip.communicator.service.httputil, net.java.sip.communicator.service.gui, org.xmlpull.v1, org.xmlpull.mxp1, diff --git a/src/net/java/sip/communicator/impl/protocol/sip/CallPeerSipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/CallPeerSipImpl.java index 9fef684ff..273264163 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/CallPeerSipImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/CallPeerSipImpl.java @@ -16,7 +16,6 @@ import javax.sip.message.*; import net.java.sip.communicator.impl.protocol.sip.sdp.*; -import net.java.sip.communicator.service.neomedia.*; import net.java.sip.communicator.service.protocol.*; import net.java.sip.communicator.service.protocol.event.*; import net.java.sip.communicator.service.protocol.media.*; diff --git a/src/net/java/sip/communicator/service/httputil/HttpUtils.java b/src/net/java/sip/communicator/service/httputil/HttpUtils.java index 86a4b0c2e..a04650ba0 100644 --- a/src/net/java/sip/communicator/service/httputil/HttpUtils.java +++ b/src/net/java/sip/communicator/service/httputil/HttpUtils.java @@ -67,7 +67,23 @@ public class HttpUtils */ public static HTTPResponseResult openURLConnection(String address) { - return openURLConnection(address, null, null); + return openURLConnection(address, null, null, null, null); + } + + /** + * Opens a connection to the address. + * @param address the address to contact. + * @param headerParamNames additional header name to include + * @param headerParamValues corresponding header value to include + * @return the result if any or null if connection was not possible + * or canceled by user. + */ + public static HTTPResponseResult openURLConnection(String address, + String[] headerParamNames, + String[] headerParamValues) + { + return openURLConnection(address, null, null, headerParamNames, + headerParamValues); } /** @@ -79,12 +95,16 @@ public static HTTPResponseResult openURLConnection(String address) * @param passwordPropertyName the property to use to retrieve/store * password value if protected site is hit, for password * CredentialsStorageService service is used. + * @param headerParamNames additional header name to include + * @param headerParamValues corresponding header value to include * @return the result if any or null if connection was not possible * or canceled by user. */ public static HTTPResponseResult openURLConnection(String address, String usernamePropertyName, - String passwordPropertyName) + String passwordPropertyName, + String[] headerParamNames, + String[] headerParamValues) { try { @@ -93,6 +113,16 @@ public static HTTPResponseResult openURLConnection(String address, usernamePropertyName, passwordPropertyName, httpGet.getURI().getHost()); + /* add additional HTTP header */ + if(headerParamNames != null && headerParamValues != null) + { + for(int i = 0 ; i < headerParamNames.length ; i++) + { + httpGet.addHeader(new BasicHeader(headerParamNames[i], + headerParamValues[i])); + } + } + HttpEntity result = executeMethod(httpClient, httpGet); if(result == null) @@ -282,6 +312,7 @@ public static HTTPResponseResult postFile(String address, /** * Posting form to address. For submission we use POST method * which is "application/x-www-form-urlencoded" encoded. + * @param address HTTP address. * @param usernamePropertyName the property to use to retrieve/store * username value if protected site is hit, for username * ConfigurationService service is used. diff --git a/src/net/java/sip/communicator/service/protocol/StunServerDescriptor.java b/src/net/java/sip/communicator/service/protocol/StunServerDescriptor.java index 71e948c9c..3dafd92d9 100644 --- a/src/net/java/sip/communicator/service/protocol/StunServerDescriptor.java +++ b/src/net/java/sip/communicator/service/protocol/StunServerDescriptor.java @@ -44,6 +44,12 @@ public class StunServerDescriptor */ private boolean isTurnSupported; + /** + * If TURN version supported by this StunServerDescriptor is not + * the RFC 5766. + */ + private boolean isOldTurn = false; + /** * The username that we need to use with the server or null if * this server does not require a user name. @@ -274,6 +280,27 @@ public static StunServerDescriptor loadDescriptor( return stunServer; } + /** + * Returns true if the TURN protocol supported is not the RFC5766 ones. + * + * @return Returns true if the TURN protocol supported is not the RFC5766 + * ones. + */ + public boolean isOldTurn() + { + return isOldTurn; + } + + /** + * Set the old TURN support. + * + * @param val value to set + */ + public void setOldTurn(boolean val) + { + this.isOldTurn = val; + } + /** * Returns a String representation of this descriptor * diff --git a/src/net/java/sip/communicator/service/protocol/media/TransportManager.java b/src/net/java/sip/communicator/service/protocol/media/TransportManager.java index 0f8293e96..24be5f6fd 100644 --- a/src/net/java/sip/communicator/service/protocol/media/TransportManager.java +++ b/src/net/java/sip/communicator/service/protocol/media/TransportManager.java @@ -96,7 +96,8 @@ public StreamConnector getStreamConnector(MediaType mediaType) if ((streamConnector == null) || streamConnector.getDataSocket().isClosed() - || streamConnector.getControlSocket().isClosed()) + || (streamConnector.getControlSocket() != null && + streamConnector.getControlSocket().isClosed())) { streamConnectors[mediaType.ordinal()] = streamConnector